First pass at supporting multimorphic lambdas in lambda sets

This commit is contained in:
Ayaz Hafiz 2022-06-28 12:13:53 -04:00 committed by ayazhafiz
parent 1c40ad6127
commit 51978e08ed
No known key found for this signature in database
GPG Key ID: B443F7A3030C9AED
14 changed files with 1061 additions and 355 deletions

View File

@ -23,7 +23,11 @@ pub const STATIC_LIST_NAME: ConstName = ConstName(b"THIS IS A STATIC LIST");
const ENTRY_POINT_NAME: &[u8] = b"mainForHost";
pub fn func_name_bytes(proc: &Proc) -> [u8; SIZE] {
func_name_bytes_help(proc.name, proc.args.iter().map(|x| x.0), &proc.ret_layout)
func_name_bytes_help(
proc.name.call_name(),
proc.args.iter().map(|x| x.0),
&proc.ret_layout,
)
}
#[inline(always)]

View File

@ -106,8 +106,8 @@ trait Backend<'a> {
proc: Proc<'a>,
layout_ids: &mut LayoutIds<'a>,
) -> (Vec<u8>, Vec<Relocation>, Vec<'a, (Symbol, String)>) {
let layout_id = layout_ids.get(proc.name, &proc.ret_layout);
let proc_name = self.symbol_to_string(proc.name, layout_id);
let layout_id = layout_ids.get(proc.name.call_name(), &proc.ret_layout);
let proc_name = self.symbol_to_string(proc.name.call_name(), layout_id);
self.reset(proc_name, proc.is_self_recursive);
self.load_args(proc.args, &proc.ret_layout);
for (layout, sym) in proc.args {

View File

@ -4185,7 +4185,7 @@ fn build_procedures_help<'a, 'ctx, 'env>(
// only have top-level thunks for this proc's module in scope
// this retain is not needed for correctness, but will cause less confusion when debugging
let home = proc.name.module_id();
let home = proc.name.call_name().module_id();
current_scope.retain_top_level_thunks_for_module(home);
build_proc(
@ -4559,7 +4559,7 @@ pub fn build_proc<'a, 'ctx, 'env>(
}
};
let ident_string = proc.name.as_str(&env.interns);
let ident_string = proc.name.call_name().as_str(&env.interns);
let fn_name: String = format!("{}_1", ident_string);
build_closure_caller(

View File

@ -380,7 +380,7 @@ impl<'a> WasmBackend<'a> {
println!("\ngenerating procedure {:?}\n", proc.name);
}
self.append_proc_debug_name(proc.name);
self.append_proc_debug_name(proc.name.call_name());
self.start_proc(proc);

View File

@ -29,11 +29,12 @@ use roc_module::symbol::{
IdentIds, IdentIdsByModule, Interns, ModuleId, ModuleIds, PQModuleName, PackageModuleIds,
PackageQualified, Symbol,
};
use roc_mono::fresh_multimorphic_symbol;
use roc_mono::ir::{
CapturedSymbols, EntryPoint, ExternalSpecializations, PartialProc, Proc, ProcLayout, Procs,
ProcsBase, UpdateModeIds,
};
use roc_mono::layout::{Layout, LayoutCache, LayoutProblem};
use roc_mono::layout::{LambdaName, Layout, LayoutCache, LayoutProblem};
use roc_parse::ast::{self, Defs, ExtractSpaces, Spaced, StrLiteral, TypeAnnotation};
use roc_parse::header::{ExposedName, ImportsEntry, PackageEntry, PlatformHeader, To, TypedIdent};
use roc_parse::header::{HeaderFor, ModuleNameEnum, PackageName};
@ -4545,8 +4546,12 @@ fn build_pending_specializations<'a>(
// never gets called by Roc code, it will never
// get specialized!
if is_host_exposed {
let layout_result =
layout_cache.raw_from_var(mono_env.arena, expr_var, mono_env.subs);
let layout_result = layout_cache.raw_from_var(
mono_env.arena,
expr_var,
mono_env.subs,
fresh_multimorphic_symbol!(mono_env),
);
// cannot specialize when e.g. main's type contains type variables
if let Err(e) = layout_result {
@ -4571,7 +4576,7 @@ fn build_pending_specializations<'a>(
procs_base.host_specializations.insert_host_exposed(
mono_env.subs,
symbol,
LambdaName::only_receiver(symbol),
annotation,
expr_var,
);
@ -4605,8 +4610,12 @@ fn build_pending_specializations<'a>(
// never gets called by Roc code, it will never
// get specialized!
if is_host_exposed {
let layout_result =
layout_cache.raw_from_var(mono_env.arena, expr_var, mono_env.subs);
let layout_result = layout_cache.raw_from_var(
mono_env.arena,
expr_var,
mono_env.subs,
fresh_multimorphic_symbol!(mono_env),
);
// cannot specialize when e.g. main's type contains type variables
if let Err(e) = layout_result {
@ -4631,7 +4640,7 @@ fn build_pending_specializations<'a>(
procs_base.host_specializations.insert_host_exposed(
mono_env.subs,
symbol,
LambdaName::only_receiver(symbol),
annotation,
expr_var,
);
@ -4683,8 +4692,12 @@ fn build_pending_specializations<'a>(
// never gets called by Roc code, it will never
// get specialized!
if is_host_exposed {
let layout_result =
layout_cache.raw_from_var(mono_env.arena, expr_var, mono_env.subs);
let layout_result = layout_cache.raw_from_var(
mono_env.arena,
expr_var,
mono_env.subs,
fresh_multimorphic_symbol!(mono_env),
);
// cannot specialize when e.g. main's type contains type variables
if let Err(e) = layout_result {
@ -4709,7 +4722,7 @@ fn build_pending_specializations<'a>(
procs_base.host_specializations.insert_host_exposed(
mono_env.subs,
symbol,
LambdaName::only_receiver(symbol),
annotation,
expr_var,
);
@ -4743,8 +4756,12 @@ fn build_pending_specializations<'a>(
// never gets called by Roc code, it will never
// get specialized!
if is_host_exposed {
let layout_result =
layout_cache.raw_from_var(mono_env.arena, expr_var, mono_env.subs);
let layout_result = layout_cache.raw_from_var(
mono_env.arena,
expr_var,
mono_env.subs,
fresh_multimorphic_symbol!(mono_env),
);
// cannot specialize when e.g. main's type contains type variables
if let Err(e) = layout_result {
@ -4769,7 +4786,7 @@ fn build_pending_specializations<'a>(
procs_base.host_specializations.insert_host_exposed(
mono_env.subs,
symbol,
LambdaName::only_receiver(symbol),
annotation,
expr_var,
);

View File

@ -263,7 +263,7 @@ impl<'a> ParamMap<'a> {
self.declarations[index + i] = param;
}
self.visit_stmt(arena, proc.name, &proc.body);
self.visit_stmt(arena, proc.name.call_name(), &proc.body);
}
fn visit_proc_always_owned(
@ -282,7 +282,7 @@ impl<'a> ParamMap<'a> {
self.declarations[index + i] = param;
}
self.visit_stmt(arena, proc.name, &proc.body);
self.visit_stmt(arena, proc.name.call_name(), &proc.body);
}
fn visit_stmt(&mut self, arena: &'a Bump, _fnid: Symbol, stmt: &Stmt<'a>) {
@ -852,10 +852,10 @@ impl<'a> BorrowInfState<'a> {
let ys = Vec::from_iter_in(proc.args.iter().map(|t| t.1), self.arena).into_bump_slice();
self.update_param_set_symbols(ys);
self.current_proc = proc.name;
self.current_proc = proc.name.call_name();
// ensure that current_proc is in the owned map
self.owned.entry(proc.name).or_default();
self.owned.entry(proc.name.call_name()).or_default();
self.collect_stmt(param_map, &proc.body);
self.update_param_map_declaration(param_map, param_offset, proc.args.len());

View File

@ -8,7 +8,7 @@ use crate::ir::{
Call, CallSpecId, CallType, Expr, HostExposedLayouts, JoinPointId, ModifyRc, Proc, ProcLayout,
SelfRecursive, Stmt, UpdateModeId,
};
use crate::layout::{Builtin, Layout, UnionLayout};
use crate::layout::{Builtin, LambdaName, Layout, UnionLayout};
mod equality;
mod refcount;
@ -343,7 +343,7 @@ impl<'a> CodeGenHelp<'a> {
};
self.specializations[spec_index].proc = Some(Proc {
name: proc_symbol,
name: LambdaName::from_non_multimorphic(proc_symbol),
args,
body,
closure_data_layout: None,

View File

@ -1406,7 +1406,7 @@ fn visit_proc<'a, 'i>(
proc: &mut Proc<'a>,
layout: ProcLayout<'a>,
) {
let params = match param_map.get_symbol(proc.name, layout) {
let params = match param_map.get_symbol(proc.name.call_name(), layout) {
Some(slice) => slice,
None => Vec::from_iter_in(
proc.args.iter().cloned().map(|(layout, symbol)| Param {

File diff suppressed because it is too large Load Diff

View File

@ -67,8 +67,8 @@ impl<'a> RawFunctionLayout<'a> {
matches!(self, RawFunctionLayout::ZeroArgumentThunk(_))
}
fn new_help<'b>(
env: &mut Env<'a, 'b>,
fn new_help<'b, F: FreshMultimorphicSymbol>(
env: &mut Env<'a, 'b, F>,
var: Variable,
content: Content,
) -> Result<Self, LayoutProblem> {
@ -153,8 +153,8 @@ impl<'a> RawFunctionLayout<'a> {
}
}
fn layout_from_lambda_set(
_env: &mut Env<'a, '_>,
fn layout_from_lambda_set<F: FreshMultimorphicSymbol>(
_env: &mut Env<'a, '_, F>,
_lset: subs::LambdaSet,
) -> Result<Self, LayoutProblem> {
unreachable!()
@ -162,8 +162,8 @@ impl<'a> RawFunctionLayout<'a> {
// Self::layout_from_flat_type(env, lset.as_tag_union())
}
fn layout_from_flat_type(
env: &mut Env<'a, '_>,
fn layout_from_flat_type<F: FreshMultimorphicSymbol>(
env: &mut Env<'a, '_, F>,
flat_type: FlatType,
) -> Result<Self, LayoutProblem> {
use roc_types::subs::FlatType::*;
@ -184,8 +184,13 @@ impl<'a> RawFunctionLayout<'a> {
let fn_args = fn_args.into_bump_slice();
let ret = arena.alloc(ret);
let lambda_set =
LambdaSet::from_var(env.arena, env.subs, closure_var, env.target_info)?;
let lambda_set = LambdaSet::from_var(
env.arena,
env.subs,
closure_var,
env.target_info,
env.fresh_multimorphic_symbol,
)?;
Ok(Self::Function(fn_args, lambda_set, ret))
}
@ -216,7 +221,10 @@ impl<'a> RawFunctionLayout<'a> {
/// Returns Err(()) if given an error, or Ok(Layout) if given a non-erroneous Structure.
/// Panics if given a FlexVar or RigidVar, since those should have been
/// monomorphized away already!
fn from_var(env: &mut Env<'a, '_>, var: Variable) -> Result<Self, LayoutProblem> {
fn from_var<F: FreshMultimorphicSymbol>(
env: &mut Env<'a, '_, F>,
var: Variable,
) -> Result<Self, LayoutProblem> {
if env.is_seen(var) {
unreachable!("The initial variable of a signature cannot be seen already")
} else {
@ -672,13 +680,16 @@ impl std::fmt::Debug for SetElement<'_> {
impl std::fmt::Debug for LambdaSet<'_> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
struct Helper<'a> {
set: &'a [(Symbol, &'a [Layout<'a>])],
set: &'a [(LambdaName, &'a [Layout<'a>])],
}
impl std::fmt::Debug for Helper<'_> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let entries = self.set.iter().map(|x| SetElement {
symbol: x.0,
symbol: match (x.0).0 {
LambdaNameInner::Name(name) => name,
LambdaNameInner::Multimorphic { alias, .. } => alias,
},
layout: x.1,
});
@ -693,10 +704,79 @@ impl std::fmt::Debug for LambdaSet<'_> {
}
}
#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)]
enum LambdaNameInner {
/// Standard lambda name assigned during canonicalize/constrain
Name(Symbol),
/// Sometimes we can end up with lambdas of the same name and different captures in the same
/// lambda set, like [[Thunk U8, Thunk Str]]. See also https://github.com/rtfeldman/roc/issues/3336.
/// We call such lambdas "multi-morphic".
///
/// The current compilation scheme in such cases is to assign an alias name for subsequent such
/// lambda names, and then code-gen those lambda variants under a different `Proc`. In our
/// example, the lambda set would be transformed to something like
/// [[Thunk U8, Multimorphic(Thunk, ThunkAliasStr) Str]] which tells us to specialize the
/// second variant using the proc `Thunk` but under the name `ThunkAliasStr`, with that
/// particular closure layout.
///
/// Currently we do no de-duplication of alias names. This does make compilation faster, but
/// also we should expect redundant multimorphic aliases to be somewhat rare, as that means a
/// non-unitary lambda set is the same in multiple areas of a program.
Multimorphic {
/// The lambda we came from, e.g. `Thunk` in the example
source: Symbol,
/// The lambda we become, e.g. `ThunkAliasStr` in the example
alias: Symbol,
},
}
#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)]
pub struct LambdaName(LambdaNameInner);
impl LambdaName {
#[inline(always)]
pub fn source_name(&self) -> Symbol {
match self.0 {
LambdaNameInner::Name(name) => name,
LambdaNameInner::Multimorphic { source, .. } => source,
}
}
#[inline(always)]
pub fn call_name(&self) -> Symbol {
match self.0 {
LambdaNameInner::Name(name) => name,
LambdaNameInner::Multimorphic { alias, .. } => alias,
}
}
#[inline(always)]
pub fn is_multimorphic(&self) -> bool {
matches!(self.0, LambdaNameInner::Multimorphic { .. })
}
#[inline(always)]
pub fn from_non_multimorphic(name: Symbol) -> Self {
Self(LambdaNameInner::Name(name))
}
#[inline(always)]
pub fn thunk(name: Symbol) -> Self {
Self(LambdaNameInner::Name(name))
}
// When the function name is known, so there can only be one possible receiver, in such cases
// the lambda cannot be multimorphic.
#[inline(always)]
pub fn only_receiver(name: Symbol) -> Self {
Self(LambdaNameInner::Name(name))
}
}
#[derive(Clone, Copy, PartialEq, Eq, Hash)]
pub struct LambdaSet<'a> {
/// collection of function names and their closure arguments
pub set: &'a [(Symbol, &'a [Layout<'a>])],
pub set: &'a [(LambdaName, &'a [Layout<'a>])],
/// how the closure will be represented at runtime
representation: &'a Layout<'a>,
}
@ -726,8 +806,13 @@ impl<'a> LambdaSet<'a> {
}
/// Does the lambda set contain the given symbol?
/// NOTE: for multimorphic variants, this checks the alias name; the source name will always be
/// the name of the first multimorphic variant.
pub fn contains(&self, symbol: Symbol) -> bool {
self.set.iter().any(|(s, _)| *s == symbol)
self.set.iter().any(|(s, _)| match s.0 {
LambdaNameInner::Name(name) => name == symbol,
LambdaNameInner::Multimorphic { alias, .. } => alias == symbol,
})
}
pub fn is_represented(&self) -> Option<Layout<'a>> {
@ -741,12 +826,92 @@ impl<'a> LambdaSet<'a> {
}
}
pub fn layout_for_member(&self, function_symbol: Symbol) -> ClosureRepresentation<'a> {
pub fn layout_for_member_with_lambda_name(
&self,
lambda_name: LambdaName,
) -> ClosureRepresentation<'a> {
debug_assert!(
self.set.iter().any(|(s, _)| *s == function_symbol),
"function symbol not in set"
self.set.iter().any(|(s, _)| *s == lambda_name),
"lambda not in set"
);
let comparator =
|other_name: LambdaName, _other_captures_layouts: &[Layout]| other_name == lambda_name;
self.layout_for_member(comparator)
}
pub fn find_lambda_name(
&self,
function_symbol: Symbol,
captures_layouts: &[Layout],
) -> LambdaName {
debug_assert!(self.contains(function_symbol), "function symbol not in set");
let comparator = |other_name: LambdaName, other_captures_layouts: &[Layout]| {
let other_name = match other_name.0 {
LambdaNameInner::Name(name) => name,
// Take the source, since we'll want to pick out the multimorphic name if it
// matches
LambdaNameInner::Multimorphic { source, .. } => source,
};
other_name == function_symbol
&& captures_layouts.iter().eq(other_captures_layouts.iter())
};
let (name, _) = self
.set
.iter()
.find(|(name, layouts)| comparator(*name, layouts))
.expect("no lambda set found");
*name
}
// Layout for a single member of the lambda set, when you are constructing a proc, and already
// know the multimorphic name (if any).
// pub fn layout_for_member_constructing_proc(
// &self,
// lambda_name: LambdaName,
// ) -> ClosureRepresentation<'a> {
// debug_assert!(
// self.set.iter().any(|(s, _)| *s == lambda_name),
// "lambda not in set"
// );
// let comparator =
// |other_name: LambdaName, _other_captures_layouts: &[Layout]| other_name == lambda_name;
// self.layout_for_member(comparator)
// }
// Layout for a single member of the lambda set, when you are constructing a closure
// representation, and maybe need to pick out a multimorphic variant.
// pub fn layout_for_member_constructing_closure_data(
// &self,
// function_symbol: Symbol,
// captures_layouts: &[Layout],
// ) -> ClosureRepresentation<'a> {
// debug_assert!(self.contains(function_symbol), "function symbol not in set");
// let comparator = |other_name: LambdaName, other_captures_layouts: &[Layout]| {
// let other_name = match other_name.0 {
// LambdaNameInner::Name(name) => name,
// // Take the source, since we'll want to pick out the multimorphic name if it
// // matches
// LambdaNameInner::Multimorphic { source, .. } => source,
// };
// other_name == function_symbol
// && captures_layouts.iter().eq(other_captures_layouts.iter())
// };
// self.layout_for_member(comparator)
// }
fn layout_for_member<F>(&self, comparator: F) -> ClosureRepresentation<'a>
where
F: Fn(LambdaName, &[Layout]) -> bool,
{
match self.representation {
Layout::Union(union) => {
// here we rely on the fact that a union in a closure would be stored in a one-element record.
@ -755,17 +920,22 @@ impl<'a> LambdaSet<'a> {
UnionLayout::NonRecursive(_) => {
// get the fields from the set, where they are sorted in alphabetic order
// (and not yet sorted by their alignment)
let (index, (_, fields)) = self
let (index, (name, fields)) = self
.set
.iter()
.enumerate()
.find(|(_, (s, _))| *s == function_symbol)
.find(|(_, (s, layouts))| comparator(*s, layouts))
.unwrap();
let closure_name = match name.0 {
LambdaNameInner::Name(name) => name,
LambdaNameInner::Multimorphic { alias, .. } => alias,
};
ClosureRepresentation::Union {
tag_id: index as TagIdIntType,
alphabetic_order_fields: fields,
closure_name: function_symbol,
closure_name,
union_layout: *union,
}
}
@ -782,12 +952,14 @@ impl<'a> LambdaSet<'a> {
}
}
Layout::Struct { .. } => {
debug_assert_eq!(self.set.len(), 1);
// get the fields from the set, where they are sorted in alphabetic order
// (and not yet sorted by their alignment)
let (_, fields) = self
.set
.iter()
.find(|(s, _)| *s == function_symbol)
.find(|(s, layouts)| comparator(*s, layouts))
.unwrap();
ClosureRepresentation::AlphabeticOrderStruct(fields)
@ -829,38 +1001,67 @@ impl<'a> LambdaSet<'a> {
}
}
pub fn from_var(
pub fn from_var<F>(
arena: &'a Bump,
subs: &Subs,
closure_var: Variable,
target_info: TargetInfo,
) -> Result<Self, LayoutProblem> {
fresh_multimorphic_symbol: &mut F,
) -> Result<Self, LayoutProblem>
where
F: FreshMultimorphicSymbol,
{
match roc_types::pretty_print::resolve_lambda_set(subs, closure_var) {
ResolvedLambdaSet::Set(mut lambdas) => {
// sort the tags; make sure ordering stays intact!
lambdas.sort();
lambdas.sort_by_key(|(sym, _)| *sym);
let mut set = Vec::with_capacity_in(lambdas.len(), arena);
let mut env = Env {
arena,
subs,
seen: Vec::new_in(arena),
target_info,
};
let mut set: Vec<(LambdaName, &[Layout])> =
Vec::with_capacity_in(lambdas.len(), arena);
let mut last_function_symbol = None;
for (function_symbol, variables) in lambdas.iter() {
let mut arguments = Vec::with_capacity_in(variables.len(), arena);
let mut env = Env {
arena,
subs,
seen: Vec::new_in(arena),
target_info,
fresh_multimorphic_symbol,
};
for var in variables {
arguments.push(Layout::from_var(&mut env, *var)?);
}
set.push((*function_symbol, arguments.into_bump_slice()));
let lambda_name = match last_function_symbol {
None => LambdaNameInner::Name(*function_symbol),
Some(last_function_symbol) => {
if function_symbol != last_function_symbol {
LambdaNameInner::Name(*function_symbol)
} else {
LambdaNameInner::Multimorphic {
source: *function_symbol,
alias: (*fresh_multimorphic_symbol)(),
}
}
}
};
let lambda_name = LambdaName(lambda_name);
set.push((lambda_name, arguments.into_bump_slice()));
last_function_symbol = Some(function_symbol);
}
let representation =
arena.alloc(Self::make_representation(arena, subs, lambdas, target_info));
let representation = arena.alloc(Self::make_representation(
arena,
subs,
lambdas,
target_info,
fresh_multimorphic_symbol,
));
Ok(LambdaSet {
set: set.into_bump_slice(),
@ -878,14 +1079,22 @@ impl<'a> LambdaSet<'a> {
}
}
fn make_representation(
fn make_representation<F: FreshMultimorphicSymbol>(
arena: &'a Bump,
subs: &Subs,
tags: std::vec::Vec<(Symbol, std::vec::Vec<Variable>)>,
target_info: TargetInfo,
fresh_multimorphic_symbol: &mut F,
) -> Layout<'a> {
// otherwise, this is a closure with a payload
let variant = union_sorted_tags_help(arena, tags, None, subs, target_info);
let variant = union_sorted_tags_help(
arena,
tags,
None,
subs,
target_info,
fresh_multimorphic_symbol,
);
use UnionVariant::*;
match variant {
@ -952,14 +1161,21 @@ pub enum Builtin<'a> {
List(&'a Layout<'a>),
}
pub struct Env<'a, 'b> {
pub struct Env<'a, 'b, F>
where
F: FreshMultimorphicSymbol,
{
target_info: TargetInfo,
arena: &'a Bump,
seen: Vec<'a, Variable>,
subs: &'b Subs,
fresh_multimorphic_symbol: &'b mut F,
}
impl<'a, 'b> Env<'a, 'b> {
impl<'a, 'b, F> Env<'a, 'b, F>
where
F: FreshMultimorphicSymbol,
{
fn is_seen(&self, var: Variable) -> bool {
let var = self.subs.get_root_key_without_compacting(var);
@ -1009,8 +1225,8 @@ impl<'a> Layout<'a> {
field_order_hash: FieldOrderHash::ZERO_FIELD_HASH,
};
fn new_help<'b>(
env: &mut Env<'a, 'b>,
fn new_help<'b, F: FreshMultimorphicSymbol>(
env: &mut Env<'a, 'b, F>,
var: Variable,
content: Content,
) -> Result<Self, LayoutProblem> {
@ -1068,7 +1284,10 @@ impl<'a> Layout<'a> {
/// Returns Err(()) if given an error, or Ok(Layout) if given a non-erroneous Structure.
/// Panics if given a FlexVar or RigidVar, since those should have been
/// monomorphized away already!
fn from_var(env: &mut Env<'a, '_>, var: Variable) -> Result<Self, LayoutProblem> {
fn from_var<F: FreshMultimorphicSymbol>(
env: &mut Env<'a, '_, F>,
var: Variable,
) -> Result<Self, LayoutProblem> {
if env.is_seen(var) {
Ok(Layout::RecursivePointer)
} else {
@ -1392,12 +1611,16 @@ impl<'a> LayoutCache<'a> {
}
}
pub fn from_var(
pub fn from_var<F>(
&mut self,
arena: &'a Bump,
var: Variable,
subs: &Subs,
) -> Result<Layout<'a>, LayoutProblem> {
fresh_multimorphic_symbol: &mut F,
) -> Result<Layout<'a>, LayoutProblem>
where
F: FreshMultimorphicSymbol,
{
// Store things according to the root Variable, to avoid duplicate work.
let var = subs.get_root_key_without_compacting(var);
@ -1406,16 +1629,18 @@ impl<'a> LayoutCache<'a> {
subs,
seen: Vec::new_in(arena),
target_info: self.target_info,
fresh_multimorphic_symbol,
};
Layout::from_var(&mut env, var)
}
pub fn raw_from_var(
pub fn raw_from_var<F: FreshMultimorphicSymbol>(
&mut self,
arena: &'a Bump,
var: Variable,
subs: &Subs,
fresh_multimorphic_symbol: &mut F,
) -> Result<RawFunctionLayout<'a>, LayoutProblem> {
// Store things according to the root Variable, to avoid duplicate work.
let var = subs.get_root_key_without_compacting(var);
@ -1425,6 +1650,7 @@ impl<'a> LayoutCache<'a> {
subs,
seen: Vec::new_in(arena),
target_info: self.target_info,
fresh_multimorphic_symbol,
};
RawFunctionLayout::from_var(&mut env, var)
}
@ -1679,8 +1905,8 @@ impl<'a> Builtin<'a> {
}
}
fn layout_from_lambda_set<'a>(
env: &mut Env<'a, '_>,
fn layout_from_lambda_set<'a, F: FreshMultimorphicSymbol>(
env: &mut Env<'a, '_, F>,
lset: subs::LambdaSet,
) -> Result<Layout<'a>, LayoutProblem> {
// Lambda set is just a tag union from the layout's perspective.
@ -1709,8 +1935,8 @@ fn layout_from_lambda_set<'a>(
}
}
fn layout_from_flat_type<'a>(
env: &mut Env<'a, '_>,
fn layout_from_flat_type<'a, F: FreshMultimorphicSymbol>(
env: &mut Env<'a, '_, F>,
flat_type: FlatType,
) -> Result<Layout<'a>, LayoutProblem> {
use roc_types::subs::FlatType::*;
@ -1818,8 +2044,13 @@ fn layout_from_flat_type<'a>(
}
}
Func(_, closure_var, _) => {
let lambda_set =
LambdaSet::from_var(env.arena, env.subs, closure_var, env.target_info)?;
let lambda_set = LambdaSet::from_var(
env.arena,
env.subs,
closure_var,
env.target_info,
env.fresh_multimorphic_symbol,
)?;
Ok(Layout::LambdaSet(lambda_set))
}
@ -1898,17 +2129,19 @@ fn layout_from_flat_type<'a>(
pub type SortedField<'a> = (Lowercase, Variable, Result<Layout<'a>, Layout<'a>>);
pub fn sort_record_fields<'a>(
pub fn sort_record_fields<'a, F: FreshMultimorphicSymbol>(
arena: &'a Bump,
var: Variable,
subs: &Subs,
target_info: TargetInfo,
fresh_multimorphic_symbol: &mut F,
) -> Result<Vec<'a, SortedField<'a>>, LayoutProblem> {
let mut env = Env {
arena,
subs,
seen: Vec::new_in(arena),
target_info,
fresh_multimorphic_symbol,
};
let (it, _) = match gather_fields_unsorted_iter(subs, RecordFields::empty(), var) {
@ -1923,8 +2156,8 @@ pub fn sort_record_fields<'a>(
sort_record_fields_help(&mut env, it)
}
fn sort_record_fields_help<'a>(
env: &mut Env<'a, '_>,
fn sort_record_fields_help<'a, F: FreshMultimorphicSymbol>(
env: &mut Env<'a, '_, F>,
fields_map: impl Iterator<Item = (Lowercase, RecordField<Variable>)>,
) -> Result<Vec<'a, SortedField<'a>>, LayoutProblem> {
let target_info = env.target_info;
@ -2110,11 +2343,12 @@ impl<'a> WrappedVariant<'a> {
}
}
pub fn union_sorted_tags<'a>(
pub fn union_sorted_tags<'a, F: FreshMultimorphicSymbol>(
arena: &'a Bump,
var: Variable,
subs: &Subs,
target_info: TargetInfo,
fresh_multimorphic_symbol: &mut F,
) -> Result<UnionVariant<'a>, LayoutProblem> {
let var =
if let Content::RecursionVar { structure, .. } = subs.get_content_without_compacting(var) {
@ -2135,7 +2369,7 @@ pub fn union_sorted_tags<'a>(
| Err((_, Content::FlexVar(_) | Content::RigidVar(_)))
| Err((_, Content::RecursionVar { .. })) => {
let opt_rec_var = get_recursion_var(subs, var);
union_sorted_tags_help(arena, tags_vec, opt_rec_var, subs, target_info)
union_sorted_tags_help(arena, tags_vec, opt_rec_var, subs, target_info, fresh_multimorphic_symbol)
}
Err((_, Content::Error)) => return Err(LayoutProblem::Erroneous),
Err(other) => panic!("invalid content in tag union variable: {:?}", other),
@ -2164,8 +2398,8 @@ fn is_recursive_tag_union(layout: &Layout) -> bool {
)
}
fn union_sorted_tags_help_new<'a, L>(
env: &mut Env<'a, '_>,
fn union_sorted_tags_help_new<'a, L, F: FreshMultimorphicSymbol>(
env: &mut Env<'a, '_, F>,
tags_list: &[(&'_ L, &[Variable])],
opt_rec_var: Option<Variable>,
) -> UnionVariant<'a>
@ -2355,12 +2589,13 @@ where
}
}
pub fn union_sorted_tags_help<'a, L>(
pub fn union_sorted_tags_help<'a, L, F: FreshMultimorphicSymbol>(
arena: &'a Bump,
mut tags_vec: std::vec::Vec<(L, std::vec::Vec<Variable>)>,
opt_rec_var: Option<Variable>,
subs: &Subs,
target_info: TargetInfo,
fresh_multimorphic_symbol: &mut F,
) -> UnionVariant<'a>
where
L: Into<TagOrClosure> + Ord + Clone,
@ -2373,6 +2608,7 @@ where
subs,
seen: Vec::new_in(arena),
target_info,
fresh_multimorphic_symbol,
};
match tags_vec.len() {
@ -2561,8 +2797,8 @@ where
}
}
fn layout_from_newtype<'a, L: Label>(
env: &mut Env<'a, '_>,
fn layout_from_newtype<'a, L: Label, F: FreshMultimorphicSymbol>(
env: &mut Env<'a, '_, F>,
tags: &UnsortedUnionLabels<L>,
) -> Layout<'a> {
debug_assert!(tags.is_newtype_wrapper(env.subs));
@ -2585,7 +2821,10 @@ fn layout_from_newtype<'a, L: Label>(
}
}
fn layout_from_union<'a, L>(env: &mut Env<'a, '_>, tags: &UnsortedUnionLabels<L>) -> Layout<'a>
fn layout_from_union<'a, L, F: FreshMultimorphicSymbol>(
env: &mut Env<'a, '_, F>,
tags: &UnsortedUnionLabels<L>,
) -> Layout<'a>
where
L: Label + Ord + Into<TagOrClosure>,
{
@ -2661,8 +2900,8 @@ where
}
}
fn layout_from_recursive_union<'a, L>(
env: &mut Env<'a, '_>,
fn layout_from_recursive_union<'a, L, F: FreshMultimorphicSymbol>(
env: &mut Env<'a, '_, F>,
rec_var: Variable,
tags: &UnsortedUnionLabels<L>,
) -> Result<Layout<'a>, LayoutProblem>
@ -2843,8 +3082,8 @@ fn layout_from_num_content<'a>(
}
}
fn dict_layout_from_key_value<'a>(
env: &mut Env<'a, '_>,
fn dict_layout_from_key_value<'a, F: FreshMultimorphicSymbol>(
env: &mut Env<'a, '_, F>,
key_var: Variable,
value_var: Variable,
) -> Result<Layout<'a>, LayoutProblem> {
@ -2876,8 +3115,11 @@ fn dict_layout_from_key_value<'a>(
)))
}
pub fn list_layout_from_elem<'a>(
env: &mut Env<'a, '_>,
pub trait FreshMultimorphicSymbol: FnMut() -> Symbol {}
impl<T> FreshMultimorphicSymbol for T where T: FnMut() -> Symbol {}
pub fn list_layout_from_elem<'a, F: FreshMultimorphicSymbol>(
env: &mut Env<'a, '_, F>,
element_var: Variable,
) -> Result<Layout<'a>, LayoutProblem> {
let is_variable = |content| matches!(content, &Content::FlexVar(_) | &Content::RigidVar(_));
@ -3032,7 +3274,7 @@ mod test {
#[test]
fn width_and_alignment_union_empty_struct() {
let lambda_set = LambdaSet {
set: &[(Symbol::LIST_MAP, &[])],
set: &[(LambdaName::from_non_multimorphic(Symbol::LIST_MAP), &[])],
representation: &Layout::UNIT,
};

View File

@ -1525,3 +1525,28 @@ fn tail_call_with_different_layout() {
"#
)
}
#[mono_test]
#[ignore]
fn lambda_sets_collide_with_captured_var() {
indoc!(
r#"
capture : a -> ({} -> Str)
capture = \val ->
thunk =
\{} ->
when val is
_ -> ""
thunk
x : [True, False]
fun =
when x is
True -> capture 1u8
False -> capture 1u64
fun {}
"#
)
}

View File

@ -130,9 +130,9 @@ impl<'a> ReplApp<'a> for CliApp {
/// Run user code that returns a type with a `Builtin` layout
/// Size of the return value is statically determined from its Rust type
fn call_function<Return, F>(&self, main_fn_name: &str, transform: F) -> Expr<'a>
fn call_function<Return, F>(&self, main_fn_name: &str, mut transform: F) -> Expr<'a>
where
F: Fn(&'a Self::Memory, Return) -> Expr<'a>,
F: FnMut(&'a Self::Memory, Return) -> Expr<'a>,
Self::Memory: 'a,
{
run_jit_function!(self.lib, main_fn_name, Return, |v| transform(&CliMemory, v))
@ -143,10 +143,10 @@ impl<'a> ReplApp<'a> for CliApp {
&self,
main_fn_name: &str,
ret_bytes: usize,
transform: F,
mut transform: F,
) -> T
where
F: Fn(&'a Self::Memory, usize) -> T,
F: FnMut(&'a Self::Memory, usize) -> T,
Self::Memory: 'a,
{
run_jit_function_dynamic_type!(self.lib, main_fn_name, ret_bytes, |v| transform(
@ -305,6 +305,7 @@ fn gen_and_eval_llvm<'a>(
let app = CliApp { lib };
let mut env = env;
let res_answer = jit_to_ast(
&arena,
&app,
@ -312,6 +313,8 @@ fn gen_and_eval_llvm<'a>(
main_fn_layout,
content,
&subs,
home,
env.interns.all_ident_ids.get_mut(&home).unwrap(),
target_info,
);

View File

@ -1,12 +1,13 @@
use bumpalo::collections::Vec;
use bumpalo::Bump;
use roc_mono::fresh_multimorphic_symbol;
use std::cmp::{max_by_key, min_by_key};
use roc_builtins::bitcode::{FloatWidth, IntWidth};
use roc_collections::all::MutMap;
use roc_module::called_via::CalledVia;
use roc_module::ident::TagName;
use roc_module::symbol::Symbol;
use roc_module::symbol::{IdentIds, ModuleId, Symbol};
use roc_mono::ir::ProcLayout;
use roc_mono::layout::{
union_sorted_tags_help, Builtin, Layout, LayoutCache, UnionLayout, UnionVariant, WrappedVariant,
@ -19,10 +20,12 @@ use roc_types::subs::{Content, FlatType, GetSubsSlice, RecordFields, Subs, Union
use crate::{ReplApp, ReplAppMemory};
struct Env<'a, 'env> {
struct Env<'a> {
home: ModuleId,
arena: &'a Bump,
subs: &'env Subs,
subs: &'a Subs,
target_info: TargetInfo,
ident_ids: &'a mut IdentIds,
}
pub enum ToAstProblem {
@ -45,12 +48,16 @@ pub fn jit_to_ast<'a, A: ReplApp<'a>>(
layout: ProcLayout<'a>,
content: &'a Content,
subs: &'a Subs,
module_id: ModuleId,
ident_ids: &'a mut IdentIds,
target_info: TargetInfo,
) -> Result<Expr<'a>, ToAstProblem> {
let env = Env {
let mut env = Env {
arena,
subs,
target_info,
home: module_id,
ident_ids,
};
match layout {
@ -59,7 +66,7 @@ pub fn jit_to_ast<'a, A: ReplApp<'a>>(
result,
} => {
// this is a thunk
jit_to_ast_help(&env, app, main_fn_name, &result, content)
jit_to_ast_help(&mut env, app, main_fn_name, &result, content)
}
_ => Err(ToAstProblem::FunctionLayout),
}
@ -86,7 +93,7 @@ enum NewtypeKind<'a> {
///
/// Returns (new type containers, optional alias content, real content).
fn unroll_newtypes_and_aliases<'a>(
env: &Env<'a, 'a>,
env: &Env<'a>,
mut content: &'a Content,
) -> (Vec<'a, NewtypeKind<'a>>, Option<&'a Content>, &'a Content) {
let mut newtype_containers = Vec::with_capacity_in(1, env.arena);
@ -134,7 +141,7 @@ fn unroll_newtypes_and_aliases<'a>(
}
fn apply_newtypes<'a>(
env: &Env<'a, '_>,
env: &Env<'a>,
newtype_containers: Vec<'a, NewtypeKind<'a>>,
mut expr: Expr<'a>,
) -> Expr<'a> {
@ -161,7 +168,7 @@ fn apply_newtypes<'a>(
expr
}
fn unroll_recursion_var<'a>(env: &Env<'a, 'a>, mut content: &'a Content) -> &'a Content {
fn unroll_recursion_var<'a>(env: &Env<'a>, mut content: &'a Content) -> &'a Content {
while let Content::RecursionVar { structure, .. } = content {
content = env.subs.get_content_without_compacting(*structure);
}
@ -169,7 +176,7 @@ fn unroll_recursion_var<'a>(env: &Env<'a, 'a>, mut content: &'a Content) -> &'a
}
fn get_tags_vars_and_variant<'a>(
env: &Env<'a, '_>,
env: &mut Env<'a>,
tags: &UnionTags,
opt_rec_var: Option<Variable>,
) -> (MutMap<TagName, std::vec::Vec<Variable>>, UnionVariant<'a>) {
@ -180,14 +187,20 @@ fn get_tags_vars_and_variant<'a>(
let vars_of_tag: MutMap<_, _> = tags_vec.iter().cloned().collect();
let union_variant =
union_sorted_tags_help(env.arena, tags_vec, opt_rec_var, env.subs, env.target_info);
let union_variant = union_sorted_tags_help(
env.arena,
tags_vec,
opt_rec_var,
env.subs,
env.target_info,
fresh_multimorphic_symbol!(env),
);
(vars_of_tag, union_variant)
}
fn expr_of_tag<'a, M: ReplAppMemory>(
env: &Env<'a, 'a>,
env: &mut Env<'a>,
mem: &'a M,
data_addr: usize,
tag_name: &TagName,
@ -211,7 +224,7 @@ fn expr_of_tag<'a, M: ReplAppMemory>(
/// Gets the tag ID of a union variant, assuming that the tag ID is stored alongside (after) the
/// tag data. The caller is expected to check that the tag ID is indeed stored this way.
fn tag_id_from_data<'a, M: ReplAppMemory>(
env: &Env<'a, 'a>,
env: &Env<'a>,
mem: &M,
union_layout: UnionLayout,
data_addr: usize,
@ -239,7 +252,7 @@ fn tag_id_from_data<'a, M: ReplAppMemory>(
/// - the tag ID
/// - the address of the data of the union variant, unmasked if the pointer held the tag ID
fn tag_id_from_recursive_ptr<'a, M: ReplAppMemory>(
env: &Env<'a, 'a>,
env: &Env<'a>,
mem: &M,
union_layout: UnionLayout,
rec_addr: usize,
@ -264,7 +277,7 @@ const OPAQUE_FUNCTION: Expr = Expr::Var {
};
fn jit_to_ast_help<'a, A: ReplApp<'a>>(
env: &Env<'a, 'a>,
env: &mut Env<'a>,
app: &'a A,
main_fn_name: &str,
layout: &Layout<'a>,
@ -344,6 +357,11 @@ fn jit_to_ast_help<'a, A: ReplApp<'a>>(
todo!("add support for rendering builtin {:?} to the REPL", other)
}
Layout::Struct { field_layouts, .. } => {
let fields = [Layout::u64(), *layout];
let layout = Layout::struct_no_name_order(&fields);
let result_stack_size = layout.stack_size(env.target_info);
let struct_addr_to_ast = |mem: &'a A::Memory, addr: usize| match raw_content {
Content::Structure(FlatType::Record(fields, _)) => {
Ok(struct_to_ast(env, mem, addr, *fields))
@ -389,11 +407,6 @@ fn jit_to_ast_help<'a, A: ReplApp<'a>>(
}
};
let fields = [Layout::u64(), *layout];
let layout = Layout::struct_no_name_order(&fields);
let result_stack_size = layout.stack_size(env.target_info);
app.call_function_dynamic_size(
main_fn_name,
result_stack_size as usize,
@ -462,7 +475,7 @@ fn jit_to_ast_help<'a, A: ReplApp<'a>>(
result.map(|e| apply_newtypes(env, newtype_containers, e))
}
fn tag_name_to_expr<'a>(env: &Env<'a, '_>, tag_name: &TagName) -> Expr<'a> {
fn tag_name_to_expr<'a>(env: &Env<'a>, tag_name: &TagName) -> Expr<'a> {
Expr::Tag(env.arena.alloc_str(&tag_name.as_ident_str()))
}
@ -475,7 +488,7 @@ enum WhenRecursive<'a> {
}
fn addr_to_ast<'a, M: ReplAppMemory>(
env: &Env<'a, 'a>,
env: &mut Env<'a>,
mem: &'a M,
addr: usize,
layout: &Layout<'a>,
@ -770,7 +783,7 @@ fn addr_to_ast<'a, M: ReplAppMemory>(
}
fn list_to_ast<'a, M: ReplAppMemory>(
env: &Env<'a, 'a>,
env: &mut Env<'a>,
mem: &'a M,
addr: usize,
len: usize,
@ -822,7 +835,7 @@ fn list_to_ast<'a, M: ReplAppMemory>(
}
fn single_tag_union_to_ast<'a, M: ReplAppMemory>(
env: &Env<'a, 'a>,
env: &mut Env<'a>,
mem: &'a M,
addr: usize,
field_layouts: &'a [Layout<'a>],
@ -849,7 +862,7 @@ fn single_tag_union_to_ast<'a, M: ReplAppMemory>(
}
fn sequence_of_expr<'a, I, M: ReplAppMemory>(
env: &Env<'a, 'a>,
env: &mut Env<'a>,
mem: &'a M,
addr: usize,
sequence: I,
@ -881,7 +894,7 @@ where
}
fn struct_to_ast<'a, M: ReplAppMemory>(
env: &Env<'a, 'a>,
env: &mut Env<'a>,
mem: &'a M,
addr: usize,
record_fields: RecordFields,
@ -900,7 +913,12 @@ fn struct_to_ast<'a, M: ReplAppMemory>(
let inner_content = env.subs.get_content_without_compacting(field.into_inner());
let field_layout = layout_cache
.from_var(arena, field.into_inner(), env.subs)
.from_var(
arena,
field.into_inner(),
env.subs,
fresh_multimorphic_symbol!(env),
)
.unwrap();
let inner_layouts = arena.alloc([field_layout]);
@ -939,7 +957,12 @@ fn struct_to_ast<'a, M: ReplAppMemory>(
for (label, field) in record_fields.sorted_iterator(subs, Variable::EMPTY_RECORD) {
let content = subs.get_content_without_compacting(field.into_inner());
let field_layout = layout_cache
.from_var(arena, field.into_inner(), env.subs)
.from_var(
arena,
field.into_inner(),
env.subs,
fresh_multimorphic_symbol!(env),
)
.unwrap();
let loc_expr = &*arena.alloc(Loc {
@ -1006,7 +1029,7 @@ fn unpack_two_element_tag_union(
}
fn bool_to_ast<'a, M: ReplAppMemory>(
env: &Env<'a, '_>,
env: &Env<'a>,
mem: &M,
value: bool,
content: &Content,
@ -1081,7 +1104,7 @@ fn bool_to_ast<'a, M: ReplAppMemory>(
}
fn byte_to_ast<'a, M: ReplAppMemory>(
env: &Env<'a, '_>,
env: &mut Env<'a>,
mem: &M,
value: u8,
content: &Content,
@ -1139,6 +1162,7 @@ fn byte_to_ast<'a, M: ReplAppMemory>(
None,
env.subs,
env.target_info,
fresh_multimorphic_symbol!(env),
);
match union_variant {

View File

@ -12,7 +12,7 @@ pub trait ReplApp<'a> {
/// The `transform` callback takes the app's memory and the returned value
fn call_function<Return, F>(&self, main_fn_name: &str, transform: F) -> Expr<'a>
where
F: Fn(&'a Self::Memory, Return) -> Expr<'a>,
F: FnMut(&'a Self::Memory, Return) -> Expr<'a>,
Self::Memory: 'a;
/// Run user code that returns a struct or union, whose size is provided as an argument
@ -24,7 +24,7 @@ pub trait ReplApp<'a> {
transform: F,
) -> T
where
F: Fn(&'a Self::Memory, usize) -> T,
F: FnMut(&'a Self::Memory, usize) -> T,
Self::Memory: 'a;
}