Programs like
```
after : ({} -> a), ({} -> b) -> ({} -> b)
fx = after (\{} -> {}) \{} -> if Bool.true then fx {} else {}
```
are legal because they always decay to functions, even if they may not
look like functions syntactically. Rather than using a syntactic check
to check for illegally-recursive functions, we should only perform such
checks after we know the types of values.
Closes#4291
The mutual-recursion checks does not admit types that are not function
types; because Roc is strict, only functional values can be involved in
mutual recursion. However, this check was exercised by checking the head
constructor of a type, which is not the correct way to do it. Aliases
and opaque types may in fact be function types as well, so we must chase
their actual contents.
Closes#4246
We have this idea of "rigid optional" fields to annotate record fields
that must necessarily be optional. That avoids the admission of programs
we cannot faithfully compile, like
```
f : {a: Str, b ? U64}
f = {a: "b", b: 1}
```
We want to lose the rigidity restriction when a generalized symbol is
used as at a specialized site; for example it should be possible to call
`f : {x ? Str} -> {}` with both `{}` and `{x : Str}`, neither of which
have a rigidly optional field unless they were to be annotated.
Prior to this commit we would loosen the rigidity restriction upon
specialization of a generalized type at a use site. However, what we
really want to do is apply the loosening during calculation of
generalization. The reason is that otherwise, we must make types that
would be ground (like `{x ? Str} -> {}`) generalized just for the sake
of the optional field annotation. But since the rigidity constraint is
irrelevant after an annotated body has been checked, we can loosen the
rigidity restriction then, which conveniently happens to coincide with
the generalization calculation.
Closes#3955
Closure captures can be transient, but previously, we did not handle
that correctly. For example, in
```
x = ""
inner = \{} -> x
outer = \{} -> inner {}
```
`outer` captures `inner`, but `inner` captures `x`, and in the body of
`outer`, we would not construct the closure data for `inner` correctly
before calling it.
There are a couple ways around this.
1. Update mono to do something when we are passed the captured
environment of a closure, rather than attempting to construct a
call-by-name's captured environment before callign it.
2. Fix-up closures during canonicalization to remove captured closures
that themselves capture, and replace them with their captures.
This patch does (2), since (1) is much more involved and is not likely
to bring a lot of wins. In general I think it's reasonable to expect
captured environments, even if transient, to be fairly shallow, so I
don't think this will produce very large closure environments.
Closes#2894
I think this makes it easier to read type variables when they come from
flex/rigid vars with pre-existing names, just give them a number suffix
to differentiate them.
When we unify two variables that end up merged, the rank of the
resulting content is the lower of the two variables being merged. But
during storage, we really do mean, take the target descriptor of the
type we're merging against, and don't try to lower to a
possibly-generalized rank! This fixes a couple bugs I didn't even
realize were present!
During the unspecialized lambda set compaction procedure, we might end
up trying to merge too many disjoint variables during unspecialized
lambda unification. Avoid doing so, by checking if we're in the
compaction procedure.