mirror of
https://github.com/roc-lang/roc.git
synced 2025-10-02 08:11:12 +00:00
Merge remote-tracking branch 'origin/trunk' into scope-smarter-storage
This commit is contained in:
commit
6028507dff
170 changed files with 9887 additions and 3991 deletions
1
AUTHORS
1
AUTHORS
|
@ -77,3 +77,4 @@ Cai Bingjun <62678643+C-BJ@users.noreply.github.com>
|
||||||
Jared Cone <jared.cone@gmail.com>
|
Jared Cone <jared.cone@gmail.com>
|
||||||
Sean Hagstrom <sean@seanhagstrom.com>
|
Sean Hagstrom <sean@seanhagstrom.com>
|
||||||
Kas Buunk <kasbuunk@icloud.com>
|
Kas Buunk <kasbuunk@icloud.com>
|
||||||
|
Oskar Hahn <mail@oshahn.de>
|
||||||
|
|
10
Cargo.lock
generated
10
Cargo.lock
generated
|
@ -3253,9 +3253,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "regex"
|
name = "regex"
|
||||||
version = "1.5.4"
|
version = "1.5.5"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "d07a8629359eb56f1e2fb1652bb04212c072a87ba68546a04065d525673ac461"
|
checksum = "1a11647b6b25ff05a515cb92c365cec08801e83423a235b51e231e1808747286"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"aho-corasick",
|
"aho-corasick",
|
||||||
"memchr",
|
"memchr",
|
||||||
|
@ -3955,7 +3955,9 @@ dependencies = [
|
||||||
"arrayvec 0.7.2",
|
"arrayvec 0.7.2",
|
||||||
"bumpalo",
|
"bumpalo",
|
||||||
"indoc",
|
"indoc",
|
||||||
|
"lazy_static",
|
||||||
"pretty_assertions",
|
"pretty_assertions",
|
||||||
|
"regex",
|
||||||
"roc_builtins",
|
"roc_builtins",
|
||||||
"roc_can",
|
"roc_can",
|
||||||
"roc_collections",
|
"roc_collections",
|
||||||
|
@ -4489,9 +4491,9 @@ checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "target-lexicon"
|
name = "target-lexicon"
|
||||||
version = "0.12.2"
|
version = "0.12.3"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "d9bffcddbc2458fa3e6058414599e3c838a022abae82e5c67b4f7f80298d5bff"
|
checksum = "d7fa7e55043acb85fca6b3c01485a2eeb6b69c5d21002e273c79e465f43b7ac1"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "tempfile"
|
name = "tempfile"
|
||||||
|
|
57
TUTORIAL.md
57
TUTORIAL.md
|
@ -115,18 +115,19 @@ Create a new file called `Hello.roc` and put this inside it:
|
||||||
|
|
||||||
```coffee
|
```coffee
|
||||||
app "hello"
|
app "hello"
|
||||||
packages { pf: "examples/cli/platform" }
|
packages { pf: "examples/interactive/cli-platform" }
|
||||||
imports [ pf.Stdout ]
|
imports [ pf.Stdout ]
|
||||||
provides [ main ] to pf
|
provides [ main ] to pf
|
||||||
|
|
||||||
main = Stdout.line "I'm a Roc application!"
|
main = Stdout.line "I'm a Roc application!"
|
||||||
```
|
```
|
||||||
|
|
||||||
> **NOTE:** This assumes you've put Hello.roc in the root directory of the
|
> **NOTE:** This assumes you've put Hello.roc in the root directory of the Roc
|
||||||
> Roc source code. If you'd like to put it somewhere else, you'll need to replace
|
> source code. If you'd like to put it somewhere else, you'll need to replace
|
||||||
> `"examples/cli/"` with the path to the `examples/cli/` folder in
|
> `"examples/interactive/cli-platform"` with the path to the
|
||||||
> that source code. In the future, Roc will have the tutorial built in, and this
|
> `examples/interactive/cli-platform` folder in that source code. In the future,
|
||||||
> aside will no longer be necessary!
|
> Roc will have the tutorial built in, and this aside will no longer be
|
||||||
|
> necessary!
|
||||||
|
|
||||||
Try running this with:
|
Try running this with:
|
||||||
|
|
||||||
|
@ -1202,6 +1203,24 @@ and also `Num.cos 1` and have them all work as expected; the number literal `1`
|
||||||
`Num *`, which is compatible with the more constrained types `Int` and `Frac`. For the same reason,
|
`Num *`, which is compatible with the more constrained types `Int` and `Frac`. For the same reason,
|
||||||
you can pass number literals to functions expecting even more constrained types, like `I32` or `F64`.
|
you can pass number literals to functions expecting even more constrained types, like `I32` or `F64`.
|
||||||
|
|
||||||
|
### Typed Number Literals
|
||||||
|
When writing a number literal in Roc you can specify the numeric type as a suffix of the literal.
|
||||||
|
`1u8` specifies `1` as an unsigned 8-bit integer, `5i32` specifies `5` as a signed 32-bit integer, etc.
|
||||||
|
The full list of possible suffixes includes:
|
||||||
|
`i8`, `u8`, `i16`, `u16`, `i32`, `u32`, `i64`, `u64`, `i128`, `u128`, `nat`, `f32`, `f64`, `dec`
|
||||||
|
|
||||||
|
### Hexadecimal Integer Literals
|
||||||
|
Integer literals can be written in hexadecimal form by prefixing with `0x` followed by hexadecimal characters.
|
||||||
|
`0xFE` evaluates to decimal `254`
|
||||||
|
The integer type can be specified as a suffix to the hexadecimal literal,
|
||||||
|
so `0xC8u8` evaluates to decimal `200` as an unsigned 8-bit integer.
|
||||||
|
|
||||||
|
### Binary Integer Literals
|
||||||
|
Integer literals can be written in binary form by prefixing with `0b` followed by the 1's and 0's representing
|
||||||
|
each bit. `0b0000_1000` evaluates to decimal `8`
|
||||||
|
The integer type can be specified as a suffix to the binary literal,
|
||||||
|
so `0b0100u8` evaluates to decimal `4` as an unsigned 8-bit integer.
|
||||||
|
|
||||||
## Interface modules
|
## Interface modules
|
||||||
|
|
||||||
[ This part of the tutorial has not been written yet. Coming soon! ]
|
[ This part of the tutorial has not been written yet. Coming soon! ]
|
||||||
|
@ -1236,7 +1255,7 @@ Let's take a closer look at the part of `Hello.roc` above `main`:
|
||||||
|
|
||||||
```coffee
|
```coffee
|
||||||
app "hello"
|
app "hello"
|
||||||
packages { pf: "examples/cli/platform" }
|
packages { pf: "examples/interactive/cli-platform" }
|
||||||
imports [ pf.Stdout ]
|
imports [ pf.Stdout ]
|
||||||
provides main to pf
|
provides main to pf
|
||||||
```
|
```
|
||||||
|
@ -1254,14 +1273,14 @@ without running it by running `roc build Hello.roc`.
|
||||||
The remaining lines all involve the *platform* this application is built on:
|
The remaining lines all involve the *platform* this application is built on:
|
||||||
|
|
||||||
```coffee
|
```coffee
|
||||||
packages { pf: "examples/cli/platform" }
|
packages { pf: "examples/interactive/cli-platform" }
|
||||||
imports [ pf.Stdout ]
|
imports [ pf.Stdout ]
|
||||||
provides main to pf
|
provides main to pf
|
||||||
```
|
```
|
||||||
|
|
||||||
The `packages { pf: "examples/cli/platform" }` part says two things:
|
The `packages { pf: "examples/interactive/cli-platform" }` part says two things:
|
||||||
|
|
||||||
- We're going to be using a *package* (that is, a collection of modules) called `"examples/cli/platform"`
|
- We're going to be using a *package* (that is, a collection of modules) called `"examples/interactive/cli-platform"`
|
||||||
- We're going to name that package `pf` so we can refer to it more concisely in the future.
|
- We're going to name that package `pf` so we can refer to it more concisely in the future.
|
||||||
|
|
||||||
The `imports [ pf.Stdout ]` line says that we want to import the `Stdout` module
|
The `imports [ pf.Stdout ]` line says that we want to import the `Stdout` module
|
||||||
|
@ -1281,17 +1300,18 @@ calling a function named `line` which is exposed by a module named
|
||||||
When we write `imports [ pf.Stdout ]`, it specifies that the `Stdout`
|
When we write `imports [ pf.Stdout ]`, it specifies that the `Stdout`
|
||||||
module comes from the `pf` package.
|
module comes from the `pf` package.
|
||||||
|
|
||||||
Since `pf` was the name we chose for the `examples/cli/platform` package
|
Since `pf` was the name we chose for the `examples/interactive/cli-platform`
|
||||||
(when we wrote `packages { pf: "examples/cli/platform" }`), this `imports` line
|
package (when we wrote `packages { pf: "examples/interactive/cli-platform" }`),
|
||||||
tells the Roc compiler that when we call `Stdout.line`, it should look for that
|
this `imports` line tells the Roc compiler that when we call `Stdout.line`, it
|
||||||
`line` function in the `Stdout` module of the `examples/cli/platform` package.
|
should look for that `line` function in the `Stdout` module of the
|
||||||
|
`examples/interactive/cli-platform` package.
|
||||||
|
|
||||||
# Building a Command-Line Interface (CLI)
|
# Building a Command-Line Interface (CLI)
|
||||||
|
|
||||||
## Tasks
|
## Tasks
|
||||||
|
|
||||||
Tasks are technically not part of the Roc language, but they're very common in
|
Tasks are technically not part of the Roc language, but they're very common in
|
||||||
platforms. Let's use the CLI platform in `examples/cli` as an example!
|
platforms. Let's use the CLI platform in `examples/interactive/cli-platform` as an example!
|
||||||
|
|
||||||
In the CLI platform, we have four operations we can do:
|
In the CLI platform, we have four operations we can do:
|
||||||
|
|
||||||
|
@ -1306,7 +1326,7 @@ First, let's do a basic "Hello World" using the tutorial app.
|
||||||
|
|
||||||
```coffee
|
```coffee
|
||||||
app "cli-tutorial"
|
app "cli-tutorial"
|
||||||
packages { pf: "examples/cli/platform" }
|
packages { pf: "examples/interactive/cli-platform" }
|
||||||
imports [ pf.Stdout ]
|
imports [ pf.Stdout ]
|
||||||
provides [ main ] to pf
|
provides [ main ] to pf
|
||||||
|
|
||||||
|
@ -1343,7 +1363,7 @@ Let's change `main` to read a line from `stdin`, and then print it back out agai
|
||||||
|
|
||||||
```swift
|
```swift
|
||||||
app "cli-tutorial"
|
app "cli-tutorial"
|
||||||
packages { pf: "examples/cli/platform" }
|
packages { pf: "examples/interactive/cli-platform" }
|
||||||
imports [ pf.Stdout, pf.Stdin, pf.Task ]
|
imports [ pf.Stdout, pf.Stdin, pf.Task ]
|
||||||
provides [ main ] to pf
|
provides [ main ] to pf
|
||||||
|
|
||||||
|
@ -1393,7 +1413,7 @@ This works, but we can make it a little nicer to read. Let's change it to the fo
|
||||||
|
|
||||||
```haskell
|
```haskell
|
||||||
app "cli-tutorial"
|
app "cli-tutorial"
|
||||||
packages { pf: "examples/cli/platform" }
|
packages { pf: "examples/interactive/cli-platform" }
|
||||||
imports [ pf.Stdout, pf.Stdin, pf.Task.{ await } ]
|
imports [ pf.Stdout, pf.Stdin, pf.Task.{ await } ]
|
||||||
provides [ main ] to pf
|
provides [ main ] to pf
|
||||||
|
|
||||||
|
@ -1944,7 +1964,6 @@ Here are various Roc expressions involving operators, and what they desugar to.
|
||||||
| `a // b` | `Num.divTrunc a b` |
|
| `a // b` | `Num.divTrunc a b` |
|
||||||
| `a ^ b` | `Num.pow a b` |
|
| `a ^ b` | `Num.pow a b` |
|
||||||
| `a % b` | `Num.rem a b` |
|
| `a % b` | `Num.rem a b` |
|
||||||
| `a %% b` | `Num.mod a b` |
|
|
||||||
| `a >> b` | `Num.shr a b` |
|
| `a >> b` | `Num.shr a b` |
|
||||||
| `a << b` | `Num.shl a b` |
|
| `a << b` | `Num.shl a b` |
|
||||||
| `-a` | `Num.neg a` |
|
| `-a` | `Num.neg a` |
|
||||||
|
|
|
@ -264,25 +264,6 @@ pub fn constrain_expr<'a>(
|
||||||
*variant_var,
|
*variant_var,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
Expr2::PrivateTag {
|
|
||||||
name,
|
|
||||||
arguments,
|
|
||||||
ext_var,
|
|
||||||
variant_var,
|
|
||||||
} => {
|
|
||||||
let tag_name = TagName::Private(*name);
|
|
||||||
|
|
||||||
constrain_tag(
|
|
||||||
arena,
|
|
||||||
env,
|
|
||||||
expected,
|
|
||||||
region,
|
|
||||||
tag_name,
|
|
||||||
arguments,
|
|
||||||
*ext_var,
|
|
||||||
*variant_var,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
Expr2::Call {
|
Expr2::Call {
|
||||||
args,
|
args,
|
||||||
expr_var,
|
expr_var,
|
||||||
|
@ -678,24 +659,28 @@ pub fn constrain_expr<'a>(
|
||||||
for (index, when_branch_id) in branches.iter_node_ids().enumerate() {
|
for (index, when_branch_id) in branches.iter_node_ids().enumerate() {
|
||||||
let when_branch = env.pool.get(when_branch_id);
|
let when_branch = env.pool.get(when_branch_id);
|
||||||
|
|
||||||
let pattern_region = region;
|
|
||||||
// let pattern_region = Region::across_all(
|
// let pattern_region = Region::across_all(
|
||||||
// when_branch.patterns.iter(env.pool).map(|v| &v.region),
|
// when_branch.patterns.iter(env.pool).map(|v| &v.region),
|
||||||
// );
|
// );
|
||||||
|
|
||||||
|
let pattern_expected = |sub_pattern, sub_region| {
|
||||||
|
PExpected::ForReason(
|
||||||
|
PReason::WhenMatch {
|
||||||
|
index: HumanIndex::zero_based(index),
|
||||||
|
sub_pattern,
|
||||||
|
},
|
||||||
|
cond_type.shallow_clone(),
|
||||||
|
sub_region,
|
||||||
|
)
|
||||||
|
};
|
||||||
|
|
||||||
let branch_con = constrain_when_branch(
|
let branch_con = constrain_when_branch(
|
||||||
arena,
|
arena,
|
||||||
env,
|
env,
|
||||||
// TODO: when_branch.value.region,
|
// TODO: when_branch.value.region,
|
||||||
region,
|
region,
|
||||||
when_branch,
|
when_branch,
|
||||||
PExpected::ForReason(
|
pattern_expected,
|
||||||
PReason::WhenMatch {
|
|
||||||
index: HumanIndex::zero_based(index),
|
|
||||||
},
|
|
||||||
cond_type.shallow_clone(),
|
|
||||||
pattern_region,
|
|
||||||
),
|
|
||||||
Expected::FromAnnotation(
|
Expected::FromAnnotation(
|
||||||
name.clone(),
|
name.clone(),
|
||||||
*arity,
|
*arity,
|
||||||
|
@ -722,22 +707,26 @@ pub fn constrain_expr<'a>(
|
||||||
for (index, when_branch_id) in branches.iter_node_ids().enumerate() {
|
for (index, when_branch_id) in branches.iter_node_ids().enumerate() {
|
||||||
let when_branch = env.pool.get(when_branch_id);
|
let when_branch = env.pool.get(when_branch_id);
|
||||||
|
|
||||||
let pattern_region = region;
|
|
||||||
// let pattern_region =
|
// let pattern_region =
|
||||||
// Region::across_all(when_branch.patterns.iter().map(|v| &v.region));
|
// Region::across_all(when_branch.patterns.iter().map(|v| &v.region));
|
||||||
|
|
||||||
|
let pattern_expected = |sub_pattern, sub_region| {
|
||||||
|
PExpected::ForReason(
|
||||||
|
PReason::WhenMatch {
|
||||||
|
index: HumanIndex::zero_based(index),
|
||||||
|
sub_pattern,
|
||||||
|
},
|
||||||
|
cond_type.shallow_clone(),
|
||||||
|
sub_region,
|
||||||
|
)
|
||||||
|
};
|
||||||
|
|
||||||
let branch_con = constrain_when_branch(
|
let branch_con = constrain_when_branch(
|
||||||
arena,
|
arena,
|
||||||
env,
|
env,
|
||||||
region,
|
region,
|
||||||
when_branch,
|
when_branch,
|
||||||
PExpected::ForReason(
|
pattern_expected,
|
||||||
PReason::WhenMatch {
|
|
||||||
index: HumanIndex::zero_based(index),
|
|
||||||
},
|
|
||||||
cond_type.shallow_clone(),
|
|
||||||
pattern_region,
|
|
||||||
),
|
|
||||||
Expected::ForReason(
|
Expected::ForReason(
|
||||||
Reason::WhenBranch {
|
Reason::WhenBranch {
|
||||||
index: HumanIndex::zero_based(index),
|
index: HumanIndex::zero_based(index),
|
||||||
|
@ -1296,7 +1285,7 @@ fn constrain_when_branch<'a>(
|
||||||
env: &mut Env,
|
env: &mut Env,
|
||||||
region: Region,
|
region: Region,
|
||||||
when_branch: &WhenBranch,
|
when_branch: &WhenBranch,
|
||||||
pattern_expected: PExpected<Type2>,
|
pattern_expected: impl Fn(HumanIndex, Region) -> PExpected<Type2>,
|
||||||
expr_expected: Expected<Type2>,
|
expr_expected: Expected<Type2>,
|
||||||
) -> Constraint<'a> {
|
) -> Constraint<'a> {
|
||||||
let when_expr = env.pool.get(when_branch.body);
|
let when_expr = env.pool.get(when_branch.body);
|
||||||
|
@ -1311,16 +1300,22 @@ fn constrain_when_branch<'a>(
|
||||||
|
|
||||||
// TODO investigate for error messages, is it better to unify all branches with a variable,
|
// TODO investigate for error messages, is it better to unify all branches with a variable,
|
||||||
// then unify that variable with the expectation?
|
// then unify that variable with the expectation?
|
||||||
for pattern_id in when_branch.patterns.iter_node_ids() {
|
for (sub_pattern, pattern_id) in when_branch.patterns.iter_node_ids().enumerate() {
|
||||||
let pattern = env.pool.get(pattern_id);
|
let pattern = env.pool.get(pattern_id);
|
||||||
|
|
||||||
|
let pattern_expected = pattern_expected(
|
||||||
|
HumanIndex::zero_based(sub_pattern),
|
||||||
|
// TODO: use the proper subpattern region. Not available to us right now.
|
||||||
|
region,
|
||||||
|
);
|
||||||
|
|
||||||
constrain_pattern(
|
constrain_pattern(
|
||||||
arena,
|
arena,
|
||||||
env,
|
env,
|
||||||
pattern,
|
pattern,
|
||||||
// loc_pattern.region,
|
// loc_pattern.region,
|
||||||
region,
|
region,
|
||||||
pattern_expected.shallow_clone(),
|
pattern_expected,
|
||||||
&mut state,
|
&mut state,
|
||||||
true,
|
true,
|
||||||
);
|
);
|
||||||
|
@ -1617,27 +1612,6 @@ pub fn constrain_pattern<'a>(
|
||||||
} => {
|
} => {
|
||||||
let tag_name = TagName::Global(name.as_str(env.pool).into());
|
let tag_name = TagName::Global(name.as_str(env.pool).into());
|
||||||
|
|
||||||
constrain_tag_pattern(
|
|
||||||
arena,
|
|
||||||
env,
|
|
||||||
region,
|
|
||||||
expected,
|
|
||||||
state,
|
|
||||||
*whole_var,
|
|
||||||
*ext_var,
|
|
||||||
arguments,
|
|
||||||
tag_name,
|
|
||||||
destruct_position,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
PrivateTag {
|
|
||||||
whole_var,
|
|
||||||
ext_var,
|
|
||||||
tag_name: name,
|
|
||||||
arguments,
|
|
||||||
} => {
|
|
||||||
let tag_name = TagName::Private(*name);
|
|
||||||
|
|
||||||
constrain_tag_pattern(
|
constrain_tag_pattern(
|
||||||
arena,
|
arena,
|
||||||
env,
|
env,
|
||||||
|
@ -1881,19 +1855,9 @@ fn num_float(pool: &mut Pool, range: TypeId) -> Type2 {
|
||||||
fn num_floatingpoint(pool: &mut Pool, range: TypeId) -> Type2 {
|
fn num_floatingpoint(pool: &mut Pool, range: TypeId) -> Type2 {
|
||||||
let range_type = pool.get(range);
|
let range_type = pool.get(range);
|
||||||
|
|
||||||
let alias_content = Type2::TagUnion(
|
let alias_content = range_type.shallow_clone();
|
||||||
PoolVec::new(
|
|
||||||
vec![(
|
|
||||||
TagName::Private(Symbol::NUM_AT_FLOATINGPOINT),
|
|
||||||
PoolVec::new(vec![range_type.shallow_clone()].into_iter(), pool),
|
|
||||||
)]
|
|
||||||
.into_iter(),
|
|
||||||
pool,
|
|
||||||
),
|
|
||||||
pool.add(Type2::EmptyTagUnion),
|
|
||||||
);
|
|
||||||
|
|
||||||
Type2::Alias(
|
Type2::Opaque(
|
||||||
Symbol::NUM_FLOATINGPOINT,
|
Symbol::NUM_FLOATINGPOINT,
|
||||||
PoolVec::new(vec![(PoolStr::new("range", pool), range)].into_iter(), pool),
|
PoolVec::new(vec![(PoolStr::new("range", pool), range)].into_iter(), pool),
|
||||||
pool.add(alias_content),
|
pool.add(alias_content),
|
||||||
|
@ -1917,37 +1881,16 @@ fn num_int(pool: &mut Pool, range: TypeId) -> Type2 {
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
fn _num_signed64(pool: &mut Pool) -> Type2 {
|
fn _num_signed64(pool: &mut Pool) -> Type2 {
|
||||||
let alias_content = Type2::TagUnion(
|
|
||||||
PoolVec::new(
|
|
||||||
vec![(
|
|
||||||
TagName::Private(Symbol::NUM_AT_SIGNED64),
|
|
||||||
PoolVec::empty(pool),
|
|
||||||
)]
|
|
||||||
.into_iter(),
|
|
||||||
pool,
|
|
||||||
),
|
|
||||||
pool.add(Type2::EmptyTagUnion),
|
|
||||||
);
|
|
||||||
|
|
||||||
Type2::Alias(
|
Type2::Alias(
|
||||||
Symbol::NUM_SIGNED64,
|
Symbol::NUM_SIGNED64,
|
||||||
PoolVec::empty(pool),
|
PoolVec::empty(pool),
|
||||||
pool.add(alias_content),
|
pool.add(Type2::EmptyTagUnion),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
fn num_unsigned32(pool: &mut Pool) -> Type2 {
|
fn num_unsigned32(pool: &mut Pool) -> Type2 {
|
||||||
let alias_content = Type2::TagUnion(
|
let alias_content = Type2::EmptyTagUnion;
|
||||||
PoolVec::new(
|
|
||||||
std::iter::once((
|
|
||||||
TagName::Private(Symbol::NUM_UNSIGNED32),
|
|
||||||
PoolVec::empty(pool),
|
|
||||||
)),
|
|
||||||
pool,
|
|
||||||
),
|
|
||||||
pool.add(Type2::EmptyTagUnion),
|
|
||||||
);
|
|
||||||
|
|
||||||
Type2::Alias(
|
Type2::Alias(
|
||||||
Symbol::NUM_UNSIGNED32,
|
Symbol::NUM_UNSIGNED32,
|
||||||
|
@ -1960,19 +1903,9 @@ fn num_unsigned32(pool: &mut Pool) -> Type2 {
|
||||||
fn _num_integer(pool: &mut Pool, range: TypeId) -> Type2 {
|
fn _num_integer(pool: &mut Pool, range: TypeId) -> Type2 {
|
||||||
let range_type = pool.get(range);
|
let range_type = pool.get(range);
|
||||||
|
|
||||||
let alias_content = Type2::TagUnion(
|
let alias_content = range_type.shallow_clone();
|
||||||
PoolVec::new(
|
|
||||||
vec![(
|
|
||||||
TagName::Private(Symbol::NUM_AT_INTEGER),
|
|
||||||
PoolVec::new(vec![range_type.shallow_clone()].into_iter(), pool),
|
|
||||||
)]
|
|
||||||
.into_iter(),
|
|
||||||
pool,
|
|
||||||
),
|
|
||||||
pool.add(Type2::EmptyTagUnion),
|
|
||||||
);
|
|
||||||
|
|
||||||
Type2::Alias(
|
Type2::Opaque(
|
||||||
Symbol::NUM_INTEGER,
|
Symbol::NUM_INTEGER,
|
||||||
PoolVec::new(vec![(PoolStr::new("range", pool), range)].into_iter(), pool),
|
PoolVec::new(vec![(PoolStr::new("range", pool), range)].into_iter(), pool),
|
||||||
pool.add(alias_content),
|
pool.add(alias_content),
|
||||||
|
@ -1983,19 +1916,9 @@ fn _num_integer(pool: &mut Pool, range: TypeId) -> Type2 {
|
||||||
fn num_num(pool: &mut Pool, type_id: TypeId) -> Type2 {
|
fn num_num(pool: &mut Pool, type_id: TypeId) -> Type2 {
|
||||||
let range_type = pool.get(type_id);
|
let range_type = pool.get(type_id);
|
||||||
|
|
||||||
let alias_content = Type2::TagUnion(
|
let alias_content = range_type.shallow_clone();
|
||||||
PoolVec::new(
|
|
||||||
vec![(
|
|
||||||
TagName::Private(Symbol::NUM_AT_NUM),
|
|
||||||
PoolVec::new(vec![range_type.shallow_clone()].into_iter(), pool),
|
|
||||||
)]
|
|
||||||
.into_iter(),
|
|
||||||
pool,
|
|
||||||
),
|
|
||||||
pool.add(Type2::EmptyTagUnion),
|
|
||||||
);
|
|
||||||
|
|
||||||
Type2::Alias(
|
Type2::Opaque(
|
||||||
Symbol::NUM_NUM,
|
Symbol::NUM_NUM,
|
||||||
PoolVec::new(
|
PoolVec::new(
|
||||||
vec![(PoolStr::new("range", pool), type_id)].into_iter(),
|
vec![(PoolStr::new("range", pool), type_id)].into_iter(),
|
||||||
|
@ -2286,18 +2209,6 @@ pub mod test_constrain {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn constrain_private_tag() {
|
|
||||||
infer_eq(
|
|
||||||
indoc!(
|
|
||||||
r#"
|
|
||||||
@Foo
|
|
||||||
"#
|
|
||||||
),
|
|
||||||
"[ @Foo ]*",
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn constrain_call_and_accessor() {
|
fn constrain_call_and_accessor() {
|
||||||
infer_eq(
|
infer_eq(
|
||||||
|
|
|
@ -154,12 +154,6 @@ pub enum Expr2 {
|
||||||
ext_var: Variable, // 4B
|
ext_var: Variable, // 4B
|
||||||
arguments: PoolVec<(Variable, ExprId)>, // 8B
|
arguments: PoolVec<(Variable, ExprId)>, // 8B
|
||||||
},
|
},
|
||||||
PrivateTag {
|
|
||||||
name: Symbol, // 8B
|
|
||||||
variant_var: Variable, // 4B
|
|
||||||
ext_var: Variable, // 4B
|
|
||||||
arguments: PoolVec<(Variable, ExprId)>, // 8B
|
|
||||||
},
|
|
||||||
Blank, // Rendered as empty box in editor
|
Blank, // Rendered as empty box in editor
|
||||||
|
|
||||||
// Compiles, but will crash if reached
|
// Compiles, but will crash if reached
|
||||||
|
|
|
@ -185,20 +185,6 @@ pub fn expr_to_expr2<'a>(
|
||||||
Output::default(),
|
Output::default(),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
PrivateTag(name) => {
|
|
||||||
// a private tag without any arguments
|
|
||||||
let ident_id = env.ident_ids.get_or_insert(&(*name).into());
|
|
||||||
let name = Symbol::new(env.home, ident_id);
|
|
||||||
(
|
|
||||||
Expr2::PrivateTag {
|
|
||||||
name,
|
|
||||||
variant_var: env.var_store.fresh(),
|
|
||||||
ext_var: env.var_store.fresh(),
|
|
||||||
arguments: PoolVec::empty(env.pool),
|
|
||||||
},
|
|
||||||
Output::default(),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
RecordUpdate {
|
RecordUpdate {
|
||||||
fields,
|
fields,
|
||||||
|
@ -568,17 +554,6 @@ pub fn expr_to_expr2<'a>(
|
||||||
name,
|
name,
|
||||||
arguments: args,
|
arguments: args,
|
||||||
},
|
},
|
||||||
Expr2::PrivateTag {
|
|
||||||
variant_var,
|
|
||||||
ext_var,
|
|
||||||
name,
|
|
||||||
..
|
|
||||||
} => Expr2::PrivateTag {
|
|
||||||
variant_var,
|
|
||||||
ext_var,
|
|
||||||
name,
|
|
||||||
arguments: args,
|
|
||||||
},
|
|
||||||
_ => {
|
_ => {
|
||||||
// This could be something like ((if True then fn1 else fn2) arg1 arg2).
|
// This could be something like ((if True then fn1 else fn2) arg1 arg2).
|
||||||
let fn_expr_id = env.add(fn_expr, fn_region);
|
let fn_expr_id = env.add(fn_expr, fn_region);
|
||||||
|
|
|
@ -47,12 +47,6 @@ pub enum Pattern2 {
|
||||||
tag_name: PoolStr, // 8B
|
tag_name: PoolStr, // 8B
|
||||||
arguments: PoolVec<(Variable, PatternId)>, // 8B
|
arguments: PoolVec<(Variable, PatternId)>, // 8B
|
||||||
},
|
},
|
||||||
PrivateTag {
|
|
||||||
whole_var: Variable, // 4B
|
|
||||||
ext_var: Variable, // 4B
|
|
||||||
tag_name: Symbol, // 8B
|
|
||||||
arguments: PoolVec<(Variable, PatternId)>, // 8B
|
|
||||||
},
|
|
||||||
RecordDestructure {
|
RecordDestructure {
|
||||||
whole_var: Variable, // 4B
|
whole_var: Variable, // 4B
|
||||||
ext_var: Variable, // 4B
|
ext_var: Variable, // 4B
|
||||||
|
@ -280,17 +274,6 @@ pub fn to_pattern2<'a>(
|
||||||
arguments: PoolVec::empty(env.pool),
|
arguments: PoolVec::empty(env.pool),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
PrivateTag(name) => {
|
|
||||||
let ident_id = env.ident_ids.get_or_insert(&(*name).into());
|
|
||||||
|
|
||||||
// Canonicalize the tag's name.
|
|
||||||
Pattern2::PrivateTag {
|
|
||||||
whole_var: env.var_store.fresh(),
|
|
||||||
ext_var: env.var_store.fresh(),
|
|
||||||
tag_name: Symbol::new(env.home, ident_id),
|
|
||||||
arguments: PoolVec::empty(env.pool),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
OpaqueRef(..) => todo_opaques!(),
|
OpaqueRef(..) => todo_opaques!(),
|
||||||
|
|
||||||
|
@ -319,16 +302,6 @@ pub fn to_pattern2<'a>(
|
||||||
tag_name: PoolStr::new(name, env.pool),
|
tag_name: PoolStr::new(name, env.pool),
|
||||||
arguments: can_patterns,
|
arguments: can_patterns,
|
||||||
},
|
},
|
||||||
PrivateTag(name) => {
|
|
||||||
let ident_id = env.ident_ids.get_or_insert(&name.into());
|
|
||||||
|
|
||||||
Pattern2::PrivateTag {
|
|
||||||
whole_var: env.var_store.fresh(),
|
|
||||||
ext_var: env.var_store.fresh(),
|
|
||||||
tag_name: Symbol::new(env.home, ident_id),
|
|
||||||
arguments: can_patterns,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
_ => unreachable!("Other patterns cannot be applied"),
|
_ => unreachable!("Other patterns cannot be applied"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -506,7 +479,7 @@ pub fn symbols_from_pattern(pool: &Pool, initial: &Pattern2) -> Vec<Symbol> {
|
||||||
symbols.push(*symbol);
|
symbols.push(*symbol);
|
||||||
}
|
}
|
||||||
|
|
||||||
GlobalTag { arguments, .. } | PrivateTag { arguments, .. } => {
|
GlobalTag { arguments, .. } => {
|
||||||
for (_, pat_id) in arguments.iter(pool) {
|
for (_, pat_id) in arguments.iter(pool) {
|
||||||
let pat = pool.get(*pat_id);
|
let pat = pool.get(*pat_id);
|
||||||
stack.push(pat);
|
stack.push(pat);
|
||||||
|
@ -567,7 +540,7 @@ pub fn symbols_and_variables_from_pattern(
|
||||||
symbols.push((*symbol, variable));
|
symbols.push((*symbol, variable));
|
||||||
}
|
}
|
||||||
|
|
||||||
GlobalTag { arguments, .. } | PrivateTag { arguments, .. } => {
|
GlobalTag { arguments, .. } => {
|
||||||
for (var, pat_id) in arguments.iter(pool) {
|
for (var, pat_id) in arguments.iter(pool) {
|
||||||
let pat = pool.get(*pat_id);
|
let pat = pool.get(*pat_id);
|
||||||
stack.push((*var, pat));
|
stack.push((*var, pat));
|
||||||
|
|
|
@ -24,6 +24,7 @@ pub enum Type2 {
|
||||||
Variable(Variable), // 4B
|
Variable(Variable), // 4B
|
||||||
|
|
||||||
Alias(Symbol, PoolVec<(PoolStr, TypeId)>, TypeId), // 24B = 8B + 8B + 4B + pad
|
Alias(Symbol, PoolVec<(PoolStr, TypeId)>, TypeId), // 24B = 8B + 8B + 4B + pad
|
||||||
|
Opaque(Symbol, PoolVec<(PoolStr, TypeId)>, TypeId), // 24B = 8B + 8B + 4B + pad
|
||||||
AsAlias(Symbol, PoolVec<(PoolStr, TypeId)>, TypeId), // 24B = 8B + 8B + 4B + pad
|
AsAlias(Symbol, PoolVec<(PoolStr, TypeId)>, TypeId), // 24B = 8B + 8B + 4B + pad
|
||||||
|
|
||||||
// 24B
|
// 24B
|
||||||
|
@ -74,6 +75,9 @@ impl ShallowClone for Type2 {
|
||||||
Self::Alias(symbol, args, alias_type_id) => {
|
Self::Alias(symbol, args, alias_type_id) => {
|
||||||
Self::Alias(*symbol, args.shallow_clone(), alias_type_id.clone())
|
Self::Alias(*symbol, args.shallow_clone(), alias_type_id.clone())
|
||||||
}
|
}
|
||||||
|
Self::Opaque(symbol, args, alias_type_id) => {
|
||||||
|
Self::Opaque(*symbol, args.shallow_clone(), alias_type_id.clone())
|
||||||
|
}
|
||||||
Self::Record(fields, ext_id) => Self::Record(fields.shallow_clone(), ext_id.clone()),
|
Self::Record(fields, ext_id) => Self::Record(fields.shallow_clone(), ext_id.clone()),
|
||||||
Self::Function(args, closure_type_id, ret_type_id) => Self::Function(
|
Self::Function(args, closure_type_id, ret_type_id) => Self::Function(
|
||||||
args.shallow_clone(),
|
args.shallow_clone(),
|
||||||
|
@ -101,7 +105,7 @@ impl Type2 {
|
||||||
Variable(v) => {
|
Variable(v) => {
|
||||||
result.insert(*v);
|
result.insert(*v);
|
||||||
}
|
}
|
||||||
Alias(_, _, actual) | AsAlias(_, _, actual) => {
|
Alias(_, _, actual) | AsAlias(_, _, actual) | Opaque(_, _, actual) => {
|
||||||
stack.push(pool.get(*actual));
|
stack.push(pool.get(*actual));
|
||||||
}
|
}
|
||||||
HostExposedAlias {
|
HostExposedAlias {
|
||||||
|
@ -702,21 +706,6 @@ fn can_tags<'a>(
|
||||||
|
|
||||||
break 'inner tag_name;
|
break 'inner tag_name;
|
||||||
}
|
}
|
||||||
Tag::Private { name, args } => {
|
|
||||||
let ident_id = env.ident_ids.get_or_insert(&name.value.into());
|
|
||||||
let symbol = Symbol::new(env.home, ident_id);
|
|
||||||
|
|
||||||
let arg_types = PoolVec::with_capacity(args.len() as u32, env.pool);
|
|
||||||
|
|
||||||
for (type_id, loc_arg) in arg_types.iter_node_ids().zip(args.iter()) {
|
|
||||||
as_type_id(env, scope, rigids, type_id, &loc_arg.value, loc_arg.region);
|
|
||||||
}
|
|
||||||
|
|
||||||
let tag_name = TagName::Private(symbol);
|
|
||||||
tag_types.push((tag_name.clone(), arg_types));
|
|
||||||
|
|
||||||
break 'inner tag_name;
|
|
||||||
}
|
|
||||||
Tag::SpaceBefore(nested, _) | Tag::SpaceAfter(nested, _) => {
|
Tag::SpaceBefore(nested, _) | Tag::SpaceAfter(nested, _) => {
|
||||||
// check the nested tag instead
|
// check the nested tag instead
|
||||||
tag = nested;
|
tag = nested;
|
||||||
|
|
|
@ -3,6 +3,7 @@
|
||||||
use bumpalo::Bump;
|
use bumpalo::Bump;
|
||||||
use roc_can::expected::{Expected, PExpected};
|
use roc_can::expected::{Expected, PExpected};
|
||||||
use roc_collections::all::{BumpMap, BumpMapDefault, MutMap};
|
use roc_collections::all::{BumpMap, BumpMapDefault, MutMap};
|
||||||
|
use roc_error_macros::internal_error;
|
||||||
use roc_module::ident::TagName;
|
use roc_module::ident::TagName;
|
||||||
use roc_module::symbol::Symbol;
|
use roc_module::symbol::Symbol;
|
||||||
use roc_region::all::{Loc, Region};
|
use roc_region::all::{Loc, Region};
|
||||||
|
@ -868,7 +869,7 @@ fn type_to_variable<'a>(
|
||||||
register(subs, rank, pools, content)
|
register(subs, rank, pools, content)
|
||||||
}
|
}
|
||||||
|
|
||||||
Alias(symbol, args, alias_type_id) => {
|
Alias(symbol, args, alias_type_id) | Opaque(symbol, args, alias_type_id) => {
|
||||||
// TODO cache in uniqueness inference gives problems! all Int's get the same uniqueness var!
|
// TODO cache in uniqueness inference gives problems! all Int's get the same uniqueness var!
|
||||||
// Cache aliases without type arguments. Commonly used aliases like `Int` would otherwise get O(n)
|
// Cache aliases without type arguments. Commonly used aliases like `Int` would otherwise get O(n)
|
||||||
// different variables (once for each occurrence). The recursion restriction is required
|
// different variables (once for each occurrence). The recursion restriction is required
|
||||||
|
@ -910,8 +911,12 @@ fn type_to_variable<'a>(
|
||||||
|
|
||||||
let alias_var = type_to_variable(arena, mempool, subs, rank, pools, cached, alias_type);
|
let alias_var = type_to_variable(arena, mempool, subs, rank, pools, cached, alias_type);
|
||||||
|
|
||||||
// TODO(opaques): take opaques into account
|
let kind = match typ {
|
||||||
let content = Content::Alias(*symbol, arg_vars, alias_var, AliasKind::Structural);
|
Alias(..) => AliasKind::Structural,
|
||||||
|
Opaque(..) => AliasKind::Opaque,
|
||||||
|
_ => internal_error!(),
|
||||||
|
};
|
||||||
|
let content = Content::Alias(*symbol, arg_vars, alias_var, kind);
|
||||||
|
|
||||||
let result = register(subs, rank, pools, content);
|
let result = register(subs, rank, pools, content);
|
||||||
|
|
||||||
|
|
|
@ -66,7 +66,7 @@ const_format = "0.2.22"
|
||||||
bumpalo = { version = "3.8.0", features = ["collections"] }
|
bumpalo = { version = "3.8.0", features = ["collections"] }
|
||||||
mimalloc = { version = "0.1.26", default-features = false }
|
mimalloc = { version = "0.1.26", default-features = false }
|
||||||
|
|
||||||
target-lexicon = "0.12.2"
|
target-lexicon = "0.12.3"
|
||||||
tempfile = "3.2.0"
|
tempfile = "3.2.0"
|
||||||
wasmer-wasi = { version = "2.0.0", optional = true }
|
wasmer-wasi = { version = "2.0.0", optional = true }
|
||||||
|
|
||||||
|
|
|
@ -618,7 +618,6 @@ impl<'a> RemoveSpaces<'a> for Expr<'a> {
|
||||||
Expr::Var { module_name, ident } => Expr::Var { module_name, ident },
|
Expr::Var { module_name, ident } => Expr::Var { module_name, ident },
|
||||||
Expr::Underscore(a) => Expr::Underscore(a),
|
Expr::Underscore(a) => Expr::Underscore(a),
|
||||||
Expr::GlobalTag(a) => Expr::GlobalTag(a),
|
Expr::GlobalTag(a) => Expr::GlobalTag(a),
|
||||||
Expr::PrivateTag(a) => Expr::PrivateTag(a),
|
|
||||||
Expr::OpaqueRef(a) => Expr::OpaqueRef(a),
|
Expr::OpaqueRef(a) => Expr::OpaqueRef(a),
|
||||||
Expr::Closure(a, b) => Expr::Closure(
|
Expr::Closure(a, b) => Expr::Closure(
|
||||||
arena.alloc(a.remove_spaces(arena)),
|
arena.alloc(a.remove_spaces(arena)),
|
||||||
|
@ -670,7 +669,6 @@ impl<'a> RemoveSpaces<'a> for Pattern<'a> {
|
||||||
match *self {
|
match *self {
|
||||||
Pattern::Identifier(a) => Pattern::Identifier(a),
|
Pattern::Identifier(a) => Pattern::Identifier(a),
|
||||||
Pattern::GlobalTag(a) => Pattern::GlobalTag(a),
|
Pattern::GlobalTag(a) => Pattern::GlobalTag(a),
|
||||||
Pattern::PrivateTag(a) => Pattern::PrivateTag(a),
|
|
||||||
Pattern::OpaqueRef(a) => Pattern::OpaqueRef(a),
|
Pattern::OpaqueRef(a) => Pattern::OpaqueRef(a),
|
||||||
Pattern::Apply(a, b) => Pattern::Apply(
|
Pattern::Apply(a, b) => Pattern::Apply(
|
||||||
arena.alloc(a.remove_spaces(arena)),
|
arena.alloc(a.remove_spaces(arena)),
|
||||||
|
@ -757,10 +755,6 @@ impl<'a> RemoveSpaces<'a> for Tag<'a> {
|
||||||
name: name.remove_spaces(arena),
|
name: name.remove_spaces(arena),
|
||||||
args: args.remove_spaces(arena),
|
args: args.remove_spaces(arena),
|
||||||
},
|
},
|
||||||
Tag::Private { name, args } => Tag::Private {
|
|
||||||
name: name.remove_spaces(arena),
|
|
||||||
args: args.remove_spaces(arena),
|
|
||||||
},
|
|
||||||
Tag::Malformed(a) => Tag::Malformed(a),
|
Tag::Malformed(a) => Tag::Malformed(a),
|
||||||
Tag::SpaceBefore(a, _) => a.remove_spaces(arena),
|
Tag::SpaceBefore(a, _) => a.remove_spaces(arena),
|
||||||
Tag::SpaceAfter(a, _) => a.remove_spaces(arena),
|
Tag::SpaceAfter(a, _) => a.remove_spaces(arena),
|
||||||
|
|
|
@ -256,8 +256,8 @@ mod cli_run {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
"hello-gui" => {
|
"hello-gui" | "breakout" => {
|
||||||
// Since this one requires opening a window, we do `roc build` on it but don't run it.
|
// Since these require opening a window, we do `roc build` on them but don't run them.
|
||||||
build_example(&file_name, &["--optimize"]);
|
build_example(&file_name, &["--optimize"]);
|
||||||
|
|
||||||
return;
|
return;
|
||||||
|
@ -394,6 +394,14 @@ mod cli_run {
|
||||||
expected_ending: "",
|
expected_ending: "",
|
||||||
use_valgrind: false,
|
use_valgrind: false,
|
||||||
},
|
},
|
||||||
|
breakout:"breakout" => Example {
|
||||||
|
filename: "breakout.roc",
|
||||||
|
executable_filename: "breakout",
|
||||||
|
stdin: &[],
|
||||||
|
input_file: None,
|
||||||
|
expected_ending: "",
|
||||||
|
use_valgrind: false,
|
||||||
|
},
|
||||||
quicksort:"algorithms" => Example {
|
quicksort:"algorithms" => Example {
|
||||||
filename: "quicksort.roc",
|
filename: "quicksort.roc",
|
||||||
executable_filename: "quicksort",
|
executable_filename: "quicksort",
|
||||||
|
|
|
@ -167,6 +167,20 @@ For a more detailed understanding of the compilation phases, see the `Phase`, `B
|
||||||
|
|
||||||
## Debugging intermediate representations
|
## Debugging intermediate representations
|
||||||
|
|
||||||
|
### Debugging the typechecker
|
||||||
|
|
||||||
|
Setting the following environment variables:
|
||||||
|
|
||||||
|
- `ROC_PRINT_UNIFICATIONS` prints all type unifications that are done,
|
||||||
|
before and after the unification.
|
||||||
|
- `ROC_PRINT_MISMATCHES` prints all type mismatches hit during unification.
|
||||||
|
- `ROC_PRETTY_PRINT_ALIAS_CONTENTS` expands the contents of aliases during
|
||||||
|
pretty-printing of types.
|
||||||
|
|
||||||
|
Note that this is only relevant during debug builds. Eventually we should have
|
||||||
|
some better debugging tools here, see https://github.com/rtfeldman/roc/issues/2486
|
||||||
|
for one.
|
||||||
|
|
||||||
### The mono IR
|
### The mono IR
|
||||||
|
|
||||||
If you observe a miscomplication, you may first want to check the generated mono
|
If you observe a miscomplication, you may first want to check the generated mono
|
||||||
|
|
|
@ -279,7 +279,8 @@ fn build_entry_point(
|
||||||
let block = builder.add_block();
|
let block = builder.add_block();
|
||||||
|
|
||||||
// to the modelling language, the arguments appear out of thin air
|
// to the modelling language, the arguments appear out of thin air
|
||||||
let argument_type = build_tuple_type(&mut builder, layout.arguments)?;
|
let argument_type =
|
||||||
|
build_tuple_type(&mut builder, layout.arguments, &WhenRecursive::Unreachable)?;
|
||||||
|
|
||||||
// does not make any assumptions about the input
|
// does not make any assumptions about the input
|
||||||
// let argument = builder.add_unknown_with(block, &[], argument_type)?;
|
// let argument = builder.add_unknown_with(block, &[], argument_type)?;
|
||||||
|
@ -308,7 +309,11 @@ fn build_entry_point(
|
||||||
|
|
||||||
let block = builder.add_block();
|
let block = builder.add_block();
|
||||||
|
|
||||||
let type_id = layout_spec(&mut builder, &Layout::struct_no_name_order(layouts))?;
|
let type_id = layout_spec(
|
||||||
|
&mut builder,
|
||||||
|
&Layout::struct_no_name_order(layouts),
|
||||||
|
&WhenRecursive::Unreachable,
|
||||||
|
)?;
|
||||||
|
|
||||||
let argument = builder.add_unknown_with(block, &[], type_id)?;
|
let argument = builder.add_unknown_with(block, &[], type_id)?;
|
||||||
|
|
||||||
|
@ -352,8 +357,9 @@ fn proc_spec<'a>(proc: &Proc<'a>) -> Result<(FuncDef, MutSet<UnionLayout<'a>>)>
|
||||||
let arg_type_id = layout_spec(
|
let arg_type_id = layout_spec(
|
||||||
&mut builder,
|
&mut builder,
|
||||||
&Layout::struct_no_name_order(&argument_layouts),
|
&Layout::struct_no_name_order(&argument_layouts),
|
||||||
|
&WhenRecursive::Unreachable,
|
||||||
)?;
|
)?;
|
||||||
let ret_type_id = layout_spec(&mut builder, &proc.ret_layout)?;
|
let ret_type_id = layout_spec(&mut builder, &proc.ret_layout, &WhenRecursive::Unreachable)?;
|
||||||
|
|
||||||
let spec = builder.build(arg_type_id, ret_type_id, root)?;
|
let spec = builder.build(arg_type_id, ret_type_id, root)?;
|
||||||
|
|
||||||
|
@ -457,10 +463,14 @@ fn stmt_spec<'a>(
|
||||||
let mut type_ids = Vec::new();
|
let mut type_ids = Vec::new();
|
||||||
|
|
||||||
for p in parameters.iter() {
|
for p in parameters.iter() {
|
||||||
type_ids.push(layout_spec(builder, &p.layout)?);
|
type_ids.push(layout_spec(
|
||||||
|
builder,
|
||||||
|
&p.layout,
|
||||||
|
&WhenRecursive::Unreachable,
|
||||||
|
)?);
|
||||||
}
|
}
|
||||||
|
|
||||||
let ret_type_id = layout_spec(builder, layout)?;
|
let ret_type_id = layout_spec(builder, layout, &WhenRecursive::Unreachable)?;
|
||||||
|
|
||||||
let jp_arg_type_id = builder.add_tuple_type(&type_ids)?;
|
let jp_arg_type_id = builder.add_tuple_type(&type_ids)?;
|
||||||
|
|
||||||
|
@ -500,14 +510,14 @@ fn stmt_spec<'a>(
|
||||||
builder.add_sub_block(block, BlockExpr(cont_block, cont_value_id))
|
builder.add_sub_block(block, BlockExpr(cont_block, cont_value_id))
|
||||||
}
|
}
|
||||||
Jump(id, symbols) => {
|
Jump(id, symbols) => {
|
||||||
let ret_type_id = layout_spec(builder, layout)?;
|
let ret_type_id = layout_spec(builder, layout, &WhenRecursive::Unreachable)?;
|
||||||
let argument = build_tuple_value(builder, env, block, symbols)?;
|
let argument = build_tuple_value(builder, env, block, symbols)?;
|
||||||
|
|
||||||
let jpid = env.join_points[id];
|
let jpid = env.join_points[id];
|
||||||
builder.add_jump(block, jpid, argument, ret_type_id)
|
builder.add_jump(block, jpid, argument, ret_type_id)
|
||||||
}
|
}
|
||||||
RuntimeError(_) => {
|
RuntimeError(_) => {
|
||||||
let type_id = layout_spec(builder, layout)?;
|
let type_id = layout_spec(builder, layout, &WhenRecursive::Unreachable)?;
|
||||||
|
|
||||||
builder.add_terminate(block, type_id)
|
builder.add_terminate(block, type_id)
|
||||||
}
|
}
|
||||||
|
@ -556,11 +566,15 @@ fn build_recursive_tuple_type(
|
||||||
builder.add_tuple_type(&field_types)
|
builder.add_tuple_type(&field_types)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn build_tuple_type(builder: &mut impl TypeContext, layouts: &[Layout]) -> Result<TypeId> {
|
fn build_tuple_type(
|
||||||
|
builder: &mut impl TypeContext,
|
||||||
|
layouts: &[Layout],
|
||||||
|
when_recursive: &WhenRecursive,
|
||||||
|
) -> Result<TypeId> {
|
||||||
let mut field_types = Vec::new();
|
let mut field_types = Vec::new();
|
||||||
|
|
||||||
for field in layouts.iter() {
|
for field in layouts.iter() {
|
||||||
field_types.push(layout_spec(builder, field)?);
|
field_types.push(layout_spec(builder, field, when_recursive)?);
|
||||||
}
|
}
|
||||||
|
|
||||||
builder.add_tuple_type(&field_types)
|
builder.add_tuple_type(&field_types)
|
||||||
|
@ -691,7 +705,7 @@ fn call_spec(
|
||||||
.map(|symbol| env.symbols[symbol])
|
.map(|symbol| env.symbols[symbol])
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
let result_type = layout_spec(builder, ret_layout)?;
|
let result_type = layout_spec(builder, ret_layout, &WhenRecursive::Unreachable)?;
|
||||||
|
|
||||||
builder.add_unknown_with(block, &arguments, result_type)
|
builder.add_unknown_with(block, &arguments, result_type)
|
||||||
}
|
}
|
||||||
|
@ -761,7 +775,8 @@ fn call_spec(
|
||||||
};
|
};
|
||||||
|
|
||||||
let state_layout = argument_layouts[0];
|
let state_layout = argument_layouts[0];
|
||||||
let state_type = layout_spec(builder, &state_layout)?;
|
let state_type =
|
||||||
|
layout_spec(builder, &state_layout, &WhenRecursive::Unreachable)?;
|
||||||
let init_state = state;
|
let init_state = state;
|
||||||
|
|
||||||
add_loop(builder, block, state_type, init_state, loop_body)
|
add_loop(builder, block, state_type, init_state, loop_body)
|
||||||
|
@ -782,7 +797,8 @@ fn call_spec(
|
||||||
};
|
};
|
||||||
|
|
||||||
let state_layout = argument_layouts[0];
|
let state_layout = argument_layouts[0];
|
||||||
let state_type = layout_spec(builder, &state_layout)?;
|
let state_type =
|
||||||
|
layout_spec(builder, &state_layout, &WhenRecursive::Unreachable)?;
|
||||||
let init_state = state;
|
let init_state = state;
|
||||||
|
|
||||||
add_loop(builder, block, state_type, init_state, loop_body)
|
add_loop(builder, block, state_type, init_state, loop_body)
|
||||||
|
@ -806,7 +822,8 @@ fn call_spec(
|
||||||
};
|
};
|
||||||
|
|
||||||
let state_layout = argument_layouts[0];
|
let state_layout = argument_layouts[0];
|
||||||
let state_type = layout_spec(builder, &state_layout)?;
|
let state_type =
|
||||||
|
layout_spec(builder, &state_layout, &WhenRecursive::Unreachable)?;
|
||||||
let init_state = state;
|
let init_state = state;
|
||||||
|
|
||||||
add_loop(builder, block, state_type, init_state, loop_body)
|
add_loop(builder, block, state_type, init_state, loop_body)
|
||||||
|
@ -828,10 +845,12 @@ fn call_spec(
|
||||||
list_append(builder, block, update_mode_var, state, new_element)
|
list_append(builder, block, update_mode_var, state, new_element)
|
||||||
};
|
};
|
||||||
|
|
||||||
let output_element_type = layout_spec(builder, return_layout)?;
|
let output_element_type =
|
||||||
|
layout_spec(builder, return_layout, &WhenRecursive::Unreachable)?;
|
||||||
|
|
||||||
let state_layout = Layout::Builtin(Builtin::List(return_layout));
|
let state_layout = Layout::Builtin(Builtin::List(return_layout));
|
||||||
let state_type = layout_spec(builder, &state_layout)?;
|
let state_type =
|
||||||
|
layout_spec(builder, &state_layout, &WhenRecursive::Unreachable)?;
|
||||||
|
|
||||||
let init_state = new_list(builder, block, output_element_type)?;
|
let init_state = new_list(builder, block, output_element_type)?;
|
||||||
|
|
||||||
|
@ -851,10 +870,12 @@ fn call_spec(
|
||||||
list_append(builder, block, update_mode_var, state, new_element)
|
list_append(builder, block, update_mode_var, state, new_element)
|
||||||
};
|
};
|
||||||
|
|
||||||
let output_element_type = layout_spec(builder, return_layout)?;
|
let output_element_type =
|
||||||
|
layout_spec(builder, return_layout, &WhenRecursive::Unreachable)?;
|
||||||
|
|
||||||
let state_layout = Layout::Builtin(Builtin::List(return_layout));
|
let state_layout = Layout::Builtin(Builtin::List(return_layout));
|
||||||
let state_type = layout_spec(builder, &state_layout)?;
|
let state_type =
|
||||||
|
layout_spec(builder, &state_layout, &WhenRecursive::Unreachable)?;
|
||||||
|
|
||||||
let init_state = new_list(builder, block, output_element_type)?;
|
let init_state = new_list(builder, block, output_element_type)?;
|
||||||
|
|
||||||
|
@ -879,7 +900,8 @@ fn call_spec(
|
||||||
};
|
};
|
||||||
|
|
||||||
let state_layout = Layout::Builtin(Builtin::List(&argument_layouts[0]));
|
let state_layout = Layout::Builtin(Builtin::List(&argument_layouts[0]));
|
||||||
let state_type = layout_spec(builder, &state_layout)?;
|
let state_type =
|
||||||
|
layout_spec(builder, &state_layout, &WhenRecursive::Unreachable)?;
|
||||||
let init_state = list;
|
let init_state = list;
|
||||||
|
|
||||||
add_loop(builder, block, state_type, init_state, loop_body)
|
add_loop(builder, block, state_type, init_state, loop_body)
|
||||||
|
@ -903,10 +925,12 @@ fn call_spec(
|
||||||
list_append(builder, block, update_mode_var, state, new_element)
|
list_append(builder, block, update_mode_var, state, new_element)
|
||||||
};
|
};
|
||||||
|
|
||||||
let output_element_type = layout_spec(builder, return_layout)?;
|
let output_element_type =
|
||||||
|
layout_spec(builder, return_layout, &WhenRecursive::Unreachable)?;
|
||||||
|
|
||||||
let state_layout = Layout::Builtin(Builtin::List(return_layout));
|
let state_layout = Layout::Builtin(Builtin::List(return_layout));
|
||||||
let state_type = layout_spec(builder, &state_layout)?;
|
let state_type =
|
||||||
|
layout_spec(builder, &state_layout, &WhenRecursive::Unreachable)?;
|
||||||
|
|
||||||
let init_state = new_list(builder, block, output_element_type)?;
|
let init_state = new_list(builder, block, output_element_type)?;
|
||||||
|
|
||||||
|
@ -936,10 +960,12 @@ fn call_spec(
|
||||||
list_append(builder, block, update_mode_var, state, new_element)
|
list_append(builder, block, update_mode_var, state, new_element)
|
||||||
};
|
};
|
||||||
|
|
||||||
let output_element_type = layout_spec(builder, return_layout)?;
|
let output_element_type =
|
||||||
|
layout_spec(builder, return_layout, &WhenRecursive::Unreachable)?;
|
||||||
|
|
||||||
let state_layout = Layout::Builtin(Builtin::List(return_layout));
|
let state_layout = Layout::Builtin(Builtin::List(return_layout));
|
||||||
let state_type = layout_spec(builder, &state_layout)?;
|
let state_type =
|
||||||
|
layout_spec(builder, &state_layout, &WhenRecursive::Unreachable)?;
|
||||||
|
|
||||||
let init_state = new_list(builder, block, output_element_type)?;
|
let init_state = new_list(builder, block, output_element_type)?;
|
||||||
|
|
||||||
|
@ -975,10 +1001,12 @@ fn call_spec(
|
||||||
list_append(builder, block, update_mode_var, state, new_element)
|
list_append(builder, block, update_mode_var, state, new_element)
|
||||||
};
|
};
|
||||||
|
|
||||||
let output_element_type = layout_spec(builder, return_layout)?;
|
let output_element_type =
|
||||||
|
layout_spec(builder, return_layout, &WhenRecursive::Unreachable)?;
|
||||||
|
|
||||||
let state_layout = Layout::Builtin(Builtin::List(return_layout));
|
let state_layout = Layout::Builtin(Builtin::List(return_layout));
|
||||||
let state_type = layout_spec(builder, &state_layout)?;
|
let state_type =
|
||||||
|
layout_spec(builder, &state_layout, &WhenRecursive::Unreachable)?;
|
||||||
|
|
||||||
let init_state = new_list(builder, block, output_element_type)?;
|
let init_state = new_list(builder, block, output_element_type)?;
|
||||||
|
|
||||||
|
@ -1010,7 +1038,8 @@ fn call_spec(
|
||||||
};
|
};
|
||||||
|
|
||||||
let state_layout = Layout::Builtin(Builtin::List(&argument_layouts[0]));
|
let state_layout = Layout::Builtin(Builtin::List(&argument_layouts[0]));
|
||||||
let state_type = layout_spec(builder, &state_layout)?;
|
let state_type =
|
||||||
|
layout_spec(builder, &state_layout, &WhenRecursive::Unreachable)?;
|
||||||
let init_state = list;
|
let init_state = list;
|
||||||
|
|
||||||
add_loop(builder, block, state_type, init_state, loop_body)
|
add_loop(builder, block, state_type, init_state, loop_body)
|
||||||
|
@ -1087,11 +1116,13 @@ fn call_spec(
|
||||||
)
|
)
|
||||||
};
|
};
|
||||||
|
|
||||||
let output_element_type = layout_spec(builder, &output_element_layout)?;
|
let output_element_type =
|
||||||
|
layout_spec(builder, &output_element_layout, &WhenRecursive::Unreachable)?;
|
||||||
let init_state = new_list(builder, block, output_element_type)?;
|
let init_state = new_list(builder, block, output_element_type)?;
|
||||||
|
|
||||||
let state_layout = Layout::Builtin(Builtin::List(&output_element_layout));
|
let state_layout = Layout::Builtin(Builtin::List(&output_element_layout));
|
||||||
let state_type = layout_spec(builder, &state_layout)?;
|
let state_type =
|
||||||
|
layout_spec(builder, &state_layout, &WhenRecursive::Unreachable)?;
|
||||||
|
|
||||||
add_loop(builder, block, state_type, init_state, loop_body)
|
add_loop(builder, block, state_type, init_state, loop_body)
|
||||||
}
|
}
|
||||||
|
@ -1108,7 +1139,8 @@ fn call_spec(
|
||||||
};
|
};
|
||||||
|
|
||||||
let state_layout = Layout::Builtin(Builtin::Bool);
|
let state_layout = Layout::Builtin(Builtin::Bool);
|
||||||
let state_type = layout_spec(builder, &state_layout)?;
|
let state_type =
|
||||||
|
layout_spec(builder, &state_layout, &WhenRecursive::Unreachable)?;
|
||||||
|
|
||||||
let init_state = new_num(builder, block)?;
|
let init_state = new_num(builder, block)?;
|
||||||
|
|
||||||
|
@ -1127,7 +1159,8 @@ fn call_spec(
|
||||||
};
|
};
|
||||||
|
|
||||||
let state_layout = Layout::Builtin(Builtin::Bool);
|
let state_layout = Layout::Builtin(Builtin::Bool);
|
||||||
let state_type = layout_spec(builder, &state_layout)?;
|
let state_type =
|
||||||
|
layout_spec(builder, &state_layout, &WhenRecursive::Unreachable)?;
|
||||||
|
|
||||||
let init_state = new_num(builder, block)?;
|
let init_state = new_num(builder, block)?;
|
||||||
|
|
||||||
|
@ -1139,7 +1172,8 @@ fn call_spec(
|
||||||
// ListFindUnsafe returns { value: v, found: Bool=Int1 }
|
// ListFindUnsafe returns { value: v, found: Bool=Int1 }
|
||||||
let output_layouts = vec![argument_layouts[0], Layout::Builtin(Builtin::Bool)];
|
let output_layouts = vec![argument_layouts[0], Layout::Builtin(Builtin::Bool)];
|
||||||
let output_layout = Layout::struct_no_name_order(&output_layouts);
|
let output_layout = Layout::struct_no_name_order(&output_layouts);
|
||||||
let output_type = layout_spec(builder, &output_layout)?;
|
let output_type =
|
||||||
|
layout_spec(builder, &output_layout, &WhenRecursive::Unreachable)?;
|
||||||
|
|
||||||
let loop_body = |builder: &mut FuncDefBuilder, block, output| {
|
let loop_body = |builder: &mut FuncDefBuilder, block, output| {
|
||||||
let bag = builder.add_get_tuple_field(block, list, LIST_BAG_INDEX)?;
|
let bag = builder.add_get_tuple_field(block, list, LIST_BAG_INDEX)?;
|
||||||
|
@ -1201,7 +1235,7 @@ fn lowlevel_spec(
|
||||||
) -> Result<ValueId> {
|
) -> Result<ValueId> {
|
||||||
use LowLevel::*;
|
use LowLevel::*;
|
||||||
|
|
||||||
let type_id = layout_spec(builder, layout)?;
|
let type_id = layout_spec(builder, layout, &WhenRecursive::Unreachable)?;
|
||||||
let mode = update_mode.to_bytes();
|
let mode = update_mode.to_bytes();
|
||||||
let update_mode_var = UpdateModeVar(&mode);
|
let update_mode_var = UpdateModeVar(&mode);
|
||||||
|
|
||||||
|
@ -1323,8 +1357,8 @@ fn lowlevel_spec(
|
||||||
}
|
}
|
||||||
DictEmpty => match layout {
|
DictEmpty => match layout {
|
||||||
Layout::Builtin(Builtin::Dict(key_layout, value_layout)) => {
|
Layout::Builtin(Builtin::Dict(key_layout, value_layout)) => {
|
||||||
let key_id = layout_spec(builder, key_layout)?;
|
let key_id = layout_spec(builder, key_layout, &WhenRecursive::Unreachable)?;
|
||||||
let value_id = layout_spec(builder, value_layout)?;
|
let value_id = layout_spec(builder, value_layout, &WhenRecursive::Unreachable)?;
|
||||||
new_dict(builder, block, key_id, value_id)
|
new_dict(builder, block, key_id, value_id)
|
||||||
}
|
}
|
||||||
_ => unreachable!("empty array does not have a list layout"),
|
_ => unreachable!("empty array does not have a list layout"),
|
||||||
|
@ -1367,7 +1401,7 @@ fn lowlevel_spec(
|
||||||
// TODO overly pessimstic
|
// TODO overly pessimstic
|
||||||
let arguments: Vec<_> = arguments.iter().map(|symbol| env.symbols[symbol]).collect();
|
let arguments: Vec<_> = arguments.iter().map(|symbol| env.symbols[symbol]).collect();
|
||||||
|
|
||||||
let result_type = layout_spec(builder, layout)?;
|
let result_type = layout_spec(builder, layout, &WhenRecursive::Unreachable)?;
|
||||||
|
|
||||||
builder.add_unknown_with(block, &arguments, result_type)
|
builder.add_unknown_with(block, &arguments, result_type)
|
||||||
}
|
}
|
||||||
|
@ -1478,7 +1512,8 @@ fn expr_spec<'a>(
|
||||||
|
|
||||||
let value_id = match tag_layout {
|
let value_id = match tag_layout {
|
||||||
UnionLayout::NonRecursive(tags) => {
|
UnionLayout::NonRecursive(tags) => {
|
||||||
let variant_types = non_recursive_variant_types(builder, tags)?;
|
let variant_types =
|
||||||
|
non_recursive_variant_types(builder, tags, &WhenRecursive::Unreachable)?;
|
||||||
let value_id = build_tuple_value(builder, env, block, arguments)?;
|
let value_id = build_tuple_value(builder, env, block, arguments)?;
|
||||||
return builder.add_make_union(block, &variant_types, *tag_id as u32, value_id);
|
return builder.add_make_union(block, &variant_types, *tag_id as u32, value_id);
|
||||||
}
|
}
|
||||||
|
@ -1592,7 +1627,7 @@ fn expr_spec<'a>(
|
||||||
builder.add_get_tuple_field(block, value_id, *index as u32)
|
builder.add_get_tuple_field(block, value_id, *index as u32)
|
||||||
}
|
}
|
||||||
Array { elem_layout, elems } => {
|
Array { elem_layout, elems } => {
|
||||||
let type_id = layout_spec(builder, elem_layout)?;
|
let type_id = layout_spec(builder, elem_layout, &WhenRecursive::Unreachable)?;
|
||||||
|
|
||||||
let list = new_list(builder, block, type_id)?;
|
let list = new_list(builder, block, type_id)?;
|
||||||
|
|
||||||
|
@ -1619,19 +1654,19 @@ fn expr_spec<'a>(
|
||||||
|
|
||||||
EmptyArray => match layout {
|
EmptyArray => match layout {
|
||||||
Layout::Builtin(Builtin::List(element_layout)) => {
|
Layout::Builtin(Builtin::List(element_layout)) => {
|
||||||
let type_id = layout_spec(builder, element_layout)?;
|
let type_id = layout_spec(builder, element_layout, &WhenRecursive::Unreachable)?;
|
||||||
new_list(builder, block, type_id)
|
new_list(builder, block, type_id)
|
||||||
}
|
}
|
||||||
_ => unreachable!("empty array does not have a list layout"),
|
_ => unreachable!("empty array does not have a list layout"),
|
||||||
},
|
},
|
||||||
Reset { symbol, .. } => {
|
Reset { symbol, .. } => {
|
||||||
let type_id = layout_spec(builder, layout)?;
|
let type_id = layout_spec(builder, layout, &WhenRecursive::Unreachable)?;
|
||||||
let value_id = env.symbols[symbol];
|
let value_id = env.symbols[symbol];
|
||||||
|
|
||||||
builder.add_unknown_with(block, &[value_id], type_id)
|
builder.add_unknown_with(block, &[value_id], type_id)
|
||||||
}
|
}
|
||||||
RuntimeErrorFunction(_) => {
|
RuntimeErrorFunction(_) => {
|
||||||
let type_id = layout_spec(builder, layout)?;
|
let type_id = layout_spec(builder, layout, &WhenRecursive::Unreachable)?;
|
||||||
|
|
||||||
builder.add_terminate(block, type_id)
|
builder.add_terminate(block, type_id)
|
||||||
}
|
}
|
||||||
|
@ -1658,18 +1693,24 @@ fn literal_spec(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn layout_spec(builder: &mut impl TypeContext, layout: &Layout) -> Result<TypeId> {
|
fn layout_spec(
|
||||||
layout_spec_help(builder, layout, &WhenRecursive::Unreachable)
|
builder: &mut impl TypeContext,
|
||||||
|
layout: &Layout,
|
||||||
|
when_recursive: &WhenRecursive,
|
||||||
|
) -> Result<TypeId> {
|
||||||
|
layout_spec_help(builder, layout, when_recursive)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn non_recursive_variant_types(
|
fn non_recursive_variant_types(
|
||||||
builder: &mut impl TypeContext,
|
builder: &mut impl TypeContext,
|
||||||
tags: &[&[Layout]],
|
tags: &[&[Layout]],
|
||||||
|
// If there is a recursive pointer latent within this layout, coming from a containing layout.
|
||||||
|
when_recursive: &WhenRecursive,
|
||||||
) -> Result<Vec<TypeId>> {
|
) -> Result<Vec<TypeId>> {
|
||||||
let mut result = Vec::with_capacity(tags.len());
|
let mut result = Vec::with_capacity(tags.len());
|
||||||
|
|
||||||
for tag in tags.iter() {
|
for tag in tags.iter() {
|
||||||
result.push(build_tuple_type(builder, tag)?);
|
result.push(build_tuple_type(builder, tag, when_recursive)?);
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(result)
|
Ok(result)
|
||||||
|
@ -1701,7 +1742,7 @@ fn layout_spec_help(
|
||||||
builder.add_tuple_type(&[])
|
builder.add_tuple_type(&[])
|
||||||
}
|
}
|
||||||
UnionLayout::NonRecursive(tags) => {
|
UnionLayout::NonRecursive(tags) => {
|
||||||
let variant_types = non_recursive_variant_types(builder, tags)?;
|
let variant_types = non_recursive_variant_types(builder, tags, when_recursive)?;
|
||||||
builder.add_union_type(&variant_types)
|
builder.add_union_type(&variant_types)
|
||||||
}
|
}
|
||||||
UnionLayout::Recursive(_)
|
UnionLayout::Recursive(_)
|
||||||
|
|
|
@ -30,7 +30,7 @@ bumpalo = { version = "3.8.0", features = ["collections"] }
|
||||||
libloading = "0.7.1"
|
libloading = "0.7.1"
|
||||||
tempfile = "3.2.0"
|
tempfile = "3.2.0"
|
||||||
inkwell = { path = "../../vendor/inkwell", optional = true }
|
inkwell = { path = "../../vendor/inkwell", optional = true }
|
||||||
target-lexicon = "0.12.2"
|
target-lexicon = "0.12.3"
|
||||||
|
|
||||||
[target.'cfg(target_os = "macos")'.dependencies]
|
[target.'cfg(target_os = "macos")'.dependencies]
|
||||||
serde_json = "1.0.69"
|
serde_json = "1.0.69"
|
||||||
|
|
|
@ -4,7 +4,7 @@ Builtins are the functions and modules that are implicitly imported into every m
|
||||||
|
|
||||||
### module/src/symbol.rs
|
### module/src/symbol.rs
|
||||||
|
|
||||||
Towards the bottom of `symbol.rs` there is a `define_builtins!` macro being used that takes many modules and function names. The first level (`List`, `Int` ..) is the module name, and the second level is the function or value name (`reverse`, `mod` ..). If you wanted to add a `Int` function called `addTwo` go to `2 Int: "Int" => {` and inside that case add to the bottom `38 INT_ADD_TWO: "addTwo"` (assuming there are 37 existing ones).
|
Towards the bottom of `symbol.rs` there is a `define_builtins!` macro being used that takes many modules and function names. The first level (`List`, `Int` ..) is the module name, and the second level is the function or value name (`reverse`, `rem` ..). If you wanted to add a `Int` function called `addTwo` go to `2 Int: "Int" => {` and inside that case add to the bottom `38 INT_ADD_TWO: "addTwo"` (assuming there are 37 existing ones).
|
||||||
|
|
||||||
Some of these have `#` inside their name (`first#list`, `#lt` ..). This is a trick we are doing to hide implementation details from Roc programmers. To a Roc programmer, a name with `#` in it is invalid, because `#` means everything after it is parsed to a comment. We are constructing these functions manually, so we are circumventing the parsing step and dont have such restrictions. We get to make functions and values with `#` which as a consequence are not accessible to Roc programmers. Roc programmers simply cannot reference them.
|
Some of these have `#` inside their name (`first#list`, `#lt` ..). This is a trick we are doing to hide implementation details from Roc programmers. To a Roc programmer, a name with `#` in it is invalid, because `#` means everything after it is parsed to a comment. We are constructing these functions manually, so we are circumventing the parsing step and dont have such restrictions. We get to make functions and values with `#` which as a consequence are not accessible to Roc programmers. Roc programmers simply cannot reference them.
|
||||||
|
|
||||||
|
|
|
@ -70,10 +70,9 @@ xor : Bool, Bool -> Bool
|
||||||
## Structural equality works as follows:
|
## Structural equality works as follows:
|
||||||
##
|
##
|
||||||
## 1. Global tags are equal if they are the same tag, and also their contents (if any) are equal.
|
## 1. Global tags are equal if they are the same tag, and also their contents (if any) are equal.
|
||||||
## 2. Private tags are equal if they are the same tag, in the same module, and also their contents (if any) are equal.
|
## 2. Records are equal if all their fields are equal.
|
||||||
## 3. Records are equal if all their fields are equal.
|
## 3. Collections ([Str], [List], [Dict], and [Set]) are equal if they are the same length, and also all their corresponding elements are equal.
|
||||||
## 4. Collections ([Str], [List], [Dict], and [Set]) are equal if they are the same length, and also all their corresponding elements are equal.
|
## 4. [Num] values are equal if their numbers are equal, with one exception: if both arguments to `isEq` are *NaN*, then `isEq` returns `False`. See `Num.isNaN` for more about *NaN*.
|
||||||
## 5. [Num] values are equal if their numbers are equal, with one exception: if both arguments to `isEq` are *NaN*, then `isEq` returns `False`. See `Num.isNaN` for more about *NaN*.
|
|
||||||
##
|
##
|
||||||
## Note that `isEq` takes `'val` instead of `val`, which means `isEq` does not
|
## Note that `isEq` takes `'val` instead of `val`, which means `isEq` does not
|
||||||
## accept arguments whose types contain functions.
|
## accept arguments whose types contain functions.
|
||||||
|
|
|
@ -93,7 +93,7 @@ interface Dict
|
||||||
##
|
##
|
||||||
## The [Dict.hasSameContents] function gives an alternative to `==` which ignores ordering
|
## The [Dict.hasSameContents] function gives an alternative to `==` which ignores ordering
|
||||||
## and returns `True` if both dictionaries have the same keys and associated values.
|
## and returns `True` if both dictionaries have the same keys and associated values.
|
||||||
Dict k v : [ @Dict k v ] # TODO k should require a hashing and equating constraint
|
Dict k v := [ Dict k v ] # TODO k should require a hashing and equating constraint
|
||||||
|
|
||||||
## An empty dictionary.
|
## An empty dictionary.
|
||||||
empty : Dict * *
|
empty : Dict * *
|
||||||
|
|
|
@ -187,7 +187,7 @@ interface List
|
||||||
## * Even when copying is faster, other list operations may still be slightly slower with persistent data structures. For example, even if it were a persistent data structure, [List.map], [List.walk], and [List.keepIf] would all need to traverse every element in the list and build up the result from scratch. These operations are all
|
## * Even when copying is faster, other list operations may still be slightly slower with persistent data structures. For example, even if it were a persistent data structure, [List.map], [List.walk], and [List.keepIf] would all need to traverse every element in the list and build up the result from scratch. These operations are all
|
||||||
## * Roc's compiler optimizes many list operations into in-place mutations behind the scenes, depending on how the list is being used. For example, [List.map], [List.keepIf], and [List.set] can all be optimized to perform in-place mutations.
|
## * Roc's compiler optimizes many list operations into in-place mutations behind the scenes, depending on how the list is being used. For example, [List.map], [List.keepIf], and [List.set] can all be optimized to perform in-place mutations.
|
||||||
## * If possible, it is usually best for performance to use large lists in a way where the optimizer can turn them into in-place mutations. If this is not possible, a persistent data structure might be faster - but this is a rare enough scenario that it would not be good for the average Roc program's performance if this were the way [List] worked by default. Instead, you can look outside Roc's standard modules for an implementation of a persistent data structure - likely built using [List] under the hood!
|
## * If possible, it is usually best for performance to use large lists in a way where the optimizer can turn them into in-place mutations. If this is not possible, a persistent data structure might be faster - but this is a rare enough scenario that it would not be good for the average Roc program's performance if this were the way [List] worked by default. Instead, you can look outside Roc's standard modules for an implementation of a persistent data structure - likely built using [List] under the hood!
|
||||||
List elem : [ @List elem ]
|
List elem := [ List elem ]
|
||||||
|
|
||||||
## Initialize
|
## Initialize
|
||||||
|
|
||||||
|
|
|
@ -61,6 +61,7 @@ interface Num
|
||||||
isPositive,
|
isPositive,
|
||||||
isZero,
|
isZero,
|
||||||
log,
|
log,
|
||||||
|
logChecked,
|
||||||
maxFloat,
|
maxFloat,
|
||||||
maxI8,
|
maxI8,
|
||||||
maxU8,
|
maxU8,
|
||||||
|
@ -88,6 +89,7 @@ interface Num
|
||||||
pow,
|
pow,
|
||||||
powInt,
|
powInt,
|
||||||
rem,
|
rem,
|
||||||
|
remChecked,
|
||||||
round,
|
round,
|
||||||
shiftLeftBy,
|
shiftLeftBy,
|
||||||
shiftRightBy,
|
shiftRightBy,
|
||||||
|
@ -97,6 +99,7 @@ interface Num
|
||||||
subChecked,
|
subChecked,
|
||||||
subWrap,
|
subWrap,
|
||||||
sqrt,
|
sqrt,
|
||||||
|
sqrtChecked,
|
||||||
tan,
|
tan,
|
||||||
toI8,
|
toI8,
|
||||||
toI8Checked,
|
toI8Checked,
|
||||||
|
@ -186,7 +189,7 @@ interface Num
|
||||||
##
|
##
|
||||||
## In practice, these are rarely needed. It's most common to write
|
## In practice, these are rarely needed. It's most common to write
|
||||||
## number literals without any suffix.
|
## number literals without any suffix.
|
||||||
Num a : [ @Num a ]
|
Num a := a
|
||||||
|
|
||||||
## A decimal number.
|
## A decimal number.
|
||||||
##
|
##
|
||||||
|
@ -220,7 +223,7 @@ Num a : [ @Num a ]
|
||||||
## [Dec] typically takes slightly less time than [F64] to perform addition and
|
## [Dec] typically takes slightly less time than [F64] to perform addition and
|
||||||
## subtraction, but 10-20 times longer to perform multiplication and division.
|
## subtraction, but 10-20 times longer to perform multiplication and division.
|
||||||
## [sqrt] and trigonometry are massively slower with [Dec] than with [F64].
|
## [sqrt] and trigonometry are massively slower with [Dec] than with [F64].
|
||||||
Dec : Float [ @Decimal128 ]
|
Dec : Num (FloatingPoint Decimal)
|
||||||
|
|
||||||
## A fixed-size number with a fractional component.
|
## A fixed-size number with a fractional component.
|
||||||
##
|
##
|
||||||
|
@ -289,7 +292,7 @@ Dec : Float [ @Decimal128 ]
|
||||||
## loops and conditionals. If you need to do performance-critical trigonometry
|
## loops and conditionals. If you need to do performance-critical trigonometry
|
||||||
## or square roots, either [F64] or [F32] is probably a better choice than the
|
## or square roots, either [F64] or [F32] is probably a better choice than the
|
||||||
## usual default choice of [Dec], despite the precision problems they bring.
|
## usual default choice of [Dec], despite the precision problems they bring.
|
||||||
Float a : Num [ @Fraction a ]
|
Float range : Num (FloatingPoint range)
|
||||||
|
|
||||||
## A fixed-size integer - that is, a number with no fractional component.
|
## A fixed-size integer - that is, a number with no fractional component.
|
||||||
##
|
##
|
||||||
|
@ -340,19 +343,19 @@ Float a : Num [ @Fraction a ]
|
||||||
## * Start by deciding if this integer should allow negative numbers, and choose signed or unsigned accordingly.
|
## * Start by deciding if this integer should allow negative numbers, and choose signed or unsigned accordingly.
|
||||||
## * Next, think about the range of numbers you expect this number to hold. Choose the smallest size you will never expect to overflow, no matter the inputs your program receives. (Validating inputs for size, and presenting the user with an error if they are too big, can help guard against overflow.)
|
## * Next, think about the range of numbers you expect this number to hold. Choose the smallest size you will never expect to overflow, no matter the inputs your program receives. (Validating inputs for size, and presenting the user with an error if they are too big, can help guard against overflow.)
|
||||||
## * Finally, if a particular numeric calculation is running too slowly, you can try experimenting with other number sizes. This rarely makes a meaningful difference, but some processors can operate on different number sizes at different speeds.
|
## * Finally, if a particular numeric calculation is running too slowly, you can try experimenting with other number sizes. This rarely makes a meaningful difference, but some processors can operate on different number sizes at different speeds.
|
||||||
Int size : Num [ @Integer size ]
|
Int range : Num (Integer range)
|
||||||
|
|
||||||
## A signed 8-bit integer, ranging from -128 to 127
|
## A signed 8-bit integer, ranging from -128 to 127
|
||||||
I8 : Int [ @Signed8 ]
|
I8 : Int Signed8
|
||||||
U8 : Int [ @Unsigned8 ]
|
U8 : Int Unsigned8
|
||||||
I16 : Int [ @Signed16 ]
|
I16 : Int Signed16
|
||||||
U16 : Int [ @Unsigned16 ]
|
U16 : Int Unsigned16
|
||||||
I32 : Int [ @Signed32 ]
|
I32 : Int Signed32
|
||||||
U32 : Int [ @Unsigned32 ]
|
U32 : Int Unsigned32
|
||||||
I64 : Int [ @Signed64 ]
|
I64 : Int Signed64
|
||||||
U64 : Int [ @Unsigned64 ]
|
U64 : Int Unsigned64
|
||||||
I128 : Int [ @Signed128 ]
|
I128 : Int Signed128
|
||||||
U128 : Int [ @Unsigned128 ]
|
U128 : Int Unsigned128
|
||||||
|
|
||||||
## A [natural number](https://en.wikipedia.org/wiki/Natural_number) represented
|
## A [natural number](https://en.wikipedia.org/wiki/Natural_number) represented
|
||||||
## as a 64-bit unsigned integer on 64-bit systems, a 32-bit unsigned integer
|
## as a 64-bit unsigned integer on 64-bit systems, a 32-bit unsigned integer
|
||||||
|
@ -364,7 +367,7 @@ U128 : Int [ @Unsigned128 ]
|
||||||
## a [List] can hold on a 64-bit system fits in a 64-bit unsigned integer, and
|
## a [List] can hold on a 64-bit system fits in a 64-bit unsigned integer, and
|
||||||
## on a 32-bit system it fits in 32-bit unsigned integer. This makes [Nat] a
|
## on a 32-bit system it fits in 32-bit unsigned integer. This makes [Nat] a
|
||||||
## good fit for [List.len] regardless of system.
|
## good fit for [List.len] regardless of system.
|
||||||
Nat : Int [ @Natural ]
|
Nat : Num (Integer Natural)
|
||||||
|
|
||||||
## A 64-bit signed integer. All number literals without decimal points are compatible with #Int values.
|
## A 64-bit signed integer. All number literals without decimal points are compatible with #Int values.
|
||||||
##
|
##
|
||||||
|
@ -440,7 +443,7 @@ Nat : Int [ @Natural ]
|
||||||
##
|
##
|
||||||
## As such, it's very important to design your code not to exceed these bounds!
|
## As such, it's very important to design your code not to exceed these bounds!
|
||||||
## If you need to do math outside these bounds, consider using a larger numeric size.
|
## If you need to do math outside these bounds, consider using a larger numeric size.
|
||||||
Int size : Num [ @Int size ]
|
Int range : Num (Integer range)
|
||||||
|
|
||||||
## Convert
|
## Convert
|
||||||
|
|
||||||
|
@ -802,27 +805,18 @@ toDec : Num * -> Dec
|
||||||
## This is the same as the #// operator.
|
## This is the same as the #// operator.
|
||||||
divTrunc : Int a, Int a -> Int a
|
divTrunc : Int a, Int a -> Int a
|
||||||
|
|
||||||
## Perform flooring modulo on two integers.
|
## Obtain the remainder (truncating modulo) from the division of two integers.
|
||||||
##
|
##
|
||||||
## Modulo is the same as remainder when working with positive numbers,
|
## `a % b` is shorthand for `Num.rem a b`.
|
||||||
## but if either number is negative, then modulo works differently.
|
|
||||||
##
|
##
|
||||||
## Additionally, flooring modulo uses [Float].floor on the result.
|
## >>> 5 % 7
|
||||||
##
|
##
|
||||||
## (Use [Float].mod for non-flooring modulo.)
|
## >>> Num.rem 5 7
|
||||||
##
|
##
|
||||||
## Return `Err DivByZero` if the second integer is zero, because division by zero is undefined in mathematics.
|
## >>> -8 % -3
|
||||||
##
|
##
|
||||||
## `a %% b` is shorthand for `Int.modFloor a b`.
|
## >>> Num.rem -8 -3
|
||||||
##
|
rem : Int a, Int a -> Int a
|
||||||
## >>> 5 %% 7
|
|
||||||
##
|
|
||||||
## >>> Int.modFloor 5 7
|
|
||||||
##
|
|
||||||
## >>> -8 %% -3
|
|
||||||
##
|
|
||||||
## >>> Int.modFloor -8 -3
|
|
||||||
#modFloor : Int a, Int a -> Result (Int a) [ DivByZero ]*
|
|
||||||
|
|
||||||
|
|
||||||
## Bitwise
|
## Bitwise
|
||||||
|
@ -1094,31 +1088,6 @@ atan : Float a -> Float a
|
||||||
## >>> |> Num.div 2.0
|
## >>> |> Num.div 2.0
|
||||||
div : Float a, Float a -> Float a
|
div : Float a, Float a -> Float a
|
||||||
|
|
||||||
## Perform modulo on two [Float]s.
|
|
||||||
##
|
|
||||||
## Modulo is the same as remainder when working with positive numbers,
|
|
||||||
## but if either number is negative, then modulo works differently.
|
|
||||||
##
|
|
||||||
## `a % b` is shorthand for `Num.mod a b`.
|
|
||||||
##
|
|
||||||
## [Division by zero is undefined in mathematics](https://en.wikipedia.org/wiki/Division_by_zero),
|
|
||||||
## and as such, so is modulo by zero. Because of this, you should make sure never
|
|
||||||
## to pass zero for the second argument to this function!
|
|
||||||
##
|
|
||||||
## Passing [mod] a [Dec] value of zero for its second argument will cause a panic.
|
|
||||||
## Passing [mod] a [F32] and [F64] value for its second argument will cause it
|
|
||||||
## to return [*NaN*](Num.isNaN).
|
|
||||||
##
|
|
||||||
## >>> 5.0 % 7.0
|
|
||||||
##
|
|
||||||
## >>> Num.mod 5 7
|
|
||||||
##
|
|
||||||
## `Num.mod` can be convenient in pipelines.
|
|
||||||
##
|
|
||||||
## >>> Num.pi
|
|
||||||
## >>> |> Num.mod 2.0
|
|
||||||
mod : Float a, Float a -> Float a
|
|
||||||
|
|
||||||
## Raises a [Float] to the power of another [Float].
|
## Raises a [Float] to the power of another [Float].
|
||||||
##
|
##
|
||||||
## `
|
## `
|
||||||
|
@ -1314,7 +1283,7 @@ isInfinite : Float * -> Bool
|
||||||
##
|
##
|
||||||
## >>> Num.isNaN 12.3
|
## >>> Num.isNaN 12.3
|
||||||
##
|
##
|
||||||
## >>> Num.isNaN (Num.sqrt -2)
|
## >>> Num.isNaN (Num.pow -1 0.5)
|
||||||
##
|
##
|
||||||
## *NaN* is unusual from other numberic values in that:
|
## *NaN* is unusual from other numberic values in that:
|
||||||
## * *NaN* is not equal to any other number, even itself. [Bool.isEq] always returns `False` if either argument is *NaN*.
|
## * *NaN* is not equal to any other number, even itself. [Bool.isEq] always returns `False` if either argument is *NaN*.
|
||||||
|
|
|
@ -13,7 +13,7 @@ interface Result
|
||||||
|
|
||||||
## The result of an operation that could fail: either the operation went
|
## The result of an operation that could fail: either the operation went
|
||||||
## okay, or else there was an error of some sort.
|
## okay, or else there was an error of some sort.
|
||||||
Result ok err : [ @Result ok err ]
|
Result ok err : [ Ok ok, Err err ]
|
||||||
|
|
||||||
## Return True if the result indicates a success, else return False
|
## Return True if the result indicates a success, else return False
|
||||||
##
|
##
|
||||||
|
|
|
@ -18,7 +18,7 @@ interface Set
|
||||||
imports []
|
imports []
|
||||||
|
|
||||||
## A Set is an unordered collection of unique elements.
|
## A Set is an unordered collection of unique elements.
|
||||||
Set elem : [ @Set elem ]
|
Set elem := [ Set elem ]
|
||||||
|
|
||||||
## An empty set.
|
## An empty set.
|
||||||
empty : Set *
|
empty : Set *
|
||||||
|
|
|
@ -116,7 +116,7 @@ interface Str
|
||||||
## It has many more tools than this module does!
|
## It has many more tools than this module does!
|
||||||
|
|
||||||
## A [Unicode](https://unicode.org) text value.
|
## A [Unicode](https://unicode.org) text value.
|
||||||
Str : [ @Str ]
|
Str := [ Str ]
|
||||||
|
|
||||||
## Convert
|
## Convert
|
||||||
|
|
||||||
|
|
|
@ -66,10 +66,13 @@ interface Num
|
||||||
isPositive,
|
isPositive,
|
||||||
isNegative,
|
isNegative,
|
||||||
rem,
|
rem,
|
||||||
|
remChecked,
|
||||||
div,
|
div,
|
||||||
divChecked,
|
divChecked,
|
||||||
sqrt,
|
sqrt,
|
||||||
|
sqrtChecked,
|
||||||
log,
|
log,
|
||||||
|
logChecked,
|
||||||
round,
|
round,
|
||||||
ceiling,
|
ceiling,
|
||||||
floor,
|
floor,
|
||||||
|
@ -155,25 +158,25 @@ interface Num
|
||||||
Bool.{ Bool }
|
Bool.{ Bool }
|
||||||
]
|
]
|
||||||
|
|
||||||
Num range : [ @Num range ]
|
Num range := range
|
||||||
Int range : Num (Integer range)
|
Int range : Num (Integer range)
|
||||||
Float range : Num (FloatingPoint range)
|
Float range : Num (FloatingPoint range)
|
||||||
|
|
||||||
Signed128 : [ @Signed128 ]
|
Signed128 := []
|
||||||
Signed64 : [ @Signed64 ]
|
Signed64 := []
|
||||||
Signed32 : [ @Signed32 ]
|
Signed32 := []
|
||||||
Signed16 : [ @Signed16 ]
|
Signed16 := []
|
||||||
Signed8 : [ @Signed8 ]
|
Signed8 := []
|
||||||
|
|
||||||
Unsigned128 : [ @Unsigned128 ]
|
Unsigned128 := []
|
||||||
Unsigned64 : [ @Unsigned64 ]
|
Unsigned64 := []
|
||||||
Unsigned32 : [ @Unsigned32 ]
|
Unsigned32 := []
|
||||||
Unsigned16 : [ @Unsigned16 ]
|
Unsigned16 := []
|
||||||
Unsigned8 : [ @Unsigned8 ]
|
Unsigned8 := []
|
||||||
|
|
||||||
Natural : [ @Natural ]
|
Natural := []
|
||||||
|
|
||||||
Integer range : [ @Integer range ]
|
Integer range := range
|
||||||
|
|
||||||
I128 : Num (Integer Signed128)
|
I128 : Num (Integer Signed128)
|
||||||
I64 : Num (Integer Signed64)
|
I64 : Num (Integer Signed64)
|
||||||
|
@ -189,11 +192,11 @@ U8 : Num (Integer Unsigned8)
|
||||||
|
|
||||||
Nat : Num (Integer Natural)
|
Nat : Num (Integer Natural)
|
||||||
|
|
||||||
Decimal : [ @Decimal ]
|
Decimal := []
|
||||||
Binary64 : [ @Binary64 ]
|
Binary64 := []
|
||||||
Binary32 : [ @Binary32 ]
|
Binary32 := []
|
||||||
|
|
||||||
FloatingPoint range : [ @FloatingPoint range ]
|
FloatingPoint range := range
|
||||||
|
|
||||||
F64 : Num (FloatingPoint Binary64)
|
F64 : Num (FloatingPoint Binary64)
|
||||||
F32 : Num (FloatingPoint Binary32)
|
F32 : Num (FloatingPoint Binary32)
|
||||||
|
@ -239,19 +242,22 @@ asin : Float a -> Float a
|
||||||
acos : Float a -> Float a
|
acos : Float a -> Float a
|
||||||
atan : Float a -> Float a
|
atan : Float a -> Float a
|
||||||
|
|
||||||
sqrt : Float a -> Result (Float a) [ SqrtOfNegative ]*
|
sqrt : Float a -> Float a
|
||||||
log : Float a -> Result (Float a) [ LogNeedsPositive ]*
|
sqrtChecked : Float a -> Result (Float a) [ SqrtOfNegative ]*
|
||||||
|
log : Float a -> Float a
|
||||||
|
logChecked : Float a -> Result (Float a) [ LogNeedsPositive ]*
|
||||||
|
|
||||||
div : Float a, Float a -> Float a
|
div : Float a, Float a -> Float a
|
||||||
divChecked : Float a, Float a -> Result (Float a) [ DivByZero ]*
|
divChecked : Float a, Float a -> Result (Float a) [ DivByZero ]*
|
||||||
|
|
||||||
divCeil : Int a, Int a -> Int a
|
divCeil : Int a, Int a -> Int a
|
||||||
divCeilChecked : Int a, Int a -> Result (Int a) [ DivByZero ]*
|
divCeilChecked : Int a, Int a -> Result (Int a) [ DivByZero ]*
|
||||||
|
|
||||||
divTrunc : Int a, Int a -> Int a
|
divTrunc : Int a, Int a -> Int a
|
||||||
divTruncChecked : Int a, Int a -> Result (Int a) [ DivByZero ]*
|
divTruncChecked : Int a, Int a -> Result (Int a) [ DivByZero ]*
|
||||||
# mod : Float a, Float a -> Result (Float a) [ DivByZero ]*
|
|
||||||
|
|
||||||
rem : Int a, Int a -> Result (Int a) [ DivByZero ]*
|
rem : Int a, Int a -> Int a
|
||||||
# mod : Int a, Int a -> Result (Int a) [ DivByZero ]*
|
remChecked : Int a, Int a -> Result (Int a) [ DivByZero ]*
|
||||||
|
|
||||||
isMultipleOf : Int a, Int a -> Bool
|
isMultipleOf : Int a, Int a -> Bool
|
||||||
|
|
||||||
bitwiseAnd : Int a, Int a -> Int a
|
bitwiseAnd : Int a, Int a -> Int a
|
||||||
|
|
|
@ -68,13 +68,9 @@ impl FloatWidth {
|
||||||
|
|
||||||
pub const fn try_from_symbol(symbol: Symbol) -> Option<Self> {
|
pub const fn try_from_symbol(symbol: Symbol) -> Option<Self> {
|
||||||
match symbol {
|
match symbol {
|
||||||
Symbol::NUM_F64 | Symbol::NUM_BINARY64 | Symbol::NUM_AT_BINARY64 => {
|
Symbol::NUM_F64 | Symbol::NUM_BINARY64 => Some(FloatWidth::F64),
|
||||||
Some(FloatWidth::F64)
|
|
||||||
}
|
|
||||||
|
|
||||||
Symbol::NUM_F32 | Symbol::NUM_BINARY32 | Symbol::NUM_AT_BINARY32 => {
|
Symbol::NUM_F32 | Symbol::NUM_BINARY32 => Some(FloatWidth::F32),
|
||||||
Some(FloatWidth::F32)
|
|
||||||
}
|
|
||||||
|
|
||||||
_ => None,
|
_ => None,
|
||||||
}
|
}
|
||||||
|
@ -136,26 +132,16 @@ impl IntWidth {
|
||||||
|
|
||||||
pub const fn try_from_symbol(symbol: Symbol) -> Option<Self> {
|
pub const fn try_from_symbol(symbol: Symbol) -> Option<Self> {
|
||||||
match symbol {
|
match symbol {
|
||||||
Symbol::NUM_I128 | Symbol::NUM_SIGNED128 | Symbol::NUM_AT_SIGNED128 => {
|
Symbol::NUM_I128 | Symbol::NUM_SIGNED128 => Some(IntWidth::I128),
|
||||||
Some(IntWidth::I128)
|
Symbol::NUM_I64 | Symbol::NUM_SIGNED64 => Some(IntWidth::I64),
|
||||||
}
|
Symbol::NUM_I32 | Symbol::NUM_SIGNED32 => Some(IntWidth::I32),
|
||||||
Symbol::NUM_I64 | Symbol::NUM_SIGNED64 | Symbol::NUM_AT_SIGNED64 => Some(IntWidth::I64),
|
Symbol::NUM_I16 | Symbol::NUM_SIGNED16 => Some(IntWidth::I16),
|
||||||
Symbol::NUM_I32 | Symbol::NUM_SIGNED32 | Symbol::NUM_AT_SIGNED32 => Some(IntWidth::I32),
|
Symbol::NUM_I8 | Symbol::NUM_SIGNED8 => Some(IntWidth::I8),
|
||||||
Symbol::NUM_I16 | Symbol::NUM_SIGNED16 | Symbol::NUM_AT_SIGNED16 => Some(IntWidth::I16),
|
Symbol::NUM_U128 | Symbol::NUM_UNSIGNED128 => Some(IntWidth::U128),
|
||||||
Symbol::NUM_I8 | Symbol::NUM_SIGNED8 | Symbol::NUM_AT_SIGNED8 => Some(IntWidth::I8),
|
Symbol::NUM_U64 | Symbol::NUM_UNSIGNED64 => Some(IntWidth::U64),
|
||||||
Symbol::NUM_U128 | Symbol::NUM_UNSIGNED128 | Symbol::NUM_AT_UNSIGNED128 => {
|
Symbol::NUM_U32 | Symbol::NUM_UNSIGNED32 => Some(IntWidth::U32),
|
||||||
Some(IntWidth::U128)
|
Symbol::NUM_U16 | Symbol::NUM_UNSIGNED16 => Some(IntWidth::U16),
|
||||||
}
|
Symbol::NUM_U8 | Symbol::NUM_UNSIGNED8 => Some(IntWidth::U8),
|
||||||
Symbol::NUM_U64 | Symbol::NUM_UNSIGNED64 | Symbol::NUM_AT_UNSIGNED64 => {
|
|
||||||
Some(IntWidth::U64)
|
|
||||||
}
|
|
||||||
Symbol::NUM_U32 | Symbol::NUM_UNSIGNED32 | Symbol::NUM_AT_UNSIGNED32 => {
|
|
||||||
Some(IntWidth::U32)
|
|
||||||
}
|
|
||||||
Symbol::NUM_U16 | Symbol::NUM_UNSIGNED16 | Symbol::NUM_AT_UNSIGNED16 => {
|
|
||||||
Some(IntWidth::U16)
|
|
||||||
}
|
|
||||||
Symbol::NUM_U8 | Symbol::NUM_UNSIGNED8 | Symbol::NUM_AT_UNSIGNED8 => Some(IntWidth::U8),
|
|
||||||
_ => None,
|
_ => None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -393,16 +393,16 @@ pub fn types() -> MutMap<Symbol, (SolvedType, Region)> {
|
||||||
Box::new(int_type(flex(TVAR2)))
|
Box::new(int_type(flex(TVAR2)))
|
||||||
);
|
);
|
||||||
|
|
||||||
// rem : Int a, Int a -> Result (Int a) [ DivByZero ]*
|
// rem : Int a, Int a -> Int a
|
||||||
add_top_level_function_type!(
|
add_top_level_function_type!(
|
||||||
Symbol::NUM_REM,
|
Symbol::NUM_REM,
|
||||||
vec![int_type(flex(TVAR1)), int_type(flex(TVAR1))],
|
vec![int_type(flex(TVAR1)), int_type(flex(TVAR1))],
|
||||||
Box::new(result_type(int_type(flex(TVAR1)), div_by_zero.clone())),
|
Box::new(int_type(flex(TVAR1))),
|
||||||
);
|
);
|
||||||
|
|
||||||
// mod : Int a, Int a -> Result (Int a) [ DivByZero ]*
|
// remChecked : Int a, Int a -> Result (Int a) [ DivByZero ]*
|
||||||
add_top_level_function_type!(
|
add_top_level_function_type!(
|
||||||
Symbol::NUM_MOD_INT,
|
Symbol::NUM_REM_CHECKED,
|
||||||
vec![int_type(flex(TVAR1)), int_type(flex(TVAR1))],
|
vec![int_type(flex(TVAR1)), int_type(flex(TVAR1))],
|
||||||
Box::new(result_type(int_type(flex(TVAR1)), div_by_zero.clone())),
|
Box::new(result_type(int_type(flex(TVAR1)), div_by_zero.clone())),
|
||||||
);
|
);
|
||||||
|
@ -680,36 +680,43 @@ pub fn types() -> MutMap<Symbol, (SolvedType, Region)> {
|
||||||
add_top_level_function_type!(
|
add_top_level_function_type!(
|
||||||
Symbol::NUM_DIV_FLOAT_CHECKED,
|
Symbol::NUM_DIV_FLOAT_CHECKED,
|
||||||
vec![float_type(flex(TVAR1)), float_type(flex(TVAR1))],
|
vec![float_type(flex(TVAR1)), float_type(flex(TVAR1))],
|
||||||
Box::new(result_type(float_type(flex(TVAR1)), div_by_zero.clone())),
|
|
||||||
);
|
|
||||||
|
|
||||||
// mod : Float a, Float a -> Result (Float a) [ DivByZero ]*
|
|
||||||
add_top_level_function_type!(
|
|
||||||
Symbol::NUM_MOD_FLOAT,
|
|
||||||
vec![float_type(flex(TVAR1)), float_type(flex(TVAR1))],
|
|
||||||
Box::new(result_type(float_type(flex(TVAR1)), div_by_zero)),
|
Box::new(result_type(float_type(flex(TVAR1)), div_by_zero)),
|
||||||
);
|
);
|
||||||
|
|
||||||
// sqrt : Float a -> Float a
|
// sqrt : Float a -> Float a
|
||||||
|
add_top_level_function_type!(
|
||||||
|
Symbol::NUM_SQRT,
|
||||||
|
vec![float_type(flex(TVAR1))],
|
||||||
|
Box::new(float_type(flex(TVAR1))),
|
||||||
|
);
|
||||||
|
|
||||||
|
// sqrtChecked : Float a -> Result (Float a) [ SqrtOfNegative ]*
|
||||||
let sqrt_of_negative = SolvedType::TagUnion(
|
let sqrt_of_negative = SolvedType::TagUnion(
|
||||||
vec![(TagName::Global("SqrtOfNegative".into()), vec![])],
|
vec![(TagName::Global("SqrtOfNegative".into()), vec![])],
|
||||||
Box::new(SolvedType::Wildcard),
|
Box::new(SolvedType::Wildcard),
|
||||||
);
|
);
|
||||||
|
|
||||||
add_top_level_function_type!(
|
add_top_level_function_type!(
|
||||||
Symbol::NUM_SQRT,
|
Symbol::NUM_SQRT_CHECKED,
|
||||||
vec![float_type(flex(TVAR1))],
|
vec![float_type(flex(TVAR1))],
|
||||||
Box::new(result_type(float_type(flex(TVAR1)), sqrt_of_negative)),
|
Box::new(result_type(float_type(flex(TVAR1)), sqrt_of_negative)),
|
||||||
);
|
);
|
||||||
|
|
||||||
// log : Float a -> Float a
|
// log : Float a -> Float a
|
||||||
|
add_top_level_function_type!(
|
||||||
|
Symbol::NUM_LOG,
|
||||||
|
vec![float_type(flex(TVAR1))],
|
||||||
|
Box::new(float_type(flex(TVAR1))),
|
||||||
|
);
|
||||||
|
|
||||||
|
// logChecked : Float a -> Result (Float a) [ LogNeedsPositive ]*
|
||||||
let log_needs_positive = SolvedType::TagUnion(
|
let log_needs_positive = SolvedType::TagUnion(
|
||||||
vec![(TagName::Global("LogNeedsPositive".into()), vec![])],
|
vec![(TagName::Global("LogNeedsPositive".into()), vec![])],
|
||||||
Box::new(SolvedType::Wildcard),
|
Box::new(SolvedType::Wildcard),
|
||||||
);
|
);
|
||||||
|
|
||||||
add_top_level_function_type!(
|
add_top_level_function_type!(
|
||||||
Symbol::NUM_LOG,
|
Symbol::NUM_LOG_CHECKED,
|
||||||
vec![float_type(flex(TVAR1))],
|
vec![float_type(flex(TVAR1))],
|
||||||
Box::new(result_type(float_type(flex(TVAR1)), log_needs_positive)),
|
Box::new(result_type(float_type(flex(TVAR1)), log_needs_positive)),
|
||||||
);
|
);
|
||||||
|
|
|
@ -365,7 +365,7 @@ pub fn find_type_def_symbols(
|
||||||
|
|
||||||
while let Some(tag) = inner_stack.pop() {
|
while let Some(tag) = inner_stack.pop() {
|
||||||
match tag {
|
match tag {
|
||||||
Tag::Global { args, .. } | Tag::Private { args, .. } => {
|
Tag::Global { args, .. } => {
|
||||||
for t in args.iter() {
|
for t in args.iter() {
|
||||||
stack.push(&t.value);
|
stack.push(&t.value);
|
||||||
}
|
}
|
||||||
|
@ -1253,31 +1253,6 @@ fn can_tags<'a>(
|
||||||
|
|
||||||
break 'inner tag_name;
|
break 'inner tag_name;
|
||||||
}
|
}
|
||||||
Tag::Private { name, args } => {
|
|
||||||
let ident_id = env.ident_ids.get_or_insert(&name.value.into());
|
|
||||||
let symbol = Symbol::new(env.home, ident_id);
|
|
||||||
let mut arg_types = Vec::with_capacity(args.len());
|
|
||||||
|
|
||||||
for arg in args.iter() {
|
|
||||||
let ann = can_annotation_help(
|
|
||||||
env,
|
|
||||||
&arg.value,
|
|
||||||
arg.region,
|
|
||||||
scope,
|
|
||||||
var_store,
|
|
||||||
introduced_variables,
|
|
||||||
local_aliases,
|
|
||||||
references,
|
|
||||||
);
|
|
||||||
|
|
||||||
arg_types.push(ann);
|
|
||||||
}
|
|
||||||
|
|
||||||
let tag_name = TagName::Private(symbol);
|
|
||||||
tag_types.push((tag_name.clone(), arg_types));
|
|
||||||
|
|
||||||
break 'inner tag_name;
|
|
||||||
}
|
|
||||||
Tag::SpaceBefore(nested, _) | Tag::SpaceAfter(nested, _) => {
|
Tag::SpaceBefore(nested, _) | Tag::SpaceAfter(nested, _) => {
|
||||||
// check the nested tag instead
|
// check the nested tag instead
|
||||||
tag = nested;
|
tag = nested;
|
||||||
|
|
|
@ -204,9 +204,12 @@ pub fn builtin_defs_map(symbol: Symbol, var_store: &mut VarStore) -> Option<Def>
|
||||||
NUM_ABS => num_abs,
|
NUM_ABS => num_abs,
|
||||||
NUM_NEG => num_neg,
|
NUM_NEG => num_neg,
|
||||||
NUM_REM => num_rem,
|
NUM_REM => num_rem,
|
||||||
|
NUM_REM_CHECKED => num_rem_checked,
|
||||||
NUM_IS_MULTIPLE_OF => num_is_multiple_of,
|
NUM_IS_MULTIPLE_OF => num_is_multiple_of,
|
||||||
NUM_SQRT => num_sqrt,
|
NUM_SQRT => num_sqrt,
|
||||||
|
NUM_SQRT_CHECKED => num_sqrt_checked,
|
||||||
NUM_LOG => num_log,
|
NUM_LOG => num_log,
|
||||||
|
NUM_LOG_CHECKED => num_log_checked,
|
||||||
NUM_ROUND => num_round,
|
NUM_ROUND => num_round,
|
||||||
NUM_IS_ODD => num_is_odd,
|
NUM_IS_ODD => num_is_odd,
|
||||||
NUM_IS_EVEN => num_is_even,
|
NUM_IS_EVEN => num_is_even,
|
||||||
|
@ -713,6 +716,23 @@ fn bool_and(symbol: Symbol, var_store: &mut VarStore) -> Def {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn num_unaryop(symbol: Symbol, var_store: &mut VarStore, op: LowLevel) -> Def {
|
||||||
|
let num_var = var_store.fresh();
|
||||||
|
let body = RunLowLevel {
|
||||||
|
op,
|
||||||
|
args: vec![(num_var, Var(Symbol::ARG_1))],
|
||||||
|
ret_var: num_var,
|
||||||
|
};
|
||||||
|
|
||||||
|
defn(
|
||||||
|
symbol,
|
||||||
|
vec![(num_var, Symbol::ARG_1)],
|
||||||
|
var_store,
|
||||||
|
body,
|
||||||
|
num_var,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
/// Num a, Num a -> Num a
|
/// Num a, Num a -> Num a
|
||||||
fn num_binop(symbol: Symbol, var_store: &mut VarStore, op: LowLevel) -> Def {
|
fn num_binop(symbol: Symbol, var_store: &mut VarStore, op: LowLevel) -> Def {
|
||||||
let num_var = var_store.fresh();
|
let num_var = var_store.fresh();
|
||||||
|
@ -1152,8 +1172,13 @@ fn num_to_float(symbol: Symbol, var_store: &mut VarStore) -> Def {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Num.sqrt : Float -> Result Float [ SqrtOfNegative ]*
|
/// Num.sqrt : Float a -> Float a
|
||||||
fn num_sqrt(symbol: Symbol, var_store: &mut VarStore) -> Def {
|
fn num_sqrt(symbol: Symbol, var_store: &mut VarStore) -> Def {
|
||||||
|
num_unaryop(symbol, var_store, LowLevel::NumSqrtUnchecked)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Num.sqrtChecked : Float a -> Result (Float a) [ SqrtOfNegative ]*
|
||||||
|
fn num_sqrt_checked(symbol: Symbol, var_store: &mut VarStore) -> Def {
|
||||||
let bool_var = var_store.fresh();
|
let bool_var = var_store.fresh();
|
||||||
let float_var = var_store.fresh();
|
let float_var = var_store.fresh();
|
||||||
let unbound_zero_var = var_store.fresh();
|
let unbound_zero_var = var_store.fresh();
|
||||||
|
@ -1201,8 +1226,13 @@ fn num_sqrt(symbol: Symbol, var_store: &mut VarStore) -> Def {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Num.log : Float -> Result Float [ LogNeedsPositive ]*
|
/// Num.log : Float a -> Float a
|
||||||
fn num_log(symbol: Symbol, var_store: &mut VarStore) -> Def {
|
fn num_log(symbol: Symbol, var_store: &mut VarStore) -> Def {
|
||||||
|
num_unaryop(symbol, var_store, LowLevel::NumLogUnchecked)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Num.logChecked : Float a -> Result (Float a) [ LogNeedsPositive ]*
|
||||||
|
fn num_log_checked(symbol: Symbol, var_store: &mut VarStore) -> Def {
|
||||||
let bool_var = var_store.fresh();
|
let bool_var = var_store.fresh();
|
||||||
let float_var = var_store.fresh();
|
let float_var = var_store.fresh();
|
||||||
let unbound_zero_var = var_store.fresh();
|
let unbound_zero_var = var_store.fresh();
|
||||||
|
@ -4084,8 +4114,13 @@ fn set_walk(symbol: Symbol, var_store: &mut VarStore) -> Def {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Num.rem : Int a, Int a -> Result (Int a) [ DivByZero ]*
|
/// Num.rem : Int a, Int a -> Int a
|
||||||
fn num_rem(symbol: Symbol, var_store: &mut VarStore) -> Def {
|
fn num_rem(symbol: Symbol, var_store: &mut VarStore) -> Def {
|
||||||
|
num_binop(symbol, var_store, LowLevel::NumRemUnchecked)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Num.remChecked : Int a, Int a -> Result (Int a) [ DivByZero ]*
|
||||||
|
fn num_rem_checked(symbol: Symbol, var_store: &mut VarStore) -> Def {
|
||||||
let num_var = var_store.fresh();
|
let num_var = var_store.fresh();
|
||||||
let unbound_zero_var = var_store.fresh();
|
let unbound_zero_var = var_store.fresh();
|
||||||
let bool_var = var_store.fresh();
|
let bool_var = var_store.fresh();
|
||||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -6,11 +6,11 @@ use crate::pattern::Pattern;
|
||||||
use crate::scope::Scope;
|
use crate::scope::Scope;
|
||||||
use roc_collections::{SendMap, VecSet};
|
use roc_collections::{SendMap, VecSet};
|
||||||
use roc_module::called_via::CalledVia;
|
use roc_module::called_via::CalledVia;
|
||||||
use roc_module::ident::TagName;
|
use roc_module::ident::{Lowercase, TagName};
|
||||||
use roc_module::symbol::Symbol;
|
use roc_module::symbol::Symbol;
|
||||||
use roc_region::all::{Loc, Region};
|
use roc_region::all::{Loc, Region};
|
||||||
use roc_types::subs::{VarStore, Variable};
|
use roc_types::subs::{VarStore, Variable};
|
||||||
use roc_types::types::{AliasKind, Type, TypeExtension};
|
use roc_types::types::{AliasKind, LambdaSet, Type, TypeExtension};
|
||||||
|
|
||||||
#[derive(Debug, Default, Clone, Copy)]
|
#[derive(Debug, Default, Clone, Copy)]
|
||||||
pub(crate) struct HostedGeneratedFunctions {
|
pub(crate) struct HostedGeneratedFunctions {
|
||||||
|
@ -30,7 +30,7 @@ pub(crate) struct HostedGeneratedFunctions {
|
||||||
///
|
///
|
||||||
/// The effect alias is implemented as
|
/// The effect alias is implemented as
|
||||||
///
|
///
|
||||||
/// Effect a : [ @Effect ({} -> a) ]
|
/// Effect a := {} -> a
|
||||||
///
|
///
|
||||||
/// For this alias we implement the functions specified in HostedGeneratedFunctions with the
|
/// For this alias we implement the functions specified in HostedGeneratedFunctions with the
|
||||||
/// standard implementation.
|
/// standard implementation.
|
||||||
|
@ -45,13 +45,7 @@ pub(crate) fn build_effect_builtins(
|
||||||
) {
|
) {
|
||||||
macro_rules! helper {
|
macro_rules! helper {
|
||||||
($f:expr) => {{
|
($f:expr) => {{
|
||||||
let (symbol, def) = $f(
|
let (symbol, def) = $f(env, scope, effect_symbol, var_store);
|
||||||
env,
|
|
||||||
scope,
|
|
||||||
effect_symbol,
|
|
||||||
TagName::Private(effect_symbol),
|
|
||||||
var_store,
|
|
||||||
);
|
|
||||||
|
|
||||||
// make the outside world know this symbol exists
|
// make the outside world know this symbol exists
|
||||||
exposed_symbols.insert(symbol);
|
exposed_symbols.insert(symbol);
|
||||||
|
@ -114,7 +108,6 @@ fn build_effect_always(
|
||||||
env: &mut Env,
|
env: &mut Env,
|
||||||
scope: &mut Scope,
|
scope: &mut Scope,
|
||||||
effect_symbol: Symbol,
|
effect_symbol: Symbol,
|
||||||
effect_tag_name: TagName,
|
|
||||||
var_store: &mut VarStore,
|
var_store: &mut VarStore,
|
||||||
) -> (Symbol, Def) {
|
) -> (Symbol, Def) {
|
||||||
// Effect.always = \value -> @Effect \{} -> value
|
// Effect.always = \value -> @Effect \{} -> value
|
||||||
|
@ -177,11 +170,15 @@ fn build_effect_always(
|
||||||
// \value -> @Effect \{} -> value
|
// \value -> @Effect \{} -> value
|
||||||
let (function_var, always_closure) = {
|
let (function_var, always_closure) = {
|
||||||
// `@Effect \{} -> value`
|
// `@Effect \{} -> value`
|
||||||
let body = Expr::Tag {
|
let (specialized_def_type, type_arguments, lambda_set_variables) =
|
||||||
variant_var: var_store.fresh(),
|
build_fresh_opaque_variables(var_store);
|
||||||
ext_var: var_store.fresh(),
|
let body = Expr::OpaqueRef {
|
||||||
name: effect_tag_name.clone(),
|
opaque_var: var_store.fresh(),
|
||||||
arguments: vec![(var_store.fresh(), Loc::at_zero(const_closure))],
|
name: effect_symbol,
|
||||||
|
argument: Box::new((var_store.fresh(), Loc::at_zero(const_closure))),
|
||||||
|
specialized_def_type,
|
||||||
|
type_arguments,
|
||||||
|
lambda_set_variables,
|
||||||
};
|
};
|
||||||
|
|
||||||
let arguments = vec![(
|
let arguments = vec![(
|
||||||
|
@ -212,9 +209,8 @@ fn build_effect_always(
|
||||||
let var_a = var_store.fresh();
|
let var_a = var_store.fresh();
|
||||||
introduced_variables.insert_named("a".into(), Loc::at_zero(var_a));
|
introduced_variables.insert_named("a".into(), Loc::at_zero(var_a));
|
||||||
|
|
||||||
let effect_a = build_effect_alias(
|
let effect_a = build_effect_opaque(
|
||||||
effect_symbol,
|
effect_symbol,
|
||||||
effect_tag_name,
|
|
||||||
"a",
|
"a",
|
||||||
var_a,
|
var_a,
|
||||||
Type::Variable(var_a),
|
Type::Variable(var_a),
|
||||||
|
@ -257,7 +253,6 @@ fn build_effect_map(
|
||||||
env: &mut Env,
|
env: &mut Env,
|
||||||
scope: &mut Scope,
|
scope: &mut Scope,
|
||||||
effect_symbol: Symbol,
|
effect_symbol: Symbol,
|
||||||
effect_tag_name: TagName,
|
|
||||||
var_store: &mut VarStore,
|
var_store: &mut VarStore,
|
||||||
) -> (Symbol, Def) {
|
) -> (Symbol, Def) {
|
||||||
// Effect.map = \@Effect thunk, mapper -> @Effect \{} -> mapper (thunk {})
|
// Effect.map = \@Effect thunk, mapper -> @Effect \{} -> mapper (thunk {})
|
||||||
|
@ -355,17 +350,22 @@ fn build_effect_map(
|
||||||
})
|
})
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// \@Effect thunk, mapper
|
||||||
|
let (specialized_def_type, type_arguments, lambda_set_variables) =
|
||||||
|
build_fresh_opaque_variables(var_store);
|
||||||
let arguments = vec![
|
let arguments = vec![
|
||||||
(
|
(
|
||||||
var_store.fresh(),
|
var_store.fresh(),
|
||||||
Loc::at_zero(Pattern::AppliedTag {
|
Loc::at_zero(Pattern::UnwrappedOpaque {
|
||||||
|
opaque: effect_symbol,
|
||||||
whole_var: var_store.fresh(),
|
whole_var: var_store.fresh(),
|
||||||
ext_var: var_store.fresh(),
|
argument: Box::new((
|
||||||
tag_name: effect_tag_name.clone(),
|
|
||||||
arguments: vec![(
|
|
||||||
var_store.fresh(),
|
var_store.fresh(),
|
||||||
Loc::at_zero(Pattern::Identifier(thunk_symbol)),
|
Loc::at_zero(Pattern::Identifier(thunk_symbol)),
|
||||||
)],
|
)),
|
||||||
|
specialized_def_type,
|
||||||
|
type_arguments,
|
||||||
|
lambda_set_variables,
|
||||||
}),
|
}),
|
||||||
),
|
),
|
||||||
(
|
(
|
||||||
|
@ -375,11 +375,15 @@ fn build_effect_map(
|
||||||
];
|
];
|
||||||
|
|
||||||
// `@Effect \{} -> (mapper (thunk {}))`
|
// `@Effect \{} -> (mapper (thunk {}))`
|
||||||
let body = Expr::Tag {
|
let (specialized_def_type, type_arguments, lambda_set_variables) =
|
||||||
variant_var: var_store.fresh(),
|
build_fresh_opaque_variables(var_store);
|
||||||
ext_var: var_store.fresh(),
|
let body = Expr::OpaqueRef {
|
||||||
name: effect_tag_name.clone(),
|
opaque_var: var_store.fresh(),
|
||||||
arguments: vec![(var_store.fresh(), Loc::at_zero(inner_closure))],
|
name: effect_symbol,
|
||||||
|
argument: Box::new((var_store.fresh(), Loc::at_zero(inner_closure))),
|
||||||
|
specialized_def_type,
|
||||||
|
type_arguments,
|
||||||
|
lambda_set_variables,
|
||||||
};
|
};
|
||||||
|
|
||||||
let function_var = var_store.fresh();
|
let function_var = var_store.fresh();
|
||||||
|
@ -405,9 +409,8 @@ fn build_effect_map(
|
||||||
introduced_variables.insert_named("a".into(), Loc::at_zero(var_a));
|
introduced_variables.insert_named("a".into(), Loc::at_zero(var_a));
|
||||||
introduced_variables.insert_named("b".into(), Loc::at_zero(var_b));
|
introduced_variables.insert_named("b".into(), Loc::at_zero(var_b));
|
||||||
|
|
||||||
let effect_a = build_effect_alias(
|
let effect_a = build_effect_opaque(
|
||||||
effect_symbol,
|
effect_symbol,
|
||||||
effect_tag_name.clone(),
|
|
||||||
"a",
|
"a",
|
||||||
var_a,
|
var_a,
|
||||||
Type::Variable(var_a),
|
Type::Variable(var_a),
|
||||||
|
@ -415,9 +418,8 @@ fn build_effect_map(
|
||||||
&mut introduced_variables,
|
&mut introduced_variables,
|
||||||
);
|
);
|
||||||
|
|
||||||
let effect_b = build_effect_alias(
|
let effect_b = build_effect_opaque(
|
||||||
effect_symbol,
|
effect_symbol,
|
||||||
effect_tag_name,
|
|
||||||
"b",
|
"b",
|
||||||
var_b,
|
var_b,
|
||||||
Type::Variable(var_b),
|
Type::Variable(var_b),
|
||||||
|
@ -469,7 +471,6 @@ fn build_effect_after(
|
||||||
env: &mut Env,
|
env: &mut Env,
|
||||||
scope: &mut Scope,
|
scope: &mut Scope,
|
||||||
effect_symbol: Symbol,
|
effect_symbol: Symbol,
|
||||||
effect_tag_name: TagName,
|
|
||||||
var_store: &mut VarStore,
|
var_store: &mut VarStore,
|
||||||
) -> (Symbol, Def) {
|
) -> (Symbol, Def) {
|
||||||
// Effect.after = \@Effect effect, toEffect -> toEffect (effect {})
|
// Effect.after = \@Effect effect, toEffect -> toEffect (effect {})
|
||||||
|
@ -533,17 +534,22 @@ fn build_effect_after(
|
||||||
Expr::Call(Box::new(boxed), arguments, CalledVia::Space)
|
Expr::Call(Box::new(boxed), arguments, CalledVia::Space)
|
||||||
};
|
};
|
||||||
|
|
||||||
|
let (specialized_def_type, type_arguments, lambda_set_variables) =
|
||||||
|
build_fresh_opaque_variables(var_store);
|
||||||
|
|
||||||
let arguments = vec![
|
let arguments = vec![
|
||||||
(
|
(
|
||||||
var_store.fresh(),
|
var_store.fresh(),
|
||||||
Loc::at_zero(Pattern::AppliedTag {
|
Loc::at_zero(Pattern::UnwrappedOpaque {
|
||||||
|
opaque: effect_symbol,
|
||||||
whole_var: var_store.fresh(),
|
whole_var: var_store.fresh(),
|
||||||
ext_var: var_store.fresh(),
|
argument: Box::new((
|
||||||
tag_name: effect_tag_name.clone(),
|
|
||||||
arguments: vec![(
|
|
||||||
var_store.fresh(),
|
var_store.fresh(),
|
||||||
Loc::at_zero(Pattern::Identifier(thunk_symbol)),
|
Loc::at_zero(Pattern::Identifier(thunk_symbol)),
|
||||||
)],
|
)),
|
||||||
|
specialized_def_type,
|
||||||
|
type_arguments,
|
||||||
|
lambda_set_variables,
|
||||||
}),
|
}),
|
||||||
),
|
),
|
||||||
(
|
(
|
||||||
|
@ -574,9 +580,8 @@ fn build_effect_after(
|
||||||
introduced_variables.insert_named("a".into(), Loc::at_zero(var_a));
|
introduced_variables.insert_named("a".into(), Loc::at_zero(var_a));
|
||||||
introduced_variables.insert_named("b".into(), Loc::at_zero(var_b));
|
introduced_variables.insert_named("b".into(), Loc::at_zero(var_b));
|
||||||
|
|
||||||
let effect_a = build_effect_alias(
|
let effect_a = build_effect_opaque(
|
||||||
effect_symbol,
|
effect_symbol,
|
||||||
effect_tag_name.clone(),
|
|
||||||
"a",
|
"a",
|
||||||
var_a,
|
var_a,
|
||||||
Type::Variable(var_a),
|
Type::Variable(var_a),
|
||||||
|
@ -584,9 +589,8 @@ fn build_effect_after(
|
||||||
&mut introduced_variables,
|
&mut introduced_variables,
|
||||||
);
|
);
|
||||||
|
|
||||||
let effect_b = build_effect_alias(
|
let effect_b = build_effect_opaque(
|
||||||
effect_symbol,
|
effect_symbol,
|
||||||
effect_tag_name,
|
|
||||||
"b",
|
"b",
|
||||||
var_b,
|
var_b,
|
||||||
Type::Variable(var_b),
|
Type::Variable(var_b),
|
||||||
|
@ -635,7 +639,7 @@ fn build_effect_after(
|
||||||
/// turn `value` into `@Effect \{} -> value`
|
/// turn `value` into `@Effect \{} -> value`
|
||||||
fn wrap_in_effect_thunk(
|
fn wrap_in_effect_thunk(
|
||||||
body: Expr,
|
body: Expr,
|
||||||
effect_tag_name: TagName,
|
effect_symbol: Symbol,
|
||||||
closure_name: Symbol,
|
closure_name: Symbol,
|
||||||
captured_symbols: Vec<Symbol>,
|
captured_symbols: Vec<Symbol>,
|
||||||
var_store: &mut VarStore,
|
var_store: &mut VarStore,
|
||||||
|
@ -667,31 +671,38 @@ fn wrap_in_effect_thunk(
|
||||||
};
|
};
|
||||||
|
|
||||||
// `@Effect \{} -> value`
|
// `@Effect \{} -> value`
|
||||||
Expr::Tag {
|
let (specialized_def_type, type_arguments, lambda_set_variables) =
|
||||||
variant_var: var_store.fresh(),
|
build_fresh_opaque_variables(var_store);
|
||||||
ext_var: var_store.fresh(),
|
Expr::OpaqueRef {
|
||||||
name: effect_tag_name,
|
opaque_var: var_store.fresh(),
|
||||||
arguments: vec![(var_store.fresh(), Loc::at_zero(const_closure))],
|
name: effect_symbol,
|
||||||
|
argument: Box::new((var_store.fresh(), Loc::at_zero(const_closure))),
|
||||||
|
specialized_def_type,
|
||||||
|
type_arguments,
|
||||||
|
lambda_set_variables,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// given `effect : Effect a`, unwrap the thunk and force it, giving a value of type `a`
|
/// given `effect : Effect a`, unwrap the thunk and force it, giving a value of type `a`
|
||||||
fn force_effect(
|
fn force_effect(
|
||||||
effect: Expr,
|
effect: Expr,
|
||||||
effect_tag_name: TagName,
|
effect_symbol: Symbol,
|
||||||
thunk_symbol: Symbol,
|
thunk_symbol: Symbol,
|
||||||
var_store: &mut VarStore,
|
var_store: &mut VarStore,
|
||||||
) -> Expr {
|
) -> Expr {
|
||||||
let whole_var = var_store.fresh();
|
let whole_var = var_store.fresh();
|
||||||
let ext_var = var_store.fresh();
|
|
||||||
|
|
||||||
let thunk_var = var_store.fresh();
|
let thunk_var = var_store.fresh();
|
||||||
|
|
||||||
let pattern = Pattern::AppliedTag {
|
let (specialized_def_type, type_arguments, lambda_set_variables) =
|
||||||
ext_var,
|
build_fresh_opaque_variables(var_store);
|
||||||
|
let pattern = Pattern::UnwrappedOpaque {
|
||||||
whole_var,
|
whole_var,
|
||||||
tag_name: effect_tag_name,
|
opaque: effect_symbol,
|
||||||
arguments: vec![(thunk_var, Loc::at_zero(Pattern::Identifier(thunk_symbol)))],
|
argument: Box::new((thunk_var, Loc::at_zero(Pattern::Identifier(thunk_symbol)))),
|
||||||
|
specialized_def_type,
|
||||||
|
type_arguments,
|
||||||
|
lambda_set_variables,
|
||||||
};
|
};
|
||||||
|
|
||||||
let pattern_vars = SendMap::default();
|
let pattern_vars = SendMap::default();
|
||||||
|
@ -728,7 +739,6 @@ fn build_effect_forever(
|
||||||
env: &mut Env,
|
env: &mut Env,
|
||||||
scope: &mut Scope,
|
scope: &mut Scope,
|
||||||
effect_symbol: Symbol,
|
effect_symbol: Symbol,
|
||||||
effect_tag_name: TagName,
|
|
||||||
var_store: &mut VarStore,
|
var_store: &mut VarStore,
|
||||||
) -> (Symbol, Def) {
|
) -> (Symbol, Def) {
|
||||||
// morally
|
// morally
|
||||||
|
@ -801,14 +811,8 @@ fn build_effect_forever(
|
||||||
.unwrap()
|
.unwrap()
|
||||||
};
|
};
|
||||||
|
|
||||||
let body = build_effect_forever_body(
|
let body =
|
||||||
env,
|
build_effect_forever_body(env, scope, effect_symbol, forever_symbol, effect, var_store);
|
||||||
scope,
|
|
||||||
effect_tag_name.clone(),
|
|
||||||
forever_symbol,
|
|
||||||
effect,
|
|
||||||
var_store,
|
|
||||||
);
|
|
||||||
|
|
||||||
let arguments = vec![(var_store.fresh(), Loc::at_zero(Pattern::Identifier(effect)))];
|
let arguments = vec![(var_store.fresh(), Loc::at_zero(Pattern::Identifier(effect)))];
|
||||||
|
|
||||||
|
@ -834,9 +838,8 @@ fn build_effect_forever(
|
||||||
introduced_variables.insert_named("a".into(), Loc::at_zero(var_a));
|
introduced_variables.insert_named("a".into(), Loc::at_zero(var_a));
|
||||||
introduced_variables.insert_named("b".into(), Loc::at_zero(var_b));
|
introduced_variables.insert_named("b".into(), Loc::at_zero(var_b));
|
||||||
|
|
||||||
let effect_a = build_effect_alias(
|
let effect_a = build_effect_opaque(
|
||||||
effect_symbol,
|
effect_symbol,
|
||||||
effect_tag_name.clone(),
|
|
||||||
"a",
|
"a",
|
||||||
var_a,
|
var_a,
|
||||||
Type::Variable(var_a),
|
Type::Variable(var_a),
|
||||||
|
@ -844,9 +847,8 @@ fn build_effect_forever(
|
||||||
&mut introduced_variables,
|
&mut introduced_variables,
|
||||||
);
|
);
|
||||||
|
|
||||||
let effect_b = build_effect_alias(
|
let effect_b = build_effect_opaque(
|
||||||
effect_symbol,
|
effect_symbol,
|
||||||
effect_tag_name,
|
|
||||||
"b",
|
"b",
|
||||||
var_b,
|
var_b,
|
||||||
Type::Variable(var_b),
|
Type::Variable(var_b),
|
||||||
|
@ -888,7 +890,7 @@ fn build_effect_forever(
|
||||||
fn build_effect_forever_body(
|
fn build_effect_forever_body(
|
||||||
env: &mut Env,
|
env: &mut Env,
|
||||||
scope: &mut Scope,
|
scope: &mut Scope,
|
||||||
effect_tag_name: TagName,
|
effect_symbol: Symbol,
|
||||||
forever_symbol: Symbol,
|
forever_symbol: Symbol,
|
||||||
effect: Symbol,
|
effect: Symbol,
|
||||||
var_store: &mut VarStore,
|
var_store: &mut VarStore,
|
||||||
|
@ -907,7 +909,7 @@ fn build_effect_forever_body(
|
||||||
let inner_body = build_effect_forever_inner_body(
|
let inner_body = build_effect_forever_inner_body(
|
||||||
env,
|
env,
|
||||||
scope,
|
scope,
|
||||||
effect_tag_name.clone(),
|
effect_symbol,
|
||||||
forever_symbol,
|
forever_symbol,
|
||||||
effect,
|
effect,
|
||||||
var_store,
|
var_store,
|
||||||
|
@ -916,7 +918,7 @@ fn build_effect_forever_body(
|
||||||
let captured_symbols = vec![effect];
|
let captured_symbols = vec![effect];
|
||||||
wrap_in_effect_thunk(
|
wrap_in_effect_thunk(
|
||||||
inner_body,
|
inner_body,
|
||||||
effect_tag_name,
|
effect_symbol,
|
||||||
closure_name,
|
closure_name,
|
||||||
captured_symbols,
|
captured_symbols,
|
||||||
var_store,
|
var_store,
|
||||||
|
@ -926,7 +928,7 @@ fn build_effect_forever_body(
|
||||||
fn build_effect_forever_inner_body(
|
fn build_effect_forever_inner_body(
|
||||||
env: &mut Env,
|
env: &mut Env,
|
||||||
scope: &mut Scope,
|
scope: &mut Scope,
|
||||||
effect_tag_name: TagName,
|
effect_symbol: Symbol,
|
||||||
forever_symbol: Symbol,
|
forever_symbol: Symbol,
|
||||||
effect: Symbol,
|
effect: Symbol,
|
||||||
var_store: &mut VarStore,
|
var_store: &mut VarStore,
|
||||||
|
@ -953,18 +955,21 @@ fn build_effect_forever_inner_body(
|
||||||
.unwrap()
|
.unwrap()
|
||||||
};
|
};
|
||||||
|
|
||||||
// Effect thunk1 = effect
|
// @Effect thunk1 = effect
|
||||||
let thunk_from_effect = {
|
let thunk_from_effect = {
|
||||||
let whole_var = var_store.fresh();
|
let whole_var = var_store.fresh();
|
||||||
let ext_var = var_store.fresh();
|
|
||||||
|
|
||||||
let thunk_var = var_store.fresh();
|
let thunk_var = var_store.fresh();
|
||||||
|
|
||||||
let pattern = Pattern::AppliedTag {
|
let (specialized_def_type, type_arguments, lambda_set_variables) =
|
||||||
ext_var,
|
build_fresh_opaque_variables(var_store);
|
||||||
|
let pattern = Pattern::UnwrappedOpaque {
|
||||||
whole_var,
|
whole_var,
|
||||||
tag_name: effect_tag_name.clone(),
|
opaque: effect_symbol,
|
||||||
arguments: vec![(thunk_var, Loc::at_zero(Pattern::Identifier(thunk1_symbol)))],
|
argument: Box::new((thunk_var, Loc::at_zero(Pattern::Identifier(thunk1_symbol)))),
|
||||||
|
specialized_def_type,
|
||||||
|
type_arguments,
|
||||||
|
lambda_set_variables,
|
||||||
};
|
};
|
||||||
|
|
||||||
let pattern_vars = SendMap::default();
|
let pattern_vars = SendMap::default();
|
||||||
|
@ -1017,12 +1022,12 @@ fn build_effect_forever_inner_body(
|
||||||
};
|
};
|
||||||
|
|
||||||
// ```
|
// ```
|
||||||
// Effect thunk2 = forever effect
|
// @Effect thunk2 = forever effect
|
||||||
// thunk2 {}
|
// thunk2 {}
|
||||||
// ```
|
// ```
|
||||||
let force_thunk2 = Loc::at_zero(force_effect(
|
let force_thunk2 = Loc::at_zero(force_effect(
|
||||||
forever_effect,
|
forever_effect,
|
||||||
effect_tag_name,
|
effect_symbol,
|
||||||
thunk2_symbol,
|
thunk2_symbol,
|
||||||
var_store,
|
var_store,
|
||||||
));
|
));
|
||||||
|
@ -1042,7 +1047,6 @@ fn build_effect_loop(
|
||||||
env: &mut Env,
|
env: &mut Env,
|
||||||
scope: &mut Scope,
|
scope: &mut Scope,
|
||||||
effect_symbol: Symbol,
|
effect_symbol: Symbol,
|
||||||
effect_tag_name: TagName,
|
|
||||||
var_store: &mut VarStore,
|
var_store: &mut VarStore,
|
||||||
) -> (Symbol, Def) {
|
) -> (Symbol, Def) {
|
||||||
let loop_symbol = new_symbol!(scope, env, "loop");
|
let loop_symbol = new_symbol!(scope, env, "loop");
|
||||||
|
@ -1052,7 +1056,7 @@ fn build_effect_loop(
|
||||||
let body = build_effect_loop_body(
|
let body = build_effect_loop_body(
|
||||||
env,
|
env,
|
||||||
scope,
|
scope,
|
||||||
effect_tag_name.clone(),
|
effect_symbol,
|
||||||
loop_symbol,
|
loop_symbol,
|
||||||
state_symbol,
|
state_symbol,
|
||||||
step_symbol,
|
step_symbol,
|
||||||
|
@ -1092,9 +1096,8 @@ fn build_effect_loop(
|
||||||
introduced_variables.insert_named("a".into(), Loc::at_zero(var_a));
|
introduced_variables.insert_named("a".into(), Loc::at_zero(var_a));
|
||||||
introduced_variables.insert_named("b".into(), Loc::at_zero(var_b));
|
introduced_variables.insert_named("b".into(), Loc::at_zero(var_b));
|
||||||
|
|
||||||
let effect_b = build_effect_alias(
|
let effect_b = build_effect_opaque(
|
||||||
effect_symbol,
|
effect_symbol,
|
||||||
effect_tag_name.clone(),
|
|
||||||
"b",
|
"b",
|
||||||
var_b,
|
var_b,
|
||||||
Type::Variable(var_b),
|
Type::Variable(var_b),
|
||||||
|
@ -1119,19 +1122,11 @@ fn build_effect_loop(
|
||||||
let closure_var = var_store.fresh();
|
let closure_var = var_store.fresh();
|
||||||
introduced_variables.insert_wildcard(Loc::at_zero(closure_var));
|
introduced_variables.insert_wildcard(Loc::at_zero(closure_var));
|
||||||
|
|
||||||
let actual = {
|
let actual = Type::Function(
|
||||||
Type::TagUnion(
|
vec![Type::EmptyRec],
|
||||||
vec![(
|
Box::new(Type::Variable(closure_var)),
|
||||||
effect_tag_name,
|
Box::new(state_type.clone()),
|
||||||
vec![Type::Function(
|
);
|
||||||
vec![Type::EmptyRec],
|
|
||||||
Box::new(Type::Variable(closure_var)),
|
|
||||||
Box::new(state_type.clone()),
|
|
||||||
)],
|
|
||||||
)],
|
|
||||||
TypeExtension::Closed,
|
|
||||||
)
|
|
||||||
};
|
|
||||||
|
|
||||||
Type::Alias {
|
Type::Alias {
|
||||||
symbol: effect_symbol,
|
symbol: effect_symbol,
|
||||||
|
@ -1140,7 +1135,7 @@ fn build_effect_loop(
|
||||||
closure_var,
|
closure_var,
|
||||||
))],
|
))],
|
||||||
actual: Box::new(actual),
|
actual: Box::new(actual),
|
||||||
kind: AliasKind::Structural,
|
kind: AliasKind::Opaque,
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -1187,7 +1182,7 @@ fn build_effect_loop(
|
||||||
fn build_effect_loop_body(
|
fn build_effect_loop_body(
|
||||||
env: &mut Env,
|
env: &mut Env,
|
||||||
scope: &mut Scope,
|
scope: &mut Scope,
|
||||||
effect_tag_name: TagName,
|
effect_symbol: Symbol,
|
||||||
loop_symbol: Symbol,
|
loop_symbol: Symbol,
|
||||||
state_symbol: Symbol,
|
state_symbol: Symbol,
|
||||||
step_symbol: Symbol,
|
step_symbol: Symbol,
|
||||||
|
@ -1207,7 +1202,7 @@ fn build_effect_loop_body(
|
||||||
let inner_body = build_effect_loop_inner_body(
|
let inner_body = build_effect_loop_inner_body(
|
||||||
env,
|
env,
|
||||||
scope,
|
scope,
|
||||||
effect_tag_name.clone(),
|
effect_symbol,
|
||||||
loop_symbol,
|
loop_symbol,
|
||||||
state_symbol,
|
state_symbol,
|
||||||
step_symbol,
|
step_symbol,
|
||||||
|
@ -1217,7 +1212,7 @@ fn build_effect_loop_body(
|
||||||
let captured_symbols = vec![state_symbol, step_symbol];
|
let captured_symbols = vec![state_symbol, step_symbol];
|
||||||
wrap_in_effect_thunk(
|
wrap_in_effect_thunk(
|
||||||
inner_body,
|
inner_body,
|
||||||
effect_tag_name,
|
effect_symbol,
|
||||||
closure_name,
|
closure_name,
|
||||||
captured_symbols,
|
captured_symbols,
|
||||||
var_store,
|
var_store,
|
||||||
|
@ -1249,7 +1244,7 @@ fn applied_tag_pattern(
|
||||||
fn build_effect_loop_inner_body(
|
fn build_effect_loop_inner_body(
|
||||||
env: &mut Env,
|
env: &mut Env,
|
||||||
scope: &mut Scope,
|
scope: &mut Scope,
|
||||||
effect_tag_name: TagName,
|
effect_symbol: Symbol,
|
||||||
loop_symbol: Symbol,
|
loop_symbol: Symbol,
|
||||||
state_symbol: Symbol,
|
state_symbol: Symbol,
|
||||||
step_symbol: Symbol,
|
step_symbol: Symbol,
|
||||||
|
@ -1264,15 +1259,18 @@ fn build_effect_loop_inner_body(
|
||||||
// Effect thunk1 = step state
|
// Effect thunk1 = step state
|
||||||
let thunk_from_effect = {
|
let thunk_from_effect = {
|
||||||
let whole_var = var_store.fresh();
|
let whole_var = var_store.fresh();
|
||||||
let ext_var = var_store.fresh();
|
|
||||||
|
|
||||||
let thunk_var = var_store.fresh();
|
let thunk_var = var_store.fresh();
|
||||||
|
|
||||||
let pattern = Pattern::AppliedTag {
|
let (specialized_def_type, type_arguments, lambda_set_variables) =
|
||||||
ext_var,
|
build_fresh_opaque_variables(var_store);
|
||||||
|
let pattern = Pattern::UnwrappedOpaque {
|
||||||
whole_var,
|
whole_var,
|
||||||
tag_name: effect_tag_name.clone(),
|
opaque: effect_symbol,
|
||||||
arguments: vec![(thunk_var, Loc::at_zero(Pattern::Identifier(thunk1_symbol)))],
|
argument: Box::new((thunk_var, Loc::at_zero(Pattern::Identifier(thunk1_symbol)))),
|
||||||
|
specialized_def_type,
|
||||||
|
type_arguments,
|
||||||
|
lambda_set_variables,
|
||||||
};
|
};
|
||||||
|
|
||||||
let pattern_vars = SendMap::default();
|
let pattern_vars = SendMap::default();
|
||||||
|
@ -1332,15 +1330,10 @@ fn build_effect_loop_inner_body(
|
||||||
};
|
};
|
||||||
|
|
||||||
// ```
|
// ```
|
||||||
// Effect thunk2 = loop effect
|
// @Effect thunk2 = loop effect
|
||||||
// thunk2 {}
|
// thunk2 {}
|
||||||
// ```
|
// ```
|
||||||
let force_thunk2 = force_effect(
|
let force_thunk2 = force_effect(loop_new_state_step, effect_symbol, thunk2_symbol, var_store);
|
||||||
loop_new_state_step,
|
|
||||||
effect_tag_name,
|
|
||||||
thunk2_symbol,
|
|
||||||
var_store,
|
|
||||||
);
|
|
||||||
|
|
||||||
let step_branch = {
|
let step_branch = {
|
||||||
let step_tag_name = TagName::Global("Step".into());
|
let step_tag_name = TagName::Global("Step".into());
|
||||||
|
@ -1387,7 +1380,7 @@ pub fn build_host_exposed_def(
|
||||||
scope: &mut Scope,
|
scope: &mut Scope,
|
||||||
symbol: Symbol,
|
symbol: Symbol,
|
||||||
ident: &str,
|
ident: &str,
|
||||||
effect_tag_name: TagName,
|
effect_symbol: Symbol,
|
||||||
var_store: &mut VarStore,
|
var_store: &mut VarStore,
|
||||||
annotation: crate::annotation::Annotation,
|
annotation: crate::annotation::Annotation,
|
||||||
) -> Def {
|
) -> Def {
|
||||||
|
@ -1400,8 +1393,15 @@ pub fn build_host_exposed_def(
|
||||||
let mut linked_symbol_arguments: Vec<(Variable, Expr)> = Vec::new();
|
let mut linked_symbol_arguments: Vec<(Variable, Expr)> = Vec::new();
|
||||||
let mut captured_symbols: Vec<(Symbol, Variable)> = Vec::new();
|
let mut captured_symbols: Vec<(Symbol, Variable)> = Vec::new();
|
||||||
|
|
||||||
|
let crate::annotation::Annotation {
|
||||||
|
introduced_variables,
|
||||||
|
typ,
|
||||||
|
aliases,
|
||||||
|
..
|
||||||
|
} = annotation;
|
||||||
|
|
||||||
let def_body = {
|
let def_body = {
|
||||||
match annotation.typ.shallow_dealias() {
|
match typ.shallow_structural_dealias() {
|
||||||
Type::Function(args, _, _) => {
|
Type::Function(args, _, _) => {
|
||||||
for i in 0..args.len() {
|
for i in 0..args.len() {
|
||||||
let name = format!("closure_arg_{}_{}", ident, i);
|
let name = format!("closure_arg_{}_{}", ident, i);
|
||||||
|
@ -1462,11 +1462,15 @@ pub fn build_host_exposed_def(
|
||||||
loc_body: Box::new(Loc::at_zero(low_level_call)),
|
loc_body: Box::new(Loc::at_zero(low_level_call)),
|
||||||
});
|
});
|
||||||
|
|
||||||
let body = Expr::Tag {
|
let (specialized_def_type, type_arguments, lambda_set_variables) =
|
||||||
variant_var: var_store.fresh(),
|
build_fresh_opaque_variables(var_store);
|
||||||
ext_var: var_store.fresh(),
|
let body = Expr::OpaqueRef {
|
||||||
name: effect_tag_name,
|
opaque_var: var_store.fresh(),
|
||||||
arguments: vec![(var_store.fresh(), Loc::at_zero(effect_closure))],
|
name: effect_symbol,
|
||||||
|
argument: Box::new((var_store.fresh(), Loc::at_zero(effect_closure))),
|
||||||
|
specialized_def_type,
|
||||||
|
type_arguments,
|
||||||
|
lambda_set_variables,
|
||||||
};
|
};
|
||||||
|
|
||||||
Expr::Closure(ClosureData {
|
Expr::Closure(ClosureData {
|
||||||
|
@ -1523,20 +1527,24 @@ pub fn build_host_exposed_def(
|
||||||
loc_body: Box::new(Loc::at_zero(low_level_call)),
|
loc_body: Box::new(Loc::at_zero(low_level_call)),
|
||||||
});
|
});
|
||||||
|
|
||||||
Expr::Tag {
|
let (specialized_def_type, type_arguments, lambda_set_variables) =
|
||||||
variant_var: var_store.fresh(),
|
build_fresh_opaque_variables(var_store);
|
||||||
ext_var: var_store.fresh(),
|
Expr::OpaqueRef {
|
||||||
name: effect_tag_name,
|
opaque_var: var_store.fresh(),
|
||||||
arguments: vec![(var_store.fresh(), Loc::at_zero(effect_closure))],
|
name: effect_symbol,
|
||||||
|
argument: Box::new((var_store.fresh(), Loc::at_zero(effect_closure))),
|
||||||
|
specialized_def_type,
|
||||||
|
type_arguments,
|
||||||
|
lambda_set_variables,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
let def_annotation = crate::def::Annotation {
|
let def_annotation = crate::def::Annotation {
|
||||||
signature: annotation.typ,
|
signature: typ,
|
||||||
introduced_variables: annotation.introduced_variables,
|
introduced_variables,
|
||||||
aliases: annotation.aliases,
|
aliases,
|
||||||
region: Region::zero(),
|
region: Region::zero(),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -1549,9 +1557,19 @@ pub fn build_host_exposed_def(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn build_effect_alias(
|
pub fn build_effect_actual(a_type: Type, var_store: &mut VarStore) -> Type {
|
||||||
|
let closure_var = var_store.fresh();
|
||||||
|
|
||||||
|
Type::Function(
|
||||||
|
vec![Type::EmptyRec],
|
||||||
|
Box::new(Type::Variable(closure_var)),
|
||||||
|
Box::new(a_type),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Effect a := {} -> a
|
||||||
|
fn build_effect_opaque(
|
||||||
effect_symbol: Symbol,
|
effect_symbol: Symbol,
|
||||||
effect_tag_name: TagName,
|
|
||||||
a_name: &str,
|
a_name: &str,
|
||||||
a_var: Variable,
|
a_var: Variable,
|
||||||
a_type: Type,
|
a_type: Type,
|
||||||
|
@ -1561,47 +1579,39 @@ fn build_effect_alias(
|
||||||
let closure_var = var_store.fresh();
|
let closure_var = var_store.fresh();
|
||||||
introduced_variables.insert_wildcard(Loc::at_zero(closure_var));
|
introduced_variables.insert_wildcard(Loc::at_zero(closure_var));
|
||||||
|
|
||||||
let actual = {
|
let actual = Type::Function(
|
||||||
Type::TagUnion(
|
vec![Type::EmptyRec],
|
||||||
vec![(
|
Box::new(Type::Variable(closure_var)),
|
||||||
effect_tag_name,
|
Box::new(a_type),
|
||||||
vec![Type::Function(
|
);
|
||||||
vec![Type::EmptyRec],
|
|
||||||
Box::new(Type::Variable(closure_var)),
|
|
||||||
Box::new(a_type),
|
|
||||||
)],
|
|
||||||
)],
|
|
||||||
TypeExtension::Closed,
|
|
||||||
)
|
|
||||||
};
|
|
||||||
|
|
||||||
Type::Alias {
|
Type::Alias {
|
||||||
symbol: effect_symbol,
|
symbol: effect_symbol,
|
||||||
type_arguments: vec![(a_name.into(), Type::Variable(a_var))],
|
type_arguments: vec![(a_name.into(), Type::Variable(a_var))],
|
||||||
lambda_set_variables: vec![roc_types::types::LambdaSet(Type::Variable(closure_var))],
|
lambda_set_variables: vec![roc_types::types::LambdaSet(Type::Variable(closure_var))],
|
||||||
actual: Box::new(actual),
|
actual: Box::new(actual),
|
||||||
kind: AliasKind::Structural,
|
kind: AliasKind::Opaque,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn build_effect_actual(
|
fn build_fresh_opaque_variables(
|
||||||
effect_tag_name: TagName,
|
|
||||||
a_type: Type,
|
|
||||||
var_store: &mut VarStore,
|
var_store: &mut VarStore,
|
||||||
) -> Type {
|
) -> (Box<Type>, Vec<(Lowercase, Type)>, Vec<LambdaSet>) {
|
||||||
let closure_var = var_store.fresh();
|
let closure_var = var_store.fresh();
|
||||||
|
|
||||||
Type::TagUnion(
|
// NB: if there are bugs, check whether not introducing variables is a problem!
|
||||||
vec![(
|
// introduced_variables.insert_wildcard(Loc::at_zero(closure_var));
|
||||||
effect_tag_name,
|
|
||||||
vec![Type::Function(
|
let a_var = var_store.fresh();
|
||||||
vec![Type::EmptyRec],
|
let actual = Type::Function(
|
||||||
Box::new(Type::Variable(closure_var)),
|
vec![Type::EmptyRec],
|
||||||
Box::new(a_type),
|
Box::new(Type::Variable(closure_var)),
|
||||||
)],
|
Box::new(Type::Variable(a_var)),
|
||||||
)],
|
);
|
||||||
TypeExtension::Closed,
|
let type_arguments = vec![("a".into(), Type::Variable(a_var))];
|
||||||
)
|
let lambda_set_variables = vec![roc_types::types::LambdaSet(Type::Variable(closure_var))];
|
||||||
|
|
||||||
|
(Box::new(actual), type_arguments, lambda_set_variables)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
|
|
|
@ -7,7 +7,7 @@ use roc_region::all::{Loc, Region};
|
||||||
|
|
||||||
/// The canonicalization environment for a particular module.
|
/// The canonicalization environment for a particular module.
|
||||||
pub struct Env<'a> {
|
pub struct Env<'a> {
|
||||||
/// The module's path. Private tags and unqualified references to identifiers
|
/// The module's path. Opaques and unqualified references to identifiers
|
||||||
/// are assumed to be relative to this path.
|
/// are assumed to be relative to this path.
|
||||||
pub home: ModuleId,
|
pub home: ModuleId,
|
||||||
|
|
||||||
|
@ -182,8 +182,4 @@ impl<'a> Env<'a> {
|
||||||
pub fn problem(&mut self, problem: Problem) {
|
pub fn problem(&mut self, problem: Problem) {
|
||||||
self.problems.push(problem)
|
self.problems.push(problem)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn register_closure(&mut self, symbol: Symbol, references: References) {
|
|
||||||
self.closures.insert(symbol, references);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -163,8 +163,7 @@ pub enum Expr {
|
||||||
name: TagName,
|
name: TagName,
|
||||||
},
|
},
|
||||||
|
|
||||||
/// A wrapping of an opaque type, like `$Age 21`
|
/// A wrapping of an opaque type, like `@Age 21`
|
||||||
// TODO(opaques): $->@ above when opaques land
|
|
||||||
OpaqueRef {
|
OpaqueRef {
|
||||||
opaque_var: Variable,
|
opaque_var: Variable,
|
||||||
name: Symbol,
|
name: Symbol,
|
||||||
|
@ -293,6 +292,23 @@ pub struct WhenBranch {
|
||||||
pub guard: Option<Loc<Expr>>,
|
pub guard: Option<Loc<Expr>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl WhenBranch {
|
||||||
|
pub fn pattern_region(&self) -> Region {
|
||||||
|
Region::span_across(
|
||||||
|
&self
|
||||||
|
.patterns
|
||||||
|
.first()
|
||||||
|
.expect("when branch has no pattern?")
|
||||||
|
.region,
|
||||||
|
&self
|
||||||
|
.patterns
|
||||||
|
.last()
|
||||||
|
.expect("when branch has no pattern?")
|
||||||
|
.region,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn canonicalize_expr<'a>(
|
pub fn canonicalize_expr<'a>(
|
||||||
env: &mut Env<'a>,
|
env: &mut Env<'a>,
|
||||||
var_store: &mut VarStore,
|
var_store: &mut VarStore,
|
||||||
|
@ -699,7 +715,9 @@ pub fn canonicalize_expr<'a>(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
env.register_closure(symbol, output.references.clone());
|
// store the references of this function in the Env. This information is used
|
||||||
|
// when we canonicalize a surrounding def (if it exists)
|
||||||
|
env.closures.insert(symbol, output.references.clone());
|
||||||
|
|
||||||
let mut captured_symbols: Vec<_> = captured_symbols
|
let mut captured_symbols: Vec<_> = captured_symbols
|
||||||
.into_iter()
|
.into_iter()
|
||||||
|
@ -811,23 +829,6 @@ pub fn canonicalize_expr<'a>(
|
||||||
Output::default(),
|
Output::default(),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
ast::Expr::PrivateTag(tag) => {
|
|
||||||
let variant_var = var_store.fresh();
|
|
||||||
let ext_var = var_store.fresh();
|
|
||||||
let tag_ident = env.ident_ids.get_or_insert(&(*tag).into());
|
|
||||||
let symbol = Symbol::new(env.home, tag_ident);
|
|
||||||
let lambda_set_symbol = env.gen_unique_symbol();
|
|
||||||
|
|
||||||
(
|
|
||||||
ZeroArgumentTag {
|
|
||||||
name: TagName::Private(symbol),
|
|
||||||
variant_var,
|
|
||||||
ext_var,
|
|
||||||
closure_name: lambda_set_symbol,
|
|
||||||
},
|
|
||||||
Output::default(),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
ast::Expr::OpaqueRef(opaque_ref) => {
|
ast::Expr::OpaqueRef(opaque_ref) => {
|
||||||
// If we're here, the opaque reference is definitely not wrapping an argument - wrapped
|
// If we're here, the opaque reference is definitely not wrapping an argument - wrapped
|
||||||
// arguments are handled in the Apply branch.
|
// arguments are handled in the Apply branch.
|
||||||
|
|
|
@ -18,3 +18,4 @@ pub mod procedure;
|
||||||
mod reference_matrix;
|
mod reference_matrix;
|
||||||
pub mod scope;
|
pub mod scope;
|
||||||
pub mod string;
|
pub mod string;
|
||||||
|
pub mod traverse;
|
||||||
|
|
|
@ -8,8 +8,8 @@ use crate::pattern::Pattern;
|
||||||
use crate::scope::Scope;
|
use crate::scope::Scope;
|
||||||
use bumpalo::Bump;
|
use bumpalo::Bump;
|
||||||
use roc_collections::{MutMap, SendMap, VecSet};
|
use roc_collections::{MutMap, SendMap, VecSet};
|
||||||
|
use roc_module::ident::Ident;
|
||||||
use roc_module::ident::Lowercase;
|
use roc_module::ident::Lowercase;
|
||||||
use roc_module::ident::{Ident, TagName};
|
|
||||||
use roc_module::symbol::{IdentIds, ModuleId, ModuleIds, Symbol};
|
use roc_module::symbol::{IdentIds, ModuleId, ModuleIds, Symbol};
|
||||||
use roc_parse::ast;
|
use roc_parse::ast;
|
||||||
use roc_parse::header::HeaderFor;
|
use roc_parse::header::HeaderFor;
|
||||||
|
@ -116,23 +116,18 @@ impl GeneratedInfo {
|
||||||
)
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
let effect_tag_name = TagName::Private(effect_symbol);
|
|
||||||
|
|
||||||
{
|
{
|
||||||
let a_var = var_store.fresh();
|
let a_var = var_store.fresh();
|
||||||
|
|
||||||
let actual = crate::effect_module::build_effect_actual(
|
let actual =
|
||||||
effect_tag_name,
|
crate::effect_module::build_effect_actual(Type::Variable(a_var), var_store);
|
||||||
Type::Variable(a_var),
|
|
||||||
var_store,
|
|
||||||
);
|
|
||||||
|
|
||||||
scope.add_alias(
|
scope.add_alias(
|
||||||
effect_symbol,
|
effect_symbol,
|
||||||
Region::zero(),
|
Region::zero(),
|
||||||
vec![Loc::at_zero(("a".into(), a_var))],
|
vec![Loc::at_zero(("a".into(), a_var))],
|
||||||
actual,
|
actual,
|
||||||
AliasKind::Structural,
|
AliasKind::Opaque,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -433,7 +428,7 @@ pub fn canonicalize_module_defs<'a>(
|
||||||
&mut scope,
|
&mut scope,
|
||||||
*symbol,
|
*symbol,
|
||||||
&ident,
|
&ident,
|
||||||
TagName::Private(effect_symbol),
|
effect_symbol,
|
||||||
var_store,
|
var_store,
|
||||||
annotation,
|
annotation,
|
||||||
);
|
);
|
||||||
|
|
|
@ -151,7 +151,6 @@ pub fn desugar_expr<'a>(arena: &'a Bump, loc_expr: &'a Loc<Expr<'a>>) -> &'a Loc
|
||||||
| MalformedClosure
|
| MalformedClosure
|
||||||
| PrecedenceConflict { .. }
|
| PrecedenceConflict { .. }
|
||||||
| GlobalTag(_)
|
| GlobalTag(_)
|
||||||
| PrivateTag(_)
|
|
||||||
| OpaqueRef(_) => loc_expr,
|
| OpaqueRef(_) => loc_expr,
|
||||||
|
|
||||||
Access(sub_expr, paths) => {
|
Access(sub_expr, paths) => {
|
||||||
|
@ -425,7 +424,6 @@ fn binop_to_function(binop: BinOp) -> (&'static str, &'static str) {
|
||||||
Slash => (ModuleName::NUM, "div"),
|
Slash => (ModuleName::NUM, "div"),
|
||||||
DoubleSlash => (ModuleName::NUM, "divTrunc"),
|
DoubleSlash => (ModuleName::NUM, "divTrunc"),
|
||||||
Percent => (ModuleName::NUM, "rem"),
|
Percent => (ModuleName::NUM, "rem"),
|
||||||
DoublePercent => (ModuleName::NUM, "mod"),
|
|
||||||
Plus => (ModuleName::NUM, "add"),
|
Plus => (ModuleName::NUM, "add"),
|
||||||
Minus => (ModuleName::NUM, "sub"),
|
Minus => (ModuleName::NUM, "sub"),
|
||||||
Equals => (ModuleName::BOOL, "isEq"),
|
Equals => (ModuleName::BOOL, "isEq"),
|
||||||
|
|
|
@ -82,6 +82,31 @@ pub enum Pattern {
|
||||||
MalformedPattern(MalformedPatternProblem, Region),
|
MalformedPattern(MalformedPatternProblem, Region),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Pattern {
|
||||||
|
pub fn opt_var(&self) -> Option<Variable> {
|
||||||
|
use Pattern::*;
|
||||||
|
match self {
|
||||||
|
Identifier(_) => None,
|
||||||
|
|
||||||
|
AppliedTag { whole_var, .. } => Some(*whole_var),
|
||||||
|
UnwrappedOpaque { whole_var, .. } => Some(*whole_var),
|
||||||
|
RecordDestructure { whole_var, .. } => Some(*whole_var),
|
||||||
|
NumLiteral(var, ..) => Some(*var),
|
||||||
|
IntLiteral(var, ..) => Some(*var),
|
||||||
|
FloatLiteral(var, ..) => Some(*var),
|
||||||
|
StrLiteral(_) => None,
|
||||||
|
SingleQuote(_) => None,
|
||||||
|
Underscore => None,
|
||||||
|
|
||||||
|
AbilityMemberSpecialization { .. } => None,
|
||||||
|
|
||||||
|
Shadowed(..) | OpaqueNotInScope(..) | UnsupportedPattern(..) | MalformedPattern(..) => {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
pub struct RecordDestruct {
|
pub struct RecordDestruct {
|
||||||
pub var: Variable,
|
pub var: Variable,
|
||||||
|
@ -244,17 +269,6 @@ pub fn canonicalize_pattern<'a>(
|
||||||
arguments: vec![],
|
arguments: vec![],
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
PrivateTag(name) => {
|
|
||||||
let ident_id = env.ident_ids.get_or_insert(&(*name).into());
|
|
||||||
|
|
||||||
// Canonicalize the tag's name.
|
|
||||||
Pattern::AppliedTag {
|
|
||||||
whole_var: var_store.fresh(),
|
|
||||||
ext_var: var_store.fresh(),
|
|
||||||
tag_name: TagName::Private(Symbol::new(env.home, ident_id)),
|
|
||||||
arguments: vec![],
|
|
||||||
}
|
|
||||||
}
|
|
||||||
OpaqueRef(name) => {
|
OpaqueRef(name) => {
|
||||||
// If this opaque ref had an argument, we would be in the "Apply" branch.
|
// If this opaque ref had an argument, we would be in the "Apply" branch.
|
||||||
let loc_name = Loc::at(region, (*name).into());
|
let loc_name = Loc::at(region, (*name).into());
|
||||||
|
@ -289,17 +303,6 @@ pub fn canonicalize_pattern<'a>(
|
||||||
arguments: can_patterns,
|
arguments: can_patterns,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
PrivateTag(name) => {
|
|
||||||
let ident_id = env.ident_ids.get_or_insert(&name.into());
|
|
||||||
let tag_name = TagName::Private(Symbol::new(env.home, ident_id));
|
|
||||||
|
|
||||||
Pattern::AppliedTag {
|
|
||||||
whole_var: var_store.fresh(),
|
|
||||||
ext_var: var_store.fresh(),
|
|
||||||
tag_name,
|
|
||||||
arguments: can_patterns,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
OpaqueRef(name) => match scope.lookup_opaque_ref(name, tag.region) {
|
OpaqueRef(name) => match scope.lookup_opaque_ref(name, tag.region) {
|
||||||
Ok((opaque, opaque_def)) => {
|
Ok((opaque, opaque_def)) => {
|
||||||
|
|
|
@ -1,7 +1,8 @@
|
||||||
// see if we get better performance with different integer types
|
// see if we get better performance with different integer types
|
||||||
pub(crate) type Element = usize;
|
type Order = bitvec::order::Lsb0;
|
||||||
pub(crate) type BitVec = bitvec::vec::BitVec<Element>;
|
type Element = usize;
|
||||||
pub(crate) type BitSlice = bitvec::prelude::BitSlice<Element>;
|
type BitVec = bitvec::vec::BitVec<Element, Order>;
|
||||||
|
type BitSlice = bitvec::prelude::BitSlice<Element, Order>;
|
||||||
|
|
||||||
/// A square boolean matrix used to store relations
|
/// A square boolean matrix used to store relations
|
||||||
///
|
///
|
||||||
|
@ -36,8 +37,8 @@ impl ReferenceMatrix {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn get(&self, index: usize) -> bool {
|
pub fn get_row_col(&self, row: usize, col: usize) -> bool {
|
||||||
self.bitvec[index]
|
self.bitvec[row * self.length + col]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -50,6 +51,7 @@ impl ReferenceMatrix {
|
||||||
//
|
//
|
||||||
// Thank you, Samuel!
|
// Thank you, Samuel!
|
||||||
impl ReferenceMatrix {
|
impl ReferenceMatrix {
|
||||||
|
#[allow(dead_code)]
|
||||||
pub fn topological_sort_into_groups(&self) -> TopologicalSort {
|
pub fn topological_sort_into_groups(&self) -> TopologicalSort {
|
||||||
if self.length == 0 {
|
if self.length == 0 {
|
||||||
return TopologicalSort::Groups { groups: Vec::new() };
|
return TopologicalSort::Groups { groups: Vec::new() };
|
||||||
|
@ -128,7 +130,7 @@ impl ReferenceMatrix {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get the strongly-connected components of the set of input nodes.
|
/// Get the strongly-connected components of the set of input nodes.
|
||||||
pub fn strongly_connected_components(&self, nodes: &[u32]) -> Vec<Vec<u32>> {
|
pub fn strongly_connected_components(&self, nodes: &[u32]) -> Sccs {
|
||||||
let mut params = Params::new(self.length, nodes);
|
let mut params = Params::new(self.length, nodes);
|
||||||
|
|
||||||
'outer: loop {
|
'outer: loop {
|
||||||
|
@ -147,6 +149,7 @@ impl ReferenceMatrix {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[allow(dead_code)]
|
||||||
pub(crate) enum TopologicalSort {
|
pub(crate) enum TopologicalSort {
|
||||||
/// There were no cycles, all nodes have been partitioned into groups
|
/// There were no cycles, all nodes have been partitioned into groups
|
||||||
Groups { groups: Vec<Vec<u32>> },
|
Groups { groups: Vec<Vec<u32>> },
|
||||||
|
@ -172,7 +175,7 @@ struct Params {
|
||||||
c: usize,
|
c: usize,
|
||||||
p: Vec<u32>,
|
p: Vec<u32>,
|
||||||
s: Vec<u32>,
|
s: Vec<u32>,
|
||||||
scc: Vec<Vec<u32>>,
|
scc: Sccs,
|
||||||
scca: Vec<u32>,
|
scca: Vec<u32>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -189,7 +192,10 @@ impl Params {
|
||||||
c: 0,
|
c: 0,
|
||||||
s: Vec::new(),
|
s: Vec::new(),
|
||||||
p: Vec::new(),
|
p: Vec::new(),
|
||||||
scc: Vec::new(),
|
scc: Sccs {
|
||||||
|
matrix: ReferenceMatrix::new(length),
|
||||||
|
components: 0,
|
||||||
|
},
|
||||||
scca: Vec::new(),
|
scca: Vec::new(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -230,15 +236,47 @@ fn recurse_onto(length: usize, bitvec: &BitVec, v: usize, params: &mut Params) {
|
||||||
if params.p.last() == Some(&(v as u32)) {
|
if params.p.last() == Some(&(v as u32)) {
|
||||||
params.p.pop();
|
params.p.pop();
|
||||||
|
|
||||||
let mut component = Vec::new();
|
|
||||||
while let Some(node) = params.s.pop() {
|
while let Some(node) = params.s.pop() {
|
||||||
component.push(node);
|
params
|
||||||
|
.scc
|
||||||
|
.matrix
|
||||||
|
.set_row_col(params.scc.components, node as usize, true);
|
||||||
params.scca.push(node);
|
params.scca.push(node);
|
||||||
params.preorders[node as usize] = Preorder::Removed;
|
params.preorders[node as usize] = Preorder::Removed;
|
||||||
if node as usize == v {
|
if node as usize == v {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
params.scc.push(component);
|
|
||||||
|
params.scc.components += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub(crate) struct Sccs {
|
||||||
|
components: usize,
|
||||||
|
matrix: ReferenceMatrix,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Sccs {
|
||||||
|
/// Iterate over the individual components. Each component is represented as a bit vector where
|
||||||
|
/// a one indicates that the node is part of the group and a zero that it is not.
|
||||||
|
///
|
||||||
|
/// A good way to get the actual nodes is the `.iter_ones()` method.
|
||||||
|
///
|
||||||
|
/// It is guaranteed that a group is non-empty, and that flattening the groups gives a valid
|
||||||
|
/// topological ordering.
|
||||||
|
pub fn groups(&self) -> std::iter::Take<bitvec::slice::Chunks<'_, Element, Order>> {
|
||||||
|
// work around a panic when requesting a chunk size of 0
|
||||||
|
let length = if self.matrix.length == 0 {
|
||||||
|
// the `.take(self.components)` ensures the resulting iterator will be empty
|
||||||
|
assert!(self.components == 0);
|
||||||
|
|
||||||
|
1
|
||||||
|
} else {
|
||||||
|
self.matrix.length
|
||||||
|
};
|
||||||
|
|
||||||
|
self.matrix.bitvec.chunks(length).take(self.components)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -134,7 +134,12 @@ fn add_aliases(var_store: &mut VarStore) -> SendMap<Symbol, Alias> {
|
||||||
let mut aliases = SendMap::default();
|
let mut aliases = SendMap::default();
|
||||||
|
|
||||||
for (symbol, builtin_alias) in solved_aliases {
|
for (symbol, builtin_alias) in solved_aliases {
|
||||||
let BuiltinAlias { region, vars, typ } = builtin_alias;
|
let BuiltinAlias {
|
||||||
|
region,
|
||||||
|
vars,
|
||||||
|
typ,
|
||||||
|
kind,
|
||||||
|
} = builtin_alias;
|
||||||
|
|
||||||
let mut free_vars = FreeVars::default();
|
let mut free_vars = FreeVars::default();
|
||||||
let typ = roc_types::solved_types::to_type(&typ, &mut free_vars, var_store);
|
let typ = roc_types::solved_types::to_type(&typ, &mut free_vars, var_store);
|
||||||
|
@ -153,8 +158,7 @@ fn add_aliases(var_store: &mut VarStore) -> SendMap<Symbol, Alias> {
|
||||||
lambda_set_variables: Vec::new(),
|
lambda_set_variables: Vec::new(),
|
||||||
recursion_variables: MutSet::default(),
|
recursion_variables: MutSet::default(),
|
||||||
type_variables: variables,
|
type_variables: variables,
|
||||||
// TODO(opaques): replace when opaques are included in the stdlib
|
kind,
|
||||||
kind: AliasKind::Structural,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
aliases.insert(symbol, alias);
|
aliases.insert(symbol, alias);
|
||||||
|
@ -201,11 +205,6 @@ impl Scope {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn lookup(&self, ident: &Ident, region: Region) -> Result<Symbol, RuntimeError> {
|
pub fn lookup(&self, ident: &Ident, region: Region) -> Result<Symbol, RuntimeError> {
|
||||||
println!(
|
|
||||||
"stats: string length: {}, ident len {}",
|
|
||||||
self.idents.string.len(),
|
|
||||||
self.idents.len()
|
|
||||||
);
|
|
||||||
match self.idents.get_symbol(ident) {
|
match self.idents.get_symbol(ident) {
|
||||||
Some(symbol) => Ok(symbol),
|
Some(symbol) => Ok(symbol),
|
||||||
None => {
|
None => {
|
||||||
|
@ -230,15 +229,14 @@ impl Scope {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Check if there is an opaque type alias referenced by `opaque_ref` referenced in the
|
/// Check if there is an opaque type alias referenced by `opaque_ref` referenced in the
|
||||||
/// current scope. E.g. `$Age` must reference an opaque `Age` declared in this module, not any
|
/// current scope. E.g. `@Age` must reference an opaque `Age` declared in this module, not any
|
||||||
/// other!
|
/// other!
|
||||||
// TODO(opaques): $->@ in the above comment
|
|
||||||
pub fn lookup_opaque_ref(
|
pub fn lookup_opaque_ref(
|
||||||
&self,
|
&self,
|
||||||
opaque_ref: &str,
|
opaque_ref: &str,
|
||||||
lookup_region: Region,
|
lookup_region: Region,
|
||||||
) -> Result<(Symbol, &Alias), RuntimeError> {
|
) -> Result<(Symbol, &Alias), RuntimeError> {
|
||||||
debug_assert!(opaque_ref.starts_with('$'));
|
debug_assert!(opaque_ref.starts_with('@'));
|
||||||
let opaque = opaque_ref[1..].into();
|
let opaque = opaque_ref[1..].into();
|
||||||
|
|
||||||
match self.idents.get_symbol_and_region(&opaque) {
|
match self.idents.get_symbol_and_region(&opaque) {
|
||||||
|
|
184
compiler/can/src/traverse.rs
Normal file
184
compiler/can/src/traverse.rs
Normal file
|
@ -0,0 +1,184 @@
|
||||||
|
//! Traversals over the can ast.
|
||||||
|
|
||||||
|
use roc_region::all::{Loc, Region};
|
||||||
|
use roc_types::subs::Variable;
|
||||||
|
|
||||||
|
use crate::{
|
||||||
|
def::{Annotation, Declaration, Def},
|
||||||
|
expr::{ClosureData, Expr, WhenBranch},
|
||||||
|
pattern::Pattern,
|
||||||
|
};
|
||||||
|
|
||||||
|
macro_rules! visit_list {
|
||||||
|
($visitor:ident, $walk:ident, $list:expr) => {
|
||||||
|
for elem in $list {
|
||||||
|
$visitor.$walk(elem)
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
fn walk_decls<V: Visitor>(visitor: &mut V, decls: &[Declaration]) {
|
||||||
|
visit_list!(visitor, visit_decl, decls)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn walk_decl<V: Visitor>(visitor: &mut V, decl: &Declaration) {
|
||||||
|
match decl {
|
||||||
|
Declaration::Declare(def) => {
|
||||||
|
visitor.visit_def(def);
|
||||||
|
}
|
||||||
|
Declaration::DeclareRec(defs) => {
|
||||||
|
visit_list!(visitor, visit_def, defs)
|
||||||
|
}
|
||||||
|
Declaration::Builtin(def) => visitor.visit_def(def),
|
||||||
|
Declaration::InvalidCycle(_cycles) => {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn walk_def<V: Visitor>(visitor: &mut V, def: &Def) {
|
||||||
|
let Def {
|
||||||
|
loc_pattern,
|
||||||
|
loc_expr,
|
||||||
|
annotation,
|
||||||
|
expr_var,
|
||||||
|
..
|
||||||
|
} = def;
|
||||||
|
|
||||||
|
visitor.visit_pattern(
|
||||||
|
&loc_pattern.value,
|
||||||
|
loc_pattern.region,
|
||||||
|
loc_pattern.value.opt_var(),
|
||||||
|
);
|
||||||
|
visitor.visit_expr(&loc_expr.value, loc_expr.region, *expr_var);
|
||||||
|
if let Some(annot) = &annotation {
|
||||||
|
visitor.visit_annotation(annot);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn walk_expr<V: Visitor>(visitor: &mut V, expr: &Expr) {
|
||||||
|
match expr {
|
||||||
|
Expr::Closure(closure_data) => walk_closure(visitor, closure_data),
|
||||||
|
Expr::When {
|
||||||
|
cond_var,
|
||||||
|
expr_var,
|
||||||
|
loc_cond,
|
||||||
|
branches,
|
||||||
|
region: _,
|
||||||
|
} => {
|
||||||
|
walk_when(visitor, *cond_var, *expr_var, loc_cond, branches);
|
||||||
|
}
|
||||||
|
e => todo!("{:?}", e),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn walk_closure<V: Visitor>(visitor: &mut V, clos: &ClosureData) {
|
||||||
|
let ClosureData {
|
||||||
|
arguments,
|
||||||
|
loc_body,
|
||||||
|
return_type,
|
||||||
|
..
|
||||||
|
} = clos;
|
||||||
|
|
||||||
|
arguments
|
||||||
|
.iter()
|
||||||
|
.for_each(|(var, arg)| visitor.visit_pattern(&arg.value, arg.region, Some(*var)));
|
||||||
|
|
||||||
|
visitor.visit_expr(&loc_body.value, loc_body.region, *return_type);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn walk_when<V: Visitor>(
|
||||||
|
visitor: &mut V,
|
||||||
|
cond_var: Variable,
|
||||||
|
expr_var: Variable,
|
||||||
|
loc_cond: &Loc<Expr>,
|
||||||
|
branches: &[WhenBranch],
|
||||||
|
) {
|
||||||
|
visitor.visit_expr(&loc_cond.value, loc_cond.region, cond_var);
|
||||||
|
|
||||||
|
branches
|
||||||
|
.iter()
|
||||||
|
.for_each(|branch| walk_when_branch(visitor, branch, expr_var));
|
||||||
|
}
|
||||||
|
|
||||||
|
fn walk_when_branch<V: Visitor>(visitor: &mut V, branch: &WhenBranch, expr_var: Variable) {
|
||||||
|
let WhenBranch {
|
||||||
|
patterns,
|
||||||
|
value,
|
||||||
|
guard,
|
||||||
|
} = branch;
|
||||||
|
|
||||||
|
patterns
|
||||||
|
.iter()
|
||||||
|
.for_each(|pat| visitor.visit_pattern(&pat.value, pat.region, pat.value.opt_var()));
|
||||||
|
visitor.visit_expr(&value.value, value.region, expr_var);
|
||||||
|
if let Some(guard) = guard {
|
||||||
|
visitor.visit_expr(&guard.value, guard.region, Variable::BOOL);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn walk_pattern<V: Visitor>(_visitor: &mut V, _pat: &Pattern) {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
|
||||||
|
trait Visitor: Sized {
|
||||||
|
fn visit_decls(&mut self, decls: &[Declaration]) {
|
||||||
|
walk_decls(self, decls);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn visit_decl(&mut self, decl: &Declaration) {
|
||||||
|
walk_decl(self, decl);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn visit_def(&mut self, def: &Def) {
|
||||||
|
walk_def(self, def);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn visit_pattern(&mut self, pat: &Pattern, _region: Region, _opt_var: Option<Variable>) {
|
||||||
|
walk_pattern(self, pat)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn visit_annotation(&mut self, _pat: &Annotation) {
|
||||||
|
// TODO
|
||||||
|
}
|
||||||
|
|
||||||
|
fn visit_expr(&mut self, expr: &Expr, _region: Region, _var: Variable) {
|
||||||
|
walk_expr(self, expr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct TypeAtVisitor {
|
||||||
|
region: Region,
|
||||||
|
typ: Option<Variable>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Visitor for TypeAtVisitor {
|
||||||
|
fn visit_expr(&mut self, expr: &Expr, region: Region, var: Variable) {
|
||||||
|
if region == self.region {
|
||||||
|
debug_assert!(self.typ.is_none());
|
||||||
|
self.typ = Some(var);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if region.contains(&self.region) {
|
||||||
|
walk_expr(self, expr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn visit_pattern(&mut self, pat: &Pattern, region: Region, opt_var: Option<Variable>) {
|
||||||
|
if region == self.region {
|
||||||
|
debug_assert!(self.typ.is_none());
|
||||||
|
self.typ = opt_var;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if region.contains(&self.region) {
|
||||||
|
walk_pattern(self, pat)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Attempts to find the type of an expression at `region`, if it exists.
|
||||||
|
pub fn find_type_at(region: Region, decls: &[Declaration]) -> Option<Variable> {
|
||||||
|
let mut visitor = TypeAtVisitor { region, typ: None };
|
||||||
|
visitor.visit_decls(decls);
|
||||||
|
visitor.typ
|
||||||
|
}
|
|
@ -87,6 +87,18 @@ impl<K: PartialEq, V> VecMap<K, V> {
|
||||||
pub fn values(&self) -> impl Iterator<Item = &V> {
|
pub fn values(&self) -> impl Iterator<Item = &V> {
|
||||||
self.values.iter()
|
self.values.iter()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn unzip(self) -> (Vec<K>, Vec<V>) {
|
||||||
|
(self.keys, self.values)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// # Safety
|
||||||
|
///
|
||||||
|
/// keys and values must have the same length, and there must not
|
||||||
|
/// be any duplicates in the keys vector
|
||||||
|
pub unsafe fn zip(keys: Vec<K>, values: Vec<V>) -> Self {
|
||||||
|
Self { keys, values }
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<K: Ord, V> Extend<(K, V)> for VecMap<K, V> {
|
impl<K: Ord, V> Extend<(K, V)> for VecMap<K, V> {
|
||||||
|
|
|
@ -2,13 +2,13 @@ use arrayvec::ArrayVec;
|
||||||
use roc_can::constraint::{Constraint, Constraints};
|
use roc_can::constraint::{Constraint, Constraints};
|
||||||
use roc_can::expected::Expected::{self, *};
|
use roc_can::expected::Expected::{self, *};
|
||||||
use roc_can::num::{FloatBound, FloatWidth, IntBound, IntWidth, NumericBound, SignDemand};
|
use roc_can::num::{FloatBound, FloatWidth, IntBound, IntWidth, NumericBound, SignDemand};
|
||||||
use roc_module::ident::{Lowercase, TagName};
|
use roc_module::ident::Lowercase;
|
||||||
use roc_module::symbol::Symbol;
|
use roc_module::symbol::Symbol;
|
||||||
use roc_region::all::Region;
|
use roc_region::all::Region;
|
||||||
use roc_types::subs::Variable;
|
use roc_types::subs::Variable;
|
||||||
|
use roc_types::types::Reason;
|
||||||
use roc_types::types::Type::{self, *};
|
use roc_types::types::Type::{self, *};
|
||||||
use roc_types::types::{AliasKind, Category};
|
use roc_types::types::{AliasKind, Category};
|
||||||
use roc_types::types::{Reason, TypeExtension};
|
|
||||||
|
|
||||||
#[must_use]
|
#[must_use]
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
|
@ -163,14 +163,14 @@ fn builtin_alias(
|
||||||
symbol: Symbol,
|
symbol: Symbol,
|
||||||
type_arguments: Vec<(Lowercase, Type)>,
|
type_arguments: Vec<(Lowercase, Type)>,
|
||||||
actual: Box<Type>,
|
actual: Box<Type>,
|
||||||
|
kind: AliasKind,
|
||||||
) -> Type {
|
) -> Type {
|
||||||
Type::Alias {
|
Type::Alias {
|
||||||
symbol,
|
symbol,
|
||||||
type_arguments,
|
type_arguments,
|
||||||
actual,
|
actual,
|
||||||
lambda_set_variables: vec![],
|
lambda_set_variables: vec![],
|
||||||
// TODO(opaques): revisit later
|
kind,
|
||||||
kind: AliasKind::Structural,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -180,49 +180,48 @@ pub fn num_float(range: Type) -> Type {
|
||||||
Symbol::NUM_FLOAT,
|
Symbol::NUM_FLOAT,
|
||||||
vec![("range".into(), range.clone())],
|
vec![("range".into(), range.clone())],
|
||||||
Box::new(num_num(num_floatingpoint(range))),
|
Box::new(num_num(num_floatingpoint(range))),
|
||||||
|
AliasKind::Structural,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn num_floatingpoint(range: Type) -> Type {
|
pub fn num_floatingpoint(range: Type) -> Type {
|
||||||
let alias_content = Type::TagUnion(
|
|
||||||
vec![(
|
|
||||||
TagName::Private(Symbol::NUM_AT_FLOATINGPOINT),
|
|
||||||
vec![range.clone()],
|
|
||||||
)],
|
|
||||||
TypeExtension::Closed,
|
|
||||||
);
|
|
||||||
|
|
||||||
builtin_alias(
|
builtin_alias(
|
||||||
Symbol::NUM_FLOATINGPOINT,
|
Symbol::NUM_FLOATINGPOINT,
|
||||||
vec![("range".into(), range)],
|
vec![("range".into(), range.clone())],
|
||||||
Box::new(alias_content),
|
Box::new(range),
|
||||||
|
AliasKind::Opaque,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn num_u32() -> Type {
|
pub fn num_u32() -> Type {
|
||||||
builtin_alias(Symbol::NUM_U32, vec![], Box::new(num_int(num_unsigned32())))
|
builtin_alias(
|
||||||
|
Symbol::NUM_U32,
|
||||||
|
vec![],
|
||||||
|
Box::new(num_int(num_unsigned32())),
|
||||||
|
AliasKind::Structural,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
fn num_unsigned32() -> Type {
|
fn num_unsigned32() -> Type {
|
||||||
let alias_content = Type::TagUnion(
|
builtin_alias(
|
||||||
vec![(TagName::Private(Symbol::NUM_AT_UNSIGNED32), vec![])],
|
Symbol::NUM_UNSIGNED32,
|
||||||
TypeExtension::Closed,
|
vec![],
|
||||||
);
|
Box::new(Type::EmptyTagUnion),
|
||||||
|
AliasKind::Opaque,
|
||||||
builtin_alias(Symbol::NUM_UNSIGNED32, vec![], Box::new(alias_content))
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn num_binary64() -> Type {
|
pub fn num_binary64() -> Type {
|
||||||
let alias_content = Type::TagUnion(
|
builtin_alias(
|
||||||
vec![(TagName::Private(Symbol::NUM_AT_BINARY64), vec![])],
|
Symbol::NUM_BINARY64,
|
||||||
TypeExtension::Closed,
|
vec![],
|
||||||
);
|
Box::new(Type::EmptyTagUnion),
|
||||||
|
AliasKind::Opaque,
|
||||||
builtin_alias(Symbol::NUM_BINARY64, vec![], Box::new(alias_content))
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
|
@ -231,47 +230,37 @@ pub fn num_int(range: Type) -> Type {
|
||||||
Symbol::NUM_INT,
|
Symbol::NUM_INT,
|
||||||
vec![("range".into(), range.clone())],
|
vec![("range".into(), range.clone())],
|
||||||
Box::new(num_num(num_integer(range))),
|
Box::new(num_num(num_integer(range))),
|
||||||
|
AliasKind::Structural,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn num_signed64() -> Type {
|
pub fn num_signed64() -> Type {
|
||||||
let alias_content = Type::TagUnion(
|
builtin_alias(
|
||||||
vec![(TagName::Private(Symbol::NUM_AT_SIGNED64), vec![])],
|
Symbol::NUM_SIGNED64,
|
||||||
TypeExtension::Closed,
|
vec![],
|
||||||
);
|
Box::new(Type::EmptyTagUnion),
|
||||||
|
AliasKind::Opaque,
|
||||||
builtin_alias(Symbol::NUM_SIGNED64, vec![], Box::new(alias_content))
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn num_integer(range: Type) -> Type {
|
pub fn num_integer(range: Type) -> Type {
|
||||||
let alias_content = Type::TagUnion(
|
|
||||||
vec![(
|
|
||||||
TagName::Private(Symbol::NUM_AT_INTEGER),
|
|
||||||
vec![range.clone()],
|
|
||||||
)],
|
|
||||||
TypeExtension::Closed,
|
|
||||||
);
|
|
||||||
|
|
||||||
builtin_alias(
|
builtin_alias(
|
||||||
Symbol::NUM_INTEGER,
|
Symbol::NUM_INTEGER,
|
||||||
vec![("range".into(), range)],
|
vec![("range".into(), range.clone())],
|
||||||
Box::new(alias_content),
|
Box::new(range),
|
||||||
|
AliasKind::Opaque,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn num_num(typ: Type) -> Type {
|
pub fn num_num(typ: Type) -> Type {
|
||||||
let alias_content = Type::TagUnion(
|
|
||||||
vec![(TagName::Private(Symbol::NUM_AT_NUM), vec![typ.clone()])],
|
|
||||||
TypeExtension::Closed,
|
|
||||||
);
|
|
||||||
|
|
||||||
builtin_alias(
|
builtin_alias(
|
||||||
Symbol::NUM_NUM,
|
Symbol::NUM_NUM,
|
||||||
vec![("range".into(), typ)],
|
vec![("range".into(), typ.clone())],
|
||||||
Box::new(alias_content),
|
Box::new(typ),
|
||||||
|
AliasKind::Opaque,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -585,20 +585,21 @@ pub fn constrain_expr(
|
||||||
branches,
|
branches,
|
||||||
..
|
..
|
||||||
} => {
|
} => {
|
||||||
// Infer the condition expression's type.
|
|
||||||
let cond_var = *cond_var;
|
let cond_var = *cond_var;
|
||||||
let cond_type = Variable(cond_var);
|
let cond_type = Variable(cond_var);
|
||||||
let expr_con = constrain_expr(
|
|
||||||
constraints,
|
|
||||||
env,
|
|
||||||
region,
|
|
||||||
&loc_cond.value,
|
|
||||||
NoExpectation(cond_type.clone()),
|
|
||||||
);
|
|
||||||
|
|
||||||
let branch_var = *expr_var;
|
let branch_var = *expr_var;
|
||||||
let branch_type = Variable(branch_var);
|
let branch_type = Variable(branch_var);
|
||||||
|
|
||||||
|
let branches_region = {
|
||||||
|
debug_assert!(!branches.is_empty());
|
||||||
|
Region::span_across(
|
||||||
|
&loc_cond.region,
|
||||||
|
// &branches.first().unwrap().region(),
|
||||||
|
&branches.last().unwrap().pattern_region(),
|
||||||
|
)
|
||||||
|
};
|
||||||
|
|
||||||
let branch_expr_reason =
|
let branch_expr_reason =
|
||||||
|expected: &Expected<Type>, index, branch_region| match expected {
|
|expected: &Expected<Type>, index, branch_region| match expected {
|
||||||
FromAnnotation(name, arity, ann_source, _typ) => {
|
FromAnnotation(name, arity, ann_source, _typ) => {
|
||||||
|
@ -647,12 +648,20 @@ pub fn constrain_expr(
|
||||||
// constraints.
|
// constraints.
|
||||||
let mut pattern_vars = Vec::with_capacity(branches.len());
|
let mut pattern_vars = Vec::with_capacity(branches.len());
|
||||||
let mut pattern_headers = SendMap::default();
|
let mut pattern_headers = SendMap::default();
|
||||||
let mut pattern_cons = Vec::with_capacity(branches.len());
|
let mut pattern_cons = Vec::with_capacity(branches.len() + 1);
|
||||||
let mut branch_cons = Vec::with_capacity(branches.len());
|
let mut branch_cons = Vec::with_capacity(branches.len());
|
||||||
|
|
||||||
for (index, when_branch) in branches.iter().enumerate() {
|
for (index, when_branch) in branches.iter().enumerate() {
|
||||||
let pattern_region =
|
let expected_pattern = |sub_pattern, sub_region| {
|
||||||
Region::across_all(when_branch.patterns.iter().map(|v| &v.region));
|
PExpected::ForReason(
|
||||||
|
PReason::WhenMatch {
|
||||||
|
index: HumanIndex::zero_based(index),
|
||||||
|
sub_pattern,
|
||||||
|
},
|
||||||
|
cond_type.clone(),
|
||||||
|
sub_region,
|
||||||
|
)
|
||||||
|
};
|
||||||
|
|
||||||
let (new_pattern_vars, new_pattern_headers, pattern_con, branch_con) =
|
let (new_pattern_vars, new_pattern_headers, pattern_con, branch_con) =
|
||||||
constrain_when_branch_help(
|
constrain_when_branch_help(
|
||||||
|
@ -660,13 +669,7 @@ pub fn constrain_expr(
|
||||||
env,
|
env,
|
||||||
region,
|
region,
|
||||||
when_branch,
|
when_branch,
|
||||||
PExpected::ForReason(
|
expected_pattern,
|
||||||
PReason::WhenMatch {
|
|
||||||
index: HumanIndex::zero_based(index),
|
|
||||||
},
|
|
||||||
cond_type.clone(),
|
|
||||||
pattern_region,
|
|
||||||
),
|
|
||||||
branch_expr_reason(
|
branch_expr_reason(
|
||||||
&expected,
|
&expected,
|
||||||
HumanIndex::zero_based(index),
|
HumanIndex::zero_based(index),
|
||||||
|
@ -696,10 +699,20 @@ pub fn constrain_expr(
|
||||||
//
|
//
|
||||||
// The return type of each branch must equal the return type of
|
// The return type of each branch must equal the return type of
|
||||||
// the entire when-expression.
|
// the entire when-expression.
|
||||||
// branch_cons.extend(pattern_cons);
|
|
||||||
// branch_constraints.push(constraints.and_constraint(pattern_cons));
|
// After solving the condition variable with what's expected from the branch patterns,
|
||||||
let mut total_cons = Vec::with_capacity(1 + 2 * branches.len() + 1);
|
// check it against the condition expression.
|
||||||
total_cons.push(expr_con);
|
// TODO: when we have exhaustiveness checking during the typechecking phase, perform
|
||||||
|
// exhaustiveness checking when this expectation fails. That will produce better error
|
||||||
|
// messages.
|
||||||
|
let cond_constraint = constrain_expr(
|
||||||
|
constraints,
|
||||||
|
env,
|
||||||
|
loc_cond.region,
|
||||||
|
&loc_cond.value,
|
||||||
|
Expected::ForReason(Reason::WhenBranches, cond_type, branches_region),
|
||||||
|
);
|
||||||
|
pattern_cons.push(cond_constraint);
|
||||||
|
|
||||||
// Solve all the pattern constraints together, introducing variables in the pattern as
|
// Solve all the pattern constraints together, introducing variables in the pattern as
|
||||||
// need be before solving the bodies.
|
// need be before solving the bodies.
|
||||||
|
@ -712,15 +725,11 @@ pub fn constrain_expr(
|
||||||
pattern_constraints,
|
pattern_constraints,
|
||||||
body_constraints,
|
body_constraints,
|
||||||
);
|
);
|
||||||
total_cons.push(when_body_con);
|
|
||||||
|
|
||||||
total_cons.push(constraints.equal_types_var(
|
let result_con =
|
||||||
branch_var,
|
constraints.equal_types_var(branch_var, expected, Category::When, region);
|
||||||
expected,
|
|
||||||
Category::When,
|
|
||||||
region,
|
|
||||||
));
|
|
||||||
|
|
||||||
|
let total_cons = [when_body_con, result_con];
|
||||||
let branch_constraints = constraints.and_constraint(total_cons);
|
let branch_constraints = constraints.and_constraint(total_cons);
|
||||||
|
|
||||||
// exhautiveness checking happens when converting to mono::Expr
|
// exhautiveness checking happens when converting to mono::Expr
|
||||||
|
@ -1122,7 +1131,7 @@ fn constrain_when_branch_help(
|
||||||
env: &Env,
|
env: &Env,
|
||||||
region: Region,
|
region: Region,
|
||||||
when_branch: &WhenBranch,
|
when_branch: &WhenBranch,
|
||||||
pattern_expected: PExpected<Type>,
|
pattern_expected: impl Fn(HumanIndex, Region) -> PExpected<Type>,
|
||||||
expr_expected: Expected<Type>,
|
expr_expected: Expected<Type>,
|
||||||
) -> (
|
) -> (
|
||||||
Vec<Variable>,
|
Vec<Variable>,
|
||||||
|
@ -1142,17 +1151,20 @@ fn constrain_when_branch_help(
|
||||||
headers: SendMap::default(),
|
headers: SendMap::default(),
|
||||||
vars: Vec::with_capacity(1),
|
vars: Vec::with_capacity(1),
|
||||||
constraints: Vec::with_capacity(1),
|
constraints: Vec::with_capacity(1),
|
||||||
|
delayed_is_open_constraints: Vec::new(),
|
||||||
};
|
};
|
||||||
|
|
||||||
// TODO investigate for error messages, is it better to unify all branches with a variable,
|
// TODO investigate for error messages, is it better to unify all branches with a variable,
|
||||||
// then unify that variable with the expectation?
|
// then unify that variable with the expectation?
|
||||||
for loc_pattern in &when_branch.patterns {
|
for (i, loc_pattern) in when_branch.patterns.iter().enumerate() {
|
||||||
|
let pattern_expected = pattern_expected(HumanIndex::zero_based(i), loc_pattern.region);
|
||||||
|
|
||||||
constrain_pattern(
|
constrain_pattern(
|
||||||
constraints,
|
constraints,
|
||||||
env,
|
env,
|
||||||
&loc_pattern.value,
|
&loc_pattern.value,
|
||||||
loc_pattern.region,
|
loc_pattern.region,
|
||||||
pattern_expected.clone(),
|
pattern_expected,
|
||||||
&mut state,
|
&mut state,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -1171,11 +1183,17 @@ fn constrain_when_branch_help(
|
||||||
);
|
);
|
||||||
|
|
||||||
// must introduce the headers from the pattern before constraining the guard
|
// must introduce the headers from the pattern before constraining the guard
|
||||||
|
state
|
||||||
|
.constraints
|
||||||
|
.append(&mut state.delayed_is_open_constraints);
|
||||||
let state_constraints = constraints.and_constraint(state.constraints);
|
let state_constraints = constraints.and_constraint(state.constraints);
|
||||||
let inner = constraints.let_constraint([], [], [], guard_constraint, ret_constraint);
|
let inner = constraints.let_constraint([], [], [], guard_constraint, ret_constraint);
|
||||||
|
|
||||||
(state_constraints, inner)
|
(state_constraints, inner)
|
||||||
} else {
|
} else {
|
||||||
|
state
|
||||||
|
.constraints
|
||||||
|
.append(&mut state.delayed_is_open_constraints);
|
||||||
let state_constraints = constraints.and_constraint(state.constraints);
|
let state_constraints = constraints.and_constraint(state.constraints);
|
||||||
(state_constraints, ret_constraint)
|
(state_constraints, ret_constraint)
|
||||||
};
|
};
|
||||||
|
@ -1267,6 +1285,7 @@ fn constrain_def_pattern(
|
||||||
headers: SendMap::default(),
|
headers: SendMap::default(),
|
||||||
vars: Vec::with_capacity(1),
|
vars: Vec::with_capacity(1),
|
||||||
constraints: Vec::with_capacity(1),
|
constraints: Vec::with_capacity(1),
|
||||||
|
delayed_is_open_constraints: vec![],
|
||||||
};
|
};
|
||||||
|
|
||||||
constrain_pattern(
|
constrain_pattern(
|
||||||
|
@ -1364,6 +1383,7 @@ fn constrain_typed_def(
|
||||||
headers: SendMap::default(),
|
headers: SendMap::default(),
|
||||||
vars: Vec::with_capacity(arguments.len()),
|
vars: Vec::with_capacity(arguments.len()),
|
||||||
constraints: Vec::with_capacity(1),
|
constraints: Vec::with_capacity(1),
|
||||||
|
delayed_is_open_constraints: vec![],
|
||||||
};
|
};
|
||||||
let mut vars = Vec::with_capacity(argument_pattern_state.vars.capacity() + 1);
|
let mut vars = Vec::with_capacity(argument_pattern_state.vars.capacity() + 1);
|
||||||
let ret_var = *ret_var;
|
let ret_var = *ret_var;
|
||||||
|
@ -1843,6 +1863,7 @@ pub fn rec_defs_help(
|
||||||
headers: SendMap::default(),
|
headers: SendMap::default(),
|
||||||
vars: Vec::with_capacity(arguments.len()),
|
vars: Vec::with_capacity(arguments.len()),
|
||||||
constraints: Vec::with_capacity(1),
|
constraints: Vec::with_capacity(1),
|
||||||
|
delayed_is_open_constraints: vec![],
|
||||||
};
|
};
|
||||||
let mut vars = Vec::with_capacity(state.vars.capacity() + 1);
|
let mut vars = Vec::with_capacity(state.vars.capacity() + 1);
|
||||||
let mut pattern_types = Vec::with_capacity(state.vars.capacity());
|
let mut pattern_types = Vec::with_capacity(state.vars.capacity());
|
||||||
|
|
|
@ -18,6 +18,7 @@ pub struct PatternState {
|
||||||
pub headers: SendMap<Symbol, Loc<Type>>,
|
pub headers: SendMap<Symbol, Loc<Type>>,
|
||||||
pub vars: Vec<Variable>,
|
pub vars: Vec<Variable>,
|
||||||
pub constraints: Vec<Constraint>,
|
pub constraints: Vec<Constraint>,
|
||||||
|
pub delayed_is_open_constraints: Vec<Constraint>,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// If there is a type annotation, the pattern state headers can be optimized by putting the
|
/// If there is a type annotation, the pattern state headers can be optimized by putting the
|
||||||
|
@ -180,7 +181,7 @@ pub fn constrain_pattern(
|
||||||
// so, we know that "x" (in this case, a tag union) must be open.
|
// so, we know that "x" (in this case, a tag union) must be open.
|
||||||
if could_be_a_tag_union(expected.get_type_ref()) {
|
if could_be_a_tag_union(expected.get_type_ref()) {
|
||||||
state
|
state
|
||||||
.constraints
|
.delayed_is_open_constraints
|
||||||
.push(constraints.is_open_type(expected.get_type()));
|
.push(constraints.is_open_type(expected.get_type()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -191,7 +192,7 @@ pub fn constrain_pattern(
|
||||||
Identifier(symbol) | Shadowed(_, _, symbol) => {
|
Identifier(symbol) | Shadowed(_, _, symbol) => {
|
||||||
if could_be_a_tag_union(expected.get_type_ref()) {
|
if could_be_a_tag_union(expected.get_type_ref()) {
|
||||||
state
|
state
|
||||||
.constraints
|
.delayed_is_open_constraints
|
||||||
.push(constraints.is_open_type(expected.get_type_ref().clone()));
|
.push(constraints.is_open_type(expected.get_type_ref().clone()));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -494,6 +495,9 @@ pub fn constrain_pattern(
|
||||||
state.vars.push(*ext_var);
|
state.vars.push(*ext_var);
|
||||||
state.constraints.push(whole_con);
|
state.constraints.push(whole_con);
|
||||||
state.constraints.push(tag_con);
|
state.constraints.push(tag_con);
|
||||||
|
state
|
||||||
|
.constraints
|
||||||
|
.append(&mut state.delayed_is_open_constraints);
|
||||||
}
|
}
|
||||||
|
|
||||||
UnwrappedOpaque {
|
UnwrappedOpaque {
|
||||||
|
|
|
@ -2,7 +2,10 @@
|
||||||
//! http://moscova.inria.fr/~maranget/papers/warn/warn.pdf
|
//! http://moscova.inria.fr/~maranget/papers/warn/warn.pdf
|
||||||
|
|
||||||
use roc_collections::all::{HumanIndex, MutMap};
|
use roc_collections::all::{HumanIndex, MutMap};
|
||||||
use roc_module::ident::{Lowercase, TagIdIntType, TagName};
|
use roc_module::{
|
||||||
|
ident::{Lowercase, TagIdIntType, TagName},
|
||||||
|
symbol::Symbol,
|
||||||
|
};
|
||||||
use roc_region::all::Region;
|
use roc_region::all::Region;
|
||||||
use roc_std::RocDec;
|
use roc_std::RocDec;
|
||||||
|
|
||||||
|
@ -15,9 +18,9 @@ pub struct Union {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Union {
|
impl Union {
|
||||||
pub fn newtype_wrapper(tag_name: TagName, arity: usize) -> Self {
|
pub fn newtype_wrapper(name: CtorName, arity: usize) -> Self {
|
||||||
let alternatives = vec![Ctor {
|
let alternatives = vec![Ctor {
|
||||||
name: tag_name,
|
name,
|
||||||
tag_id: TagId(0),
|
tag_id: TagId(0),
|
||||||
arity,
|
arity,
|
||||||
}];
|
}];
|
||||||
|
@ -40,9 +43,24 @@ pub enum RenderAs {
|
||||||
#[derive(Clone, Debug, PartialEq, Eq, Hash, Copy)]
|
#[derive(Clone, Debug, PartialEq, Eq, Hash, Copy)]
|
||||||
pub struct TagId(pub TagIdIntType);
|
pub struct TagId(pub TagIdIntType);
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
|
||||||
|
pub enum CtorName {
|
||||||
|
Tag(TagName),
|
||||||
|
Opaque(Symbol),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl CtorName {
|
||||||
|
pub fn is_tag(&self, tag_name: &TagName) -> bool {
|
||||||
|
match self {
|
||||||
|
Self::Tag(test) => test == tag_name,
|
||||||
|
_ => false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
|
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
|
||||||
pub struct Ctor {
|
pub struct Ctor {
|
||||||
pub name: TagName,
|
pub name: CtorName,
|
||||||
pub tag_id: TagId,
|
pub tag_id: TagId,
|
||||||
pub arity: usize,
|
pub arity: usize,
|
||||||
}
|
}
|
||||||
|
|
|
@ -468,9 +468,7 @@ impl<'a> Formattable for Tag<'a> {
|
||||||
use self::Tag::*;
|
use self::Tag::*;
|
||||||
|
|
||||||
match self {
|
match self {
|
||||||
Global { args, .. } | Private { args, .. } => {
|
Global { args, .. } => args.iter().any(|arg| (&arg.value).is_multiline()),
|
||||||
args.iter().any(|arg| (&arg.value).is_multiline())
|
|
||||||
}
|
|
||||||
Tag::SpaceBefore(_, _) | Tag::SpaceAfter(_, _) => true,
|
Tag::SpaceBefore(_, _) | Tag::SpaceAfter(_, _) => true,
|
||||||
Malformed(text) => text.chars().any(|c| c == '\n'),
|
Malformed(text) => text.chars().any(|c| c == '\n'),
|
||||||
}
|
}
|
||||||
|
@ -503,24 +501,6 @@ impl<'a> Formattable for Tag<'a> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Tag::Private { name, args } => {
|
|
||||||
debug_assert!(name.value.starts_with('@'));
|
|
||||||
buf.indent(indent);
|
|
||||||
buf.push_str(name.value);
|
|
||||||
if is_multiline {
|
|
||||||
let arg_indent = indent + INDENT;
|
|
||||||
|
|
||||||
for arg in *args {
|
|
||||||
buf.newline();
|
|
||||||
arg.format_with_options(buf, Parens::InApply, Newlines::No, arg_indent);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
for arg in *args {
|
|
||||||
buf.spaces(1);
|
|
||||||
arg.format_with_options(buf, Parens::InApply, Newlines::No, indent);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Tag::SpaceBefore(_, _) | Tag::SpaceAfter(_, _) => unreachable!(),
|
Tag::SpaceBefore(_, _) | Tag::SpaceAfter(_, _) => unreachable!(),
|
||||||
Tag::Malformed(raw) => {
|
Tag::Malformed(raw) => {
|
||||||
buf.indent(indent);
|
buf.indent(indent);
|
||||||
|
|
|
@ -191,12 +191,26 @@ pub fn fmt_body<'a, 'buf>(
|
||||||
buf.push_str(" =");
|
buf.push_str(" =");
|
||||||
if body.is_multiline() {
|
if body.is_multiline() {
|
||||||
match body {
|
match body {
|
||||||
Expr::SpaceBefore(_, _) => {
|
Expr::SpaceBefore(sub_def, spaces) => {
|
||||||
body.format_with_options(buf, Parens::NotNeeded, Newlines::Yes, indent + INDENT);
|
let should_outdent = match sub_def {
|
||||||
}
|
Expr::Record { .. } | Expr::List { .. } => {
|
||||||
Expr::Record { .. } | Expr::List { .. } => {
|
let is_only_newlines = spaces.iter().all(|s| s.is_newline());
|
||||||
buf.newline();
|
is_only_newlines && sub_def.is_multiline()
|
||||||
body.format_with_options(buf, Parens::NotNeeded, Newlines::Yes, indent + INDENT);
|
}
|
||||||
|
_ => false,
|
||||||
|
};
|
||||||
|
|
||||||
|
if should_outdent {
|
||||||
|
buf.spaces(1);
|
||||||
|
sub_def.format_with_options(buf, Parens::NotNeeded, Newlines::Yes, indent);
|
||||||
|
} else {
|
||||||
|
body.format_with_options(
|
||||||
|
buf,
|
||||||
|
Parens::NotNeeded,
|
||||||
|
Newlines::Yes,
|
||||||
|
indent + INDENT,
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
_ => {
|
_ => {
|
||||||
buf.spaces(1);
|
buf.spaces(1);
|
||||||
|
|
|
@ -38,7 +38,6 @@ impl<'a> Formattable for Expr<'a> {
|
||||||
| MalformedIdent(_, _)
|
| MalformedIdent(_, _)
|
||||||
| MalformedClosure
|
| MalformedClosure
|
||||||
| GlobalTag(_)
|
| GlobalTag(_)
|
||||||
| PrivateTag(_)
|
|
||||||
| OpaqueRef(_) => false,
|
| OpaqueRef(_) => false,
|
||||||
|
|
||||||
// These expressions always have newlines
|
// These expressions always have newlines
|
||||||
|
@ -187,13 +186,73 @@ impl<'a> Formattable for Expr<'a> {
|
||||||
|
|
||||||
let multiline_args = loc_args.iter().any(|loc_arg| loc_arg.is_multiline());
|
let multiline_args = loc_args.iter().any(|loc_arg| loc_arg.is_multiline());
|
||||||
|
|
||||||
if multiline_args {
|
let mut found_multiline_expr = false;
|
||||||
|
let mut iter = loc_args.iter().peekable();
|
||||||
|
|
||||||
|
while let Some(loc_arg) = iter.next() {
|
||||||
|
if iter.peek().is_none() {
|
||||||
|
found_multiline_expr = match loc_arg.value {
|
||||||
|
SpaceBefore(sub_expr, spaces) => match sub_expr {
|
||||||
|
Record { .. } | List { .. } => {
|
||||||
|
let is_only_newlines = spaces.iter().all(|s| s.is_newline());
|
||||||
|
is_only_newlines
|
||||||
|
&& !found_multiline_expr
|
||||||
|
&& sub_expr.is_multiline()
|
||||||
|
}
|
||||||
|
_ => false,
|
||||||
|
},
|
||||||
|
Record { .. } | List { .. } | Closure { .. } => {
|
||||||
|
!found_multiline_expr && loc_arg.is_multiline()
|
||||||
|
}
|
||||||
|
_ => false,
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
found_multiline_expr = loc_arg.is_multiline();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let should_outdent_last_arg = found_multiline_expr;
|
||||||
|
|
||||||
|
if multiline_args && !should_outdent_last_arg {
|
||||||
let arg_indent = indent + INDENT;
|
let arg_indent = indent + INDENT;
|
||||||
|
|
||||||
for loc_arg in loc_args.iter() {
|
for loc_arg in loc_args.iter() {
|
||||||
buf.newline();
|
buf.newline();
|
||||||
loc_arg.format_with_options(buf, Parens::InApply, Newlines::No, arg_indent);
|
loc_arg.format_with_options(buf, Parens::InApply, Newlines::No, arg_indent);
|
||||||
}
|
}
|
||||||
|
} else if multiline_args && should_outdent_last_arg {
|
||||||
|
let mut iter = loc_args.iter().peekable();
|
||||||
|
while let Some(loc_arg) = iter.next() {
|
||||||
|
buf.spaces(1);
|
||||||
|
|
||||||
|
if iter.peek().is_none() {
|
||||||
|
match loc_arg.value {
|
||||||
|
SpaceBefore(sub_expr, _) => {
|
||||||
|
sub_expr.format_with_options(
|
||||||
|
buf,
|
||||||
|
Parens::InApply,
|
||||||
|
Newlines::Yes,
|
||||||
|
indent,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
loc_arg.format_with_options(
|
||||||
|
buf,
|
||||||
|
Parens::InApply,
|
||||||
|
Newlines::Yes,
|
||||||
|
indent,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
loc_arg.format_with_options(
|
||||||
|
buf,
|
||||||
|
Parens::InApply,
|
||||||
|
Newlines::Yes,
|
||||||
|
indent,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
for loc_arg in loc_args.iter() {
|
for loc_arg in loc_args.iter() {
|
||||||
buf.spaces(1);
|
buf.spaces(1);
|
||||||
|
@ -213,7 +272,7 @@ impl<'a> Formattable for Expr<'a> {
|
||||||
buf.indent(indent);
|
buf.indent(indent);
|
||||||
buf.push_str(string);
|
buf.push_str(string);
|
||||||
}
|
}
|
||||||
GlobalTag(string) | PrivateTag(string) | OpaqueRef(string) => {
|
GlobalTag(string) | OpaqueRef(string) => {
|
||||||
buf.indent(indent);
|
buf.indent(indent);
|
||||||
buf.push_str(string)
|
buf.push_str(string)
|
||||||
}
|
}
|
||||||
|
@ -372,7 +431,6 @@ fn push_op(buf: &mut Buf, op: BinOp) {
|
||||||
called_via::BinOp::Slash => buf.push('/'),
|
called_via::BinOp::Slash => buf.push('/'),
|
||||||
called_via::BinOp::DoubleSlash => buf.push_str("//"),
|
called_via::BinOp::DoubleSlash => buf.push_str("//"),
|
||||||
called_via::BinOp::Percent => buf.push('%'),
|
called_via::BinOp::Percent => buf.push('%'),
|
||||||
called_via::BinOp::DoublePercent => buf.push_str("%%"),
|
|
||||||
called_via::BinOp::Plus => buf.push('+'),
|
called_via::BinOp::Plus => buf.push('+'),
|
||||||
called_via::BinOp::Minus => buf.push('-'),
|
called_via::BinOp::Minus => buf.push('-'),
|
||||||
called_via::BinOp::Equals => buf.push_str("=="),
|
called_via::BinOp::Equals => buf.push_str("=="),
|
||||||
|
@ -847,7 +905,34 @@ fn fmt_closure<'a, 'buf>(
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
loc_ret.format_with_options(buf, Parens::NotNeeded, Newlines::Yes, body_indent);
|
if is_multiline {
|
||||||
|
match &loc_ret.value {
|
||||||
|
SpaceBefore(sub_expr, spaces) => {
|
||||||
|
let should_outdent = match sub_expr {
|
||||||
|
Record { .. } | List { .. } => {
|
||||||
|
let is_only_newlines = spaces.iter().all(|s| s.is_newline());
|
||||||
|
is_only_newlines && sub_expr.is_multiline()
|
||||||
|
}
|
||||||
|
_ => false,
|
||||||
|
};
|
||||||
|
|
||||||
|
if should_outdent {
|
||||||
|
buf.spaces(1);
|
||||||
|
sub_expr.format_with_options(buf, Parens::NotNeeded, Newlines::Yes, indent);
|
||||||
|
} else {
|
||||||
|
loc_ret.format_with_options(buf, Parens::NotNeeded, Newlines::Yes, body_indent);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Record { .. } | List { .. } => {
|
||||||
|
loc_ret.format_with_options(buf, Parens::NotNeeded, Newlines::Yes, indent);
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
loc_ret.format_with_options(buf, Parens::NotNeeded, Newlines::Yes, body_indent);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
loc_ret.format_with_options(buf, Parens::NotNeeded, Newlines::Yes, body_indent);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn fmt_backpassing<'a, 'buf>(
|
fn fmt_backpassing<'a, 'buf>(
|
||||||
|
@ -1104,7 +1189,6 @@ fn sub_expr_requests_parens(expr: &Expr<'_>) -> bool {
|
||||||
| BinOp::Slash
|
| BinOp::Slash
|
||||||
| BinOp::DoubleSlash
|
| BinOp::DoubleSlash
|
||||||
| BinOp::Percent
|
| BinOp::Percent
|
||||||
| BinOp::DoublePercent
|
|
||||||
| BinOp::Plus
|
| BinOp::Plus
|
||||||
| BinOp::Minus
|
| BinOp::Minus
|
||||||
| BinOp::Equals
|
| BinOp::Equals
|
||||||
|
|
|
@ -29,7 +29,6 @@ impl<'a> Formattable for Pattern<'a> {
|
||||||
|
|
||||||
Pattern::Identifier(_)
|
Pattern::Identifier(_)
|
||||||
| Pattern::GlobalTag(_)
|
| Pattern::GlobalTag(_)
|
||||||
| Pattern::PrivateTag(_)
|
|
||||||
| Pattern::OpaqueRef(_)
|
| Pattern::OpaqueRef(_)
|
||||||
| Pattern::Apply(_, _)
|
| Pattern::Apply(_, _)
|
||||||
| Pattern::NumLiteral(..)
|
| Pattern::NumLiteral(..)
|
||||||
|
@ -58,7 +57,7 @@ impl<'a> Formattable for Pattern<'a> {
|
||||||
buf.indent(indent);
|
buf.indent(indent);
|
||||||
buf.push_str(string)
|
buf.push_str(string)
|
||||||
}
|
}
|
||||||
GlobalTag(name) | PrivateTag(name) | OpaqueRef(name) => {
|
GlobalTag(name) | OpaqueRef(name) => {
|
||||||
buf.indent(indent);
|
buf.indent(indent);
|
||||||
buf.push_str(name);
|
buf.push_str(name);
|
||||||
}
|
}
|
||||||
|
|
|
@ -626,6 +626,392 @@ mod test_fmt {
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn lambda_returns_record() {
|
||||||
|
expr_formats_same(indoc!(
|
||||||
|
r#"
|
||||||
|
toRecord = \_ -> {
|
||||||
|
x: 1,
|
||||||
|
y: 2,
|
||||||
|
z: 3,
|
||||||
|
}
|
||||||
|
|
||||||
|
toRecord
|
||||||
|
"#
|
||||||
|
));
|
||||||
|
|
||||||
|
expr_formats_same(indoc!(
|
||||||
|
r#"
|
||||||
|
func = \_ ->
|
||||||
|
{ x: 1, y: 2, z: 3 }
|
||||||
|
|
||||||
|
func
|
||||||
|
"#
|
||||||
|
));
|
||||||
|
|
||||||
|
expr_formats_same(indoc!(
|
||||||
|
r#"
|
||||||
|
toRecord = \_ ->
|
||||||
|
val = 0
|
||||||
|
|
||||||
|
{
|
||||||
|
x: 1,
|
||||||
|
y: 2,
|
||||||
|
z: 3,
|
||||||
|
}
|
||||||
|
|
||||||
|
toRecord
|
||||||
|
"#
|
||||||
|
));
|
||||||
|
|
||||||
|
expr_formats_to(
|
||||||
|
indoc!(
|
||||||
|
r#"
|
||||||
|
toRecord = \_ ->
|
||||||
|
{
|
||||||
|
x: 1,
|
||||||
|
y: 2,
|
||||||
|
z: 3,
|
||||||
|
}
|
||||||
|
|
||||||
|
toRecord
|
||||||
|
"#
|
||||||
|
),
|
||||||
|
indoc!(
|
||||||
|
r#"
|
||||||
|
toRecord = \_ -> {
|
||||||
|
x: 1,
|
||||||
|
y: 2,
|
||||||
|
z: 3,
|
||||||
|
}
|
||||||
|
|
||||||
|
toRecord
|
||||||
|
"#
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn lambda_returns_list() {
|
||||||
|
expr_formats_same(indoc!(
|
||||||
|
r#"
|
||||||
|
toList = \_ -> [
|
||||||
|
1,
|
||||||
|
2,
|
||||||
|
3,
|
||||||
|
]
|
||||||
|
|
||||||
|
toList
|
||||||
|
"#
|
||||||
|
));
|
||||||
|
|
||||||
|
expr_formats_same(indoc!(
|
||||||
|
r#"
|
||||||
|
func = \_ ->
|
||||||
|
[ 1, 2, 3 ]
|
||||||
|
|
||||||
|
func
|
||||||
|
"#
|
||||||
|
));
|
||||||
|
|
||||||
|
expr_formats_same(indoc!(
|
||||||
|
r#"
|
||||||
|
toList = \_ ->
|
||||||
|
val = 0
|
||||||
|
|
||||||
|
[
|
||||||
|
1,
|
||||||
|
2,
|
||||||
|
3,
|
||||||
|
]
|
||||||
|
|
||||||
|
toList
|
||||||
|
"#
|
||||||
|
));
|
||||||
|
|
||||||
|
expr_formats_to(
|
||||||
|
indoc!(
|
||||||
|
r#"
|
||||||
|
toList = \_ ->
|
||||||
|
[
|
||||||
|
1,
|
||||||
|
2,
|
||||||
|
3,
|
||||||
|
]
|
||||||
|
|
||||||
|
toList
|
||||||
|
"#
|
||||||
|
),
|
||||||
|
indoc!(
|
||||||
|
r#"
|
||||||
|
toList = \_ -> [
|
||||||
|
1,
|
||||||
|
2,
|
||||||
|
3,
|
||||||
|
]
|
||||||
|
|
||||||
|
toList
|
||||||
|
"#
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn multiline_list_func_arg() {
|
||||||
|
expr_formats_same(indoc!(
|
||||||
|
r#"
|
||||||
|
result = func arg [
|
||||||
|
1,
|
||||||
|
2,
|
||||||
|
3,
|
||||||
|
]
|
||||||
|
|
||||||
|
result
|
||||||
|
"#
|
||||||
|
));
|
||||||
|
|
||||||
|
expr_formats_to(
|
||||||
|
indoc!(
|
||||||
|
r#"
|
||||||
|
result = func arg
|
||||||
|
[ 1, 2, 3 ]
|
||||||
|
|
||||||
|
result
|
||||||
|
"#
|
||||||
|
),
|
||||||
|
indoc!(
|
||||||
|
r#"
|
||||||
|
result = func
|
||||||
|
arg
|
||||||
|
[ 1, 2, 3 ]
|
||||||
|
|
||||||
|
result
|
||||||
|
"#
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
expr_formats_to(
|
||||||
|
indoc!(
|
||||||
|
r#"
|
||||||
|
result = func arg [
|
||||||
|
1,
|
||||||
|
2,
|
||||||
|
3,
|
||||||
|
]
|
||||||
|
|
||||||
|
result
|
||||||
|
"#
|
||||||
|
),
|
||||||
|
indoc!(
|
||||||
|
r#"
|
||||||
|
result = func arg [
|
||||||
|
1,
|
||||||
|
2,
|
||||||
|
3,
|
||||||
|
]
|
||||||
|
|
||||||
|
result
|
||||||
|
"#
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
expr_formats_to(
|
||||||
|
indoc!(
|
||||||
|
r#"
|
||||||
|
result = func [
|
||||||
|
1,
|
||||||
|
2,
|
||||||
|
3,
|
||||||
|
]
|
||||||
|
arg
|
||||||
|
|
||||||
|
result
|
||||||
|
"#
|
||||||
|
),
|
||||||
|
indoc!(
|
||||||
|
r#"
|
||||||
|
result = func
|
||||||
|
[
|
||||||
|
1,
|
||||||
|
2,
|
||||||
|
3,
|
||||||
|
]
|
||||||
|
arg
|
||||||
|
|
||||||
|
result
|
||||||
|
"#
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
expr_formats_to(
|
||||||
|
indoc!(
|
||||||
|
r#"
|
||||||
|
result = func arg
|
||||||
|
[
|
||||||
|
1,
|
||||||
|
2,
|
||||||
|
3,
|
||||||
|
]
|
||||||
|
|
||||||
|
result
|
||||||
|
"#
|
||||||
|
),
|
||||||
|
indoc!(
|
||||||
|
r#"
|
||||||
|
result = func arg [
|
||||||
|
1,
|
||||||
|
2,
|
||||||
|
3,
|
||||||
|
]
|
||||||
|
|
||||||
|
result
|
||||||
|
"#
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
expr_formats_same(indoc!(
|
||||||
|
r#"
|
||||||
|
result = func
|
||||||
|
arg
|
||||||
|
[
|
||||||
|
1,
|
||||||
|
2,
|
||||||
|
3,
|
||||||
|
]
|
||||||
|
|
||||||
|
result
|
||||||
|
"#
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn multiline_record_func_arg() {
|
||||||
|
expr_formats_same(indoc!(
|
||||||
|
r#"
|
||||||
|
result = func arg {
|
||||||
|
x: 1,
|
||||||
|
y: 2,
|
||||||
|
z: 3,
|
||||||
|
}
|
||||||
|
|
||||||
|
result
|
||||||
|
"#
|
||||||
|
));
|
||||||
|
|
||||||
|
expr_formats_to(
|
||||||
|
indoc!(
|
||||||
|
r#"
|
||||||
|
result = func arg
|
||||||
|
{ x: 1, y: 2, z: 3 }
|
||||||
|
|
||||||
|
result
|
||||||
|
"#
|
||||||
|
),
|
||||||
|
indoc!(
|
||||||
|
r#"
|
||||||
|
result = func
|
||||||
|
arg
|
||||||
|
{ x: 1, y: 2, z: 3 }
|
||||||
|
|
||||||
|
result
|
||||||
|
"#
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
expr_formats_to(
|
||||||
|
indoc!(
|
||||||
|
r#"
|
||||||
|
result = func arg {
|
||||||
|
x: 1,
|
||||||
|
y: 2,
|
||||||
|
z: 3,
|
||||||
|
}
|
||||||
|
|
||||||
|
result
|
||||||
|
"#
|
||||||
|
),
|
||||||
|
indoc!(
|
||||||
|
r#"
|
||||||
|
result = func arg {
|
||||||
|
x: 1,
|
||||||
|
y: 2,
|
||||||
|
z: 3,
|
||||||
|
}
|
||||||
|
|
||||||
|
result
|
||||||
|
"#
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
expr_formats_to(
|
||||||
|
indoc!(
|
||||||
|
r#"
|
||||||
|
result = func {
|
||||||
|
x: 1,
|
||||||
|
y: 2,
|
||||||
|
z: 3,
|
||||||
|
}
|
||||||
|
arg
|
||||||
|
|
||||||
|
result
|
||||||
|
"#
|
||||||
|
),
|
||||||
|
indoc!(
|
||||||
|
r#"
|
||||||
|
result = func
|
||||||
|
{
|
||||||
|
x: 1,
|
||||||
|
y: 2,
|
||||||
|
z: 3,
|
||||||
|
}
|
||||||
|
arg
|
||||||
|
|
||||||
|
result
|
||||||
|
"#
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
expr_formats_to(
|
||||||
|
indoc!(
|
||||||
|
r#"
|
||||||
|
result = func arg
|
||||||
|
{
|
||||||
|
x: 1,
|
||||||
|
y: 2,
|
||||||
|
z: 3,
|
||||||
|
}
|
||||||
|
|
||||||
|
result
|
||||||
|
"#
|
||||||
|
),
|
||||||
|
indoc!(
|
||||||
|
r#"
|
||||||
|
result = func arg {
|
||||||
|
x: 1,
|
||||||
|
y: 2,
|
||||||
|
z: 3,
|
||||||
|
}
|
||||||
|
|
||||||
|
result
|
||||||
|
"#
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
expr_formats_same(indoc!(
|
||||||
|
r#"
|
||||||
|
result = func
|
||||||
|
arg
|
||||||
|
{
|
||||||
|
x: 1,
|
||||||
|
y: 2,
|
||||||
|
z: 3,
|
||||||
|
}
|
||||||
|
|
||||||
|
result
|
||||||
|
"#
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn record_updating() {
|
fn record_updating() {
|
||||||
expr_formats_same(indoc!(
|
expr_formats_same(indoc!(
|
||||||
|
@ -1301,6 +1687,27 @@ mod test_fmt {
|
||||||
fn multi_line_list_def() {
|
fn multi_line_list_def() {
|
||||||
expr_formats_same(indoc!(
|
expr_formats_same(indoc!(
|
||||||
r#"
|
r#"
|
||||||
|
l = [
|
||||||
|
1,
|
||||||
|
2,
|
||||||
|
]
|
||||||
|
|
||||||
|
l
|
||||||
|
"#
|
||||||
|
));
|
||||||
|
|
||||||
|
expr_formats_same(indoc!(
|
||||||
|
r#"
|
||||||
|
l =
|
||||||
|
[ 1, 2 ]
|
||||||
|
|
||||||
|
l
|
||||||
|
"#
|
||||||
|
));
|
||||||
|
|
||||||
|
expr_formats_to(
|
||||||
|
indoc!(
|
||||||
|
r#"
|
||||||
l =
|
l =
|
||||||
[
|
[
|
||||||
1,
|
1,
|
||||||
|
@ -1308,8 +1715,19 @@ mod test_fmt {
|
||||||
]
|
]
|
||||||
|
|
||||||
l
|
l
|
||||||
"#
|
"#
|
||||||
));
|
),
|
||||||
|
indoc!(
|
||||||
|
r#"
|
||||||
|
l = [
|
||||||
|
1,
|
||||||
|
2,
|
||||||
|
]
|
||||||
|
|
||||||
|
l
|
||||||
|
"#
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
expr_formats_to(
|
expr_formats_to(
|
||||||
indoc!(
|
indoc!(
|
||||||
|
@ -1324,11 +1742,10 @@ mod test_fmt {
|
||||||
),
|
),
|
||||||
indoc!(
|
indoc!(
|
||||||
r#"
|
r#"
|
||||||
results =
|
results = [
|
||||||
[
|
Ok 4,
|
||||||
Ok 4,
|
Ok 5,
|
||||||
Ok 5,
|
]
|
||||||
]
|
|
||||||
|
|
||||||
allOks results
|
allOks results
|
||||||
"#
|
"#
|
||||||
|
@ -1417,18 +1834,69 @@ mod test_fmt {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn multi_line_record_def() {
|
fn multi_line_record_def() {
|
||||||
|
expr_formats_same(indoc!(
|
||||||
|
r#"
|
||||||
|
pos = {
|
||||||
|
x: 4,
|
||||||
|
y: 11,
|
||||||
|
z: 16,
|
||||||
|
}
|
||||||
|
|
||||||
|
pos
|
||||||
|
"#
|
||||||
|
));
|
||||||
|
|
||||||
expr_formats_same(indoc!(
|
expr_formats_same(indoc!(
|
||||||
r#"
|
r#"
|
||||||
pos =
|
pos =
|
||||||
|
{ x: 4, y: 11, z: 16 }
|
||||||
|
|
||||||
|
pos
|
||||||
|
"#
|
||||||
|
));
|
||||||
|
|
||||||
|
expr_formats_same(indoc!(
|
||||||
|
r#"
|
||||||
|
myDef =
|
||||||
|
list = [
|
||||||
|
a,
|
||||||
|
b,
|
||||||
|
]
|
||||||
|
|
||||||
{
|
{
|
||||||
|
c,
|
||||||
|
d,
|
||||||
|
}
|
||||||
|
|
||||||
|
myDef
|
||||||
|
"#
|
||||||
|
));
|
||||||
|
|
||||||
|
expr_formats_to(
|
||||||
|
indoc!(
|
||||||
|
r#"
|
||||||
|
pos =
|
||||||
|
{
|
||||||
|
x: 4,
|
||||||
|
y: 11,
|
||||||
|
z: 16,
|
||||||
|
}
|
||||||
|
|
||||||
|
pos
|
||||||
|
"#
|
||||||
|
),
|
||||||
|
indoc!(
|
||||||
|
r#"
|
||||||
|
pos = {
|
||||||
x: 4,
|
x: 4,
|
||||||
y: 11,
|
y: 11,
|
||||||
z: 16,
|
z: 16,
|
||||||
}
|
}
|
||||||
|
|
||||||
pos
|
pos
|
||||||
"#
|
"#
|
||||||
));
|
),
|
||||||
|
);
|
||||||
|
|
||||||
expr_formats_to(
|
expr_formats_to(
|
||||||
indoc!(
|
indoc!(
|
||||||
|
@ -1443,11 +1911,10 @@ mod test_fmt {
|
||||||
),
|
),
|
||||||
indoc!(
|
indoc!(
|
||||||
r#"
|
r#"
|
||||||
pos =
|
pos = {
|
||||||
{
|
x: 5,
|
||||||
x: 5,
|
y: 10,
|
||||||
y: 10,
|
}
|
||||||
}
|
|
||||||
|
|
||||||
pos
|
pos
|
||||||
"#
|
"#
|
||||||
|
@ -2537,7 +3004,7 @@ mod test_fmt {
|
||||||
indoc!(
|
indoc!(
|
||||||
r#"
|
r#"
|
||||||
2 % 3
|
2 % 3
|
||||||
%% 5
|
// 5
|
||||||
+ 7
|
+ 7
|
||||||
"#
|
"#
|
||||||
),
|
),
|
||||||
|
@ -2545,7 +3012,7 @@ mod test_fmt {
|
||||||
r#"
|
r#"
|
||||||
2
|
2
|
||||||
% 3
|
% 3
|
||||||
%% 5
|
// 5
|
||||||
+ 7
|
+ 7
|
||||||
"#
|
"#
|
||||||
),
|
),
|
||||||
|
@ -2619,6 +3086,18 @@ mod test_fmt {
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn func_call_trailing_multiline_lambda() {
|
||||||
|
expr_formats_same(indoc!(
|
||||||
|
r#"
|
||||||
|
list = List.map [ 1, 2, 3 ] \x ->
|
||||||
|
x + 1
|
||||||
|
|
||||||
|
list
|
||||||
|
"#
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
// MODULES
|
// MODULES
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
|
|
@ -19,7 +19,7 @@ roc_mono = { path = "../mono" }
|
||||||
roc_target = { path = "../roc_target" }
|
roc_target = { path = "../roc_target" }
|
||||||
roc_error_macros = { path = "../../error_macros" }
|
roc_error_macros = { path = "../../error_macros" }
|
||||||
bumpalo = { version = "3.8.0", features = ["collections"] }
|
bumpalo = { version = "3.8.0", features = ["collections"] }
|
||||||
target-lexicon = "0.12.2"
|
target-lexicon = "0.12.3"
|
||||||
# TODO: Deal with the update of object to 0.27.
|
# TODO: Deal with the update of object to 0.27.
|
||||||
# It looks like it breaks linking the generated objects.
|
# It looks like it breaks linking the generated objects.
|
||||||
# Probably just need to specify an extra field that used to be implicit or something.
|
# Probably just need to specify an extra field that used to be implicit or something.
|
||||||
|
|
|
@ -913,9 +913,6 @@ trait Backend<'a> {
|
||||||
TagName::Closure(sym) => {
|
TagName::Closure(sym) => {
|
||||||
self.set_last_seen(*sym, stmt);
|
self.set_last_seen(*sym, stmt);
|
||||||
}
|
}
|
||||||
TagName::Private(sym) => {
|
|
||||||
self.set_last_seen(*sym, stmt);
|
|
||||||
}
|
|
||||||
TagName::Global(_) => {}
|
TagName::Global(_) => {}
|
||||||
}
|
}
|
||||||
for sym in *arguments {
|
for sym in *arguments {
|
||||||
|
|
|
@ -18,4 +18,4 @@ roc_std = { path = "../../roc_std", default-features = false }
|
||||||
morphic_lib = { path = "../../vendor/morphic_lib" }
|
morphic_lib = { path = "../../vendor/morphic_lib" }
|
||||||
bumpalo = { version = "3.8.0", features = ["collections"] }
|
bumpalo = { version = "3.8.0", features = ["collections"] }
|
||||||
inkwell = { path = "../../vendor/inkwell" }
|
inkwell = { path = "../../vendor/inkwell" }
|
||||||
target-lexicon = "0.12.2"
|
target-lexicon = "0.12.3"
|
||||||
|
|
|
@ -6915,7 +6915,6 @@ fn build_float_binop<'a, 'ctx, 'env>(
|
||||||
NumGte => bd.build_float_compare(OGE, lhs, rhs, "float_gte").into(),
|
NumGte => bd.build_float_compare(OGE, lhs, rhs, "float_gte").into(),
|
||||||
NumLt => bd.build_float_compare(OLT, lhs, rhs, "float_lt").into(),
|
NumLt => bd.build_float_compare(OLT, lhs, rhs, "float_lt").into(),
|
||||||
NumLte => bd.build_float_compare(OLE, lhs, rhs, "float_lte").into(),
|
NumLte => bd.build_float_compare(OLE, lhs, rhs, "float_lte").into(),
|
||||||
NumRemUnchecked => bd.build_float_rem(lhs, rhs, "rem_float").into(),
|
|
||||||
NumDivUnchecked => bd.build_float_div(lhs, rhs, "div_float").into(),
|
NumDivUnchecked => bd.build_float_div(lhs, rhs, "div_float").into(),
|
||||||
NumPow => env.call_intrinsic(&LLVM_POW[float_width], &[lhs.into(), rhs.into()]),
|
NumPow => env.call_intrinsic(&LLVM_POW[float_width], &[lhs.into(), rhs.into()]),
|
||||||
_ => {
|
_ => {
|
||||||
|
|
|
@ -1809,7 +1809,39 @@ fn modify_refcount_union_help<'a, 'ctx, 'env>(
|
||||||
|
|
||||||
for (i, field_layout) in field_layouts.iter().enumerate() {
|
for (i, field_layout) in field_layouts.iter().enumerate() {
|
||||||
if let Layout::RecursivePointer = field_layout {
|
if let Layout::RecursivePointer = field_layout {
|
||||||
panic!("non-recursive tag unions cannot contain naked recursion pointers!");
|
let recursive_union_layout = match when_recursive {
|
||||||
|
WhenRecursive::Unreachable => {
|
||||||
|
panic!("non-recursive tag unions cannot contain naked recursion pointers!");
|
||||||
|
}
|
||||||
|
WhenRecursive::Loop(recursive_union_layout) => recursive_union_layout,
|
||||||
|
};
|
||||||
|
|
||||||
|
// This field is a pointer to the recursive pointer.
|
||||||
|
let field_ptr = env
|
||||||
|
.builder
|
||||||
|
.build_struct_gep(cast_tag_data_pointer, i as u32, "modify_tag_field")
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
// This is the actual pointer to the recursive data.
|
||||||
|
let field_value = env.builder.build_load(field_ptr, "load_recursive_pointer");
|
||||||
|
|
||||||
|
debug_assert!(field_value.is_pointer_value());
|
||||||
|
|
||||||
|
// therefore we must cast it to our desired type
|
||||||
|
let union_type =
|
||||||
|
basic_type_from_layout(env, &Layout::Union(*recursive_union_layout));
|
||||||
|
let recursive_ptr_field_value =
|
||||||
|
cast_basic_basic(env.builder, field_value, union_type);
|
||||||
|
|
||||||
|
modify_refcount_layout_help(
|
||||||
|
env,
|
||||||
|
parent,
|
||||||
|
layout_ids,
|
||||||
|
mode.to_call_mode(fn_val),
|
||||||
|
when_recursive,
|
||||||
|
recursive_ptr_field_value,
|
||||||
|
&Layout::RecursivePointer,
|
||||||
|
)
|
||||||
} else if field_layout.contains_refcounted() {
|
} else if field_layout.contains_refcounted() {
|
||||||
let field_ptr = env
|
let field_ptr = env
|
||||||
.builder
|
.builder
|
||||||
|
|
|
@ -1,7 +1,5 @@
|
||||||
use crate::docs::DocEntry::DetachedDoc;
|
use crate::docs::DocEntry::DetachedDoc;
|
||||||
use crate::docs::TypeAnnotation::{
|
use crate::docs::TypeAnnotation::{Apply, BoundVariable, Function, NoTypeAnn, Record, TagUnion};
|
||||||
Apply, BoundVariable, Function, NoTypeAnn, ObscuredRecord, ObscuredTagUnion, Record, TagUnion,
|
|
||||||
};
|
|
||||||
use crate::file::LoadedModule;
|
use crate::file::LoadedModule;
|
||||||
use roc_can::scope::Scope;
|
use roc_can::scope::Scope;
|
||||||
use roc_error_macros::todo_abilities;
|
use roc_error_macros::todo_abilities;
|
||||||
|
@ -274,36 +272,20 @@ fn type_to_docs(in_func_type_ann: bool, type_annotation: ast::TypeAnnotation) ->
|
||||||
ast::TypeAnnotation::TagUnion { tags, ext } => {
|
ast::TypeAnnotation::TagUnion { tags, ext } => {
|
||||||
let mut tags_to_render: Vec<Tag> = Vec::new();
|
let mut tags_to_render: Vec<Tag> = Vec::new();
|
||||||
|
|
||||||
let mut any_tags_are_private = false;
|
|
||||||
|
|
||||||
for tag in tags.iter() {
|
for tag in tags.iter() {
|
||||||
match tag_to_doc(in_func_type_ann, tag.value) {
|
if let Some(tag_ann) = tag_to_doc(in_func_type_ann, tag.value) {
|
||||||
None => {
|
tags_to_render.push(tag_ann);
|
||||||
any_tags_are_private = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
Some(tag_ann) => {
|
|
||||||
tags_to_render.push(tag_ann);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if any_tags_are_private {
|
let extension = match ext {
|
||||||
if in_func_type_ann {
|
None => NoTypeAnn,
|
||||||
ObscuredTagUnion
|
Some(ext_type_ann) => type_to_docs(in_func_type_ann, ext_type_ann.value),
|
||||||
} else {
|
};
|
||||||
NoTypeAnn
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
let extension = match ext {
|
|
||||||
None => NoTypeAnn,
|
|
||||||
Some(ext_type_ann) => type_to_docs(in_func_type_ann, ext_type_ann.value),
|
|
||||||
};
|
|
||||||
|
|
||||||
TagUnion {
|
TagUnion {
|
||||||
tags: tags_to_render,
|
tags: tags_to_render,
|
||||||
extension: Box::new(extension),
|
extension: Box::new(extension),
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ast::TypeAnnotation::BoundVariable(var_name) => BoundVariable(var_name.to_string()),
|
ast::TypeAnnotation::BoundVariable(var_name) => BoundVariable(var_name.to_string()),
|
||||||
|
@ -328,35 +310,19 @@ fn type_to_docs(in_func_type_ann: bool, type_annotation: ast::TypeAnnotation) ->
|
||||||
ast::TypeAnnotation::Record { fields, ext } => {
|
ast::TypeAnnotation::Record { fields, ext } => {
|
||||||
let mut doc_fields = Vec::new();
|
let mut doc_fields = Vec::new();
|
||||||
|
|
||||||
let mut any_fields_include_private_tags = false;
|
|
||||||
|
|
||||||
for field in fields.items {
|
for field in fields.items {
|
||||||
match record_field_to_doc(in_func_type_ann, field.value) {
|
if let Some(doc_field) = record_field_to_doc(in_func_type_ann, field.value) {
|
||||||
None => {
|
doc_fields.push(doc_field);
|
||||||
any_fields_include_private_tags = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
Some(doc_field) => {
|
|
||||||
doc_fields.push(doc_field);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if any_fields_include_private_tags {
|
let extension = match ext {
|
||||||
if in_func_type_ann {
|
None => NoTypeAnn,
|
||||||
ObscuredRecord
|
Some(ext_type_ann) => type_to_docs(in_func_type_ann, ext_type_ann.value),
|
||||||
} else {
|
};
|
||||||
NoTypeAnn
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
let extension = match ext {
|
|
||||||
None => NoTypeAnn,
|
|
||||||
Some(ext_type_ann) => type_to_docs(in_func_type_ann, ext_type_ann.value),
|
|
||||||
};
|
|
||||||
|
|
||||||
Record {
|
Record {
|
||||||
fields: doc_fields,
|
fields: doc_fields,
|
||||||
extension: Box::new(extension),
|
extension: Box::new(extension),
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ast::TypeAnnotation::SpaceBefore(&sub_type_ann, _) => {
|
ast::TypeAnnotation::SpaceBefore(&sub_type_ann, _) => {
|
||||||
|
@ -404,8 +370,7 @@ fn record_field_to_doc(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// The Option here represents if it is private. Private tags
|
// The Option here represents if it is malformed.
|
||||||
// evaluate to `None`.
|
|
||||||
fn tag_to_doc(in_func_ann: bool, tag: ast::Tag) -> Option<Tag> {
|
fn tag_to_doc(in_func_ann: bool, tag: ast::Tag) -> Option<Tag> {
|
||||||
match tag {
|
match tag {
|
||||||
ast::Tag::Global { name, args } => Some(Tag {
|
ast::Tag::Global { name, args } => Some(Tag {
|
||||||
|
@ -420,7 +385,6 @@ fn tag_to_doc(in_func_ann: bool, tag: ast::Tag) -> Option<Tag> {
|
||||||
type_vars
|
type_vars
|
||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
ast::Tag::Private { .. } => None,
|
|
||||||
ast::Tag::SpaceBefore(&sub_tag, _) => tag_to_doc(in_func_ann, sub_tag),
|
ast::Tag::SpaceBefore(&sub_tag, _) => tag_to_doc(in_func_ann, sub_tag),
|
||||||
ast::Tag::SpaceAfter(&sub_tag, _) => tag_to_doc(in_func_ann, sub_tag),
|
ast::Tag::SpaceAfter(&sub_tag, _) => tag_to_doc(in_func_ann, sub_tag),
|
||||||
ast::Tag::Malformed(_) => None,
|
ast::Tag::Malformed(_) => None,
|
||||||
|
|
|
@ -39,7 +39,7 @@ use roc_solve::solve;
|
||||||
use roc_target::TargetInfo;
|
use roc_target::TargetInfo;
|
||||||
use roc_types::solved_types::Solved;
|
use roc_types::solved_types::Solved;
|
||||||
use roc_types::subs::{Subs, VarStore, Variable};
|
use roc_types::subs::{Subs, VarStore, Variable};
|
||||||
use roc_types::types::{Alias, AliasCommon, TypeExtension};
|
use roc_types::types::{Alias, AliasCommon, AliasKind, TypeExtension};
|
||||||
use std::collections::hash_map::Entry::{Occupied, Vacant};
|
use std::collections::hash_map::Entry::{Occupied, Vacant};
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::io;
|
use std::io;
|
||||||
|
@ -4715,50 +4715,35 @@ fn default_aliases() -> roc_solve::solve::Aliases {
|
||||||
|
|
||||||
let mut var_store = VarStore::default();
|
let mut var_store = VarStore::default();
|
||||||
|
|
||||||
|
// Num range := range
|
||||||
{
|
{
|
||||||
let symbol = Symbol::NUM_NUM;
|
let symbol = Symbol::NUM_NUM;
|
||||||
let tvar = var_store.fresh();
|
let tvar = var_store.fresh();
|
||||||
|
|
||||||
let typ = Type::TagUnion(
|
|
||||||
vec![(
|
|
||||||
TagName::Private(Symbol::NUM_AT_NUM),
|
|
||||||
vec![Type::Variable(tvar)],
|
|
||||||
)],
|
|
||||||
TypeExtension::Closed,
|
|
||||||
);
|
|
||||||
|
|
||||||
let alias = Alias {
|
let alias = Alias {
|
||||||
region: Region::zero(),
|
region: Region::zero(),
|
||||||
type_variables: vec![Loc::at_zero(("range".into(), tvar))],
|
type_variables: vec![Loc::at_zero(("range".into(), tvar))],
|
||||||
lambda_set_variables: Default::default(),
|
lambda_set_variables: Default::default(),
|
||||||
recursion_variables: Default::default(),
|
recursion_variables: Default::default(),
|
||||||
typ,
|
typ: Type::Variable(tvar),
|
||||||
kind: roc_types::types::AliasKind::Structural,
|
kind: roc_types::types::AliasKind::Structural,
|
||||||
};
|
};
|
||||||
|
|
||||||
solve_aliases.insert(symbol, alias);
|
solve_aliases.insert(symbol, alias);
|
||||||
}
|
}
|
||||||
|
|
||||||
// FloatingPoint range : [ @FloatingPoint range ]
|
// FloatingPoint range := []
|
||||||
{
|
{
|
||||||
let symbol = Symbol::NUM_FLOATINGPOINT;
|
let symbol = Symbol::NUM_FLOATINGPOINT;
|
||||||
let tvar = var_store.fresh();
|
let tvar = var_store.fresh();
|
||||||
|
|
||||||
let typ = Type::TagUnion(
|
|
||||||
vec![(
|
|
||||||
TagName::Private(Symbol::NUM_AT_FLOATINGPOINT),
|
|
||||||
vec![Type::Variable(tvar)],
|
|
||||||
)],
|
|
||||||
TypeExtension::Closed,
|
|
||||||
);
|
|
||||||
|
|
||||||
let alias = Alias {
|
let alias = Alias {
|
||||||
region: Region::zero(),
|
region: Region::zero(),
|
||||||
type_variables: vec![Loc::at_zero(("range".into(), tvar))],
|
type_variables: vec![Loc::at_zero(("range".into(), tvar))],
|
||||||
lambda_set_variables: Default::default(),
|
lambda_set_variables: Default::default(),
|
||||||
recursion_variables: Default::default(),
|
recursion_variables: Default::default(),
|
||||||
typ,
|
typ: Type::Variable(tvar),
|
||||||
kind: roc_types::types::AliasKind::Structural,
|
kind: roc_types::types::AliasKind::Opaque,
|
||||||
};
|
};
|
||||||
|
|
||||||
solve_aliases.insert(symbol, alias);
|
solve_aliases.insert(symbol, alias);
|
||||||
|
@ -4773,11 +4758,13 @@ fn default_aliases() -> roc_solve::solve::Aliases {
|
||||||
symbol: Symbol::NUM_NUM,
|
symbol: Symbol::NUM_NUM,
|
||||||
type_arguments: vec![(
|
type_arguments: vec![(
|
||||||
"range".into(),
|
"range".into(),
|
||||||
Type::DelayedAlias(AliasCommon {
|
Type::Alias {
|
||||||
symbol: Symbol::NUM_INTEGER,
|
symbol: Symbol::NUM_INTEGER,
|
||||||
type_arguments: vec![("range".into(), Type::Variable(tvar))],
|
type_arguments: vec![("range".into(), Type::Variable(tvar))],
|
||||||
lambda_set_variables: vec![],
|
lambda_set_variables: vec![],
|
||||||
}),
|
actual: Box::new(Type::Variable(tvar)),
|
||||||
|
kind: AliasKind::Opaque,
|
||||||
|
},
|
||||||
)],
|
)],
|
||||||
lambda_set_variables: vec![],
|
lambda_set_variables: vec![],
|
||||||
});
|
});
|
||||||
|
@ -4794,6 +4781,7 @@ fn default_aliases() -> roc_solve::solve::Aliases {
|
||||||
solve_aliases.insert(symbol, alias);
|
solve_aliases.insert(symbol, alias);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Float range : Num (FloatingPoint range)
|
||||||
{
|
{
|
||||||
let symbol = Symbol::NUM_FLOAT;
|
let symbol = Symbol::NUM_FLOAT;
|
||||||
let tvar = var_store.fresh();
|
let tvar = var_store.fresh();
|
||||||
|
@ -4802,11 +4790,13 @@ fn default_aliases() -> roc_solve::solve::Aliases {
|
||||||
symbol: Symbol::NUM_NUM,
|
symbol: Symbol::NUM_NUM,
|
||||||
type_arguments: vec![(
|
type_arguments: vec![(
|
||||||
"range".into(),
|
"range".into(),
|
||||||
Type::DelayedAlias(AliasCommon {
|
Type::Alias {
|
||||||
symbol: Symbol::NUM_FLOATINGPOINT,
|
symbol: Symbol::NUM_FLOATINGPOINT,
|
||||||
type_arguments: vec![("range".into(), Type::Variable(tvar))],
|
type_arguments: vec![("range".into(), Type::Variable(tvar))],
|
||||||
lambda_set_variables: vec![],
|
lambda_set_variables: vec![],
|
||||||
}),
|
actual: Box::new(Type::Variable(tvar)),
|
||||||
|
kind: AliasKind::Opaque,
|
||||||
|
},
|
||||||
)],
|
)],
|
||||||
lambda_set_variables: vec![],
|
lambda_set_variables: vec![],
|
||||||
});
|
});
|
||||||
|
@ -4823,24 +4813,17 @@ fn default_aliases() -> roc_solve::solve::Aliases {
|
||||||
solve_aliases.insert(symbol, alias);
|
solve_aliases.insert(symbol, alias);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Integer range := range
|
||||||
{
|
{
|
||||||
let symbol = Symbol::NUM_INTEGER;
|
let symbol = Symbol::NUM_INTEGER;
|
||||||
let tvar = var_store.fresh();
|
let tvar = var_store.fresh();
|
||||||
|
|
||||||
let typ = Type::TagUnion(
|
|
||||||
vec![(
|
|
||||||
TagName::Private(Symbol::NUM_AT_INTEGER),
|
|
||||||
vec![Type::Variable(tvar)],
|
|
||||||
)],
|
|
||||||
TypeExtension::Closed,
|
|
||||||
);
|
|
||||||
|
|
||||||
let alias = Alias {
|
let alias = Alias {
|
||||||
region: Region::zero(),
|
region: Region::zero(),
|
||||||
type_variables: vec![Loc::at_zero(("range".into(), tvar))],
|
type_variables: vec![Loc::at_zero(("range".into(), tvar))],
|
||||||
lambda_set_variables: Default::default(),
|
lambda_set_variables: Default::default(),
|
||||||
recursion_variables: Default::default(),
|
recursion_variables: Default::default(),
|
||||||
typ,
|
typ: Type::Variable(tvar),
|
||||||
kind: roc_types::types::AliasKind::Structural,
|
kind: roc_types::types::AliasKind::Structural,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -4875,38 +4858,33 @@ fn default_aliases() -> roc_solve::solve::Aliases {
|
||||||
solve_aliases.insert(symbol, alias);
|
solve_aliases.insert(symbol, alias);
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut unit_function = |alias_name: Symbol, at_tag_name: Symbol| {
|
let mut zero_opaque = |alias_name: Symbol| {
|
||||||
let typ = Type::TagUnion(
|
|
||||||
vec![(TagName::Private(at_tag_name), vec![])],
|
|
||||||
TypeExtension::Closed,
|
|
||||||
);
|
|
||||||
|
|
||||||
let alias = Alias {
|
let alias = Alias {
|
||||||
region: Region::zero(),
|
region: Region::zero(),
|
||||||
type_variables: vec![],
|
type_variables: vec![],
|
||||||
lambda_set_variables: Default::default(),
|
lambda_set_variables: Default::default(),
|
||||||
recursion_variables: Default::default(),
|
recursion_variables: Default::default(),
|
||||||
typ,
|
typ: Type::EmptyTagUnion,
|
||||||
kind: roc_types::types::AliasKind::Structural,
|
kind: AliasKind::Opaque,
|
||||||
};
|
};
|
||||||
|
|
||||||
solve_aliases.insert(alias_name, alias);
|
solve_aliases.insert(alias_name, alias);
|
||||||
};
|
};
|
||||||
|
|
||||||
unit_function(Symbol::NUM_SIGNED8, Symbol::NUM_AT_SIGNED8);
|
zero_opaque(Symbol::NUM_SIGNED8);
|
||||||
unit_function(Symbol::NUM_SIGNED16, Symbol::NUM_AT_SIGNED16);
|
zero_opaque(Symbol::NUM_SIGNED16);
|
||||||
unit_function(Symbol::NUM_SIGNED32, Symbol::NUM_AT_SIGNED32);
|
zero_opaque(Symbol::NUM_SIGNED32);
|
||||||
unit_function(Symbol::NUM_SIGNED64, Symbol::NUM_AT_SIGNED64);
|
zero_opaque(Symbol::NUM_SIGNED64);
|
||||||
unit_function(Symbol::NUM_SIGNED128, Symbol::NUM_AT_SIGNED128);
|
zero_opaque(Symbol::NUM_SIGNED128);
|
||||||
|
|
||||||
unit_function(Symbol::NUM_UNSIGNED8, Symbol::NUM_AT_UNSIGNED8);
|
zero_opaque(Symbol::NUM_UNSIGNED8);
|
||||||
unit_function(Symbol::NUM_UNSIGNED16, Symbol::NUM_AT_UNSIGNED16);
|
zero_opaque(Symbol::NUM_UNSIGNED16);
|
||||||
unit_function(Symbol::NUM_UNSIGNED32, Symbol::NUM_AT_UNSIGNED32);
|
zero_opaque(Symbol::NUM_UNSIGNED32);
|
||||||
unit_function(Symbol::NUM_UNSIGNED64, Symbol::NUM_AT_UNSIGNED64);
|
zero_opaque(Symbol::NUM_UNSIGNED64);
|
||||||
unit_function(Symbol::NUM_UNSIGNED128, Symbol::NUM_AT_UNSIGNED128);
|
zero_opaque(Symbol::NUM_UNSIGNED128);
|
||||||
|
|
||||||
unit_function(Symbol::NUM_BINARY32, Symbol::NUM_AT_BINARY32);
|
zero_opaque(Symbol::NUM_BINARY32);
|
||||||
unit_function(Symbol::NUM_BINARY64, Symbol::NUM_AT_BINARY64);
|
zero_opaque(Symbol::NUM_BINARY64);
|
||||||
|
|
||||||
solve_aliases
|
solve_aliases
|
||||||
}
|
}
|
||||||
|
|
|
@ -757,9 +757,9 @@ mod test_load {
|
||||||
r#"
|
r#"
|
||||||
interface Main exposes [ twenty, readAge ] imports [ Age.{ Age } ]
|
interface Main exposes [ twenty, readAge ] imports [ Age.{ Age } ]
|
||||||
|
|
||||||
twenty = $Age 20
|
twenty = @Age 20
|
||||||
|
|
||||||
readAge = \$Age n -> n
|
readAge = \@Age n -> n
|
||||||
"#
|
"#
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
@ -775,7 +775,7 @@ mod test_load {
|
||||||
|
|
||||||
The unwrapped opaque type Age referenced here:
|
The unwrapped opaque type Age referenced here:
|
||||||
|
|
||||||
3│ twenty = $Age 20
|
3│ twenty = @Age 20
|
||||||
^^^^
|
^^^^
|
||||||
|
|
||||||
is imported from another module:
|
is imported from another module:
|
||||||
|
@ -789,7 +789,7 @@ mod test_load {
|
||||||
|
|
||||||
The unwrapped opaque type Age referenced here:
|
The unwrapped opaque type Age referenced here:
|
||||||
|
|
||||||
5│ readAge = \$Age n -> n
|
5│ readAge = \@Age n -> n
|
||||||
^^^^
|
^^^^
|
||||||
|
|
||||||
is imported from another module:
|
is imported from another module:
|
||||||
|
|
|
@ -34,7 +34,6 @@ pub enum BinOp {
|
||||||
Slash,
|
Slash,
|
||||||
DoubleSlash,
|
DoubleSlash,
|
||||||
Percent,
|
Percent,
|
||||||
DoublePercent,
|
|
||||||
Plus,
|
Plus,
|
||||||
Minus,
|
Minus,
|
||||||
Equals,
|
Equals,
|
||||||
|
@ -58,8 +57,8 @@ impl BinOp {
|
||||||
pub fn width(self) -> u16 {
|
pub fn width(self) -> u16 {
|
||||||
match self {
|
match self {
|
||||||
Caret | Star | Slash | Percent | Plus | Minus | LessThan | GreaterThan => 1,
|
Caret | Star | Slash | Percent | Plus | Minus | LessThan | GreaterThan => 1,
|
||||||
DoubleSlash | DoublePercent | Equals | NotEquals | LessThanOrEq | GreaterThanOrEq
|
DoubleSlash | Equals | NotEquals | LessThanOrEq | GreaterThanOrEq | And | Or
|
||||||
| And | Or | Pizza => 2,
|
| Pizza => 2,
|
||||||
Assignment | IsAliasType | IsOpaqueType | Backpassing => unreachable!(),
|
Assignment | IsAliasType | IsOpaqueType | Backpassing => unreachable!(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -97,9 +96,7 @@ impl BinOp {
|
||||||
use self::Associativity::*;
|
use self::Associativity::*;
|
||||||
|
|
||||||
match self {
|
match self {
|
||||||
Pizza | Star | Slash | DoubleSlash | DoublePercent | Percent | Plus | Minus => {
|
Pizza | Star | Slash | DoubleSlash | Percent | Plus | Minus => LeftAssociative,
|
||||||
LeftAssociative
|
|
||||||
}
|
|
||||||
And | Or | Caret => RightAssociative,
|
And | Or | Caret => RightAssociative,
|
||||||
Equals | NotEquals | LessThan | GreaterThan | LessThanOrEq | GreaterThanOrEq => {
|
Equals | NotEquals | LessThan | GreaterThan | LessThanOrEq | GreaterThanOrEq => {
|
||||||
NonAssociative
|
NonAssociative
|
||||||
|
@ -111,7 +108,7 @@ impl BinOp {
|
||||||
fn precedence(self) -> u8 {
|
fn precedence(self) -> u8 {
|
||||||
match self {
|
match self {
|
||||||
Caret => 7,
|
Caret => 7,
|
||||||
Star | Slash | DoubleSlash | DoublePercent | Percent => 6,
|
Star | Slash | DoubleSlash | Percent => 6,
|
||||||
Plus | Minus => 5,
|
Plus | Minus => 5,
|
||||||
Equals | NotEquals | LessThan | GreaterThan | LessThanOrEq | GreaterThanOrEq => 4,
|
Equals | NotEquals | LessThan | GreaterThan | LessThanOrEq | GreaterThanOrEq => 4,
|
||||||
And => 3,
|
And => 3,
|
||||||
|
@ -142,7 +139,6 @@ impl std::fmt::Display for BinOp {
|
||||||
Slash => "/",
|
Slash => "/",
|
||||||
DoubleSlash => "//",
|
DoubleSlash => "//",
|
||||||
Percent => "%",
|
Percent => "%",
|
||||||
DoublePercent => "%%",
|
|
||||||
Plus => "+",
|
Plus => "+",
|
||||||
Minus => "-",
|
Minus => "-",
|
||||||
Equals => "==",
|
Equals => "==",
|
||||||
|
|
|
@ -53,10 +53,6 @@ pub enum TagName {
|
||||||
/// into integers. (Record field labels work the same way, for the same reason.)
|
/// into integers. (Record field labels work the same way, for the same reason.)
|
||||||
Global(Uppercase),
|
Global(Uppercase),
|
||||||
|
|
||||||
/// Private tags are associated with a specific module, and as such use a
|
|
||||||
/// Symbol just like all other module-specific identifiers.
|
|
||||||
Private(Symbol),
|
|
||||||
|
|
||||||
/// Used to connect the closure size to the function it corresponds to
|
/// Used to connect the closure size to the function it corresponds to
|
||||||
Closure(Symbol),
|
Closure(Symbol),
|
||||||
}
|
}
|
||||||
|
@ -69,9 +65,6 @@ impl TagName {
|
||||||
pub fn as_ident_str(&self, interns: &Interns, home: ModuleId) -> IdentStr {
|
pub fn as_ident_str(&self, interns: &Interns, home: ModuleId) -> IdentStr {
|
||||||
match self {
|
match self {
|
||||||
TagName::Global(uppercase) => uppercase.as_ident_str().clone(),
|
TagName::Global(uppercase) => uppercase.as_ident_str().clone(),
|
||||||
TagName::Private(symbol) => {
|
|
||||||
symbol.fully_qualified(interns, home).as_ident_str().clone()
|
|
||||||
}
|
|
||||||
TagName::Closure(symbol) => {
|
TagName::Closure(symbol) => {
|
||||||
symbol.fully_qualified(interns, home).as_ident_str().clone()
|
symbol.fully_qualified(interns, home).as_ident_str().clone()
|
||||||
}
|
}
|
||||||
|
|
|
@ -294,14 +294,17 @@ impl LowLevelWrapperType {
|
||||||
Symbol::NUM_DIV_FLOAT_CHECKED => WrapperIsRequired,
|
Symbol::NUM_DIV_FLOAT_CHECKED => WrapperIsRequired,
|
||||||
Symbol::NUM_DIV_CEIL => CanBeReplacedBy(NumDivCeilUnchecked),
|
Symbol::NUM_DIV_CEIL => CanBeReplacedBy(NumDivCeilUnchecked),
|
||||||
Symbol::NUM_DIV_CEIL_CHECKED => WrapperIsRequired,
|
Symbol::NUM_DIV_CEIL_CHECKED => WrapperIsRequired,
|
||||||
Symbol::NUM_REM => WrapperIsRequired,
|
Symbol::NUM_REM => CanBeReplacedBy(NumRemUnchecked),
|
||||||
|
Symbol::NUM_REM_CHECKED => WrapperIsRequired,
|
||||||
Symbol::NUM_IS_MULTIPLE_OF => CanBeReplacedBy(NumIsMultipleOf),
|
Symbol::NUM_IS_MULTIPLE_OF => CanBeReplacedBy(NumIsMultipleOf),
|
||||||
Symbol::NUM_ABS => CanBeReplacedBy(NumAbs),
|
Symbol::NUM_ABS => CanBeReplacedBy(NumAbs),
|
||||||
Symbol::NUM_NEG => CanBeReplacedBy(NumNeg),
|
Symbol::NUM_NEG => CanBeReplacedBy(NumNeg),
|
||||||
Symbol::NUM_SIN => CanBeReplacedBy(NumSin),
|
Symbol::NUM_SIN => CanBeReplacedBy(NumSin),
|
||||||
Symbol::NUM_COS => CanBeReplacedBy(NumCos),
|
Symbol::NUM_COS => CanBeReplacedBy(NumCos),
|
||||||
Symbol::NUM_SQRT => WrapperIsRequired,
|
Symbol::NUM_SQRT => CanBeReplacedBy(NumSqrtUnchecked),
|
||||||
Symbol::NUM_LOG => WrapperIsRequired,
|
Symbol::NUM_SQRT_CHECKED => WrapperIsRequired,
|
||||||
|
Symbol::NUM_LOG => CanBeReplacedBy(NumLogUnchecked),
|
||||||
|
Symbol::NUM_LOG_CHECKED => WrapperIsRequired,
|
||||||
Symbol::NUM_ROUND => CanBeReplacedBy(NumRound),
|
Symbol::NUM_ROUND => CanBeReplacedBy(NumRound),
|
||||||
Symbol::NUM_TO_FLOAT => CanBeReplacedBy(NumToFloat),
|
Symbol::NUM_TO_FLOAT => CanBeReplacedBy(NumToFloat),
|
||||||
Symbol::NUM_POW => CanBeReplacedBy(NumPow),
|
Symbol::NUM_POW => CanBeReplacedBy(NumPow),
|
||||||
|
|
|
@ -907,164 +907,143 @@ define_builtins! {
|
||||||
}
|
}
|
||||||
1 NUM: "Num" => {
|
1 NUM: "Num" => {
|
||||||
0 NUM_NUM: "Num" // the Num.Num type alias
|
0 NUM_NUM: "Num" // the Num.Num type alias
|
||||||
1 NUM_AT_NUM: "@Num" // the Num.@Num private tag
|
1 NUM_I128: "I128" // the Num.I128 type alias
|
||||||
2 NUM_I128: "I128" // the Num.I128 type alias
|
2 NUM_U128: "U128" // the Num.U128 type alias
|
||||||
3 NUM_U128: "U128" // the Num.U128 type alias
|
3 NUM_I64: "I64" // the Num.I64 type alias
|
||||||
4 NUM_I64: "I64" // the Num.I64 type alias
|
4 NUM_U64: "U64" // the Num.U64 type alias
|
||||||
5 NUM_U64: "U64" // the Num.U64 type alias
|
5 NUM_I32: "I32" // the Num.I32 type alias
|
||||||
6 NUM_I32: "I32" // the Num.I32 type alias
|
6 NUM_U32: "U32" // the Num.U32 type alias
|
||||||
7 NUM_U32: "U32" // the Num.U32 type alias
|
7 NUM_I16: "I16" // the Num.I16 type alias
|
||||||
8 NUM_I16: "I16" // the Num.I16 type alias
|
8 NUM_U16: "U16" // the Num.U16 type alias
|
||||||
9 NUM_U16: "U16" // the Num.U16 type alias
|
9 NUM_I8: "I8" // the Num.I8 type alias
|
||||||
10 NUM_I8: "I8" // the Num.I8 type alias
|
10 NUM_U8: "U8" // the Num.U8 type alias
|
||||||
11 NUM_U8: "U8" // the Num.U8 type alias
|
11 NUM_INTEGER: "Integer" // Int : Num Integer
|
||||||
12 NUM_INTEGER: "Integer" // Int : Num Integer
|
12 NUM_F64: "F64" // the Num.F64 type alias
|
||||||
13 NUM_AT_INTEGER: "@Integer" // the Int.@Integer private tag
|
13 NUM_F32: "F32" // the Num.F32 type alias
|
||||||
14 NUM_F64: "F64" // the Num.F64 type alias
|
14 NUM_FLOATINGPOINT: "FloatingPoint" // Float : Num FloatingPoint
|
||||||
15 NUM_F32: "F32" // the Num.F32 type alias
|
15 NUM_MAX_FLOAT: "maxFloat"
|
||||||
16 NUM_FLOATINGPOINT: "FloatingPoint" // Float : Num FloatingPoint
|
16 NUM_MIN_FLOAT: "minFloat"
|
||||||
17 NUM_AT_FLOATINGPOINT: "@FloatingPoint" // the Float.@FloatingPoint private tag
|
17 NUM_ABS: "abs"
|
||||||
18 NUM_MAX_FLOAT: "maxFloat"
|
18 NUM_NEG: "neg"
|
||||||
19 NUM_MIN_FLOAT: "minFloat"
|
19 NUM_ADD: "add"
|
||||||
20 NUM_ABS: "abs"
|
20 NUM_SUB: "sub"
|
||||||
21 NUM_NEG: "neg"
|
21 NUM_MUL: "mul"
|
||||||
22 NUM_ADD: "add"
|
22 NUM_LT: "isLt"
|
||||||
23 NUM_SUB: "sub"
|
23 NUM_LTE: "isLte"
|
||||||
24 NUM_MUL: "mul"
|
24 NUM_GT: "isGt"
|
||||||
25 NUM_LT: "isLt"
|
25 NUM_GTE: "isGte"
|
||||||
26 NUM_LTE: "isLte"
|
26 NUM_TO_FLOAT: "toFloat"
|
||||||
27 NUM_GT: "isGt"
|
27 NUM_SIN: "sin"
|
||||||
28 NUM_GTE: "isGte"
|
28 NUM_COS: "cos"
|
||||||
29 NUM_TO_FLOAT: "toFloat"
|
29 NUM_TAN: "tan"
|
||||||
30 NUM_SIN: "sin"
|
30 NUM_IS_ZERO: "isZero"
|
||||||
31 NUM_COS: "cos"
|
31 NUM_IS_EVEN: "isEven"
|
||||||
32 NUM_TAN: "tan"
|
32 NUM_IS_ODD: "isOdd"
|
||||||
33 NUM_IS_ZERO: "isZero"
|
33 NUM_IS_POSITIVE: "isPositive"
|
||||||
34 NUM_IS_EVEN: "isEven"
|
34 NUM_IS_NEGATIVE: "isNegative"
|
||||||
35 NUM_IS_ODD: "isOdd"
|
35 NUM_REM: "rem"
|
||||||
36 NUM_IS_POSITIVE: "isPositive"
|
36 NUM_REM_CHECKED: "remChecked"
|
||||||
37 NUM_IS_NEGATIVE: "isNegative"
|
37 NUM_DIV_FLOAT: "div"
|
||||||
38 NUM_REM: "rem"
|
38 NUM_DIV_FLOAT_CHECKED: "divChecked"
|
||||||
39 NUM_REM_CHECKED: "remChecked"
|
39 NUM_DIV_TRUNC: "divTrunc"
|
||||||
40 NUM_DIV_FLOAT: "div"
|
40 NUM_DIV_TRUNC_CHECKED: "divTruncChecked"
|
||||||
41 NUM_DIV_FLOAT_CHECKED: "divChecked"
|
41 NUM_SQRT: "sqrt"
|
||||||
42 NUM_DIV_TRUNC: "divTrunc"
|
42 NUM_SQRT_CHECKED: "sqrtChecked"
|
||||||
43 NUM_DIV_TRUNC_CHECKED: "divTruncChecked"
|
43 NUM_LOG: "log"
|
||||||
44 NUM_MOD_INT: "modInt"
|
44 NUM_LOG_CHECKED: "logChecked"
|
||||||
45 NUM_MOD_INT_CHECKED: "modIntChecked"
|
45 NUM_ROUND: "round"
|
||||||
46 NUM_MOD_FLOAT: "modFloat"
|
46 NUM_COMPARE: "compare"
|
||||||
47 NUM_MOD_FLOAT_CHECKED: "modFloatChecked"
|
47 NUM_POW: "pow"
|
||||||
48 NUM_SQRT: "sqrt"
|
48 NUM_CEILING: "ceiling"
|
||||||
49 NUM_SQRT_CHECKED: "sqrtChecked"
|
49 NUM_POW_INT: "powInt"
|
||||||
50 NUM_LOG: "log"
|
50 NUM_FLOOR: "floor"
|
||||||
51 NUM_LOG_CHECKED: "logChecked"
|
51 NUM_ADD_WRAP: "addWrap"
|
||||||
52 NUM_ROUND: "round"
|
52 NUM_ADD_CHECKED: "addChecked"
|
||||||
53 NUM_COMPARE: "compare"
|
53 NUM_ADD_SATURATED: "addSaturated"
|
||||||
54 NUM_POW: "pow"
|
54 NUM_ATAN: "atan"
|
||||||
55 NUM_CEILING: "ceiling"
|
55 NUM_ACOS: "acos"
|
||||||
56 NUM_POW_INT: "powInt"
|
56 NUM_ASIN: "asin"
|
||||||
57 NUM_FLOOR: "floor"
|
57 NUM_SIGNED128: "Signed128"
|
||||||
58 NUM_ADD_WRAP: "addWrap"
|
58 NUM_SIGNED64: "Signed64"
|
||||||
59 NUM_ADD_CHECKED: "addChecked"
|
59 NUM_SIGNED32: "Signed32"
|
||||||
60 NUM_ADD_SATURATED: "addSaturated"
|
60 NUM_SIGNED16: "Signed16"
|
||||||
61 NUM_ATAN: "atan"
|
61 NUM_SIGNED8: "Signed8"
|
||||||
62 NUM_ACOS: "acos"
|
62 NUM_UNSIGNED128: "Unsigned128"
|
||||||
63 NUM_ASIN: "asin"
|
63 NUM_UNSIGNED64: "Unsigned64"
|
||||||
64 NUM_AT_SIGNED128: "@Signed128"
|
64 NUM_UNSIGNED32: "Unsigned32"
|
||||||
65 NUM_SIGNED128: "Signed128"
|
65 NUM_UNSIGNED16: "Unsigned16"
|
||||||
66 NUM_AT_SIGNED64: "@Signed64"
|
66 NUM_UNSIGNED8: "Unsigned8"
|
||||||
67 NUM_SIGNED64: "Signed64"
|
67 NUM_BINARY64: "Binary64"
|
||||||
68 NUM_AT_SIGNED32: "@Signed32"
|
68 NUM_BINARY32: "Binary32"
|
||||||
69 NUM_SIGNED32: "Signed32"
|
69 NUM_BITWISE_AND: "bitwiseAnd"
|
||||||
70 NUM_AT_SIGNED16: "@Signed16"
|
70 NUM_BITWISE_XOR: "bitwiseXor"
|
||||||
71 NUM_SIGNED16: "Signed16"
|
71 NUM_BITWISE_OR: "bitwiseOr"
|
||||||
72 NUM_AT_SIGNED8: "@Signed8"
|
72 NUM_SHIFT_LEFT: "shiftLeftBy"
|
||||||
73 NUM_SIGNED8: "Signed8"
|
73 NUM_SHIFT_RIGHT: "shiftRightBy"
|
||||||
74 NUM_AT_UNSIGNED128: "@Unsigned128"
|
74 NUM_SHIFT_RIGHT_ZERO_FILL: "shiftRightZfBy"
|
||||||
75 NUM_UNSIGNED128: "Unsigned128"
|
75 NUM_SUB_WRAP: "subWrap"
|
||||||
76 NUM_AT_UNSIGNED64: "@Unsigned64"
|
76 NUM_SUB_CHECKED: "subChecked"
|
||||||
77 NUM_UNSIGNED64: "Unsigned64"
|
77 NUM_SUB_SATURATED: "subSaturated"
|
||||||
78 NUM_AT_UNSIGNED32: "@Unsigned32"
|
78 NUM_MUL_WRAP: "mulWrap"
|
||||||
79 NUM_UNSIGNED32: "Unsigned32"
|
79 NUM_MUL_CHECKED: "mulChecked"
|
||||||
80 NUM_AT_UNSIGNED16: "@Unsigned16"
|
80 NUM_INT: "Int"
|
||||||
81 NUM_UNSIGNED16: "Unsigned16"
|
81 NUM_FLOAT: "Float"
|
||||||
82 NUM_AT_UNSIGNED8: "@Unsigned8"
|
82 NUM_NATURAL: "Natural"
|
||||||
83 NUM_UNSIGNED8: "Unsigned8"
|
83 NUM_NAT: "Nat"
|
||||||
84 NUM_AT_BINARY64: "@Binary64"
|
84 NUM_INT_CAST: "intCast"
|
||||||
85 NUM_BINARY64: "Binary64"
|
85 NUM_IS_MULTIPLE_OF: "isMultipleOf"
|
||||||
86 NUM_AT_BINARY32: "@Binary32"
|
86 NUM_DECIMAL: "Decimal"
|
||||||
87 NUM_BINARY32: "Binary32"
|
87 NUM_DEC: "Dec" // the Num.Dectype alias
|
||||||
88 NUM_BITWISE_AND: "bitwiseAnd"
|
88 NUM_BYTES_TO_U16: "bytesToU16"
|
||||||
89 NUM_BITWISE_XOR: "bitwiseXor"
|
89 NUM_BYTES_TO_U32: "bytesToU32"
|
||||||
90 NUM_BITWISE_OR: "bitwiseOr"
|
90 NUM_CAST_TO_NAT: "#castToNat"
|
||||||
91 NUM_SHIFT_LEFT: "shiftLeftBy"
|
91 NUM_DIV_CEIL: "divCeil"
|
||||||
92 NUM_SHIFT_RIGHT: "shiftRightBy"
|
92 NUM_DIV_CEIL_CHECKED: "divCeilChecked"
|
||||||
93 NUM_SHIFT_RIGHT_ZERO_FILL: "shiftRightZfBy"
|
93 NUM_TO_STR: "toStr"
|
||||||
94 NUM_SUB_WRAP: "subWrap"
|
94 NUM_MIN_I8: "minI8"
|
||||||
95 NUM_SUB_CHECKED: "subChecked"
|
95 NUM_MAX_I8: "maxI8"
|
||||||
96 NUM_SUB_SATURATED: "subSaturated"
|
96 NUM_MIN_U8: "minU8"
|
||||||
97 NUM_MUL_WRAP: "mulWrap"
|
97 NUM_MAX_U8: "maxU8"
|
||||||
98 NUM_MUL_CHECKED: "mulChecked"
|
98 NUM_MIN_I16: "minI16"
|
||||||
99 NUM_INT: "Int"
|
99 NUM_MAX_I16: "maxI16"
|
||||||
100 NUM_FLOAT: "Float"
|
100 NUM_MIN_U16: "minU16"
|
||||||
101 NUM_AT_NATURAL: "@Natural"
|
101 NUM_MAX_U16: "maxU16"
|
||||||
102 NUM_NATURAL: "Natural"
|
102 NUM_MIN_I32: "minI32"
|
||||||
103 NUM_NAT: "Nat"
|
103 NUM_MAX_I32: "maxI32"
|
||||||
104 NUM_INT_CAST: "intCast"
|
104 NUM_MIN_U32: "minU32"
|
||||||
105 NUM_IS_MULTIPLE_OF: "isMultipleOf"
|
105 NUM_MAX_U32: "maxU32"
|
||||||
106 NUM_AT_DECIMAL: "@Decimal"
|
106 NUM_MIN_I64: "minI64"
|
||||||
107 NUM_DECIMAL: "Decimal"
|
107 NUM_MAX_I64: "maxI64"
|
||||||
108 NUM_DEC: "Dec" // the Num.Dectype alias
|
108 NUM_MIN_U64: "minU64"
|
||||||
109 NUM_BYTES_TO_U16: "bytesToU16"
|
109 NUM_MAX_U64: "maxU64"
|
||||||
110 NUM_BYTES_TO_U32: "bytesToU32"
|
110 NUM_MIN_I128: "minI128"
|
||||||
111 NUM_CAST_TO_NAT: "#castToNat"
|
111 NUM_MAX_I128: "maxI128"
|
||||||
112 NUM_DIV_CEIL: "divCeil"
|
112 NUM_TO_I8: "toI8"
|
||||||
113 NUM_DIV_CEIL_CHECKED: "divCeilChecked"
|
113 NUM_TO_I8_CHECKED: "toI8Checked"
|
||||||
114 NUM_TO_STR: "toStr"
|
114 NUM_TO_I16: "toI16"
|
||||||
115 NUM_MIN_I8: "minI8"
|
115 NUM_TO_I16_CHECKED: "toI16Checked"
|
||||||
116 NUM_MAX_I8: "maxI8"
|
116 NUM_TO_I32: "toI32"
|
||||||
117 NUM_MIN_U8: "minU8"
|
117 NUM_TO_I32_CHECKED: "toI32Checked"
|
||||||
118 NUM_MAX_U8: "maxU8"
|
118 NUM_TO_I64: "toI64"
|
||||||
119 NUM_MIN_I16: "minI16"
|
119 NUM_TO_I64_CHECKED: "toI64Checked"
|
||||||
120 NUM_MAX_I16: "maxI16"
|
120 NUM_TO_I128: "toI128"
|
||||||
121 NUM_MIN_U16: "minU16"
|
121 NUM_TO_I128_CHECKED: "toI128Checked"
|
||||||
122 NUM_MAX_U16: "maxU16"
|
122 NUM_TO_U8: "toU8"
|
||||||
123 NUM_MIN_I32: "minI32"
|
123 NUM_TO_U8_CHECKED: "toU8Checked"
|
||||||
124 NUM_MAX_I32: "maxI32"
|
124 NUM_TO_U16: "toU16"
|
||||||
125 NUM_MIN_U32: "minU32"
|
125 NUM_TO_U16_CHECKED: "toU16Checked"
|
||||||
126 NUM_MAX_U32: "maxU32"
|
126 NUM_TO_U32: "toU32"
|
||||||
127 NUM_MIN_I64: "minI64"
|
127 NUM_TO_U32_CHECKED: "toU32Checked"
|
||||||
128 NUM_MAX_I64: "maxI64"
|
128 NUM_TO_U64: "toU64"
|
||||||
129 NUM_MIN_U64: "minU64"
|
129 NUM_TO_U64_CHECKED: "toU64Checked"
|
||||||
130 NUM_MAX_U64: "maxU64"
|
130 NUM_TO_U128: "toU128"
|
||||||
131 NUM_MIN_I128: "minI128"
|
131 NUM_TO_U128_CHECKED: "toU128Checked"
|
||||||
132 NUM_MAX_I128: "maxI128"
|
132 NUM_TO_NAT: "toNat"
|
||||||
133 NUM_TO_I8: "toI8"
|
133 NUM_TO_NAT_CHECKED: "toNatChecked"
|
||||||
134 NUM_TO_I8_CHECKED: "toI8Checked"
|
134 NUM_TO_F32: "toF32"
|
||||||
135 NUM_TO_I16: "toI16"
|
135 NUM_TO_F32_CHECKED: "toF32Checked"
|
||||||
136 NUM_TO_I16_CHECKED: "toI16Checked"
|
136 NUM_TO_F64: "toF64"
|
||||||
137 NUM_TO_I32: "toI32"
|
137 NUM_TO_F64_CHECKED: "toF64Checked"
|
||||||
138 NUM_TO_I32_CHECKED: "toI32Checked"
|
|
||||||
139 NUM_TO_I64: "toI64"
|
|
||||||
140 NUM_TO_I64_CHECKED: "toI64Checked"
|
|
||||||
141 NUM_TO_I128: "toI128"
|
|
||||||
142 NUM_TO_I128_CHECKED: "toI128Checked"
|
|
||||||
143 NUM_TO_U8: "toU8"
|
|
||||||
144 NUM_TO_U8_CHECKED: "toU8Checked"
|
|
||||||
145 NUM_TO_U16: "toU16"
|
|
||||||
146 NUM_TO_U16_CHECKED: "toU16Checked"
|
|
||||||
147 NUM_TO_U32: "toU32"
|
|
||||||
148 NUM_TO_U32_CHECKED: "toU32Checked"
|
|
||||||
149 NUM_TO_U64: "toU64"
|
|
||||||
150 NUM_TO_U64_CHECKED: "toU64Checked"
|
|
||||||
151 NUM_TO_U128: "toU128"
|
|
||||||
152 NUM_TO_U128_CHECKED: "toU128Checked"
|
|
||||||
153 NUM_TO_NAT: "toNat"
|
|
||||||
154 NUM_TO_NAT_CHECKED: "toNatChecked"
|
|
||||||
155 NUM_TO_F32: "toF32"
|
|
||||||
156 NUM_TO_F32_CHECKED: "toF32Checked"
|
|
||||||
157 NUM_TO_F64: "toF64"
|
|
||||||
158 NUM_TO_F64_CHECKED: "toF64Checked"
|
|
||||||
}
|
}
|
||||||
2 BOOL: "Bool" => {
|
2 BOOL: "Bool" => {
|
||||||
0 BOOL_BOOL: "Bool" // the Bool.Bool type alias
|
0 BOOL_BOOL: "Bool" // the Bool.Bool type alias
|
||||||
|
@ -1081,101 +1060,99 @@ define_builtins! {
|
||||||
}
|
}
|
||||||
3 STR: "Str" => {
|
3 STR: "Str" => {
|
||||||
0 STR_STR: "Str" imported // the Str.Str type alias
|
0 STR_STR: "Str" imported // the Str.Str type alias
|
||||||
1 STR_AT_STR: "@Str" // the Str.@Str private tag
|
1 STR_IS_EMPTY: "isEmpty"
|
||||||
2 STR_IS_EMPTY: "isEmpty"
|
2 STR_APPEND: "#append" // unused
|
||||||
3 STR_APPEND: "#append" // unused
|
3 STR_CONCAT: "concat"
|
||||||
4 STR_CONCAT: "concat"
|
4 STR_JOIN_WITH: "joinWith"
|
||||||
5 STR_JOIN_WITH: "joinWith"
|
5 STR_SPLIT: "split"
|
||||||
6 STR_SPLIT: "split"
|
6 STR_COUNT_GRAPHEMES: "countGraphemes"
|
||||||
7 STR_COUNT_GRAPHEMES: "countGraphemes"
|
7 STR_STARTS_WITH: "startsWith"
|
||||||
8 STR_STARTS_WITH: "startsWith"
|
8 STR_ENDS_WITH: "endsWith"
|
||||||
9 STR_ENDS_WITH: "endsWith"
|
9 STR_FROM_UTF8: "fromUtf8"
|
||||||
10 STR_FROM_UTF8: "fromUtf8"
|
10 STR_UT8_PROBLEM: "Utf8Problem" // the Utf8Problem type alias
|
||||||
11 STR_UT8_PROBLEM: "Utf8Problem" // the Utf8Problem type alias
|
11 STR_UT8_BYTE_PROBLEM: "Utf8ByteProblem" // the Utf8ByteProblem type alias
|
||||||
12 STR_UT8_BYTE_PROBLEM: "Utf8ByteProblem" // the Utf8ByteProblem type alias
|
12 STR_TO_UTF8: "toUtf8"
|
||||||
13 STR_TO_UTF8: "toUtf8"
|
13 STR_STARTS_WITH_CODE_PT: "startsWithCodePt"
|
||||||
14 STR_STARTS_WITH_CODE_PT: "startsWithCodePt"
|
14 STR_ALIAS_ANALYSIS_STATIC: "#aliasAnalysisStatic" // string with the static lifetime
|
||||||
15 STR_ALIAS_ANALYSIS_STATIC: "#aliasAnalysisStatic" // string with the static lifetime
|
15 STR_FROM_UTF8_RANGE: "fromUtf8Range"
|
||||||
16 STR_FROM_UTF8_RANGE: "fromUtf8Range"
|
16 STR_REPEAT: "repeat"
|
||||||
17 STR_REPEAT: "repeat"
|
17 STR_TRIM: "trim"
|
||||||
18 STR_TRIM: "trim"
|
18 STR_TRIM_LEFT: "trimLeft"
|
||||||
19 STR_TRIM_LEFT: "trimLeft"
|
19 STR_TRIM_RIGHT: "trimRight"
|
||||||
20 STR_TRIM_RIGHT: "trimRight"
|
20 STR_TO_DEC: "toDec"
|
||||||
21 STR_TO_DEC: "toDec"
|
21 STR_TO_F64: "toF64"
|
||||||
22 STR_TO_F64: "toF64"
|
22 STR_TO_F32: "toF32"
|
||||||
23 STR_TO_F32: "toF32"
|
23 STR_TO_NAT: "toNat"
|
||||||
24 STR_TO_NAT: "toNat"
|
24 STR_TO_U128: "toU128"
|
||||||
25 STR_TO_U128: "toU128"
|
25 STR_TO_I128: "toI128"
|
||||||
26 STR_TO_I128: "toI128"
|
26 STR_TO_U64: "toU64"
|
||||||
27 STR_TO_U64: "toU64"
|
27 STR_TO_I64: "toI64"
|
||||||
28 STR_TO_I64: "toI64"
|
28 STR_TO_U32: "toU32"
|
||||||
29 STR_TO_U32: "toU32"
|
29 STR_TO_I32: "toI32"
|
||||||
30 STR_TO_I32: "toI32"
|
30 STR_TO_U16: "toU16"
|
||||||
31 STR_TO_U16: "toU16"
|
31 STR_TO_I16: "toI16"
|
||||||
32 STR_TO_I16: "toI16"
|
32 STR_TO_U8: "toU8"
|
||||||
33 STR_TO_U8: "toU8"
|
33 STR_TO_I8: "toI8"
|
||||||
34 STR_TO_I8: "toI8"
|
|
||||||
}
|
}
|
||||||
4 LIST: "List" => {
|
4 LIST: "List" => {
|
||||||
0 LIST_LIST: "List" imported // the List.List type alias
|
0 LIST_LIST: "List" imported // the List.List type alias
|
||||||
1 LIST_AT_LIST: "@List" // the List.@List private tag
|
1 LIST_IS_EMPTY: "isEmpty"
|
||||||
2 LIST_IS_EMPTY: "isEmpty"
|
2 LIST_GET: "get"
|
||||||
3 LIST_GET: "get"
|
3 LIST_SET: "set"
|
||||||
4 LIST_SET: "set"
|
4 LIST_APPEND: "append"
|
||||||
5 LIST_APPEND: "append"
|
5 LIST_MAP: "map"
|
||||||
6 LIST_MAP: "map"
|
6 LIST_LEN: "len"
|
||||||
7 LIST_LEN: "len"
|
7 LIST_WALK_BACKWARDS: "walkBackwards"
|
||||||
8 LIST_WALK_BACKWARDS: "walkBackwards"
|
8 LIST_CONCAT: "concat"
|
||||||
9 LIST_CONCAT: "concat"
|
9 LIST_FIRST: "first"
|
||||||
10 LIST_FIRST: "first"
|
10 LIST_SINGLE: "single"
|
||||||
11 LIST_SINGLE: "single"
|
11 LIST_REPEAT: "repeat"
|
||||||
12 LIST_REPEAT: "repeat"
|
12 LIST_REVERSE: "reverse"
|
||||||
13 LIST_REVERSE: "reverse"
|
13 LIST_PREPEND: "prepend"
|
||||||
14 LIST_PREPEND: "prepend"
|
14 LIST_JOIN: "join"
|
||||||
15 LIST_JOIN: "join"
|
15 LIST_KEEP_IF: "keepIf"
|
||||||
16 LIST_KEEP_IF: "keepIf"
|
16 LIST_CONTAINS: "contains"
|
||||||
17 LIST_CONTAINS: "contains"
|
17 LIST_SUM: "sum"
|
||||||
18 LIST_SUM: "sum"
|
18 LIST_WALK: "walk"
|
||||||
19 LIST_WALK: "walk"
|
19 LIST_LAST: "last"
|
||||||
20 LIST_LAST: "last"
|
20 LIST_KEEP_OKS: "keepOks"
|
||||||
21 LIST_KEEP_OKS: "keepOks"
|
21 LIST_KEEP_ERRS: "keepErrs"
|
||||||
22 LIST_KEEP_ERRS: "keepErrs"
|
22 LIST_MAP_WITH_INDEX: "mapWithIndex"
|
||||||
23 LIST_MAP_WITH_INDEX: "mapWithIndex"
|
23 LIST_MAP2: "map2"
|
||||||
24 LIST_MAP2: "map2"
|
24 LIST_MAP3: "map3"
|
||||||
25 LIST_MAP3: "map3"
|
25 LIST_PRODUCT: "product"
|
||||||
26 LIST_PRODUCT: "product"
|
26 LIST_WALK_UNTIL: "walkUntil"
|
||||||
27 LIST_WALK_UNTIL: "walkUntil"
|
27 LIST_RANGE: "range"
|
||||||
28 LIST_RANGE: "range"
|
28 LIST_SORT_WITH: "sortWith"
|
||||||
29 LIST_SORT_WITH: "sortWith"
|
29 LIST_DROP: "drop"
|
||||||
30 LIST_DROP: "drop"
|
30 LIST_SWAP: "swap"
|
||||||
31 LIST_SWAP: "swap"
|
31 LIST_DROP_AT: "dropAt"
|
||||||
32 LIST_DROP_AT: "dropAt"
|
32 LIST_DROP_LAST: "dropLast"
|
||||||
33 LIST_DROP_LAST: "dropLast"
|
33 LIST_MIN: "min"
|
||||||
34 LIST_MIN: "min"
|
34 LIST_MIN_LT: "#minlt"
|
||||||
35 LIST_MIN_LT: "#minlt"
|
35 LIST_MAX: "max"
|
||||||
36 LIST_MAX: "max"
|
36 LIST_MAX_GT: "#maxGt"
|
||||||
37 LIST_MAX_GT: "#maxGt"
|
37 LIST_MAP4: "map4"
|
||||||
38 LIST_MAP4: "map4"
|
38 LIST_DROP_FIRST: "dropFirst"
|
||||||
39 LIST_DROP_FIRST: "dropFirst"
|
39 LIST_JOIN_MAP: "joinMap"
|
||||||
40 LIST_JOIN_MAP: "joinMap"
|
40 LIST_JOIN_MAP_CONCAT: "#joinMapConcat"
|
||||||
41 LIST_JOIN_MAP_CONCAT: "#joinMapConcat"
|
41 LIST_ANY: "any"
|
||||||
42 LIST_ANY: "any"
|
42 LIST_TAKE_FIRST: "takeFirst"
|
||||||
43 LIST_TAKE_FIRST: "takeFirst"
|
43 LIST_TAKE_LAST: "takeLast"
|
||||||
44 LIST_TAKE_LAST: "takeLast"
|
44 LIST_FIND: "find"
|
||||||
45 LIST_FIND: "find"
|
45 LIST_FIND_RESULT: "#find_result" // symbol used in the definition of List.find
|
||||||
46 LIST_FIND_RESULT: "#find_result" // symbol used in the definition of List.find
|
46 LIST_SUBLIST: "sublist"
|
||||||
47 LIST_SUBLIST: "sublist"
|
47 LIST_INTERSPERSE: "intersperse"
|
||||||
48 LIST_INTERSPERSE: "intersperse"
|
48 LIST_INTERSPERSE_CLOS: "#intersperseClos"
|
||||||
49 LIST_INTERSPERSE_CLOS: "#intersperseClos"
|
49 LIST_SPLIT: "split"
|
||||||
50 LIST_SPLIT: "split"
|
50 LIST_SPLIT_CLOS: "#splitClos"
|
||||||
51 LIST_SPLIT_CLOS: "#splitClos"
|
51 LIST_ALL: "all"
|
||||||
52 LIST_ALL: "all"
|
52 LIST_DROP_IF: "dropIf"
|
||||||
53 LIST_DROP_IF: "dropIf"
|
53 LIST_DROP_IF_PREDICATE: "#dropIfPred"
|
||||||
54 LIST_DROP_IF_PREDICATE: "#dropIfPred"
|
54 LIST_SORT_ASC: "sortAsc"
|
||||||
55 LIST_SORT_ASC: "sortAsc"
|
55 LIST_SORT_DESC: "sortDesc"
|
||||||
56 LIST_SORT_DESC: "sortDesc"
|
56 LIST_SORT_DESC_COMPARE: "#sortDescCompare"
|
||||||
57 LIST_SORT_DESC_COMPARE: "#sortDescCompare"
|
57 LIST_REPLACE: "replace"
|
||||||
58 LIST_REPLACE: "replace"
|
|
||||||
}
|
}
|
||||||
5 RESULT: "Result" => {
|
5 RESULT: "Result" => {
|
||||||
0 RESULT_RESULT: "Result" // the Result.Result type alias
|
0 RESULT_RESULT: "Result" // the Result.Result type alias
|
||||||
|
@ -1192,41 +1169,39 @@ define_builtins! {
|
||||||
}
|
}
|
||||||
6 DICT: "Dict" => {
|
6 DICT: "Dict" => {
|
||||||
0 DICT_DICT: "Dict" imported // the Dict.Dict type alias
|
0 DICT_DICT: "Dict" imported // the Dict.Dict type alias
|
||||||
1 DICT_AT_DICT: "@Dict" // the Dict.@Dict private tag
|
1 DICT_EMPTY: "empty"
|
||||||
2 DICT_EMPTY: "empty"
|
2 DICT_SINGLE: "single"
|
||||||
3 DICT_SINGLE: "single"
|
3 DICT_GET: "get"
|
||||||
4 DICT_GET: "get"
|
4 DICT_GET_RESULT: "#get_result" // symbol used in the definition of Dict.get
|
||||||
5 DICT_GET_RESULT: "#get_result" // symbol used in the definition of Dict.get
|
5 DICT_WALK: "walk"
|
||||||
6 DICT_WALK: "walk"
|
6 DICT_INSERT: "insert"
|
||||||
7 DICT_INSERT: "insert"
|
7 DICT_LEN: "len"
|
||||||
8 DICT_LEN: "len"
|
|
||||||
|
|
||||||
9 DICT_REMOVE: "remove"
|
8 DICT_REMOVE: "remove"
|
||||||
10 DICT_CONTAINS: "contains"
|
9 DICT_CONTAINS: "contains"
|
||||||
11 DICT_KEYS: "keys"
|
10 DICT_KEYS: "keys"
|
||||||
12 DICT_VALUES: "values"
|
11 DICT_VALUES: "values"
|
||||||
|
|
||||||
13 DICT_UNION: "union"
|
12 DICT_UNION: "union"
|
||||||
14 DICT_INTERSECTION: "intersection"
|
13 DICT_INTERSECTION: "intersection"
|
||||||
15 DICT_DIFFERENCE: "difference"
|
14 DICT_DIFFERENCE: "difference"
|
||||||
}
|
}
|
||||||
7 SET: "Set" => {
|
7 SET: "Set" => {
|
||||||
0 SET_SET: "Set" imported // the Set.Set type alias
|
0 SET_SET: "Set" imported // the Set.Set type alias
|
||||||
1 SET_AT_SET: "@Set" // the Set.@Set private tag
|
1 SET_EMPTY: "empty"
|
||||||
2 SET_EMPTY: "empty"
|
2 SET_SINGLE: "single"
|
||||||
3 SET_SINGLE: "single"
|
3 SET_LEN: "len"
|
||||||
4 SET_LEN: "len"
|
4 SET_INSERT: "insert"
|
||||||
5 SET_INSERT: "insert"
|
5 SET_REMOVE: "remove"
|
||||||
6 SET_REMOVE: "remove"
|
6 SET_UNION: "union"
|
||||||
7 SET_UNION: "union"
|
7 SET_DIFFERENCE: "difference"
|
||||||
8 SET_DIFFERENCE: "difference"
|
8 SET_INTERSECTION: "intersection"
|
||||||
9 SET_INTERSECTION: "intersection"
|
9 SET_TO_LIST: "toList"
|
||||||
10 SET_TO_LIST: "toList"
|
10 SET_FROM_LIST: "fromList"
|
||||||
11 SET_FROM_LIST: "fromList"
|
11 SET_WALK: "walk"
|
||||||
12 SET_WALK: "walk"
|
12 SET_WALK_USER_FUNCTION: "#walk_user_function"
|
||||||
13 SET_WALK_USER_FUNCTION: "#walk_user_function"
|
13 SET_CONTAINS: "contains"
|
||||||
14 SET_CONTAINS: "contains"
|
14 SET_TO_DICT: "toDict"
|
||||||
15 SET_TO_DICT: "toDict"
|
|
||||||
}
|
}
|
||||||
8 BOX: "Box" => {
|
8 BOX: "Box" => {
|
||||||
0 BOX_BOX_TYPE: "Box" imported // the Box.Box opaque type
|
0 BOX_BOX_TYPE: "Box" imported // the Box.Box opaque type
|
||||||
|
|
|
@ -4,7 +4,7 @@ use crate::ir::{
|
||||||
use crate::layout::{Builtin, Layout, LayoutCache, TagIdIntType, UnionLayout};
|
use crate::layout::{Builtin, Layout, LayoutCache, TagIdIntType, UnionLayout};
|
||||||
use roc_builtins::bitcode::{FloatWidth, IntWidth};
|
use roc_builtins::bitcode::{FloatWidth, IntWidth};
|
||||||
use roc_collections::all::{MutMap, MutSet};
|
use roc_collections::all::{MutMap, MutSet};
|
||||||
use roc_exhaustive::{Ctor, RenderAs, TagId, Union};
|
use roc_exhaustive::{Ctor, CtorName, RenderAs, TagId, Union};
|
||||||
use roc_module::ident::TagName;
|
use roc_module::ident::TagName;
|
||||||
use roc_module::low_level::LowLevel;
|
use roc_module::low_level::LowLevel;
|
||||||
use roc_module::symbol::Symbol;
|
use roc_module::symbol::Symbol;
|
||||||
|
@ -82,7 +82,7 @@ enum GuardedTest<'a> {
|
||||||
enum Test<'a> {
|
enum Test<'a> {
|
||||||
IsCtor {
|
IsCtor {
|
||||||
tag_id: TagIdIntType,
|
tag_id: TagIdIntType,
|
||||||
tag_name: TagName,
|
ctor_name: CtorName,
|
||||||
union: roc_exhaustive::Union,
|
union: roc_exhaustive::Union,
|
||||||
arguments: Vec<(Pattern<'a>, Layout<'a>)>,
|
arguments: Vec<(Pattern<'a>, Layout<'a>)>,
|
||||||
},
|
},
|
||||||
|
@ -512,7 +512,7 @@ fn test_at_path<'a>(
|
||||||
render_as: RenderAs::Tag,
|
render_as: RenderAs::Tag,
|
||||||
alternatives: vec![Ctor {
|
alternatives: vec![Ctor {
|
||||||
tag_id: TagId(0),
|
tag_id: TagId(0),
|
||||||
name: TagName::Global(RECORD_TAG_NAME.into()),
|
name: CtorName::Tag(TagName::Global(RECORD_TAG_NAME.into())),
|
||||||
arity: destructs.len(),
|
arity: destructs.len(),
|
||||||
}],
|
}],
|
||||||
};
|
};
|
||||||
|
@ -532,7 +532,7 @@ fn test_at_path<'a>(
|
||||||
|
|
||||||
IsCtor {
|
IsCtor {
|
||||||
tag_id: 0,
|
tag_id: 0,
|
||||||
tag_name: TagName::Global(RECORD_TAG_NAME.into()),
|
ctor_name: CtorName::Tag(TagName::Global(RECORD_TAG_NAME.into())),
|
||||||
union,
|
union,
|
||||||
arguments,
|
arguments,
|
||||||
}
|
}
|
||||||
|
@ -543,11 +543,12 @@ fn test_at_path<'a>(
|
||||||
arguments,
|
arguments,
|
||||||
} => {
|
} => {
|
||||||
let tag_id = 0;
|
let tag_id = 0;
|
||||||
let union = Union::newtype_wrapper(tag_name.clone(), arguments.len());
|
let union =
|
||||||
|
Union::newtype_wrapper(CtorName::Tag(tag_name.clone()), arguments.len());
|
||||||
|
|
||||||
IsCtor {
|
IsCtor {
|
||||||
tag_id,
|
tag_id,
|
||||||
tag_name: tag_name.clone(),
|
ctor_name: CtorName::Tag(tag_name.clone()),
|
||||||
union,
|
union,
|
||||||
arguments: arguments.to_vec(),
|
arguments: arguments.to_vec(),
|
||||||
}
|
}
|
||||||
|
@ -561,7 +562,7 @@ fn test_at_path<'a>(
|
||||||
..
|
..
|
||||||
} => IsCtor {
|
} => IsCtor {
|
||||||
tag_id: *tag_id,
|
tag_id: *tag_id,
|
||||||
tag_name: tag_name.clone(),
|
ctor_name: CtorName::Tag(tag_name.clone()),
|
||||||
union: union.clone(),
|
union: union.clone(),
|
||||||
arguments: arguments.to_vec(),
|
arguments: arguments.to_vec(),
|
||||||
},
|
},
|
||||||
|
@ -571,14 +572,14 @@ fn test_at_path<'a>(
|
||||||
render_as: RenderAs::Tag,
|
render_as: RenderAs::Tag,
|
||||||
alternatives: vec![Ctor {
|
alternatives: vec![Ctor {
|
||||||
tag_id: TagId(0),
|
tag_id: TagId(0),
|
||||||
name: TagName::Private(*opaque),
|
name: CtorName::Opaque(*opaque),
|
||||||
arity: 1,
|
arity: 1,
|
||||||
}],
|
}],
|
||||||
};
|
};
|
||||||
|
|
||||||
IsCtor {
|
IsCtor {
|
||||||
tag_id: 0,
|
tag_id: 0,
|
||||||
tag_name: TagName::Private(*opaque),
|
ctor_name: CtorName::Opaque(*opaque),
|
||||||
union,
|
union,
|
||||||
arguments: vec![(**argument).clone()],
|
arguments: vec![(**argument).clone()],
|
||||||
}
|
}
|
||||||
|
@ -680,11 +681,11 @@ fn to_relevant_branch_help<'a>(
|
||||||
|
|
||||||
RecordDestructure(destructs, _) => match test {
|
RecordDestructure(destructs, _) => match test {
|
||||||
IsCtor {
|
IsCtor {
|
||||||
tag_name: test_name,
|
ctor_name: test_name,
|
||||||
tag_id,
|
tag_id,
|
||||||
..
|
..
|
||||||
} => {
|
} => {
|
||||||
debug_assert!(test_name == &TagName::Global(RECORD_TAG_NAME.into()));
|
debug_assert!(test_name == &CtorName::Tag(TagName::Global(RECORD_TAG_NAME.into())));
|
||||||
let sub_positions = destructs.into_iter().enumerate().map(|(index, destruct)| {
|
let sub_positions = destructs.into_iter().enumerate().map(|(index, destruct)| {
|
||||||
let pattern = match destruct.typ {
|
let pattern = match destruct.typ {
|
||||||
DestructType::Guard(guard) => guard.clone(),
|
DestructType::Guard(guard) => guard.clone(),
|
||||||
|
@ -713,11 +714,11 @@ fn to_relevant_branch_help<'a>(
|
||||||
|
|
||||||
OpaqueUnwrap { opaque, argument } => match test {
|
OpaqueUnwrap { opaque, argument } => match test {
|
||||||
IsCtor {
|
IsCtor {
|
||||||
tag_name: test_opaque_tag_name,
|
ctor_name: test_opaque_tag_name,
|
||||||
tag_id,
|
tag_id,
|
||||||
..
|
..
|
||||||
} => {
|
} => {
|
||||||
debug_assert_eq!(test_opaque_tag_name, &TagName::Private(opaque));
|
debug_assert_eq!(test_opaque_tag_name, &CtorName::Opaque(opaque));
|
||||||
|
|
||||||
let (argument, _) = *argument;
|
let (argument, _) = *argument;
|
||||||
|
|
||||||
|
@ -744,10 +745,10 @@ fn to_relevant_branch_help<'a>(
|
||||||
..
|
..
|
||||||
} => match test {
|
} => match test {
|
||||||
IsCtor {
|
IsCtor {
|
||||||
tag_name: test_name,
|
ctor_name: test_name,
|
||||||
tag_id: test_id,
|
tag_id: test_id,
|
||||||
..
|
..
|
||||||
} if &tag_name == test_name => {
|
} if test_name.is_tag(&tag_name) => {
|
||||||
let tag_id = 0;
|
let tag_id = 0;
|
||||||
debug_assert_eq!(tag_id, *test_id);
|
debug_assert_eq!(tag_id, *test_id);
|
||||||
|
|
||||||
|
@ -785,10 +786,10 @@ fn to_relevant_branch_help<'a>(
|
||||||
} => {
|
} => {
|
||||||
match test {
|
match test {
|
||||||
IsCtor {
|
IsCtor {
|
||||||
tag_name: test_name,
|
ctor_name: test_name,
|
||||||
tag_id: test_id,
|
tag_id: test_id,
|
||||||
..
|
..
|
||||||
} if &tag_name == test_name => {
|
} if test_name.is_tag(&tag_name) => {
|
||||||
debug_assert_eq!(tag_id, *test_id);
|
debug_assert_eq!(tag_id, *test_id);
|
||||||
|
|
||||||
// the test matches the constructor of this pattern
|
// the test matches the constructor of this pattern
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
use crate::ir::DestructType;
|
use crate::ir::DestructType;
|
||||||
use roc_collections::all::HumanIndex;
|
use roc_collections::all::HumanIndex;
|
||||||
use roc_exhaustive::{
|
use roc_exhaustive::{
|
||||||
is_useful, Context, Ctor, Error, Guard, Literal, Pattern, RenderAs, TagId, Union,
|
is_useful, Context, Ctor, CtorName, Error, Guard, Literal, Pattern, RenderAs, TagId, Union,
|
||||||
};
|
};
|
||||||
use roc_module::ident::{TagIdIntType, TagName};
|
use roc_module::ident::{TagIdIntType, TagName};
|
||||||
use roc_region::all::{Loc, Region};
|
use roc_region::all::{Loc, Region};
|
||||||
|
@ -45,7 +45,7 @@ fn simplify(pattern: &crate::ir::Pattern) -> Pattern {
|
||||||
let union = Union {
|
let union = Union {
|
||||||
render_as: RenderAs::Record(field_names),
|
render_as: RenderAs::Record(field_names),
|
||||||
alternatives: vec![Ctor {
|
alternatives: vec![Ctor {
|
||||||
name: TagName::Global("#Record".into()),
|
name: CtorName::Tag(TagName::Global("#Record".into())),
|
||||||
tag_id,
|
tag_id,
|
||||||
arity: destructures.len(),
|
arity: destructures.len(),
|
||||||
}],
|
}],
|
||||||
|
@ -62,7 +62,7 @@ fn simplify(pattern: &crate::ir::Pattern) -> Pattern {
|
||||||
let simplified_args: std::vec::Vec<_> =
|
let simplified_args: std::vec::Vec<_> =
|
||||||
arguments.iter().map(|v| simplify(&v.0)).collect();
|
arguments.iter().map(|v| simplify(&v.0)).collect();
|
||||||
Ctor(
|
Ctor(
|
||||||
Union::newtype_wrapper(tag_name.clone(), arguments.len()),
|
Union::newtype_wrapper(CtorName::Tag(tag_name.clone()), arguments.len()),
|
||||||
TagId(tag_id),
|
TagId(tag_id),
|
||||||
simplified_args,
|
simplified_args,
|
||||||
)
|
)
|
||||||
|
@ -87,7 +87,7 @@ fn simplify(pattern: &crate::ir::Pattern) -> Pattern {
|
||||||
let union = Union {
|
let union = Union {
|
||||||
render_as: RenderAs::Opaque,
|
render_as: RenderAs::Opaque,
|
||||||
alternatives: vec![Ctor {
|
alternatives: vec![Ctor {
|
||||||
name: TagName::Private(*opaque),
|
name: CtorName::Opaque(*opaque),
|
||||||
tag_id,
|
tag_id,
|
||||||
arity: 1,
|
arity: 1,
|
||||||
}],
|
}],
|
||||||
|
@ -169,7 +169,7 @@ fn to_nonredundant_rows(
|
||||||
render_as: RenderAs::Guard,
|
render_as: RenderAs::Guard,
|
||||||
alternatives: vec![Ctor {
|
alternatives: vec![Ctor {
|
||||||
tag_id,
|
tag_id,
|
||||||
name: TagName::Global("#Guard".into()),
|
name: CtorName::Tag(TagName::Global("#Guard".into())),
|
||||||
arity: 2,
|
arity: 2,
|
||||||
}],
|
}],
|
||||||
};
|
};
|
||||||
|
|
|
@ -10,7 +10,7 @@ use roc_builtins::bitcode::{FloatWidth, IntWidth};
|
||||||
use roc_can::abilities::AbilitiesStore;
|
use roc_can::abilities::AbilitiesStore;
|
||||||
use roc_can::expr::{ClosureData, IntValue};
|
use roc_can::expr::{ClosureData, IntValue};
|
||||||
use roc_collections::all::{default_hasher, BumpMap, BumpMapDefault, MutMap};
|
use roc_collections::all::{default_hasher, BumpMap, BumpMapDefault, MutMap};
|
||||||
use roc_exhaustive::{Ctor, Guard, RenderAs, TagId};
|
use roc_exhaustive::{Ctor, CtorName, Guard, RenderAs, TagId};
|
||||||
use roc_module::ident::{ForeignSymbol, Lowercase, TagName};
|
use roc_module::ident::{ForeignSymbol, Lowercase, TagName};
|
||||||
use roc_module::low_level::LowLevel;
|
use roc_module::low_level::LowLevel;
|
||||||
use roc_module::symbol::{IdentIds, ModuleId, Symbol};
|
use roc_module::symbol::{IdentIds, ModuleId, Symbol};
|
||||||
|
@ -1656,7 +1656,6 @@ impl<'a> Expr<'a> {
|
||||||
} => {
|
} => {
|
||||||
let doc_tag = match tag_name {
|
let doc_tag = match tag_name {
|
||||||
TagName::Global(s) => alloc.text(s.as_str()),
|
TagName::Global(s) => alloc.text(s.as_str()),
|
||||||
TagName::Private(s) => symbol_to_doc(alloc, *s),
|
|
||||||
TagName::Closure(s) => alloc
|
TagName::Closure(s) => alloc
|
||||||
.text("ClosureTag(")
|
.text("ClosureTag(")
|
||||||
.append(symbol_to_doc(alloc, *s))
|
.append(symbol_to_doc(alloc, *s))
|
||||||
|
@ -1678,7 +1677,6 @@ impl<'a> Expr<'a> {
|
||||||
} => {
|
} => {
|
||||||
let doc_tag = match tag_name {
|
let doc_tag = match tag_name {
|
||||||
TagName::Global(s) => alloc.text(s.as_str()),
|
TagName::Global(s) => alloc.text(s.as_str()),
|
||||||
TagName::Private(s) => alloc.text(format!("{}", s)),
|
|
||||||
TagName::Closure(s) => alloc
|
TagName::Closure(s) => alloc
|
||||||
.text("ClosureTag(")
|
.text("ClosureTag(")
|
||||||
.append(symbol_to_doc(alloc, *s))
|
.append(symbol_to_doc(alloc, *s))
|
||||||
|
@ -3499,15 +3497,24 @@ pub fn with_hole<'a>(
|
||||||
|
|
||||||
OpaqueRef { argument, .. } => {
|
OpaqueRef { argument, .. } => {
|
||||||
let (arg_var, loc_arg_expr) = *argument;
|
let (arg_var, loc_arg_expr) = *argument;
|
||||||
with_hole(
|
|
||||||
env,
|
match can_reuse_symbol(env, procs, &loc_arg_expr.value) {
|
||||||
loc_arg_expr.value,
|
// Opaques decay to their argument.
|
||||||
arg_var,
|
ReuseSymbol::Value(real_name) => {
|
||||||
procs,
|
let mut result = hole.clone();
|
||||||
layout_cache,
|
substitute_in_exprs(arena, &mut result, assigned, real_name);
|
||||||
assigned,
|
result
|
||||||
hole,
|
}
|
||||||
)
|
_ => with_hole(
|
||||||
|
env,
|
||||||
|
loc_arg_expr.value,
|
||||||
|
arg_var,
|
||||||
|
procs,
|
||||||
|
layout_cache,
|
||||||
|
assigned,
|
||||||
|
hole,
|
||||||
|
),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Record {
|
Record {
|
||||||
|
@ -8030,7 +8037,7 @@ fn from_can_pattern_help<'a>(
|
||||||
render_as: RenderAs::Tag,
|
render_as: RenderAs::Tag,
|
||||||
alternatives: vec![Ctor {
|
alternatives: vec![Ctor {
|
||||||
tag_id: TagId(0),
|
tag_id: TagId(0),
|
||||||
name: tag_name.clone(),
|
name: CtorName::Tag(tag_name.clone()),
|
||||||
arity: 0,
|
arity: 0,
|
||||||
}],
|
}],
|
||||||
},
|
},
|
||||||
|
@ -8043,12 +8050,12 @@ fn from_can_pattern_help<'a>(
|
||||||
alternatives: vec![
|
alternatives: vec![
|
||||||
Ctor {
|
Ctor {
|
||||||
tag_id: TagId(0),
|
tag_id: TagId(0),
|
||||||
name: ffalse,
|
name: CtorName::Tag(ffalse),
|
||||||
arity: 0,
|
arity: 0,
|
||||||
},
|
},
|
||||||
Ctor {
|
Ctor {
|
||||||
tag_id: TagId(1),
|
tag_id: TagId(1),
|
||||||
name: ttrue,
|
name: CtorName::Tag(ttrue),
|
||||||
arity: 0,
|
arity: 0,
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
|
@ -8064,7 +8071,7 @@ fn from_can_pattern_help<'a>(
|
||||||
for (i, tag_name) in tag_names.into_iter().enumerate() {
|
for (i, tag_name) in tag_names.into_iter().enumerate() {
|
||||||
ctors.push(Ctor {
|
ctors.push(Ctor {
|
||||||
tag_id: TagId(i as _),
|
tag_id: TagId(i as _),
|
||||||
name: tag_name,
|
name: CtorName::Tag(tag_name),
|
||||||
arity: 0,
|
arity: 0,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -8155,7 +8162,7 @@ fn from_can_pattern_help<'a>(
|
||||||
for (i, (tag_name, args)) in tags.iter().enumerate() {
|
for (i, (tag_name, args)) in tags.iter().enumerate() {
|
||||||
ctors.push(Ctor {
|
ctors.push(Ctor {
|
||||||
tag_id: TagId(i as _),
|
tag_id: TagId(i as _),
|
||||||
name: tag_name.clone(),
|
name: CtorName::Tag(tag_name.clone()),
|
||||||
arity: args.len(),
|
arity: args.len(),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -8206,7 +8213,7 @@ fn from_can_pattern_help<'a>(
|
||||||
for (i, (tag_name, args)) in tags.iter().enumerate() {
|
for (i, (tag_name, args)) in tags.iter().enumerate() {
|
||||||
ctors.push(Ctor {
|
ctors.push(Ctor {
|
||||||
tag_id: TagId(i as _),
|
tag_id: TagId(i as _),
|
||||||
name: tag_name.clone(),
|
name: CtorName::Tag(tag_name.clone()),
|
||||||
// don't include tag discriminant in arity
|
// don't include tag discriminant in arity
|
||||||
arity: args.len() - 1,
|
arity: args.len() - 1,
|
||||||
})
|
})
|
||||||
|
@ -8251,7 +8258,7 @@ fn from_can_pattern_help<'a>(
|
||||||
|
|
||||||
ctors.push(Ctor {
|
ctors.push(Ctor {
|
||||||
tag_id: TagId(0),
|
tag_id: TagId(0),
|
||||||
name: tag_name.clone(),
|
name: CtorName::Tag(tag_name.clone()),
|
||||||
arity: fields.len(),
|
arity: fields.len(),
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -8298,7 +8305,7 @@ fn from_can_pattern_help<'a>(
|
||||||
if i == nullable_id as usize {
|
if i == nullable_id as usize {
|
||||||
ctors.push(Ctor {
|
ctors.push(Ctor {
|
||||||
tag_id: TagId(i as _),
|
tag_id: TagId(i as _),
|
||||||
name: nullable_name.clone(),
|
name: CtorName::Tag(nullable_name.clone()),
|
||||||
// don't include tag discriminant in arity
|
// don't include tag discriminant in arity
|
||||||
arity: 0,
|
arity: 0,
|
||||||
});
|
});
|
||||||
|
@ -8308,7 +8315,7 @@ fn from_can_pattern_help<'a>(
|
||||||
|
|
||||||
ctors.push(Ctor {
|
ctors.push(Ctor {
|
||||||
tag_id: TagId(i as _),
|
tag_id: TagId(i as _),
|
||||||
name: tag_name.clone(),
|
name: CtorName::Tag(tag_name.clone()),
|
||||||
// don't include tag discriminant in arity
|
// don't include tag discriminant in arity
|
||||||
arity: args.len() - 1,
|
arity: args.len() - 1,
|
||||||
});
|
});
|
||||||
|
@ -8319,7 +8326,7 @@ fn from_can_pattern_help<'a>(
|
||||||
if i == nullable_id as usize {
|
if i == nullable_id as usize {
|
||||||
ctors.push(Ctor {
|
ctors.push(Ctor {
|
||||||
tag_id: TagId(i as _),
|
tag_id: TagId(i as _),
|
||||||
name: nullable_name.clone(),
|
name: CtorName::Tag(nullable_name.clone()),
|
||||||
// don't include tag discriminant in arity
|
// don't include tag discriminant in arity
|
||||||
arity: 0,
|
arity: 0,
|
||||||
});
|
});
|
||||||
|
@ -8369,13 +8376,13 @@ fn from_can_pattern_help<'a>(
|
||||||
|
|
||||||
ctors.push(Ctor {
|
ctors.push(Ctor {
|
||||||
tag_id: TagId(nullable_id as _),
|
tag_id: TagId(nullable_id as _),
|
||||||
name: nullable_name.clone(),
|
name: CtorName::Tag(nullable_name.clone()),
|
||||||
arity: 0,
|
arity: 0,
|
||||||
});
|
});
|
||||||
|
|
||||||
ctors.push(Ctor {
|
ctors.push(Ctor {
|
||||||
tag_id: TagId(!nullable_id as _),
|
tag_id: TagId(!nullable_id as _),
|
||||||
name: nullable_name.clone(),
|
name: CtorName::Tag(nullable_name.clone()),
|
||||||
// FIXME drop tag
|
// FIXME drop tag
|
||||||
arity: other_fields.len() - 1,
|
arity: other_fields.len() - 1,
|
||||||
});
|
});
|
||||||
|
@ -8631,9 +8638,9 @@ pub fn num_argument_to_int_or_float(
|
||||||
num_argument_to_int_or_float(subs, target_info, var, true)
|
num_argument_to_int_or_float(subs, target_info, var, true)
|
||||||
}
|
}
|
||||||
|
|
||||||
Symbol::NUM_DECIMAL | Symbol::NUM_AT_DECIMAL => IntOrFloat::DecimalFloatType,
|
Symbol::NUM_DECIMAL => IntOrFloat::DecimalFloatType,
|
||||||
|
|
||||||
Symbol::NUM_NAT | Symbol::NUM_NATURAL | Symbol::NUM_AT_NATURAL => {
|
Symbol::NUM_NAT | Symbol::NUM_NATURAL => {
|
||||||
let int_width = match target_info.ptr_width() {
|
let int_width = match target_info.ptr_width() {
|
||||||
roc_target::PtrWidth::Bytes4 => IntWidth::U32,
|
roc_target::PtrWidth::Bytes4 => IntWidth::U32,
|
||||||
roc_target::PtrWidth::Bytes8 => IntWidth::U64,
|
roc_target::PtrWidth::Bytes8 => IntWidth::U64,
|
||||||
|
|
|
@ -311,6 +311,50 @@ impl<'a> UnionLayout<'a> {
|
||||||
.append(alloc.intersperse(tags_doc, ", "))
|
.append(alloc.intersperse(tags_doc, ", "))
|
||||||
.append(alloc.text("]"))
|
.append(alloc.text("]"))
|
||||||
}
|
}
|
||||||
|
Recursive(tags) => {
|
||||||
|
let tags_doc = tags.iter().map(|fields| {
|
||||||
|
alloc.text("C ").append(alloc.intersperse(
|
||||||
|
fields.iter().map(|x| x.to_doc(alloc, Parens::InTypeParam)),
|
||||||
|
" ",
|
||||||
|
))
|
||||||
|
});
|
||||||
|
alloc
|
||||||
|
.text("[<r>")
|
||||||
|
.append(alloc.intersperse(tags_doc, ", "))
|
||||||
|
.append(alloc.text("]"))
|
||||||
|
}
|
||||||
|
NonNullableUnwrapped(fields) => {
|
||||||
|
let fields_doc = alloc.text("C ").append(alloc.intersperse(
|
||||||
|
fields.iter().map(|x| x.to_doc(alloc, Parens::InTypeParam)),
|
||||||
|
" ",
|
||||||
|
));
|
||||||
|
alloc
|
||||||
|
.text("[<rnnu>")
|
||||||
|
.append(fields_doc)
|
||||||
|
.append(alloc.text("]"))
|
||||||
|
}
|
||||||
|
NullableUnwrapped {
|
||||||
|
nullable_id,
|
||||||
|
other_fields,
|
||||||
|
} => {
|
||||||
|
let fields_doc = alloc.text("C ").append(
|
||||||
|
alloc.intersperse(
|
||||||
|
other_fields
|
||||||
|
.iter()
|
||||||
|
.map(|x| x.to_doc(alloc, Parens::InTypeParam)),
|
||||||
|
" ",
|
||||||
|
),
|
||||||
|
);
|
||||||
|
let tags_doc = if nullable_id {
|
||||||
|
alloc.concat(vec![alloc.text("<null>, "), fields_doc])
|
||||||
|
} else {
|
||||||
|
alloc.concat(vec![fields_doc, alloc.text(", <null>")])
|
||||||
|
};
|
||||||
|
alloc
|
||||||
|
.text("[<rnu>")
|
||||||
|
.append(tags_doc)
|
||||||
|
.append(alloc.text("]"))
|
||||||
|
}
|
||||||
_ => alloc.text("TODO"),
|
_ => alloc.text("TODO"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -939,6 +983,16 @@ pub const fn round_up_to_alignment(width: u32, alignment: u32) -> u32 {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
pub fn is_unresolved_var(subs: &Subs, var: Variable) -> bool {
|
||||||
|
use Content::*;
|
||||||
|
let content = subs.get_content_without_compacting(var);
|
||||||
|
matches!(
|
||||||
|
content,
|
||||||
|
FlexVar(..) | RigidVar(..) | FlexAbleVar(..) | RigidAbleVar(..),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
impl<'a> Layout<'a> {
|
impl<'a> Layout<'a> {
|
||||||
pub const VOID: Self = Layout::Union(UnionLayout::NonRecursive(&[]));
|
pub const VOID: Self = Layout::Union(UnionLayout::NonRecursive(&[]));
|
||||||
pub const UNIT: Self = Layout::Struct {
|
pub const UNIT: Self = Layout::Struct {
|
||||||
|
@ -971,12 +1025,24 @@ impl<'a> Layout<'a> {
|
||||||
}
|
}
|
||||||
|
|
||||||
match symbol {
|
match symbol {
|
||||||
Symbol::NUM_DECIMAL | Symbol::NUM_AT_DECIMAL => {
|
Symbol::NUM_DECIMAL => return Ok(Layout::Builtin(Builtin::Decimal)),
|
||||||
return Ok(Layout::Builtin(Builtin::Decimal))
|
|
||||||
|
Symbol::NUM_NAT | Symbol::NUM_NATURAL => {
|
||||||
|
return Ok(Layout::usize(env.target_info))
|
||||||
}
|
}
|
||||||
|
|
||||||
Symbol::NUM_NAT | Symbol::NUM_NATURAL | Symbol::NUM_AT_NATURAL => {
|
Symbol::NUM_NUM | Symbol::NUM_INT | Symbol::NUM_INTEGER
|
||||||
return Ok(Layout::usize(env.target_info))
|
if is_unresolved_var(env.subs, actual_var) =>
|
||||||
|
{
|
||||||
|
// default to i64
|
||||||
|
return Ok(Layout::i64());
|
||||||
|
}
|
||||||
|
|
||||||
|
Symbol::NUM_FLOAT | Symbol::NUM_FLOATINGPOINT
|
||||||
|
if is_unresolved_var(env.subs, actual_var) =>
|
||||||
|
{
|
||||||
|
// default to f64
|
||||||
|
return Ok(Layout::f64());
|
||||||
}
|
}
|
||||||
|
|
||||||
_ => Self::from_var(env, actual_var),
|
_ => Self::from_var(env, actual_var),
|
||||||
|
@ -1645,7 +1711,7 @@ fn layout_from_flat_type<'a>(
|
||||||
Ok(Layout::f32())
|
Ok(Layout::f32())
|
||||||
}
|
}
|
||||||
|
|
||||||
Symbol::NUM_NUM | Symbol::NUM_AT_NUM => {
|
Symbol::NUM_NUM => {
|
||||||
// Num.Num should only ever have 1 argument, e.g. Num.Num Int.Integer
|
// Num.Num should only ever have 1 argument, e.g. Num.Num Int.Integer
|
||||||
debug_assert_eq!(args.len(), 1);
|
debug_assert_eq!(args.len(), 1);
|
||||||
|
|
||||||
|
@ -1731,7 +1797,7 @@ fn layout_from_flat_type<'a>(
|
||||||
|
|
||||||
debug_assert!(ext_var_is_empty_tag_union(subs, ext_var));
|
debug_assert!(ext_var_is_empty_tag_union(subs, ext_var));
|
||||||
|
|
||||||
Ok(layout_from_tag_union(arena, &tags, subs, env.target_info))
|
Ok(layout_from_tag_union(env, &tags))
|
||||||
}
|
}
|
||||||
FunctionOrTagUnion(tag_name, _, ext_var) => {
|
FunctionOrTagUnion(tag_name, _, ext_var) => {
|
||||||
debug_assert!(
|
debug_assert!(
|
||||||
|
@ -1742,7 +1808,7 @@ fn layout_from_flat_type<'a>(
|
||||||
let union_tags = UnionTags::from_tag_name_index(tag_name);
|
let union_tags = UnionTags::from_tag_name_index(tag_name);
|
||||||
let (tags, _) = union_tags.unsorted_tags_and_ext(subs, ext_var);
|
let (tags, _) = union_tags.unsorted_tags_and_ext(subs, ext_var);
|
||||||
|
|
||||||
Ok(layout_from_tag_union(arena, &tags, subs, env.target_info))
|
Ok(layout_from_tag_union(env, &tags))
|
||||||
}
|
}
|
||||||
RecursiveTagUnion(rec_var, tags, ext_var) => {
|
RecursiveTagUnion(rec_var, tags, ext_var) => {
|
||||||
let (tags, ext_var) = tags.unsorted_tags_and_ext(subs, ext_var);
|
let (tags, ext_var) = tags.unsorted_tags_and_ext(subs, ext_var);
|
||||||
|
@ -2071,23 +2137,14 @@ fn is_recursive_tag_union(layout: &Layout) -> bool {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn union_sorted_tags_help_new<'a>(
|
fn union_sorted_tags_help_new<'a>(
|
||||||
arena: &'a Bump,
|
env: &mut Env<'a, '_>,
|
||||||
tags_list: &[(&'_ TagName, &[Variable])],
|
tags_list: &[(&'_ TagName, &[Variable])],
|
||||||
opt_rec_var: Option<Variable>,
|
opt_rec_var: Option<Variable>,
|
||||||
subs: &Subs,
|
|
||||||
target_info: TargetInfo,
|
|
||||||
) -> UnionVariant<'a> {
|
) -> UnionVariant<'a> {
|
||||||
// sort up front; make sure the ordering stays intact!
|
// sort up front; make sure the ordering stays intact!
|
||||||
let mut tags_list = Vec::from_iter_in(tags_list.iter(), arena);
|
let mut tags_list = Vec::from_iter_in(tags_list.iter(), env.arena);
|
||||||
tags_list.sort_unstable_by(|(a, _), (b, _)| a.cmp(b));
|
tags_list.sort_unstable_by(|(a, _), (b, _)| a.cmp(b));
|
||||||
|
|
||||||
let mut env = Env {
|
|
||||||
arena,
|
|
||||||
subs,
|
|
||||||
seen: Vec::new_in(arena),
|
|
||||||
target_info,
|
|
||||||
};
|
|
||||||
|
|
||||||
match tags_list.len() {
|
match tags_list.len() {
|
||||||
0 => {
|
0 => {
|
||||||
// trying to instantiate a type with no values
|
// trying to instantiate a type with no values
|
||||||
|
@ -2098,39 +2155,29 @@ fn union_sorted_tags_help_new<'a>(
|
||||||
let tag_name = tag_name.clone();
|
let tag_name = tag_name.clone();
|
||||||
|
|
||||||
// just one tag in the union (but with arguments) can be a struct
|
// just one tag in the union (but with arguments) can be a struct
|
||||||
let mut layouts = Vec::with_capacity_in(tags_list.len(), arena);
|
let mut layouts = Vec::with_capacity_in(tags_list.len(), env.arena);
|
||||||
|
|
||||||
// special-case NUM_AT_NUM: if its argument is a FlexVar, make it Int
|
for &var in arguments {
|
||||||
match tag_name {
|
match Layout::from_var(env, var) {
|
||||||
TagName::Private(Symbol::NUM_AT_NUM) => {
|
Ok(layout) => {
|
||||||
let var = arguments[0];
|
layouts.push(layout);
|
||||||
layouts
|
}
|
||||||
.push(unwrap_num_tag(subs, var, target_info).expect("invalid num layout"));
|
Err(LayoutProblem::UnresolvedTypeVar(_)) => {
|
||||||
}
|
// If we encounter an unbound type var (e.g. `Ok *`)
|
||||||
_ => {
|
// then it's zero-sized; In the future we may drop this argument
|
||||||
for &var in arguments {
|
// completely, but for now we represent it with the empty tag union
|
||||||
match Layout::from_var(&mut env, var) {
|
layouts.push(Layout::VOID)
|
||||||
Ok(layout) => {
|
}
|
||||||
layouts.push(layout);
|
Err(LayoutProblem::Erroneous) => {
|
||||||
}
|
// An erroneous type var will code gen to a runtime
|
||||||
Err(LayoutProblem::UnresolvedTypeVar(_)) => {
|
// error, so we don't need to store any data for it.
|
||||||
// If we encounter an unbound type var (e.g. `Ok *`)
|
|
||||||
// then it's zero-sized; In the future we may drop this argument
|
|
||||||
// completely, but for now we represent it with the empty tag union
|
|
||||||
layouts.push(Layout::VOID)
|
|
||||||
}
|
|
||||||
Err(LayoutProblem::Erroneous) => {
|
|
||||||
// An erroneous type var will code gen to a runtime
|
|
||||||
// error, so we don't need to store any data for it.
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
layouts.sort_by(|layout1, layout2| {
|
layouts.sort_by(|layout1, layout2| {
|
||||||
let size1 = layout1.alignment_bytes(target_info);
|
let size1 = layout1.alignment_bytes(env.target_info);
|
||||||
let size2 = layout2.alignment_bytes(target_info);
|
let size2 = layout2.alignment_bytes(env.target_info);
|
||||||
|
|
||||||
size2.cmp(&size1)
|
size2.cmp(&size1)
|
||||||
});
|
});
|
||||||
|
@ -2151,7 +2198,7 @@ fn union_sorted_tags_help_new<'a>(
|
||||||
}
|
}
|
||||||
num_tags => {
|
num_tags => {
|
||||||
// default path
|
// default path
|
||||||
let mut answer = Vec::with_capacity_in(tags_list.len(), arena);
|
let mut answer = Vec::with_capacity_in(tags_list.len(), env.arena);
|
||||||
let mut has_any_arguments = false;
|
let mut has_any_arguments = false;
|
||||||
|
|
||||||
let mut nullable: Option<(TagIdIntType, TagName)> = None;
|
let mut nullable: Option<(TagIdIntType, TagName)> = None;
|
||||||
|
@ -2174,17 +2221,19 @@ fn union_sorted_tags_help_new<'a>(
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut arg_layouts = Vec::with_capacity_in(arguments.len() + 1, arena);
|
let mut arg_layouts = Vec::with_capacity_in(arguments.len() + 1, env.arena);
|
||||||
|
|
||||||
for &var in arguments {
|
for &var in arguments {
|
||||||
match Layout::from_var(&mut env, var) {
|
match Layout::from_var(env, var) {
|
||||||
Ok(layout) => {
|
Ok(layout) => {
|
||||||
has_any_arguments = true;
|
has_any_arguments = true;
|
||||||
|
|
||||||
// make sure to not unroll recursive types!
|
// make sure to not unroll recursive types!
|
||||||
let self_recursion = opt_rec_var.is_some()
|
let self_recursion = opt_rec_var.is_some()
|
||||||
&& subs.get_root_key_without_compacting(var)
|
&& env.subs.get_root_key_without_compacting(var)
|
||||||
== subs.get_root_key_without_compacting(opt_rec_var.unwrap())
|
== env
|
||||||
|
.subs
|
||||||
|
.get_root_key_without_compacting(opt_rec_var.unwrap())
|
||||||
&& is_recursive_tag_union(&layout);
|
&& is_recursive_tag_union(&layout);
|
||||||
|
|
||||||
if self_recursion {
|
if self_recursion {
|
||||||
|
@ -2207,8 +2256,8 @@ fn union_sorted_tags_help_new<'a>(
|
||||||
}
|
}
|
||||||
|
|
||||||
arg_layouts.sort_by(|layout1, layout2| {
|
arg_layouts.sort_by(|layout1, layout2| {
|
||||||
let size1 = layout1.alignment_bytes(target_info);
|
let size1 = layout1.alignment_bytes(env.target_info);
|
||||||
let size2 = layout2.alignment_bytes(target_info);
|
let size2 = layout2.alignment_bytes(env.target_info);
|
||||||
|
|
||||||
size2.cmp(&size1)
|
size2.cmp(&size1)
|
||||||
});
|
});
|
||||||
|
@ -2229,7 +2278,7 @@ fn union_sorted_tags_help_new<'a>(
|
||||||
3..=MAX_ENUM_SIZE if !has_any_arguments => {
|
3..=MAX_ENUM_SIZE if !has_any_arguments => {
|
||||||
// type can be stored in a byte
|
// type can be stored in a byte
|
||||||
// needs the sorted tag names to determine the tag_id
|
// needs the sorted tag names to determine the tag_id
|
||||||
let mut tag_names = Vec::with_capacity_in(answer.len(), arena);
|
let mut tag_names = Vec::with_capacity_in(answer.len(), env.arena);
|
||||||
|
|
||||||
for (tag_name, _) in answer {
|
for (tag_name, _) in answer {
|
||||||
tag_names.push(tag_name);
|
tag_names.push(tag_name);
|
||||||
|
@ -2303,37 +2352,26 @@ pub fn union_sorted_tags_help<'a>(
|
||||||
let mut layouts = Vec::with_capacity_in(tags_vec.len(), arena);
|
let mut layouts = Vec::with_capacity_in(tags_vec.len(), arena);
|
||||||
let mut contains_zero_sized = false;
|
let mut contains_zero_sized = false;
|
||||||
|
|
||||||
// special-case NUM_AT_NUM: if its argument is a FlexVar, make it Int
|
for var in arguments {
|
||||||
match tag_name {
|
match Layout::from_var(&mut env, var) {
|
||||||
TagName::Private(Symbol::NUM_AT_NUM) => {
|
Ok(layout) => {
|
||||||
layouts.push(
|
// Drop any zero-sized arguments like {}
|
||||||
unwrap_num_tag(subs, arguments[0], target_info)
|
if !layout.is_dropped_because_empty() {
|
||||||
.expect("invalid num layout"),
|
layouts.push(layout);
|
||||||
);
|
} else {
|
||||||
}
|
contains_zero_sized = true;
|
||||||
_ => {
|
|
||||||
for var in arguments {
|
|
||||||
match Layout::from_var(&mut env, var) {
|
|
||||||
Ok(layout) => {
|
|
||||||
// Drop any zero-sized arguments like {}
|
|
||||||
if !layout.is_dropped_because_empty() {
|
|
||||||
layouts.push(layout);
|
|
||||||
} else {
|
|
||||||
contains_zero_sized = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Err(LayoutProblem::UnresolvedTypeVar(_)) => {
|
|
||||||
// If we encounter an unbound type var (e.g. `Ok *`)
|
|
||||||
// then it's zero-sized; In the future we may drop this argument
|
|
||||||
// completely, but for now we represent it with the empty tag union
|
|
||||||
layouts.push(Layout::VOID)
|
|
||||||
}
|
|
||||||
Err(LayoutProblem::Erroneous) => {
|
|
||||||
// An erroneous type var will code gen to a runtime
|
|
||||||
// error, so we don't need to store any data for it.
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Err(LayoutProblem::UnresolvedTypeVar(_)) => {
|
||||||
|
// If we encounter an unbound type var (e.g. `Ok *`)
|
||||||
|
// then it's zero-sized; In the future we may drop this argument
|
||||||
|
// completely, but for now we represent it with the empty tag union
|
||||||
|
layouts.push(Layout::VOID)
|
||||||
|
}
|
||||||
|
Err(LayoutProblem::Erroneous) => {
|
||||||
|
// An erroneous type var will code gen to a runtime
|
||||||
|
// error, so we don't need to store any data for it.
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2488,128 +2526,95 @@ pub fn union_sorted_tags_help<'a>(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn layout_from_newtype<'a>(
|
fn layout_from_newtype<'a>(env: &mut Env<'a, '_>, tags: &UnsortedUnionTags) -> Layout<'a> {
|
||||||
arena: &'a Bump,
|
debug_assert!(tags.is_newtype_wrapper(env.subs));
|
||||||
tags: &UnsortedUnionTags,
|
|
||||||
subs: &Subs,
|
|
||||||
target_info: TargetInfo,
|
|
||||||
) -> Layout<'a> {
|
|
||||||
debug_assert!(tags.is_newtype_wrapper(subs));
|
|
||||||
|
|
||||||
let (tag_name, var) = tags.get_newtype(subs);
|
let (_tag_name, var) = tags.get_newtype(env.subs);
|
||||||
|
|
||||||
if tag_name == &TagName::Private(Symbol::NUM_AT_NUM) {
|
match Layout::from_var(env, var) {
|
||||||
unwrap_num_tag(subs, var, target_info).expect("invalid Num argument")
|
Ok(layout) => layout,
|
||||||
} else {
|
Err(LayoutProblem::UnresolvedTypeVar(_)) => {
|
||||||
let mut env = Env {
|
// If we encounter an unbound type var (e.g. `Ok *`)
|
||||||
arena,
|
// then it's zero-sized; In the future we may drop this argument
|
||||||
subs,
|
// completely, but for now we represent it with the empty tag union
|
||||||
seen: Vec::new_in(arena),
|
Layout::VOID
|
||||||
target_info,
|
}
|
||||||
};
|
Err(LayoutProblem::Erroneous) => {
|
||||||
|
// An erroneous type var will code gen to a runtime
|
||||||
match Layout::from_var(&mut env, var) {
|
// error, so we don't need to store any data for it.
|
||||||
Ok(layout) => layout,
|
todo!()
|
||||||
Err(LayoutProblem::UnresolvedTypeVar(_)) => {
|
|
||||||
// If we encounter an unbound type var (e.g. `Ok *`)
|
|
||||||
// then it's zero-sized; In the future we may drop this argument
|
|
||||||
// completely, but for now we represent it with the empty tag union
|
|
||||||
Layout::VOID
|
|
||||||
}
|
|
||||||
Err(LayoutProblem::Erroneous) => {
|
|
||||||
// An erroneous type var will code gen to a runtime
|
|
||||||
// error, so we don't need to store any data for it.
|
|
||||||
todo!()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn layout_from_tag_union<'a>(
|
fn layout_from_tag_union<'a>(env: &mut Env<'a, '_>, tags: &UnsortedUnionTags) -> Layout<'a> {
|
||||||
arena: &'a Bump,
|
|
||||||
tags: &UnsortedUnionTags,
|
|
||||||
subs: &Subs,
|
|
||||||
target_info: TargetInfo,
|
|
||||||
) -> Layout<'a> {
|
|
||||||
use UnionVariant::*;
|
use UnionVariant::*;
|
||||||
|
|
||||||
if tags.is_newtype_wrapper(subs) {
|
if tags.is_newtype_wrapper(env.subs) {
|
||||||
return layout_from_newtype(arena, tags, subs, target_info);
|
return layout_from_newtype(env, tags);
|
||||||
}
|
}
|
||||||
|
|
||||||
let tags_vec = &tags.tags;
|
let tags_vec = &tags.tags;
|
||||||
|
|
||||||
match tags_vec.get(0) {
|
let opt_rec_var = None;
|
||||||
Some((tag_name, arguments)) if *tag_name == &TagName::Private(Symbol::NUM_AT_NUM) => {
|
let variant = union_sorted_tags_help_new(env, tags_vec, opt_rec_var);
|
||||||
debug_assert_eq!(arguments.len(), 1);
|
|
||||||
|
|
||||||
let &var = arguments.iter().next().unwrap();
|
match variant {
|
||||||
|
Never => Layout::VOID,
|
||||||
|
Unit | UnitWithArguments => Layout::UNIT,
|
||||||
|
BoolUnion { .. } => Layout::bool(),
|
||||||
|
ByteUnion(_) => Layout::u8(),
|
||||||
|
Newtype {
|
||||||
|
arguments: field_layouts,
|
||||||
|
..
|
||||||
|
} => {
|
||||||
|
let answer1 = if field_layouts.len() == 1 {
|
||||||
|
field_layouts[0]
|
||||||
|
} else {
|
||||||
|
Layout::struct_no_name_order(field_layouts.into_bump_slice())
|
||||||
|
};
|
||||||
|
|
||||||
unwrap_num_tag(subs, var, target_info).expect("invalid Num argument")
|
answer1
|
||||||
}
|
}
|
||||||
_ => {
|
Wrapped(variant) => {
|
||||||
let opt_rec_var = None;
|
use WrappedVariant::*;
|
||||||
let variant =
|
|
||||||
union_sorted_tags_help_new(arena, tags_vec, opt_rec_var, subs, target_info);
|
|
||||||
|
|
||||||
match variant {
|
match variant {
|
||||||
Never => Layout::VOID,
|
NonRecursive {
|
||||||
Unit | UnitWithArguments => Layout::UNIT,
|
sorted_tag_layouts: tags,
|
||||||
BoolUnion { .. } => Layout::bool(),
|
|
||||||
ByteUnion(_) => Layout::u8(),
|
|
||||||
Newtype {
|
|
||||||
arguments: field_layouts,
|
|
||||||
..
|
|
||||||
} => {
|
} => {
|
||||||
let answer1 = if field_layouts.len() == 1 {
|
let mut tag_layouts = Vec::with_capacity_in(tags.len(), env.arena);
|
||||||
field_layouts[0]
|
tag_layouts.extend(tags.iter().map(|r| r.1));
|
||||||
} else {
|
|
||||||
Layout::struct_no_name_order(field_layouts.into_bump_slice())
|
|
||||||
};
|
|
||||||
|
|
||||||
answer1
|
Layout::Union(UnionLayout::NonRecursive(tag_layouts.into_bump_slice()))
|
||||||
}
|
}
|
||||||
Wrapped(variant) => {
|
|
||||||
use WrappedVariant::*;
|
|
||||||
|
|
||||||
match variant {
|
Recursive {
|
||||||
NonRecursive {
|
sorted_tag_layouts: tags,
|
||||||
sorted_tag_layouts: tags,
|
} => {
|
||||||
} => {
|
let mut tag_layouts = Vec::with_capacity_in(tags.len(), env.arena);
|
||||||
let mut tag_layouts = Vec::with_capacity_in(tags.len(), arena);
|
tag_layouts.extend(tags.iter().map(|r| r.1));
|
||||||
tag_layouts.extend(tags.iter().map(|r| r.1));
|
|
||||||
|
|
||||||
Layout::Union(UnionLayout::NonRecursive(tag_layouts.into_bump_slice()))
|
debug_assert!(tag_layouts.len() > 1);
|
||||||
}
|
Layout::Union(UnionLayout::Recursive(tag_layouts.into_bump_slice()))
|
||||||
|
|
||||||
Recursive {
|
|
||||||
sorted_tag_layouts: tags,
|
|
||||||
} => {
|
|
||||||
let mut tag_layouts = Vec::with_capacity_in(tags.len(), arena);
|
|
||||||
tag_layouts.extend(tags.iter().map(|r| r.1));
|
|
||||||
|
|
||||||
debug_assert!(tag_layouts.len() > 1);
|
|
||||||
Layout::Union(UnionLayout::Recursive(tag_layouts.into_bump_slice()))
|
|
||||||
}
|
|
||||||
|
|
||||||
NullableWrapped {
|
|
||||||
nullable_id,
|
|
||||||
nullable_name: _,
|
|
||||||
sorted_tag_layouts: tags,
|
|
||||||
} => {
|
|
||||||
let mut tag_layouts = Vec::with_capacity_in(tags.len(), arena);
|
|
||||||
tag_layouts.extend(tags.iter().map(|r| r.1));
|
|
||||||
|
|
||||||
Layout::Union(UnionLayout::NullableWrapped {
|
|
||||||
nullable_id,
|
|
||||||
other_tags: tag_layouts.into_bump_slice(),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
NullableUnwrapped { .. } => todo!(),
|
|
||||||
NonNullableUnwrapped { .. } => todo!(),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
NullableWrapped {
|
||||||
|
nullable_id,
|
||||||
|
nullable_name: _,
|
||||||
|
sorted_tag_layouts: tags,
|
||||||
|
} => {
|
||||||
|
let mut tag_layouts = Vec::with_capacity_in(tags.len(), env.arena);
|
||||||
|
tag_layouts.extend(tags.iter().map(|r| r.1));
|
||||||
|
|
||||||
|
Layout::Union(UnionLayout::NullableWrapped {
|
||||||
|
nullable_id,
|
||||||
|
other_tags: tag_layouts.into_bump_slice(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
NullableUnwrapped { .. } => todo!(),
|
||||||
|
NonNullableUnwrapped { .. } => todo!(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2707,88 +2712,6 @@ fn layout_from_num_content<'a>(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn unwrap_num_tag<'a>(
|
|
||||||
subs: &Subs,
|
|
||||||
var: Variable,
|
|
||||||
target_info: TargetInfo,
|
|
||||||
) -> Result<Layout<'a>, LayoutProblem> {
|
|
||||||
match subs.get_content_without_compacting(var) {
|
|
||||||
Content::Alias(Symbol::NUM_INTEGER, args, _, _) => {
|
|
||||||
debug_assert!(args.len() == 1);
|
|
||||||
|
|
||||||
let precision_var = subs[args.all_variables().into_iter().next().unwrap()];
|
|
||||||
|
|
||||||
let precision = subs.get_content_without_compacting(precision_var);
|
|
||||||
|
|
||||||
match precision {
|
|
||||||
Content::Alias(symbol, args, _, _) => {
|
|
||||||
debug_assert!(args.is_empty());
|
|
||||||
|
|
||||||
let layout = match *symbol {
|
|
||||||
Symbol::NUM_SIGNED128 => Layout::i128(),
|
|
||||||
Symbol::NUM_SIGNED64 => Layout::i64(),
|
|
||||||
Symbol::NUM_SIGNED32 => Layout::i32(),
|
|
||||||
Symbol::NUM_SIGNED16 => Layout::i16(),
|
|
||||||
Symbol::NUM_SIGNED8 => Layout::i8(),
|
|
||||||
Symbol::NUM_UNSIGNED128 => Layout::u128(),
|
|
||||||
Symbol::NUM_UNSIGNED64 => Layout::u64(),
|
|
||||||
Symbol::NUM_UNSIGNED32 => Layout::u32(),
|
|
||||||
Symbol::NUM_UNSIGNED16 => Layout::u16(),
|
|
||||||
Symbol::NUM_UNSIGNED8 => Layout::u8(),
|
|
||||||
Symbol::NUM_NATURAL => Layout::usize(target_info),
|
|
||||||
|
|
||||||
_ => unreachable!("not a valid int variant: {:?} {:?}", symbol, args),
|
|
||||||
};
|
|
||||||
|
|
||||||
Ok(layout)
|
|
||||||
}
|
|
||||||
Content::FlexVar(_) | Content::RigidVar(_) => {
|
|
||||||
// default to i64
|
|
||||||
Ok(Layout::i64())
|
|
||||||
}
|
|
||||||
_ => unreachable!("not a valid int variant: {:?}", precision),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Content::Alias(Symbol::NUM_FLOATINGPOINT, args, _, _) => {
|
|
||||||
debug_assert!(args.len() == 1);
|
|
||||||
|
|
||||||
let precision_var = subs[args.all_variables().into_iter().next().unwrap()];
|
|
||||||
|
|
||||||
let precision = subs.get_content_without_compacting(precision_var);
|
|
||||||
|
|
||||||
match precision {
|
|
||||||
Content::Alias(Symbol::NUM_BINARY32, args, _, _) => {
|
|
||||||
debug_assert!(args.is_empty());
|
|
||||||
|
|
||||||
Ok(Layout::f32())
|
|
||||||
}
|
|
||||||
Content::Alias(Symbol::NUM_BINARY64, args, _, _) => {
|
|
||||||
debug_assert!(args.is_empty());
|
|
||||||
|
|
||||||
Ok(Layout::f64())
|
|
||||||
}
|
|
||||||
Content::Alias(Symbol::NUM_DECIMAL, args, _, _) => {
|
|
||||||
debug_assert!(args.is_empty());
|
|
||||||
|
|
||||||
Ok(Layout::Builtin(Builtin::Decimal))
|
|
||||||
}
|
|
||||||
Content::FlexVar(_) | Content::RigidVar(_) => {
|
|
||||||
// default to f64
|
|
||||||
Ok(Layout::f64())
|
|
||||||
}
|
|
||||||
_ => unreachable!("not a valid float variant: {:?}", precision),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Content::FlexVar(_) | Content::RigidVar(_) => {
|
|
||||||
// If this was still a (Num *) then default to compiling it to i64
|
|
||||||
Ok(Layout::default_integer())
|
|
||||||
}
|
|
||||||
other => {
|
|
||||||
todo!("TODO non structure Num.@Num flat_type {:?}", other);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn dict_layout_from_key_value<'a>(
|
fn dict_layout_from_key_value<'a>(
|
||||||
env: &mut Env<'a, '_>,
|
env: &mut Env<'a, '_>,
|
||||||
key_var: Variable,
|
key_var: Variable,
|
||||||
|
|
|
@ -344,7 +344,6 @@ impl LambdaSet {
|
||||||
layouts.symbols.push(*symbol);
|
layouts.symbols.push(*symbol);
|
||||||
}
|
}
|
||||||
TagName::Global(_) => unreachable!("lambda set tags must be closure tags"),
|
TagName::Global(_) => unreachable!("lambda set tags must be closure tags"),
|
||||||
TagName::Private(_) => unreachable!("lambda set tags must be closure tags"),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -678,11 +677,9 @@ impl Layout {
|
||||||
}
|
}
|
||||||
|
|
||||||
match symbol {
|
match symbol {
|
||||||
Symbol::NUM_DECIMAL | Symbol::NUM_AT_DECIMAL => Ok(Layout::Decimal),
|
Symbol::NUM_DECIMAL => Ok(Layout::Decimal),
|
||||||
|
|
||||||
Symbol::NUM_NAT | Symbol::NUM_NATURAL | Symbol::NUM_AT_NATURAL => {
|
Symbol::NUM_NAT | Symbol::NUM_NATURAL => Ok(layouts.usize()),
|
||||||
Ok(layouts.usize())
|
|
||||||
}
|
|
||||||
|
|
||||||
_ => {
|
_ => {
|
||||||
// at this point we throw away alias information
|
// at this point we throw away alias information
|
||||||
|
|
|
@ -30,7 +30,6 @@
|
||||||
">="
|
">="
|
||||||
">"
|
">"
|
||||||
"^"
|
"^"
|
||||||
"%%"
|
|
||||||
"%"
|
"%"
|
||||||
|
|
||||||
"->"
|
"->"
|
|
@ -189,10 +189,8 @@ pub enum Expr<'a> {
|
||||||
|
|
||||||
// Tags
|
// Tags
|
||||||
GlobalTag(&'a str),
|
GlobalTag(&'a str),
|
||||||
PrivateTag(&'a str),
|
|
||||||
|
|
||||||
// Reference to an opaque type, e.g. $Opaq
|
// Reference to an opaque type, e.g. @Opaq
|
||||||
// TODO(opaques): $->@ in the above comment
|
|
||||||
OpaqueRef(&'a str),
|
OpaqueRef(&'a str),
|
||||||
|
|
||||||
// Pattern Matching
|
// Pattern Matching
|
||||||
|
@ -446,11 +444,6 @@ pub enum Tag<'a> {
|
||||||
args: &'a [Loc<TypeAnnotation<'a>>],
|
args: &'a [Loc<TypeAnnotation<'a>>],
|
||||||
},
|
},
|
||||||
|
|
||||||
Private {
|
|
||||||
name: Loc<&'a str>,
|
|
||||||
args: &'a [Loc<TypeAnnotation<'a>>],
|
|
||||||
},
|
|
||||||
|
|
||||||
// We preserve this for the formatter; canonicalization ignores it.
|
// We preserve this for the formatter; canonicalization ignores it.
|
||||||
SpaceBefore(&'a Tag<'a>, &'a [CommentOrNewline<'a>]),
|
SpaceBefore(&'a Tag<'a>, &'a [CommentOrNewline<'a>]),
|
||||||
SpaceAfter(&'a Tag<'a>, &'a [CommentOrNewline<'a>]),
|
SpaceAfter(&'a Tag<'a>, &'a [CommentOrNewline<'a>]),
|
||||||
|
@ -523,7 +516,6 @@ pub enum Pattern<'a> {
|
||||||
Identifier(&'a str),
|
Identifier(&'a str),
|
||||||
|
|
||||||
GlobalTag(&'a str),
|
GlobalTag(&'a str),
|
||||||
PrivateTag(&'a str),
|
|
||||||
|
|
||||||
OpaqueRef(&'a str),
|
OpaqueRef(&'a str),
|
||||||
|
|
||||||
|
@ -579,7 +571,6 @@ impl<'a> Pattern<'a> {
|
||||||
pub fn from_ident(arena: &'a Bump, ident: Ident<'a>) -> Pattern<'a> {
|
pub fn from_ident(arena: &'a Bump, ident: Ident<'a>) -> Pattern<'a> {
|
||||||
match ident {
|
match ident {
|
||||||
Ident::GlobalTag(string) => Pattern::GlobalTag(string),
|
Ident::GlobalTag(string) => Pattern::GlobalTag(string),
|
||||||
Ident::PrivateTag(string) => Pattern::PrivateTag(string),
|
|
||||||
Ident::OpaqueRef(string) => Pattern::OpaqueRef(string),
|
Ident::OpaqueRef(string) => Pattern::OpaqueRef(string),
|
||||||
Ident::Access { module_name, parts } => {
|
Ident::Access { module_name, parts } => {
|
||||||
if parts.len() == 1 {
|
if parts.len() == 1 {
|
||||||
|
@ -629,7 +620,6 @@ impl<'a> Pattern<'a> {
|
||||||
match (self, other) {
|
match (self, other) {
|
||||||
(Identifier(x), Identifier(y)) => x == y,
|
(Identifier(x), Identifier(y)) => x == y,
|
||||||
(GlobalTag(x), GlobalTag(y)) => x == y,
|
(GlobalTag(x), GlobalTag(y)) => x == y,
|
||||||
(PrivateTag(x), PrivateTag(y)) => x == y,
|
|
||||||
(Apply(constructor_x, args_x), Apply(constructor_y, args_y)) => {
|
(Apply(constructor_x, args_x), Apply(constructor_y, args_y)) => {
|
||||||
let equivalent_args = args_x
|
let equivalent_args = args_x
|
||||||
.iter()
|
.iter()
|
||||||
|
@ -927,7 +917,7 @@ impl<'a> Expr<'a> {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn is_tag(&self) -> bool {
|
pub fn is_tag(&self) -> bool {
|
||||||
matches!(self, Expr::GlobalTag(_) | Expr::PrivateTag(_))
|
matches!(self, Expr::GlobalTag(_))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -196,6 +196,7 @@ fn parse_loc_term_or_underscore<'a>(
|
||||||
) -> ParseResult<'a, Loc<Expr<'a>>, EExpr<'a>> {
|
) -> ParseResult<'a, Loc<Expr<'a>>, EExpr<'a>> {
|
||||||
one_of!(
|
one_of!(
|
||||||
loc_expr_in_parens_etc_help(min_indent),
|
loc_expr_in_parens_etc_help(min_indent),
|
||||||
|
loc!(specialize(EExpr::If, if_expr_help(min_indent, options))),
|
||||||
loc!(specialize(EExpr::Str, string_literal_help())),
|
loc!(specialize(EExpr::Str, string_literal_help())),
|
||||||
loc!(specialize(EExpr::SingleQuote, single_quote_literal_help())),
|
loc!(specialize(EExpr::SingleQuote, single_quote_literal_help())),
|
||||||
loc!(specialize(EExpr::Number, positive_number_literal_help())),
|
loc!(specialize(EExpr::Number, positive_number_literal_help())),
|
||||||
|
@ -1509,8 +1510,8 @@ fn parse_expr_operator<'a>(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Err((NoProgress, _, _)) => {
|
Err((NoProgress, expr, e)) => {
|
||||||
todo!()
|
todo!("{:?} {:?}", expr, e)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
@ -1763,7 +1764,6 @@ fn expr_to_pattern_help<'a>(arena: &'a Bump, expr: &Expr<'a>) -> Result<Pattern<
|
||||||
}
|
}
|
||||||
Expr::Underscore(opt_name) => Ok(Pattern::Underscore(opt_name)),
|
Expr::Underscore(opt_name) => Ok(Pattern::Underscore(opt_name)),
|
||||||
Expr::GlobalTag(value) => Ok(Pattern::GlobalTag(value)),
|
Expr::GlobalTag(value) => Ok(Pattern::GlobalTag(value)),
|
||||||
Expr::PrivateTag(value) => Ok(Pattern::PrivateTag(value)),
|
|
||||||
Expr::OpaqueRef(value) => Ok(Pattern::OpaqueRef(value)),
|
Expr::OpaqueRef(value) => Ok(Pattern::OpaqueRef(value)),
|
||||||
Expr::Apply(loc_val, loc_args, _) => {
|
Expr::Apply(loc_val, loc_args, _) => {
|
||||||
let region = loc_val.region;
|
let region = loc_val.region;
|
||||||
|
@ -2437,7 +2437,6 @@ where
|
||||||
fn ident_to_expr<'a>(arena: &'a Bump, src: Ident<'a>) -> Expr<'a> {
|
fn ident_to_expr<'a>(arena: &'a Bump, src: Ident<'a>) -> Expr<'a> {
|
||||||
match src {
|
match src {
|
||||||
Ident::GlobalTag(string) => Expr::GlobalTag(string),
|
Ident::GlobalTag(string) => Expr::GlobalTag(string),
|
||||||
Ident::PrivateTag(string) => Expr::PrivateTag(string),
|
|
||||||
Ident::OpaqueRef(string) => Expr::OpaqueRef(string),
|
Ident::OpaqueRef(string) => Expr::OpaqueRef(string),
|
||||||
Ident::Access { module_name, parts } => {
|
Ident::Access { module_name, parts } => {
|
||||||
let mut iter = parts.iter();
|
let mut iter = parts.iter();
|
||||||
|
@ -2762,7 +2761,6 @@ where
|
||||||
"&&" => good!(BinOp::And, 2),
|
"&&" => good!(BinOp::And, 2),
|
||||||
"||" => good!(BinOp::Or, 2),
|
"||" => good!(BinOp::Or, 2),
|
||||||
"//" => good!(BinOp::DoubleSlash, 2),
|
"//" => good!(BinOp::DoubleSlash, 2),
|
||||||
"%%" => good!(BinOp::DoublePercent, 2),
|
|
||||||
"->" => {
|
"->" => {
|
||||||
// makes no progress, so it does not interfere with `_ if isGood -> ...`
|
// makes no progress, so it does not interfere with `_ if isGood -> ...`
|
||||||
Err((NoProgress, to_error("->", state.pos()), state))
|
Err((NoProgress, to_error("->", state.pos()), state))
|
||||||
|
|
|
@ -37,9 +37,6 @@ pub enum Ident<'a> {
|
||||||
/// Foo or Bar
|
/// Foo or Bar
|
||||||
GlobalTag(&'a str),
|
GlobalTag(&'a str),
|
||||||
/// @Foo or @Bar
|
/// @Foo or @Bar
|
||||||
PrivateTag(&'a str),
|
|
||||||
/// $Foo or $Bar
|
|
||||||
// TODO(opaques): $->@ in the above comment
|
|
||||||
OpaqueRef(&'a str),
|
OpaqueRef(&'a str),
|
||||||
/// foo or foo.bar or Foo.Bar.baz.qux
|
/// foo or foo.bar or Foo.Bar.baz.qux
|
||||||
Access {
|
Access {
|
||||||
|
@ -57,7 +54,7 @@ impl<'a> Ident<'a> {
|
||||||
use self::Ident::*;
|
use self::Ident::*;
|
||||||
|
|
||||||
match self {
|
match self {
|
||||||
GlobalTag(string) | PrivateTag(string) | OpaqueRef(string) => string.len(),
|
GlobalTag(string) | OpaqueRef(string) => string.len(),
|
||||||
Access { module_name, parts } => {
|
Access { module_name, parts } => {
|
||||||
let mut len = if module_name.is_empty() {
|
let mut len = if module_name.is_empty() {
|
||||||
0
|
0
|
||||||
|
@ -101,24 +98,7 @@ pub fn lowercase_ident<'a>() -> impl Parser<'a, &'a str, ()> {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn tag_name<'a>() -> impl Parser<'a, &'a str, ()> {
|
pub fn tag_name<'a>() -> impl Parser<'a, &'a str, ()> {
|
||||||
move |arena, state: State<'a>| {
|
move |arena, state: State<'a>| uppercase_ident().parse(arena, state)
|
||||||
if state.bytes().starts_with(b"@") {
|
|
||||||
match chomp_private_tag_or_opaque(
|
|
||||||
/* private tag */ true,
|
|
||||||
state.bytes(),
|
|
||||||
state.pos(),
|
|
||||||
) {
|
|
||||||
Err(BadIdent::Start(_)) => Err((NoProgress, (), state)),
|
|
||||||
Err(_) => Err((MadeProgress, (), state)),
|
|
||||||
Ok(ident) => {
|
|
||||||
let width = ident.len();
|
|
||||||
Ok((MadeProgress, ident, state.advance(width)))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
uppercase_ident().parse(arena, state)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// This could be:
|
/// This could be:
|
||||||
|
@ -242,7 +222,6 @@ pub enum BadIdent {
|
||||||
WeirdDotAccess(Position),
|
WeirdDotAccess(Position),
|
||||||
WeirdDotQualified(Position),
|
WeirdDotQualified(Position),
|
||||||
StrayDot(Position),
|
StrayDot(Position),
|
||||||
BadPrivateTag(Position),
|
|
||||||
BadOpaqueRef(Position),
|
BadOpaqueRef(Position),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -311,21 +290,13 @@ fn chomp_accessor(buffer: &[u8], pos: Position) -> Result<&str, BadIdent> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// a `@Token` private tag
|
/// a `@Token` opaque
|
||||||
fn chomp_private_tag_or_opaque(
|
fn chomp_opaque_ref(buffer: &[u8], pos: Position) -> Result<&str, BadIdent> {
|
||||||
private_tag: bool, // If false, opaque
|
|
||||||
buffer: &[u8],
|
|
||||||
pos: Position,
|
|
||||||
) -> Result<&str, BadIdent> {
|
|
||||||
// assumes the leading `@` has NOT been chomped already
|
// assumes the leading `@` has NOT been chomped already
|
||||||
debug_assert_eq!(buffer.get(0), Some(if private_tag { &b'@' } else { &b'$' }));
|
debug_assert_eq!(buffer.get(0), Some(&b'@'));
|
||||||
use encode_unicode::CharExt;
|
use encode_unicode::CharExt;
|
||||||
|
|
||||||
let bad_ident = if private_tag {
|
let bad_ident = BadIdent::BadOpaqueRef;
|
||||||
BadIdent::BadPrivateTag
|
|
||||||
} else {
|
|
||||||
BadIdent::BadOpaqueRef
|
|
||||||
};
|
|
||||||
|
|
||||||
match chomp_uppercase_part(&buffer[1..]) {
|
match chomp_uppercase_part(&buffer[1..]) {
|
||||||
Ok(name) => {
|
Ok(name) => {
|
||||||
|
@ -362,15 +333,11 @@ fn chomp_identifier_chain<'a>(
|
||||||
}
|
}
|
||||||
Err(fail) => return Err((1, fail)),
|
Err(fail) => return Err((1, fail)),
|
||||||
},
|
},
|
||||||
c @ ('@' | '$') => match chomp_private_tag_or_opaque(c == '@', buffer, pos) {
|
'@' => match chomp_opaque_ref(buffer, pos) {
|
||||||
Ok(tagname) => {
|
Ok(tagname) => {
|
||||||
let bytes_parsed = tagname.len();
|
let bytes_parsed = tagname.len();
|
||||||
|
|
||||||
let ident = if c == '@' {
|
let ident = Ident::OpaqueRef;
|
||||||
Ident::PrivateTag
|
|
||||||
} else {
|
|
||||||
Ident::OpaqueRef
|
|
||||||
};
|
|
||||||
|
|
||||||
return Ok((bytes_parsed as u32, ident(tagname)));
|
return Ok((bytes_parsed as u32, ident(tagname)));
|
||||||
}
|
}
|
||||||
|
|
|
@ -240,14 +240,10 @@ fn loc_ident_pattern_help<'a>(
|
||||||
Ok((MadeProgress, loc_tag, state))
|
Ok((MadeProgress, loc_tag, state))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Ident::PrivateTag(name) | Ident::OpaqueRef(name) => {
|
Ident::OpaqueRef(name) => {
|
||||||
let loc_pat = Loc {
|
let loc_pat = Loc {
|
||||||
region: loc_ident.region,
|
region: loc_ident.region,
|
||||||
value: if matches!(loc_ident.value, Ident::PrivateTag(..)) {
|
value: Pattern::OpaqueRef(name),
|
||||||
Pattern::PrivateTag(name)
|
|
||||||
} else {
|
|
||||||
Pattern::OpaqueRef(name)
|
|
||||||
},
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// Make sure `@Foo Bar 1` is parsed as `@Foo (Bar) 1`, and not `@Foo (Bar 1)`
|
// Make sure `@Foo Bar 1` is parsed as `@Foo (Bar) 1`, and not `@Foo (Bar 1)`
|
||||||
|
|
|
@ -214,16 +214,9 @@ fn tag_type<'a>(min_indent: u32) -> impl Parser<'a, Tag<'a>, ETypeTagUnion<'a>>
|
||||||
let (_, args, state) = specialize_ref(ETypeTagUnion::Type, loc_applied_args_e(min_indent))
|
let (_, args, state) = specialize_ref(ETypeTagUnion::Type, loc_applied_args_e(min_indent))
|
||||||
.parse(arena, state)?;
|
.parse(arena, state)?;
|
||||||
|
|
||||||
let result = if name.value.starts_with('@') {
|
let result = Tag::Global {
|
||||||
Tag::Private {
|
name,
|
||||||
name,
|
args: args.into_bump_slice(),
|
||||||
args: args.into_bump_slice(),
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
Tag::Global {
|
|
||||||
name,
|
|
||||||
args: args.into_bump_slice(),
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
Ok((MadeProgress, result, state))
|
Ok((MadeProgress, result, state))
|
||||||
|
|
|
@ -1,14 +0,0 @@
|
||||||
Apply(
|
|
||||||
@0-5 PrivateTag(
|
|
||||||
"@Whee",
|
|
||||||
),
|
|
||||||
[
|
|
||||||
@6-8 Num(
|
|
||||||
"12",
|
|
||||||
),
|
|
||||||
@9-11 Num(
|
|
||||||
"34",
|
|
||||||
),
|
|
||||||
],
|
|
||||||
Space,
|
|
||||||
)
|
|
|
@ -1 +0,0 @@
|
||||||
@Whee 12 34
|
|
|
@ -1,3 +0,0 @@
|
||||||
PrivateTag(
|
|
||||||
"@Whee",
|
|
||||||
)
|
|
|
@ -1 +0,0 @@
|
||||||
@Whee
|
|
|
@ -1,3 +1,3 @@
|
||||||
OpaqueRef(
|
OpaqueRef(
|
||||||
"$Age",
|
"@Age",
|
||||||
)
|
)
|
||||||
|
|
|
@ -1 +1 @@
|
||||||
$Age
|
@Age
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
Apply(
|
Apply(
|
||||||
@0-4 OpaqueRef(
|
@0-4 OpaqueRef(
|
||||||
"$Age",
|
"@Age",
|
||||||
),
|
),
|
||||||
[
|
[
|
||||||
@5-6 Var {
|
@5-6 Var {
|
||||||
|
|
|
@ -1 +1 @@
|
||||||
$Age m n
|
@Age m n
|
||||||
|
|
|
@ -8,7 +8,7 @@ When(
|
||||||
patterns: [
|
patterns: [
|
||||||
@12-16 SpaceBefore(
|
@12-16 SpaceBefore(
|
||||||
OpaqueRef(
|
OpaqueRef(
|
||||||
"$Age",
|
"@Age",
|
||||||
),
|
),
|
||||||
[
|
[
|
||||||
Newline,
|
Newline,
|
||||||
|
|
|
@ -1,2 +1,2 @@
|
||||||
when n is
|
when n is
|
||||||
$Age -> 1
|
@Age -> 1
|
||||||
|
|
|
@ -9,7 +9,7 @@ When(
|
||||||
@12-20 SpaceBefore(
|
@12-20 SpaceBefore(
|
||||||
Apply(
|
Apply(
|
||||||
@12-16 OpaqueRef(
|
@12-16 OpaqueRef(
|
||||||
"$Add",
|
"@Add",
|
||||||
),
|
),
|
||||||
[
|
[
|
||||||
@17-18 Identifier(
|
@17-18 Identifier(
|
||||||
|
|
|
@ -1,2 +1,2 @@
|
||||||
when n is
|
when n is
|
||||||
$Add n m -> n + m
|
@Add n m -> n + m
|
||||||
|
|
25
compiler/parse/tests/snapshots/pass/plus_if.expr.result-ast
Normal file
25
compiler/parse/tests/snapshots/pass/plus_if.expr.result-ast
Normal file
|
@ -0,0 +1,25 @@
|
||||||
|
BinOps(
|
||||||
|
[
|
||||||
|
(
|
||||||
|
@0-1 Num(
|
||||||
|
"1",
|
||||||
|
),
|
||||||
|
@2-3 Star,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
@4-25 If(
|
||||||
|
[
|
||||||
|
(
|
||||||
|
@7-11 GlobalTag(
|
||||||
|
"True",
|
||||||
|
),
|
||||||
|
@17-18 Num(
|
||||||
|
"1",
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
@24-25 Num(
|
||||||
|
"1",
|
||||||
|
),
|
||||||
|
),
|
||||||
|
)
|
1
compiler/parse/tests/snapshots/pass/plus_if.expr.roc
Normal file
1
compiler/parse/tests/snapshots/pass/plus_if.expr.roc
Normal file
|
@ -0,0 +1 @@
|
||||||
|
1 * if True then 1 else 1
|
|
@ -1,6 +0,0 @@
|
||||||
MalformedIdent(
|
|
||||||
"@One.Two.Whee",
|
|
||||||
BadPrivateTag(
|
|
||||||
@4,
|
|
||||||
),
|
|
||||||
)
|
|
|
@ -1 +0,0 @@
|
||||||
@One.Two.Whee
|
|
|
@ -122,6 +122,7 @@ mod test_parse {
|
||||||
snapshot_tests! {
|
snapshot_tests! {
|
||||||
fail/type_argument_no_arrow.expr,
|
fail/type_argument_no_arrow.expr,
|
||||||
fail/type_double_comma.expr,
|
fail/type_double_comma.expr,
|
||||||
|
pass/plus_if.expr,
|
||||||
pass/list_closing_indent_not_enough.expr,
|
pass/list_closing_indent_not_enough.expr,
|
||||||
pass/ability_single_line.expr,
|
pass/ability_single_line.expr,
|
||||||
pass/ability_multi_line.expr,
|
pass/ability_multi_line.expr,
|
||||||
|
@ -133,7 +134,6 @@ mod test_parse {
|
||||||
pass/annotated_tag_destructure.expr,
|
pass/annotated_tag_destructure.expr,
|
||||||
pass/apply_global_tag.expr,
|
pass/apply_global_tag.expr,
|
||||||
pass/apply_parenthetical_global_tag_args.expr,
|
pass/apply_parenthetical_global_tag_args.expr,
|
||||||
pass/apply_private_tag.expr,
|
|
||||||
pass/apply_three_args.expr,
|
pass/apply_three_args.expr,
|
||||||
pass/apply_two_args.expr,
|
pass/apply_two_args.expr,
|
||||||
pass/apply_unary_negation.expr,
|
pass/apply_unary_negation.expr,
|
||||||
|
@ -142,7 +142,6 @@ mod test_parse {
|
||||||
pass/basic_docs.expr,
|
pass/basic_docs.expr,
|
||||||
pass/basic_field.expr,
|
pass/basic_field.expr,
|
||||||
pass/basic_global_tag.expr,
|
pass/basic_global_tag.expr,
|
||||||
pass/basic_private_tag.expr,
|
|
||||||
pass/basic_var.expr,
|
pass/basic_var.expr,
|
||||||
pass/closure_with_underscores.expr,
|
pass/closure_with_underscores.expr,
|
||||||
pass/comment_after_def.module,
|
pass/comment_after_def.module,
|
||||||
|
@ -232,7 +231,6 @@ mod test_parse {
|
||||||
pass/pos_inf_float.expr,
|
pass/pos_inf_float.expr,
|
||||||
pass/positive_float.expr,
|
pass/positive_float.expr,
|
||||||
pass/positive_int.expr,
|
pass/positive_int.expr,
|
||||||
pass/private_qualified_tag.expr,
|
|
||||||
pass/provides_type.header,
|
pass/provides_type.header,
|
||||||
pass/qualified_field.expr,
|
pass/qualified_field.expr,
|
||||||
pass/qualified_global_tag.expr,
|
pass/qualified_global_tag.expr,
|
||||||
|
@ -316,7 +314,17 @@ mod test_parse {
|
||||||
if std::env::var("ROC_PARSER_SNAPSHOT_TEST_OVERWRITE").is_ok() {
|
if std::env::var("ROC_PARSER_SNAPSHOT_TEST_OVERWRITE").is_ok() {
|
||||||
std::fs::write(&result_path, actual_result).unwrap();
|
std::fs::write(&result_path, actual_result).unwrap();
|
||||||
} else {
|
} else {
|
||||||
let expected_result = std::fs::read_to_string(&result_path).unwrap();
|
let expected_result = std::fs::read_to_string(&result_path).unwrap_or_else(|e| {
|
||||||
|
panic!(
|
||||||
|
"Error opening test output file {}:\n\
|
||||||
|
{:?}
|
||||||
|
Supposing the file is missing, consider running the tests with:\n\
|
||||||
|
`env ROC_PARSER_SNAPSHOT_TEST_OVERWRITE=1 cargo test ...`\n\
|
||||||
|
and committing the file that creates.",
|
||||||
|
result_path.display(),
|
||||||
|
e
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
assert_multiline_str_eq!(expected_result, actual_result);
|
assert_multiline_str_eq!(expected_result, actual_result);
|
||||||
}
|
}
|
||||||
|
@ -645,114 +653,6 @@ mod test_parse {
|
||||||
// );
|
// );
|
||||||
// }
|
// }
|
||||||
|
|
||||||
// #[test]
|
|
||||||
// fn ann_private_open_union() {
|
|
||||||
// let arena = Bump::new();
|
|
||||||
// let newline = bumpalo::vec![in &arena; Newline];
|
|
||||||
// let newlines = bumpalo::vec![in &arena; Newline, Newline];
|
|
||||||
// let tag1 = Tag::Private {
|
|
||||||
// name: Located::new(0, 0, 8, 13, "@True"),
|
|
||||||
// args: &[],
|
|
||||||
// };
|
|
||||||
// let tag2arg1 = Located::new(0, 0, 24, 27, TypeAnnotation::Apply("", "Two", &[]));
|
|
||||||
// let tag2arg2 = Located::new(0, 0, 28, 34, TypeAnnotation::Apply("", "Things", &[]));
|
|
||||||
// let tag2args = bumpalo::vec![in &arena; tag2arg1, tag2arg2];
|
|
||||||
// let tag2 = Tag::Private {
|
|
||||||
// name: Located::new(0, 0, 15, 23, "@Perhaps"),
|
|
||||||
// args: tag2args.into_bump_slice(),
|
|
||||||
// };
|
|
||||||
// let tags = bumpalo::vec![in &arena;
|
|
||||||
// Located::new(0, 0, 8, 13, tag1),
|
|
||||||
// Located::new(0, 0, 15, 34, tag2)
|
|
||||||
// ];
|
|
||||||
// let loc_wildcard = Located::new(0, 0, 36, 37, TypeAnnotation::Wildcard);
|
|
||||||
// let applied_ann = TypeAnnotation::TagUnion {
|
|
||||||
// tags: tags.into_bump_slice(),
|
|
||||||
// ext: Some(arena.alloc(loc_wildcard)),
|
|
||||||
// };
|
|
||||||
// let signature = Def::Annotation(
|
|
||||||
// Located::new(0, 0, 0, 3, Identifier("foo")),
|
|
||||||
// Located::new(0, 0, 6, 37, applied_ann),
|
|
||||||
// );
|
|
||||||
// let def = Def::Body(
|
|
||||||
// arena.alloc(Located::new(1, 1, 0, 3, Identifier("foo"))),
|
|
||||||
// arena.alloc(Located::new(1, 1, 6, 10, Expr::GlobalTag("True"))),
|
|
||||||
// );
|
|
||||||
// let spaced_def = Def::SpaceBefore(arena.alloc(def), newline.into_bump_slice());
|
|
||||||
// let loc_def = &*arena.alloc(Located::new(1, 1, 0, 10, spaced_def));
|
|
||||||
|
|
||||||
// let loc_ann = &*arena.alloc(Located::new(0, 0, 0, 3, signature));
|
|
||||||
// let defs = &[loc_ann, loc_def];
|
|
||||||
// let ret = Expr::SpaceBefore(arena.alloc(Num("42")), newlines.into_bump_slice());
|
|
||||||
// let loc_ret = Located::new(3, 3, 0, 2, ret);
|
|
||||||
// let expected = Defs(defs, arena.alloc(loc_ret));
|
|
||||||
|
|
||||||
// assert_parses_to(
|
|
||||||
// indoc!(
|
|
||||||
// r#"
|
|
||||||
// foo : [ @True, @Perhaps Two Things ]*
|
|
||||||
// foo = True
|
|
||||||
|
|
||||||
// 42
|
|
||||||
// "#
|
|
||||||
// ),
|
|
||||||
// expected,
|
|
||||||
// );
|
|
||||||
// }
|
|
||||||
|
|
||||||
// #[test]
|
|
||||||
// fn ann_private_closed_union() {
|
|
||||||
// let arena = Bump::new();
|
|
||||||
// let newline = bumpalo::vec![in &arena; Newline];
|
|
||||||
// let newlines = bumpalo::vec![in &arena; Newline, Newline];
|
|
||||||
// let tag1 = Tag::Private {
|
|
||||||
// name: Located::new(0, 0, 8, 13, "@True"),
|
|
||||||
// args: &[],
|
|
||||||
// };
|
|
||||||
// let tag2arg = Located::new(0, 0, 24, 29, TypeAnnotation::Apply("", "Thing", &[]));
|
|
||||||
// let tag2args = bumpalo::vec![in &arena; tag2arg];
|
|
||||||
// let tag2 = Tag::Private {
|
|
||||||
// name: Located::new(0, 0, 15, 23, "@Perhaps"),
|
|
||||||
// args: tag2args.into_bump_slice(),
|
|
||||||
// };
|
|
||||||
// let tags = bumpalo::vec![in &arena;
|
|
||||||
// Located::new(0, 0, 8, 13, tag1),
|
|
||||||
// Located::new(0, 0, 15, 29, tag2)
|
|
||||||
// ];
|
|
||||||
// let applied_ann = TypeAnnotation::TagUnion {
|
|
||||||
// tags: tags.into_bump_slice(),
|
|
||||||
// ext: None,
|
|
||||||
// };
|
|
||||||
// let signature = Def::Annotation(
|
|
||||||
// Located::new(0, 0, 0, 3, Identifier("foo")),
|
|
||||||
// Located::new(0, 0, 6, 31, applied_ann),
|
|
||||||
// );
|
|
||||||
// let def = Def::Body(
|
|
||||||
// arena.alloc(Located::new(1, 1, 0, 3, Identifier("foo"))),
|
|
||||||
// arena.alloc(Located::new(1, 1, 6, 10, Expr::GlobalTag("True"))),
|
|
||||||
// );
|
|
||||||
// let spaced_def = Def::SpaceBefore(arena.alloc(def), newline.into_bump_slice());
|
|
||||||
// let loc_def = &*arena.alloc(Located::new(1, 1, 0, 10, spaced_def));
|
|
||||||
|
|
||||||
// let loc_ann = &*arena.alloc(Located::new(0, 0, 0, 3, signature));
|
|
||||||
// let defs = &[loc_ann, loc_def];
|
|
||||||
// let ret = Expr::SpaceBefore(arena.alloc(Num("42")), newlines.into_bump_slice());
|
|
||||||
// let loc_ret = Located::new(3, 3, 0, 2, ret);
|
|
||||||
// let expected = Defs(defs, arena.alloc(loc_ret));
|
|
||||||
|
|
||||||
// assert_parses_to(
|
|
||||||
// indoc!(
|
|
||||||
// r#"
|
|
||||||
// foo : [ @True, @Perhaps Thing ]
|
|
||||||
// foo = True
|
|
||||||
|
|
||||||
// 42
|
|
||||||
// "#
|
|
||||||
// ),
|
|
||||||
// expected,
|
|
||||||
// );
|
|
||||||
// }
|
|
||||||
|
|
||||||
// #[test]
|
// #[test]
|
||||||
// fn ann_global_open_union() {
|
// fn ann_global_open_union() {
|
||||||
// let arena = Bump::new();
|
// let arena = Bump::new();
|
||||||
|
|
|
@ -377,6 +377,17 @@ impl LineInfo {
|
||||||
end: self.convert_pos(region.end()),
|
end: self.convert_pos(region.end()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn convert_line_column(&self, lc: LineColumn) -> Position {
|
||||||
|
let offset = self.line_offsets[lc.line as usize] + lc.column;
|
||||||
|
Position::new(offset)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn convert_line_column_region(&self, lc_region: LineColumnRegion) -> Region {
|
||||||
|
let start = self.convert_line_column(lc_region.start);
|
||||||
|
let end = self.convert_line_column(lc_region.end);
|
||||||
|
Region::new(start, end)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
|
|
@ -6,4 +6,4 @@ license = "UPL-1.0"
|
||||||
edition = "2018"
|
edition = "2018"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
target-lexicon = "0.12.2"
|
target-lexicon = "0.12.3"
|
||||||
|
|
|
@ -28,3 +28,5 @@ pretty_assertions = "1.0.0"
|
||||||
indoc = "1.0.3"
|
indoc = "1.0.3"
|
||||||
tempfile = "3.2.0"
|
tempfile = "3.2.0"
|
||||||
bumpalo = { version = "3.8.0", features = ["collections"] }
|
bumpalo = { version = "3.8.0", features = ["collections"] }
|
||||||
|
regex = "1.5.5"
|
||||||
|
lazy_static = "1.4.0"
|
||||||
|
|
|
@ -5,6 +5,7 @@ use roc_can::constraint::Constraint::{self, *};
|
||||||
use roc_can::constraint::{Constraints, LetConstraint};
|
use roc_can::constraint::{Constraints, LetConstraint};
|
||||||
use roc_can::expected::{Expected, PExpected};
|
use roc_can::expected::{Expected, PExpected};
|
||||||
use roc_collections::all::MutMap;
|
use roc_collections::all::MutMap;
|
||||||
|
use roc_error_macros::internal_error;
|
||||||
use roc_module::ident::TagName;
|
use roc_module::ident::TagName;
|
||||||
use roc_module::symbol::Symbol;
|
use roc_module::symbol::Symbol;
|
||||||
use roc_region::all::{Loc, Region};
|
use roc_region::all::{Loc, Region};
|
||||||
|
@ -195,20 +196,20 @@ impl Aliases {
|
||||||
register(subs, rank, pools, content)
|
register(subs, rank, pools, content)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Instantiate an alias of the form `Foo a : [ @Foo a ]`
|
/// Build an alias of the form `Num range := range`
|
||||||
fn instantiate_num_at_alias(
|
fn build_num_opaque(
|
||||||
subs: &mut Subs,
|
subs: &mut Subs,
|
||||||
rank: Rank,
|
rank: Rank,
|
||||||
pools: &mut Pools,
|
pools: &mut Pools,
|
||||||
tag_name_slice: SubsSlice<TagName>,
|
symbol: Symbol,
|
||||||
range_slice: SubsSlice<Variable>,
|
range_var: Variable,
|
||||||
) -> Variable {
|
) -> Variable {
|
||||||
let variable_slices = SubsSlice::extend_new(&mut subs.variable_slices, [range_slice]);
|
let content = Content::Alias(
|
||||||
|
symbol,
|
||||||
let union_tags = UnionTags::from_slices(tag_name_slice, variable_slices);
|
AliasVariables::insert_into_subs(subs, [range_var], []),
|
||||||
let ext_var = Variable::EMPTY_TAG_UNION;
|
range_var,
|
||||||
let flat_type = FlatType::TagUnion(union_tags, ext_var);
|
AliasKind::Opaque,
|
||||||
let content = Content::Structure(flat_type);
|
);
|
||||||
|
|
||||||
register(subs, rank, pools, content)
|
register(subs, rank, pools, content)
|
||||||
}
|
}
|
||||||
|
@ -227,126 +228,46 @@ impl Aliases {
|
||||||
|
|
||||||
Some(var)
|
Some(var)
|
||||||
}
|
}
|
||||||
Symbol::NUM_NUM => {
|
Symbol::NUM_NUM | Symbol::NUM_FLOATINGPOINT | Symbol::NUM_INTEGER => {
|
||||||
let var = Self::instantiate_num_at_alias(
|
// These are opaque types Num range := range (respectively for FloatingPoint and
|
||||||
subs,
|
// Integer). They should not have been built as DelayedAliases!
|
||||||
rank,
|
internal_error!("Attempting to build delayed instantiation of opaque num");
|
||||||
pools,
|
|
||||||
Subs::NUM_AT_NUM,
|
|
||||||
SubsSlice::new(alias_variables.variables_start, 1),
|
|
||||||
);
|
|
||||||
|
|
||||||
Some(var)
|
|
||||||
}
|
|
||||||
Symbol::NUM_FLOATINGPOINT => {
|
|
||||||
let var = Self::instantiate_num_at_alias(
|
|
||||||
subs,
|
|
||||||
rank,
|
|
||||||
pools,
|
|
||||||
Subs::NUM_AT_FLOATINGPOINT,
|
|
||||||
SubsSlice::new(alias_variables.variables_start, 1),
|
|
||||||
);
|
|
||||||
|
|
||||||
Some(var)
|
|
||||||
}
|
|
||||||
Symbol::NUM_INTEGER => {
|
|
||||||
let var = Self::instantiate_num_at_alias(
|
|
||||||
subs,
|
|
||||||
rank,
|
|
||||||
pools,
|
|
||||||
Subs::NUM_AT_INTEGER,
|
|
||||||
SubsSlice::new(alias_variables.variables_start, 1),
|
|
||||||
);
|
|
||||||
|
|
||||||
Some(var)
|
|
||||||
}
|
}
|
||||||
Symbol::NUM_INT => {
|
Symbol::NUM_INT => {
|
||||||
// [ @Integer range ]
|
// Int range : Num (Integer range)
|
||||||
let integer_content_var = Self::instantiate_builtin_aliases(
|
//
|
||||||
self,
|
// build `Integer range := range`
|
||||||
|
let integer_content_var = Self::build_num_opaque(
|
||||||
subs,
|
subs,
|
||||||
rank,
|
rank,
|
||||||
pools,
|
pools,
|
||||||
Symbol::NUM_INTEGER,
|
Symbol::NUM_INTEGER,
|
||||||
alias_variables,
|
subs.variables[alias_variables.variables_start as usize],
|
||||||
)
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
// Integer range (alias variable is the same as `Int range`)
|
|
||||||
let integer_alias_variables = alias_variables;
|
|
||||||
let integer_content = Content::Alias(
|
|
||||||
Symbol::NUM_INTEGER,
|
|
||||||
integer_alias_variables,
|
|
||||||
integer_content_var,
|
|
||||||
AliasKind::Structural,
|
|
||||||
);
|
|
||||||
let integer_alias_var = register(subs, rank, pools, integer_content);
|
|
||||||
|
|
||||||
// [ @Num (Integer range) ]
|
|
||||||
let num_alias_variables =
|
|
||||||
AliasVariables::insert_into_subs(subs, [integer_alias_var], []);
|
|
||||||
let num_content_var = Self::instantiate_builtin_aliases(
|
|
||||||
self,
|
|
||||||
subs,
|
|
||||||
rank,
|
|
||||||
pools,
|
|
||||||
Symbol::NUM_NUM,
|
|
||||||
num_alias_variables,
|
|
||||||
)
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
let num_content = Content::Alias(
|
|
||||||
Symbol::NUM_NUM,
|
|
||||||
num_alias_variables,
|
|
||||||
num_content_var,
|
|
||||||
AliasKind::Structural,
|
|
||||||
);
|
);
|
||||||
|
|
||||||
Some(register(subs, rank, pools, num_content))
|
// build `Num (Integer range) := Integer range`
|
||||||
|
let num_content_var =
|
||||||
|
Self::build_num_opaque(subs, rank, pools, Symbol::NUM_NUM, integer_content_var);
|
||||||
|
|
||||||
|
Some(num_content_var)
|
||||||
}
|
}
|
||||||
Symbol::NUM_FLOAT => {
|
Symbol::NUM_FLOAT => {
|
||||||
// [ @FloatingPoint range ]
|
// Float range : Num (FloatingPoint range)
|
||||||
let fpoint_content_var = Self::instantiate_builtin_aliases(
|
//
|
||||||
self,
|
// build `FloatingPoint range := range`
|
||||||
|
let fpoint_content_var = Self::build_num_opaque(
|
||||||
subs,
|
subs,
|
||||||
rank,
|
rank,
|
||||||
pools,
|
pools,
|
||||||
Symbol::NUM_FLOATINGPOINT,
|
Symbol::NUM_FLOATINGPOINT,
|
||||||
alias_variables,
|
subs.variables[alias_variables.variables_start as usize],
|
||||||
)
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
// FloatingPoint range (alias variable is the same as `Float range`)
|
|
||||||
let fpoint_alias_variables = alias_variables;
|
|
||||||
let fpoint_content = Content::Alias(
|
|
||||||
Symbol::NUM_FLOATINGPOINT,
|
|
||||||
fpoint_alias_variables,
|
|
||||||
fpoint_content_var,
|
|
||||||
AliasKind::Structural,
|
|
||||||
);
|
|
||||||
let fpoint_alias_var = register(subs, rank, pools, fpoint_content);
|
|
||||||
|
|
||||||
// [ @Num (FloatingPoint range) ]
|
|
||||||
let num_alias_variables =
|
|
||||||
AliasVariables::insert_into_subs(subs, [fpoint_alias_var], []);
|
|
||||||
let num_content_var = Self::instantiate_builtin_aliases(
|
|
||||||
self,
|
|
||||||
subs,
|
|
||||||
rank,
|
|
||||||
pools,
|
|
||||||
Symbol::NUM_NUM,
|
|
||||||
num_alias_variables,
|
|
||||||
)
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
let num_content = Content::Alias(
|
|
||||||
Symbol::NUM_NUM,
|
|
||||||
num_alias_variables,
|
|
||||||
num_content_var,
|
|
||||||
AliasKind::Structural,
|
|
||||||
);
|
);
|
||||||
|
|
||||||
Some(register(subs, rank, pools, num_content))
|
// build `Num (FloatingPoint range) := FloatingPoint range`
|
||||||
|
let num_content_var =
|
||||||
|
Self::build_num_opaque(subs, rank, pools, Symbol::NUM_NUM, fpoint_content_var);
|
||||||
|
|
||||||
|
Some(num_content_var)
|
||||||
}
|
}
|
||||||
_ => None,
|
_ => None,
|
||||||
}
|
}
|
||||||
|
@ -1183,26 +1104,34 @@ fn solve(
|
||||||
let actual =
|
let actual =
|
||||||
either_type_index_to_var(constraints, subs, rank, pools, aliases, *type_index);
|
either_type_index_to_var(constraints, subs, rank, pools, aliases, *type_index);
|
||||||
|
|
||||||
let mut new_desc = subs.get(actual);
|
let mut stack = vec![actual];
|
||||||
match new_desc.content {
|
while let Some(var) = stack.pop() {
|
||||||
Content::Structure(FlatType::TagUnion(tags, _)) => {
|
use {Content::*, FlatType::*};
|
||||||
let new_ext = subs.fresh_unnamed_flex_var();
|
|
||||||
subs.set_rank(new_ext, new_desc.rank);
|
let mut desc = subs.get(var);
|
||||||
let new_union = Content::Structure(FlatType::TagUnion(tags, new_ext));
|
if let Structure(TagUnion(tags, ext)) = desc.content {
|
||||||
new_desc.content = new_union;
|
if let Structure(EmptyTagUnion) = subs.get_content_without_compacting(ext) {
|
||||||
subs.set(actual, new_desc);
|
let new_ext = subs.fresh_unnamed_flex_var();
|
||||||
state
|
subs.set_rank(new_ext, desc.rank);
|
||||||
}
|
let new_union = Structure(TagUnion(tags, new_ext));
|
||||||
_ => {
|
desc.content = new_union;
|
||||||
// Today, an "open" constraint doesn't affect any types
|
subs.set(var, desc);
|
||||||
// other than tag unions. Recursive tag unions are constructed
|
}
|
||||||
// at a later time (during occurs checks after tag unions are
|
|
||||||
// resolved), so that's not handled here either.
|
// Also open up all nested tag unions.
|
||||||
// NB: Handle record types here if we add presence constraints
|
let all_vars = tags.variables().into_iter();
|
||||||
// to their type inference as well.
|
stack.extend(all_vars.flat_map(|slice| subs[slice]).map(|var| subs[var]));
|
||||||
state
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Today, an "open" constraint doesn't affect any types
|
||||||
|
// other than tag unions. Recursive tag unions are constructed
|
||||||
|
// at a later time (during occurs checks after tag unions are
|
||||||
|
// resolved), so that's not handled here either.
|
||||||
|
// NB: Handle record types here if we add presence constraints
|
||||||
|
// to their type inference as well.
|
||||||
}
|
}
|
||||||
|
|
||||||
|
state
|
||||||
}
|
}
|
||||||
IncludesTag(index) => {
|
IncludesTag(index) => {
|
||||||
let includes_tag = &constraints.includes_tags[index.index()];
|
let includes_tag = &constraints.includes_tags[index.index()];
|
||||||
|
|
|
@ -10,14 +10,54 @@ mod helpers;
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod solve_expr {
|
mod solve_expr {
|
||||||
use crate::helpers::with_larger_debug_stack;
|
use crate::helpers::with_larger_debug_stack;
|
||||||
|
use lazy_static::lazy_static;
|
||||||
|
use regex::Regex;
|
||||||
|
use roc_can::traverse::find_type_at;
|
||||||
use roc_load::LoadedModule;
|
use roc_load::LoadedModule;
|
||||||
|
use roc_module::symbol::{Interns, ModuleId};
|
||||||
|
use roc_problem::can::Problem;
|
||||||
|
use roc_region::all::{LineColumn, LineColumnRegion, LineInfo, Region};
|
||||||
|
use roc_reporting::report::{can_problem, type_problem, RocDocAllocator};
|
||||||
|
use roc_solve::solve::TypeError;
|
||||||
use roc_types::pretty_print::{content_to_string, name_all_type_vars};
|
use roc_types::pretty_print::{content_to_string, name_all_type_vars};
|
||||||
|
use std::path::PathBuf;
|
||||||
|
|
||||||
// HELPERS
|
// HELPERS
|
||||||
|
|
||||||
fn run_load_and_infer(src: &str) -> Result<LoadedModule, std::io::Error> {
|
lazy_static! {
|
||||||
|
static ref RE_TYPE_QUERY: Regex = Regex::new(r#"^\s*#\s*(?P<where>\^+)\s*$"#).unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Copy)]
|
||||||
|
struct TypeQuery(Region);
|
||||||
|
|
||||||
|
fn parse_queries(src: &str) -> Vec<TypeQuery> {
|
||||||
|
let line_info = LineInfo::new(src);
|
||||||
|
let mut queries = vec![];
|
||||||
|
for (i, line) in src.lines().enumerate() {
|
||||||
|
if let Some(capture) = RE_TYPE_QUERY.captures(line) {
|
||||||
|
let wher = capture.name("where").unwrap();
|
||||||
|
let (start, end) = (wher.start() as u32, wher.end() as u32);
|
||||||
|
let last_line = i as u32 - 1;
|
||||||
|
let start_lc = LineColumn {
|
||||||
|
line: last_line,
|
||||||
|
column: start,
|
||||||
|
};
|
||||||
|
let end_lc = LineColumn {
|
||||||
|
line: last_line,
|
||||||
|
column: end,
|
||||||
|
};
|
||||||
|
let lc_region = LineColumnRegion::new(start_lc, end_lc);
|
||||||
|
let region = line_info.convert_line_column_region(lc_region);
|
||||||
|
|
||||||
|
queries.push(TypeQuery(region));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
queries
|
||||||
|
}
|
||||||
|
|
||||||
|
fn run_load_and_infer(src: &str) -> Result<(LoadedModule, String), std::io::Error> {
|
||||||
use bumpalo::Bump;
|
use bumpalo::Bump;
|
||||||
use std::path::PathBuf;
|
|
||||||
use tempfile::tempdir;
|
use tempfile::tempdir;
|
||||||
|
|
||||||
let arena = &Bump::new();
|
let arena = &Bump::new();
|
||||||
|
@ -54,51 +94,78 @@ mod solve_expr {
|
||||||
};
|
};
|
||||||
|
|
||||||
let loaded = loaded.expect("failed to load module");
|
let loaded = loaded.expect("failed to load module");
|
||||||
Ok(loaded)
|
Ok((loaded, module_src.to_string()))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn infer_eq_help(
|
fn format_problems(
|
||||||
src: &str,
|
src: &str,
|
||||||
) -> Result<
|
home: ModuleId,
|
||||||
(
|
interns: &Interns,
|
||||||
Vec<roc_solve::solve::TypeError>,
|
can_problems: Vec<Problem>,
|
||||||
Vec<roc_problem::can::Problem>,
|
type_problems: Vec<TypeError>,
|
||||||
String,
|
) -> (String, String) {
|
||||||
),
|
let filename = PathBuf::from("test.roc");
|
||||||
std::io::Error,
|
let src_lines: Vec<&str> = src.split('\n').collect();
|
||||||
> {
|
let lines = LineInfo::new(src);
|
||||||
let LoadedModule {
|
let alloc = RocDocAllocator::new(&src_lines, home, &interns);
|
||||||
module_id: home,
|
|
||||||
mut can_problems,
|
let mut can_reports = vec![];
|
||||||
mut type_problems,
|
let mut type_reports = vec![];
|
||||||
interns,
|
|
||||||
mut solved,
|
for problem in can_problems {
|
||||||
exposed_to_host,
|
let report = can_problem(&alloc, &lines, filename.clone(), problem.clone());
|
||||||
..
|
can_reports.push(report.pretty(&alloc));
|
||||||
} = run_load_and_infer(src)?;
|
}
|
||||||
|
|
||||||
|
for problem in type_problems {
|
||||||
|
if let Some(report) = type_problem(&alloc, &lines, filename.clone(), problem.clone()) {
|
||||||
|
type_reports.push(report.pretty(&alloc));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut can_reports_buf = String::new();
|
||||||
|
let mut type_reports_buf = String::new();
|
||||||
|
use roc_reporting::report::CiWrite;
|
||||||
|
alloc
|
||||||
|
.stack(can_reports)
|
||||||
|
.1
|
||||||
|
.render_raw(70, &mut CiWrite::new(&mut can_reports_buf))
|
||||||
|
.unwrap();
|
||||||
|
alloc
|
||||||
|
.stack(type_reports)
|
||||||
|
.1
|
||||||
|
.render_raw(70, &mut CiWrite::new(&mut type_reports_buf))
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
(can_reports_buf, type_reports_buf)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn infer_eq_help(src: &str) -> Result<(String, String, String), std::io::Error> {
|
||||||
|
let (
|
||||||
|
LoadedModule {
|
||||||
|
module_id: home,
|
||||||
|
mut can_problems,
|
||||||
|
mut type_problems,
|
||||||
|
interns,
|
||||||
|
mut solved,
|
||||||
|
exposed_to_host,
|
||||||
|
..
|
||||||
|
},
|
||||||
|
src,
|
||||||
|
) = run_load_and_infer(src)?;
|
||||||
|
|
||||||
let mut can_problems = can_problems.remove(&home).unwrap_or_default();
|
let mut can_problems = can_problems.remove(&home).unwrap_or_default();
|
||||||
let type_problems = type_problems.remove(&home).unwrap_or_default();
|
let type_problems = type_problems.remove(&home).unwrap_or_default();
|
||||||
|
|
||||||
|
// Disregard UnusedDef problems, because those are unavoidable when
|
||||||
|
// returning a function from the test expression.
|
||||||
|
can_problems.retain(|prob| !matches!(prob, roc_problem::can::Problem::UnusedDef(_, _)));
|
||||||
|
|
||||||
|
let (can_problems, type_problems) =
|
||||||
|
format_problems(&src, home, &interns, can_problems, type_problems);
|
||||||
|
|
||||||
let subs = solved.inner_mut();
|
let subs = solved.inner_mut();
|
||||||
|
|
||||||
// assert!(can_problems.is_empty());
|
|
||||||
// assert!(type_problems.is_empty());
|
|
||||||
// let CanExprOut {
|
|
||||||
// output,
|
|
||||||
// var_store,
|
|
||||||
// var,
|
|
||||||
// constraint,
|
|
||||||
// home,
|
|
||||||
// interns,
|
|
||||||
// problems: mut can_problems,
|
|
||||||
// ..
|
|
||||||
// } = can_expr(src);
|
|
||||||
// let mut subs = Subs::new(var_store.into());
|
|
||||||
|
|
||||||
// TODO fix this
|
|
||||||
// assert_correct_variable_usage(&constraint);
|
|
||||||
|
|
||||||
// name type vars
|
// name type vars
|
||||||
for var in exposed_to_host.values() {
|
for var in exposed_to_host.values() {
|
||||||
name_all_type_vars(*var, subs);
|
name_all_type_vars(*var, subs);
|
||||||
|
@ -112,10 +179,6 @@ mod solve_expr {
|
||||||
|
|
||||||
let actual_str = content_to_string(content, subs, home, &interns);
|
let actual_str = content_to_string(content, subs, home, &interns);
|
||||||
|
|
||||||
// Disregard UnusedDef problems, because those are unavoidable when
|
|
||||||
// returning a function from the test expression.
|
|
||||||
can_problems.retain(|prob| !matches!(prob, roc_problem::can::Problem::UnusedDef(_, _)));
|
|
||||||
|
|
||||||
Ok((type_problems, can_problems, actual_str))
|
Ok((type_problems, can_problems, actual_str))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -143,7 +206,11 @@ mod solve_expr {
|
||||||
fn infer_eq(src: &str, expected: &str) {
|
fn infer_eq(src: &str, expected: &str) {
|
||||||
let (_, can_problems, actual) = infer_eq_help(src).unwrap();
|
let (_, can_problems, actual) = infer_eq_help(src).unwrap();
|
||||||
|
|
||||||
assert_eq!(can_problems, Vec::new(), "Canonicalization problems: ");
|
assert!(
|
||||||
|
can_problems.is_empty(),
|
||||||
|
"Canonicalization problems: {}",
|
||||||
|
can_problems
|
||||||
|
);
|
||||||
|
|
||||||
assert_eq!(actual, expected.to_string());
|
assert_eq!(actual, expected.to_string());
|
||||||
}
|
}
|
||||||
|
@ -151,16 +218,73 @@ mod solve_expr {
|
||||||
fn infer_eq_without_problem(src: &str, expected: &str) {
|
fn infer_eq_without_problem(src: &str, expected: &str) {
|
||||||
let (type_problems, can_problems, actual) = infer_eq_help(src).unwrap();
|
let (type_problems, can_problems, actual) = infer_eq_help(src).unwrap();
|
||||||
|
|
||||||
assert_eq!(can_problems, Vec::new(), "Canonicalization problems: ");
|
assert!(
|
||||||
|
can_problems.is_empty(),
|
||||||
|
"Canonicalization problems: {}",
|
||||||
|
can_problems
|
||||||
|
);
|
||||||
|
|
||||||
if !type_problems.is_empty() {
|
if !type_problems.is_empty() {
|
||||||
// fail with an assert, but print the problems normally so rust doesn't try to diff
|
// fail with an assert, but print the problems normally so rust doesn't try to diff
|
||||||
// an empty vec with the problems.
|
// an empty vec with the problems.
|
||||||
panic!("expected:\n{:?}\ninferred:\n{:?}", expected, actual);
|
panic!(
|
||||||
|
"expected:\n{:?}\ninferred:\n{:?}\nproblems:\n{}",
|
||||||
|
expected, actual, type_problems,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
assert_eq!(actual, expected.to_string());
|
assert_eq!(actual, expected.to_string());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn infer_queries(src: &str, expected: &[&'static str]) {
|
||||||
|
let (
|
||||||
|
LoadedModule {
|
||||||
|
module_id: home,
|
||||||
|
mut can_problems,
|
||||||
|
mut type_problems,
|
||||||
|
mut declarations_by_id,
|
||||||
|
mut solved,
|
||||||
|
interns,
|
||||||
|
..
|
||||||
|
},
|
||||||
|
src,
|
||||||
|
) = run_load_and_infer(src).unwrap();
|
||||||
|
|
||||||
|
let decls = declarations_by_id.remove(&home).unwrap();
|
||||||
|
let subs = solved.inner_mut();
|
||||||
|
|
||||||
|
let can_problems = can_problems.remove(&home).unwrap_or_default();
|
||||||
|
let type_problems = type_problems.remove(&home).unwrap_or_default();
|
||||||
|
|
||||||
|
let (can_problems, type_problems) =
|
||||||
|
format_problems(&src, home, &interns, can_problems, type_problems);
|
||||||
|
|
||||||
|
assert!(
|
||||||
|
can_problems.is_empty(),
|
||||||
|
"Canonicalization problems: {}",
|
||||||
|
can_problems
|
||||||
|
);
|
||||||
|
assert!(type_problems.is_empty(), "Type problems: {}", type_problems);
|
||||||
|
|
||||||
|
let queries = parse_queries(&src);
|
||||||
|
assert!(!queries.is_empty(), "No queries provided!");
|
||||||
|
|
||||||
|
let mut solved_queries = Vec::with_capacity(queries.len());
|
||||||
|
for TypeQuery(region) in queries.into_iter() {
|
||||||
|
let start = region.start().offset;
|
||||||
|
let end = region.end().offset;
|
||||||
|
let text = &src[start as usize..end as usize];
|
||||||
|
let var = find_type_at(region, &decls).expect(&format!("No type for {}!", &text));
|
||||||
|
|
||||||
|
name_all_type_vars(var, subs);
|
||||||
|
let content = subs.get_content_without_compacting(var);
|
||||||
|
let actual_str = content_to_string(content, subs, home, &interns);
|
||||||
|
|
||||||
|
solved_queries.push(format!("{} : {}", text, actual_str));
|
||||||
|
}
|
||||||
|
|
||||||
|
assert_eq!(solved_queries, expected)
|
||||||
|
}
|
||||||
|
|
||||||
fn check_inferred_abilities<'a, I>(src: &'a str, expected_specializations: I)
|
fn check_inferred_abilities<'a, I>(src: &'a str, expected_specializations: I)
|
||||||
where
|
where
|
||||||
I: IntoIterator<Item = (&'a str, &'a str)>,
|
I: IntoIterator<Item = (&'a str, &'a str)>,
|
||||||
|
@ -172,7 +296,7 @@ mod solve_expr {
|
||||||
interns,
|
interns,
|
||||||
abilities_store,
|
abilities_store,
|
||||||
..
|
..
|
||||||
} = run_load_and_infer(src).unwrap();
|
} = run_load_and_infer(src).unwrap().0;
|
||||||
|
|
||||||
let can_problems = can_problems.remove(&home).unwrap_or_default();
|
let can_problems = can_problems.remove(&home).unwrap_or_default();
|
||||||
let type_problems = type_problems.remove(&home).unwrap_or_default();
|
let type_problems = type_problems.remove(&home).unwrap_or_default();
|
||||||
|
@ -1383,18 +1507,6 @@ mod solve_expr {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn single_private_tag_pattern() {
|
|
||||||
infer_eq(
|
|
||||||
indoc!(
|
|
||||||
r#"
|
|
||||||
\@Foo -> 42
|
|
||||||
"#
|
|
||||||
),
|
|
||||||
"[ @Foo ] -> Num *",
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn two_tag_pattern() {
|
fn two_tag_pattern() {
|
||||||
infer_eq(
|
infer_eq(
|
||||||
|
@ -1422,18 +1534,6 @@ mod solve_expr {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn private_tag_application() {
|
|
||||||
infer_eq(
|
|
||||||
indoc!(
|
|
||||||
r#"
|
|
||||||
@Foo "happy" 2020
|
|
||||||
"#
|
|
||||||
),
|
|
||||||
"[ @Foo Str (Num *) ]*",
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn record_extraction() {
|
fn record_extraction() {
|
||||||
infer_eq(
|
infer_eq(
|
||||||
|
@ -1500,19 +1600,6 @@ mod solve_expr {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn private_tag_with_field() {
|
|
||||||
infer_eq(
|
|
||||||
indoc!(
|
|
||||||
r#"
|
|
||||||
when @Foo "blah" is
|
|
||||||
@Foo x -> x
|
|
||||||
"#
|
|
||||||
),
|
|
||||||
"Str",
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn qualified_annotation_num_integer() {
|
fn qualified_annotation_num_integer() {
|
||||||
infer_eq(
|
infer_eq(
|
||||||
|
@ -4115,31 +4202,6 @@ mod solve_expr {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn double_tag_application_pattern_private() {
|
|
||||||
infer_eq_without_problem(
|
|
||||||
indoc!(
|
|
||||||
r#"
|
|
||||||
app "test" provides [ main ] to "./platform"
|
|
||||||
|
|
||||||
Foo : [ @Foo [ @Bar ] I64, @Empty ]
|
|
||||||
|
|
||||||
foo : Foo
|
|
||||||
foo = @Foo @Bar 1
|
|
||||||
|
|
||||||
main =
|
|
||||||
when foo is
|
|
||||||
@Foo @Bar 1 ->
|
|
||||||
@Foo @Bar 2
|
|
||||||
|
|
||||||
x ->
|
|
||||||
x
|
|
||||||
"#
|
|
||||||
),
|
|
||||||
"[ @Empty, @Foo [ @Bar ] I64 ]",
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn recursive_function_with_rigid() {
|
fn recursive_function_with_rigid() {
|
||||||
infer_eq_without_problem(
|
infer_eq_without_problem(
|
||||||
|
@ -5313,6 +5375,24 @@ mod solve_expr {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn issue_2458_swapped_order() {
|
||||||
|
infer_eq_without_problem(
|
||||||
|
indoc!(
|
||||||
|
r#"
|
||||||
|
Bar a : Foo a
|
||||||
|
Foo a : [ Blah (Result (Bar a) { val: a }) ]
|
||||||
|
|
||||||
|
v : Bar U8
|
||||||
|
v = Blah (Ok (Blah (Err { val: 1 })))
|
||||||
|
|
||||||
|
v
|
||||||
|
"#
|
||||||
|
),
|
||||||
|
"Bar U8",
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
// https://github.com/rtfeldman/roc/issues/2379
|
// https://github.com/rtfeldman/roc/issues/2379
|
||||||
#[test]
|
#[test]
|
||||||
fn copy_vars_referencing_copied_vars() {
|
fn copy_vars_referencing_copied_vars() {
|
||||||
|
@ -5400,7 +5480,7 @@ mod solve_expr {
|
||||||
r#"
|
r#"
|
||||||
Age := U32
|
Age := U32
|
||||||
|
|
||||||
$Age 21
|
@Age 21
|
||||||
"#
|
"#
|
||||||
),
|
),
|
||||||
r#"Age"#,
|
r#"Age"#,
|
||||||
|
@ -5415,7 +5495,7 @@ mod solve_expr {
|
||||||
Age := U32
|
Age := U32
|
||||||
|
|
||||||
a : Age
|
a : Age
|
||||||
a = $Age 21
|
a = @Age 21
|
||||||
|
|
||||||
a
|
a
|
||||||
"#
|
"#
|
||||||
|
@ -5431,7 +5511,7 @@ mod solve_expr {
|
||||||
r#"
|
r#"
|
||||||
Id n := [ Id U32 n ]
|
Id n := [ Id U32 n ]
|
||||||
|
|
||||||
$Id (Id 21 "sasha")
|
@Id (Id 21 "sasha")
|
||||||
"#
|
"#
|
||||||
),
|
),
|
||||||
r#"Id Str"#,
|
r#"Id Str"#,
|
||||||
|
@ -5446,7 +5526,7 @@ mod solve_expr {
|
||||||
Id n := [ Id U32 n ]
|
Id n := [ Id U32 n ]
|
||||||
|
|
||||||
a : Id Str
|
a : Id Str
|
||||||
a = $Id (Id 21 "sasha")
|
a = @Id (Id 21 "sasha")
|
||||||
|
|
||||||
a
|
a
|
||||||
"#
|
"#
|
||||||
|
@ -5464,8 +5544,8 @@ mod solve_expr {
|
||||||
condition : Bool
|
condition : Bool
|
||||||
|
|
||||||
if condition
|
if condition
|
||||||
then $Id (Id 21 (Y "sasha"))
|
then @Id (Id 21 (Y "sasha"))
|
||||||
else $Id (Id 21 (Z "felix"))
|
else @Id (Id 21 (Z "felix"))
|
||||||
"#
|
"#
|
||||||
),
|
),
|
||||||
r#"Id [ Y Str, Z Str ]*"#,
|
r#"Id [ Y Str, Z Str ]*"#,
|
||||||
|
@ -5483,8 +5563,8 @@ mod solve_expr {
|
||||||
v : Id [ Y Str, Z Str ]
|
v : Id [ Y Str, Z Str ]
|
||||||
v =
|
v =
|
||||||
if condition
|
if condition
|
||||||
then $Id (Id 21 (Y "sasha"))
|
then @Id (Id 21 (Y "sasha"))
|
||||||
else $Id (Id 21 (Z "felix"))
|
else @Id (Id 21 (Z "felix"))
|
||||||
|
|
||||||
v
|
v
|
||||||
"#
|
"#
|
||||||
|
@ -5500,7 +5580,7 @@ mod solve_expr {
|
||||||
r#"
|
r#"
|
||||||
Age := U32
|
Age := U32
|
||||||
|
|
||||||
\$Age n -> n
|
\@Age n -> n
|
||||||
"#
|
"#
|
||||||
),
|
),
|
||||||
r#"Age -> U32"#,
|
r#"Age -> U32"#,
|
||||||
|
@ -5515,7 +5595,7 @@ mod solve_expr {
|
||||||
Age := U32
|
Age := U32
|
||||||
|
|
||||||
v : Age -> U32
|
v : Age -> U32
|
||||||
v = \$Age n -> n
|
v = \@Age n -> n
|
||||||
v
|
v
|
||||||
"#
|
"#
|
||||||
),
|
),
|
||||||
|
@ -5530,7 +5610,7 @@ mod solve_expr {
|
||||||
r#"
|
r#"
|
||||||
Id n := [ Id U32 n ]
|
Id n := [ Id U32 n ]
|
||||||
|
|
||||||
\$Id (Id _ n) -> n
|
\@Id (Id _ n) -> n
|
||||||
"#
|
"#
|
||||||
),
|
),
|
||||||
r#"Id a -> a"#,
|
r#"Id a -> a"#,
|
||||||
|
@ -5545,7 +5625,7 @@ mod solve_expr {
|
||||||
Id n := [ Id U32 n ]
|
Id n := [ Id U32 n ]
|
||||||
|
|
||||||
v : Id a -> a
|
v : Id a -> a
|
||||||
v = \$Id (Id _ n) -> n
|
v = \@Id (Id _ n) -> n
|
||||||
|
|
||||||
v
|
v
|
||||||
"#
|
"#
|
||||||
|
@ -5563,7 +5643,7 @@ mod solve_expr {
|
||||||
|
|
||||||
strToBool : Str -> Bool
|
strToBool : Str -> Bool
|
||||||
|
|
||||||
\$Id (Id _ n) -> strToBool n
|
\@Id (Id _ n) -> strToBool n
|
||||||
"#
|
"#
|
||||||
),
|
),
|
||||||
r#"Id Str -> Bool"#,
|
r#"Id Str -> Bool"#,
|
||||||
|
@ -5580,7 +5660,7 @@ mod solve_expr {
|
||||||
strToBool : Str -> Bool
|
strToBool : Str -> Bool
|
||||||
|
|
||||||
v : Id Str -> Bool
|
v : Id Str -> Bool
|
||||||
v = \$Id (Id _ n) -> strToBool n
|
v = \@Id (Id _ n) -> strToBool n
|
||||||
|
|
||||||
v
|
v
|
||||||
"#
|
"#
|
||||||
|
@ -5598,9 +5678,9 @@ mod solve_expr {
|
||||||
|
|
||||||
\id ->
|
\id ->
|
||||||
when id is
|
when id is
|
||||||
$Id (Id _ A) -> ""
|
@Id (Id _ A) -> ""
|
||||||
$Id (Id _ B) -> ""
|
@Id (Id _ B) -> ""
|
||||||
$Id (Id _ (C { a: "" })) -> ""
|
@Id (Id _ (C { a: "" })) -> ""
|
||||||
"#
|
"#
|
||||||
),
|
),
|
||||||
r#"Id [ A, B, C { a : Str }* ] -> Str"#,
|
r#"Id [ A, B, C { a : Str }* ] -> Str"#,
|
||||||
|
@ -5617,9 +5697,9 @@ mod solve_expr {
|
||||||
f : Id [ A, B, C { a : Str }e ] -> Str
|
f : Id [ A, B, C { a : Str }e ] -> Str
|
||||||
f = \id ->
|
f = \id ->
|
||||||
when id is
|
when id is
|
||||||
$Id (Id _ A) -> ""
|
@Id (Id _ A) -> ""
|
||||||
$Id (Id _ B) -> ""
|
@Id (Id _ B) -> ""
|
||||||
$Id (Id _ (C { a: "" })) -> ""
|
@Id (Id _ (C { a: "" })) -> ""
|
||||||
|
|
||||||
f
|
f
|
||||||
"#
|
"#
|
||||||
|
@ -5635,7 +5715,7 @@ mod solve_expr {
|
||||||
r#"
|
r#"
|
||||||
app "test" provides [ effectAlways ] to "./platform"
|
app "test" provides [ effectAlways ] to "./platform"
|
||||||
|
|
||||||
Effect a : [ @Effect ({} -> a) ]
|
Effect a := {} -> a
|
||||||
|
|
||||||
effectAlways : a -> Effect a
|
effectAlways : a -> Effect a
|
||||||
effectAlways = \x ->
|
effectAlways = \x ->
|
||||||
|
@ -5703,8 +5783,8 @@ mod solve_expr {
|
||||||
insert : Outer k, k -> Outer k
|
insert : Outer k, k -> Outer k
|
||||||
insert = \m, var ->
|
insert = \m, var ->
|
||||||
when m is
|
when m is
|
||||||
$Outer Empty -> $Outer (Wrapped var)
|
@Outer Empty -> @Outer (Wrapped var)
|
||||||
$Outer (Wrapped _) -> $Outer (Wrapped var)
|
@Outer (Wrapped _) -> @Outer (Wrapped var)
|
||||||
|
|
||||||
insert
|
insert
|
||||||
"#
|
"#
|
||||||
|
@ -5720,9 +5800,9 @@ mod solve_expr {
|
||||||
r#"
|
r#"
|
||||||
Outer k := [ Empty, Wrapped k ]
|
Outer k := [ Empty, Wrapped k ]
|
||||||
|
|
||||||
when ($Outer Empty) is
|
when (@Outer Empty) is
|
||||||
$Outer Empty -> $Outer (Wrapped "")
|
@Outer Empty -> @Outer (Wrapped "")
|
||||||
$Outer (Wrapped k) -> $Outer (Wrapped k)
|
@Outer (Wrapped k) -> @Outer (Wrapped k)
|
||||||
"#
|
"#
|
||||||
),
|
),
|
||||||
r#"Outer Str"#,
|
r#"Outer Str"#,
|
||||||
|
@ -5736,9 +5816,9 @@ mod solve_expr {
|
||||||
r#"
|
r#"
|
||||||
Outer := [ A, B ]
|
Outer := [ A, B ]
|
||||||
|
|
||||||
when ($Outer A) is
|
when (@Outer A) is
|
||||||
$Outer A -> $Outer A
|
@Outer A -> @Outer A
|
||||||
$Outer B -> $Outer B
|
@Outer B -> @Outer B
|
||||||
"#
|
"#
|
||||||
),
|
),
|
||||||
r#"Outer"#,
|
r#"Outer"#,
|
||||||
|
@ -5795,7 +5875,7 @@ mod solve_expr {
|
||||||
|
|
||||||
Id := U64
|
Id := U64
|
||||||
|
|
||||||
hash = \$Id n -> n
|
hash = \@Id n -> n
|
||||||
"#
|
"#
|
||||||
),
|
),
|
||||||
[("Hash:hash", "Id")],
|
[("Hash:hash", "Id")],
|
||||||
|
@ -5815,8 +5895,8 @@ mod solve_expr {
|
||||||
|
|
||||||
Id := U64
|
Id := U64
|
||||||
|
|
||||||
hash = \$Id n -> n
|
hash = \@Id n -> n
|
||||||
hash32 = \$Id n -> Num.toU32 n
|
hash32 = \@Id n -> Num.toU32 n
|
||||||
"#
|
"#
|
||||||
),
|
),
|
||||||
[("Hash:hash", "Id"), ("Hash:hash32", "Id")],
|
[("Hash:hash", "Id"), ("Hash:hash32", "Id")],
|
||||||
|
@ -5840,11 +5920,11 @@ mod solve_expr {
|
||||||
|
|
||||||
Id := U64
|
Id := U64
|
||||||
|
|
||||||
hash = \$Id n -> n
|
hash = \@Id n -> n
|
||||||
hash32 = \$Id n -> Num.toU32 n
|
hash32 = \@Id n -> Num.toU32 n
|
||||||
|
|
||||||
eq = \$Id m, $Id n -> m == n
|
eq = \@Id m, @Id n -> m == n
|
||||||
le = \$Id m, $Id n -> m < n
|
le = \@Id m, @Id n -> m < n
|
||||||
"#
|
"#
|
||||||
),
|
),
|
||||||
[
|
[
|
||||||
|
@ -5869,7 +5949,7 @@ mod solve_expr {
|
||||||
Id := U64
|
Id := U64
|
||||||
|
|
||||||
hash : Id -> U64
|
hash : Id -> U64
|
||||||
hash = \$Id n -> n
|
hash = \@Id n -> n
|
||||||
"#
|
"#
|
||||||
),
|
),
|
||||||
[("Hash:hash", "Id")],
|
[("Hash:hash", "Id")],
|
||||||
|
@ -5907,9 +5987,9 @@ mod solve_expr {
|
||||||
|
|
||||||
Id := U64
|
Id := U64
|
||||||
|
|
||||||
hash = \$Id n -> n
|
hash = \@Id n -> n
|
||||||
|
|
||||||
zero = hash ($Id 0)
|
zero = hash (@Id 0)
|
||||||
"#
|
"#
|
||||||
),
|
),
|
||||||
"U64",
|
"U64",
|
||||||
|
@ -6000,9 +6080,9 @@ mod solve_expr {
|
||||||
hashEq = \x, y -> hash x == hash y
|
hashEq = \x, y -> hash x == hash y
|
||||||
|
|
||||||
Id := U64
|
Id := U64
|
||||||
hash = \$Id n -> n
|
hash = \@Id n -> n
|
||||||
|
|
||||||
result = hashEq ($Id 100) ($Id 101)
|
result = hashEq (@Id 100) (@Id 101)
|
||||||
"#
|
"#
|
||||||
),
|
),
|
||||||
"Bool",
|
"Bool",
|
||||||
|
@ -6022,15 +6102,88 @@ mod solve_expr {
|
||||||
mulHashes = \x, y -> hash x * hash y
|
mulHashes = \x, y -> hash x * hash y
|
||||||
|
|
||||||
Id := U64
|
Id := U64
|
||||||
hash = \$Id n -> n
|
hash = \@Id n -> n
|
||||||
|
|
||||||
Three := {}
|
Three := {}
|
||||||
hash = \$Three _ -> 3
|
hash = \@Three _ -> 3
|
||||||
|
|
||||||
result = mulHashes ($Id 100) ($Three {})
|
result = mulHashes (@Id 100) (@Three {})
|
||||||
"#
|
"#
|
||||||
),
|
),
|
||||||
"U64",
|
"U64",
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn intermediate_branch_types() {
|
||||||
|
infer_queries(
|
||||||
|
indoc!(
|
||||||
|
r#"
|
||||||
|
app "test" provides [ foo ] to "./platform"
|
||||||
|
|
||||||
|
foo : Bool -> Str
|
||||||
|
foo = \ob ->
|
||||||
|
# ^^
|
||||||
|
when ob is
|
||||||
|
# ^^
|
||||||
|
True -> "A"
|
||||||
|
# ^^^^
|
||||||
|
False -> "B"
|
||||||
|
# ^^^^^
|
||||||
|
"#
|
||||||
|
),
|
||||||
|
&[
|
||||||
|
"ob : Bool",
|
||||||
|
"ob : [ False, True ]",
|
||||||
|
"True : [ False, True ]",
|
||||||
|
"False : [ False, True ]",
|
||||||
|
],
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn nested_open_tag_union() {
|
||||||
|
infer_eq_without_problem(
|
||||||
|
indoc!(
|
||||||
|
r#"
|
||||||
|
app "test" provides [ go ] to "./platform"
|
||||||
|
|
||||||
|
Expr : [
|
||||||
|
Wrap Expr,
|
||||||
|
Val I64,
|
||||||
|
]
|
||||||
|
|
||||||
|
go : Expr -> Expr
|
||||||
|
go = \e ->
|
||||||
|
when P e is
|
||||||
|
P (Wrap (Val _)) -> Wrap e
|
||||||
|
|
||||||
|
# This branch should force the first argument to `P` and
|
||||||
|
# the first argument to `Wrap` to be an open tag union.
|
||||||
|
# This tests checks that we don't regress on that.
|
||||||
|
P y1 -> Wrap y1
|
||||||
|
"#
|
||||||
|
),
|
||||||
|
indoc!(r#"Expr -> Expr"#),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn opaque_and_alias_unify() {
|
||||||
|
infer_eq_without_problem(
|
||||||
|
indoc!(
|
||||||
|
r#"
|
||||||
|
app "test" provides [ always ] to "./platform"
|
||||||
|
|
||||||
|
Effect a := {} -> a
|
||||||
|
|
||||||
|
Task a err : Effect (Result a err)
|
||||||
|
|
||||||
|
always : a -> Task a *
|
||||||
|
always = \x -> @Effect (\{} -> Ok x)
|
||||||
|
"#
|
||||||
|
),
|
||||||
|
"a -> Task a *",
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -37,7 +37,7 @@ bumpalo = { version = "3.8.0", features = ["collections"] }
|
||||||
either = "1.6.1"
|
either = "1.6.1"
|
||||||
libc = "0.2.106"
|
libc = "0.2.106"
|
||||||
inkwell = { path = "../../vendor/inkwell" }
|
inkwell = { path = "../../vendor/inkwell" }
|
||||||
target-lexicon = "0.12.2"
|
target-lexicon = "0.12.3"
|
||||||
libloading = "0.7.1"
|
libloading = "0.7.1"
|
||||||
wasmer-wasi = "2.0.0"
|
wasmer-wasi = "2.0.0"
|
||||||
tempfile = "3.2.0"
|
tempfile = "3.2.0"
|
||||||
|
|
|
@ -23,9 +23,9 @@ fn hash_specialization() {
|
||||||
|
|
||||||
Id := U64
|
Id := U64
|
||||||
|
|
||||||
hash = \$Id n -> n
|
hash = \@Id n -> n
|
||||||
|
|
||||||
main = hash ($Id 1234)
|
main = hash (@Id 1234)
|
||||||
"#
|
"#
|
||||||
),
|
),
|
||||||
1234,
|
1234,
|
||||||
|
@ -46,13 +46,13 @@ fn hash_specialization_multiple_add() {
|
||||||
|
|
||||||
Id := U64
|
Id := U64
|
||||||
|
|
||||||
hash = \$Id n -> n
|
hash = \@Id n -> n
|
||||||
|
|
||||||
One := {}
|
One := {}
|
||||||
|
|
||||||
hash = \$One _ -> 1
|
hash = \@One _ -> 1
|
||||||
|
|
||||||
main = hash ($Id 1234) + hash ($One {})
|
main = hash (@Id 1234) + hash (@One {})
|
||||||
"#
|
"#
|
||||||
),
|
),
|
||||||
1235,
|
1235,
|
||||||
|
@ -73,11 +73,11 @@ fn alias_member_specialization() {
|
||||||
|
|
||||||
Id := U64
|
Id := U64
|
||||||
|
|
||||||
hash = \$Id n -> n
|
hash = \@Id n -> n
|
||||||
|
|
||||||
main =
|
main =
|
||||||
aliasedHash = hash
|
aliasedHash = hash
|
||||||
aliasedHash ($Id 1234)
|
aliasedHash (@Id 1234)
|
||||||
"#
|
"#
|
||||||
),
|
),
|
||||||
1234,
|
1234,
|
||||||
|
@ -100,9 +100,9 @@ fn ability_constrained_in_non_member_usage() {
|
||||||
mulHashes = \x, y -> hash x * hash y
|
mulHashes = \x, y -> hash x * hash y
|
||||||
|
|
||||||
Id := U64
|
Id := U64
|
||||||
hash = \$Id n -> n
|
hash = \@Id n -> n
|
||||||
|
|
||||||
result = mulHashes ($Id 5) ($Id 7)
|
result = mulHashes (@Id 5) (@Id 7)
|
||||||
"#
|
"#
|
||||||
),
|
),
|
||||||
35,
|
35,
|
||||||
|
@ -124,9 +124,9 @@ fn ability_constrained_in_non_member_usage_inferred() {
|
||||||
mulHashes = \x, y -> hash x * hash y
|
mulHashes = \x, y -> hash x * hash y
|
||||||
|
|
||||||
Id := U64
|
Id := U64
|
||||||
hash = \$Id n -> n
|
hash = \@Id n -> n
|
||||||
|
|
||||||
result = mulHashes ($Id 5) ($Id 7)
|
result = mulHashes (@Id 5) (@Id 7)
|
||||||
"#
|
"#
|
||||||
),
|
),
|
||||||
35,
|
35,
|
||||||
|
@ -149,12 +149,12 @@ fn ability_constrained_in_non_member_multiple_specializations() {
|
||||||
mulHashes = \x, y -> hash x * hash y
|
mulHashes = \x, y -> hash x * hash y
|
||||||
|
|
||||||
Id := U64
|
Id := U64
|
||||||
hash = \$Id n -> n
|
hash = \@Id n -> n
|
||||||
|
|
||||||
Three := {}
|
Three := {}
|
||||||
hash = \$Three _ -> 3
|
hash = \@Three _ -> 3
|
||||||
|
|
||||||
result = mulHashes ($Id 100) ($Three {})
|
result = mulHashes (@Id 100) (@Three {})
|
||||||
"#
|
"#
|
||||||
),
|
),
|
||||||
300,
|
300,
|
||||||
|
@ -176,12 +176,12 @@ fn ability_constrained_in_non_member_multiple_specializations_inferred() {
|
||||||
mulHashes = \x, y -> hash x * hash y
|
mulHashes = \x, y -> hash x * hash y
|
||||||
|
|
||||||
Id := U64
|
Id := U64
|
||||||
hash = \$Id n -> n
|
hash = \@Id n -> n
|
||||||
|
|
||||||
Three := {}
|
Three := {}
|
||||||
hash = \$Three _ -> 3
|
hash = \@Three _ -> 3
|
||||||
|
|
||||||
result = mulHashes ($Id 100) ($Three {})
|
result = mulHashes (@Id 100) (@Three {})
|
||||||
"#
|
"#
|
||||||
),
|
),
|
||||||
300,
|
300,
|
||||||
|
@ -204,12 +204,12 @@ fn ability_used_as_type_still_compiles() {
|
||||||
mulHashes = \x, y -> hash x * hash y
|
mulHashes = \x, y -> hash x * hash y
|
||||||
|
|
||||||
Id := U64
|
Id := U64
|
||||||
hash = \$Id n -> n
|
hash = \@Id n -> n
|
||||||
|
|
||||||
Three := {}
|
Three := {}
|
||||||
hash = \$Three _ -> 3
|
hash = \@Three _ -> 3
|
||||||
|
|
||||||
result = mulHashes ($Id 100) ($Three {})
|
result = mulHashes (@Id 100) (@Three {})
|
||||||
"#
|
"#
|
||||||
),
|
),
|
||||||
300,
|
300,
|
||||||
|
|
|
@ -2534,7 +2534,7 @@ fn list_keep_oks() {
|
||||||
RocList<i64>
|
RocList<i64>
|
||||||
);
|
);
|
||||||
assert_evals_to!(
|
assert_evals_to!(
|
||||||
"List.keepOks [1,2] (\\x -> x % 2)",
|
"List.keepOks [1,2] (\\x -> Num.remChecked x 2)",
|
||||||
RocList::from_slice(&[1, 0]),
|
RocList::from_slice(&[1, 0]),
|
||||||
RocList<i64>
|
RocList<i64>
|
||||||
);
|
);
|
||||||
|
@ -2561,7 +2561,7 @@ fn list_keep_errs() {
|
||||||
assert_evals_to!(
|
assert_evals_to!(
|
||||||
indoc!(
|
indoc!(
|
||||||
r#"
|
r#"
|
||||||
List.keepErrs [0,1,2] (\x -> x % 0 |> Result.mapErr (\_ -> 32))
|
List.keepErrs [0,1,2] (\x -> Num.remChecked x 0 |> Result.mapErr (\_ -> 32))
|
||||||
"#
|
"#
|
||||||
),
|
),
|
||||||
RocList::from_slice(&[32, 32, 32]),
|
RocList::from_slice(&[32, 32, 32]),
|
||||||
|
|
|
@ -473,7 +473,7 @@ fn f64_sqrt() {
|
||||||
assert_evals_to!(
|
assert_evals_to!(
|
||||||
indoc!(
|
indoc!(
|
||||||
r#"
|
r#"
|
||||||
when Num.sqrt 100 is
|
when Num.sqrtChecked 100 is
|
||||||
Ok val -> val
|
Ok val -> val
|
||||||
Err _ -> -1
|
Err _ -> -1
|
||||||
"#
|
"#
|
||||||
|
@ -489,9 +489,7 @@ fn f64_log() {
|
||||||
assert_evals_to!(
|
assert_evals_to!(
|
||||||
indoc!(
|
indoc!(
|
||||||
r#"
|
r#"
|
||||||
when Num.log 7.38905609893 is
|
Num.log 7.38905609893
|
||||||
Ok val -> val
|
|
||||||
Err _ -> -1
|
|
||||||
"#
|
"#
|
||||||
),
|
),
|
||||||
1.999999999999912,
|
1.999999999999912,
|
||||||
|
@ -501,11 +499,11 @@ fn f64_log() {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
#[cfg(any(feature = "gen-llvm"))]
|
#[cfg(any(feature = "gen-llvm"))]
|
||||||
fn f64_log_one() {
|
fn f64_log_checked_one() {
|
||||||
assert_evals_to!(
|
assert_evals_to!(
|
||||||
indoc!(
|
indoc!(
|
||||||
r#"
|
r#"
|
||||||
when Num.log 1 is
|
when Num.logChecked 1 is
|
||||||
Ok val -> val
|
Ok val -> val
|
||||||
Err _ -> -1
|
Err _ -> -1
|
||||||
"#
|
"#
|
||||||
|
@ -521,7 +519,7 @@ fn f64_sqrt_zero() {
|
||||||
assert_evals_to!(
|
assert_evals_to!(
|
||||||
indoc!(
|
indoc!(
|
||||||
r#"
|
r#"
|
||||||
when Num.sqrt 0 is
|
when Num.sqrtChecked 0 is
|
||||||
Ok val -> val
|
Ok val -> val
|
||||||
Err _ -> -1
|
Err _ -> -1
|
||||||
"#
|
"#
|
||||||
|
@ -533,11 +531,11 @@ fn f64_sqrt_zero() {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
#[cfg(any(feature = "gen-llvm"))]
|
#[cfg(any(feature = "gen-llvm"))]
|
||||||
fn f64_sqrt_negative() {
|
fn f64_sqrt_checked_negative() {
|
||||||
assert_evals_to!(
|
assert_evals_to!(
|
||||||
indoc!(
|
indoc!(
|
||||||
r#"
|
r#"
|
||||||
when Num.sqrt -1 is
|
when Num.sqrtChecked -1 is
|
||||||
Err _ -> 42
|
Err _ -> 42
|
||||||
Ok val -> val
|
Ok val -> val
|
||||||
"#
|
"#
|
||||||
|
@ -549,11 +547,11 @@ fn f64_sqrt_negative() {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
#[cfg(any(feature = "gen-llvm"))]
|
#[cfg(any(feature = "gen-llvm"))]
|
||||||
fn f64_log_zero() {
|
fn f64_log_checked_zero() {
|
||||||
assert_evals_to!(
|
assert_evals_to!(
|
||||||
indoc!(
|
indoc!(
|
||||||
r#"
|
r#"
|
||||||
when Num.log 0 is
|
when Num.logChecked 0 is
|
||||||
Err _ -> 42
|
Err _ -> 42
|
||||||
Ok val -> val
|
Ok val -> val
|
||||||
"#
|
"#
|
||||||
|
@ -569,13 +567,12 @@ fn f64_log_negative() {
|
||||||
assert_evals_to!(
|
assert_evals_to!(
|
||||||
indoc!(
|
indoc!(
|
||||||
r#"
|
r#"
|
||||||
when Num.log -1 is
|
Num.log -1
|
||||||
Err _ -> 42
|
|
||||||
Ok val -> val
|
|
||||||
"#
|
"#
|
||||||
),
|
),
|
||||||
42.0,
|
true,
|
||||||
f64
|
f64,
|
||||||
|
|f: f64| f.is_nan()
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1082,9 +1079,7 @@ fn gen_rem_i64() {
|
||||||
assert_evals_to!(
|
assert_evals_to!(
|
||||||
indoc!(
|
indoc!(
|
||||||
r#"
|
r#"
|
||||||
when Num.rem 8 3 is
|
Num.rem 8 3
|
||||||
Ok val -> val
|
|
||||||
Err _ -> -1
|
|
||||||
"#
|
"#
|
||||||
),
|
),
|
||||||
2,
|
2,
|
||||||
|
@ -1094,11 +1089,11 @@ fn gen_rem_i64() {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
#[cfg(any(feature = "gen-llvm"))]
|
#[cfg(any(feature = "gen-llvm"))]
|
||||||
fn gen_rem_div_by_zero_i64() {
|
fn gen_rem_checked_div_by_zero_i64() {
|
||||||
assert_evals_to!(
|
assert_evals_to!(
|
||||||
indoc!(
|
indoc!(
|
||||||
r#"
|
r#"
|
||||||
when Num.rem 8 0 is
|
when Num.remChecked 8 0 is
|
||||||
Err DivByZero -> 4
|
Err DivByZero -> 4
|
||||||
Ok _ -> -23
|
Ok _ -> -23
|
||||||
"#
|
"#
|
||||||
|
|
|
@ -593,6 +593,27 @@ fn top_level_constant() {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
#[ignore]
|
||||||
|
#[cfg(any(feature = "gen-llvm", feature = "gen-dev", feature = "gen-wasm"))]
|
||||||
|
fn top_level_destructure() {
|
||||||
|
assert_evals_to!(
|
||||||
|
indoc!(
|
||||||
|
r#"
|
||||||
|
app "test" provides [ main ] to "./platform"
|
||||||
|
|
||||||
|
{a, b} = { a: 1, b: 2 }
|
||||||
|
|
||||||
|
main =
|
||||||
|
|
||||||
|
a + b
|
||||||
|
"#
|
||||||
|
),
|
||||||
|
3,
|
||||||
|
i64
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
#[cfg(any(feature = "gen-llvm"))]
|
#[cfg(any(feature = "gen-llvm"))]
|
||||||
fn linked_list_len_0() {
|
fn linked_list_len_0() {
|
||||||
|
@ -1113,7 +1134,7 @@ fn io_poc_effect() {
|
||||||
r#"
|
r#"
|
||||||
app "test" provides [ main ] to "./platform"
|
app "test" provides [ main ] to "./platform"
|
||||||
|
|
||||||
Effect a : [ @Effect ({} -> a) ]
|
Effect a := {} -> a
|
||||||
|
|
||||||
succeed : a -> Effect a
|
succeed : a -> Effect a
|
||||||
succeed = \x -> @Effect \{} -> x
|
succeed = \x -> @Effect \{} -> x
|
||||||
|
@ -1172,7 +1193,7 @@ fn return_wrapped_function_pointer() {
|
||||||
r#"
|
r#"
|
||||||
app "test" provides [ main ] to "./platform"
|
app "test" provides [ main ] to "./platform"
|
||||||
|
|
||||||
Effect a : [ @Effect ({} -> a) ]
|
Effect a := {} -> a
|
||||||
|
|
||||||
foo : Effect {}
|
foo : Effect {}
|
||||||
foo = @Effect \{} -> {}
|
foo = @Effect \{} -> {}
|
||||||
|
@ -1217,7 +1238,7 @@ fn return_wrapped_closure() {
|
||||||
r#"
|
r#"
|
||||||
app "test" provides [ main ] to "./platform"
|
app "test" provides [ main ] to "./platform"
|
||||||
|
|
||||||
Effect a : [ @Effect ({} -> a) ]
|
Effect a := {} -> a
|
||||||
|
|
||||||
foo : Effect {}
|
foo : Effect {}
|
||||||
foo =
|
foo =
|
||||||
|
@ -1843,7 +1864,7 @@ fn task_always_twice() {
|
||||||
r#"
|
r#"
|
||||||
app "test" provides [ main ] to "./platform"
|
app "test" provides [ main ] to "./platform"
|
||||||
|
|
||||||
Effect a : [ @Effect ({} -> a) ]
|
Effect a := {} -> a
|
||||||
|
|
||||||
effectAlways : a -> Effect a
|
effectAlways : a -> Effect a
|
||||||
effectAlways = \x ->
|
effectAlways = \x ->
|
||||||
|
@ -1888,7 +1909,7 @@ fn wildcard_rigid() {
|
||||||
r#"
|
r#"
|
||||||
app "test" provides [ main ] to "./platform"
|
app "test" provides [ main ] to "./platform"
|
||||||
|
|
||||||
Effect a : [ @Effect ({} -> a) ]
|
Effect a := {} -> a
|
||||||
|
|
||||||
Task a err : Effect (Result a err)
|
Task a err : Effect (Result a err)
|
||||||
|
|
||||||
|
@ -1918,7 +1939,7 @@ fn alias_of_alias_with_type_arguments() {
|
||||||
r#"
|
r#"
|
||||||
app "test" provides [ main ] to "./platform"
|
app "test" provides [ main ] to "./platform"
|
||||||
|
|
||||||
Effect a : [ @Effect a ]
|
Effect a := a
|
||||||
|
|
||||||
Task a err : Effect (Result a err)
|
Task a err : Effect (Result a err)
|
||||||
|
|
||||||
|
@ -1948,7 +1969,7 @@ fn todo_bad_error_message() {
|
||||||
r#"
|
r#"
|
||||||
app "test" provides [ main ] to "./platform"
|
app "test" provides [ main ] to "./platform"
|
||||||
|
|
||||||
Effect a : [ @Effect ({} -> a) ]
|
Effect a := {} -> a
|
||||||
|
|
||||||
effectAlways : a -> Effect a
|
effectAlways : a -> Effect a
|
||||||
effectAlways = \x ->
|
effectAlways = \x ->
|
||||||
|
@ -2972,6 +2993,7 @@ fn mix_function_and_closure_level_of_indirection() {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
#[cfg(any(feature = "gen-llvm"))]
|
#[cfg(any(feature = "gen-llvm"))]
|
||||||
|
#[cfg_attr(debug_assertions, ignore)] // this test stack-overflows the compiler in debug mode
|
||||||
fn do_pass_bool_byte_closure_layout() {
|
fn do_pass_bool_byte_closure_layout() {
|
||||||
// see https://github.com/rtfeldman/roc/pull/1706
|
// see https://github.com/rtfeldman/roc/pull/1706
|
||||||
// the distinction is actually important, dropping that info means some functions just get
|
// the distinction is actually important, dropping that info means some functions just get
|
||||||
|
@ -3079,7 +3101,7 @@ fn nested_rigid_alias() {
|
||||||
r#"
|
r#"
|
||||||
app "test" provides [ main ] to "./platform"
|
app "test" provides [ main ] to "./platform"
|
||||||
|
|
||||||
Identity a : [ @Identity a ]
|
Identity a := a
|
||||||
|
|
||||||
foo : Identity a -> Identity a
|
foo : Identity a -> Identity a
|
||||||
foo = \list ->
|
foo = \list ->
|
||||||
|
@ -3106,15 +3128,15 @@ fn nested_rigid_tag_union() {
|
||||||
r#"
|
r#"
|
||||||
app "test" provides [ main ] to "./platform"
|
app "test" provides [ main ] to "./platform"
|
||||||
|
|
||||||
foo : [ @Identity a ] -> [ @Identity a ]
|
foo : [ Identity a ] -> [ Identity a ]
|
||||||
foo = \list ->
|
foo = \list ->
|
||||||
p2 : [ @Identity a ]
|
p2 : [ Identity a ]
|
||||||
p2 = list
|
p2 = list
|
||||||
|
|
||||||
p2
|
p2
|
||||||
|
|
||||||
main =
|
main =
|
||||||
when foo (@Identity "foo") is
|
when foo (Identity "foo") is
|
||||||
_ -> "hello world"
|
_ -> "hello world"
|
||||||
"#
|
"#
|
||||||
),
|
),
|
||||||
|
@ -3200,7 +3222,7 @@ fn recursively_build_effect() {
|
||||||
always {} |> after \_ -> nestHelp (m - 1)
|
always {} |> after \_ -> nestHelp (m - 1)
|
||||||
|
|
||||||
|
|
||||||
XEffect a : [ @XEffect ({} -> a) ]
|
XEffect a := {} -> a
|
||||||
|
|
||||||
always : a -> XEffect a
|
always : a -> XEffect a
|
||||||
always = \x -> @XEffect (\{} -> x)
|
always = \x -> @XEffect (\{} -> x)
|
||||||
|
|
|
@ -1055,10 +1055,10 @@ fn phantom_polymorphic() {
|
||||||
r"#
|
r"#
|
||||||
Point coordinate : [ Point coordinate I64 I64 ]
|
Point coordinate : [ Point coordinate I64 I64 ]
|
||||||
|
|
||||||
World : [ @World ]
|
World := {}
|
||||||
|
|
||||||
zero : Point World
|
zero : Point World
|
||||||
zero = Point @World 0 0
|
zero = Point (@World {}) 0 0
|
||||||
|
|
||||||
add : Point a -> Point a
|
add : Point a -> Point a
|
||||||
add = \(Point c x y) -> (Point c x y)
|
add = \(Point c x y) -> (Point c x y)
|
||||||
|
@ -1580,3 +1580,28 @@ fn issue_2725_alias_polymorphic_lambda() {
|
||||||
i64
|
i64
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
|
||||||
|
fn opaque_assign_to_symbol() {
|
||||||
|
assert_evals_to!(
|
||||||
|
indoc!(
|
||||||
|
r#"
|
||||||
|
app "test" provides [ out ] to "./platform"
|
||||||
|
|
||||||
|
Variable := U8
|
||||||
|
|
||||||
|
fromUtf8 : U8 -> Result Variable [ InvalidVariableUtf8 ]
|
||||||
|
fromUtf8 = \char ->
|
||||||
|
Ok (@Variable char)
|
||||||
|
|
||||||
|
out =
|
||||||
|
when fromUtf8 98 is
|
||||||
|
Ok (@Variable n) -> n
|
||||||
|
_ -> 1
|
||||||
|
"#
|
||||||
|
),
|
||||||
|
98,
|
||||||
|
u8
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue