Commit Graph

338 Commits

Author SHA1 Message Date
Ayaz Hafiz
5fceb9ceb7
Push implicit openness vars through 2023-01-16 10:52:23 -06:00
Ayaz Hafiz
a47841d426
Generalize tag openness extension variables 2023-01-16 10:52:23 -06:00
Ayaz Hafiz
1c93727822
Add a notion of "openness" tag extensions suitable only for size-polymorphism 2023-01-16 10:52:23 -06:00
Ayaz Hafiz
b8712bcb30
Weaken records in let-bindings
This change also means we must update the interface of `Dict.empty` and
`Set.empty` from

```
Dict.empty : Dict k v
```

to

```
Dict.empty : {} -> Dict k v
```
2023-01-14 15:33:51 +01:00
Ayaz Hafiz
cb1373a3a4
Weaken let-binding calls 2023-01-14 15:33:49 +01:00
Ayaz Hafiz
d27a72de1f
Weaken let sequences under let binding 2023-01-14 15:33:49 +01:00
Folkert de Vries
e3a213c0dc
Merge pull request #4882 from roc-lang/weakening-3
Begin weakening let-bindings to non-function, non-number expressions
2023-01-14 15:32:27 +01:00
Ayaz Hafiz
7c4cfba4b8
Rephrase solve comments as per Folkert's review 2023-01-13 12:58:11 -06:00
Ayaz Hafiz
9462f44bb7
Weaken variables bound to when expressions 2023-01-12 10:02:21 -06:00
Ayaz Hafiz
b2cdddbdfb
Weaken lists 2023-01-12 10:02:20 -06:00
Folkert de Vries
386983a657
Merge pull request #4881 from roc-lang/rename-rank-none
Rename rank none to rank generalized
2023-01-12 00:25:24 +01:00
Ayaz Hafiz
d214598a16
Rename rank none to rank generalized 2023-01-11 14:55:18 -06:00
Ayaz Hafiz
058644aa96
Implement weakening of variables introduced in branch patterns
Variables introduced in branch patterns should never be generalized in
the new weakening model. This implements that. The strategy is:

- when we have a let-binding that should be weakened, do not introduce
  its bound variables in a new (higher) rank
- instead, introduce them at the current rank, and also solve the
  let-binding at the current rank
- if any of those variables should then be generalized relative to the
  current rank, they will be so when the current rank is popped and
  generalized
2023-01-11 14:28:46 -06:00
Ayaz Hafiz
7febddd1ea
Get rid of unneeded unsafe code and explain max_rank adjustment 2023-01-11 14:28:46 -06:00
Ayaz Hafiz
ddda00036e
Add more comments to solve 2023-01-11 14:28:46 -06:00
Ayaz Hafiz
c2cb94a927
Decide rank to work in for weakening 2023-01-11 14:28:46 -06:00
Ayaz Hafiz
5ccedca093
Implement de-generalization for weakened let bindings 2023-01-11 14:28:45 -06:00
Ayaz Hafiz
dc30dbc8a4
Add rank-generalization test 2023-01-10 12:46:34 -06:00
Ayaz Hafiz
cb9f776781
Remove check for redundancy before doing rank adjustment
Presently while generalizing type variables, we check variables
introduced at a scope for redundancy (whether they are not the root of
some unified set of variables). If a variable is redundant, its rank is
not adjusted. I believe the current logic to be the following:

- Each root of a unification tree will be introduced at some point,
  exactly once. Its point of introduction will determine the rank of the
  tree it's the root of
- If a variable is redundant, all of its redundant usages must be at the
  same rank (assuming let generalization proceeds correctly),
  so there is no need to adjust their rank as well
- As such, there is no need to adjust the rank of redundant variables,
  as a performance optimization.

I believe this to be a hold-over from the original version of the solver
derived from the elm-compiler.

In our implementation however rank adjustment is very cheap (thanks to
SoA, ranks are likely in the cache lines already anyway because we just
adjusted variables at this point).

However, there is a larger problem here - ranks must be adjusted for
redundant variables as we begin to support weakened type variables.

The motivating case is

```
\x -> when x is
  _x -> Green
```

we would like this code generalized as `* -> [Green]*`. `when`
expressions have each branch solved via let-bindings; in particular, for
the singleton branch we introduce `_x` of the appropriate type and solve
the body as `[Green]*`.

Today, `[Green]*` would be generalized in the context of the inner scope
that binds `_x`, which means it is generalized in the body `\x -> ...`
as a whole.

However, with weakening, we do not want this behavior! In particular, we
do not want to actually generalize `_x` in the context of the branch
body. Doing so means you could write things like

```
main = \{} -> when Red is
    x ->
        y : [Red]
        y = x

        z : [Red, Green]
        z = x

        {y, z}
```

