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>
|
||||
Sean Hagstrom <sean@seanhagstrom.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]]
|
||||
name = "regex"
|
||||
version = "1.5.4"
|
||||
version = "1.5.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d07a8629359eb56f1e2fb1652bb04212c072a87ba68546a04065d525673ac461"
|
||||
checksum = "1a11647b6b25ff05a515cb92c365cec08801e83423a235b51e231e1808747286"
|
||||
dependencies = [
|
||||
"aho-corasick",
|
||||
"memchr",
|
||||
|
@ -3955,7 +3955,9 @@ dependencies = [
|
|||
"arrayvec 0.7.2",
|
||||
"bumpalo",
|
||||
"indoc",
|
||||
"lazy_static",
|
||||
"pretty_assertions",
|
||||
"regex",
|
||||
"roc_builtins",
|
||||
"roc_can",
|
||||
"roc_collections",
|
||||
|
@ -4489,9 +4491,9 @@ checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369"
|
|||
|
||||
[[package]]
|
||||
name = "target-lexicon"
|
||||
version = "0.12.2"
|
||||
version = "0.12.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d9bffcddbc2458fa3e6058414599e3c838a022abae82e5c67b4f7f80298d5bff"
|
||||
checksum = "d7fa7e55043acb85fca6b3c01485a2eeb6b69c5d21002e273c79e465f43b7ac1"
|
||||
|
||||
[[package]]
|
||||
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
|
||||
app "hello"
|
||||
packages { pf: "examples/cli/platform" }
|
||||
packages { pf: "examples/interactive/cli-platform" }
|
||||
imports [ pf.Stdout ]
|
||||
provides [ main ] to pf
|
||||
|
||||
main = Stdout.line "I'm a Roc application!"
|
||||
```
|
||||
|
||||
> **NOTE:** This assumes you've put Hello.roc in the root directory of the
|
||||
> Roc 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
|
||||
> that source code. In the future, Roc will have the tutorial built in, and this
|
||||
> aside will no longer be necessary!
|
||||
> **NOTE:** This assumes you've put Hello.roc in the root directory of the Roc
|
||||
> source code. If you'd like to put it somewhere else, you'll need to replace
|
||||
> `"examples/interactive/cli-platform"` with the path to the
|
||||
> `examples/interactive/cli-platform` folder in that source code. In the future,
|
||||
> Roc will have the tutorial built in, and this aside will no longer be
|
||||
> necessary!
|
||||
|
||||
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,
|
||||
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
|
||||
|
||||
[ 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
|
||||
app "hello"
|
||||
packages { pf: "examples/cli/platform" }
|
||||
packages { pf: "examples/interactive/cli-platform" }
|
||||
imports [ pf.Stdout ]
|
||||
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:
|
||||
|
||||
```coffee
|
||||
packages { pf: "examples/cli/platform" }
|
||||
packages { pf: "examples/interactive/cli-platform" }
|
||||
imports [ pf.Stdout ]
|
||||
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.
|
||||
|
||||
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`
|
||||
module comes from the `pf` package.
|
||||
|
||||
Since `pf` was the name we chose for the `examples/cli/platform` package
|
||||
(when we wrote `packages { pf: "examples/cli/platform" }`), this `imports` line
|
||||
tells the Roc compiler that when we call `Stdout.line`, it should look for that
|
||||
`line` function in the `Stdout` module of the `examples/cli/platform` package.
|
||||
Since `pf` was the name we chose for the `examples/interactive/cli-platform`
|
||||
package (when we wrote `packages { pf: "examples/interactive/cli-platform" }`),
|
||||
this `imports` line tells the Roc compiler that when we call `Stdout.line`, it
|
||||
should look for that `line` function in the `Stdout` module of the
|
||||
`examples/interactive/cli-platform` package.
|
||||
|
||||
# Building a Command-Line Interface (CLI)
|
||||
|
||||
## Tasks
|
||||
|
||||
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:
|
||||
|
||||
|
@ -1306,7 +1326,7 @@ First, let's do a basic "Hello World" using the tutorial app.
|
|||
|
||||
```coffee
|
||||
app "cli-tutorial"
|
||||
packages { pf: "examples/cli/platform" }
|
||||
packages { pf: "examples/interactive/cli-platform" }
|
||||
imports [ pf.Stdout ]
|
||||
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
|
||||
app "cli-tutorial"
|
||||
packages { pf: "examples/cli/platform" }
|
||||
packages { pf: "examples/interactive/cli-platform" }
|
||||
imports [ pf.Stdout, pf.Stdin, pf.Task ]
|
||||
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
|
||||
app "cli-tutorial"
|
||||
packages { pf: "examples/cli/platform" }
|
||||
packages { pf: "examples/interactive/cli-platform" }
|
||||
imports [ pf.Stdout, pf.Stdin, pf.Task.{ await } ]
|
||||
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.pow a b` |
|
||||
| `a % b` | `Num.rem a b` |
|
||||
| `a %% b` | `Num.mod a b` |
|
||||
| `a >> b` | `Num.shr a b` |
|
||||
| `a << b` | `Num.shl a b` |
|
||||
| `-a` | `Num.neg a` |
|
||||
|
|
|
@ -264,25 +264,6 @@ pub fn constrain_expr<'a>(
|
|||
*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 {
|
||||
args,
|
||||
expr_var,
|
||||
|
@ -678,24 +659,28 @@ pub fn constrain_expr<'a>(
|
|||
for (index, when_branch_id) in branches.iter_node_ids().enumerate() {
|
||||
let when_branch = env.pool.get(when_branch_id);
|
||||
|
||||
let pattern_region = region;
|
||||
// let pattern_region = Region::across_all(
|
||||
// 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(
|
||||
arena,
|
||||
env,
|
||||
// TODO: when_branch.value.region,
|
||||
region,
|
||||
when_branch,
|
||||
PExpected::ForReason(
|
||||
PReason::WhenMatch {
|
||||
index: HumanIndex::zero_based(index),
|
||||
},
|
||||
cond_type.shallow_clone(),
|
||||
pattern_region,
|
||||
),
|
||||
pattern_expected,
|
||||
Expected::FromAnnotation(
|
||||
name.clone(),
|
||||
*arity,
|
||||
|
@ -722,22 +707,26 @@ pub fn constrain_expr<'a>(
|
|||
for (index, when_branch_id) in branches.iter_node_ids().enumerate() {
|
||||
let when_branch = env.pool.get(when_branch_id);
|
||||
|
||||
let pattern_region = region;
|
||||
// let pattern_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(
|
||||
arena,
|
||||
env,
|
||||
region,
|
||||
when_branch,
|
||||
PExpected::ForReason(
|
||||
PReason::WhenMatch {
|
||||
index: HumanIndex::zero_based(index),
|
||||
},
|
||||
cond_type.shallow_clone(),
|
||||
pattern_region,
|
||||
),
|
||||
pattern_expected,
|
||||
Expected::ForReason(
|
||||
Reason::WhenBranch {
|
||||
index: HumanIndex::zero_based(index),
|
||||
|
@ -1296,7 +1285,7 @@ fn constrain_when_branch<'a>(
|
|||
env: &mut Env,
|
||||
region: Region,
|
||||
when_branch: &WhenBranch,
|
||||
pattern_expected: PExpected<Type2>,
|
||||
pattern_expected: impl Fn(HumanIndex, Region) -> PExpected<Type2>,
|
||||
expr_expected: Expected<Type2>,
|
||||
) -> Constraint<'a> {
|
||||
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,
|
||||
// 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_expected = pattern_expected(
|
||||
HumanIndex::zero_based(sub_pattern),
|
||||
// TODO: use the proper subpattern region. Not available to us right now.
|
||||
region,
|
||||
);
|
||||
|
||||
constrain_pattern(
|
||||
arena,
|
||||
env,
|
||||
pattern,
|
||||
// loc_pattern.region,
|
||||
region,
|
||||
pattern_expected.shallow_clone(),
|
||||
pattern_expected,
|
||||
&mut state,
|
||||
true,
|
||||
);
|
||||
|
@ -1617,27 +1612,6 @@ pub fn constrain_pattern<'a>(
|
|||
} => {
|
||||
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(
|
||||
arena,
|
||||
env,
|
||||
|
@ -1881,19 +1855,9 @@ fn num_float(pool: &mut Pool, range: TypeId) -> Type2 {
|
|||
fn num_floatingpoint(pool: &mut Pool, range: TypeId) -> Type2 {
|
||||
let range_type = pool.get(range);
|
||||
|
||||
let alias_content = Type2::TagUnion(
|
||||
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),
|
||||
);
|
||||
let alias_content = range_type.shallow_clone();
|
||||
|
||||
Type2::Alias(
|
||||
Type2::Opaque(
|
||||
Symbol::NUM_FLOATINGPOINT,
|
||||
PoolVec::new(vec![(PoolStr::new("range", pool), range)].into_iter(), pool),
|
||||
pool.add(alias_content),
|
||||
|
@ -1917,37 +1881,16 @@ fn num_int(pool: &mut Pool, range: TypeId) -> Type2 {
|
|||
|
||||
#[inline(always)]
|
||||
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(
|
||||
Symbol::NUM_SIGNED64,
|
||||
PoolVec::empty(pool),
|
||||
pool.add(alias_content),
|
||||
pool.add(Type2::EmptyTagUnion),
|
||||
)
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn num_unsigned32(pool: &mut Pool) -> Type2 {
|
||||
let alias_content = Type2::TagUnion(
|
||||
PoolVec::new(
|
||||
std::iter::once((
|
||||
TagName::Private(Symbol::NUM_UNSIGNED32),
|
||||
PoolVec::empty(pool),
|
||||
)),
|
||||
pool,
|
||||
),
|
||||
pool.add(Type2::EmptyTagUnion),
|
||||
);
|
||||
let alias_content = Type2::EmptyTagUnion;
|
||||
|
||||
Type2::Alias(
|
||||
Symbol::NUM_UNSIGNED32,
|
||||
|
@ -1960,19 +1903,9 @@ fn num_unsigned32(pool: &mut Pool) -> Type2 {
|
|||
fn _num_integer(pool: &mut Pool, range: TypeId) -> Type2 {
|
||||
let range_type = pool.get(range);
|
||||
|
||||
let alias_content = Type2::TagUnion(
|
||||
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),
|
||||
);
|
||||
let alias_content = range_type.shallow_clone();
|
||||
|
||||
Type2::Alias(
|
||||
Type2::Opaque(
|
||||
Symbol::NUM_INTEGER,
|
||||
PoolVec::new(vec![(PoolStr::new("range", pool), range)].into_iter(), pool),
|
||||
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 {
|
||||
let range_type = pool.get(type_id);
|
||||
|
||||
let alias_content = Type2::TagUnion(
|
||||
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),
|
||||
);
|
||||
let alias_content = range_type.shallow_clone();
|
||||
|
||||
Type2::Alias(
|
||||
Type2::Opaque(
|
||||
Symbol::NUM_NUM,
|
||||
PoolVec::new(
|
||||
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]
|
||||
fn constrain_call_and_accessor() {
|
||||
infer_eq(
|
||||
|
|
|
@ -154,12 +154,6 @@ pub enum Expr2 {
|
|||
ext_var: Variable, // 4B
|
||||
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
|
||||
|
||||
// Compiles, but will crash if reached
|
||||
|
|
|
@ -185,20 +185,6 @@ pub fn expr_to_expr2<'a>(
|
|||
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 {
|
||||
fields,
|
||||
|
@ -568,17 +554,6 @@ pub fn expr_to_expr2<'a>(
|
|||
name,
|
||||
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).
|
||||
let fn_expr_id = env.add(fn_expr, fn_region);
|
||||
|
|
|
@ -47,12 +47,6 @@ pub enum Pattern2 {
|
|||
tag_name: PoolStr, // 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 {
|
||||
whole_var: Variable, // 4B
|
||||
ext_var: Variable, // 4B
|
||||
|
@ -280,17 +274,6 @@ pub fn to_pattern2<'a>(
|
|||
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!(),
|
||||
|
||||
|
@ -319,16 +302,6 @@ pub fn to_pattern2<'a>(
|
|||
tag_name: PoolStr::new(name, env.pool),
|
||||
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"),
|
||||
}
|
||||
}
|
||||
|
@ -506,7 +479,7 @@ pub fn symbols_from_pattern(pool: &Pool, initial: &Pattern2) -> Vec<Symbol> {
|
|||
symbols.push(*symbol);
|
||||
}
|
||||
|
||||
GlobalTag { arguments, .. } | PrivateTag { arguments, .. } => {
|
||||
GlobalTag { arguments, .. } => {
|
||||
for (_, pat_id) in arguments.iter(pool) {
|
||||
let pat = pool.get(*pat_id);
|
||||
stack.push(pat);
|
||||
|
@ -567,7 +540,7 @@ pub fn symbols_and_variables_from_pattern(
|
|||
symbols.push((*symbol, variable));
|
||||
}
|
||||
|
||||
GlobalTag { arguments, .. } | PrivateTag { arguments, .. } => {
|
||||
GlobalTag { arguments, .. } => {
|
||||
for (var, pat_id) in arguments.iter(pool) {
|
||||
let pat = pool.get(*pat_id);
|
||||
stack.push((*var, pat));
|
||||
|
|
|
@ -24,6 +24,7 @@ pub enum Type2 {
|
|||
Variable(Variable), // 4B
|
||||
|
||||
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
|
||||
|
||||
// 24B
|
||||
|
@ -74,6 +75,9 @@ impl ShallowClone for Type2 {
|
|||
Self::Alias(symbol, args, alias_type_id) => {
|
||||
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::Function(args, closure_type_id, ret_type_id) => Self::Function(
|
||||
args.shallow_clone(),
|
||||
|
@ -101,7 +105,7 @@ impl Type2 {
|
|||
Variable(v) => {
|
||||
result.insert(*v);
|
||||
}
|
||||
Alias(_, _, actual) | AsAlias(_, _, actual) => {
|
||||
Alias(_, _, actual) | AsAlias(_, _, actual) | Opaque(_, _, actual) => {
|
||||
stack.push(pool.get(*actual));
|
||||
}
|
||||
HostExposedAlias {
|
||||
|
@ -702,21 +706,6 @@ fn can_tags<'a>(
|
|||
|
||||
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, _) => {
|
||||
// check the nested tag instead
|
||||
tag = nested;
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
use bumpalo::Bump;
|
||||
use roc_can::expected::{Expected, PExpected};
|
||||
use roc_collections::all::{BumpMap, BumpMapDefault, MutMap};
|
||||
use roc_error_macros::internal_error;
|
||||
use roc_module::ident::TagName;
|
||||
use roc_module::symbol::Symbol;
|
||||
use roc_region::all::{Loc, Region};
|
||||
|
@ -868,7 +869,7 @@ fn type_to_variable<'a>(
|
|||
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!
|
||||
// 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
|
||||
|
@ -910,8 +911,12 @@ fn type_to_variable<'a>(
|
|||
|
||||
let alias_var = type_to_variable(arena, mempool, subs, rank, pools, cached, alias_type);
|
||||
|
||||
// TODO(opaques): take opaques into account
|
||||
let content = Content::Alias(*symbol, arg_vars, alias_var, AliasKind::Structural);
|
||||
let kind = match typ {
|
||||
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);
|
||||
|
||||
|
|
|
@ -66,7 +66,7 @@ const_format = "0.2.22"
|
|||
bumpalo = { version = "3.8.0", features = ["collections"] }
|
||||
mimalloc = { version = "0.1.26", default-features = false }
|
||||
|
||||
target-lexicon = "0.12.2"
|
||||
target-lexicon = "0.12.3"
|
||||
tempfile = "3.2.0"
|
||||
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::Underscore(a) => Expr::Underscore(a),
|
||||
Expr::GlobalTag(a) => Expr::GlobalTag(a),
|
||||
Expr::PrivateTag(a) => Expr::PrivateTag(a),
|
||||
Expr::OpaqueRef(a) => Expr::OpaqueRef(a),
|
||||
Expr::Closure(a, b) => Expr::Closure(
|
||||
arena.alloc(a.remove_spaces(arena)),
|
||||
|
@ -670,7 +669,6 @@ impl<'a> RemoveSpaces<'a> for Pattern<'a> {
|
|||
match *self {
|
||||
Pattern::Identifier(a) => Pattern::Identifier(a),
|
||||
Pattern::GlobalTag(a) => Pattern::GlobalTag(a),
|
||||
Pattern::PrivateTag(a) => Pattern::PrivateTag(a),
|
||||
Pattern::OpaqueRef(a) => Pattern::OpaqueRef(a),
|
||||
Pattern::Apply(a, b) => Pattern::Apply(
|
||||
arena.alloc(a.remove_spaces(arena)),
|
||||
|
@ -757,10 +755,6 @@ impl<'a> RemoveSpaces<'a> for Tag<'a> {
|
|||
name: name.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::SpaceBefore(a, _) => a.remove_spaces(arena),
|
||||
Tag::SpaceAfter(a, _) => a.remove_spaces(arena),
|
||||
|
|
|
@ -256,8 +256,8 @@ mod cli_run {
|
|||
return;
|
||||
}
|
||||
}
|
||||
"hello-gui" => {
|
||||
// Since this one requires opening a window, we do `roc build` on it but don't run it.
|
||||
"hello-gui" | "breakout" => {
|
||||
// Since these require opening a window, we do `roc build` on them but don't run them.
|
||||
build_example(&file_name, &["--optimize"]);
|
||||
|
||||
return;
|
||||
|
@ -394,6 +394,14 @@ mod cli_run {
|
|||
expected_ending: "",
|
||||
use_valgrind: false,
|
||||
},
|
||||
breakout:"breakout" => Example {
|
||||
filename: "breakout.roc",
|
||||
executable_filename: "breakout",
|
||||
stdin: &[],
|
||||
input_file: None,
|
||||
expected_ending: "",
|
||||
use_valgrind: false,
|
||||
},
|
||||
quicksort:"algorithms" => Example {
|
||||
filename: "quicksort.roc",
|
||||
executable_filename: "quicksort",
|
||||
|
|
|
@ -167,6 +167,20 @@ For a more detailed understanding of the compilation phases, see the `Phase`, `B
|
|||
|
||||
## 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
|
||||
|
||||
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();
|
||||
|
||||
// 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
|
||||
// let argument = builder.add_unknown_with(block, &[], argument_type)?;
|
||||
|
@ -308,7 +309,11 @@ fn build_entry_point(
|
|||
|
||||
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)?;
|
||||
|
||||
|
@ -352,8 +357,9 @@ fn proc_spec<'a>(proc: &Proc<'a>) -> Result<(FuncDef, MutSet<UnionLayout<'a>>)>
|
|||
let arg_type_id = layout_spec(
|
||||
&mut builder,
|
||||
&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)?;
|
||||
|
||||
|
@ -457,10 +463,14 @@ fn stmt_spec<'a>(
|
|||
let mut type_ids = Vec::new();
|
||||
|
||||
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)?;
|
||||
|
||||
|
@ -500,14 +510,14 @@ fn stmt_spec<'a>(
|
|||
builder.add_sub_block(block, BlockExpr(cont_block, cont_value_id))
|
||||
}
|
||||
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 jpid = env.join_points[id];
|
||||
builder.add_jump(block, jpid, argument, ret_type_id)
|
||||
}
|
||||
RuntimeError(_) => {
|
||||
let type_id = layout_spec(builder, layout)?;
|
||||
let type_id = layout_spec(builder, layout, &WhenRecursive::Unreachable)?;
|
||||
|
||||
builder.add_terminate(block, type_id)
|
||||
}
|
||||
|
@ -556,11 +566,15 @@ fn build_recursive_tuple_type(
|
|||
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();
|
||||
|
||||
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)
|
||||
|
@ -691,7 +705,7 @@ fn call_spec(
|
|||
.map(|symbol| env.symbols[symbol])
|
||||
.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)
|
||||
}
|
||||
|
@ -761,7 +775,8 @@ fn call_spec(
|
|||
};
|
||||
|
||||
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;
|
||||
|
||||
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_type = layout_spec(builder, &state_layout)?;
|
||||
let state_type =
|
||||
layout_spec(builder, &state_layout, &WhenRecursive::Unreachable)?;
|
||||
let init_state = state;
|
||||
|
||||
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_type = layout_spec(builder, &state_layout)?;
|
||||
let state_type =
|
||||
layout_spec(builder, &state_layout, &WhenRecursive::Unreachable)?;
|
||||
let init_state = state;
|
||||
|
||||
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)
|
||||
};
|
||||
|
||||
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_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)?;
|
||||
|
||||
|
@ -851,10 +870,12 @@ fn call_spec(
|
|||
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_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)?;
|
||||
|
||||
|
@ -879,7 +900,8 @@ fn call_spec(
|
|||
};
|
||||
|
||||
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;
|
||||
|
||||
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)
|
||||
};
|
||||
|
||||
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_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)?;
|
||||
|
||||
|
@ -936,10 +960,12 @@ fn call_spec(
|
|||
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_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)?;
|
||||
|
||||
|
@ -975,10 +1001,12 @@ fn call_spec(
|
|||
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_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)?;
|
||||
|
||||
|
@ -1010,7 +1038,8 @@ fn call_spec(
|
|||
};
|
||||
|
||||
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;
|
||||
|
||||
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 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)
|
||||
}
|
||||
|
@ -1108,7 +1139,8 @@ fn call_spec(
|
|||
};
|
||||
|
||||
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)?;
|
||||
|
||||
|
@ -1127,7 +1159,8 @@ fn call_spec(
|
|||
};
|
||||
|
||||
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)?;
|
||||
|
||||
|
@ -1139,7 +1172,8 @@ fn call_spec(
|
|||
// ListFindUnsafe returns { value: v, found: Bool=Int1 }
|
||||
let output_layouts = vec![argument_layouts[0], Layout::Builtin(Builtin::Bool)];
|
||||
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 bag = builder.add_get_tuple_field(block, list, LIST_BAG_INDEX)?;
|
||||
|
@ -1201,7 +1235,7 @@ fn lowlevel_spec(
|
|||
) -> Result<ValueId> {
|
||||
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 update_mode_var = UpdateModeVar(&mode);
|
||||
|
||||
|
@ -1323,8 +1357,8 @@ fn lowlevel_spec(
|
|||
}
|
||||
DictEmpty => match layout {
|
||||
Layout::Builtin(Builtin::Dict(key_layout, value_layout)) => {
|
||||
let key_id = layout_spec(builder, key_layout)?;
|
||||
let value_id = layout_spec(builder, value_layout)?;
|
||||
let key_id = layout_spec(builder, key_layout, &WhenRecursive::Unreachable)?;
|
||||
let value_id = layout_spec(builder, value_layout, &WhenRecursive::Unreachable)?;
|
||||
new_dict(builder, block, key_id, value_id)
|
||||
}
|
||||
_ => unreachable!("empty array does not have a list layout"),
|
||||
|
@ -1367,7 +1401,7 @@ fn lowlevel_spec(
|
|||
// TODO overly pessimstic
|
||||
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)
|
||||
}
|
||||
|
@ -1478,7 +1512,8 @@ fn expr_spec<'a>(
|
|||
|
||||
let value_id = match tag_layout {
|
||||
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)?;
|
||||
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)
|
||||
}
|
||||
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)?;
|
||||
|
||||
|
@ -1619,19 +1654,19 @@ fn expr_spec<'a>(
|
|||
|
||||
EmptyArray => match 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)
|
||||
}
|
||||
_ => unreachable!("empty array does not have a list layout"),
|
||||
},
|
||||
Reset { symbol, .. } => {
|
||||
let type_id = layout_spec(builder, layout)?;
|
||||
let type_id = layout_spec(builder, layout, &WhenRecursive::Unreachable)?;
|
||||
let value_id = env.symbols[symbol];
|
||||
|
||||
builder.add_unknown_with(block, &[value_id], type_id)
|
||||
}
|
||||
RuntimeErrorFunction(_) => {
|
||||
let type_id = layout_spec(builder, layout)?;
|
||||
let type_id = layout_spec(builder, layout, &WhenRecursive::Unreachable)?;
|
||||
|
||||
builder.add_terminate(block, type_id)
|
||||
}
|
||||
|
@ -1658,18 +1693,24 @@ fn literal_spec(
|
|||
}
|
||||
}
|
||||
|
||||
fn layout_spec(builder: &mut impl TypeContext, layout: &Layout) -> Result<TypeId> {
|
||||
layout_spec_help(builder, layout, &WhenRecursive::Unreachable)
|
||||
fn layout_spec(
|
||||
builder: &mut impl TypeContext,
|
||||
layout: &Layout,
|
||||
when_recursive: &WhenRecursive,
|
||||
) -> Result<TypeId> {
|
||||
layout_spec_help(builder, layout, when_recursive)
|
||||
}
|
||||
|
||||
fn non_recursive_variant_types(
|
||||
builder: &mut impl TypeContext,
|
||||
tags: &[&[Layout]],
|
||||
// If there is a recursive pointer latent within this layout, coming from a containing layout.
|
||||
when_recursive: &WhenRecursive,
|
||||
) -> Result<Vec<TypeId>> {
|
||||
let mut result = Vec::with_capacity(tags.len());
|
||||
|
||||
for tag in tags.iter() {
|
||||
result.push(build_tuple_type(builder, tag)?);
|
||||
result.push(build_tuple_type(builder, tag, when_recursive)?);
|
||||
}
|
||||
|
||||
Ok(result)
|
||||
|
@ -1701,7 +1742,7 @@ fn layout_spec_help(
|
|||
builder.add_tuple_type(&[])
|
||||
}
|
||||
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)
|
||||
}
|
||||
UnionLayout::Recursive(_)
|
||||
|
|
|
@ -30,7 +30,7 @@ bumpalo = { version = "3.8.0", features = ["collections"] }
|
|||
libloading = "0.7.1"
|
||||
tempfile = "3.2.0"
|
||||
inkwell = { path = "../../vendor/inkwell", optional = true }
|
||||
target-lexicon = "0.12.2"
|
||||
target-lexicon = "0.12.3"
|
||||
|
||||
[target.'cfg(target_os = "macos")'.dependencies]
|
||||
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
|
||||
|
||||
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.
|
||||
|
||||
|
|
|
@ -70,10 +70,9 @@ xor : Bool, Bool -> Bool
|
|||
## Structural equality works as follows:
|
||||
##
|
||||
## 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.
|
||||
## 3. Records are equal if all their fields 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.
|
||||
## 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*.
|
||||
## 2. 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. [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
|
||||
## accept arguments whose types contain functions.
|
||||
|
|
|
@ -93,7 +93,7 @@ interface Dict
|
|||
##
|
||||
## The [Dict.hasSameContents] function gives an alternative to `==` which ignores ordering
|
||||
## 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.
|
||||
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
|
||||
## * 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!
|
||||
List elem : [ @List elem ]
|
||||
List elem := [ List elem ]
|
||||
|
||||
## Initialize
|
||||
|
||||
|
|
|
@ -61,6 +61,7 @@ interface Num
|
|||
isPositive,
|
||||
isZero,
|
||||
log,
|
||||
logChecked,
|
||||
maxFloat,
|
||||
maxI8,
|
||||
maxU8,
|
||||
|
@ -88,6 +89,7 @@ interface Num
|
|||
pow,
|
||||
powInt,
|
||||
rem,
|
||||
remChecked,
|
||||
round,
|
||||
shiftLeftBy,
|
||||
shiftRightBy,
|
||||
|
@ -97,6 +99,7 @@ interface Num
|
|||
subChecked,
|
||||
subWrap,
|
||||
sqrt,
|
||||
sqrtChecked,
|
||||
tan,
|
||||
toI8,
|
||||
toI8Checked,
|
||||
|
@ -186,7 +189,7 @@ interface Num
|
|||
##
|
||||
## In practice, these are rarely needed. It's most common to write
|
||||
## number literals without any suffix.
|
||||
Num a : [ @Num a ]
|
||||
Num a := a
|
||||
|
||||
## A decimal number.
|
||||
##
|
||||
|
@ -220,7 +223,7 @@ Num a : [ @Num a ]
|
|||
## [Dec] typically takes slightly less time than [F64] to perform addition and
|
||||
## subtraction, but 10-20 times longer to perform multiplication and division.
|
||||
## [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.
|
||||
##
|
||||
|
@ -289,7 +292,7 @@ Dec : Float [ @Decimal128 ]
|
|||
## 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
|
||||
## 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.
|
||||
##
|
||||
|
@ -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.
|
||||
## * 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.
|
||||
Int size : Num [ @Integer size ]
|
||||
Int range : Num (Integer range)
|
||||
|
||||
## A signed 8-bit integer, ranging from -128 to 127
|
||||
I8 : Int [ @Signed8 ]
|
||||
U8 : Int [ @Unsigned8 ]
|
||||
I16 : Int [ @Signed16 ]
|
||||
U16 : Int [ @Unsigned16 ]
|
||||
I32 : Int [ @Signed32 ]
|
||||
U32 : Int [ @Unsigned32 ]
|
||||
I64 : Int [ @Signed64 ]
|
||||
U64 : Int [ @Unsigned64 ]
|
||||
I128 : Int [ @Signed128 ]
|
||||
U128 : Int [ @Unsigned128 ]
|
||||
I8 : Int Signed8
|
||||
U8 : Int Unsigned8
|
||||
I16 : Int Signed16
|
||||
U16 : Int Unsigned16
|
||||
I32 : Int Signed32
|
||||
U32 : Int Unsigned32
|
||||
I64 : Int Signed64
|
||||
U64 : Int Unsigned64
|
||||
I128 : Int Signed128
|
||||
U128 : Int Unsigned128
|
||||
|
||||
## 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
|
||||
|
@ -364,7 +367,7 @@ U128 : Int [ @Unsigned128 ]
|
|||
## 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
|
||||
## 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.
|
||||
##
|
||||
|
@ -440,7 +443,7 @@ Nat : Int [ @Natural ]
|
|||
##
|
||||
## 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.
|
||||
Int size : Num [ @Int size ]
|
||||
Int range : Num (Integer range)
|
||||
|
||||
## Convert
|
||||
|
||||
|
@ -802,27 +805,18 @@ toDec : Num * -> Dec
|
|||
## This is the same as the #// operator.
|
||||
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,
|
||||
## but if either number is negative, then modulo works differently.
|
||||
## `a % b` is shorthand for `Num.rem a b`.
|
||||
##
|
||||
## 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`.
|
||||
##
|
||||
## >>> 5 %% 7
|
||||
##
|
||||
## >>> Int.modFloor 5 7
|
||||
##
|
||||
## >>> -8 %% -3
|
||||
##
|
||||
## >>> Int.modFloor -8 -3
|
||||
#modFloor : Int a, Int a -> Result (Int a) [ DivByZero ]*
|
||||
## >>> Num.rem -8 -3
|
||||
rem : Int a, Int a -> Int a
|
||||
|
||||
|
||||
## Bitwise
|
||||
|
@ -1094,31 +1088,6 @@ atan : Float a -> Float a
|
|||
## >>> |> Num.div 2.0
|
||||
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].
|
||||
##
|
||||
## `
|
||||
|
@ -1314,7 +1283,7 @@ isInfinite : Float * -> Bool
|
|||
##
|
||||
## >>> 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 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
|
||||
## 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
|
||||
##
|
||||
|
|
|
@ -18,7 +18,7 @@ interface Set
|
|||
imports []
|
||||
|
||||
## A Set is an unordered collection of unique elements.
|
||||
Set elem : [ @Set elem ]
|
||||
Set elem := [ Set elem ]
|
||||
|
||||
## An empty set.
|
||||
empty : Set *
|
||||
|
|
|
@ -116,7 +116,7 @@ interface Str
|
|||
## It has many more tools than this module does!
|
||||
|
||||
## A [Unicode](https://unicode.org) text value.
|
||||
Str : [ @Str ]
|
||||
Str := [ Str ]
|
||||
|
||||
## Convert
|
||||
|
||||
|
|
|
@ -66,10 +66,13 @@ interface Num
|
|||
isPositive,
|
||||
isNegative,
|
||||
rem,
|
||||
remChecked,
|
||||
div,
|
||||
divChecked,
|
||||
sqrt,
|
||||
sqrtChecked,
|
||||
log,
|
||||
logChecked,
|
||||
round,
|
||||
ceiling,
|
||||
floor,
|
||||
|
@ -155,25 +158,25 @@ interface Num
|
|||
Bool.{ Bool }
|
||||
]
|
||||
|
||||
Num range : [ @Num range ]
|
||||
Num range := range
|
||||
Int range : Num (Integer range)
|
||||
Float range : Num (FloatingPoint range)
|
||||
|
||||
Signed128 : [ @Signed128 ]
|
||||
Signed64 : [ @Signed64 ]
|
||||
Signed32 : [ @Signed32 ]
|
||||
Signed16 : [ @Signed16 ]
|
||||
Signed8 : [ @Signed8 ]
|
||||
Signed128 := []
|
||||
Signed64 := []
|
||||
Signed32 := []
|
||||
Signed16 := []
|
||||
Signed8 := []
|
||||
|
||||
Unsigned128 : [ @Unsigned128 ]
|
||||
Unsigned64 : [ @Unsigned64 ]
|
||||
Unsigned32 : [ @Unsigned32 ]
|
||||
Unsigned16 : [ @Unsigned16 ]
|
||||
Unsigned8 : [ @Unsigned8 ]
|
||||
Unsigned128 := []
|
||||
Unsigned64 := []
|
||||
Unsigned32 := []
|
||||
Unsigned16 := []
|
||||
Unsigned8 := []
|
||||
|
||||
Natural : [ @Natural ]
|
||||
Natural := []
|
||||
|
||||
Integer range : [ @Integer range ]
|
||||
Integer range := range
|
||||
|
||||
I128 : Num (Integer Signed128)
|
||||
I64 : Num (Integer Signed64)
|
||||
|
@ -189,11 +192,11 @@ U8 : Num (Integer Unsigned8)
|
|||
|
||||
Nat : Num (Integer Natural)
|
||||
|
||||
Decimal : [ @Decimal ]
|
||||
Binary64 : [ @Binary64 ]
|
||||
Binary32 : [ @Binary32 ]
|
||||
Decimal := []
|
||||
Binary64 := []
|
||||
Binary32 := []
|
||||
|
||||
FloatingPoint range : [ @FloatingPoint range ]
|
||||
FloatingPoint range := range
|
||||
|
||||
F64 : Num (FloatingPoint Binary64)
|
||||
F32 : Num (FloatingPoint Binary32)
|
||||
|
@ -239,19 +242,22 @@ asin : Float a -> Float a
|
|||
acos : Float a -> Float a
|
||||
atan : Float a -> Float a
|
||||
|
||||
sqrt : Float a -> Result (Float a) [ SqrtOfNegative ]*
|
||||
log : Float a -> Result (Float a) [ LogNeedsPositive ]*
|
||||
sqrt : Float a -> Float a
|
||||
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
|
||||
divChecked : Float a, Float a -> Result (Float a) [ DivByZero ]*
|
||||
|
||||
divCeil : Int a, Int a -> Int a
|
||||
divCeilChecked : Int a, Int a -> Result (Int a) [ DivByZero ]*
|
||||
|
||||
divTrunc : Int a, Int a -> Int a
|
||||
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 ]*
|
||||
# mod : Int a, Int a -> Result (Int a) [ DivByZero ]*
|
||||
rem : Int a, Int a -> Int a
|
||||
remChecked : Int a, Int a -> Result (Int a) [ DivByZero ]*
|
||||
|
||||
isMultipleOf : Int a, Int a -> Bool
|
||||
|
||||
bitwiseAnd : Int a, Int a -> Int a
|
||||
|
|
|
@ -68,13 +68,9 @@ impl FloatWidth {
|
|||
|
||||
pub const fn try_from_symbol(symbol: Symbol) -> Option<Self> {
|
||||
match symbol {
|
||||
Symbol::NUM_F64 | Symbol::NUM_BINARY64 | Symbol::NUM_AT_BINARY64 => {
|
||||
Some(FloatWidth::F64)
|
||||
}
|
||||
Symbol::NUM_F64 | Symbol::NUM_BINARY64 => Some(FloatWidth::F64),
|
||||
|
||||
Symbol::NUM_F32 | Symbol::NUM_BINARY32 | Symbol::NUM_AT_BINARY32 => {
|
||||
Some(FloatWidth::F32)
|
||||
}
|
||||
Symbol::NUM_F32 | Symbol::NUM_BINARY32 => Some(FloatWidth::F32),
|
||||
|
||||
_ => None,
|
||||
}
|
||||
|
@ -136,26 +132,16 @@ impl IntWidth {
|
|||
|
||||
pub const fn try_from_symbol(symbol: Symbol) -> Option<Self> {
|
||||
match symbol {
|
||||
Symbol::NUM_I128 | Symbol::NUM_SIGNED128 | Symbol::NUM_AT_SIGNED128 => {
|
||||
Some(IntWidth::I128)
|
||||
}
|
||||
Symbol::NUM_I64 | Symbol::NUM_SIGNED64 | Symbol::NUM_AT_SIGNED64 => Some(IntWidth::I64),
|
||||
Symbol::NUM_I32 | Symbol::NUM_SIGNED32 | Symbol::NUM_AT_SIGNED32 => Some(IntWidth::I32),
|
||||
Symbol::NUM_I16 | Symbol::NUM_SIGNED16 | Symbol::NUM_AT_SIGNED16 => Some(IntWidth::I16),
|
||||
Symbol::NUM_I8 | Symbol::NUM_SIGNED8 | Symbol::NUM_AT_SIGNED8 => Some(IntWidth::I8),
|
||||
Symbol::NUM_U128 | Symbol::NUM_UNSIGNED128 | Symbol::NUM_AT_UNSIGNED128 => {
|
||||
Some(IntWidth::U128)
|
||||
}
|
||||
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),
|
||||
Symbol::NUM_I128 | Symbol::NUM_SIGNED128 => Some(IntWidth::I128),
|
||||
Symbol::NUM_I64 | Symbol::NUM_SIGNED64 => Some(IntWidth::I64),
|
||||
Symbol::NUM_I32 | Symbol::NUM_SIGNED32 => Some(IntWidth::I32),
|
||||
Symbol::NUM_I16 | Symbol::NUM_SIGNED16 => Some(IntWidth::I16),
|
||||
Symbol::NUM_I8 | Symbol::NUM_SIGNED8 => Some(IntWidth::I8),
|
||||
Symbol::NUM_U128 | Symbol::NUM_UNSIGNED128 => Some(IntWidth::U128),
|
||||
Symbol::NUM_U64 | Symbol::NUM_UNSIGNED64 => Some(IntWidth::U64),
|
||||
Symbol::NUM_U32 | Symbol::NUM_UNSIGNED32 => Some(IntWidth::U32),
|
||||
Symbol::NUM_U16 | Symbol::NUM_UNSIGNED16 => Some(IntWidth::U16),
|
||||
Symbol::NUM_U8 | Symbol::NUM_UNSIGNED8 => Some(IntWidth::U8),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
|
|
@ -393,16 +393,16 @@ pub fn types() -> MutMap<Symbol, (SolvedType, Region)> {
|
|||
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!(
|
||||
Symbol::NUM_REM,
|
||||
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!(
|
||||
Symbol::NUM_MOD_INT,
|
||||
Symbol::NUM_REM_CHECKED,
|
||||
vec![int_type(flex(TVAR1)), int_type(flex(TVAR1))],
|
||||
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!(
|
||||
Symbol::NUM_DIV_FLOAT_CHECKED,
|
||||
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)),
|
||||
);
|
||||
|
||||
// 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(
|
||||
vec![(TagName::Global("SqrtOfNegative".into()), vec![])],
|
||||
Box::new(SolvedType::Wildcard),
|
||||
);
|
||||
|
||||
add_top_level_function_type!(
|
||||
Symbol::NUM_SQRT,
|
||||
Symbol::NUM_SQRT_CHECKED,
|
||||
vec![float_type(flex(TVAR1))],
|
||||
Box::new(result_type(float_type(flex(TVAR1)), sqrt_of_negative)),
|
||||
);
|
||||
|
||||
// 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(
|
||||
vec![(TagName::Global("LogNeedsPositive".into()), vec![])],
|
||||
Box::new(SolvedType::Wildcard),
|
||||
);
|
||||
|
||||
add_top_level_function_type!(
|
||||
Symbol::NUM_LOG,
|
||||
Symbol::NUM_LOG_CHECKED,
|
||||
vec![float_type(flex(TVAR1))],
|
||||
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() {
|
||||
match tag {
|
||||
Tag::Global { args, .. } | Tag::Private { args, .. } => {
|
||||
Tag::Global { args, .. } => {
|
||||
for t in args.iter() {
|
||||
stack.push(&t.value);
|
||||
}
|
||||
|
@ -1253,31 +1253,6 @@ fn can_tags<'a>(
|
|||
|
||||
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, _) => {
|
||||
// check the nested tag instead
|
||||
tag = nested;
|
||||
|
|
|
@ -204,9 +204,12 @@ pub fn builtin_defs_map(symbol: Symbol, var_store: &mut VarStore) -> Option<Def>
|
|||
NUM_ABS => num_abs,
|
||||
NUM_NEG => num_neg,
|
||||
NUM_REM => num_rem,
|
||||
NUM_REM_CHECKED => num_rem_checked,
|
||||
NUM_IS_MULTIPLE_OF => num_is_multiple_of,
|
||||
NUM_SQRT => num_sqrt,
|
||||
NUM_SQRT_CHECKED => num_sqrt_checked,
|
||||
NUM_LOG => num_log,
|
||||
NUM_LOG_CHECKED => num_log_checked,
|
||||
NUM_ROUND => num_round,
|
||||
NUM_IS_ODD => num_is_odd,
|
||||
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
|
||||
fn num_binop(symbol: Symbol, var_store: &mut VarStore, op: LowLevel) -> Def {
|
||||
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 {
|
||||
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 float_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 {
|
||||
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 float_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 {
|
||||
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 unbound_zero_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 roc_collections::{SendMap, VecSet};
|
||||
use roc_module::called_via::CalledVia;
|
||||
use roc_module::ident::TagName;
|
||||
use roc_module::ident::{Lowercase, TagName};
|
||||
use roc_module::symbol::Symbol;
|
||||
use roc_region::all::{Loc, Region};
|
||||
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)]
|
||||
pub(crate) struct HostedGeneratedFunctions {
|
||||
|
@ -30,7 +30,7 @@ pub(crate) struct HostedGeneratedFunctions {
|
|||
///
|
||||
/// 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
|
||||
/// standard implementation.
|
||||
|
@ -45,13 +45,7 @@ pub(crate) fn build_effect_builtins(
|
|||
) {
|
||||
macro_rules! helper {
|
||||
($f:expr) => {{
|
||||
let (symbol, def) = $f(
|
||||
env,
|
||||
scope,
|
||||
effect_symbol,
|
||||
TagName::Private(effect_symbol),
|
||||
var_store,
|
||||
);
|
||||
let (symbol, def) = $f(env, scope, effect_symbol, var_store);
|
||||
|
||||
// make the outside world know this symbol exists
|
||||
exposed_symbols.insert(symbol);
|
||||
|
@ -114,7 +108,6 @@ fn build_effect_always(
|
|||
env: &mut Env,
|
||||
scope: &mut Scope,
|
||||
effect_symbol: Symbol,
|
||||
effect_tag_name: TagName,
|
||||
var_store: &mut VarStore,
|
||||
) -> (Symbol, Def) {
|
||||
// Effect.always = \value -> @Effect \{} -> value
|
||||
|
@ -177,11 +170,15 @@ fn build_effect_always(
|
|||
// \value -> @Effect \{} -> value
|
||||
let (function_var, always_closure) = {
|
||||
// `@Effect \{} -> value`
|
||||
let body = Expr::Tag {
|
||||
variant_var: var_store.fresh(),
|
||||
ext_var: var_store.fresh(),
|
||||
name: effect_tag_name.clone(),
|
||||
arguments: vec![(var_store.fresh(), Loc::at_zero(const_closure))],
|
||||
let (specialized_def_type, type_arguments, lambda_set_variables) =
|
||||
build_fresh_opaque_variables(var_store);
|
||||
let body = Expr::OpaqueRef {
|
||||
opaque_var: var_store.fresh(),
|
||||
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![(
|
||||
|
@ -212,9 +209,8 @@ fn build_effect_always(
|
|||
let var_a = var_store.fresh();
|
||||
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_tag_name,
|
||||
"a",
|
||||
var_a,
|
||||
Type::Variable(var_a),
|
||||
|
@ -257,7 +253,6 @@ fn build_effect_map(
|
|||
env: &mut Env,
|
||||
scope: &mut Scope,
|
||||
effect_symbol: Symbol,
|
||||
effect_tag_name: TagName,
|
||||
var_store: &mut VarStore,
|
||||
) -> (Symbol, Def) {
|
||||
// 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![
|
||||
(
|
||||
var_store.fresh(),
|
||||
Loc::at_zero(Pattern::AppliedTag {
|
||||
Loc::at_zero(Pattern::UnwrappedOpaque {
|
||||
opaque: effect_symbol,
|
||||
whole_var: var_store.fresh(),
|
||||
ext_var: var_store.fresh(),
|
||||
tag_name: effect_tag_name.clone(),
|
||||
arguments: vec![(
|
||||
argument: Box::new((
|
||||
var_store.fresh(),
|
||||
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 {}))`
|
||||
let body = Expr::Tag {
|
||||
variant_var: var_store.fresh(),
|
||||
ext_var: var_store.fresh(),
|
||||
name: effect_tag_name.clone(),
|
||||
arguments: vec![(var_store.fresh(), Loc::at_zero(inner_closure))],
|
||||
let (specialized_def_type, type_arguments, lambda_set_variables) =
|
||||
build_fresh_opaque_variables(var_store);
|
||||
let body = Expr::OpaqueRef {
|
||||
opaque_var: var_store.fresh(),
|
||||
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();
|
||||
|
@ -405,9 +409,8 @@ fn build_effect_map(
|
|||
introduced_variables.insert_named("a".into(), Loc::at_zero(var_a));
|
||||
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_tag_name.clone(),
|
||||
"a",
|
||||
var_a,
|
||||
Type::Variable(var_a),
|
||||
|
@ -415,9 +418,8 @@ fn build_effect_map(
|
|||
&mut introduced_variables,
|
||||
);
|
||||
|
||||
let effect_b = build_effect_alias(
|
||||
let effect_b = build_effect_opaque(
|
||||
effect_symbol,
|
||||
effect_tag_name,
|
||||
"b",
|
||||
var_b,
|
||||
Type::Variable(var_b),
|
||||
|
@ -469,7 +471,6 @@ fn build_effect_after(
|
|||
env: &mut Env,
|
||||
scope: &mut Scope,
|
||||
effect_symbol: Symbol,
|
||||
effect_tag_name: TagName,
|
||||
var_store: &mut VarStore,
|
||||
) -> (Symbol, Def) {
|
||||
// Effect.after = \@Effect effect, toEffect -> toEffect (effect {})
|
||||
|
@ -533,17 +534,22 @@ fn build_effect_after(
|
|||
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![
|
||||
(
|
||||
var_store.fresh(),
|
||||
Loc::at_zero(Pattern::AppliedTag {
|
||||
Loc::at_zero(Pattern::UnwrappedOpaque {
|
||||
opaque: effect_symbol,
|
||||
whole_var: var_store.fresh(),
|
||||
ext_var: var_store.fresh(),
|
||||
tag_name: effect_tag_name.clone(),
|
||||
arguments: vec![(
|
||||
argument: Box::new((
|
||||
var_store.fresh(),
|
||||
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("b".into(), Loc::at_zero(var_b));
|
||||
|
||||
let effect_a = build_effect_alias(
|
||||
let effect_a = build_effect_opaque(
|
||||
effect_symbol,
|
||||
effect_tag_name.clone(),
|
||||
"a",
|
||||
var_a,
|
||||
Type::Variable(var_a),
|
||||
|
@ -584,9 +589,8 @@ fn build_effect_after(
|
|||
&mut introduced_variables,
|
||||
);
|
||||
|
||||
let effect_b = build_effect_alias(
|
||||
let effect_b = build_effect_opaque(
|
||||
effect_symbol,
|
||||
effect_tag_name,
|
||||
"b",
|
||||
var_b,
|
||||
Type::Variable(var_b),
|
||||
|
@ -635,7 +639,7 @@ fn build_effect_after(
|
|||
/// turn `value` into `@Effect \{} -> value`
|
||||
fn wrap_in_effect_thunk(
|
||||
body: Expr,
|
||||
effect_tag_name: TagName,
|
||||
effect_symbol: Symbol,
|
||||
closure_name: Symbol,
|
||||
captured_symbols: Vec<Symbol>,
|
||||
var_store: &mut VarStore,
|
||||
|
@ -667,31 +671,38 @@ fn wrap_in_effect_thunk(
|
|||
};
|
||||
|
||||
// `@Effect \{} -> value`
|
||||
Expr::Tag {
|
||||
variant_var: var_store.fresh(),
|
||||
ext_var: var_store.fresh(),
|
||||
name: effect_tag_name,
|
||||
arguments: vec![(var_store.fresh(), Loc::at_zero(const_closure))],
|
||||
let (specialized_def_type, type_arguments, lambda_set_variables) =
|
||||
build_fresh_opaque_variables(var_store);
|
||||
Expr::OpaqueRef {
|
||||
opaque_var: var_store.fresh(),
|
||||
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`
|
||||
fn force_effect(
|
||||
effect: Expr,
|
||||
effect_tag_name: TagName,
|
||||
effect_symbol: Symbol,
|
||||
thunk_symbol: Symbol,
|
||||
var_store: &mut VarStore,
|
||||
) -> Expr {
|
||||
let whole_var = var_store.fresh();
|
||||
let ext_var = var_store.fresh();
|
||||
|
||||
let thunk_var = var_store.fresh();
|
||||
|
||||
let pattern = Pattern::AppliedTag {
|
||||
ext_var,
|
||||
let (specialized_def_type, type_arguments, lambda_set_variables) =
|
||||
build_fresh_opaque_variables(var_store);
|
||||
let pattern = Pattern::UnwrappedOpaque {
|
||||
whole_var,
|
||||
tag_name: effect_tag_name,
|
||||
arguments: vec![(thunk_var, Loc::at_zero(Pattern::Identifier(thunk_symbol)))],
|
||||
opaque: effect_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();
|
||||
|
@ -728,7 +739,6 @@ fn build_effect_forever(
|
|||
env: &mut Env,
|
||||
scope: &mut Scope,
|
||||
effect_symbol: Symbol,
|
||||
effect_tag_name: TagName,
|
||||
var_store: &mut VarStore,
|
||||
) -> (Symbol, Def) {
|
||||
// morally
|
||||
|
@ -801,14 +811,8 @@ fn build_effect_forever(
|
|||
.unwrap()
|
||||
};
|
||||
|
||||
let body = build_effect_forever_body(
|
||||
env,
|
||||
scope,
|
||||
effect_tag_name.clone(),
|
||||
forever_symbol,
|
||||
effect,
|
||||
var_store,
|
||||
);
|
||||
let body =
|
||||
build_effect_forever_body(env, scope, effect_symbol, forever_symbol, effect, var_store);
|
||||
|
||||
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("b".into(), Loc::at_zero(var_b));
|
||||
|
||||
let effect_a = build_effect_alias(
|
||||
let effect_a = build_effect_opaque(
|
||||
effect_symbol,
|
||||
effect_tag_name.clone(),
|
||||
"a",
|
||||
var_a,
|
||||
Type::Variable(var_a),
|
||||
|
@ -844,9 +847,8 @@ fn build_effect_forever(
|
|||
&mut introduced_variables,
|
||||
);
|
||||
|
||||
let effect_b = build_effect_alias(
|
||||
let effect_b = build_effect_opaque(
|
||||
effect_symbol,
|
||||
effect_tag_name,
|
||||
"b",
|
||||
var_b,
|
||||
Type::Variable(var_b),
|
||||
|
@ -888,7 +890,7 @@ fn build_effect_forever(
|
|||
fn build_effect_forever_body(
|
||||
env: &mut Env,
|
||||
scope: &mut Scope,
|
||||
effect_tag_name: TagName,
|
||||
effect_symbol: Symbol,
|
||||
forever_symbol: Symbol,
|
||||
effect: Symbol,
|
||||
var_store: &mut VarStore,
|
||||
|
@ -907,7 +909,7 @@ fn build_effect_forever_body(
|
|||
let inner_body = build_effect_forever_inner_body(
|
||||
env,
|
||||
scope,
|
||||
effect_tag_name.clone(),
|
||||
effect_symbol,
|
||||
forever_symbol,
|
||||
effect,
|
||||
var_store,
|
||||
|
@ -916,7 +918,7 @@ fn build_effect_forever_body(
|
|||
let captured_symbols = vec![effect];
|
||||
wrap_in_effect_thunk(
|
||||
inner_body,
|
||||
effect_tag_name,
|
||||
effect_symbol,
|
||||
closure_name,
|
||||
captured_symbols,
|
||||
var_store,
|
||||
|
@ -926,7 +928,7 @@ fn build_effect_forever_body(
|
|||
fn build_effect_forever_inner_body(
|
||||
env: &mut Env,
|
||||
scope: &mut Scope,
|
||||
effect_tag_name: TagName,
|
||||
effect_symbol: Symbol,
|
||||
forever_symbol: Symbol,
|
||||
effect: Symbol,
|
||||
var_store: &mut VarStore,
|
||||
|
@ -953,18 +955,21 @@ fn build_effect_forever_inner_body(
|
|||
.unwrap()
|
||||
};
|
||||
|
||||
// Effect thunk1 = effect
|
||||
// @Effect thunk1 = effect
|
||||
let thunk_from_effect = {
|
||||
let whole_var = var_store.fresh();
|
||||
let ext_var = var_store.fresh();
|
||||
|
||||
let thunk_var = var_store.fresh();
|
||||
|
||||
let pattern = Pattern::AppliedTag {
|
||||
ext_var,
|
||||
let (specialized_def_type, type_arguments, lambda_set_variables) =
|
||||
build_fresh_opaque_variables(var_store);
|
||||
let pattern = Pattern::UnwrappedOpaque {
|
||||
whole_var,
|
||||
tag_name: effect_tag_name.clone(),
|
||||
arguments: vec![(thunk_var, Loc::at_zero(Pattern::Identifier(thunk1_symbol)))],
|
||||
opaque: effect_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();
|
||||
|
@ -1017,12 +1022,12 @@ fn build_effect_forever_inner_body(
|
|||
};
|
||||
|
||||
// ```
|
||||
// Effect thunk2 = forever effect
|
||||
// @Effect thunk2 = forever effect
|
||||
// thunk2 {}
|
||||
// ```
|
||||
let force_thunk2 = Loc::at_zero(force_effect(
|
||||
forever_effect,
|
||||
effect_tag_name,
|
||||
effect_symbol,
|
||||
thunk2_symbol,
|
||||
var_store,
|
||||
));
|
||||
|
@ -1042,7 +1047,6 @@ fn build_effect_loop(
|
|||
env: &mut Env,
|
||||
scope: &mut Scope,
|
||||
effect_symbol: Symbol,
|
||||
effect_tag_name: TagName,
|
||||
var_store: &mut VarStore,
|
||||
) -> (Symbol, Def) {
|
||||
let loop_symbol = new_symbol!(scope, env, "loop");
|
||||
|
@ -1052,7 +1056,7 @@ fn build_effect_loop(
|
|||
let body = build_effect_loop_body(
|
||||
env,
|
||||
scope,
|
||||
effect_tag_name.clone(),
|
||||
effect_symbol,
|
||||
loop_symbol,
|
||||
state_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("b".into(), Loc::at_zero(var_b));
|
||||
|
||||
let effect_b = build_effect_alias(
|
||||
let effect_b = build_effect_opaque(
|
||||
effect_symbol,
|
||||
effect_tag_name.clone(),
|
||||
"b",
|
||||
var_b,
|
||||
Type::Variable(var_b),
|
||||
|
@ -1119,19 +1122,11 @@ fn build_effect_loop(
|
|||
let closure_var = var_store.fresh();
|
||||
introduced_variables.insert_wildcard(Loc::at_zero(closure_var));
|
||||
|
||||
let actual = {
|
||||
Type::TagUnion(
|
||||
vec![(
|
||||
effect_tag_name,
|
||||
vec![Type::Function(
|
||||
let actual = Type::Function(
|
||||
vec![Type::EmptyRec],
|
||||
Box::new(Type::Variable(closure_var)),
|
||||
Box::new(state_type.clone()),
|
||||
)],
|
||||
)],
|
||||
TypeExtension::Closed,
|
||||
)
|
||||
};
|
||||
);
|
||||
|
||||
Type::Alias {
|
||||
symbol: effect_symbol,
|
||||
|
@ -1140,7 +1135,7 @@ fn build_effect_loop(
|
|||
closure_var,
|
||||
))],
|
||||
actual: Box::new(actual),
|
||||
kind: AliasKind::Structural,
|
||||
kind: AliasKind::Opaque,
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -1187,7 +1182,7 @@ fn build_effect_loop(
|
|||
fn build_effect_loop_body(
|
||||
env: &mut Env,
|
||||
scope: &mut Scope,
|
||||
effect_tag_name: TagName,
|
||||
effect_symbol: Symbol,
|
||||
loop_symbol: Symbol,
|
||||
state_symbol: Symbol,
|
||||
step_symbol: Symbol,
|
||||
|
@ -1207,7 +1202,7 @@ fn build_effect_loop_body(
|
|||
let inner_body = build_effect_loop_inner_body(
|
||||
env,
|
||||
scope,
|
||||
effect_tag_name.clone(),
|
||||
effect_symbol,
|
||||
loop_symbol,
|
||||
state_symbol,
|
||||
step_symbol,
|
||||
|
@ -1217,7 +1212,7 @@ fn build_effect_loop_body(
|
|||
let captured_symbols = vec![state_symbol, step_symbol];
|
||||
wrap_in_effect_thunk(
|
||||
inner_body,
|
||||
effect_tag_name,
|
||||
effect_symbol,
|
||||
closure_name,
|
||||
captured_symbols,
|
||||
var_store,
|
||||
|
@ -1249,7 +1244,7 @@ fn applied_tag_pattern(
|
|||
fn build_effect_loop_inner_body(
|
||||
env: &mut Env,
|
||||
scope: &mut Scope,
|
||||
effect_tag_name: TagName,
|
||||
effect_symbol: Symbol,
|
||||
loop_symbol: Symbol,
|
||||
state_symbol: Symbol,
|
||||
step_symbol: Symbol,
|
||||
|
@ -1264,15 +1259,18 @@ fn build_effect_loop_inner_body(
|
|||
// Effect thunk1 = step state
|
||||
let thunk_from_effect = {
|
||||
let whole_var = var_store.fresh();
|
||||
let ext_var = var_store.fresh();
|
||||
|
||||
let thunk_var = var_store.fresh();
|
||||
|
||||
let pattern = Pattern::AppliedTag {
|
||||
ext_var,
|
||||
let (specialized_def_type, type_arguments, lambda_set_variables) =
|
||||
build_fresh_opaque_variables(var_store);
|
||||
let pattern = Pattern::UnwrappedOpaque {
|
||||
whole_var,
|
||||
tag_name: effect_tag_name.clone(),
|
||||
arguments: vec![(thunk_var, Loc::at_zero(Pattern::Identifier(thunk1_symbol)))],
|
||||
opaque: effect_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();
|
||||
|
@ -1332,15 +1330,10 @@ fn build_effect_loop_inner_body(
|
|||
};
|
||||
|
||||
// ```
|
||||
// Effect thunk2 = loop effect
|
||||
// @Effect thunk2 = loop effect
|
||||
// thunk2 {}
|
||||
// ```
|
||||
let force_thunk2 = force_effect(
|
||||
loop_new_state_step,
|
||||
effect_tag_name,
|
||||
thunk2_symbol,
|
||||
var_store,
|
||||
);
|
||||
let force_thunk2 = force_effect(loop_new_state_step, effect_symbol, thunk2_symbol, var_store);
|
||||
|
||||
let step_branch = {
|
||||
let step_tag_name = TagName::Global("Step".into());
|
||||
|
@ -1387,7 +1380,7 @@ pub fn build_host_exposed_def(
|
|||
scope: &mut Scope,
|
||||
symbol: Symbol,
|
||||
ident: &str,
|
||||
effect_tag_name: TagName,
|
||||
effect_symbol: Symbol,
|
||||
var_store: &mut VarStore,
|
||||
annotation: crate::annotation::Annotation,
|
||||
) -> Def {
|
||||
|
@ -1400,8 +1393,15 @@ pub fn build_host_exposed_def(
|
|||
let mut linked_symbol_arguments: Vec<(Variable, Expr)> = Vec::new();
|
||||
let mut captured_symbols: Vec<(Symbol, Variable)> = Vec::new();
|
||||
|
||||
let crate::annotation::Annotation {
|
||||
introduced_variables,
|
||||
typ,
|
||||
aliases,
|
||||
..
|
||||
} = annotation;
|
||||
|
||||
let def_body = {
|
||||
match annotation.typ.shallow_dealias() {
|
||||
match typ.shallow_structural_dealias() {
|
||||
Type::Function(args, _, _) => {
|
||||
for i in 0..args.len() {
|
||||
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)),
|
||||
});
|
||||
|
||||
let body = Expr::Tag {
|
||||
variant_var: var_store.fresh(),
|
||||
ext_var: var_store.fresh(),
|
||||
name: effect_tag_name,
|
||||
arguments: vec![(var_store.fresh(), Loc::at_zero(effect_closure))],
|
||||
let (specialized_def_type, type_arguments, lambda_set_variables) =
|
||||
build_fresh_opaque_variables(var_store);
|
||||
let body = Expr::OpaqueRef {
|
||||
opaque_var: var_store.fresh(),
|
||||
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 {
|
||||
|
@ -1523,20 +1527,24 @@ pub fn build_host_exposed_def(
|
|||
loc_body: Box::new(Loc::at_zero(low_level_call)),
|
||||
});
|
||||
|
||||
Expr::Tag {
|
||||
variant_var: var_store.fresh(),
|
||||
ext_var: var_store.fresh(),
|
||||
name: effect_tag_name,
|
||||
arguments: vec![(var_store.fresh(), Loc::at_zero(effect_closure))],
|
||||
let (specialized_def_type, type_arguments, lambda_set_variables) =
|
||||
build_fresh_opaque_variables(var_store);
|
||||
Expr::OpaqueRef {
|
||||
opaque_var: var_store.fresh(),
|
||||
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 {
|
||||
signature: annotation.typ,
|
||||
introduced_variables: annotation.introduced_variables,
|
||||
aliases: annotation.aliases,
|
||||
signature: typ,
|
||||
introduced_variables,
|
||||
aliases,
|
||||
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_tag_name: TagName,
|
||||
a_name: &str,
|
||||
a_var: Variable,
|
||||
a_type: Type,
|
||||
|
@ -1561,47 +1579,39 @@ fn build_effect_alias(
|
|||
let closure_var = var_store.fresh();
|
||||
introduced_variables.insert_wildcard(Loc::at_zero(closure_var));
|
||||
|
||||
let actual = {
|
||||
Type::TagUnion(
|
||||
vec![(
|
||||
effect_tag_name,
|
||||
vec![Type::Function(
|
||||
let actual = Type::Function(
|
||||
vec![Type::EmptyRec],
|
||||
Box::new(Type::Variable(closure_var)),
|
||||
Box::new(a_type),
|
||||
)],
|
||||
)],
|
||||
TypeExtension::Closed,
|
||||
)
|
||||
};
|
||||
);
|
||||
|
||||
Type::Alias {
|
||||
symbol: effect_symbol,
|
||||
type_arguments: vec![(a_name.into(), Type::Variable(a_var))],
|
||||
lambda_set_variables: vec![roc_types::types::LambdaSet(Type::Variable(closure_var))],
|
||||
actual: Box::new(actual),
|
||||
kind: AliasKind::Structural,
|
||||
kind: AliasKind::Opaque,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn build_effect_actual(
|
||||
effect_tag_name: TagName,
|
||||
a_type: Type,
|
||||
fn build_fresh_opaque_variables(
|
||||
var_store: &mut VarStore,
|
||||
) -> Type {
|
||||
) -> (Box<Type>, Vec<(Lowercase, Type)>, Vec<LambdaSet>) {
|
||||
let closure_var = var_store.fresh();
|
||||
|
||||
Type::TagUnion(
|
||||
vec![(
|
||||
effect_tag_name,
|
||||
vec![Type::Function(
|
||||
// NB: if there are bugs, check whether not introducing variables is a problem!
|
||||
// introduced_variables.insert_wildcard(Loc::at_zero(closure_var));
|
||||
|
||||
let a_var = var_store.fresh();
|
||||
let actual = Type::Function(
|
||||
vec![Type::EmptyRec],
|
||||
Box::new(Type::Variable(closure_var)),
|
||||
Box::new(a_type),
|
||||
)],
|
||||
)],
|
||||
TypeExtension::Closed,
|
||||
)
|
||||
Box::new(Type::Variable(a_var)),
|
||||
);
|
||||
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)]
|
||||
|
|
|
@ -7,7 +7,7 @@ use roc_region::all::{Loc, Region};
|
|||
|
||||
/// The canonicalization environment for a particular module.
|
||||
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.
|
||||
pub home: ModuleId,
|
||||
|
||||
|
@ -182,8 +182,4 @@ impl<'a> Env<'a> {
|
|||
pub fn problem(&mut self, problem: 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,
|
||||
},
|
||||
|
||||
/// A wrapping of an opaque type, like `$Age 21`
|
||||
// TODO(opaques): $->@ above when opaques land
|
||||
/// A wrapping of an opaque type, like `@Age 21`
|
||||
OpaqueRef {
|
||||
opaque_var: Variable,
|
||||
name: Symbol,
|
||||
|
@ -293,6 +292,23 @@ pub struct WhenBranch {
|
|||
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>(
|
||||
env: &mut Env<'a>,
|
||||
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
|
||||
.into_iter()
|
||||
|
@ -811,23 +829,6 @@ pub fn canonicalize_expr<'a>(
|
|||
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) => {
|
||||
// If we're here, the opaque reference is definitely not wrapping an argument - wrapped
|
||||
// arguments are handled in the Apply branch.
|
||||
|
|
|
@ -18,3 +18,4 @@ pub mod procedure;
|
|||
mod reference_matrix;
|
||||
pub mod scope;
|
||||
pub mod string;
|
||||
pub mod traverse;
|
||||
|
|
|
@ -8,8 +8,8 @@ use crate::pattern::Pattern;
|
|||
use crate::scope::Scope;
|
||||
use bumpalo::Bump;
|
||||
use roc_collections::{MutMap, SendMap, VecSet};
|
||||
use roc_module::ident::Ident;
|
||||
use roc_module::ident::Lowercase;
|
||||
use roc_module::ident::{Ident, TagName};
|
||||
use roc_module::symbol::{IdentIds, ModuleId, ModuleIds, Symbol};
|
||||
use roc_parse::ast;
|
||||
use roc_parse::header::HeaderFor;
|
||||
|
@ -116,23 +116,18 @@ impl GeneratedInfo {
|
|||
)
|
||||
.unwrap();
|
||||
|
||||
let effect_tag_name = TagName::Private(effect_symbol);
|
||||
|
||||
{
|
||||
let a_var = var_store.fresh();
|
||||
|
||||
let actual = crate::effect_module::build_effect_actual(
|
||||
effect_tag_name,
|
||||
Type::Variable(a_var),
|
||||
var_store,
|
||||
);
|
||||
let actual =
|
||||
crate::effect_module::build_effect_actual(Type::Variable(a_var), var_store);
|
||||
|
||||
scope.add_alias(
|
||||
effect_symbol,
|
||||
Region::zero(),
|
||||
vec![Loc::at_zero(("a".into(), a_var))],
|
||||
actual,
|
||||
AliasKind::Structural,
|
||||
AliasKind::Opaque,
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -433,7 +428,7 @@ pub fn canonicalize_module_defs<'a>(
|
|||
&mut scope,
|
||||
*symbol,
|
||||
&ident,
|
||||
TagName::Private(effect_symbol),
|
||||
effect_symbol,
|
||||
var_store,
|
||||
annotation,
|
||||
);
|
||||
|
|
|
@ -151,7 +151,6 @@ pub fn desugar_expr<'a>(arena: &'a Bump, loc_expr: &'a Loc<Expr<'a>>) -> &'a Loc
|
|||
| MalformedClosure
|
||||
| PrecedenceConflict { .. }
|
||||
| GlobalTag(_)
|
||||
| PrivateTag(_)
|
||||
| OpaqueRef(_) => loc_expr,
|
||||
|
||||
Access(sub_expr, paths) => {
|
||||
|
@ -425,7 +424,6 @@ fn binop_to_function(binop: BinOp) -> (&'static str, &'static str) {
|
|||
Slash => (ModuleName::NUM, "div"),
|
||||
DoubleSlash => (ModuleName::NUM, "divTrunc"),
|
||||
Percent => (ModuleName::NUM, "rem"),
|
||||
DoublePercent => (ModuleName::NUM, "mod"),
|
||||
Plus => (ModuleName::NUM, "add"),
|
||||
Minus => (ModuleName::NUM, "sub"),
|
||||
Equals => (ModuleName::BOOL, "isEq"),
|
||||
|
|
|
@ -82,6 +82,31 @@ pub enum Pattern {
|
|||
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)]
|
||||
pub struct RecordDestruct {
|
||||
pub var: Variable,
|
||||
|
@ -244,17 +269,6 @@ pub fn canonicalize_pattern<'a>(
|
|||
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) => {
|
||||
// If this opaque ref had an argument, we would be in the "Apply" branch.
|
||||
let loc_name = Loc::at(region, (*name).into());
|
||||
|
@ -289,17 +303,6 @@ pub fn canonicalize_pattern<'a>(
|
|||
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) {
|
||||
Ok((opaque, opaque_def)) => {
|
||||
|
|
|
@ -1,7 +1,8 @@
|
|||
// see if we get better performance with different integer types
|
||||
pub(crate) type Element = usize;
|
||||
pub(crate) type BitVec = bitvec::vec::BitVec<Element>;
|
||||
pub(crate) type BitSlice = bitvec::prelude::BitSlice<Element>;
|
||||
type Order = bitvec::order::Lsb0;
|
||||
type Element = usize;
|
||||
type BitVec = bitvec::vec::BitVec<Element, Order>;
|
||||
type BitSlice = bitvec::prelude::BitSlice<Element, Order>;
|
||||
|
||||
/// A square boolean matrix used to store relations
|
||||
///
|
||||
|
@ -36,8 +37,8 @@ impl ReferenceMatrix {
|
|||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub fn get(&self, index: usize) -> bool {
|
||||
self.bitvec[index]
|
||||
pub fn get_row_col(&self, row: usize, col: usize) -> bool {
|
||||
self.bitvec[row * self.length + col]
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -50,6 +51,7 @@ impl ReferenceMatrix {
|
|||
//
|
||||
// Thank you, Samuel!
|
||||
impl ReferenceMatrix {
|
||||
#[allow(dead_code)]
|
||||
pub fn topological_sort_into_groups(&self) -> TopologicalSort {
|
||||
if self.length == 0 {
|
||||
return TopologicalSort::Groups { groups: Vec::new() };
|
||||
|
@ -128,7 +130,7 @@ impl ReferenceMatrix {
|
|||
}
|
||||
|
||||
/// 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);
|
||||
|
||||
'outer: loop {
|
||||
|
@ -147,6 +149,7 @@ impl ReferenceMatrix {
|
|||
}
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub(crate) enum TopologicalSort {
|
||||
/// There were no cycles, all nodes have been partitioned into groups
|
||||
Groups { groups: Vec<Vec<u32>> },
|
||||
|
@ -172,7 +175,7 @@ struct Params {
|
|||
c: usize,
|
||||
p: Vec<u32>,
|
||||
s: Vec<u32>,
|
||||
scc: Vec<Vec<u32>>,
|
||||
scc: Sccs,
|
||||
scca: Vec<u32>,
|
||||
}
|
||||
|
||||
|
@ -189,7 +192,10 @@ impl Params {
|
|||
c: 0,
|
||||
s: Vec::new(),
|
||||
p: Vec::new(),
|
||||
scc: Vec::new(),
|
||||
scc: Sccs {
|
||||
matrix: ReferenceMatrix::new(length),
|
||||
components: 0,
|
||||
},
|
||||
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)) {
|
||||
params.p.pop();
|
||||
|
||||
let mut component = Vec::new();
|
||||
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.preorders[node as usize] = Preorder::Removed;
|
||||
if node as usize == v {
|
||||
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();
|
||||
|
||||
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 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(),
|
||||
recursion_variables: MutSet::default(),
|
||||
type_variables: variables,
|
||||
// TODO(opaques): replace when opaques are included in the stdlib
|
||||
kind: AliasKind::Structural,
|
||||
kind,
|
||||
};
|
||||
|
||||
aliases.insert(symbol, alias);
|
||||
|
@ -201,11 +205,6 @@ impl Scope {
|
|||
}
|
||||
|
||||
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) {
|
||||
Some(symbol) => Ok(symbol),
|
||||
None => {
|
||||
|
@ -230,15 +229,14 @@ impl Scope {
|
|||
}
|
||||
|
||||
/// 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!
|
||||
// TODO(opaques): $->@ in the above comment
|
||||
pub fn lookup_opaque_ref(
|
||||
&self,
|
||||
opaque_ref: &str,
|
||||
lookup_region: Region,
|
||||
) -> Result<(Symbol, &Alias), RuntimeError> {
|
||||
debug_assert!(opaque_ref.starts_with('$'));
|
||||
debug_assert!(opaque_ref.starts_with('@'));
|
||||
let opaque = opaque_ref[1..].into();
|
||||
|
||||
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> {
|
||||
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> {
|
||||
|
|
|
@ -2,13 +2,13 @@ use arrayvec::ArrayVec;
|
|||
use roc_can::constraint::{Constraint, Constraints};
|
||||
use roc_can::expected::Expected::{self, *};
|
||||
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_region::all::Region;
|
||||
use roc_types::subs::Variable;
|
||||
use roc_types::types::Reason;
|
||||
use roc_types::types::Type::{self, *};
|
||||
use roc_types::types::{AliasKind, Category};
|
||||
use roc_types::types::{Reason, TypeExtension};
|
||||
|
||||
#[must_use]
|
||||
#[inline(always)]
|
||||
|
@ -163,14 +163,14 @@ fn builtin_alias(
|
|||
symbol: Symbol,
|
||||
type_arguments: Vec<(Lowercase, Type)>,
|
||||
actual: Box<Type>,
|
||||
kind: AliasKind,
|
||||
) -> Type {
|
||||
Type::Alias {
|
||||
symbol,
|
||||
type_arguments,
|
||||
actual,
|
||||
lambda_set_variables: vec![],
|
||||
// TODO(opaques): revisit later
|
||||
kind: AliasKind::Structural,
|
||||
kind,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -180,49 +180,48 @@ pub fn num_float(range: Type) -> Type {
|
|||
Symbol::NUM_FLOAT,
|
||||
vec![("range".into(), range.clone())],
|
||||
Box::new(num_num(num_floatingpoint(range))),
|
||||
AliasKind::Structural,
|
||||
)
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
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(
|
||||
Symbol::NUM_FLOATINGPOINT,
|
||||
vec![("range".into(), range)],
|
||||
Box::new(alias_content),
|
||||
vec![("range".into(), range.clone())],
|
||||
Box::new(range),
|
||||
AliasKind::Opaque,
|
||||
)
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
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)]
|
||||
fn num_unsigned32() -> Type {
|
||||
let alias_content = Type::TagUnion(
|
||||
vec![(TagName::Private(Symbol::NUM_AT_UNSIGNED32), vec![])],
|
||||
TypeExtension::Closed,
|
||||
);
|
||||
|
||||
builtin_alias(Symbol::NUM_UNSIGNED32, vec![], Box::new(alias_content))
|
||||
builtin_alias(
|
||||
Symbol::NUM_UNSIGNED32,
|
||||
vec![],
|
||||
Box::new(Type::EmptyTagUnion),
|
||||
AliasKind::Opaque,
|
||||
)
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub fn num_binary64() -> Type {
|
||||
let alias_content = Type::TagUnion(
|
||||
vec![(TagName::Private(Symbol::NUM_AT_BINARY64), vec![])],
|
||||
TypeExtension::Closed,
|
||||
);
|
||||
|
||||
builtin_alias(Symbol::NUM_BINARY64, vec![], Box::new(alias_content))
|
||||
builtin_alias(
|
||||
Symbol::NUM_BINARY64,
|
||||
vec![],
|
||||
Box::new(Type::EmptyTagUnion),
|
||||
AliasKind::Opaque,
|
||||
)
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
|
@ -231,47 +230,37 @@ pub fn num_int(range: Type) -> Type {
|
|||
Symbol::NUM_INT,
|
||||
vec![("range".into(), range.clone())],
|
||||
Box::new(num_num(num_integer(range))),
|
||||
AliasKind::Structural,
|
||||
)
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub fn num_signed64() -> Type {
|
||||
let alias_content = Type::TagUnion(
|
||||
vec![(TagName::Private(Symbol::NUM_AT_SIGNED64), vec![])],
|
||||
TypeExtension::Closed,
|
||||
);
|
||||
|
||||
builtin_alias(Symbol::NUM_SIGNED64, vec![], Box::new(alias_content))
|
||||
builtin_alias(
|
||||
Symbol::NUM_SIGNED64,
|
||||
vec![],
|
||||
Box::new(Type::EmptyTagUnion),
|
||||
AliasKind::Opaque,
|
||||
)
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
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(
|
||||
Symbol::NUM_INTEGER,
|
||||
vec![("range".into(), range)],
|
||||
Box::new(alias_content),
|
||||
vec![("range".into(), range.clone())],
|
||||
Box::new(range),
|
||||
AliasKind::Opaque,
|
||||
)
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
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(
|
||||
Symbol::NUM_NUM,
|
||||
vec![("range".into(), typ)],
|
||||
Box::new(alias_content),
|
||||
vec![("range".into(), typ.clone())],
|
||||
Box::new(typ),
|
||||
AliasKind::Opaque,
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
@ -585,20 +585,21 @@ pub fn constrain_expr(
|
|||
branches,
|
||||
..
|
||||
} => {
|
||||
// Infer the condition expression's type.
|
||||
let cond_var = *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_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 =
|
||||
|expected: &Expected<Type>, index, branch_region| match expected {
|
||||
FromAnnotation(name, arity, ann_source, _typ) => {
|
||||
|
@ -647,12 +648,20 @@ pub fn constrain_expr(
|
|||
// constraints.
|
||||
let mut pattern_vars = Vec::with_capacity(branches.len());
|
||||
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());
|
||||
|
||||
for (index, when_branch) in branches.iter().enumerate() {
|
||||
let pattern_region =
|
||||
Region::across_all(when_branch.patterns.iter().map(|v| &v.region));
|
||||
let expected_pattern = |sub_pattern, sub_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) =
|
||||
constrain_when_branch_help(
|
||||
|
@ -660,13 +669,7 @@ pub fn constrain_expr(
|
|||
env,
|
||||
region,
|
||||
when_branch,
|
||||
PExpected::ForReason(
|
||||
PReason::WhenMatch {
|
||||
index: HumanIndex::zero_based(index),
|
||||
},
|
||||
cond_type.clone(),
|
||||
pattern_region,
|
||||
),
|
||||
expected_pattern,
|
||||
branch_expr_reason(
|
||||
&expected,
|
||||
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 entire when-expression.
|
||||
// branch_cons.extend(pattern_cons);
|
||||
// branch_constraints.push(constraints.and_constraint(pattern_cons));
|
||||
let mut total_cons = Vec::with_capacity(1 + 2 * branches.len() + 1);
|
||||
total_cons.push(expr_con);
|
||||
|
||||
// After solving the condition variable with what's expected from the branch patterns,
|
||||
// check it against the condition expression.
|
||||
// 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
|
||||
// need be before solving the bodies.
|
||||
|
@ -712,15 +725,11 @@ pub fn constrain_expr(
|
|||
pattern_constraints,
|
||||
body_constraints,
|
||||
);
|
||||
total_cons.push(when_body_con);
|
||||
|
||||
total_cons.push(constraints.equal_types_var(
|
||||
branch_var,
|
||||
expected,
|
||||
Category::When,
|
||||
region,
|
||||
));
|
||||
let result_con =
|
||||
constraints.equal_types_var(branch_var, expected, Category::When, region);
|
||||
|
||||
let total_cons = [when_body_con, result_con];
|
||||
let branch_constraints = constraints.and_constraint(total_cons);
|
||||
|
||||
// exhautiveness checking happens when converting to mono::Expr
|
||||
|
@ -1122,7 +1131,7 @@ fn constrain_when_branch_help(
|
|||
env: &Env,
|
||||
region: Region,
|
||||
when_branch: &WhenBranch,
|
||||
pattern_expected: PExpected<Type>,
|
||||
pattern_expected: impl Fn(HumanIndex, Region) -> PExpected<Type>,
|
||||
expr_expected: Expected<Type>,
|
||||
) -> (
|
||||
Vec<Variable>,
|
||||
|
@ -1142,17 +1151,20 @@ fn constrain_when_branch_help(
|
|||
headers: SendMap::default(),
|
||||
vars: 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,
|
||||
// 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(
|
||||
constraints,
|
||||
env,
|
||||
&loc_pattern.value,
|
||||
loc_pattern.region,
|
||||
pattern_expected.clone(),
|
||||
pattern_expected,
|
||||
&mut state,
|
||||
);
|
||||
}
|
||||
|
@ -1171,11 +1183,17 @@ fn constrain_when_branch_help(
|
|||
);
|
||||
|
||||
// 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 inner = constraints.let_constraint([], [], [], guard_constraint, ret_constraint);
|
||||
|
||||
(state_constraints, inner)
|
||||
} else {
|
||||
state
|
||||
.constraints
|
||||
.append(&mut state.delayed_is_open_constraints);
|
||||
let state_constraints = constraints.and_constraint(state.constraints);
|
||||
(state_constraints, ret_constraint)
|
||||
};
|
||||
|
@ -1267,6 +1285,7 @@ fn constrain_def_pattern(
|
|||
headers: SendMap::default(),
|
||||
vars: Vec::with_capacity(1),
|
||||
constraints: Vec::with_capacity(1),
|
||||
delayed_is_open_constraints: vec![],
|
||||
};
|
||||
|
||||
constrain_pattern(
|
||||
|
@ -1364,6 +1383,7 @@ fn constrain_typed_def(
|
|||
headers: SendMap::default(),
|
||||
vars: Vec::with_capacity(arguments.len()),
|
||||
constraints: Vec::with_capacity(1),
|
||||
delayed_is_open_constraints: vec![],
|
||||
};
|
||||
let mut vars = Vec::with_capacity(argument_pattern_state.vars.capacity() + 1);
|
||||
let ret_var = *ret_var;
|
||||
|
@ -1843,6 +1863,7 @@ pub fn rec_defs_help(
|
|||
headers: SendMap::default(),
|
||||
vars: Vec::with_capacity(arguments.len()),
|
||||
constraints: Vec::with_capacity(1),
|
||||
delayed_is_open_constraints: vec![],
|
||||
};
|
||||
let mut vars = Vec::with_capacity(state.vars.capacity() + 1);
|
||||
let mut pattern_types = Vec::with_capacity(state.vars.capacity());
|
||||
|
|
|
@ -18,6 +18,7 @@ pub struct PatternState {
|
|||
pub headers: SendMap<Symbol, Loc<Type>>,
|
||||
pub vars: Vec<Variable>,
|
||||
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
|
||||
|
@ -180,7 +181,7 @@ pub fn constrain_pattern(
|
|||
// so, we know that "x" (in this case, a tag union) must be open.
|
||||
if could_be_a_tag_union(expected.get_type_ref()) {
|
||||
state
|
||||
.constraints
|
||||
.delayed_is_open_constraints
|
||||
.push(constraints.is_open_type(expected.get_type()));
|
||||
}
|
||||
}
|
||||
|
@ -191,7 +192,7 @@ pub fn constrain_pattern(
|
|||
Identifier(symbol) | Shadowed(_, _, symbol) => {
|
||||
if could_be_a_tag_union(expected.get_type_ref()) {
|
||||
state
|
||||
.constraints
|
||||
.delayed_is_open_constraints
|
||||
.push(constraints.is_open_type(expected.get_type_ref().clone()));
|
||||
}
|
||||
|
||||
|
@ -494,6 +495,9 @@ pub fn constrain_pattern(
|
|||
state.vars.push(*ext_var);
|
||||
state.constraints.push(whole_con);
|
||||
state.constraints.push(tag_con);
|
||||
state
|
||||
.constraints
|
||||
.append(&mut state.delayed_is_open_constraints);
|
||||
}
|
||||
|
||||
UnwrappedOpaque {
|
||||
|
|
|
@ -2,7 +2,10 @@
|
|||
//! http://moscova.inria.fr/~maranget/papers/warn/warn.pdf
|
||||
|
||||
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_std::RocDec;
|
||||
|
||||
|
@ -15,9 +18,9 @@ pub struct 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 {
|
||||
name: tag_name,
|
||||
name,
|
||||
tag_id: TagId(0),
|
||||
arity,
|
||||
}];
|
||||
|
@ -40,9 +43,24 @@ pub enum RenderAs {
|
|||
#[derive(Clone, Debug, PartialEq, Eq, Hash, Copy)]
|
||||
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)]
|
||||
pub struct Ctor {
|
||||
pub name: TagName,
|
||||
pub name: CtorName,
|
||||
pub tag_id: TagId,
|
||||
pub arity: usize,
|
||||
}
|
||||
|
|
|
@ -468,9 +468,7 @@ impl<'a> Formattable for Tag<'a> {
|
|||
use self::Tag::*;
|
||||
|
||||
match self {
|
||||
Global { args, .. } | Private { args, .. } => {
|
||||
args.iter().any(|arg| (&arg.value).is_multiline())
|
||||
}
|
||||
Global { args, .. } => args.iter().any(|arg| (&arg.value).is_multiline()),
|
||||
Tag::SpaceBefore(_, _) | Tag::SpaceAfter(_, _) => true,
|
||||
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::Malformed(raw) => {
|
||||
buf.indent(indent);
|
||||
|
|
|
@ -191,12 +191,26 @@ pub fn fmt_body<'a, 'buf>(
|
|||
buf.push_str(" =");
|
||||
if body.is_multiline() {
|
||||
match body {
|
||||
Expr::SpaceBefore(_, _) => {
|
||||
body.format_with_options(buf, Parens::NotNeeded, Newlines::Yes, indent + INDENT);
|
||||
}
|
||||
Expr::SpaceBefore(sub_def, spaces) => {
|
||||
let should_outdent = match sub_def {
|
||||
Expr::Record { .. } | Expr::List { .. } => {
|
||||
buf.newline();
|
||||
body.format_with_options(buf, Parens::NotNeeded, Newlines::Yes, indent + INDENT);
|
||||
let is_only_newlines = spaces.iter().all(|s| s.is_newline());
|
||||
is_only_newlines && sub_def.is_multiline()
|
||||
}
|
||||
_ => 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);
|
||||
|
|
|
@ -38,7 +38,6 @@ impl<'a> Formattable for Expr<'a> {
|
|||
| MalformedIdent(_, _)
|
||||
| MalformedClosure
|
||||
| GlobalTag(_)
|
||||
| PrivateTag(_)
|
||||
| OpaqueRef(_) => false,
|
||||
|
||||
// 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());
|
||||
|
||||
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;
|
||||
|
||||
for loc_arg in loc_args.iter() {
|
||||
buf.newline();
|
||||
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 {
|
||||
for loc_arg in loc_args.iter() {
|
||||
buf.spaces(1);
|
||||
|
@ -213,7 +272,7 @@ impl<'a> Formattable for Expr<'a> {
|
|||
buf.indent(indent);
|
||||
buf.push_str(string);
|
||||
}
|
||||
GlobalTag(string) | PrivateTag(string) | OpaqueRef(string) => {
|
||||
GlobalTag(string) | OpaqueRef(string) => {
|
||||
buf.indent(indent);
|
||||
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::DoubleSlash => buf.push_str("//"),
|
||||
called_via::BinOp::Percent => buf.push('%'),
|
||||
called_via::BinOp::DoublePercent => buf.push_str("%%"),
|
||||
called_via::BinOp::Plus => buf.push('+'),
|
||||
called_via::BinOp::Minus => buf.push('-'),
|
||||
called_via::BinOp::Equals => buf.push_str("=="),
|
||||
|
@ -847,8 +905,35 @@ fn fmt_closure<'a, 'buf>(
|
|||
}
|
||||
};
|
||||
|
||||
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>(
|
||||
buf: &mut Buf<'buf>,
|
||||
|
@ -1104,7 +1189,6 @@ fn sub_expr_requests_parens(expr: &Expr<'_>) -> bool {
|
|||
| BinOp::Slash
|
||||
| BinOp::DoubleSlash
|
||||
| BinOp::Percent
|
||||
| BinOp::DoublePercent
|
||||
| BinOp::Plus
|
||||
| BinOp::Minus
|
||||
| BinOp::Equals
|
||||
|
|
|
@ -29,7 +29,6 @@ impl<'a> Formattable for Pattern<'a> {
|
|||
|
||||
Pattern::Identifier(_)
|
||||
| Pattern::GlobalTag(_)
|
||||
| Pattern::PrivateTag(_)
|
||||
| Pattern::OpaqueRef(_)
|
||||
| Pattern::Apply(_, _)
|
||||
| Pattern::NumLiteral(..)
|
||||
|
@ -58,7 +57,7 @@ impl<'a> Formattable for Pattern<'a> {
|
|||
buf.indent(indent);
|
||||
buf.push_str(string)
|
||||
}
|
||||
GlobalTag(name) | PrivateTag(name) | OpaqueRef(name) => {
|
||||
GlobalTag(name) | OpaqueRef(name) => {
|
||||
buf.indent(indent);
|
||||
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]
|
||||
fn record_updating() {
|
||||
expr_formats_same(indoc!(
|
||||
|
@ -1300,6 +1686,27 @@ mod test_fmt {
|
|||
#[test]
|
||||
fn multi_line_list_def() {
|
||||
expr_formats_same(indoc!(
|
||||
r#"
|
||||
l = [
|
||||
1,
|
||||
2,
|
||||
]
|
||||
|
||||
l
|
||||
"#
|
||||
));
|
||||
|
||||
expr_formats_same(indoc!(
|
||||
r#"
|
||||
l =
|
||||
[ 1, 2 ]
|
||||
|
||||
l
|
||||
"#
|
||||
));
|
||||
|
||||
expr_formats_to(
|
||||
indoc!(
|
||||
r#"
|
||||
l =
|
||||
[
|
||||
|
@ -1309,7 +1716,18 @@ mod test_fmt {
|
|||
|
||||
l
|
||||
"#
|
||||
));
|
||||
),
|
||||
indoc!(
|
||||
r#"
|
||||
l = [
|
||||
1,
|
||||
2,
|
||||
]
|
||||
|
||||
l
|
||||
"#
|
||||
),
|
||||
);
|
||||
|
||||
expr_formats_to(
|
||||
indoc!(
|
||||
|
@ -1324,8 +1742,7 @@ mod test_fmt {
|
|||
),
|
||||
indoc!(
|
||||
r#"
|
||||
results =
|
||||
[
|
||||
results = [
|
||||
Ok 4,
|
||||
Ok 5,
|
||||
]
|
||||
|
@ -1418,6 +1835,45 @@ mod test_fmt {
|
|||
#[test]
|
||||
fn multi_line_record_def() {
|
||||
expr_formats_same(indoc!(
|
||||
r#"
|
||||
pos = {
|
||||
x: 4,
|
||||
y: 11,
|
||||
z: 16,
|
||||
}
|
||||
|
||||
pos
|
||||
"#
|
||||
));
|
||||
|
||||
expr_formats_same(indoc!(
|
||||
r#"
|
||||
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 =
|
||||
{
|
||||
|
@ -1428,7 +1884,19 @@ mod test_fmt {
|
|||
|
||||
pos
|
||||
"#
|
||||
));
|
||||
),
|
||||
indoc!(
|
||||
r#"
|
||||
pos = {
|
||||
x: 4,
|
||||
y: 11,
|
||||
z: 16,
|
||||
}
|
||||
|
||||
pos
|
||||
"#
|
||||
),
|
||||
);
|
||||
|
||||
expr_formats_to(
|
||||
indoc!(
|
||||
|
@ -1443,8 +1911,7 @@ mod test_fmt {
|
|||
),
|
||||
indoc!(
|
||||
r#"
|
||||
pos =
|
||||
{
|
||||
pos = {
|
||||
x: 5,
|
||||
y: 10,
|
||||
}
|
||||
|
@ -2537,7 +3004,7 @@ mod test_fmt {
|
|||
indoc!(
|
||||
r#"
|
||||
2 % 3
|
||||
%% 5
|
||||
// 5
|
||||
+ 7
|
||||
"#
|
||||
),
|
||||
|
@ -2545,7 +3012,7 @@ mod test_fmt {
|
|||
r#"
|
||||
2
|
||||
% 3
|
||||
%% 5
|
||||
// 5
|
||||
+ 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
|
||||
|
||||
#[test]
|
||||
|
|
|
@ -19,7 +19,7 @@ roc_mono = { path = "../mono" }
|
|||
roc_target = { path = "../roc_target" }
|
||||
roc_error_macros = { path = "../../error_macros" }
|
||||
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.
|
||||
# It looks like it breaks linking the generated objects.
|
||||
# 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) => {
|
||||
self.set_last_seen(*sym, stmt);
|
||||
}
|
||||
TagName::Private(sym) => {
|
||||
self.set_last_seen(*sym, stmt);
|
||||
}
|
||||
TagName::Global(_) => {}
|
||||
}
|
||||
for sym in *arguments {
|
||||
|
|
|
@ -18,4 +18,4 @@ roc_std = { path = "../../roc_std", default-features = false }
|
|||
morphic_lib = { path = "../../vendor/morphic_lib" }
|
||||
bumpalo = { version = "3.8.0", features = ["collections"] }
|
||||
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(),
|
||||
NumLt => bd.build_float_compare(OLT, lhs, rhs, "float_lt").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(),
|
||||
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() {
|
||||
if let Layout::RecursivePointer = field_layout {
|
||||
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() {
|
||||
let field_ptr = env
|
||||
.builder
|
||||
|
|
|
@ -1,7 +1,5 @@
|
|||
use crate::docs::DocEntry::DetachedDoc;
|
||||
use crate::docs::TypeAnnotation::{
|
||||
Apply, BoundVariable, Function, NoTypeAnn, ObscuredRecord, ObscuredTagUnion, Record, TagUnion,
|
||||
};
|
||||
use crate::docs::TypeAnnotation::{Apply, BoundVariable, Function, NoTypeAnn, Record, TagUnion};
|
||||
use crate::file::LoadedModule;
|
||||
use roc_can::scope::Scope;
|
||||
use roc_error_macros::todo_abilities;
|
||||
|
@ -274,27 +272,12 @@ fn type_to_docs(in_func_type_ann: bool, type_annotation: ast::TypeAnnotation) ->
|
|||
ast::TypeAnnotation::TagUnion { tags, ext } => {
|
||||
let mut tags_to_render: Vec<Tag> = Vec::new();
|
||||
|
||||
let mut any_tags_are_private = false;
|
||||
|
||||
for tag in tags.iter() {
|
||||
match tag_to_doc(in_func_type_ann, tag.value) {
|
||||
None => {
|
||||
any_tags_are_private = true;
|
||||
break;
|
||||
}
|
||||
Some(tag_ann) => {
|
||||
if let Some(tag_ann) = tag_to_doc(in_func_type_ann, tag.value) {
|
||||
tags_to_render.push(tag_ann);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if any_tags_are_private {
|
||||
if in_func_type_ann {
|
||||
ObscuredTagUnion
|
||||
} else {
|
||||
NoTypeAnn
|
||||
}
|
||||
} else {
|
||||
let extension = match ext {
|
||||
None => NoTypeAnn,
|
||||
Some(ext_type_ann) => type_to_docs(in_func_type_ann, ext_type_ann.value),
|
||||
|
@ -305,7 +288,6 @@ fn type_to_docs(in_func_type_ann: bool, type_annotation: ast::TypeAnnotation) ->
|
|||
extension: Box::new(extension),
|
||||
}
|
||||
}
|
||||
}
|
||||
ast::TypeAnnotation::BoundVariable(var_name) => BoundVariable(var_name.to_string()),
|
||||
ast::TypeAnnotation::Apply(module_name, type_name, type_ann_parts) => {
|
||||
let mut name = String::new();
|
||||
|
@ -328,26 +310,11 @@ fn type_to_docs(in_func_type_ann: bool, type_annotation: ast::TypeAnnotation) ->
|
|||
ast::TypeAnnotation::Record { fields, ext } => {
|
||||
let mut doc_fields = Vec::new();
|
||||
|
||||
let mut any_fields_include_private_tags = false;
|
||||
|
||||
for field in fields.items {
|
||||
match record_field_to_doc(in_func_type_ann, field.value) {
|
||||
None => {
|
||||
any_fields_include_private_tags = true;
|
||||
break;
|
||||
}
|
||||
Some(doc_field) => {
|
||||
if let Some(doc_field) = record_field_to_doc(in_func_type_ann, field.value) {
|
||||
doc_fields.push(doc_field);
|
||||
}
|
||||
}
|
||||
}
|
||||
if any_fields_include_private_tags {
|
||||
if in_func_type_ann {
|
||||
ObscuredRecord
|
||||
} else {
|
||||
NoTypeAnn
|
||||
}
|
||||
} else {
|
||||
let extension = match ext {
|
||||
None => NoTypeAnn,
|
||||
Some(ext_type_ann) => type_to_docs(in_func_type_ann, ext_type_ann.value),
|
||||
|
@ -358,7 +325,6 @@ fn type_to_docs(in_func_type_ann: bool, type_annotation: ast::TypeAnnotation) ->
|
|||
extension: Box::new(extension),
|
||||
}
|
||||
}
|
||||
}
|
||||
ast::TypeAnnotation::SpaceBefore(&sub_type_ann, _) => {
|
||||
type_to_docs(in_func_type_ann, sub_type_ann)
|
||||
}
|
||||
|
@ -404,8 +370,7 @@ fn record_field_to_doc(
|
|||
}
|
||||
}
|
||||
|
||||
// The Option here represents if it is private. Private tags
|
||||
// evaluate to `None`.
|
||||
// The Option here represents if it is malformed.
|
||||
fn tag_to_doc(in_func_ann: bool, tag: ast::Tag) -> Option<Tag> {
|
||||
match 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
|
||||
},
|
||||
}),
|
||||
ast::Tag::Private { .. } => None,
|
||||
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::Malformed(_) => None,
|
||||
|
|
|
@ -39,7 +39,7 @@ use roc_solve::solve;
|
|||
use roc_target::TargetInfo;
|
||||
use roc_types::solved_types::Solved;
|
||||
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::HashMap;
|
||||
use std::io;
|
||||
|
@ -4715,50 +4715,35 @@ fn default_aliases() -> roc_solve::solve::Aliases {
|
|||
|
||||
let mut var_store = VarStore::default();
|
||||
|
||||
// Num range := range
|
||||
{
|
||||
let symbol = Symbol::NUM_NUM;
|
||||
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 {
|
||||
region: Region::zero(),
|
||||
type_variables: vec![Loc::at_zero(("range".into(), tvar))],
|
||||
lambda_set_variables: Default::default(),
|
||||
recursion_variables: Default::default(),
|
||||
typ,
|
||||
typ: Type::Variable(tvar),
|
||||
kind: roc_types::types::AliasKind::Structural,
|
||||
};
|
||||
|
||||
solve_aliases.insert(symbol, alias);
|
||||
}
|
||||
|
||||
// FloatingPoint range : [ @FloatingPoint range ]
|
||||
// FloatingPoint range := []
|
||||
{
|
||||
let symbol = Symbol::NUM_FLOATINGPOINT;
|
||||
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 {
|
||||
region: Region::zero(),
|
||||
type_variables: vec![Loc::at_zero(("range".into(), tvar))],
|
||||
lambda_set_variables: Default::default(),
|
||||
recursion_variables: Default::default(),
|
||||
typ,
|
||||
kind: roc_types::types::AliasKind::Structural,
|
||||
typ: Type::Variable(tvar),
|
||||
kind: roc_types::types::AliasKind::Opaque,
|
||||
};
|
||||
|
||||
solve_aliases.insert(symbol, alias);
|
||||
|
@ -4773,11 +4758,13 @@ fn default_aliases() -> roc_solve::solve::Aliases {
|
|||
symbol: Symbol::NUM_NUM,
|
||||
type_arguments: vec![(
|
||||
"range".into(),
|
||||
Type::DelayedAlias(AliasCommon {
|
||||
Type::Alias {
|
||||
symbol: Symbol::NUM_INTEGER,
|
||||
type_arguments: vec![("range".into(), Type::Variable(tvar))],
|
||||
lambda_set_variables: vec![],
|
||||
}),
|
||||
actual: Box::new(Type::Variable(tvar)),
|
||||
kind: AliasKind::Opaque,
|
||||
},
|
||||
)],
|
||||
lambda_set_variables: vec![],
|
||||
});
|
||||
|
@ -4794,6 +4781,7 @@ fn default_aliases() -> roc_solve::solve::Aliases {
|
|||
solve_aliases.insert(symbol, alias);
|
||||
}
|
||||
|
||||
// Float range : Num (FloatingPoint range)
|
||||
{
|
||||
let symbol = Symbol::NUM_FLOAT;
|
||||
let tvar = var_store.fresh();
|
||||
|
@ -4802,11 +4790,13 @@ fn default_aliases() -> roc_solve::solve::Aliases {
|
|||
symbol: Symbol::NUM_NUM,
|
||||
type_arguments: vec![(
|
||||
"range".into(),
|
||||
Type::DelayedAlias(AliasCommon {
|
||||
Type::Alias {
|
||||
symbol: Symbol::NUM_FLOATINGPOINT,
|
||||
type_arguments: vec![("range".into(), Type::Variable(tvar))],
|
||||
lambda_set_variables: vec![],
|
||||
}),
|
||||
actual: Box::new(Type::Variable(tvar)),
|
||||
kind: AliasKind::Opaque,
|
||||
},
|
||||
)],
|
||||
lambda_set_variables: vec![],
|
||||
});
|
||||
|
@ -4823,24 +4813,17 @@ fn default_aliases() -> roc_solve::solve::Aliases {
|
|||
solve_aliases.insert(symbol, alias);
|
||||
}
|
||||
|
||||
// Integer range := range
|
||||
{
|
||||
let symbol = Symbol::NUM_INTEGER;
|
||||
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 {
|
||||
region: Region::zero(),
|
||||
type_variables: vec![Loc::at_zero(("range".into(), tvar))],
|
||||
lambda_set_variables: Default::default(),
|
||||
recursion_variables: Default::default(),
|
||||
typ,
|
||||
typ: Type::Variable(tvar),
|
||||
kind: roc_types::types::AliasKind::Structural,
|
||||
};
|
||||
|
||||
|
@ -4875,38 +4858,33 @@ fn default_aliases() -> roc_solve::solve::Aliases {
|
|||
solve_aliases.insert(symbol, alias);
|
||||
}
|
||||
|
||||
let mut unit_function = |alias_name: Symbol, at_tag_name: Symbol| {
|
||||
let typ = Type::TagUnion(
|
||||
vec![(TagName::Private(at_tag_name), vec![])],
|
||||
TypeExtension::Closed,
|
||||
);
|
||||
|
||||
let mut zero_opaque = |alias_name: Symbol| {
|
||||
let alias = Alias {
|
||||
region: Region::zero(),
|
||||
type_variables: vec![],
|
||||
lambda_set_variables: Default::default(),
|
||||
recursion_variables: Default::default(),
|
||||
typ,
|
||||
kind: roc_types::types::AliasKind::Structural,
|
||||
typ: Type::EmptyTagUnion,
|
||||
kind: AliasKind::Opaque,
|
||||
};
|
||||
|
||||
solve_aliases.insert(alias_name, alias);
|
||||
};
|
||||
|
||||
unit_function(Symbol::NUM_SIGNED8, Symbol::NUM_AT_SIGNED8);
|
||||
unit_function(Symbol::NUM_SIGNED16, Symbol::NUM_AT_SIGNED16);
|
||||
unit_function(Symbol::NUM_SIGNED32, Symbol::NUM_AT_SIGNED32);
|
||||
unit_function(Symbol::NUM_SIGNED64, Symbol::NUM_AT_SIGNED64);
|
||||
unit_function(Symbol::NUM_SIGNED128, Symbol::NUM_AT_SIGNED128);
|
||||
zero_opaque(Symbol::NUM_SIGNED8);
|
||||
zero_opaque(Symbol::NUM_SIGNED16);
|
||||
zero_opaque(Symbol::NUM_SIGNED32);
|
||||
zero_opaque(Symbol::NUM_SIGNED64);
|
||||
zero_opaque(Symbol::NUM_SIGNED128);
|
||||
|
||||
unit_function(Symbol::NUM_UNSIGNED8, Symbol::NUM_AT_UNSIGNED8);
|
||||
unit_function(Symbol::NUM_UNSIGNED16, Symbol::NUM_AT_UNSIGNED16);
|
||||
unit_function(Symbol::NUM_UNSIGNED32, Symbol::NUM_AT_UNSIGNED32);
|
||||
unit_function(Symbol::NUM_UNSIGNED64, Symbol::NUM_AT_UNSIGNED64);
|
||||
unit_function(Symbol::NUM_UNSIGNED128, Symbol::NUM_AT_UNSIGNED128);
|
||||
zero_opaque(Symbol::NUM_UNSIGNED8);
|
||||
zero_opaque(Symbol::NUM_UNSIGNED16);
|
||||
zero_opaque(Symbol::NUM_UNSIGNED32);
|
||||
zero_opaque(Symbol::NUM_UNSIGNED64);
|
||||
zero_opaque(Symbol::NUM_UNSIGNED128);
|
||||
|
||||
unit_function(Symbol::NUM_BINARY32, Symbol::NUM_AT_BINARY32);
|
||||
unit_function(Symbol::NUM_BINARY64, Symbol::NUM_AT_BINARY64);
|
||||
zero_opaque(Symbol::NUM_BINARY32);
|
||||
zero_opaque(Symbol::NUM_BINARY64);
|
||||
|
||||
solve_aliases
|
||||
}
|
||||
|
|
|
@ -757,9 +757,9 @@ mod test_load {
|
|||
r#"
|
||||
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:
|
||||
|
||||
3│ twenty = $Age 20
|
||||
3│ twenty = @Age 20
|
||||
^^^^
|
||||
|
||||
is imported from another module:
|
||||
|
@ -789,7 +789,7 @@ mod test_load {
|
|||
|
||||
The unwrapped opaque type Age referenced here:
|
||||
|
||||
5│ readAge = \$Age n -> n
|
||||
5│ readAge = \@Age n -> n
|
||||
^^^^
|
||||
|
||||
is imported from another module:
|
||||
|
|
|
@ -34,7 +34,6 @@ pub enum BinOp {
|
|||
Slash,
|
||||
DoubleSlash,
|
||||
Percent,
|
||||
DoublePercent,
|
||||
Plus,
|
||||
Minus,
|
||||
Equals,
|
||||
|
@ -58,8 +57,8 @@ impl BinOp {
|
|||
pub fn width(self) -> u16 {
|
||||
match self {
|
||||
Caret | Star | Slash | Percent | Plus | Minus | LessThan | GreaterThan => 1,
|
||||
DoubleSlash | DoublePercent | Equals | NotEquals | LessThanOrEq | GreaterThanOrEq
|
||||
| And | Or | Pizza => 2,
|
||||
DoubleSlash | Equals | NotEquals | LessThanOrEq | GreaterThanOrEq | And | Or
|
||||
| Pizza => 2,
|
||||
Assignment | IsAliasType | IsOpaqueType | Backpassing => unreachable!(),
|
||||
}
|
||||
}
|
||||
|
@ -97,9 +96,7 @@ impl BinOp {
|
|||
use self::Associativity::*;
|
||||
|
||||
match self {
|
||||
Pizza | Star | Slash | DoubleSlash | DoublePercent | Percent | Plus | Minus => {
|
||||
LeftAssociative
|
||||
}
|
||||
Pizza | Star | Slash | DoubleSlash | Percent | Plus | Minus => LeftAssociative,
|
||||
And | Or | Caret => RightAssociative,
|
||||
Equals | NotEquals | LessThan | GreaterThan | LessThanOrEq | GreaterThanOrEq => {
|
||||
NonAssociative
|
||||
|
@ -111,7 +108,7 @@ impl BinOp {
|
|||
fn precedence(self) -> u8 {
|
||||
match self {
|
||||
Caret => 7,
|
||||
Star | Slash | DoubleSlash | DoublePercent | Percent => 6,
|
||||
Star | Slash | DoubleSlash | Percent => 6,
|
||||
Plus | Minus => 5,
|
||||
Equals | NotEquals | LessThan | GreaterThan | LessThanOrEq | GreaterThanOrEq => 4,
|
||||
And => 3,
|
||||
|
@ -142,7 +139,6 @@ impl std::fmt::Display for BinOp {
|
|||
Slash => "/",
|
||||
DoubleSlash => "//",
|
||||
Percent => "%",
|
||||
DoublePercent => "%%",
|
||||
Plus => "+",
|
||||
Minus => "-",
|
||||
Equals => "==",
|
||||
|
|
|
@ -53,10 +53,6 @@ pub enum TagName {
|
|||
/// into integers. (Record field labels work the same way, for the same reason.)
|
||||
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
|
||||
Closure(Symbol),
|
||||
}
|
||||
|
@ -69,9 +65,6 @@ impl TagName {
|
|||
pub fn as_ident_str(&self, interns: &Interns, home: ModuleId) -> IdentStr {
|
||||
match self {
|
||||
TagName::Global(uppercase) => uppercase.as_ident_str().clone(),
|
||||
TagName::Private(symbol) => {
|
||||
symbol.fully_qualified(interns, home).as_ident_str().clone()
|
||||
}
|
||||
TagName::Closure(symbol) => {
|
||||
symbol.fully_qualified(interns, home).as_ident_str().clone()
|
||||
}
|
||||
|
|
|
@ -294,14 +294,17 @@ impl LowLevelWrapperType {
|
|||
Symbol::NUM_DIV_FLOAT_CHECKED => WrapperIsRequired,
|
||||
Symbol::NUM_DIV_CEIL => CanBeReplacedBy(NumDivCeilUnchecked),
|
||||
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_ABS => CanBeReplacedBy(NumAbs),
|
||||
Symbol::NUM_NEG => CanBeReplacedBy(NumNeg),
|
||||
Symbol::NUM_SIN => CanBeReplacedBy(NumSin),
|
||||
Symbol::NUM_COS => CanBeReplacedBy(NumCos),
|
||||
Symbol::NUM_SQRT => WrapperIsRequired,
|
||||
Symbol::NUM_LOG => WrapperIsRequired,
|
||||
Symbol::NUM_SQRT => CanBeReplacedBy(NumSqrtUnchecked),
|
||||
Symbol::NUM_SQRT_CHECKED => WrapperIsRequired,
|
||||
Symbol::NUM_LOG => CanBeReplacedBy(NumLogUnchecked),
|
||||
Symbol::NUM_LOG_CHECKED => WrapperIsRequired,
|
||||
Symbol::NUM_ROUND => CanBeReplacedBy(NumRound),
|
||||
Symbol::NUM_TO_FLOAT => CanBeReplacedBy(NumToFloat),
|
||||
Symbol::NUM_POW => CanBeReplacedBy(NumPow),
|
||||
|
|
|
@ -907,164 +907,143 @@ define_builtins! {
|
|||
}
|
||||
1 NUM: "Num" => {
|
||||
0 NUM_NUM: "Num" // the Num.Num type alias
|
||||
1 NUM_AT_NUM: "@Num" // the Num.@Num private tag
|
||||
2 NUM_I128: "I128" // the Num.I128 type alias
|
||||
3 NUM_U128: "U128" // the Num.U128 type alias
|
||||
4 NUM_I64: "I64" // the Num.I64 type alias
|
||||
5 NUM_U64: "U64" // the Num.U64 type alias
|
||||
6 NUM_I32: "I32" // the Num.I32 type alias
|
||||
7 NUM_U32: "U32" // the Num.U32 type alias
|
||||
8 NUM_I16: "I16" // the Num.I16 type alias
|
||||
9 NUM_U16: "U16" // the Num.U16 type alias
|
||||
10 NUM_I8: "I8" // the Num.I8 type alias
|
||||
11 NUM_U8: "U8" // the Num.U8 type alias
|
||||
12 NUM_INTEGER: "Integer" // Int : Num Integer
|
||||
13 NUM_AT_INTEGER: "@Integer" // the Int.@Integer private tag
|
||||
14 NUM_F64: "F64" // the Num.F64 type alias
|
||||
15 NUM_F32: "F32" // the Num.F32 type alias
|
||||
16 NUM_FLOATINGPOINT: "FloatingPoint" // Float : Num FloatingPoint
|
||||
17 NUM_AT_FLOATINGPOINT: "@FloatingPoint" // the Float.@FloatingPoint private tag
|
||||
18 NUM_MAX_FLOAT: "maxFloat"
|
||||
19 NUM_MIN_FLOAT: "minFloat"
|
||||
20 NUM_ABS: "abs"
|
||||
21 NUM_NEG: "neg"
|
||||
22 NUM_ADD: "add"
|
||||
23 NUM_SUB: "sub"
|
||||
24 NUM_MUL: "mul"
|
||||
25 NUM_LT: "isLt"
|
||||
26 NUM_LTE: "isLte"
|
||||
27 NUM_GT: "isGt"
|
||||
28 NUM_GTE: "isGte"
|
||||
29 NUM_TO_FLOAT: "toFloat"
|
||||
30 NUM_SIN: "sin"
|
||||
31 NUM_COS: "cos"
|
||||
32 NUM_TAN: "tan"
|
||||
33 NUM_IS_ZERO: "isZero"
|
||||
34 NUM_IS_EVEN: "isEven"
|
||||
35 NUM_IS_ODD: "isOdd"
|
||||
36 NUM_IS_POSITIVE: "isPositive"
|
||||
37 NUM_IS_NEGATIVE: "isNegative"
|
||||
38 NUM_REM: "rem"
|
||||
39 NUM_REM_CHECKED: "remChecked"
|
||||
40 NUM_DIV_FLOAT: "div"
|
||||
41 NUM_DIV_FLOAT_CHECKED: "divChecked"
|
||||
42 NUM_DIV_TRUNC: "divTrunc"
|
||||
43 NUM_DIV_TRUNC_CHECKED: "divTruncChecked"
|
||||
44 NUM_MOD_INT: "modInt"
|
||||
45 NUM_MOD_INT_CHECKED: "modIntChecked"
|
||||
46 NUM_MOD_FLOAT: "modFloat"
|
||||
47 NUM_MOD_FLOAT_CHECKED: "modFloatChecked"
|
||||
48 NUM_SQRT: "sqrt"
|
||||
49 NUM_SQRT_CHECKED: "sqrtChecked"
|
||||
50 NUM_LOG: "log"
|
||||
51 NUM_LOG_CHECKED: "logChecked"
|
||||
52 NUM_ROUND: "round"
|
||||
53 NUM_COMPARE: "compare"
|
||||
54 NUM_POW: "pow"
|
||||
55 NUM_CEILING: "ceiling"
|
||||
56 NUM_POW_INT: "powInt"
|
||||
57 NUM_FLOOR: "floor"
|
||||
58 NUM_ADD_WRAP: "addWrap"
|
||||
59 NUM_ADD_CHECKED: "addChecked"
|
||||
60 NUM_ADD_SATURATED: "addSaturated"
|
||||
61 NUM_ATAN: "atan"
|
||||
62 NUM_ACOS: "acos"
|
||||
63 NUM_ASIN: "asin"
|
||||
64 NUM_AT_SIGNED128: "@Signed128"
|
||||
65 NUM_SIGNED128: "Signed128"
|
||||
66 NUM_AT_SIGNED64: "@Signed64"
|
||||
67 NUM_SIGNED64: "Signed64"
|
||||
68 NUM_AT_SIGNED32: "@Signed32"
|
||||
69 NUM_SIGNED32: "Signed32"
|
||||
70 NUM_AT_SIGNED16: "@Signed16"
|
||||
71 NUM_SIGNED16: "Signed16"
|
||||
72 NUM_AT_SIGNED8: "@Signed8"
|
||||
73 NUM_SIGNED8: "Signed8"
|
||||
74 NUM_AT_UNSIGNED128: "@Unsigned128"
|
||||
75 NUM_UNSIGNED128: "Unsigned128"
|
||||
76 NUM_AT_UNSIGNED64: "@Unsigned64"
|
||||
77 NUM_UNSIGNED64: "Unsigned64"
|
||||
78 NUM_AT_UNSIGNED32: "@Unsigned32"
|
||||
79 NUM_UNSIGNED32: "Unsigned32"
|
||||
80 NUM_AT_UNSIGNED16: "@Unsigned16"
|
||||
81 NUM_UNSIGNED16: "Unsigned16"
|
||||
82 NUM_AT_UNSIGNED8: "@Unsigned8"
|
||||
83 NUM_UNSIGNED8: "Unsigned8"
|
||||
84 NUM_AT_BINARY64: "@Binary64"
|
||||
85 NUM_BINARY64: "Binary64"
|
||||
86 NUM_AT_BINARY32: "@Binary32"
|
||||
87 NUM_BINARY32: "Binary32"
|
||||
88 NUM_BITWISE_AND: "bitwiseAnd"
|
||||
89 NUM_BITWISE_XOR: "bitwiseXor"
|
||||
90 NUM_BITWISE_OR: "bitwiseOr"
|
||||
91 NUM_SHIFT_LEFT: "shiftLeftBy"
|
||||
92 NUM_SHIFT_RIGHT: "shiftRightBy"
|
||||
93 NUM_SHIFT_RIGHT_ZERO_FILL: "shiftRightZfBy"
|
||||
94 NUM_SUB_WRAP: "subWrap"
|
||||
95 NUM_SUB_CHECKED: "subChecked"
|
||||
96 NUM_SUB_SATURATED: "subSaturated"
|
||||
97 NUM_MUL_WRAP: "mulWrap"
|
||||
98 NUM_MUL_CHECKED: "mulChecked"
|
||||
99 NUM_INT: "Int"
|
||||
100 NUM_FLOAT: "Float"
|
||||
101 NUM_AT_NATURAL: "@Natural"
|
||||
102 NUM_NATURAL: "Natural"
|
||||
103 NUM_NAT: "Nat"
|
||||
104 NUM_INT_CAST: "intCast"
|
||||
105 NUM_IS_MULTIPLE_OF: "isMultipleOf"
|
||||
106 NUM_AT_DECIMAL: "@Decimal"
|
||||
107 NUM_DECIMAL: "Decimal"
|
||||
108 NUM_DEC: "Dec" // the Num.Dectype alias
|
||||
109 NUM_BYTES_TO_U16: "bytesToU16"
|
||||
110 NUM_BYTES_TO_U32: "bytesToU32"
|
||||
111 NUM_CAST_TO_NAT: "#castToNat"
|
||||
112 NUM_DIV_CEIL: "divCeil"
|
||||
113 NUM_DIV_CEIL_CHECKED: "divCeilChecked"
|
||||
114 NUM_TO_STR: "toStr"
|
||||
115 NUM_MIN_I8: "minI8"
|
||||
116 NUM_MAX_I8: "maxI8"
|
||||
117 NUM_MIN_U8: "minU8"
|
||||
118 NUM_MAX_U8: "maxU8"
|
||||
119 NUM_MIN_I16: "minI16"
|
||||
120 NUM_MAX_I16: "maxI16"
|
||||
121 NUM_MIN_U16: "minU16"
|
||||
122 NUM_MAX_U16: "maxU16"
|
||||
123 NUM_MIN_I32: "minI32"
|
||||
124 NUM_MAX_I32: "maxI32"
|
||||
125 NUM_MIN_U32: "minU32"
|
||||
126 NUM_MAX_U32: "maxU32"
|
||||
127 NUM_MIN_I64: "minI64"
|
||||
128 NUM_MAX_I64: "maxI64"
|
||||
129 NUM_MIN_U64: "minU64"
|
||||
130 NUM_MAX_U64: "maxU64"
|
||||
131 NUM_MIN_I128: "minI128"
|
||||
132 NUM_MAX_I128: "maxI128"
|
||||
133 NUM_TO_I8: "toI8"
|
||||
134 NUM_TO_I8_CHECKED: "toI8Checked"
|
||||
135 NUM_TO_I16: "toI16"
|
||||
136 NUM_TO_I16_CHECKED: "toI16Checked"
|
||||
137 NUM_TO_I32: "toI32"
|
||||
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"
|
||||
1 NUM_I128: "I128" // the Num.I128 type alias
|
||||
2 NUM_U128: "U128" // the Num.U128 type alias
|
||||
3 NUM_I64: "I64" // the Num.I64 type alias
|
||||
4 NUM_U64: "U64" // the Num.U64 type alias
|
||||
5 NUM_I32: "I32" // the Num.I32 type alias
|
||||
6 NUM_U32: "U32" // the Num.U32 type alias
|
||||
7 NUM_I16: "I16" // the Num.I16 type alias
|
||||
8 NUM_U16: "U16" // the Num.U16 type alias
|
||||
9 NUM_I8: "I8" // the Num.I8 type alias
|
||||
10 NUM_U8: "U8" // the Num.U8 type alias
|
||||
11 NUM_INTEGER: "Integer" // Int : Num Integer
|
||||
12 NUM_F64: "F64" // the Num.F64 type alias
|
||||
13 NUM_F32: "F32" // the Num.F32 type alias
|
||||
14 NUM_FLOATINGPOINT: "FloatingPoint" // Float : Num FloatingPoint
|
||||
15 NUM_MAX_FLOAT: "maxFloat"
|
||||
16 NUM_MIN_FLOAT: "minFloat"
|
||||
17 NUM_ABS: "abs"
|
||||
18 NUM_NEG: "neg"
|
||||
19 NUM_ADD: "add"
|
||||
20 NUM_SUB: "sub"
|
||||
21 NUM_MUL: "mul"
|
||||
22 NUM_LT: "isLt"
|
||||
23 NUM_LTE: "isLte"
|
||||
24 NUM_GT: "isGt"
|
||||
25 NUM_GTE: "isGte"
|
||||
26 NUM_TO_FLOAT: "toFloat"
|
||||
27 NUM_SIN: "sin"
|
||||
28 NUM_COS: "cos"
|
||||
29 NUM_TAN: "tan"
|
||||
30 NUM_IS_ZERO: "isZero"
|
||||
31 NUM_IS_EVEN: "isEven"
|
||||
32 NUM_IS_ODD: "isOdd"
|
||||
33 NUM_IS_POSITIVE: "isPositive"
|
||||
34 NUM_IS_NEGATIVE: "isNegative"
|
||||
35 NUM_REM: "rem"
|
||||
36 NUM_REM_CHECKED: "remChecked"
|
||||
37 NUM_DIV_FLOAT: "div"
|
||||
38 NUM_DIV_FLOAT_CHECKED: "divChecked"
|
||||
39 NUM_DIV_TRUNC: "divTrunc"
|
||||
40 NUM_DIV_TRUNC_CHECKED: "divTruncChecked"
|
||||
41 NUM_SQRT: "sqrt"
|
||||
42 NUM_SQRT_CHECKED: "sqrtChecked"
|
||||
43 NUM_LOG: "log"
|
||||
44 NUM_LOG_CHECKED: "logChecked"
|
||||
45 NUM_ROUND: "round"
|
||||
46 NUM_COMPARE: "compare"
|
||||
47 NUM_POW: "pow"
|
||||
48 NUM_CEILING: "ceiling"
|
||||
49 NUM_POW_INT: "powInt"
|
||||
50 NUM_FLOOR: "floor"
|
||||
51 NUM_ADD_WRAP: "addWrap"
|
||||
52 NUM_ADD_CHECKED: "addChecked"
|
||||
53 NUM_ADD_SATURATED: "addSaturated"
|
||||
54 NUM_ATAN: "atan"
|
||||
55 NUM_ACOS: "acos"
|
||||
56 NUM_ASIN: "asin"
|
||||
57 NUM_SIGNED128: "Signed128"
|
||||
58 NUM_SIGNED64: "Signed64"
|
||||
59 NUM_SIGNED32: "Signed32"
|
||||
60 NUM_SIGNED16: "Signed16"
|
||||
61 NUM_SIGNED8: "Signed8"
|
||||
62 NUM_UNSIGNED128: "Unsigned128"
|
||||
63 NUM_UNSIGNED64: "Unsigned64"
|
||||
64 NUM_UNSIGNED32: "Unsigned32"
|
||||
65 NUM_UNSIGNED16: "Unsigned16"
|
||||
66 NUM_UNSIGNED8: "Unsigned8"
|
||||
67 NUM_BINARY64: "Binary64"
|
||||
68 NUM_BINARY32: "Binary32"
|
||||
69 NUM_BITWISE_AND: "bitwiseAnd"
|
||||
70 NUM_BITWISE_XOR: "bitwiseXor"
|
||||
71 NUM_BITWISE_OR: "bitwiseOr"
|
||||
72 NUM_SHIFT_LEFT: "shiftLeftBy"
|
||||
73 NUM_SHIFT_RIGHT: "shiftRightBy"
|
||||
74 NUM_SHIFT_RIGHT_ZERO_FILL: "shiftRightZfBy"
|
||||
75 NUM_SUB_WRAP: "subWrap"
|
||||
76 NUM_SUB_CHECKED: "subChecked"
|
||||
77 NUM_SUB_SATURATED: "subSaturated"
|
||||
78 NUM_MUL_WRAP: "mulWrap"
|
||||
79 NUM_MUL_CHECKED: "mulChecked"
|
||||
80 NUM_INT: "Int"
|
||||
81 NUM_FLOAT: "Float"
|
||||
82 NUM_NATURAL: "Natural"
|
||||
83 NUM_NAT: "Nat"
|
||||
84 NUM_INT_CAST: "intCast"
|
||||
85 NUM_IS_MULTIPLE_OF: "isMultipleOf"
|
||||
86 NUM_DECIMAL: "Decimal"
|
||||
87 NUM_DEC: "Dec" // the Num.Dectype alias
|
||||
88 NUM_BYTES_TO_U16: "bytesToU16"
|
||||
89 NUM_BYTES_TO_U32: "bytesToU32"
|
||||
90 NUM_CAST_TO_NAT: "#castToNat"
|
||||
91 NUM_DIV_CEIL: "divCeil"
|
||||
92 NUM_DIV_CEIL_CHECKED: "divCeilChecked"
|
||||
93 NUM_TO_STR: "toStr"
|
||||
94 NUM_MIN_I8: "minI8"
|
||||
95 NUM_MAX_I8: "maxI8"
|
||||
96 NUM_MIN_U8: "minU8"
|
||||
97 NUM_MAX_U8: "maxU8"
|
||||
98 NUM_MIN_I16: "minI16"
|
||||
99 NUM_MAX_I16: "maxI16"
|
||||
100 NUM_MIN_U16: "minU16"
|
||||
101 NUM_MAX_U16: "maxU16"
|
||||
102 NUM_MIN_I32: "minI32"
|
||||
103 NUM_MAX_I32: "maxI32"
|
||||
104 NUM_MIN_U32: "minU32"
|
||||
105 NUM_MAX_U32: "maxU32"
|
||||
106 NUM_MIN_I64: "minI64"
|
||||
107 NUM_MAX_I64: "maxI64"
|
||||
108 NUM_MIN_U64: "minU64"
|
||||
109 NUM_MAX_U64: "maxU64"
|
||||
110 NUM_MIN_I128: "minI128"
|
||||
111 NUM_MAX_I128: "maxI128"
|
||||
112 NUM_TO_I8: "toI8"
|
||||
113 NUM_TO_I8_CHECKED: "toI8Checked"
|
||||
114 NUM_TO_I16: "toI16"
|
||||
115 NUM_TO_I16_CHECKED: "toI16Checked"
|
||||
116 NUM_TO_I32: "toI32"
|
||||
117 NUM_TO_I32_CHECKED: "toI32Checked"
|
||||
118 NUM_TO_I64: "toI64"
|
||||
119 NUM_TO_I64_CHECKED: "toI64Checked"
|
||||
120 NUM_TO_I128: "toI128"
|
||||
121 NUM_TO_I128_CHECKED: "toI128Checked"
|
||||
122 NUM_TO_U8: "toU8"
|
||||
123 NUM_TO_U8_CHECKED: "toU8Checked"
|
||||
124 NUM_TO_U16: "toU16"
|
||||
125 NUM_TO_U16_CHECKED: "toU16Checked"
|
||||
126 NUM_TO_U32: "toU32"
|
||||
127 NUM_TO_U32_CHECKED: "toU32Checked"
|
||||
128 NUM_TO_U64: "toU64"
|
||||
129 NUM_TO_U64_CHECKED: "toU64Checked"
|
||||
130 NUM_TO_U128: "toU128"
|
||||
131 NUM_TO_U128_CHECKED: "toU128Checked"
|
||||
132 NUM_TO_NAT: "toNat"
|
||||
133 NUM_TO_NAT_CHECKED: "toNatChecked"
|
||||
134 NUM_TO_F32: "toF32"
|
||||
135 NUM_TO_F32_CHECKED: "toF32Checked"
|
||||
136 NUM_TO_F64: "toF64"
|
||||
137 NUM_TO_F64_CHECKED: "toF64Checked"
|
||||
}
|
||||
2 BOOL: "Bool" => {
|
||||
0 BOOL_BOOL: "Bool" // the Bool.Bool type alias
|
||||
|
@ -1081,101 +1060,99 @@ define_builtins! {
|
|||
}
|
||||
3 STR: "Str" => {
|
||||
0 STR_STR: "Str" imported // the Str.Str type alias
|
||||
1 STR_AT_STR: "@Str" // the Str.@Str private tag
|
||||
2 STR_IS_EMPTY: "isEmpty"
|
||||
3 STR_APPEND: "#append" // unused
|
||||
4 STR_CONCAT: "concat"
|
||||
5 STR_JOIN_WITH: "joinWith"
|
||||
6 STR_SPLIT: "split"
|
||||
7 STR_COUNT_GRAPHEMES: "countGraphemes"
|
||||
8 STR_STARTS_WITH: "startsWith"
|
||||
9 STR_ENDS_WITH: "endsWith"
|
||||
10 STR_FROM_UTF8: "fromUtf8"
|
||||
11 STR_UT8_PROBLEM: "Utf8Problem" // the Utf8Problem type alias
|
||||
12 STR_UT8_BYTE_PROBLEM: "Utf8ByteProblem" // the Utf8ByteProblem type alias
|
||||
13 STR_TO_UTF8: "toUtf8"
|
||||
14 STR_STARTS_WITH_CODE_PT: "startsWithCodePt"
|
||||
15 STR_ALIAS_ANALYSIS_STATIC: "#aliasAnalysisStatic" // string with the static lifetime
|
||||
16 STR_FROM_UTF8_RANGE: "fromUtf8Range"
|
||||
17 STR_REPEAT: "repeat"
|
||||
18 STR_TRIM: "trim"
|
||||
19 STR_TRIM_LEFT: "trimLeft"
|
||||
20 STR_TRIM_RIGHT: "trimRight"
|
||||
21 STR_TO_DEC: "toDec"
|
||||
22 STR_TO_F64: "toF64"
|
||||
23 STR_TO_F32: "toF32"
|
||||
24 STR_TO_NAT: "toNat"
|
||||
25 STR_TO_U128: "toU128"
|
||||
26 STR_TO_I128: "toI128"
|
||||
27 STR_TO_U64: "toU64"
|
||||
28 STR_TO_I64: "toI64"
|
||||
29 STR_TO_U32: "toU32"
|
||||
30 STR_TO_I32: "toI32"
|
||||
31 STR_TO_U16: "toU16"
|
||||
32 STR_TO_I16: "toI16"
|
||||
33 STR_TO_U8: "toU8"
|
||||
34 STR_TO_I8: "toI8"
|
||||
1 STR_IS_EMPTY: "isEmpty"
|
||||
2 STR_APPEND: "#append" // unused
|
||||
3 STR_CONCAT: "concat"
|
||||
4 STR_JOIN_WITH: "joinWith"
|
||||
5 STR_SPLIT: "split"
|
||||
6 STR_COUNT_GRAPHEMES: "countGraphemes"
|
||||
7 STR_STARTS_WITH: "startsWith"
|
||||
8 STR_ENDS_WITH: "endsWith"
|
||||
9 STR_FROM_UTF8: "fromUtf8"
|
||||
10 STR_UT8_PROBLEM: "Utf8Problem" // the Utf8Problem type alias
|
||||
11 STR_UT8_BYTE_PROBLEM: "Utf8ByteProblem" // the Utf8ByteProblem type alias
|
||||
12 STR_TO_UTF8: "toUtf8"
|
||||
13 STR_STARTS_WITH_CODE_PT: "startsWithCodePt"
|
||||
14 STR_ALIAS_ANALYSIS_STATIC: "#aliasAnalysisStatic" // string with the static lifetime
|
||||
15 STR_FROM_UTF8_RANGE: "fromUtf8Range"
|
||||
16 STR_REPEAT: "repeat"
|
||||
17 STR_TRIM: "trim"
|
||||
18 STR_TRIM_LEFT: "trimLeft"
|
||||
19 STR_TRIM_RIGHT: "trimRight"
|
||||
20 STR_TO_DEC: "toDec"
|
||||
21 STR_TO_F64: "toF64"
|
||||
22 STR_TO_F32: "toF32"
|
||||
23 STR_TO_NAT: "toNat"
|
||||
24 STR_TO_U128: "toU128"
|
||||
25 STR_TO_I128: "toI128"
|
||||
26 STR_TO_U64: "toU64"
|
||||
27 STR_TO_I64: "toI64"
|
||||
28 STR_TO_U32: "toU32"
|
||||
29 STR_TO_I32: "toI32"
|
||||
30 STR_TO_U16: "toU16"
|
||||
31 STR_TO_I16: "toI16"
|
||||
32 STR_TO_U8: "toU8"
|
||||
33 STR_TO_I8: "toI8"
|
||||
}
|
||||
4 LIST: "List" => {
|
||||
0 LIST_LIST: "List" imported // the List.List type alias
|
||||
1 LIST_AT_LIST: "@List" // the List.@List private tag
|
||||
2 LIST_IS_EMPTY: "isEmpty"
|
||||
3 LIST_GET: "get"
|
||||
4 LIST_SET: "set"
|
||||
5 LIST_APPEND: "append"
|
||||
6 LIST_MAP: "map"
|
||||
7 LIST_LEN: "len"
|
||||
8 LIST_WALK_BACKWARDS: "walkBackwards"
|
||||
9 LIST_CONCAT: "concat"
|
||||
10 LIST_FIRST: "first"
|
||||
11 LIST_SINGLE: "single"
|
||||
12 LIST_REPEAT: "repeat"
|
||||
13 LIST_REVERSE: "reverse"
|
||||
14 LIST_PREPEND: "prepend"
|
||||
15 LIST_JOIN: "join"
|
||||
16 LIST_KEEP_IF: "keepIf"
|
||||
17 LIST_CONTAINS: "contains"
|
||||
18 LIST_SUM: "sum"
|
||||
19 LIST_WALK: "walk"
|
||||
20 LIST_LAST: "last"
|
||||
21 LIST_KEEP_OKS: "keepOks"
|
||||
22 LIST_KEEP_ERRS: "keepErrs"
|
||||
23 LIST_MAP_WITH_INDEX: "mapWithIndex"
|
||||
24 LIST_MAP2: "map2"
|
||||
25 LIST_MAP3: "map3"
|
||||
26 LIST_PRODUCT: "product"
|
||||
27 LIST_WALK_UNTIL: "walkUntil"
|
||||
28 LIST_RANGE: "range"
|
||||
29 LIST_SORT_WITH: "sortWith"
|
||||
30 LIST_DROP: "drop"
|
||||
31 LIST_SWAP: "swap"
|
||||
32 LIST_DROP_AT: "dropAt"
|
||||
33 LIST_DROP_LAST: "dropLast"
|
||||
34 LIST_MIN: "min"
|
||||
35 LIST_MIN_LT: "#minlt"
|
||||
36 LIST_MAX: "max"
|
||||
37 LIST_MAX_GT: "#maxGt"
|
||||
38 LIST_MAP4: "map4"
|
||||
39 LIST_DROP_FIRST: "dropFirst"
|
||||
40 LIST_JOIN_MAP: "joinMap"
|
||||
41 LIST_JOIN_MAP_CONCAT: "#joinMapConcat"
|
||||
42 LIST_ANY: "any"
|
||||
43 LIST_TAKE_FIRST: "takeFirst"
|
||||
44 LIST_TAKE_LAST: "takeLast"
|
||||
45 LIST_FIND: "find"
|
||||
46 LIST_FIND_RESULT: "#find_result" // symbol used in the definition of List.find
|
||||
47 LIST_SUBLIST: "sublist"
|
||||
48 LIST_INTERSPERSE: "intersperse"
|
||||
49 LIST_INTERSPERSE_CLOS: "#intersperseClos"
|
||||
50 LIST_SPLIT: "split"
|
||||
51 LIST_SPLIT_CLOS: "#splitClos"
|
||||
52 LIST_ALL: "all"
|
||||
53 LIST_DROP_IF: "dropIf"
|
||||
54 LIST_DROP_IF_PREDICATE: "#dropIfPred"
|
||||
55 LIST_SORT_ASC: "sortAsc"
|
||||
56 LIST_SORT_DESC: "sortDesc"
|
||||
57 LIST_SORT_DESC_COMPARE: "#sortDescCompare"
|
||||
58 LIST_REPLACE: "replace"
|
||||
1 LIST_IS_EMPTY: "isEmpty"
|
||||
2 LIST_GET: "get"
|
||||
3 LIST_SET: "set"
|
||||
4 LIST_APPEND: "append"
|
||||
5 LIST_MAP: "map"
|
||||
6 LIST_LEN: "len"
|
||||
7 LIST_WALK_BACKWARDS: "walkBackwards"
|
||||
8 LIST_CONCAT: "concat"
|
||||
9 LIST_FIRST: "first"
|
||||
10 LIST_SINGLE: "single"
|
||||
11 LIST_REPEAT: "repeat"
|
||||
12 LIST_REVERSE: "reverse"
|
||||
13 LIST_PREPEND: "prepend"
|
||||
14 LIST_JOIN: "join"
|
||||
15 LIST_KEEP_IF: "keepIf"
|
||||
16 LIST_CONTAINS: "contains"
|
||||
17 LIST_SUM: "sum"
|
||||
18 LIST_WALK: "walk"
|
||||
19 LIST_LAST: "last"
|
||||
20 LIST_KEEP_OKS: "keepOks"
|
||||
21 LIST_KEEP_ERRS: "keepErrs"
|
||||
22 LIST_MAP_WITH_INDEX: "mapWithIndex"
|
||||
23 LIST_MAP2: "map2"
|
||||
24 LIST_MAP3: "map3"
|
||||
25 LIST_PRODUCT: "product"
|
||||
26 LIST_WALK_UNTIL: "walkUntil"
|
||||
27 LIST_RANGE: "range"
|
||||
28 LIST_SORT_WITH: "sortWith"
|
||||
29 LIST_DROP: "drop"
|
||||
30 LIST_SWAP: "swap"
|
||||
31 LIST_DROP_AT: "dropAt"
|
||||
32 LIST_DROP_LAST: "dropLast"
|
||||
33 LIST_MIN: "min"
|
||||
34 LIST_MIN_LT: "#minlt"
|
||||
35 LIST_MAX: "max"
|
||||
36 LIST_MAX_GT: "#maxGt"
|
||||
37 LIST_MAP4: "map4"
|
||||
38 LIST_DROP_FIRST: "dropFirst"
|
||||
39 LIST_JOIN_MAP: "joinMap"
|
||||
40 LIST_JOIN_MAP_CONCAT: "#joinMapConcat"
|
||||
41 LIST_ANY: "any"
|
||||
42 LIST_TAKE_FIRST: "takeFirst"
|
||||
43 LIST_TAKE_LAST: "takeLast"
|
||||
44 LIST_FIND: "find"
|
||||
45 LIST_FIND_RESULT: "#find_result" // symbol used in the definition of List.find
|
||||
46 LIST_SUBLIST: "sublist"
|
||||
47 LIST_INTERSPERSE: "intersperse"
|
||||
48 LIST_INTERSPERSE_CLOS: "#intersperseClos"
|
||||
49 LIST_SPLIT: "split"
|
||||
50 LIST_SPLIT_CLOS: "#splitClos"
|
||||
51 LIST_ALL: "all"
|
||||
52 LIST_DROP_IF: "dropIf"
|
||||
53 LIST_DROP_IF_PREDICATE: "#dropIfPred"
|
||||
54 LIST_SORT_ASC: "sortAsc"
|
||||
55 LIST_SORT_DESC: "sortDesc"
|
||||
56 LIST_SORT_DESC_COMPARE: "#sortDescCompare"
|
||||
57 LIST_REPLACE: "replace"
|
||||
}
|
||||
5 RESULT: "Result" => {
|
||||
0 RESULT_RESULT: "Result" // the Result.Result type alias
|
||||
|
@ -1192,41 +1169,39 @@ define_builtins! {
|
|||
}
|
||||
6 DICT: "Dict" => {
|
||||
0 DICT_DICT: "Dict" imported // the Dict.Dict type alias
|
||||
1 DICT_AT_DICT: "@Dict" // the Dict.@Dict private tag
|
||||
2 DICT_EMPTY: "empty"
|
||||
3 DICT_SINGLE: "single"
|
||||
4 DICT_GET: "get"
|
||||
5 DICT_GET_RESULT: "#get_result" // symbol used in the definition of Dict.get
|
||||
6 DICT_WALK: "walk"
|
||||
7 DICT_INSERT: "insert"
|
||||
8 DICT_LEN: "len"
|
||||
1 DICT_EMPTY: "empty"
|
||||
2 DICT_SINGLE: "single"
|
||||
3 DICT_GET: "get"
|
||||
4 DICT_GET_RESULT: "#get_result" // symbol used in the definition of Dict.get
|
||||
5 DICT_WALK: "walk"
|
||||
6 DICT_INSERT: "insert"
|
||||
7 DICT_LEN: "len"
|
||||
|
||||
9 DICT_REMOVE: "remove"
|
||||
10 DICT_CONTAINS: "contains"
|
||||
11 DICT_KEYS: "keys"
|
||||
12 DICT_VALUES: "values"
|
||||
8 DICT_REMOVE: "remove"
|
||||
9 DICT_CONTAINS: "contains"
|
||||
10 DICT_KEYS: "keys"
|
||||
11 DICT_VALUES: "values"
|
||||
|
||||
13 DICT_UNION: "union"
|
||||
14 DICT_INTERSECTION: "intersection"
|
||||
15 DICT_DIFFERENCE: "difference"
|
||||
12 DICT_UNION: "union"
|
||||
13 DICT_INTERSECTION: "intersection"
|
||||
14 DICT_DIFFERENCE: "difference"
|
||||
}
|
||||
7 SET: "Set" => {
|
||||
0 SET_SET: "Set" imported // the Set.Set type alias
|
||||
1 SET_AT_SET: "@Set" // the Set.@Set private tag
|
||||
2 SET_EMPTY: "empty"
|
||||
3 SET_SINGLE: "single"
|
||||
4 SET_LEN: "len"
|
||||
5 SET_INSERT: "insert"
|
||||
6 SET_REMOVE: "remove"
|
||||
7 SET_UNION: "union"
|
||||
8 SET_DIFFERENCE: "difference"
|
||||
9 SET_INTERSECTION: "intersection"
|
||||
10 SET_TO_LIST: "toList"
|
||||
11 SET_FROM_LIST: "fromList"
|
||||
12 SET_WALK: "walk"
|
||||
13 SET_WALK_USER_FUNCTION: "#walk_user_function"
|
||||
14 SET_CONTAINS: "contains"
|
||||
15 SET_TO_DICT: "toDict"
|
||||
1 SET_EMPTY: "empty"
|
||||
2 SET_SINGLE: "single"
|
||||
3 SET_LEN: "len"
|
||||
4 SET_INSERT: "insert"
|
||||
5 SET_REMOVE: "remove"
|
||||
6 SET_UNION: "union"
|
||||
7 SET_DIFFERENCE: "difference"
|
||||
8 SET_INTERSECTION: "intersection"
|
||||
9 SET_TO_LIST: "toList"
|
||||
10 SET_FROM_LIST: "fromList"
|
||||
11 SET_WALK: "walk"
|
||||
12 SET_WALK_USER_FUNCTION: "#walk_user_function"
|
||||
13 SET_CONTAINS: "contains"
|
||||
14 SET_TO_DICT: "toDict"
|
||||
}
|
||||
8 BOX: "Box" => {
|
||||
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 roc_builtins::bitcode::{FloatWidth, IntWidth};
|
||||
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::low_level::LowLevel;
|
||||
use roc_module::symbol::Symbol;
|
||||
|
@ -82,7 +82,7 @@ enum GuardedTest<'a> {
|
|||
enum Test<'a> {
|
||||
IsCtor {
|
||||
tag_id: TagIdIntType,
|
||||
tag_name: TagName,
|
||||
ctor_name: CtorName,
|
||||
union: roc_exhaustive::Union,
|
||||
arguments: Vec<(Pattern<'a>, Layout<'a>)>,
|
||||
},
|
||||
|
@ -512,7 +512,7 @@ fn test_at_path<'a>(
|
|||
render_as: RenderAs::Tag,
|
||||
alternatives: vec![Ctor {
|
||||
tag_id: TagId(0),
|
||||
name: TagName::Global(RECORD_TAG_NAME.into()),
|
||||
name: CtorName::Tag(TagName::Global(RECORD_TAG_NAME.into())),
|
||||
arity: destructs.len(),
|
||||
}],
|
||||
};
|
||||
|
@ -532,7 +532,7 @@ fn test_at_path<'a>(
|
|||
|
||||
IsCtor {
|
||||
tag_id: 0,
|
||||
tag_name: TagName::Global(RECORD_TAG_NAME.into()),
|
||||
ctor_name: CtorName::Tag(TagName::Global(RECORD_TAG_NAME.into())),
|
||||
union,
|
||||
arguments,
|
||||
}
|
||||
|
@ -543,11 +543,12 @@ fn test_at_path<'a>(
|
|||
arguments,
|
||||
} => {
|
||||
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 {
|
||||
tag_id,
|
||||
tag_name: tag_name.clone(),
|
||||
ctor_name: CtorName::Tag(tag_name.clone()),
|
||||
union,
|
||||
arguments: arguments.to_vec(),
|
||||
}
|
||||
|
@ -561,7 +562,7 @@ fn test_at_path<'a>(
|
|||
..
|
||||
} => IsCtor {
|
||||
tag_id: *tag_id,
|
||||
tag_name: tag_name.clone(),
|
||||
ctor_name: CtorName::Tag(tag_name.clone()),
|
||||
union: union.clone(),
|
||||
arguments: arguments.to_vec(),
|
||||
},
|
||||
|
@ -571,14 +572,14 @@ fn test_at_path<'a>(
|
|||
render_as: RenderAs::Tag,
|
||||
alternatives: vec![Ctor {
|
||||
tag_id: TagId(0),
|
||||
name: TagName::Private(*opaque),
|
||||
name: CtorName::Opaque(*opaque),
|
||||
arity: 1,
|
||||
}],
|
||||
};
|
||||
|
||||
IsCtor {
|
||||
tag_id: 0,
|
||||
tag_name: TagName::Private(*opaque),
|
||||
ctor_name: CtorName::Opaque(*opaque),
|
||||
union,
|
||||
arguments: vec![(**argument).clone()],
|
||||
}
|
||||
|
@ -680,11 +681,11 @@ fn to_relevant_branch_help<'a>(
|
|||
|
||||
RecordDestructure(destructs, _) => match test {
|
||||
IsCtor {
|
||||
tag_name: test_name,
|
||||
ctor_name: test_name,
|
||||
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 pattern = match destruct.typ {
|
||||
DestructType::Guard(guard) => guard.clone(),
|
||||
|
@ -713,11 +714,11 @@ fn to_relevant_branch_help<'a>(
|
|||
|
||||
OpaqueUnwrap { opaque, argument } => match test {
|
||||
IsCtor {
|
||||
tag_name: test_opaque_tag_name,
|
||||
ctor_name: test_opaque_tag_name,
|
||||
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;
|
||||
|
||||
|
@ -744,10 +745,10 @@ fn to_relevant_branch_help<'a>(
|
|||
..
|
||||
} => match test {
|
||||
IsCtor {
|
||||
tag_name: test_name,
|
||||
ctor_name: test_name,
|
||||
tag_id: test_id,
|
||||
..
|
||||
} if &tag_name == test_name => {
|
||||
} if test_name.is_tag(&tag_name) => {
|
||||
let tag_id = 0;
|
||||
debug_assert_eq!(tag_id, *test_id);
|
||||
|
||||
|
@ -785,10 +786,10 @@ fn to_relevant_branch_help<'a>(
|
|||
} => {
|
||||
match test {
|
||||
IsCtor {
|
||||
tag_name: test_name,
|
||||
ctor_name: test_name,
|
||||
tag_id: test_id,
|
||||
..
|
||||
} if &tag_name == test_name => {
|
||||
} if test_name.is_tag(&tag_name) => {
|
||||
debug_assert_eq!(tag_id, *test_id);
|
||||
|
||||
// the test matches the constructor of this pattern
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
use crate::ir::DestructType;
|
||||
use roc_collections::all::HumanIndex;
|
||||
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_region::all::{Loc, Region};
|
||||
|
@ -45,7 +45,7 @@ fn simplify(pattern: &crate::ir::Pattern) -> Pattern {
|
|||
let union = Union {
|
||||
render_as: RenderAs::Record(field_names),
|
||||
alternatives: vec![Ctor {
|
||||
name: TagName::Global("#Record".into()),
|
||||
name: CtorName::Tag(TagName::Global("#Record".into())),
|
||||
tag_id,
|
||||
arity: destructures.len(),
|
||||
}],
|
||||
|
@ -62,7 +62,7 @@ fn simplify(pattern: &crate::ir::Pattern) -> Pattern {
|
|||
let simplified_args: std::vec::Vec<_> =
|
||||
arguments.iter().map(|v| simplify(&v.0)).collect();
|
||||
Ctor(
|
||||
Union::newtype_wrapper(tag_name.clone(), arguments.len()),
|
||||
Union::newtype_wrapper(CtorName::Tag(tag_name.clone()), arguments.len()),
|
||||
TagId(tag_id),
|
||||
simplified_args,
|
||||
)
|
||||
|
@ -87,7 +87,7 @@ fn simplify(pattern: &crate::ir::Pattern) -> Pattern {
|
|||
let union = Union {
|
||||
render_as: RenderAs::Opaque,
|
||||
alternatives: vec![Ctor {
|
||||
name: TagName::Private(*opaque),
|
||||
name: CtorName::Opaque(*opaque),
|
||||
tag_id,
|
||||
arity: 1,
|
||||
}],
|
||||
|
@ -169,7 +169,7 @@ fn to_nonredundant_rows(
|
|||
render_as: RenderAs::Guard,
|
||||
alternatives: vec![Ctor {
|
||||
tag_id,
|
||||
name: TagName::Global("#Guard".into()),
|
||||
name: CtorName::Tag(TagName::Global("#Guard".into())),
|
||||
arity: 2,
|
||||
}],
|
||||
};
|
||||
|
|
|
@ -10,7 +10,7 @@ use roc_builtins::bitcode::{FloatWidth, IntWidth};
|
|||
use roc_can::abilities::AbilitiesStore;
|
||||
use roc_can::expr::{ClosureData, IntValue};
|
||||
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::low_level::LowLevel;
|
||||
use roc_module::symbol::{IdentIds, ModuleId, Symbol};
|
||||
|
@ -1656,7 +1656,6 @@ impl<'a> Expr<'a> {
|
|||
} => {
|
||||
let doc_tag = match tag_name {
|
||||
TagName::Global(s) => alloc.text(s.as_str()),
|
||||
TagName::Private(s) => symbol_to_doc(alloc, *s),
|
||||
TagName::Closure(s) => alloc
|
||||
.text("ClosureTag(")
|
||||
.append(symbol_to_doc(alloc, *s))
|
||||
|
@ -1678,7 +1677,6 @@ impl<'a> Expr<'a> {
|
|||
} => {
|
||||
let doc_tag = match tag_name {
|
||||
TagName::Global(s) => alloc.text(s.as_str()),
|
||||
TagName::Private(s) => alloc.text(format!("{}", s)),
|
||||
TagName::Closure(s) => alloc
|
||||
.text("ClosureTag(")
|
||||
.append(symbol_to_doc(alloc, *s))
|
||||
|
@ -3499,7 +3497,15 @@ pub fn with_hole<'a>(
|
|||
|
||||
OpaqueRef { argument, .. } => {
|
||||
let (arg_var, loc_arg_expr) = *argument;
|
||||
with_hole(
|
||||
|
||||
match can_reuse_symbol(env, procs, &loc_arg_expr.value) {
|
||||
// Opaques decay to their argument.
|
||||
ReuseSymbol::Value(real_name) => {
|
||||
let mut result = hole.clone();
|
||||
substitute_in_exprs(arena, &mut result, assigned, real_name);
|
||||
result
|
||||
}
|
||||
_ => with_hole(
|
||||
env,
|
||||
loc_arg_expr.value,
|
||||
arg_var,
|
||||
|
@ -3507,7 +3513,8 @@ pub fn with_hole<'a>(
|
|||
layout_cache,
|
||||
assigned,
|
||||
hole,
|
||||
)
|
||||
),
|
||||
}
|
||||
}
|
||||
|
||||
Record {
|
||||
|
@ -8030,7 +8037,7 @@ fn from_can_pattern_help<'a>(
|
|||
render_as: RenderAs::Tag,
|
||||
alternatives: vec![Ctor {
|
||||
tag_id: TagId(0),
|
||||
name: tag_name.clone(),
|
||||
name: CtorName::Tag(tag_name.clone()),
|
||||
arity: 0,
|
||||
}],
|
||||
},
|
||||
|
@ -8043,12 +8050,12 @@ fn from_can_pattern_help<'a>(
|
|||
alternatives: vec![
|
||||
Ctor {
|
||||
tag_id: TagId(0),
|
||||
name: ffalse,
|
||||
name: CtorName::Tag(ffalse),
|
||||
arity: 0,
|
||||
},
|
||||
Ctor {
|
||||
tag_id: TagId(1),
|
||||
name: ttrue,
|
||||
name: CtorName::Tag(ttrue),
|
||||
arity: 0,
|
||||
},
|
||||
],
|
||||
|
@ -8064,7 +8071,7 @@ fn from_can_pattern_help<'a>(
|
|||
for (i, tag_name) in tag_names.into_iter().enumerate() {
|
||||
ctors.push(Ctor {
|
||||
tag_id: TagId(i as _),
|
||||
name: tag_name,
|
||||
name: CtorName::Tag(tag_name),
|
||||
arity: 0,
|
||||
})
|
||||
}
|
||||
|
@ -8155,7 +8162,7 @@ fn from_can_pattern_help<'a>(
|
|||
for (i, (tag_name, args)) in tags.iter().enumerate() {
|
||||
ctors.push(Ctor {
|
||||
tag_id: TagId(i as _),
|
||||
name: tag_name.clone(),
|
||||
name: CtorName::Tag(tag_name.clone()),
|
||||
arity: args.len(),
|
||||
})
|
||||
}
|
||||
|
@ -8206,7 +8213,7 @@ fn from_can_pattern_help<'a>(
|
|||
for (i, (tag_name, args)) in tags.iter().enumerate() {
|
||||
ctors.push(Ctor {
|
||||
tag_id: TagId(i as _),
|
||||
name: tag_name.clone(),
|
||||
name: CtorName::Tag(tag_name.clone()),
|
||||
// don't include tag discriminant in arity
|
||||
arity: args.len() - 1,
|
||||
})
|
||||
|
@ -8251,7 +8258,7 @@ fn from_can_pattern_help<'a>(
|
|||
|
||||
ctors.push(Ctor {
|
||||
tag_id: TagId(0),
|
||||
name: tag_name.clone(),
|
||||
name: CtorName::Tag(tag_name.clone()),
|
||||
arity: fields.len(),
|
||||
});
|
||||
|
||||
|
@ -8298,7 +8305,7 @@ fn from_can_pattern_help<'a>(
|
|||
if i == nullable_id as usize {
|
||||
ctors.push(Ctor {
|
||||
tag_id: TagId(i as _),
|
||||
name: nullable_name.clone(),
|
||||
name: CtorName::Tag(nullable_name.clone()),
|
||||
// don't include tag discriminant in arity
|
||||
arity: 0,
|
||||
});
|
||||
|
@ -8308,7 +8315,7 @@ fn from_can_pattern_help<'a>(
|
|||
|
||||
ctors.push(Ctor {
|
||||
tag_id: TagId(i as _),
|
||||
name: tag_name.clone(),
|
||||
name: CtorName::Tag(tag_name.clone()),
|
||||
// don't include tag discriminant in arity
|
||||
arity: args.len() - 1,
|
||||
});
|
||||
|
@ -8319,7 +8326,7 @@ fn from_can_pattern_help<'a>(
|
|||
if i == nullable_id as usize {
|
||||
ctors.push(Ctor {
|
||||
tag_id: TagId(i as _),
|
||||
name: nullable_name.clone(),
|
||||
name: CtorName::Tag(nullable_name.clone()),
|
||||
// don't include tag discriminant in arity
|
||||
arity: 0,
|
||||
});
|
||||
|
@ -8369,13 +8376,13 @@ fn from_can_pattern_help<'a>(
|
|||
|
||||
ctors.push(Ctor {
|
||||
tag_id: TagId(nullable_id as _),
|
||||
name: nullable_name.clone(),
|
||||
name: CtorName::Tag(nullable_name.clone()),
|
||||
arity: 0,
|
||||
});
|
||||
|
||||
ctors.push(Ctor {
|
||||
tag_id: TagId(!nullable_id as _),
|
||||
name: nullable_name.clone(),
|
||||
name: CtorName::Tag(nullable_name.clone()),
|
||||
// FIXME drop tag
|
||||
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)
|
||||
}
|
||||
|
||||
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() {
|
||||
roc_target::PtrWidth::Bytes4 => IntWidth::U32,
|
||||
roc_target::PtrWidth::Bytes8 => IntWidth::U64,
|
||||
|
|
|
@ -311,6 +311,50 @@ impl<'a> UnionLayout<'a> {
|
|||
.append(alloc.intersperse(tags_doc, ", "))
|
||||
.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"),
|
||||
}
|
||||
}
|
||||
|
@ -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> {
|
||||
pub const VOID: Self = Layout::Union(UnionLayout::NonRecursive(&[]));
|
||||
pub const UNIT: Self = Layout::Struct {
|
||||
|
@ -971,12 +1025,24 @@ impl<'a> Layout<'a> {
|
|||
}
|
||||
|
||||
match symbol {
|
||||
Symbol::NUM_DECIMAL | Symbol::NUM_AT_DECIMAL => {
|
||||
return Ok(Layout::Builtin(Builtin::Decimal))
|
||||
Symbol::NUM_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 => {
|
||||
return Ok(Layout::usize(env.target_info))
|
||||
Symbol::NUM_NUM | Symbol::NUM_INT | Symbol::NUM_INTEGER
|
||||
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),
|
||||
|
@ -1645,7 +1711,7 @@ fn layout_from_flat_type<'a>(
|
|||
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
|
||||
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));
|
||||
|
||||
Ok(layout_from_tag_union(arena, &tags, subs, env.target_info))
|
||||
Ok(layout_from_tag_union(env, &tags))
|
||||
}
|
||||
FunctionOrTagUnion(tag_name, _, ext_var) => {
|
||||
debug_assert!(
|
||||
|
@ -1742,7 +1808,7 @@ fn layout_from_flat_type<'a>(
|
|||
let union_tags = UnionTags::from_tag_name_index(tag_name);
|
||||
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) => {
|
||||
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>(
|
||||
arena: &'a Bump,
|
||||
env: &mut Env<'a, '_>,
|
||||
tags_list: &[(&'_ TagName, &[Variable])],
|
||||
opt_rec_var: Option<Variable>,
|
||||
subs: &Subs,
|
||||
target_info: TargetInfo,
|
||||
) -> UnionVariant<'a> {
|
||||
// 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));
|
||||
|
||||
let mut env = Env {
|
||||
arena,
|
||||
subs,
|
||||
seen: Vec::new_in(arena),
|
||||
target_info,
|
||||
};
|
||||
|
||||
match tags_list.len() {
|
||||
0 => {
|
||||
// trying to instantiate a type with no values
|
||||
|
@ -2098,18 +2155,10 @@ fn union_sorted_tags_help_new<'a>(
|
|||
let tag_name = tag_name.clone();
|
||||
|
||||
// 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
|
||||
match tag_name {
|
||||
TagName::Private(Symbol::NUM_AT_NUM) => {
|
||||
let var = arguments[0];
|
||||
layouts
|
||||
.push(unwrap_num_tag(subs, var, target_info).expect("invalid num layout"));
|
||||
}
|
||||
_ => {
|
||||
for &var in arguments {
|
||||
match Layout::from_var(&mut env, var) {
|
||||
match Layout::from_var(env, var) {
|
||||
Ok(layout) => {
|
||||
layouts.push(layout);
|
||||
}
|
||||
|
@ -2125,12 +2174,10 @@ fn union_sorted_tags_help_new<'a>(
|
|||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
layouts.sort_by(|layout1, layout2| {
|
||||
let size1 = layout1.alignment_bytes(target_info);
|
||||
let size2 = layout2.alignment_bytes(target_info);
|
||||
let size1 = layout1.alignment_bytes(env.target_info);
|
||||
let size2 = layout2.alignment_bytes(env.target_info);
|
||||
|
||||
size2.cmp(&size1)
|
||||
});
|
||||
|
@ -2151,7 +2198,7 @@ fn union_sorted_tags_help_new<'a>(
|
|||
}
|
||||
num_tags => {
|
||||
// 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 nullable: Option<(TagIdIntType, TagName)> = None;
|
||||
|
@ -2174,17 +2221,19 @@ fn union_sorted_tags_help_new<'a>(
|
|||
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 {
|
||||
match Layout::from_var(&mut env, var) {
|
||||
match Layout::from_var(env, var) {
|
||||
Ok(layout) => {
|
||||
has_any_arguments = true;
|
||||
|
||||
// make sure to not unroll recursive types!
|
||||
let self_recursion = opt_rec_var.is_some()
|
||||
&& subs.get_root_key_without_compacting(var)
|
||||
== subs.get_root_key_without_compacting(opt_rec_var.unwrap())
|
||||
&& env.subs.get_root_key_without_compacting(var)
|
||||
== env
|
||||
.subs
|
||||
.get_root_key_without_compacting(opt_rec_var.unwrap())
|
||||
&& is_recursive_tag_union(&layout);
|
||||
|
||||
if self_recursion {
|
||||
|
@ -2207,8 +2256,8 @@ fn union_sorted_tags_help_new<'a>(
|
|||
}
|
||||
|
||||
arg_layouts.sort_by(|layout1, layout2| {
|
||||
let size1 = layout1.alignment_bytes(target_info);
|
||||
let size2 = layout2.alignment_bytes(target_info);
|
||||
let size1 = layout1.alignment_bytes(env.target_info);
|
||||
let size2 = layout2.alignment_bytes(env.target_info);
|
||||
|
||||
size2.cmp(&size1)
|
||||
});
|
||||
|
@ -2229,7 +2278,7 @@ fn union_sorted_tags_help_new<'a>(
|
|||
3..=MAX_ENUM_SIZE if !has_any_arguments => {
|
||||
// type can be stored in a byte
|
||||
// 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 {
|
||||
tag_names.push(tag_name);
|
||||
|
@ -2303,15 +2352,6 @@ pub fn union_sorted_tags_help<'a>(
|
|||
let mut layouts = Vec::with_capacity_in(tags_vec.len(), arena);
|
||||
let mut contains_zero_sized = false;
|
||||
|
||||
// special-case NUM_AT_NUM: if its argument is a FlexVar, make it Int
|
||||
match tag_name {
|
||||
TagName::Private(Symbol::NUM_AT_NUM) => {
|
||||
layouts.push(
|
||||
unwrap_num_tag(subs, arguments[0], target_info)
|
||||
.expect("invalid num layout"),
|
||||
);
|
||||
}
|
||||
_ => {
|
||||
for var in arguments {
|
||||
match Layout::from_var(&mut env, var) {
|
||||
Ok(layout) => {
|
||||
|
@ -2334,8 +2374,6 @@ pub fn union_sorted_tags_help<'a>(
|
|||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
layouts.sort_by(|layout1, layout2| {
|
||||
let size1 = layout1.alignment_bytes(target_info);
|
||||
|
@ -2488,27 +2526,12 @@ pub fn union_sorted_tags_help<'a>(
|
|||
}
|
||||
}
|
||||
|
||||
fn layout_from_newtype<'a>(
|
||||
arena: &'a Bump,
|
||||
tags: &UnsortedUnionTags,
|
||||
subs: &Subs,
|
||||
target_info: TargetInfo,
|
||||
) -> Layout<'a> {
|
||||
debug_assert!(tags.is_newtype_wrapper(subs));
|
||||
fn layout_from_newtype<'a>(env: &mut Env<'a, '_>, tags: &UnsortedUnionTags) -> Layout<'a> {
|
||||
debug_assert!(tags.is_newtype_wrapper(env.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) {
|
||||
unwrap_num_tag(subs, var, target_info).expect("invalid Num argument")
|
||||
} else {
|
||||
let mut env = Env {
|
||||
arena,
|
||||
subs,
|
||||
seen: Vec::new_in(arena),
|
||||
target_info,
|
||||
};
|
||||
|
||||
match Layout::from_var(&mut env, var) {
|
||||
match Layout::from_var(env, var) {
|
||||
Ok(layout) => layout,
|
||||
Err(LayoutProblem::UnresolvedTypeVar(_)) => {
|
||||
// If we encounter an unbound type var (e.g. `Ok *`)
|
||||
|
@ -2523,34 +2546,18 @@ fn layout_from_newtype<'a>(
|
|||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn layout_from_tag_union<'a>(
|
||||
arena: &'a Bump,
|
||||
tags: &UnsortedUnionTags,
|
||||
subs: &Subs,
|
||||
target_info: TargetInfo,
|
||||
) -> Layout<'a> {
|
||||
fn layout_from_tag_union<'a>(env: &mut Env<'a, '_>, tags: &UnsortedUnionTags) -> Layout<'a> {
|
||||
use UnionVariant::*;
|
||||
|
||||
if tags.is_newtype_wrapper(subs) {
|
||||
return layout_from_newtype(arena, tags, subs, target_info);
|
||||
if tags.is_newtype_wrapper(env.subs) {
|
||||
return layout_from_newtype(env, tags);
|
||||
}
|
||||
|
||||
let tags_vec = &tags.tags;
|
||||
|
||||
match tags_vec.get(0) {
|
||||
Some((tag_name, arguments)) if *tag_name == &TagName::Private(Symbol::NUM_AT_NUM) => {
|
||||
debug_assert_eq!(arguments.len(), 1);
|
||||
|
||||
let &var = arguments.iter().next().unwrap();
|
||||
|
||||
unwrap_num_tag(subs, var, target_info).expect("invalid Num argument")
|
||||
}
|
||||
_ => {
|
||||
let opt_rec_var = None;
|
||||
let variant =
|
||||
union_sorted_tags_help_new(arena, tags_vec, opt_rec_var, subs, target_info);
|
||||
let variant = union_sorted_tags_help_new(env, tags_vec, opt_rec_var);
|
||||
|
||||
match variant {
|
||||
Never => Layout::VOID,
|
||||
|
@ -2576,7 +2583,7 @@ fn layout_from_tag_union<'a>(
|
|||
NonRecursive {
|
||||
sorted_tag_layouts: tags,
|
||||
} => {
|
||||
let mut tag_layouts = Vec::with_capacity_in(tags.len(), arena);
|
||||
let mut tag_layouts = Vec::with_capacity_in(tags.len(), env.arena);
|
||||
tag_layouts.extend(tags.iter().map(|r| r.1));
|
||||
|
||||
Layout::Union(UnionLayout::NonRecursive(tag_layouts.into_bump_slice()))
|
||||
|
@ -2585,7 +2592,7 @@ fn layout_from_tag_union<'a>(
|
|||
Recursive {
|
||||
sorted_tag_layouts: tags,
|
||||
} => {
|
||||
let mut tag_layouts = Vec::with_capacity_in(tags.len(), arena);
|
||||
let mut tag_layouts = Vec::with_capacity_in(tags.len(), env.arena);
|
||||
tag_layouts.extend(tags.iter().map(|r| r.1));
|
||||
|
||||
debug_assert!(tag_layouts.len() > 1);
|
||||
|
@ -2597,7 +2604,7 @@ fn layout_from_tag_union<'a>(
|
|||
nullable_name: _,
|
||||
sorted_tag_layouts: tags,
|
||||
} => {
|
||||
let mut tag_layouts = Vec::with_capacity_in(tags.len(), arena);
|
||||
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 {
|
||||
|
@ -2612,8 +2619,6 @@ fn layout_from_tag_union<'a>(
|
|||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(debug_assertions)]
|
||||
pub fn ext_var_is_empty_record(subs: &Subs, ext_var: Variable) -> bool {
|
||||
|
@ -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>(
|
||||
env: &mut Env<'a, '_>,
|
||||
key_var: Variable,
|
||||
|
|
|
@ -344,7 +344,6 @@ impl LambdaSet {
|
|||
layouts.symbols.push(*symbol);
|
||||
}
|
||||
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 {
|
||||
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 => {
|
||||
Ok(layouts.usize())
|
||||
}
|
||||
Symbol::NUM_NAT | Symbol::NUM_NATURAL => Ok(layouts.usize()),
|
||||
|
||||
_ => {
|
||||
// at this point we throw away alias information
|
||||
|
|
|
@ -30,7 +30,6 @@
|
|||
">="
|
||||
">"
|
||||
"^"
|
||||
"%%"
|
||||
"%"
|
||||
|
||||
"->"
|
|
@ -189,10 +189,8 @@ pub enum Expr<'a> {
|
|||
|
||||
// Tags
|
||||
GlobalTag(&'a str),
|
||||
PrivateTag(&'a str),
|
||||
|
||||
// Reference to an opaque type, e.g. $Opaq
|
||||
// TODO(opaques): $->@ in the above comment
|
||||
// Reference to an opaque type, e.g. @Opaq
|
||||
OpaqueRef(&'a str),
|
||||
|
||||
// Pattern Matching
|
||||
|
@ -446,11 +444,6 @@ pub enum Tag<'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.
|
||||
SpaceBefore(&'a Tag<'a>, &'a [CommentOrNewline<'a>]),
|
||||
SpaceAfter(&'a Tag<'a>, &'a [CommentOrNewline<'a>]),
|
||||
|
@ -523,7 +516,6 @@ pub enum Pattern<'a> {
|
|||
Identifier(&'a str),
|
||||
|
||||
GlobalTag(&'a str),
|
||||
PrivateTag(&'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> {
|
||||
match ident {
|
||||
Ident::GlobalTag(string) => Pattern::GlobalTag(string),
|
||||
Ident::PrivateTag(string) => Pattern::PrivateTag(string),
|
||||
Ident::OpaqueRef(string) => Pattern::OpaqueRef(string),
|
||||
Ident::Access { module_name, parts } => {
|
||||
if parts.len() == 1 {
|
||||
|
@ -629,7 +620,6 @@ impl<'a> Pattern<'a> {
|
|||
match (self, other) {
|
||||
(Identifier(x), Identifier(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)) => {
|
||||
let equivalent_args = args_x
|
||||
.iter()
|
||||
|
@ -927,7 +917,7 @@ impl<'a> Expr<'a> {
|
|||
}
|
||||
|
||||
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>> {
|
||||
one_of!(
|
||||
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::SingleQuote, single_quote_literal_help())),
|
||||
loc!(specialize(EExpr::Number, positive_number_literal_help())),
|
||||
|
@ -1509,8 +1510,8 @@ fn parse_expr_operator<'a>(
|
|||
}
|
||||
}
|
||||
}
|
||||
Err((NoProgress, _, _)) => {
|
||||
todo!()
|
||||
Err((NoProgress, expr, e)) => {
|
||||
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::GlobalTag(value) => Ok(Pattern::GlobalTag(value)),
|
||||
Expr::PrivateTag(value) => Ok(Pattern::PrivateTag(value)),
|
||||
Expr::OpaqueRef(value) => Ok(Pattern::OpaqueRef(value)),
|
||||
Expr::Apply(loc_val, loc_args, _) => {
|
||||
let region = loc_val.region;
|
||||
|
@ -2437,7 +2437,6 @@ where
|
|||
fn ident_to_expr<'a>(arena: &'a Bump, src: Ident<'a>) -> Expr<'a> {
|
||||
match src {
|
||||
Ident::GlobalTag(string) => Expr::GlobalTag(string),
|
||||
Ident::PrivateTag(string) => Expr::PrivateTag(string),
|
||||
Ident::OpaqueRef(string) => Expr::OpaqueRef(string),
|
||||
Ident::Access { module_name, parts } => {
|
||||
let mut iter = parts.iter();
|
||||
|
@ -2762,7 +2761,6 @@ where
|
|||
"&&" => good!(BinOp::And, 2),
|
||||
"||" => good!(BinOp::Or, 2),
|
||||
"//" => good!(BinOp::DoubleSlash, 2),
|
||||
"%%" => good!(BinOp::DoublePercent, 2),
|
||||
"->" => {
|
||||
// makes no progress, so it does not interfere with `_ if isGood -> ...`
|
||||
Err((NoProgress, to_error("->", state.pos()), state))
|
||||
|
|
|
@ -37,9 +37,6 @@ pub enum Ident<'a> {
|
|||
/// Foo or Bar
|
||||
GlobalTag(&'a str),
|
||||
/// @Foo or @Bar
|
||||
PrivateTag(&'a str),
|
||||
/// $Foo or $Bar
|
||||
// TODO(opaques): $->@ in the above comment
|
||||
OpaqueRef(&'a str),
|
||||
/// foo or foo.bar or Foo.Bar.baz.qux
|
||||
Access {
|
||||
|
@ -57,7 +54,7 @@ impl<'a> Ident<'a> {
|
|||
use self::Ident::*;
|
||||
|
||||
match self {
|
||||
GlobalTag(string) | PrivateTag(string) | OpaqueRef(string) => string.len(),
|
||||
GlobalTag(string) | OpaqueRef(string) => string.len(),
|
||||
Access { module_name, parts } => {
|
||||
let mut len = if module_name.is_empty() {
|
||||
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, ()> {
|
||||
move |arena, state: State<'a>| {
|
||||
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)
|
||||
}
|
||||
}
|
||||
move |arena, state: State<'a>| uppercase_ident().parse(arena, state)
|
||||
}
|
||||
|
||||
/// This could be:
|
||||
|
@ -242,7 +222,6 @@ pub enum BadIdent {
|
|||
WeirdDotAccess(Position),
|
||||
WeirdDotQualified(Position),
|
||||
StrayDot(Position),
|
||||
BadPrivateTag(Position),
|
||||
BadOpaqueRef(Position),
|
||||
}
|
||||
|
||||
|
@ -311,21 +290,13 @@ fn chomp_accessor(buffer: &[u8], pos: Position) -> Result<&str, BadIdent> {
|
|||
}
|
||||
}
|
||||
|
||||
/// a `@Token` private tag
|
||||
fn chomp_private_tag_or_opaque(
|
||||
private_tag: bool, // If false, opaque
|
||||
buffer: &[u8],
|
||||
pos: Position,
|
||||
) -> Result<&str, BadIdent> {
|
||||
/// a `@Token` opaque
|
||||
fn chomp_opaque_ref(buffer: &[u8], pos: Position) -> Result<&str, BadIdent> {
|
||||
// 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;
|
||||
|
||||
let bad_ident = if private_tag {
|
||||
BadIdent::BadPrivateTag
|
||||
} else {
|
||||
BadIdent::BadOpaqueRef
|
||||
};
|
||||
let bad_ident = BadIdent::BadOpaqueRef;
|
||||
|
||||
match chomp_uppercase_part(&buffer[1..]) {
|
||||
Ok(name) => {
|
||||
|
@ -362,15 +333,11 @@ fn chomp_identifier_chain<'a>(
|
|||
}
|
||||
Err(fail) => return Err((1, fail)),
|
||||
},
|
||||
c @ ('@' | '$') => match chomp_private_tag_or_opaque(c == '@', buffer, pos) {
|
||||
'@' => match chomp_opaque_ref(buffer, pos) {
|
||||
Ok(tagname) => {
|
||||
let bytes_parsed = tagname.len();
|
||||
|
||||
let ident = if c == '@' {
|
||||
Ident::PrivateTag
|
||||
} else {
|
||||
Ident::OpaqueRef
|
||||
};
|
||||
let ident = Ident::OpaqueRef;
|
||||
|
||||
return Ok((bytes_parsed as u32, ident(tagname)));
|
||||
}
|
||||
|
|
|
@ -240,14 +240,10 @@ fn loc_ident_pattern_help<'a>(
|
|||
Ok((MadeProgress, loc_tag, state))
|
||||
}
|
||||
}
|
||||
Ident::PrivateTag(name) | Ident::OpaqueRef(name) => {
|
||||
Ident::OpaqueRef(name) => {
|
||||
let loc_pat = Loc {
|
||||
region: loc_ident.region,
|
||||
value: if matches!(loc_ident.value, Ident::PrivateTag(..)) {
|
||||
Pattern::PrivateTag(name)
|
||||
} else {
|
||||
Pattern::OpaqueRef(name)
|
||||
},
|
||||
value: Pattern::OpaqueRef(name),
|
||||
};
|
||||
|
||||
// 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))
|
||||
.parse(arena, state)?;
|
||||
|
||||
let result = if name.value.starts_with('@') {
|
||||
Tag::Private {
|
||||
let result = Tag::Global {
|
||||
name,
|
||||
args: args.into_bump_slice(),
|
||||
}
|
||||
} else {
|
||||
Tag::Global {
|
||||
name,
|
||||
args: args.into_bump_slice(),
|
||||
}
|
||||
};
|
||||
|
||||
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(
|
||||
"$Age",
|
||||
"@Age",
|
||||
)
|
||||
|
|
|
@ -1 +1 @@
|
|||
$Age
|
||||
@Age
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
Apply(
|
||||
@0-4 OpaqueRef(
|
||||
"$Age",
|
||||
"@Age",
|
||||
),
|
||||
[
|
||||
@5-6 Var {
|
||||
|
|
|
@ -1 +1 @@
|
|||
$Age m n
|
||||
@Age m n
|
||||
|
|
|
@ -8,7 +8,7 @@ When(
|
|||
patterns: [
|
||||
@12-16 SpaceBefore(
|
||||
OpaqueRef(
|
||||
"$Age",
|
||||
"@Age",
|
||||
),
|
||||
[
|
||||
Newline,
|
||||
|
|
|
@ -1,2 +1,2 @@
|
|||
when n is
|
||||
$Age -> 1
|
||||
@Age -> 1
|
||||
|
|
|
@ -9,7 +9,7 @@ When(
|
|||
@12-20 SpaceBefore(
|
||||
Apply(
|
||||
@12-16 OpaqueRef(
|
||||
"$Add",
|
||||
"@Add",
|
||||
),
|
||||
[
|
||||
@17-18 Identifier(
|
||||
|
|
|
@ -1,2 +1,2 @@
|
|||
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! {
|
||||
fail/type_argument_no_arrow.expr,
|
||||
fail/type_double_comma.expr,
|
||||
pass/plus_if.expr,
|
||||
pass/list_closing_indent_not_enough.expr,
|
||||
pass/ability_single_line.expr,
|
||||
pass/ability_multi_line.expr,
|
||||
|
@ -133,7 +134,6 @@ mod test_parse {
|
|||
pass/annotated_tag_destructure.expr,
|
||||
pass/apply_global_tag.expr,
|
||||
pass/apply_parenthetical_global_tag_args.expr,
|
||||
pass/apply_private_tag.expr,
|
||||
pass/apply_three_args.expr,
|
||||
pass/apply_two_args.expr,
|
||||
pass/apply_unary_negation.expr,
|
||||
|
@ -142,7 +142,6 @@ mod test_parse {
|
|||
pass/basic_docs.expr,
|
||||
pass/basic_field.expr,
|
||||
pass/basic_global_tag.expr,
|
||||
pass/basic_private_tag.expr,
|
||||
pass/basic_var.expr,
|
||||
pass/closure_with_underscores.expr,
|
||||
pass/comment_after_def.module,
|
||||
|
@ -232,7 +231,6 @@ mod test_parse {
|
|||
pass/pos_inf_float.expr,
|
||||
pass/positive_float.expr,
|
||||
pass/positive_int.expr,
|
||||
pass/private_qualified_tag.expr,
|
||||
pass/provides_type.header,
|
||||
pass/qualified_field.expr,
|
||||
pass/qualified_global_tag.expr,
|
||||
|
@ -316,7 +314,17 @@ mod test_parse {
|
|||
if std::env::var("ROC_PARSER_SNAPSHOT_TEST_OVERWRITE").is_ok() {
|
||||
std::fs::write(&result_path, actual_result).unwrap();
|
||||
} 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);
|
||||
}
|
||||
|
@ -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]
|
||||
// fn ann_global_open_union() {
|
||||
// let arena = Bump::new();
|
||||
|
|
|
@ -377,6 +377,17 @@ impl LineInfo {
|
|||
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]
|
||||
|
|
|
@ -6,4 +6,4 @@ license = "UPL-1.0"
|
|||
edition = "2018"
|
||||
|
||||
[dependencies]
|
||||
target-lexicon = "0.12.2"
|
||||
target-lexicon = "0.12.3"
|
||||
|
|
|
@ -28,3 +28,5 @@ pretty_assertions = "1.0.0"
|
|||
indoc = "1.0.3"
|
||||
tempfile = "3.2.0"
|
||||
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::expected::{Expected, PExpected};
|
||||
use roc_collections::all::MutMap;
|
||||
use roc_error_macros::internal_error;
|
||||
use roc_module::ident::TagName;
|
||||
use roc_module::symbol::Symbol;
|
||||
use roc_region::all::{Loc, Region};
|
||||
|
@ -195,20 +196,20 @@ impl Aliases {
|
|||
register(subs, rank, pools, content)
|
||||
}
|
||||
|
||||
/// Instantiate an alias of the form `Foo a : [ @Foo a ]`
|
||||
fn instantiate_num_at_alias(
|
||||
/// Build an alias of the form `Num range := range`
|
||||
fn build_num_opaque(
|
||||
subs: &mut Subs,
|
||||
rank: Rank,
|
||||
pools: &mut Pools,
|
||||
tag_name_slice: SubsSlice<TagName>,
|
||||
range_slice: SubsSlice<Variable>,
|
||||
symbol: Symbol,
|
||||
range_var: Variable,
|
||||
) -> Variable {
|
||||
let variable_slices = SubsSlice::extend_new(&mut subs.variable_slices, [range_slice]);
|
||||
|
||||
let union_tags = UnionTags::from_slices(tag_name_slice, variable_slices);
|
||||
let ext_var = Variable::EMPTY_TAG_UNION;
|
||||
let flat_type = FlatType::TagUnion(union_tags, ext_var);
|
||||
let content = Content::Structure(flat_type);
|
||||
let content = Content::Alias(
|
||||
symbol,
|
||||
AliasVariables::insert_into_subs(subs, [range_var], []),
|
||||
range_var,
|
||||
AliasKind::Opaque,
|
||||
);
|
||||
|
||||
register(subs, rank, pools, content)
|
||||
}
|
||||
|
@ -227,126 +228,46 @@ impl Aliases {
|
|||
|
||||
Some(var)
|
||||
}
|
||||
Symbol::NUM_NUM => {
|
||||
let var = Self::instantiate_num_at_alias(
|
||||
subs,
|
||||
rank,
|
||||
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_NUM | Symbol::NUM_FLOATINGPOINT | Symbol::NUM_INTEGER => {
|
||||
// These are opaque types Num range := range (respectively for FloatingPoint and
|
||||
// Integer). They should not have been built as DelayedAliases!
|
||||
internal_error!("Attempting to build delayed instantiation of opaque num");
|
||||
}
|
||||
Symbol::NUM_INT => {
|
||||
// [ @Integer range ]
|
||||
let integer_content_var = Self::instantiate_builtin_aliases(
|
||||
self,
|
||||
// Int range : Num (Integer range)
|
||||
//
|
||||
// build `Integer range := range`
|
||||
let integer_content_var = Self::build_num_opaque(
|
||||
subs,
|
||||
rank,
|
||||
pools,
|
||||
Symbol::NUM_INTEGER,
|
||||
alias_variables,
|
||||
)
|
||||
.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,
|
||||
subs.variables[alias_variables.variables_start as usize],
|
||||
);
|
||||
|
||||
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 => {
|
||||
// [ @FloatingPoint range ]
|
||||
let fpoint_content_var = Self::instantiate_builtin_aliases(
|
||||
self,
|
||||
// Float range : Num (FloatingPoint range)
|
||||
//
|
||||
// build `FloatingPoint range := range`
|
||||
let fpoint_content_var = Self::build_num_opaque(
|
||||
subs,
|
||||
rank,
|
||||
pools,
|
||||
Symbol::NUM_FLOATINGPOINT,
|
||||
alias_variables,
|
||||
)
|
||||
.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,
|
||||
subs.variables[alias_variables.variables_start as usize],
|
||||
);
|
||||
|
||||
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,
|
||||
}
|
||||
|
@ -1183,27 +1104,35 @@ fn solve(
|
|||
let actual =
|
||||
either_type_index_to_var(constraints, subs, rank, pools, aliases, *type_index);
|
||||
|
||||
let mut new_desc = subs.get(actual);
|
||||
match new_desc.content {
|
||||
Content::Structure(FlatType::TagUnion(tags, _)) => {
|
||||
let mut stack = vec![actual];
|
||||
while let Some(var) = stack.pop() {
|
||||
use {Content::*, FlatType::*};
|
||||
|
||||
let mut desc = subs.get(var);
|
||||
if let Structure(TagUnion(tags, ext)) = desc.content {
|
||||
if let Structure(EmptyTagUnion) = subs.get_content_without_compacting(ext) {
|
||||
let new_ext = subs.fresh_unnamed_flex_var();
|
||||
subs.set_rank(new_ext, new_desc.rank);
|
||||
let new_union = Content::Structure(FlatType::TagUnion(tags, new_ext));
|
||||
new_desc.content = new_union;
|
||||
subs.set(actual, new_desc);
|
||||
state
|
||||
subs.set_rank(new_ext, desc.rank);
|
||||
let new_union = Structure(TagUnion(tags, new_ext));
|
||||
desc.content = new_union;
|
||||
subs.set(var, desc);
|
||||
}
|
||||
_ => {
|
||||
|
||||
// Also open up all nested tag unions.
|
||||
let all_vars = tags.variables().into_iter();
|
||||
stack.extend(all_vars.flat_map(|slice| subs[slice]).map(|var| subs[var]));
|
||||
}
|
||||
|
||||
// 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) => {
|
||||
let includes_tag = &constraints.includes_tags[index.index()];
|
||||
|
||||
|
|
|
@ -10,14 +10,54 @@ mod helpers;
|
|||
#[cfg(test)]
|
||||
mod solve_expr {
|
||||
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_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 std::path::PathBuf;
|
||||
|
||||
// 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 std::path::PathBuf;
|
||||
use tempfile::tempdir;
|
||||
|
||||
let arena = &Bump::new();
|
||||
|
@ -54,20 +94,55 @@ mod solve_expr {
|
|||
};
|
||||
|
||||
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,
|
||||
) -> Result<
|
||||
(
|
||||
Vec<roc_solve::solve::TypeError>,
|
||||
Vec<roc_problem::can::Problem>,
|
||||
String,
|
||||
),
|
||||
std::io::Error,
|
||||
> {
|
||||
let LoadedModule {
|
||||
home: ModuleId,
|
||||
interns: &Interns,
|
||||
can_problems: Vec<Problem>,
|
||||
type_problems: Vec<TypeError>,
|
||||
) -> (String, String) {
|
||||
let filename = PathBuf::from("test.roc");
|
||||
let src_lines: Vec<&str> = src.split('\n').collect();
|
||||
let lines = LineInfo::new(src);
|
||||
let alloc = RocDocAllocator::new(&src_lines, home, &interns);
|
||||
|
||||
let mut can_reports = vec![];
|
||||
let mut type_reports = vec![];
|
||||
|
||||
for problem in can_problems {
|
||||
let report = can_problem(&alloc, &lines, filename.clone(), problem.clone());
|
||||
can_reports.push(report.pretty(&alloc));
|
||||
}
|
||||
|
||||
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,
|
||||
|
@ -75,30 +150,22 @@ mod solve_expr {
|
|||
mut solved,
|
||||
exposed_to_host,
|
||||
..
|
||||
} = run_load_and_infer(src)?;
|
||||
},
|
||||
src,
|
||||
) = run_load_and_infer(src)?;
|
||||
|
||||
let mut can_problems = can_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();
|
||||
|
||||
// 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
|
||||
for var in exposed_to_host.values() {
|
||||
name_all_type_vars(*var, subs);
|
||||
|
@ -112,10 +179,6 @@ mod solve_expr {
|
|||
|
||||
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))
|
||||
}
|
||||
|
||||
|
@ -143,7 +206,11 @@ mod solve_expr {
|
|||
fn infer_eq(src: &str, expected: &str) {
|
||||
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());
|
||||
}
|
||||
|
@ -151,16 +218,73 @@ mod solve_expr {
|
|||
fn infer_eq_without_problem(src: &str, expected: &str) {
|
||||
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() {
|
||||
// fail with an assert, but print the problems normally so rust doesn't try to diff
|
||||
// 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());
|
||||
}
|
||||
|
||||
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)
|
||||
where
|
||||
I: IntoIterator<Item = (&'a str, &'a str)>,
|
||||
|
@ -172,7 +296,7 @@ mod solve_expr {
|
|||
interns,
|
||||
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 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]
|
||||
fn two_tag_pattern() {
|
||||
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]
|
||||
fn record_extraction() {
|
||||
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]
|
||||
fn qualified_annotation_num_integer() {
|
||||
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]
|
||||
fn recursive_function_with_rigid() {
|
||||
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
|
||||
#[test]
|
||||
fn copy_vars_referencing_copied_vars() {
|
||||
|
@ -5400,7 +5480,7 @@ mod solve_expr {
|
|||
r#"
|
||||
Age := U32
|
||||
|
||||
$Age 21
|
||||
@Age 21
|
||||
"#
|
||||
),
|
||||
r#"Age"#,
|
||||
|
@ -5415,7 +5495,7 @@ mod solve_expr {
|
|||
Age := U32
|
||||
|
||||
a : Age
|
||||
a = $Age 21
|
||||
a = @Age 21
|
||||
|
||||
a
|
||||
"#
|
||||
|
@ -5431,7 +5511,7 @@ mod solve_expr {
|
|||
r#"
|
||||
Id n := [ Id U32 n ]
|
||||
|
||||
$Id (Id 21 "sasha")
|
||||
@Id (Id 21 "sasha")
|
||||
"#
|
||||
),
|
||||
r#"Id Str"#,
|
||||
|
@ -5446,7 +5526,7 @@ mod solve_expr {
|
|||
Id n := [ Id U32 n ]
|
||||
|
||||
a : Id Str
|
||||
a = $Id (Id 21 "sasha")
|
||||
a = @Id (Id 21 "sasha")
|
||||
|
||||
a
|
||||
"#
|
||||
|
@ -5464,8 +5544,8 @@ mod solve_expr {
|
|||
condition : Bool
|
||||
|
||||
if condition
|
||||
then $Id (Id 21 (Y "sasha"))
|
||||
else $Id (Id 21 (Z "felix"))
|
||||
then @Id (Id 21 (Y "sasha"))
|
||||
else @Id (Id 21 (Z "felix"))
|
||||
"#
|
||||
),
|
||||
r#"Id [ Y Str, Z Str ]*"#,
|
||||
|
@ -5483,8 +5563,8 @@ mod solve_expr {
|
|||
v : Id [ Y Str, Z Str ]
|
||||
v =
|
||||
if condition
|
||||
then $Id (Id 21 (Y "sasha"))
|
||||
else $Id (Id 21 (Z "felix"))
|
||||
then @Id (Id 21 (Y "sasha"))
|
||||
else @Id (Id 21 (Z "felix"))
|
||||
|
||||
v
|
||||
"#
|
||||
|
@ -5500,7 +5580,7 @@ mod solve_expr {
|
|||
r#"
|
||||
Age := U32
|
||||
|
||||
\$Age n -> n
|
||||
\@Age n -> n
|
||||
"#
|
||||
),
|
||||
r#"Age -> U32"#,
|
||||
|
@ -5515,7 +5595,7 @@ mod solve_expr {
|
|||
Age := U32
|
||||
|
||||
v : Age -> U32
|
||||
v = \$Age n -> n
|
||||
v = \@Age n -> n
|
||||
v
|
||||
"#
|
||||
),
|
||||
|
@ -5530,7 +5610,7 @@ mod solve_expr {
|
|||
r#"
|
||||
Id n := [ Id U32 n ]
|
||||
|
||||
\$Id (Id _ n) -> n
|
||||
\@Id (Id _ n) -> n
|
||||
"#
|
||||
),
|
||||
r#"Id a -> a"#,
|
||||
|
@ -5545,7 +5625,7 @@ mod solve_expr {
|
|||
Id n := [ Id U32 n ]
|
||||
|
||||
v : Id a -> a
|
||||
v = \$Id (Id _ n) -> n
|
||||
v = \@Id (Id _ n) -> n
|
||||
|
||||
v
|
||||
"#
|
||||
|
@ -5563,7 +5643,7 @@ mod solve_expr {
|
|||
|
||||
strToBool : Str -> Bool
|
||||
|
||||
\$Id (Id _ n) -> strToBool n
|
||||
\@Id (Id _ n) -> strToBool n
|
||||
"#
|
||||
),
|
||||
r#"Id Str -> Bool"#,
|
||||
|
@ -5580,7 +5660,7 @@ mod solve_expr {
|
|||
strToBool : Str -> Bool
|
||||
|
||||
v : Id Str -> Bool
|
||||
v = \$Id (Id _ n) -> strToBool n
|
||||
v = \@Id (Id _ n) -> strToBool n
|
||||
|
||||
v
|
||||
"#
|
||||
|
@ -5598,9 +5678,9 @@ mod solve_expr {
|
|||
|
||||
\id ->
|
||||
when id is
|
||||
$Id (Id _ A) -> ""
|
||||
$Id (Id _ B) -> ""
|
||||
$Id (Id _ (C { a: "" })) -> ""
|
||||
@Id (Id _ A) -> ""
|
||||
@Id (Id _ B) -> ""
|
||||
@Id (Id _ (C { a: "" })) -> ""
|
||||
"#
|
||||
),
|
||||
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 ->
|
||||
when id is
|
||||
$Id (Id _ A) -> ""
|
||||
$Id (Id _ B) -> ""
|
||||
$Id (Id _ (C { a: "" })) -> ""
|
||||
@Id (Id _ A) -> ""
|
||||
@Id (Id _ B) -> ""
|
||||
@Id (Id _ (C { a: "" })) -> ""
|
||||
|
||||
f
|
||||
"#
|
||||
|
@ -5635,7 +5715,7 @@ mod solve_expr {
|
|||
r#"
|
||||
app "test" provides [ effectAlways ] to "./platform"
|
||||
|
||||
Effect a : [ @Effect ({} -> a) ]
|
||||
Effect a := {} -> a
|
||||
|
||||
effectAlways : a -> Effect a
|
||||
effectAlways = \x ->
|
||||
|
@ -5703,8 +5783,8 @@ mod solve_expr {
|
|||
insert : Outer k, k -> Outer k
|
||||
insert = \m, var ->
|
||||
when m is
|
||||
$Outer Empty -> $Outer (Wrapped var)
|
||||
$Outer (Wrapped _) -> $Outer (Wrapped var)
|
||||
@Outer Empty -> @Outer (Wrapped var)
|
||||
@Outer (Wrapped _) -> @Outer (Wrapped var)
|
||||
|
||||
insert
|
||||
"#
|
||||
|
@ -5720,9 +5800,9 @@ mod solve_expr {
|
|||
r#"
|
||||
Outer k := [ Empty, Wrapped k ]
|
||||
|
||||
when ($Outer Empty) is
|
||||
$Outer Empty -> $Outer (Wrapped "")
|
||||
$Outer (Wrapped k) -> $Outer (Wrapped k)
|
||||
when (@Outer Empty) is
|
||||
@Outer Empty -> @Outer (Wrapped "")
|
||||
@Outer (Wrapped k) -> @Outer (Wrapped k)
|
||||
"#
|
||||
),
|
||||
r#"Outer Str"#,
|
||||
|
@ -5736,9 +5816,9 @@ mod solve_expr {
|
|||
r#"
|
||||
Outer := [ A, B ]
|
||||
|
||||
when ($Outer A) is
|
||||
$Outer A -> $Outer A
|
||||
$Outer B -> $Outer B
|
||||
when (@Outer A) is
|
||||
@Outer A -> @Outer A
|
||||
@Outer B -> @Outer B
|
||||
"#
|
||||
),
|
||||
r#"Outer"#,
|
||||
|
@ -5795,7 +5875,7 @@ mod solve_expr {
|
|||
|
||||
Id := U64
|
||||
|
||||
hash = \$Id n -> n
|
||||
hash = \@Id n -> n
|
||||
"#
|
||||
),
|
||||
[("Hash:hash", "Id")],
|
||||
|
@ -5815,8 +5895,8 @@ mod solve_expr {
|
|||
|
||||
Id := U64
|
||||
|
||||
hash = \$Id n -> n
|
||||
hash32 = \$Id n -> Num.toU32 n
|
||||
hash = \@Id n -> n
|
||||
hash32 = \@Id n -> Num.toU32 n
|
||||
"#
|
||||
),
|
||||
[("Hash:hash", "Id"), ("Hash:hash32", "Id")],
|
||||
|
@ -5840,11 +5920,11 @@ mod solve_expr {
|
|||
|
||||
Id := U64
|
||||
|
||||
hash = \$Id n -> n
|
||||
hash32 = \$Id n -> Num.toU32 n
|
||||
hash = \@Id n -> n
|
||||
hash32 = \@Id n -> Num.toU32 n
|
||||
|
||||
eq = \$Id m, $Id n -> m == n
|
||||
le = \$Id m, $Id n -> m < n
|
||||
eq = \@Id m, @Id n -> m == n
|
||||
le = \@Id m, @Id n -> m < n
|
||||
"#
|
||||
),
|
||||
[
|
||||
|
@ -5869,7 +5949,7 @@ mod solve_expr {
|
|||
Id := U64
|
||||
|
||||
hash : Id -> U64
|
||||
hash = \$Id n -> n
|
||||
hash = \@Id n -> n
|
||||
"#
|
||||
),
|
||||
[("Hash:hash", "Id")],
|
||||
|
@ -5907,9 +5987,9 @@ mod solve_expr {
|
|||
|
||||
Id := U64
|
||||
|
||||
hash = \$Id n -> n
|
||||
hash = \@Id n -> n
|
||||
|
||||
zero = hash ($Id 0)
|
||||
zero = hash (@Id 0)
|
||||
"#
|
||||
),
|
||||
"U64",
|
||||
|
@ -6000,9 +6080,9 @@ mod solve_expr {
|
|||
hashEq = \x, y -> hash x == hash y
|
||||
|
||||
Id := U64
|
||||
hash = \$Id n -> n
|
||||
hash = \@Id n -> n
|
||||
|
||||
result = hashEq ($Id 100) ($Id 101)
|
||||
result = hashEq (@Id 100) (@Id 101)
|
||||
"#
|
||||
),
|
||||
"Bool",
|
||||
|
@ -6022,15 +6102,88 @@ mod solve_expr {
|
|||
mulHashes = \x, y -> hash x * hash y
|
||||
|
||||
Id := U64
|
||||
hash = \$Id n -> n
|
||||
hash = \@Id n -> n
|
||||
|
||||
Three := {}
|
||||
hash = \$Three _ -> 3
|
||||
hash = \@Three _ -> 3
|
||||
|
||||
result = mulHashes ($Id 100) ($Three {})
|
||||
result = mulHashes (@Id 100) (@Three {})
|
||||
"#
|
||||
),
|
||||
"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"
|
||||
libc = "0.2.106"
|
||||
inkwell = { path = "../../vendor/inkwell" }
|
||||
target-lexicon = "0.12.2"
|
||||
target-lexicon = "0.12.3"
|
||||
libloading = "0.7.1"
|
||||
wasmer-wasi = "2.0.0"
|
||||
tempfile = "3.2.0"
|
||||
|
|
|
@ -23,9 +23,9 @@ fn hash_specialization() {
|
|||
|
||||
Id := U64
|
||||
|
||||
hash = \$Id n -> n
|
||||
hash = \@Id n -> n
|
||||
|
||||
main = hash ($Id 1234)
|
||||
main = hash (@Id 1234)
|
||||
"#
|
||||
),
|
||||
1234,
|
||||
|
@ -46,13 +46,13 @@ fn hash_specialization_multiple_add() {
|
|||
|
||||
Id := U64
|
||||
|
||||
hash = \$Id n -> n
|
||||
hash = \@Id n -> n
|
||||
|
||||
One := {}
|
||||
|
||||
hash = \$One _ -> 1
|
||||
hash = \@One _ -> 1
|
||||
|
||||
main = hash ($Id 1234) + hash ($One {})
|
||||
main = hash (@Id 1234) + hash (@One {})
|
||||
"#
|
||||
),
|
||||
1235,
|
||||
|
@ -73,11 +73,11 @@ fn alias_member_specialization() {
|
|||
|
||||
Id := U64
|
||||
|
||||
hash = \$Id n -> n
|
||||
hash = \@Id n -> n
|
||||
|
||||
main =
|
||||
aliasedHash = hash
|
||||
aliasedHash ($Id 1234)
|
||||
aliasedHash (@Id 1234)
|
||||
"#
|
||||
),
|
||||
1234,
|
||||
|
@ -100,9 +100,9 @@ fn ability_constrained_in_non_member_usage() {
|
|||
mulHashes = \x, y -> hash x * hash y
|
||||
|
||||
Id := U64
|
||||
hash = \$Id n -> n
|
||||
hash = \@Id n -> n
|
||||
|
||||
result = mulHashes ($Id 5) ($Id 7)
|
||||
result = mulHashes (@Id 5) (@Id 7)
|
||||
"#
|
||||
),
|
||||
35,
|
||||
|
@ -124,9 +124,9 @@ fn ability_constrained_in_non_member_usage_inferred() {
|
|||
mulHashes = \x, y -> hash x * hash y
|
||||
|
||||
Id := U64
|
||||
hash = \$Id n -> n
|
||||
hash = \@Id n -> n
|
||||
|
||||
result = mulHashes ($Id 5) ($Id 7)
|
||||
result = mulHashes (@Id 5) (@Id 7)
|
||||
"#
|
||||
),
|
||||
35,
|
||||
|
@ -149,12 +149,12 @@ fn ability_constrained_in_non_member_multiple_specializations() {
|
|||
mulHashes = \x, y -> hash x * hash y
|
||||
|
||||
Id := U64
|
||||
hash = \$Id n -> n
|
||||
hash = \@Id n -> n
|
||||
|
||||
Three := {}
|
||||
hash = \$Three _ -> 3
|
||||
hash = \@Three _ -> 3
|
||||
|
||||
result = mulHashes ($Id 100) ($Three {})
|
||||
result = mulHashes (@Id 100) (@Three {})
|
||||
"#
|
||||
),
|
||||
300,
|
||||
|
@ -176,12 +176,12 @@ fn ability_constrained_in_non_member_multiple_specializations_inferred() {
|
|||
mulHashes = \x, y -> hash x * hash y
|
||||
|
||||
Id := U64
|
||||
hash = \$Id n -> n
|
||||
hash = \@Id n -> n
|
||||
|
||||
Three := {}
|
||||
hash = \$Three _ -> 3
|
||||
hash = \@Three _ -> 3
|
||||
|
||||
result = mulHashes ($Id 100) ($Three {})
|
||||
result = mulHashes (@Id 100) (@Three {})
|
||||
"#
|
||||
),
|
||||
300,
|
||||
|
@ -204,12 +204,12 @@ fn ability_used_as_type_still_compiles() {
|
|||
mulHashes = \x, y -> hash x * hash y
|
||||
|
||||
Id := U64
|
||||
hash = \$Id n -> n
|
||||
hash = \@Id n -> n
|
||||
|
||||
Three := {}
|
||||
hash = \$Three _ -> 3
|
||||
hash = \@Three _ -> 3
|
||||
|
||||
result = mulHashes ($Id 100) ($Three {})
|
||||
result = mulHashes (@Id 100) (@Three {})
|
||||
"#
|
||||
),
|
||||
300,
|
||||
|
|
|
@ -2534,7 +2534,7 @@ fn list_keep_oks() {
|
|||
RocList<i64>
|
||||
);
|
||||
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<i64>
|
||||
);
|
||||
|
@ -2561,7 +2561,7 @@ fn list_keep_errs() {
|
|||
assert_evals_to!(
|
||||
indoc!(
|
||||
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]),
|
||||
|
|
|
@ -473,7 +473,7 @@ fn f64_sqrt() {
|
|||
assert_evals_to!(
|
||||
indoc!(
|
||||
r#"
|
||||
when Num.sqrt 100 is
|
||||
when Num.sqrtChecked 100 is
|
||||
Ok val -> val
|
||||
Err _ -> -1
|
||||
"#
|
||||
|
@ -489,9 +489,7 @@ fn f64_log() {
|
|||
assert_evals_to!(
|
||||
indoc!(
|
||||
r#"
|
||||
when Num.log 7.38905609893 is
|
||||
Ok val -> val
|
||||
Err _ -> -1
|
||||
Num.log 7.38905609893
|
||||
"#
|
||||
),
|
||||
1.999999999999912,
|
||||
|
@ -501,11 +499,11 @@ fn f64_log() {
|
|||
|
||||
#[test]
|
||||
#[cfg(any(feature = "gen-llvm"))]
|
||||
fn f64_log_one() {
|
||||
fn f64_log_checked_one() {
|
||||
assert_evals_to!(
|
||||
indoc!(
|
||||
r#"
|
||||
when Num.log 1 is
|
||||
when Num.logChecked 1 is
|
||||
Ok val -> val
|
||||
Err _ -> -1
|
||||
"#
|
||||
|
@ -521,7 +519,7 @@ fn f64_sqrt_zero() {
|
|||
assert_evals_to!(
|
||||
indoc!(
|
||||
r#"
|
||||
when Num.sqrt 0 is
|
||||
when Num.sqrtChecked 0 is
|
||||
Ok val -> val
|
||||
Err _ -> -1
|
||||
"#
|
||||
|
@ -533,11 +531,11 @@ fn f64_sqrt_zero() {
|
|||
|
||||
#[test]
|
||||
#[cfg(any(feature = "gen-llvm"))]
|
||||
fn f64_sqrt_negative() {
|
||||
fn f64_sqrt_checked_negative() {
|
||||
assert_evals_to!(
|
||||
indoc!(
|
||||
r#"
|
||||
when Num.sqrt -1 is
|
||||
when Num.sqrtChecked -1 is
|
||||
Err _ -> 42
|
||||
Ok val -> val
|
||||
"#
|
||||
|
@ -549,11 +547,11 @@ fn f64_sqrt_negative() {
|
|||
|
||||
#[test]
|
||||
#[cfg(any(feature = "gen-llvm"))]
|
||||
fn f64_log_zero() {
|
||||
fn f64_log_checked_zero() {
|
||||
assert_evals_to!(
|
||||
indoc!(
|
||||
r#"
|
||||
when Num.log 0 is
|
||||
when Num.logChecked 0 is
|
||||
Err _ -> 42
|
||||
Ok val -> val
|
||||
"#
|
||||
|
@ -569,13 +567,12 @@ fn f64_log_negative() {
|
|||
assert_evals_to!(
|
||||
indoc!(
|
||||
r#"
|
||||
when Num.log -1 is
|
||||
Err _ -> 42
|
||||
Ok val -> val
|
||||
Num.log -1
|
||||
"#
|
||||
),
|
||||
42.0,
|
||||
f64
|
||||
true,
|
||||
f64,
|
||||
|f: f64| f.is_nan()
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -1082,9 +1079,7 @@ fn gen_rem_i64() {
|
|||
assert_evals_to!(
|
||||
indoc!(
|
||||
r#"
|
||||
when Num.rem 8 3 is
|
||||
Ok val -> val
|
||||
Err _ -> -1
|
||||
Num.rem 8 3
|
||||
"#
|
||||
),
|
||||
2,
|
||||
|
@ -1094,11 +1089,11 @@ fn gen_rem_i64() {
|
|||
|
||||
#[test]
|
||||
#[cfg(any(feature = "gen-llvm"))]
|
||||
fn gen_rem_div_by_zero_i64() {
|
||||
fn gen_rem_checked_div_by_zero_i64() {
|
||||
assert_evals_to!(
|
||||
indoc!(
|
||||
r#"
|
||||
when Num.rem 8 0 is
|
||||
when Num.remChecked 8 0 is
|
||||
Err DivByZero -> 4
|
||||
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]
|
||||
#[cfg(any(feature = "gen-llvm"))]
|
||||
fn linked_list_len_0() {
|
||||
|
@ -1113,7 +1134,7 @@ fn io_poc_effect() {
|
|||
r#"
|
||||
app "test" provides [ main ] to "./platform"
|
||||
|
||||
Effect a : [ @Effect ({} -> a) ]
|
||||
Effect a := {} -> a
|
||||
|
||||
succeed : a -> Effect a
|
||||
succeed = \x -> @Effect \{} -> x
|
||||
|
@ -1172,7 +1193,7 @@ fn return_wrapped_function_pointer() {
|
|||
r#"
|
||||
app "test" provides [ main ] to "./platform"
|
||||
|
||||
Effect a : [ @Effect ({} -> a) ]
|
||||
Effect a := {} -> a
|
||||
|
||||
foo : Effect {}
|
||||
foo = @Effect \{} -> {}
|
||||
|
@ -1217,7 +1238,7 @@ fn return_wrapped_closure() {
|
|||
r#"
|
||||
app "test" provides [ main ] to "./platform"
|
||||
|
||||
Effect a : [ @Effect ({} -> a) ]
|
||||
Effect a := {} -> a
|
||||
|
||||
foo : Effect {}
|
||||
foo =
|
||||
|
@ -1843,7 +1864,7 @@ fn task_always_twice() {
|
|||
r#"
|
||||
app "test" provides [ main ] to "./platform"
|
||||
|
||||
Effect a : [ @Effect ({} -> a) ]
|
||||
Effect a := {} -> a
|
||||
|
||||
effectAlways : a -> Effect a
|
||||
effectAlways = \x ->
|
||||
|
@ -1888,7 +1909,7 @@ fn wildcard_rigid() {
|
|||
r#"
|
||||
app "test" provides [ main ] to "./platform"
|
||||
|
||||
Effect a : [ @Effect ({} -> a) ]
|
||||
Effect a := {} -> a
|
||||
|
||||
Task a err : Effect (Result a err)
|
||||
|
||||
|
@ -1918,7 +1939,7 @@ fn alias_of_alias_with_type_arguments() {
|
|||
r#"
|
||||
app "test" provides [ main ] to "./platform"
|
||||
|
||||
Effect a : [ @Effect a ]
|
||||
Effect a := a
|
||||
|
||||
Task a err : Effect (Result a err)
|
||||
|
||||
|
@ -1948,7 +1969,7 @@ fn todo_bad_error_message() {
|
|||
r#"
|
||||
app "test" provides [ main ] to "./platform"
|
||||
|
||||
Effect a : [ @Effect ({} -> a) ]
|
||||
Effect a := {} -> a
|
||||
|
||||
effectAlways : a -> Effect a
|
||||
effectAlways = \x ->
|
||||
|
@ -2972,6 +2993,7 @@ fn mix_function_and_closure_level_of_indirection() {
|
|||
|
||||
#[test]
|
||||
#[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() {
|
||||
// see https://github.com/rtfeldman/roc/pull/1706
|
||||
// the distinction is actually important, dropping that info means some functions just get
|
||||
|
@ -3079,7 +3101,7 @@ fn nested_rigid_alias() {
|
|||
r#"
|
||||
app "test" provides [ main ] to "./platform"
|
||||
|
||||
Identity a : [ @Identity a ]
|
||||
Identity a := a
|
||||
|
||||
foo : Identity a -> Identity a
|
||||
foo = \list ->
|
||||
|
@ -3106,15 +3128,15 @@ fn nested_rigid_tag_union() {
|
|||
r#"
|
||||
app "test" provides [ main ] to "./platform"
|
||||
|
||||
foo : [ @Identity a ] -> [ @Identity a ]
|
||||
foo : [ Identity a ] -> [ Identity a ]
|
||||
foo = \list ->
|
||||
p2 : [ @Identity a ]
|
||||
p2 : [ Identity a ]
|
||||
p2 = list
|
||||
|
||||
p2
|
||||
|
||||
main =
|
||||
when foo (@Identity "foo") is
|
||||
when foo (Identity "foo") is
|
||||
_ -> "hello world"
|
||||
"#
|
||||
),
|
||||
|
@ -3200,7 +3222,7 @@ fn recursively_build_effect() {
|
|||
always {} |> after \_ -> nestHelp (m - 1)
|
||||
|
||||
|
||||
XEffect a : [ @XEffect ({} -> a) ]
|
||||
XEffect a := {} -> a
|
||||
|
||||
always : a -> XEffect a
|
||||
always = \x -> @XEffect (\{} -> x)
|
||||
|
|
|
@ -1055,10 +1055,10 @@ fn phantom_polymorphic() {
|
|||
r"#
|
||||
Point coordinate : [ Point coordinate I64 I64 ]
|
||||
|
||||
World : [ @World ]
|
||||
World := {}
|
||||
|
||||
zero : Point World
|
||||
zero = Point @World 0 0
|
||||
zero = Point (@World {}) 0 0
|
||||
|
||||
add : Point a -> Point a
|
||||
add = \(Point c x y) -> (Point c x y)
|
||||
|
@ -1580,3 +1580,28 @@ fn issue_2725_alias_polymorphic_lambda() {
|
|||
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