Merge pull request #3541 from rtfeldman/rocasync

Changes to get roc-async working
This commit is contained in:
Folkert de Vries 2022-07-18 19:22:07 +02:00 committed by GitHub
commit ca38ec4eb5
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
6 changed files with 158 additions and 164 deletions

View file

@ -194,7 +194,7 @@ where
RawFunctionLayout::ZeroArgumentThunk(_) => {
let bytes = func_name_bytes_help(
*symbol,
[Layout::UNIT],
[],
CapturesNiche::no_niche(),
&top_level.result,
);

View file

@ -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,
);
}
}
}

View file

@ -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,
),
);
}
}
}

View file

@ -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,

View file

@ -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));
}

View file

@ -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);