which is exactly the kind of spurious generalization that the weakening
design is trying to avoid.

So, we want to introduce `[Green]*` at the rank of the body `\x -> ...`;
let's call this `rank_body`, and let's say `[Green]*` is introduced as
`branch_var`. Let's say the return type variable is `ret_var`.

Now we must be careful. If after unification `ret_var ~ branch_var` we have that
`branch_var` becomes the root, then despite `ret_var` (and `branch_var`) being at
`rank_body` (which is also the rank that will promoted to generalization),
the tree given by `branch_var` won't be generalized, because `ret_var` will be
seen as redundant! In fact it is, because `branch_var` was introdued
previously, but that doesn't matter - we want the variable to be
generalized at the level of the outer let-binding `main = \{} -> ...`.

This problem is not unique to when-branches; for example we can observe
the same symptom with

```
main = \{} ->
    x = Green
    x
```

where here we'd like `x` to not be generalized inside the body of
`main`, but have it be generalized relative to the body of `main` (that
is, main should have signature `{} -> [Green]*`, but you cannot use `x`
itself polymorphically inside the body of `main`).

As such, the easiest solution as far as I can see, in the presence of
weakening, is to allow rank-adjustment and generalization of redundant
variables if they are permitted to be generalized relative to a lower
scope.

This should preserve soundness; the main source of unsoundness in
rank-based let generalization is making sure something like

```
\x ->
    y = \z -> x z
    y
```

has type `(a -> b) -> (a -> b)` and not e.g. `(a -> b) -> (c -> d)` due
to `x` being instantiated at a higher rank in `y = ...` than it
actually is. Note that this change cannot affect this case at all, since
we are still doing the rank-adjustment pass at higher ranks, unifying
lowers ranked variables to their minimum relative rank, and introduction
only happens in the lower-ranked scopes.
2023-01-10 12:05:06 -06:00
Ayaz
7c61d0d278
Merge pull request #4843 from roc-lang/pattern-as-can
Pattern as can
2023-01-08 19:36:40 -06:00
Folkert
c2ddeb0de0
fix and test as pattern type inference 2023-01-08 16:40:03 +01:00
Ayaz Hafiz
3b0e2429e6
Support printing weak type variables in tests
Unbound type variables that are not at the generalization rank will now
be printed as `w_a` in solve tests.
2023-01-04 16:24:19 -06:00
Folkert
26e5ac85d4
remove argument from the from_str functions 2022-12-29 16:50:19 +01:00
Joshua Warner
174f7d5e4d
Fix bug in unifying records
This was leading us to have an infinitely-recursive type, which eventually causes layout to stack-overflow

Fixes #4739
2022-12-17 11:37:19 -08:00
Ayaz Hafiz
bd06714fd2
Make sure to constrain dbgs with existential correctly 2022-12-14 17:02:29 -06:00
Ayaz Hafiz
cd2b936a59
Ensure that disjoint nested lambda sets force parents to be disjoint
We must be careful to ensure that if unifying nested lambda sets
results in disjoint lambdas, that the parent lambda sets are
ultimately treated disjointly as well.
Consider

```
  v1: {} -[ foo ({} -[ bar Str ]-> {}) ]-> {}
~ v2: {} -[ foo ({} -[ bar U64 ]-> {}) ]-> {}
```

When considering unification of the nested sets

```
  [ bar Str ]
~ [ bar U64 ]
```

we should not unify these sets, even disjointly, because that would
ultimately lead us to unifying

```
v1 ~ v2
=> {} -[ foo ({} -[ bar Str, bar U64 ]-> {}) ] -> {}
```

which is quite wrong - we do not have a lambda `foo` that captures
either `bar captures: Str` or `bar captures: U64`, we have two
different lambdas `foo` that capture different `bars`. The target
unification is

```
v1 ~ v2
=> {} -[ foo ({} -[ bar Str ]-> {}),
         foo ({} -[ bar U64 ]-> {}) ] -> {}
```

Closes #4712
2022-12-12 14:51:18 -06:00
Ayaz Hafiz
f178a86f99
Support pretty-printing can decls in solve tests 2022-12-12 14:50:14 -06:00
Ayaz Hafiz
27dfe974df
Only print lambda ident names in types if they are ambiguous 2022-12-12 14:48:09 -06:00
Ayaz Hafiz
09353733fa
Make sure to register imported variables when importing specializations
Closes #4671
2022-12-05 11:44:28 -06:00
Ayaz Hafiz
4159b83214
Fix imports 2022-12-03 13:17:35 -08:00
Ayaz Hafiz
2e56405c1e
Fix obligation checking for rigid able vars 2022-12-03 13:17:34 -08:00
Brendan Hansknecht
28835d5bf3
some bug fixes 2022-12-03 13:17:34 -08:00
Ayaz Hafiz
3605008fce
Update tests to check eq unbound float is resolved to dec 2022-12-01 11:41:42 -06:00
Ayaz Hafiz
ed7d4f8f63
Obligation checking of floating point for Eq succeeds only with Dec 2022-12-01 11:31:41 -06:00
Ayaz Hafiz
590535a42b
Obligation checking Eq for floating point types may never succeed 2022-12-01 10:02:37 -06:00
Richard Feldman
b2beeb770e
Merge remote-tracking branch 'origin/main' into https-packages 2022-11-25 19:50:06 -05:00
Richard Feldman
791025d3ed
Merge pull request #4579 from roc-lang/underivable-rigid-better-error
Suggest binding type variables to an ability when they're unsatisfied
2022-11-25 19:46:11 -05:00
Ayaz
8c0ff4c839
Merge pull request #4558 from roc-lang/specialization-sets-for-impl-opaques
Support custom abilities for opaques with immaterial lambda sets
2022-11-25 16:42:32 -06:00
Ayaz
15e372373a
Merge branch 'main' into underivable-rigid-better-error
Signed-off-by: Ayaz <20735482+ayazhafiz@users.noreply.github.com>
2022-11-25 16:33:57 -06:00
Ayaz Hafiz
e2b30e5301
Constrain + solve crash 2022-11-24 14:46:50 -06:00
Ayaz Hafiz
9dc489c2b0
First pass constraining crash 2022-11-24 14:46:49 -06:00
Richard Feldman
f5cb2d73a1
Merge branch 'precompiled-legacy' into https-packages 2022-11-24 04:29:56 -05:00
Ayaz
3059fd31c9
Merge branch 'main' into specialization-sets-for-impl-opaques
Signed-off-by: Ayaz <20735482+ayazhafiz@users.noreply.github.com>
2022-11-23 17:12:04 -06:00
Ayaz Hafiz
7c3a0807c2
Suggest binding type variables to an ability when they're unsatisfied 2022-11-23 16:37:50 -06:00
Ayaz Hafiz
c70b23ac9c
Debug Work 2022-11-22 13:04:27 -06:00
Ayaz Hafiz
dce4d6c4c7
Force occurs check for introduced types after checking annotated bodies
The current type inference scheme is such that we first introduce the
types for annotation functions, then check their bodies without
additional re-generalization. As part of generalization, we also perform
occurs checks to fix-up recursive tag unions.

However, type annotations can contain type inference variables that are
neither part of the generalization scheme, nor are re-generalized later
on, and in fact end up forming a closure of a recursive type. If we do
not catch and break such closures into recursive types, things go bad
soon after in later stages of the compiler.

To deal with this, re-introduce the values of recursive values after we
check their definitions, forcing an occurs check. This introduction is
benign because we already generalized appropriate type variables anyway.
Though, the introduction is somewhat unnecessary, and I have ideas on
how to make all of this simpler and more performant. That will come in
the future.
2022-11-22 12:58:51 -06:00
Ayaz Hafiz
414a320358
Constrain flex inference variables without re-generalization
When constraining a recursive function like

```
f : _ -> {}
f : \_ -> f {}
```

our first step is to solve the value type of `f` relative to its
annotation. We have to be careful that the inference variable in the
signature of `f` is not generalized until after the body of `f` is
solved. Otherwise, we end up admitting polymorphic recursion.
2022-11-22 10:43:59 -06:00
Ayaz Hafiz
e36618b9e9
Support custom abilities for opaques with immaterial lambda sets
If a specialization of an ability member has a lambda set that is not
reflected in the unspecialized lambda sets of the member's prototype
signature, then the specialization lambda set is deemed to be immaterial
to the specialization lambda set mapping, and we don't need to associate
it with a particular region from the prototype signature.

This can happen when an opaque contains functions that are some specific
than the generalized prototype signature; for example, when we are
defining a custom impl for an opaque with functions.

Addresses a bug found in 8c3158c3e0
2022-11-21 18:03:56 -06:00
Ayaz Hafiz
e1afd964c7
Correct when fixpoint-fixed type variables can be reunified
With fixpoint-fixing, we don't want to re-unify type variables that were
just fixed, because doing so may change their shapes in ways that we
explicitly just set them up not to be changed (as fixpoint-fixing
clobbers type variable contents).

However, this restriction need only apply when we re-unify two type
variables that were both involved in the same fixpoint-fixing cycle. If
we have a type variable T that was involved in fixpoint-fixing, and we
unify it with U that wasn't, we know that the $U \notin \bar{T}$, where
$\bar{T}$ is the recursive closure of T. In these cases, we do want to
permit the usual in-band unification of $T \sim U$.
2022-11-21 15:57:36 -06:00
Richard Feldman
721841fa1f
Provide roc_cache_dir everywhere 2022-11-20 19:53:48 -05:00