Previously, we determined whether a closure argument should be added to
the proc layout of a compiled function by checking whether the lambda
set was material at all. But, the extension for a single function should
be added if and only if the function itself captures.
Some of the head-constructor tests we generate can be supersets of other tests.
Edges must be ordered so that more general tests always happen after their
specialized variants.
For example, patterns
[1, ..] -> ...
[2, 1, ..] -> ...
may generate the edges
ListLen(>=1) -> <rest>
ListLen(>=2) -> <rest>
but evaluated in exactly this order, the second edge is never reachable.
The necessary ordering is
ListLen(>=2) -> <rest>
ListLen(>=1) -> <rest>
Closes#4732
And provide expectation subs when expectations should be compiled. This
will be useful for getting the specialized type of an expect lookup when
it's available.
This actually isn't needed, because the backends must lookup the layout
from the environment anyway. So it's enough to lookup the symbol and
find its layout, there is no need to additionally store it.
When compiling a pattern match like
```
[] -> ..
[_] -> ..
[_, ..] -> ..
```
to a decision tree, we must make sure that the last test (len >= 1)
does not touch the branch reached by the second test (len == 1). It is
enough to ban (len >=) tests from ever touching exact-sized list
patterns, because a spread test (len >=) can never reach an exact-sized
test.
On the other hand, an exact-sized test can reach a spread pattern,
because in
```
[_, _] -> ..
[..] -> ..
```
the last branch generates tests for patterns `[]` and `[_]`, and we would
like those patterns to be covered by the spread test (len >= 0)!
Closes#4685
This patch provides errors for defs that are used only in
possibly-mutual recursion, and are not reachable outside of their
recursive closures. For example:
```
test_report!(
mutual_recursion_not_reached_nested,
indoc!(
r#"
app "test" provides [main] to "./platform"
main =
f = \{} -> if Bool.true then "" else g {}
g = \{} -> if Bool.true then "" else f {}
""
"#
),
@r###"
── DEFINITIONs ONLY USED IN RECURSION ──────────────────── /code/proj/Main.roc ─
These 2 definitions are only used in mutual recursion with themselves:
4│> f = \{} -> if Bool.true then "" else g {}
5│> g = \{} -> if Bool.true then "" else f {}
If you don't intend to use or export any of them, they should all be
removed!
"###
);
```
Previously, a program like
```
main =
f =
n = 1
\{} -[#lam]-> n # suppose lambda set = #lam
f {}
```
would be transformed to
```
main =
n = 1
f = \{} -[#lam]-> n
f {}
```
However, the IR lowering procedure is such that we would then associate
`f` as definining the procedure given the lambda set `#lam`. This is not
correct, as `f` is really a function pointer in this circumstance,
rather than the definer of `#lam`.
Instead, the transformation we want to perform is
```
main =
n = 1
#lam = \{} -[#lam]-> n
f = #lam
f {}
```
Which is what this patch does
Closes#2403
Previously this was a bit hacky, we worked around having to update
symbol substitutions by special-casing the rest of the program. There's
not need to do that.