mirror of
https://github.com/roc-lang/roc.git
synced 2025-09-28 14:24:45 +00:00
bitvec all the things
This commit is contained in:
parent
b0ceaf0372
commit
452447232f
3 changed files with 88 additions and 67 deletions
|
@ -467,6 +467,7 @@ pub(crate) fn canonicalize_defs<'a>(
|
||||||
// Now that we know the alias dependency graph, we can try to insert recursion variables
|
// Now that we know the alias dependency graph, we can try to insert recursion variables
|
||||||
// where aliases are recursive tag unions, or detect illegal recursions.
|
// where aliases are recursive tag unions, or detect illegal recursions.
|
||||||
let mut aliases = correct_mutual_recursive_type_alias(env, aliases, var_store);
|
let mut aliases = correct_mutual_recursive_type_alias(env, aliases, var_store);
|
||||||
|
|
||||||
for (symbol, alias) in aliases.iter() {
|
for (symbol, alias) in aliases.iter() {
|
||||||
scope.add_alias(
|
scope.add_alias(
|
||||||
*symbol,
|
*symbol,
|
||||||
|
@ -1908,19 +1909,10 @@ fn correct_mutual_recursive_type_alias<'a>(
|
||||||
let capacity = original_aliases.len();
|
let capacity = original_aliases.len();
|
||||||
let mut matrix = ReferenceMatrix::new(capacity);
|
let mut matrix = ReferenceMatrix::new(capacity);
|
||||||
|
|
||||||
let (symbols_introduced, mut aliases): (Vec<_>, Vec<_>) = original_aliases
|
let (symbols_introduced, mut aliases): (Vec<_>, Vec<_>) = original_aliases.into_iter().unzip();
|
||||||
.into_iter()
|
|
||||||
.map(|(k, v)| (k, Some(v)))
|
|
||||||
.unzip();
|
|
||||||
|
|
||||||
for (index, alias) in aliases.iter().enumerate() {
|
for (index, alias) in aliases.iter().enumerate() {
|
||||||
let it = alias
|
for referenced in alias.typ.symbols() {
|
||||||
.as_ref()
|
|
||||||
.map(|a| a.typ.symbols().into_iter())
|
|
||||||
.into_iter()
|
|
||||||
.flatten();
|
|
||||||
|
|
||||||
for referenced in it {
|
|
||||||
match symbols_introduced.iter().position(|k| referenced == *k) {
|
match symbols_introduced.iter().position(|k| referenced == *k) {
|
||||||
None => { /* ignore */ }
|
None => { /* ignore */ }
|
||||||
Some(ref_id) => matrix.set_row_col(index, ref_id, true),
|
Some(ref_id) => matrix.set_row_col(index, ref_id, true),
|
||||||
|
@ -1928,7 +1920,8 @@ fn correct_mutual_recursive_type_alias<'a>(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut solved_aliases = ImMap::default();
|
let mut solved_aliases_bitvec = bitvec::vec::BitVec::<usize>::repeat(false, capacity);
|
||||||
|
let mut pending_aliases_bitvec = bitvec::vec::BitVec::repeat(false, capacity);
|
||||||
|
|
||||||
let group: Vec<_> = (0u32..capacity as u32).collect();
|
let group: Vec<_> = (0u32..capacity as u32).collect();
|
||||||
|
|
||||||
|
@ -1937,15 +1930,12 @@ fn correct_mutual_recursive_type_alias<'a>(
|
||||||
for cycle in cycles {
|
for cycle in cycles {
|
||||||
debug_assert!(!cycle.is_empty());
|
debug_assert!(!cycle.is_empty());
|
||||||
|
|
||||||
let mut pending_aliases: ImMap<_, _> = cycle
|
// zero out all the bits
|
||||||
.iter()
|
pending_aliases_bitvec.set_elements(0);
|
||||||
.map(|index| {
|
|
||||||
(
|
for index in cycle.iter() {
|
||||||
symbols_introduced[*index as usize],
|
pending_aliases_bitvec.set(*index as usize, true);
|
||||||
aliases[*index as usize].take().unwrap(),
|
}
|
||||||
)
|
|
||||||
})
|
|
||||||
.collect();
|
|
||||||
|
|
||||||
// Make sure we report only one error for the cycle, not an error for every
|
// Make sure we report only one error for the cycle, not an error for every
|
||||||
// alias in the cycle.
|
// alias in the cycle.
|
||||||
|
@ -1955,23 +1945,43 @@ fn correct_mutual_recursive_type_alias<'a>(
|
||||||
// depends on.
|
// depends on.
|
||||||
// We only need to worry about symbols in this SCC or any prior one, since the SCCs
|
// We only need to worry about symbols in this SCC or any prior one, since the SCCs
|
||||||
// were sorted topologically, and we've already instantiated aliases coming from other
|
// were sorted topologically, and we've already instantiated aliases coming from other
|
||||||
// modules.
|
let mut to_instantiate_bitvec = solved_aliases_bitvec | &pending_aliases_bitvec;
|
||||||
// NB: ImMap::clone is O(1): https://docs.rs/im/latest/src/im/hash/map.rs.html#1527-1544
|
|
||||||
let mut to_instantiate = solved_aliases.clone().union(pending_aliases.clone());
|
|
||||||
|
|
||||||
for index in cycle.iter() {
|
for index in cycle.iter() {
|
||||||
let rec = symbols_introduced[*index as usize];
|
let index = *index as usize;
|
||||||
|
|
||||||
let alias = pending_aliases.get_mut(&rec).unwrap();
|
|
||||||
// Don't try to instantiate the alias itself in its definition.
|
// Don't try to instantiate the alias itself in its definition.
|
||||||
let original_alias_def = to_instantiate.remove(&rec).unwrap();
|
to_instantiate_bitvec.set(index, false);
|
||||||
|
|
||||||
let helper = |s| to_instantiate.get(&s);
|
// now we do something sneaky. In `can_instantiate_symbol` we want to be able to
|
||||||
|
// take a reference to an `Alias` in the `aliases` vec. That would not work if
|
||||||
|
// we also had a mutable reference to an alias in that vec. So we swap out the
|
||||||
|
// type.
|
||||||
|
let alias_region = aliases[index].region;
|
||||||
|
let mut alias_type = Type::EmptyRec;
|
||||||
|
|
||||||
|
std::mem::swap(&mut alias_type, &mut aliases[index].typ);
|
||||||
|
|
||||||
|
let can_instantiate_symbol = |s| match symbols_introduced.iter().position(|i| *i == s) {
|
||||||
|
Some(s_index) if to_instantiate_bitvec[s_index] => aliases.get(s_index),
|
||||||
|
_ => None,
|
||||||
|
};
|
||||||
|
|
||||||
let mut new_lambda_sets = ImSet::default();
|
let mut new_lambda_sets = ImSet::default();
|
||||||
alias
|
alias_type.instantiate_aliases(
|
||||||
.typ
|
alias_region,
|
||||||
.instantiate_aliases(alias.region, &helper, var_store, &mut new_lambda_sets);
|
&can_instantiate_symbol,
|
||||||
|
var_store,
|
||||||
|
&mut new_lambda_sets,
|
||||||
|
);
|
||||||
|
|
||||||
|
// swap the type back
|
||||||
|
std::mem::swap(&mut alias_type, &mut aliases[index].typ);
|
||||||
|
|
||||||
|
// We can instantiate this alias in future iterations
|
||||||
|
to_instantiate_bitvec.set(index, true);
|
||||||
|
|
||||||
|
let alias = &mut aliases[index];
|
||||||
|
|
||||||
for lambda_set_var in new_lambda_sets {
|
for lambda_set_var in new_lambda_sets {
|
||||||
alias
|
alias
|
||||||
|
@ -1979,10 +1989,9 @@ fn correct_mutual_recursive_type_alias<'a>(
|
||||||
.push(LambdaSet(Type::Variable(lambda_set_var)));
|
.push(LambdaSet(Type::Variable(lambda_set_var)));
|
||||||
}
|
}
|
||||||
|
|
||||||
to_instantiate.insert(rec, original_alias_def);
|
|
||||||
|
|
||||||
// Now mark the alias recursive, if it needs to be.
|
// Now mark the alias recursive, if it needs to be.
|
||||||
let is_self_recursive = alias.typ.contains_symbol(rec);
|
let rec = symbols_introduced[index];
|
||||||
|
let is_self_recursive = cycle.len() == 1 && matrix.get_row_col(index, index);
|
||||||
let is_mutually_recursive = cycle.len() > 1;
|
let is_mutually_recursive = cycle.len() > 1;
|
||||||
|
|
||||||
if is_self_recursive || is_mutually_recursive {
|
if is_self_recursive || is_mutually_recursive {
|
||||||
|
@ -2001,20 +2010,24 @@ fn correct_mutual_recursive_type_alias<'a>(
|
||||||
// all the types in the cycle are narrow newtypes. We can't figure this out until now,
|
// all the types in the cycle are narrow newtypes. We can't figure this out until now,
|
||||||
// because we need all the types to be deeply instantiated.
|
// because we need all the types to be deeply instantiated.
|
||||||
let all_are_narrow = cycle.iter().all(|index| {
|
let all_are_narrow = cycle.iter().all(|index| {
|
||||||
let sym = &symbols_introduced[*index as usize];
|
let index = *index as usize;
|
||||||
let typ = &pending_aliases.get(sym).unwrap().typ;
|
let typ = &aliases[index].typ;
|
||||||
matches!(typ, Type::RecursiveTagUnion(..)) && typ.is_narrow()
|
matches!(typ, Type::RecursiveTagUnion(..)) && typ.is_narrow()
|
||||||
});
|
});
|
||||||
|
|
||||||
if all_are_narrow {
|
if all_are_narrow {
|
||||||
// This cycle is illegal!
|
// This cycle is illegal!
|
||||||
let mut rest: Vec<Symbol> = cycle
|
|
||||||
|
let mut cycle = cycle;
|
||||||
|
let first_index = cycle.pop().unwrap() as usize;
|
||||||
|
|
||||||
|
let rest: Vec<Symbol> = cycle
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(|i| symbols_introduced[i as usize])
|
.map(|i| symbols_introduced[i as usize])
|
||||||
.collect();
|
.collect();
|
||||||
let alias_name = rest.pop().unwrap();
|
|
||||||
|
|
||||||
let alias = pending_aliases.get_mut(&alias_name).unwrap();
|
let alias_name = symbols_introduced[first_index];
|
||||||
|
let alias = aliases.get_mut(first_index).unwrap();
|
||||||
|
|
||||||
mark_cyclic_alias(
|
mark_cyclic_alias(
|
||||||
env,
|
env,
|
||||||
|
@ -2026,11 +2039,14 @@ fn correct_mutual_recursive_type_alias<'a>(
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Now, promote all resolved aliases in this cycle as solved.
|
// We've instantiated all we could, so all instantiatable aliases are solved now
|
||||||
solved_aliases.extend(pending_aliases);
|
solved_aliases_bitvec = to_instantiate_bitvec;
|
||||||
}
|
}
|
||||||
|
|
||||||
solved_aliases
|
symbols_introduced
|
||||||
|
.into_iter()
|
||||||
|
.zip(aliases.into_iter())
|
||||||
|
.collect()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn make_tag_union_of_alias_recursive<'a>(
|
fn make_tag_union_of_alias_recursive<'a>(
|
||||||
|
|
|
@ -40,6 +40,11 @@ impl ReferenceMatrix {
|
||||||
self.bitvec[index]
|
self.bitvec[index]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
pub fn get_row_col(&self, row: usize, col: usize) -> bool {
|
||||||
|
self.bitvec[row * self.length + col]
|
||||||
|
}
|
||||||
|
|
||||||
pub fn is_recursive(&self, index: usize) -> bool {
|
pub fn is_recursive(&self, index: usize) -> bool {
|
||||||
let mut scheduled = self.row_slice(index).to_bitvec();
|
let mut scheduled = self.row_slice(index).to_bitvec();
|
||||||
let mut visited = self.row_slice(index).to_bitvec();
|
let mut visited = self.row_slice(index).to_bitvec();
|
||||||
|
|
|
@ -3264,15 +3264,15 @@ mod test_reporting {
|
||||||
── DUPLICATE FIELD NAME ────────────────────────────────── /code/proj/Main.roc ─
|
── DUPLICATE FIELD NAME ────────────────────────────────── /code/proj/Main.roc ─
|
||||||
|
|
||||||
This record type defines the `.foo` field twice!
|
This record type defines the `.foo` field twice!
|
||||||
|
|
||||||
1│ a : { foo : Num.I64, bar : {}, foo : Str }
|
1│ a : { foo : Num.I64, bar : {}, foo : Str }
|
||||||
^^^^^^^^^^^^^ ^^^^^^^^^
|
^^^^^^^^^^^^^ ^^^^^^^^^
|
||||||
|
|
||||||
In the rest of the program, I will only use the latter definition:
|
In the rest of the program, I will only use the latter definition:
|
||||||
|
|
||||||
1│ a : { foo : Num.I64, bar : {}, foo : Str }
|
1│ a : { foo : Num.I64, bar : {}, foo : Str }
|
||||||
^^^^^^^^^
|
^^^^^^^^^
|
||||||
|
|
||||||
For clarity, remove the previous `.foo` definitions from this record
|
For clarity, remove the previous `.foo` definitions from this record
|
||||||
type.
|
type.
|
||||||
"#
|
"#
|
||||||
|
@ -3296,15 +3296,15 @@ mod test_reporting {
|
||||||
── DUPLICATE TAG NAME ──────────────────────────────────── /code/proj/Main.roc ─
|
── DUPLICATE TAG NAME ──────────────────────────────────── /code/proj/Main.roc ─
|
||||||
|
|
||||||
This tag union type defines the `Foo` tag twice!
|
This tag union type defines the `Foo` tag twice!
|
||||||
|
|
||||||
1│ a : [ Foo Num.I64, Bar {}, Foo Str ]
|
1│ a : [ Foo Num.I64, Bar {}, Foo Str ]
|
||||||
^^^^^^^^^^^ ^^^^^^^
|
^^^^^^^^^^^ ^^^^^^^
|
||||||
|
|
||||||
In the rest of the program, I will only use the latter definition:
|
In the rest of the program, I will only use the latter definition:
|
||||||
|
|
||||||
1│ a : [ Foo Num.I64, Bar {}, Foo Str ]
|
1│ a : [ Foo Num.I64, Bar {}, Foo Str ]
|
||||||
^^^^^^^
|
^^^^^^^
|
||||||
|
|
||||||
For clarity, remove the previous `Foo` definitions from this tag union
|
For clarity, remove the previous `Foo` definitions from this tag union
|
||||||
type.
|
type.
|
||||||
"#
|
"#
|
||||||
|
@ -3433,10 +3433,10 @@ mod test_reporting {
|
||||||
── TOO MANY TYPE ARGUMENTS ─────────────────────────────── /code/proj/Main.roc ─
|
── TOO MANY TYPE ARGUMENTS ─────────────────────────────── /code/proj/Main.roc ─
|
||||||
|
|
||||||
The `Num` alias expects 1 type argument, but it got 2 instead:
|
The `Num` alias expects 1 type argument, but it got 2 instead:
|
||||||
|
|
||||||
1│ a : Num.Num Num.I64 Num.F64
|
1│ a : Num.Num Num.I64 Num.F64
|
||||||
^^^^^^^^^^^^^^^^^^^^^^^
|
^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
Are there missing parentheses?
|
Are there missing parentheses?
|
||||||
"#
|
"#
|
||||||
),
|
),
|
||||||
|
@ -3459,10 +3459,10 @@ mod test_reporting {
|
||||||
── TOO MANY TYPE ARGUMENTS ─────────────────────────────── /code/proj/Main.roc ─
|
── TOO MANY TYPE ARGUMENTS ─────────────────────────────── /code/proj/Main.roc ─
|
||||||
|
|
||||||
The `Num` alias expects 1 type argument, but it got 2 instead:
|
The `Num` alias expects 1 type argument, but it got 2 instead:
|
||||||
|
|
||||||
1│ f : Str -> Num.Num Num.I64 Num.F64
|
1│ f : Str -> Num.Num Num.I64 Num.F64
|
||||||
^^^^^^^^^^^^^^^^^^^^^^^
|
^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
Are there missing parentheses?
|
Are there missing parentheses?
|
||||||
"#
|
"#
|
||||||
),
|
),
|
||||||
|
@ -3646,8 +3646,8 @@ mod test_reporting {
|
||||||
This `ACons` global tag application has the type:
|
This `ACons` global tag application has the type:
|
||||||
|
|
||||||
[ ACons (Num (Integer Signed64)) [
|
[ ACons (Num (Integer Signed64)) [
|
||||||
BCons (Num (Integer Signed64)) [ ACons Str [ BCons I64 a, BNil ],
|
BCons (Num (Integer Signed64)) [ ACons Str [
|
||||||
ANil ], BNil ], ANil ]
|
BCons I64 (AList I64 I64), BNil ] as a, ANil ], BNil ], ANil ]
|
||||||
|
|
||||||
But the type annotation on `x` says it should be:
|
But the type annotation on `x` says it should be:
|
||||||
|
|
||||||
|
@ -7092,20 +7092,20 @@ I need all branches in an `if` to have the same type!
|
||||||
indoc!(
|
indoc!(
|
||||||
r#"
|
r#"
|
||||||
── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─
|
── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─
|
||||||
|
|
||||||
Something is off with the body of the `inner` definition:
|
Something is off with the body of the `inner` definition:
|
||||||
|
|
||||||
3│ inner : * -> *
|
3│ inner : * -> *
|
||||||
4│ inner = \y -> y
|
4│ inner = \y -> y
|
||||||
^
|
^
|
||||||
|
|
||||||
The type annotation on `inner` says this `y` value should have the type:
|
The type annotation on `inner` says this `y` value should have the type:
|
||||||
|
|
||||||
*
|
*
|
||||||
|
|
||||||
However, the type of this `y` value is connected to another type in a
|
However, the type of this `y` value is connected to another type in a
|
||||||
way that isn't reflected in this annotation.
|
way that isn't reflected in this annotation.
|
||||||
|
|
||||||
Tip: Any connection between types must use a named type variable, not
|
Tip: Any connection between types must use a named type variable, not
|
||||||
a `*`! Maybe the annotation on `inner` should have a named type variable
|
a `*`! Maybe the annotation on `inner` should have a named type variable
|
||||||
in place of the `*`?
|
in place of the `*`?
|
||||||
|
@ -8867,21 +8867,21 @@ I need all branches in an `if` to have the same type!
|
||||||
^^^^^^^^^^^
|
^^^^^^^^^^^
|
||||||
|
|
||||||
Did you mean one of these?
|
Did you mean one of these?
|
||||||
|
|
||||||
Type
|
Type
|
||||||
True
|
True
|
||||||
Box
|
Box
|
||||||
Ok
|
Ok
|
||||||
|
|
||||||
── UNRECOGNIZED NAME ───────────────────────────────────── /code/proj/Main.roc ─
|
── UNRECOGNIZED NAME ───────────────────────────────────── /code/proj/Main.roc ─
|
||||||
|
|
||||||
I cannot find a `UnknownType` value
|
I cannot find a `UnknownType` value
|
||||||
|
|
||||||
3│ insertHelper : UnknownType, Type -> Type
|
3│ insertHelper : UnknownType, Type -> Type
|
||||||
^^^^^^^^^^^
|
^^^^^^^^^^^
|
||||||
|
|
||||||
Did you mean one of these?
|
Did you mean one of these?
|
||||||
|
|
||||||
Type
|
Type
|
||||||
True
|
True
|
||||||
insertHelper
|
insertHelper
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue