mirror of
https://github.com/roc-lang/roc.git
synced 2025-10-02 08:11:12 +00:00
Merge remote-tracking branch 'origin/trunk' into builtins-in-roc-delayed-alias
This commit is contained in:
commit
4e1197165b
181 changed files with 9495 additions and 2273 deletions
|
@ -724,6 +724,23 @@ impl<'a> BorrowInfState<'a> {
|
|||
// the function must take it as an owned parameter
|
||||
self.own_args_if_param(xs);
|
||||
}
|
||||
|
||||
ExprBox { symbol: x } => {
|
||||
self.own_var(z);
|
||||
|
||||
// if the used symbol is an argument to the current function,
|
||||
// the function must take it as an owned parameter
|
||||
self.own_args_if_param(&[*x]);
|
||||
}
|
||||
|
||||
ExprUnbox { symbol: x } => {
|
||||
// if the boxed value is owned, the box is
|
||||
self.if_is_owned_then_own(*x, z);
|
||||
|
||||
// if the extracted value is owned, the structure must be too
|
||||
self.if_is_owned_then_own(z, *x);
|
||||
}
|
||||
|
||||
Reset { symbol: x, .. } => {
|
||||
self.own_var(z);
|
||||
self.own_var(*x);
|
||||
|
@ -1012,6 +1029,10 @@ pub fn lowlevel_borrow_signature(arena: &Bump, op: LowLevel) -> &[bool] {
|
|||
|
||||
ExpectTrue => arena.alloc_slice_copy(&[irrelevant]),
|
||||
|
||||
BoxExpr | UnboxExpr => {
|
||||
unreachable!("These lowlevel operations are turned into mono Expr's")
|
||||
}
|
||||
|
||||
PtrCast | RefCountInc | RefCountDec => {
|
||||
unreachable!("Only inserted *after* borrow checking: {:?}", op);
|
||||
}
|
||||
|
|
|
@ -34,6 +34,7 @@ pub fn eq_generic<'a>(
|
|||
Layout::Builtin(Builtin::List(elem_layout)) => eq_list(root, ident_ids, ctx, elem_layout),
|
||||
Layout::Struct { field_layouts, .. } => eq_struct(root, ident_ids, ctx, field_layouts),
|
||||
Layout::Union(union_layout) => eq_tag_union(root, ident_ids, ctx, union_layout),
|
||||
Layout::Boxed(inner_layout) => eq_boxed(root, ident_ids, ctx, inner_layout),
|
||||
Layout::LambdaSet(_) => unreachable!("`==` is not defined on functions"),
|
||||
Layout::RecursivePointer => {
|
||||
unreachable!(
|
||||
|
@ -528,6 +529,15 @@ fn eq_tag_fields<'a>(
|
|||
stmt
|
||||
}
|
||||
|
||||
fn eq_boxed<'a>(
|
||||
_root: &mut CodeGenHelp<'a>,
|
||||
_ident_ids: &mut IdentIds,
|
||||
_ctx: &mut Context<'a>,
|
||||
_inner_layout: &'a Layout<'a>,
|
||||
) -> Stmt<'a> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
/// List equality
|
||||
/// We can't use `ListGetUnsafe` because it increments the refcount, and we don't want that.
|
||||
/// Another way to dereference a heap pointer is to use `Expr::UnionAtIndex`.
|
||||
|
|
|
@ -378,7 +378,13 @@ impl<'a> CodeGenHelp<'a> {
|
|||
Layout::Union(UnionLayout::NonRecursive(new_tags.into_bump_slice()))
|
||||
}
|
||||
|
||||
Layout::Union(_) => layout,
|
||||
Layout::Union(_) => {
|
||||
// we always fully unroll recursive types. That means tha when we find a
|
||||
// recursive tag union we can replace it with the layout
|
||||
layout
|
||||
}
|
||||
|
||||
Layout::Boxed(inner) => self.replace_rec_ptr(ctx, *inner),
|
||||
|
||||
Layout::LambdaSet(lambda_set) => {
|
||||
self.replace_rec_ptr(ctx, lambda_set.runtime_representation())
|
||||
|
@ -476,5 +482,7 @@ fn layout_needs_helper_proc(layout: &Layout, op: HelperOp) -> bool {
|
|||
Layout::Union(_) => true,
|
||||
|
||||
Layout::LambdaSet(_) | Layout::RecursivePointer => false,
|
||||
|
||||
Layout::Boxed(_) => true,
|
||||
}
|
||||
}
|
||||
|
|
|
@ -122,6 +122,7 @@ pub fn refcount_generic<'a>(
|
|||
refcount_generic(root, ident_ids, ctx, runtime_layout, structure)
|
||||
}
|
||||
Layout::RecursivePointer => rc_todo(),
|
||||
Layout::Boxed(_) => rc_todo(),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -155,6 +156,7 @@ pub fn is_rc_implemented_yet(layout: &Layout) -> bool {
|
|||
is_rc_implemented_yet(&lambda_set.runtime_representation())
|
||||
}
|
||||
Layout::RecursivePointer => true,
|
||||
Layout::Boxed(_) => false,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -114,6 +114,10 @@ pub fn occurring_variables_expr(expr: &Expr<'_>, result: &mut MutSet<Symbol>) {
|
|||
result.insert(*x);
|
||||
}
|
||||
|
||||
ExprBox { symbol } | ExprUnbox { symbol } => {
|
||||
result.insert(*symbol);
|
||||
}
|
||||
|
||||
EmptyArray | RuntimeErrorFunction(_) | Literal(_) => {}
|
||||
|
||||
GetTagId {
|
||||
|
@ -756,6 +760,28 @@ impl<'a> Context<'a> {
|
|||
self.arena.alloc(Stmt::Let(z, v, l, b))
|
||||
}
|
||||
|
||||
ExprBox { symbol: x } => {
|
||||
// mimics Tag
|
||||
self.add_inc_before_consume_all(
|
||||
&[x],
|
||||
self.arena.alloc(Stmt::Let(z, v, l, b)),
|
||||
b_live_vars,
|
||||
)
|
||||
}
|
||||
|
||||
ExprUnbox { symbol: x } => {
|
||||
// mimics UnionAtIndex
|
||||
let b = self.add_dec_if_needed(x, b, b_live_vars);
|
||||
let info_x = self.get_var_info(x);
|
||||
let b = if info_x.consume {
|
||||
self.add_inc(z, 1, b)
|
||||
} else {
|
||||
b
|
||||
};
|
||||
|
||||
self.arena.alloc(Stmt::Let(z, v, l, b))
|
||||
}
|
||||
|
||||
EmptyArray | Literal(_) | Reset { .. } | RuntimeErrorFunction(_) => {
|
||||
// EmptyArray is always stack-allocated
|
||||
// function pointers are persistent
|
||||
|
|
|
@ -112,6 +112,11 @@ pub struct PartialProcs<'a> {
|
|||
/// maps a function name (symbol) to an index
|
||||
symbols: Vec<'a, Symbol>,
|
||||
|
||||
/// An entry (a, b) means `a` directly references the lambda value of `b`,
|
||||
/// i.e. this came from a `let a = b in ...` where `b` was defined as a
|
||||
/// lambda earlier.
|
||||
references: Vec<'a, (Symbol, Symbol)>,
|
||||
|
||||
partial_procs: Vec<'a, PartialProc<'a>>,
|
||||
}
|
||||
|
||||
|
@ -119,6 +124,7 @@ impl<'a> PartialProcs<'a> {
|
|||
fn new_in(arena: &'a Bump) -> Self {
|
||||
Self {
|
||||
symbols: Vec::new_in(arena),
|
||||
references: Vec::new_in(arena),
|
||||
partial_procs: Vec::new_in(arena),
|
||||
}
|
||||
}
|
||||
|
@ -126,7 +132,16 @@ impl<'a> PartialProcs<'a> {
|
|||
self.symbol_to_id(symbol).is_some()
|
||||
}
|
||||
|
||||
fn symbol_to_id(&self, symbol: Symbol) -> Option<PartialProcId> {
|
||||
fn symbol_to_id(&self, mut symbol: Symbol) -> Option<PartialProcId> {
|
||||
while let Some(real_symbol) = self
|
||||
.references
|
||||
.iter()
|
||||
.find(|(alias, _)| *alias == symbol)
|
||||
.map(|(_, real)| real)
|
||||
{
|
||||
symbol = *real_symbol;
|
||||
}
|
||||
|
||||
self.symbols
|
||||
.iter()
|
||||
.position(|s| *s == symbol)
|
||||
|
@ -157,6 +172,21 @@ impl<'a> PartialProcs<'a> {
|
|||
|
||||
id
|
||||
}
|
||||
|
||||
pub fn insert_alias(&mut self, alias: Symbol, real_symbol: Symbol) {
|
||||
debug_assert!(
|
||||
!self.contains_key(alias),
|
||||
"{:?} is inserted as a partial proc twice: that's a bug!",
|
||||
alias,
|
||||
);
|
||||
debug_assert!(
|
||||
self.contains_key(real_symbol),
|
||||
"{:?} is not a partial proc or another alias: that's a bug!",
|
||||
real_symbol,
|
||||
);
|
||||
|
||||
self.references.push((alias, real_symbol));
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
|
@ -1505,6 +1535,14 @@ pub enum Expr<'a> {
|
|||
},
|
||||
EmptyArray,
|
||||
|
||||
ExprBox {
|
||||
symbol: Symbol,
|
||||
},
|
||||
|
||||
ExprUnbox {
|
||||
symbol: Symbol,
|
||||
},
|
||||
|
||||
Reuse {
|
||||
symbol: Symbol,
|
||||
update_tag_id: bool,
|
||||
|
@ -1682,6 +1720,10 @@ impl<'a> Expr<'a> {
|
|||
.text("GetTagId ")
|
||||
.append(symbol_to_doc(alloc, *structure)),
|
||||
|
||||
ExprBox { symbol, .. } => alloc.text("Box ").append(symbol_to_doc(alloc, *symbol)),
|
||||
|
||||
ExprUnbox { symbol, .. } => alloc.text("Unbox ").append(symbol_to_doc(alloc, *symbol)),
|
||||
|
||||
UnionAtIndex {
|
||||
tag_id,
|
||||
structure,
|
||||
|
@ -4615,6 +4657,18 @@ pub fn with_hole<'a>(
|
|||
let xs = arg_symbols[0];
|
||||
match_on_closure_argument!(ListFindUnsafe, [xs])
|
||||
}
|
||||
BoxExpr => {
|
||||
debug_assert_eq!(arg_symbols.len(), 1);
|
||||
let x = arg_symbols[0];
|
||||
|
||||
Stmt::Let(assigned, Expr::ExprBox { symbol: x }, layout, hole)
|
||||
}
|
||||
UnboxExpr => {
|
||||
debug_assert_eq!(arg_symbols.len(), 1);
|
||||
let x = arg_symbols[0];
|
||||
|
||||
Stmt::Let(assigned, Expr::ExprUnbox { symbol: x }, layout, hole)
|
||||
}
|
||||
_ => {
|
||||
let call = self::Call {
|
||||
call_type: CallType::LowLevel {
|
||||
|
@ -6179,6 +6233,14 @@ fn substitute_in_expr<'a>(
|
|||
}
|
||||
}
|
||||
|
||||
ExprBox { symbol } => {
|
||||
substitute(subs, *symbol).map(|new_symbol| ExprBox { symbol: new_symbol })
|
||||
}
|
||||
|
||||
ExprUnbox { symbol } => {
|
||||
substitute(subs, *symbol).map(|new_symbol| ExprUnbox { symbol: new_symbol })
|
||||
}
|
||||
|
||||
StructAtIndex {
|
||||
index,
|
||||
structure,
|
||||
|
@ -6660,10 +6722,10 @@ where
|
|||
}
|
||||
|
||||
// Otherwise we're dealing with an alias to something that doesn't need to be specialized, or
|
||||
// whose usages will already be specialized in the rest of the program. Let's just build the
|
||||
// rest of the program now to get our hole.
|
||||
let mut result = build_rest(env, procs, layout_cache);
|
||||
// whose usages will already be specialized in the rest of the program.
|
||||
if procs.is_imported_module_thunk(right) {
|
||||
let result = build_rest(env, procs, layout_cache);
|
||||
|
||||
// if this is an imported symbol, then we must make sure it is
|
||||
// specialized, and wrap the original in a function pointer.
|
||||
add_needed_external(procs, env, variable, right);
|
||||
|
@ -6673,25 +6735,25 @@ where
|
|||
|
||||
force_thunk(env, right, layout, left, env.arena.alloc(result))
|
||||
} else if env.is_imported_symbol(right) {
|
||||
let result = build_rest(env, procs, layout_cache);
|
||||
|
||||
// if this is an imported symbol, then we must make sure it is
|
||||
// specialized, and wrap the original in a function pointer.
|
||||
add_needed_external(procs, env, variable, right);
|
||||
|
||||
// then we must construct its closure; since imported symbols have no closure, we use the empty struct
|
||||
let_empty_struct(left, env.arena.alloc(result))
|
||||
} else if procs.partial_procs.contains_key(right) {
|
||||
// This is an alias to a function defined in this module.
|
||||
// Attach the alias, then build the rest of the module, so that we reference and specialize
|
||||
// the correct proc.
|
||||
procs.partial_procs.insert_alias(left, right);
|
||||
build_rest(env, procs, layout_cache)
|
||||
} else {
|
||||
// This should be a fully specialized value. Replace the alias with the original symbol.
|
||||
let mut result = build_rest(env, procs, layout_cache);
|
||||
substitute_in_exprs(env.arena, &mut result, left, right);
|
||||
|
||||
// if the substituted variable is a function, make sure we specialize it
|
||||
reuse_function_symbol(
|
||||
env,
|
||||
procs,
|
||||
layout_cache,
|
||||
Some(variable),
|
||||
right,
|
||||
result,
|
||||
right,
|
||||
)
|
||||
result
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -7253,7 +7315,7 @@ fn call_by_name_help<'a>(
|
|||
// this is a case like `Str.concat`, an imported standard function, applied to zero arguments
|
||||
|
||||
// imported symbols cannot capture anything
|
||||
let captured= &[];
|
||||
let captured = &[];
|
||||
|
||||
construct_closure_data(
|
||||
env,
|
||||
|
@ -8434,7 +8496,7 @@ pub fn num_argument_to_int_or_float(
|
|||
debug_assert!(args.len() == 1);
|
||||
|
||||
// Recurse on the second argument
|
||||
let var = subs[args.variables().into_iter().next().unwrap()];
|
||||
let var = subs[args.all_variables().into_iter().next().unwrap()];
|
||||
num_argument_to_int_or_float(subs, target_info, var, false)
|
||||
}
|
||||
|
||||
|
@ -8452,7 +8514,7 @@ pub fn num_argument_to_int_or_float(
|
|||
debug_assert!(args.len() == 1);
|
||||
|
||||
// Recurse on the second argument
|
||||
let var = subs[args.variables().into_iter().next().unwrap()];
|
||||
let var = subs[args.all_variables().into_iter().next().unwrap()];
|
||||
num_argument_to_int_or_float(subs, target_info, var, true)
|
||||
}
|
||||
|
||||
|
|
|
@ -250,6 +250,7 @@ pub enum Layout<'a> {
|
|||
field_order_hash: FieldOrderHash,
|
||||
field_layouts: &'a [Layout<'a>],
|
||||
},
|
||||
Boxed(&'a Layout<'a>),
|
||||
Union(UnionLayout<'a>),
|
||||
LambdaSet(LambdaSet<'a>),
|
||||
RecursivePointer,
|
||||
|
@ -997,7 +998,7 @@ impl<'a> Layout<'a> {
|
|||
}
|
||||
}
|
||||
LambdaSet(lambda_set) => lambda_set.runtime_representation().safe_to_memcpy(),
|
||||
RecursivePointer => {
|
||||
Boxed(_) | RecursivePointer => {
|
||||
// We cannot memcpy pointers, because then we would have the same pointer in multiple places!
|
||||
false
|
||||
}
|
||||
|
@ -1066,6 +1067,7 @@ impl<'a> Layout<'a> {
|
|||
.runtime_representation()
|
||||
.stack_size_without_alignment(target_info),
|
||||
RecursivePointer => target_info.ptr_width() as u32,
|
||||
Boxed(_) => target_info.ptr_width() as u32,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1114,6 +1116,7 @@ impl<'a> Layout<'a> {
|
|||
.alignment_bytes(target_info),
|
||||
Layout::Builtin(builtin) => builtin.alignment_bytes(target_info),
|
||||
Layout::RecursivePointer => target_info.ptr_width() as u32,
|
||||
Layout::Boxed(_) => target_info.ptr_width() as u32,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1126,6 +1129,7 @@ impl<'a> Layout<'a> {
|
|||
.runtime_representation()
|
||||
.allocation_alignment_bytes(target_info),
|
||||
Layout::RecursivePointer => unreachable!("should be looked up to get an actual layout"),
|
||||
Layout::Boxed(inner) => inner.allocation_alignment_bytes(target_info),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1172,6 +1176,7 @@ impl<'a> Layout<'a> {
|
|||
}
|
||||
LambdaSet(lambda_set) => lambda_set.runtime_representation().contains_refcounted(),
|
||||
RecursivePointer => true,
|
||||
Boxed(_) => true,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1196,6 +1201,10 @@ impl<'a> Layout<'a> {
|
|||
Union(union_layout) => union_layout.to_doc(alloc, parens),
|
||||
LambdaSet(lambda_set) => lambda_set.runtime_representation().to_doc(alloc, parens),
|
||||
RecursivePointer => alloc.text("*self"),
|
||||
Boxed(inner) => alloc
|
||||
.text("Boxed(")
|
||||
.append(inner.to_doc(alloc, parens))
|
||||
.append(")"),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1617,6 +1626,15 @@ fn layout_from_flat_type<'a>(
|
|||
Symbol::LIST_LIST => list_layout_from_elem(env, args[0]),
|
||||
Symbol::DICT_DICT => dict_layout_from_key_value(env, args[0], args[1]),
|
||||
Symbol::SET_SET => dict_layout_from_key_value(env, args[0], Variable::EMPTY_RECORD),
|
||||
Symbol::BOX_BOX_TYPE => {
|
||||
// Num.Num should only ever have 1 argument, e.g. Num.Num Int.Integer
|
||||
debug_assert_eq!(args.len(), 1);
|
||||
|
||||
let inner_var = args[0];
|
||||
let inner_layout = Layout::from_var(env, inner_var)?;
|
||||
|
||||
Ok(Layout::Boxed(env.arena.alloc(inner_layout)))
|
||||
}
|
||||
_ => {
|
||||
panic!(
|
||||
"TODO layout_from_flat_type for Apply({:?}, {:?})",
|
||||
|
@ -1976,7 +1994,15 @@ pub fn union_sorted_tags<'a>(
|
|||
|
||||
let mut tags_vec = std::vec::Vec::new();
|
||||
let result = match roc_types::pretty_print::chase_ext_tag_union(subs, var, &mut tags_vec) {
|
||||
Ok(()) | Err((_, Content::FlexVar(_))) | Err((_, Content::RecursionVar { .. })) => {
|
||||
Ok(())
|
||||
// Admit type variables in the extension for now. This may come from things that never got
|
||||
// monomorphized, like in
|
||||
// x : [ A ]*
|
||||
// x = A
|
||||
// x
|
||||
// In such cases it's fine to drop the variable. We may be proven wrong in the future...
|
||||
| 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)
|
||||
}
|
||||
|
@ -2574,7 +2600,7 @@ pub fn ext_var_is_empty_tag_union(subs: &Subs, ext_var: Variable) -> bool {
|
|||
// the ext_var is empty
|
||||
let mut ext_fields = std::vec::Vec::new();
|
||||
match roc_types::pretty_print::chase_ext_tag_union(subs, ext_var, &mut ext_fields) {
|
||||
Ok(()) | Err((_, Content::FlexVar(_))) => ext_fields.is_empty(),
|
||||
Ok(()) | Err((_, Content::FlexVar(_) | Content::RigidVar(_))) => ext_fields.is_empty(),
|
||||
Err(content) => panic!("invalid content in ext_var: {:?}", content),
|
||||
}
|
||||
}
|
||||
|
@ -2652,7 +2678,7 @@ fn unwrap_num_tag<'a>(
|
|||
Content::Alias(Symbol::NUM_INTEGER, args, _, _) => {
|
||||
debug_assert!(args.len() == 1);
|
||||
|
||||
let precision_var = subs[args.variables().into_iter().next().unwrap()];
|
||||
let precision_var = subs[args.all_variables().into_iter().next().unwrap()];
|
||||
|
||||
let precision = subs.get_content_without_compacting(precision_var);
|
||||
|
||||
|
@ -2688,7 +2714,7 @@ fn unwrap_num_tag<'a>(
|
|||
Content::Alias(Symbol::NUM_FLOATINGPOINT, args, _, _) => {
|
||||
debug_assert!(args.len() == 1);
|
||||
|
||||
let precision_var = subs[args.variables().into_iter().next().unwrap()];
|
||||
let precision_var = subs[args.all_variables().into_iter().next().unwrap()];
|
||||
|
||||
let precision = subs.get_content_without_compacting(precision_var);
|
||||
|
||||
|
|
|
@ -239,6 +239,12 @@ fn insert_reset<'a>(
|
|||
stack.push((symbol, expr, expr_layout));
|
||||
stmt = rest;
|
||||
}
|
||||
|
||||
ExprBox { .. } | ExprUnbox { .. } => {
|
||||
// TODO
|
||||
break;
|
||||
}
|
||||
|
||||
Literal(_)
|
||||
| Call(_)
|
||||
| Tag { .. }
|
||||
|
@ -620,6 +626,8 @@ fn has_live_var_expr<'a>(expr: &'a Expr<'a>, needle: Symbol) -> bool {
|
|||
symbol, arguments, ..
|
||||
} => needle == *symbol || arguments.iter().any(|s| *s == needle),
|
||||
Expr::Reset { symbol, .. } => needle == *symbol,
|
||||
Expr::ExprBox { symbol, .. } => needle == *symbol,
|
||||
Expr::ExprUnbox { symbol, .. } => needle == *symbol,
|
||||
Expr::RuntimeErrorFunction(_) => false,
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue