mirror of
https://github.com/roc-lang/roc.git
synced 2025-08-03 11:52:19 +00:00
Merge pull request #3541 from rtfeldman/rocasync
Changes to get roc-async working
This commit is contained in:
commit
ca38ec4eb5
6 changed files with 158 additions and 164 deletions
|
@ -194,7 +194,7 @@ where
|
|||
RawFunctionLayout::ZeroArgumentThunk(_) => {
|
||||
let bytes = func_name_bytes_help(
|
||||
*symbol,
|
||||
[Layout::UNIT],
|
||||
[],
|
||||
CapturesNiche::no_niche(),
|
||||
&top_level.result,
|
||||
);
|
||||
|
|
|
@ -4917,8 +4917,22 @@ pub fn build_proc<'a, 'ctx, 'env>(
|
|||
)
|
||||
}
|
||||
|
||||
RawFunctionLayout::ZeroArgumentThunk(_) => {
|
||||
// do nothing
|
||||
RawFunctionLayout::ZeroArgumentThunk(result) => {
|
||||
// Define only the return value size, since this is a thunk
|
||||
//
|
||||
// * roc__mainForHost_1_Update_result_size() -> i64
|
||||
let ident_string = proc.name.name().as_str(&env.interns);
|
||||
let fn_name: String = format!("{}_1", ident_string);
|
||||
|
||||
let result_type = basic_type_from_layout(env, &result);
|
||||
|
||||
build_host_exposed_alias_size_help(
|
||||
env,
|
||||
&fn_name,
|
||||
name,
|
||||
Some("result"),
|
||||
result_type,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1086,8 +1086,6 @@ impl<'a> Procs<'a> {
|
|||
// (We had a bug around this before this system existed!)
|
||||
self.specialized.mark_in_progress(name.name(), layout);
|
||||
|
||||
let outside_layout = layout;
|
||||
|
||||
let partial_proc_id = if let Some(partial_proc_id) =
|
||||
self.partial_procs.symbol_to_id(name.name())
|
||||
{
|
||||
|
@ -1126,26 +1124,29 @@ impl<'a> Procs<'a> {
|
|||
&[],
|
||||
partial_proc_id,
|
||||
) {
|
||||
Ok((proc, layout)) => {
|
||||
let top_level = ProcLayout::from_raw(
|
||||
Ok((proc, _ignore_layout)) => {
|
||||
// the `layout` is a function pointer, while `_ignore_layout` can be a
|
||||
// closure. We only specialize functions, storing this value with a closure
|
||||
// layout will give trouble.
|
||||
let arguments = Vec::from_iter_in(
|
||||
proc.args.iter().map(|(l, _)| *l),
|
||||
env.arena,
|
||||
layout,
|
||||
proc.name.captures_niche(),
|
||||
);
|
||||
)
|
||||
.into_bump_slice();
|
||||
|
||||
debug_assert_eq!(
|
||||
outside_layout, top_level,
|
||||
"different raw layouts for {:?}",
|
||||
proc.name
|
||||
);
|
||||
|
||||
if self.is_module_thunk(proc.name.name()) {
|
||||
debug_assert!(top_level.arguments.is_empty());
|
||||
}
|
||||
let proper_layout = ProcLayout {
|
||||
arguments,
|
||||
result: proc.ret_layout,
|
||||
captures_niche: proc.name.captures_niche(),
|
||||
};
|
||||
|
||||
// NOTE: some functions are specialized to have a closure, but don't actually
|
||||
// need any closure argument. Here is where we correct this sort of thing,
|
||||
// by trusting the layout of the Proc, not of what we specialize for
|
||||
self.specialized.remove_specialized(name.name(), &layout);
|
||||
self.specialized.insert_specialized(
|
||||
name.name(),
|
||||
top_level,
|
||||
proper_layout,
|
||||
proc,
|
||||
);
|
||||
}
|
||||
|
@ -1240,7 +1241,7 @@ impl<'a> Procs<'a> {
|
|||
captures_niche: proc.name.captures_niche(),
|
||||
};
|
||||
|
||||
// NOTE: some function are specialized to have a closure, but don't actually
|
||||
// NOTE: some functions are specialized to have a closure, but don't actually
|
||||
// need any closure argument. Here is where we correct this sort of thing,
|
||||
// by trusting the layout of the Proc, not of what we specialize for
|
||||
self.specialized.remove_specialized(symbol.name(), &layout);
|
||||
|
@ -2660,23 +2661,38 @@ fn specialize_suspended<'a>(
|
|||
};
|
||||
|
||||
match specialize_variable(env, procs, name, layout_cache, var, &[], partial_proc) {
|
||||
Ok((proc, layout)) => {
|
||||
// TODO thiscode is duplicated elsewhere
|
||||
let top_level = ProcLayout::from_raw(env.arena, layout, proc.name.captures_niche());
|
||||
Ok((proc, _layout)) => {
|
||||
// TODO this code is duplicated elsewhere
|
||||
|
||||
// the `layout` is a function pointer, while `_ignore_layout` can be a
|
||||
// closure. We only specialize functions, storing this value with a closure
|
||||
// layout will give trouble.
|
||||
let arguments = Vec::from_iter_in(proc.args.iter().map(|(l, _)| *l), env.arena)
|
||||
.into_bump_slice();
|
||||
|
||||
let proper_layout = ProcLayout {
|
||||
arguments,
|
||||
result: proc.ret_layout,
|
||||
captures_niche: proc.name.captures_niche(),
|
||||
};
|
||||
if procs.is_module_thunk(proc.name.name()) {
|
||||
debug_assert!(
|
||||
top_level.arguments.is_empty(),
|
||||
proper_layout.arguments.is_empty(),
|
||||
"{:?} from {:?}",
|
||||
name,
|
||||
layout
|
||||
proper_layout
|
||||
);
|
||||
}
|
||||
|
||||
debug_assert_eq!(outside_layout, top_level, " in {:?}", name);
|
||||
// NOTE: some functions are specialized to have a closure, but don't actually
|
||||
// need any closure argument. Here is where we correct this sort of thing,
|
||||
// by trusting the layout of the Proc, not of what we specialize for
|
||||
procs
|
||||
.specialized
|
||||
.insert_specialized(name.name(), top_level, proc);
|
||||
.remove_specialized(name.name(), &outside_layout);
|
||||
procs
|
||||
.specialized
|
||||
.insert_specialized(name.name(), proper_layout, proc);
|
||||
}
|
||||
Err(SpecializeFailure {
|
||||
attempted_layout, ..
|
||||
|
@ -3005,8 +3021,35 @@ fn specialize_external<'a>(
|
|||
|
||||
aliases.insert(*symbol, (name, top_level, layout));
|
||||
}
|
||||
RawFunctionLayout::ZeroArgumentThunk(_) => {
|
||||
unreachable!("so far");
|
||||
RawFunctionLayout::ZeroArgumentThunk(result) => {
|
||||
let assigned = env.unique_symbol();
|
||||
let hole = env.arena.alloc(Stmt::Ret(assigned));
|
||||
let forced = force_thunk(env, lambda_name.name(), result, assigned, hole);
|
||||
|
||||
let proc = Proc {
|
||||
name: LambdaName::no_niche(name),
|
||||
args: &[],
|
||||
body: forced,
|
||||
closure_data_layout: None,
|
||||
ret_layout: result,
|
||||
is_self_recursive: SelfRecursive::NotSelfRecursive,
|
||||
must_own_arguments: false,
|
||||
host_exposed_layouts: HostExposedLayouts::NotHostExposed,
|
||||
};
|
||||
|
||||
let top_level =
|
||||
ProcLayout::from_raw(env.arena, layout, CapturesNiche::no_niche());
|
||||
|
||||
procs.specialized.insert_specialized(name, top_level, proc);
|
||||
|
||||
aliases.insert(
|
||||
*symbol,
|
||||
(
|
||||
name,
|
||||
ProcLayout::new(env.arena, &[], CapturesNiche::no_niche(), result),
|
||||
layout,
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2984,6 +2984,15 @@ fn type_to_variable<'a>(
|
|||
let copy_var = helper!(arg_type);
|
||||
subs.variables[target_index] = copy_var;
|
||||
}
|
||||
let it = (new_variables.indices().skip(type_arguments.len()))
|
||||
.zip(lambda_set_variables);
|
||||
for (target_index, ls) in it {
|
||||
// We MUST do this now, otherwise when linking the ambient function during
|
||||
// instantiation of the real var, there will be nothing to link against.
|
||||
let copy_var =
|
||||
type_to_variable(subs, rank, pools, arena, aliases, &ls.0, true);
|
||||
subs.variables[target_index] = copy_var;
|
||||
}
|
||||
|
||||
AliasVariables {
|
||||
variables_start: new_variables.start,
|
||||
|
|
|
@ -1905,18 +1905,9 @@ impl Subs {
|
|||
/// reference chain r -> t1 -> t2 -> r, [occurs] will return `Err(r, [t2, t1, r])`.
|
||||
///
|
||||
/// This ignores [Content::RecursionVar]s that occur recursively, because those are
|
||||
/// already priced in and expected to occur. Use [Subs::occurs_including_recursion_vars] if you
|
||||
/// need to check for recursion var occurrence.
|
||||
/// already priced in and expected to occur.
|
||||
pub fn occurs(&self, var: Variable) -> Result<(), (Variable, Vec<Variable>)> {
|
||||
occurs(self, &[], var, false)
|
||||
}
|
||||
|
||||
/// Like [Subs::occurs], but also errors when recursion vars occur.
|
||||
pub fn occurs_including_recursion_vars(
|
||||
&self,
|
||||
var: Variable,
|
||||
) -> Result<(), (Variable, Vec<Variable>)> {
|
||||
occurs(self, &[], var, true)
|
||||
occurs(self, &[], var)
|
||||
}
|
||||
|
||||
pub fn mark_tag_union_recursive(
|
||||
|
@ -3072,7 +3063,6 @@ fn occurs(
|
|||
subs: &Subs,
|
||||
seen: &[Variable],
|
||||
input_var: Variable,
|
||||
include_recursion_var: bool,
|
||||
) -> Result<(), (Variable, Vec<Variable>)> {
|
||||
use self::Content::*;
|
||||
use self::FlatType::*;
|
||||
|
@ -3096,53 +3086,34 @@ fn occurs(
|
|||
new_seen.push(root_var);
|
||||
|
||||
match flat_type {
|
||||
Apply(_, args) => short_circuit(
|
||||
subs,
|
||||
root_var,
|
||||
&new_seen,
|
||||
subs.get_subs_slice(*args).iter(),
|
||||
include_recursion_var,
|
||||
),
|
||||
Apply(_, args) => {
|
||||
short_circuit(subs, root_var, &new_seen, subs.get_subs_slice(*args).iter())
|
||||
}
|
||||
Func(arg_vars, closure_var, ret_var) => {
|
||||
let it = once(ret_var)
|
||||
.chain(once(closure_var))
|
||||
.chain(subs.get_subs_slice(*arg_vars).iter());
|
||||
short_circuit(subs, root_var, &new_seen, it, include_recursion_var)
|
||||
short_circuit(subs, root_var, &new_seen, it)
|
||||
}
|
||||
Record(vars_by_field, ext_var) => {
|
||||
let slice =
|
||||
SubsSlice::new(vars_by_field.variables_start, vars_by_field.length);
|
||||
let it = once(ext_var).chain(subs.get_subs_slice(slice).iter());
|
||||
short_circuit(subs, root_var, &new_seen, it, include_recursion_var)
|
||||
short_circuit(subs, root_var, &new_seen, it)
|
||||
}
|
||||
TagUnion(tags, ext_var) => {
|
||||
occurs_union(subs, root_var, &new_seen, include_recursion_var, tags)?;
|
||||
occurs_union(subs, root_var, &new_seen, tags)?;
|
||||
|
||||
short_circuit_help(
|
||||
subs,
|
||||
root_var,
|
||||
&new_seen,
|
||||
*ext_var,
|
||||
include_recursion_var,
|
||||
)
|
||||
short_circuit_help(subs, root_var, &new_seen, *ext_var)
|
||||
}
|
||||
FunctionOrTagUnion(_, _, ext_var) => {
|
||||
let it = once(ext_var);
|
||||
short_circuit(subs, root_var, &new_seen, it, include_recursion_var)
|
||||
short_circuit(subs, root_var, &new_seen, it)
|
||||
}
|
||||
RecursiveTagUnion(rec_var, tags, ext_var) => {
|
||||
if include_recursion_var {
|
||||
new_seen.push(subs.get_root_key_without_compacting(*rec_var));
|
||||
}
|
||||
occurs_union(subs, root_var, &new_seen, include_recursion_var, tags)?;
|
||||
RecursiveTagUnion(_, tags, ext_var) => {
|
||||
occurs_union(subs, root_var, &new_seen, tags)?;
|
||||
|
||||
short_circuit_help(
|
||||
subs,
|
||||
root_var,
|
||||
&new_seen,
|
||||
*ext_var,
|
||||
include_recursion_var,
|
||||
)
|
||||
short_circuit_help(subs, root_var, &new_seen, *ext_var)
|
||||
}
|
||||
EmptyRecord | EmptyTagUnion | Erroneous(_) => Ok(()),
|
||||
}
|
||||
|
@ -3153,30 +3124,24 @@ fn occurs(
|
|||
|
||||
for var_index in args.into_iter() {
|
||||
let var = subs[var_index];
|
||||
short_circuit_help(subs, root_var, &new_seen, var, include_recursion_var)?;
|
||||
short_circuit_help(subs, root_var, &new_seen, var)?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
LambdaSet(self::LambdaSet {
|
||||
solved,
|
||||
recursion_var,
|
||||
recursion_var: _,
|
||||
unspecialized: _,
|
||||
ambient_function: _,
|
||||
}) => {
|
||||
let mut new_seen = seen.to_owned();
|
||||
new_seen.push(root_var);
|
||||
|
||||
if include_recursion_var {
|
||||
if let Some(v) = recursion_var.into_variable() {
|
||||
new_seen.push(subs.get_root_key_without_compacting(v));
|
||||
}
|
||||
}
|
||||
|
||||
// unspecialized lambda vars excluded because they are not explicitly part of the
|
||||
// type (they only matter after being resolved).
|
||||
|
||||
occurs_union(subs, root_var, &new_seen, include_recursion_var, solved)
|
||||
occurs_union(subs, root_var, &new_seen, solved)
|
||||
}
|
||||
RangedNumber(_range_vars) => Ok(()),
|
||||
}
|
||||
|
@ -3188,14 +3153,13 @@ fn occurs_union<L: Label>(
|
|||
subs: &Subs,
|
||||
root_var: Variable,
|
||||
seen: &[Variable],
|
||||
include_recursion_var: bool,
|
||||
tags: &UnionLabels<L>,
|
||||
) -> Result<(), (Variable, Vec<Variable>)> {
|
||||
for slice_index in tags.variables() {
|
||||
let slice = subs[slice_index];
|
||||
for var_index in slice {
|
||||
let var = subs[var_index];
|
||||
short_circuit_help(subs, root_var, seen, var, include_recursion_var)?;
|
||||
short_circuit_help(subs, root_var, seen, var)?;
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
|
@ -3207,13 +3171,12 @@ fn short_circuit<'a, T>(
|
|||
root_key: Variable,
|
||||
seen: &[Variable],
|
||||
iter: T,
|
||||
include_recursion_var: bool,
|
||||
) -> Result<(), (Variable, Vec<Variable>)>
|
||||
where
|
||||
T: Iterator<Item = &'a Variable>,
|
||||
{
|
||||
for var in iter {
|
||||
short_circuit_help(subs, root_key, seen, *var, include_recursion_var)?;
|
||||
short_circuit_help(subs, root_key, seen, *var)?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
|
@ -3225,9 +3188,8 @@ fn short_circuit_help(
|
|||
root_key: Variable,
|
||||
seen: &[Variable],
|
||||
var: Variable,
|
||||
include_recursion_var: bool,
|
||||
) -> Result<(), (Variable, Vec<Variable>)> {
|
||||
if let Err((v, mut vec)) = occurs(subs, seen, var, include_recursion_var) {
|
||||
if let Err((v, mut vec)) = occurs(subs, seen, var) {
|
||||
vec.push(root_key);
|
||||
return Err((v, vec));
|
||||
}
|
||||
|
|
|
@ -890,15 +890,7 @@ fn unify_structure<M: MetaCollector>(
|
|||
RecursionVar { structure, .. } => match flat_type {
|
||||
FlatType::TagUnion(_, _) => {
|
||||
// unify the structure with this unrecursive tag union
|
||||
let mut outcome = unify_pool(subs, pool, ctx.first, *structure, ctx.mode);
|
||||
|
||||
if outcome.mismatches.is_empty() {
|
||||
outcome.union(fix_tag_union_recursion_variable(
|
||||
subs, ctx, ctx.first, other,
|
||||
));
|
||||
}
|
||||
|
||||
outcome
|
||||
unify_pool(subs, pool, ctx.first, *structure, ctx.mode)
|
||||
}
|
||||
FlatType::RecursiveTagUnion(rec, _, _) => {
|
||||
debug_assert!(is_recursion_var(subs, *rec));
|
||||
|
@ -907,15 +899,7 @@ fn unify_structure<M: MetaCollector>(
|
|||
}
|
||||
FlatType::FunctionOrTagUnion(_, _, _) => {
|
||||
// unify the structure with this unrecursive tag union
|
||||
let mut outcome = unify_pool(subs, pool, ctx.first, *structure, ctx.mode);
|
||||
|
||||
if outcome.mismatches.is_empty() {
|
||||
outcome.union(fix_tag_union_recursion_variable(
|
||||
subs, ctx, ctx.first, other,
|
||||
));
|
||||
}
|
||||
|
||||
outcome
|
||||
unify_pool(subs, pool, ctx.first, *structure, ctx.mode)
|
||||
}
|
||||
// Only tag unions can be recursive; everything else is an error.
|
||||
_ => mismatch!(
|
||||
|
@ -1376,57 +1360,6 @@ fn unify_lambda_set_help<M: MetaCollector>(
|
|||
whole_outcome
|
||||
}
|
||||
|
||||
/// Ensures that a non-recursive tag union, when unified with a recursion var to become a recursive
|
||||
/// tag union, properly contains a recursion variable that recurses on itself.
|
||||
//
|
||||
// When might this not be the case? For example, in the code
|
||||
//
|
||||
// Indirect : [Indirect ConsList]
|
||||
//
|
||||
// ConsList : [Nil, Cons Indirect]
|
||||
//
|
||||
// l : ConsList
|
||||
// l = Cons (Indirect (Cons (Indirect Nil)))
|
||||
// # ^^^^^^^^^^^^^^^~~~~~~~~~~~~~~~~~~~~~^ region-a
|
||||
// # ~~~~~~~~~~~~~~~~~~~~~ region-b
|
||||
// l
|
||||
//
|
||||
// Suppose `ConsList` has the expanded type `[Nil, Cons [Indirect <rec>]] as <rec>`.
|
||||
// After unifying the tag application annotated "region-b" with the recursion variable `<rec>`,
|
||||
// the tentative total-type of the application annotated "region-a" would be
|
||||
// `<v> = [Nil, Cons [Indirect <v>]] as <rec>`. That is, the type of the recursive tag union
|
||||
// would be inlined at the site "v", rather than passing through the correct recursion variable
|
||||
// "rec" first.
|
||||
//
|
||||
// This is not incorrect from a type perspective, but causes problems later on for e.g. layout
|
||||
// determination, which expects recursion variables to be placed correctly. Attempting to detect
|
||||
// this during layout generation does not work so well because it may be that there *are* recursive
|
||||
// tag unions that should be inlined, and not pass through recursion variables. So instead, try to
|
||||
// resolve these cases here.
|
||||
//
|
||||
// See tests labeled "issue_2810" for more examples.
|
||||
fn fix_tag_union_recursion_variable<M: MetaCollector>(
|
||||
subs: &mut Subs,
|
||||
ctx: &Context,
|
||||
tag_union_promoted_to_recursive: Variable,
|
||||
recursion_var: &Content,
|
||||
) -> Outcome<M> {
|
||||
debug_assert!(matches!(
|
||||
subs.get_content_without_compacting(tag_union_promoted_to_recursive),
|
||||
Structure(FlatType::RecursiveTagUnion(..))
|
||||
));
|
||||
|
||||
let has_recursing_recursive_variable = subs
|
||||
.occurs_including_recursion_vars(tag_union_promoted_to_recursive)
|
||||
.is_err();
|
||||
|
||||
if !has_recursing_recursive_variable {
|
||||
merge(subs, ctx, *recursion_var)
|
||||
} else {
|
||||
Outcome::default()
|
||||
}
|
||||
}
|
||||
|
||||
fn unify_record<M: MetaCollector>(
|
||||
subs: &mut Subs,
|
||||
pool: &mut Pool,
|
||||
|
@ -2097,14 +2030,47 @@ fn unify_shared_tags_new<M: MetaCollector>(
|
|||
|
||||
outcome.union(unify_pool(subs, pool, actual, expected, ctx.mode));
|
||||
|
||||
// clearly, this is very suspicious: these variables have just been unified. And yet,
|
||||
// not doing this leads to stack overflows
|
||||
if let Rec::Right(_) = recursion_var {
|
||||
if outcome.mismatches.is_empty() {
|
||||
matching_vars.push(expected);
|
||||
}
|
||||
} else if outcome.mismatches.is_empty() {
|
||||
matching_vars.push(actual);
|
||||
if outcome.mismatches.is_empty() {
|
||||
// If one of the variables is a recursion var, keep that one, so that we avoid inlining
|
||||
// a recursive tag union type content where we should have a recursion var instead.
|
||||
//
|
||||
// When might this happen? For example, in the code
|
||||
//
|
||||
// Indirect : [Indirect ConsList]
|
||||
//
|
||||
// ConsList : [Nil, Cons Indirect]
|
||||
//
|
||||
// l : ConsList
|
||||
// l = Cons (Indirect (Cons (Indirect Nil)))
|
||||
// # ^^^^^^^^^^^^^^^~~~~~~~~~~~~~~~~~~~~~^ region-a
|
||||
// # ~~~~~~~~~~~~~~~~~~~~~ region-b
|
||||
// l
|
||||
//
|
||||
// Suppose `ConsList` has the expanded type `[Nil, Cons [Indirect <rec>]] as <rec>`.
|
||||
// After unifying the tag application annotated "region-b" with the recursion variable `<rec>`,
|
||||
// we might have that e.g. `actual` is `<rec>` and `expected` is `[Cons (Indirect ...)]`.
|
||||
//
|
||||
// Now, we need to be careful to set the type we choose to represent the merged type
|
||||
// here to be `<rec>`, not the tag union content of `expected`! Otherwise, we will
|
||||
// have lost a recursion variable in the recursive tag union.
|
||||
//
|
||||
// This would not be incorrect from a type perspective, but causes problems later on for e.g.
|
||||
// layout generation, which expects recursion variables to be placed correctly. Attempting to detect
|
||||
// this during layout generation does not work so well because it may be that there *are* recursive
|
||||
// tag unions that should be inlined, and not pass through recursion variables. So instead, resolve
|
||||
// these cases here.
|
||||
//
|
||||
// See tests labeled "issue_2810" for more examples.
|
||||
let merged_var = match (
|
||||
(actual, subs.get_content_unchecked(actual)),
|
||||
(expected, subs.get_content_unchecked(expected)),
|
||||
) {
|
||||
((var, Content::RecursionVar { .. }), _)
|
||||
| (_, (var, Content::RecursionVar { .. })) => var,
|
||||
_ => actual,
|
||||
};
|
||||
|
||||
matching_vars.push(merged_var);
|
||||
}
|
||||
|
||||
total_outcome.union(outcome);
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue