Merge remote-tracking branch 'origin/trunk' into scope-smarter-storage

This commit is contained in:
Folkert 2022-04-25 21:24:44 +02:00
commit 6028507dff
No known key found for this signature in database
GPG key ID: 1F17F6FFD112B97C
170 changed files with 9887 additions and 3991 deletions

View file

@ -77,3 +77,4 @@ Cai Bingjun <62678643+C-BJ@users.noreply.github.com>
Jared Cone <jared.cone@gmail.com> Jared Cone <jared.cone@gmail.com>
Sean Hagstrom <sean@seanhagstrom.com> Sean Hagstrom <sean@seanhagstrom.com>
Kas Buunk <kasbuunk@icloud.com> Kas Buunk <kasbuunk@icloud.com>
Oskar Hahn <mail@oshahn.de>

10
Cargo.lock generated
View file

@ -3253,9 +3253,9 @@ dependencies = [
[[package]] [[package]]
name = "regex" name = "regex"
version = "1.5.4" version = "1.5.5"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d07a8629359eb56f1e2fb1652bb04212c072a87ba68546a04065d525673ac461" checksum = "1a11647b6b25ff05a515cb92c365cec08801e83423a235b51e231e1808747286"
dependencies = [ dependencies = [
"aho-corasick", "aho-corasick",
"memchr", "memchr",
@ -3955,7 +3955,9 @@ dependencies = [
"arrayvec 0.7.2", "arrayvec 0.7.2",
"bumpalo", "bumpalo",
"indoc", "indoc",
"lazy_static",
"pretty_assertions", "pretty_assertions",
"regex",
"roc_builtins", "roc_builtins",
"roc_can", "roc_can",
"roc_collections", "roc_collections",
@ -4489,9 +4491,9 @@ checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369"
[[package]] [[package]]
name = "target-lexicon" name = "target-lexicon"
version = "0.12.2" version = "0.12.3"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d9bffcddbc2458fa3e6058414599e3c838a022abae82e5c67b4f7f80298d5bff" checksum = "d7fa7e55043acb85fca6b3c01485a2eeb6b69c5d21002e273c79e465f43b7ac1"
[[package]] [[package]]
name = "tempfile" name = "tempfile"

View file

@ -115,18 +115,19 @@ Create a new file called `Hello.roc` and put this inside it:
```coffee ```coffee
app "hello" app "hello"
packages { pf: "examples/cli/platform" } packages { pf: "examples/interactive/cli-platform" }
imports [ pf.Stdout ] imports [ pf.Stdout ]
provides [ main ] to pf provides [ main ] to pf
main = Stdout.line "I'm a Roc application!" main = Stdout.line "I'm a Roc application!"
``` ```
> **NOTE:** This assumes you've put Hello.roc in the root directory of the > **NOTE:** This assumes you've put Hello.roc in the root directory of the Roc
> Roc source code. If you'd like to put it somewhere else, you'll need to replace > source code. If you'd like to put it somewhere else, you'll need to replace
> `"examples/cli/"` with the path to the `examples/cli/` folder in > `"examples/interactive/cli-platform"` with the path to the
> that source code. In the future, Roc will have the tutorial built in, and this > `examples/interactive/cli-platform` folder in that source code. In the future,
> aside will no longer be necessary! > Roc will have the tutorial built in, and this aside will no longer be
> necessary!
Try running this with: Try running this with:
@ -1202,6 +1203,24 @@ and also `Num.cos 1` and have them all work as expected; the number literal `1`
`Num *`, which is compatible with the more constrained types `Int` and `Frac`. For the same reason, `Num *`, which is compatible with the more constrained types `Int` and `Frac`. For the same reason,
you can pass number literals to functions expecting even more constrained types, like `I32` or `F64`. you can pass number literals to functions expecting even more constrained types, like `I32` or `F64`.
### Typed Number Literals
When writing a number literal in Roc you can specify the numeric type as a suffix of the literal.
`1u8` specifies `1` as an unsigned 8-bit integer, `5i32` specifies `5` as a signed 32-bit integer, etc.
The full list of possible suffixes includes:
`i8`, `u8`, `i16`, `u16`, `i32`, `u32`, `i64`, `u64`, `i128`, `u128`, `nat`, `f32`, `f64`, `dec`
### Hexadecimal Integer Literals
Integer literals can be written in hexadecimal form by prefixing with `0x` followed by hexadecimal characters.
`0xFE` evaluates to decimal `254`
The integer type can be specified as a suffix to the hexadecimal literal,
so `0xC8u8` evaluates to decimal `200` as an unsigned 8-bit integer.
### Binary Integer Literals
Integer literals can be written in binary form by prefixing with `0b` followed by the 1's and 0's representing
each bit. `0b0000_1000` evaluates to decimal `8`
The integer type can be specified as a suffix to the binary literal,
so `0b0100u8` evaluates to decimal `4` as an unsigned 8-bit integer.
## Interface modules ## Interface modules
[ This part of the tutorial has not been written yet. Coming soon! ] [ This part of the tutorial has not been written yet. Coming soon! ]
@ -1236,7 +1255,7 @@ Let's take a closer look at the part of `Hello.roc` above `main`:
```coffee ```coffee
app "hello" app "hello"
packages { pf: "examples/cli/platform" } packages { pf: "examples/interactive/cli-platform" }
imports [ pf.Stdout ] imports [ pf.Stdout ]
provides main to pf provides main to pf
``` ```
@ -1254,14 +1273,14 @@ without running it by running `roc build Hello.roc`.
The remaining lines all involve the *platform* this application is built on: The remaining lines all involve the *platform* this application is built on:
```coffee ```coffee
packages { pf: "examples/cli/platform" } packages { pf: "examples/interactive/cli-platform" }
imports [ pf.Stdout ] imports [ pf.Stdout ]
provides main to pf provides main to pf
``` ```
The `packages { pf: "examples/cli/platform" }` part says two things: The `packages { pf: "examples/interactive/cli-platform" }` part says two things:
- We're going to be using a *package* (that is, a collection of modules) called `"examples/cli/platform"` - We're going to be using a *package* (that is, a collection of modules) called `"examples/interactive/cli-platform"`
- We're going to name that package `pf` so we can refer to it more concisely in the future. - We're going to name that package `pf` so we can refer to it more concisely in the future.
The `imports [ pf.Stdout ]` line says that we want to import the `Stdout` module The `imports [ pf.Stdout ]` line says that we want to import the `Stdout` module
@ -1281,17 +1300,18 @@ calling a function named `line` which is exposed by a module named
When we write `imports [ pf.Stdout ]`, it specifies that the `Stdout` When we write `imports [ pf.Stdout ]`, it specifies that the `Stdout`
module comes from the `pf` package. module comes from the `pf` package.
Since `pf` was the name we chose for the `examples/cli/platform` package Since `pf` was the name we chose for the `examples/interactive/cli-platform`
(when we wrote `packages { pf: "examples/cli/platform" }`), this `imports` line package (when we wrote `packages { pf: "examples/interactive/cli-platform" }`),
tells the Roc compiler that when we call `Stdout.line`, it should look for that this `imports` line tells the Roc compiler that when we call `Stdout.line`, it
`line` function in the `Stdout` module of the `examples/cli/platform` package. should look for that `line` function in the `Stdout` module of the
`examples/interactive/cli-platform` package.
# Building a Command-Line Interface (CLI) # Building a Command-Line Interface (CLI)
## Tasks ## Tasks
Tasks are technically not part of the Roc language, but they're very common in Tasks are technically not part of the Roc language, but they're very common in
platforms. Let's use the CLI platform in `examples/cli` as an example! platforms. Let's use the CLI platform in `examples/interactive/cli-platform` as an example!
In the CLI platform, we have four operations we can do: In the CLI platform, we have four operations we can do:
@ -1306,7 +1326,7 @@ First, let's do a basic "Hello World" using the tutorial app.
```coffee ```coffee
app "cli-tutorial" app "cli-tutorial"
packages { pf: "examples/cli/platform" } packages { pf: "examples/interactive/cli-platform" }
imports [ pf.Stdout ] imports [ pf.Stdout ]
provides [ main ] to pf provides [ main ] to pf
@ -1343,7 +1363,7 @@ Let's change `main` to read a line from `stdin`, and then print it back out agai
```swift ```swift
app "cli-tutorial" app "cli-tutorial"
packages { pf: "examples/cli/platform" } packages { pf: "examples/interactive/cli-platform" }
imports [ pf.Stdout, pf.Stdin, pf.Task ] imports [ pf.Stdout, pf.Stdin, pf.Task ]
provides [ main ] to pf provides [ main ] to pf
@ -1393,7 +1413,7 @@ This works, but we can make it a little nicer to read. Let's change it to the fo
```haskell ```haskell
app "cli-tutorial" app "cli-tutorial"
packages { pf: "examples/cli/platform" } packages { pf: "examples/interactive/cli-platform" }
imports [ pf.Stdout, pf.Stdin, pf.Task.{ await } ] imports [ pf.Stdout, pf.Stdin, pf.Task.{ await } ]
provides [ main ] to pf provides [ main ] to pf
@ -1944,7 +1964,6 @@ Here are various Roc expressions involving operators, and what they desugar to.
| `a // b` | `Num.divTrunc a b` | | `a // b` | `Num.divTrunc a b` |
| `a ^ b` | `Num.pow a b` | | `a ^ b` | `Num.pow a b` |
| `a % b` | `Num.rem a b` | | `a % b` | `Num.rem a b` |
| `a %% b` | `Num.mod a b` |
| `a >> b` | `Num.shr a b` | | `a >> b` | `Num.shr a b` |
| `a << b` | `Num.shl a b` | | `a << b` | `Num.shl a b` |
| `-a` | `Num.neg a` | | `-a` | `Num.neg a` |

View file

@ -264,25 +264,6 @@ pub fn constrain_expr<'a>(
*variant_var, *variant_var,
) )
} }
Expr2::PrivateTag {
name,
arguments,
ext_var,
variant_var,
} => {
let tag_name = TagName::Private(*name);
constrain_tag(
arena,
env,
expected,
region,
tag_name,
arguments,
*ext_var,
*variant_var,
)
}
Expr2::Call { Expr2::Call {
args, args,
expr_var, expr_var,
@ -678,24 +659,28 @@ pub fn constrain_expr<'a>(
for (index, when_branch_id) in branches.iter_node_ids().enumerate() { for (index, when_branch_id) in branches.iter_node_ids().enumerate() {
let when_branch = env.pool.get(when_branch_id); let when_branch = env.pool.get(when_branch_id);
let pattern_region = region;
// let pattern_region = Region::across_all( // let pattern_region = Region::across_all(
// when_branch.patterns.iter(env.pool).map(|v| &v.region), // when_branch.patterns.iter(env.pool).map(|v| &v.region),
// ); // );
let pattern_expected = |sub_pattern, sub_region| {
PExpected::ForReason(
PReason::WhenMatch {
index: HumanIndex::zero_based(index),
sub_pattern,
},
cond_type.shallow_clone(),
sub_region,
)
};
let branch_con = constrain_when_branch( let branch_con = constrain_when_branch(
arena, arena,
env, env,
// TODO: when_branch.value.region, // TODO: when_branch.value.region,
region, region,
when_branch, when_branch,
PExpected::ForReason( pattern_expected,
PReason::WhenMatch {
index: HumanIndex::zero_based(index),
},
cond_type.shallow_clone(),
pattern_region,
),
Expected::FromAnnotation( Expected::FromAnnotation(
name.clone(), name.clone(),
*arity, *arity,
@ -722,22 +707,26 @@ pub fn constrain_expr<'a>(
for (index, when_branch_id) in branches.iter_node_ids().enumerate() { for (index, when_branch_id) in branches.iter_node_ids().enumerate() {
let when_branch = env.pool.get(when_branch_id); let when_branch = env.pool.get(when_branch_id);
let pattern_region = region;
// let pattern_region = // let pattern_region =
// Region::across_all(when_branch.patterns.iter().map(|v| &v.region)); // Region::across_all(when_branch.patterns.iter().map(|v| &v.region));
let pattern_expected = |sub_pattern, sub_region| {
PExpected::ForReason(
PReason::WhenMatch {
index: HumanIndex::zero_based(index),
sub_pattern,
},
cond_type.shallow_clone(),
sub_region,
)
};
let branch_con = constrain_when_branch( let branch_con = constrain_when_branch(
arena, arena,
env, env,
region, region,
when_branch, when_branch,
PExpected::ForReason( pattern_expected,
PReason::WhenMatch {
index: HumanIndex::zero_based(index),
},
cond_type.shallow_clone(),
pattern_region,
),
Expected::ForReason( Expected::ForReason(
Reason::WhenBranch { Reason::WhenBranch {
index: HumanIndex::zero_based(index), index: HumanIndex::zero_based(index),
@ -1296,7 +1285,7 @@ fn constrain_when_branch<'a>(
env: &mut Env, env: &mut Env,
region: Region, region: Region,
when_branch: &WhenBranch, when_branch: &WhenBranch,
pattern_expected: PExpected<Type2>, pattern_expected: impl Fn(HumanIndex, Region) -> PExpected<Type2>,
expr_expected: Expected<Type2>, expr_expected: Expected<Type2>,
) -> Constraint<'a> { ) -> Constraint<'a> {
let when_expr = env.pool.get(when_branch.body); let when_expr = env.pool.get(when_branch.body);
@ -1311,16 +1300,22 @@ fn constrain_when_branch<'a>(
// TODO investigate for error messages, is it better to unify all branches with a variable, // TODO investigate for error messages, is it better to unify all branches with a variable,
// then unify that variable with the expectation? // then unify that variable with the expectation?
for pattern_id in when_branch.patterns.iter_node_ids() { for (sub_pattern, pattern_id) in when_branch.patterns.iter_node_ids().enumerate() {
let pattern = env.pool.get(pattern_id); let pattern = env.pool.get(pattern_id);
let pattern_expected = pattern_expected(
HumanIndex::zero_based(sub_pattern),
// TODO: use the proper subpattern region. Not available to us right now.
region,
);
constrain_pattern( constrain_pattern(
arena, arena,
env, env,
pattern, pattern,
// loc_pattern.region, // loc_pattern.region,
region, region,
pattern_expected.shallow_clone(), pattern_expected,
&mut state, &mut state,
true, true,
); );
@ -1617,27 +1612,6 @@ pub fn constrain_pattern<'a>(
} => { } => {
let tag_name = TagName::Global(name.as_str(env.pool).into()); let tag_name = TagName::Global(name.as_str(env.pool).into());
constrain_tag_pattern(
arena,
env,
region,
expected,
state,
*whole_var,
*ext_var,
arguments,
tag_name,
destruct_position,
);
}
PrivateTag {
whole_var,
ext_var,
tag_name: name,
arguments,
} => {
let tag_name = TagName::Private(*name);
constrain_tag_pattern( constrain_tag_pattern(
arena, arena,
env, env,
@ -1881,19 +1855,9 @@ fn num_float(pool: &mut Pool, range: TypeId) -> Type2 {
fn num_floatingpoint(pool: &mut Pool, range: TypeId) -> Type2 { fn num_floatingpoint(pool: &mut Pool, range: TypeId) -> Type2 {
let range_type = pool.get(range); let range_type = pool.get(range);
let alias_content = Type2::TagUnion( let alias_content = range_type.shallow_clone();
PoolVec::new(
vec![(
TagName::Private(Symbol::NUM_AT_FLOATINGPOINT),
PoolVec::new(vec![range_type.shallow_clone()].into_iter(), pool),
)]
.into_iter(),
pool,
),
pool.add(Type2::EmptyTagUnion),
);
Type2::Alias( Type2::Opaque(
Symbol::NUM_FLOATINGPOINT, Symbol::NUM_FLOATINGPOINT,
PoolVec::new(vec![(PoolStr::new("range", pool), range)].into_iter(), pool), PoolVec::new(vec![(PoolStr::new("range", pool), range)].into_iter(), pool),
pool.add(alias_content), pool.add(alias_content),
@ -1917,37 +1881,16 @@ fn num_int(pool: &mut Pool, range: TypeId) -> Type2 {
#[inline(always)] #[inline(always)]
fn _num_signed64(pool: &mut Pool) -> Type2 { fn _num_signed64(pool: &mut Pool) -> Type2 {
let alias_content = Type2::TagUnion(
PoolVec::new(
vec![(
TagName::Private(Symbol::NUM_AT_SIGNED64),
PoolVec::empty(pool),
)]
.into_iter(),
pool,
),
pool.add(Type2::EmptyTagUnion),
);
Type2::Alias( Type2::Alias(
Symbol::NUM_SIGNED64, Symbol::NUM_SIGNED64,
PoolVec::empty(pool), PoolVec::empty(pool),
pool.add(alias_content), pool.add(Type2::EmptyTagUnion),
) )
} }
#[inline(always)] #[inline(always)]
fn num_unsigned32(pool: &mut Pool) -> Type2 { fn num_unsigned32(pool: &mut Pool) -> Type2 {
let alias_content = Type2::TagUnion( let alias_content = Type2::EmptyTagUnion;
PoolVec::new(
std::iter::once((
TagName::Private(Symbol::NUM_UNSIGNED32),
PoolVec::empty(pool),
)),
pool,
),
pool.add(Type2::EmptyTagUnion),
);
Type2::Alias( Type2::Alias(
Symbol::NUM_UNSIGNED32, Symbol::NUM_UNSIGNED32,
@ -1960,19 +1903,9 @@ fn num_unsigned32(pool: &mut Pool) -> Type2 {
fn _num_integer(pool: &mut Pool, range: TypeId) -> Type2 { fn _num_integer(pool: &mut Pool, range: TypeId) -> Type2 {
let range_type = pool.get(range); let range_type = pool.get(range);
let alias_content = Type2::TagUnion( let alias_content = range_type.shallow_clone();
PoolVec::new(
vec![(
TagName::Private(Symbol::NUM_AT_INTEGER),
PoolVec::new(vec![range_type.shallow_clone()].into_iter(), pool),
)]
.into_iter(),
pool,
),
pool.add(Type2::EmptyTagUnion),
);
Type2::Alias( Type2::Opaque(
Symbol::NUM_INTEGER, Symbol::NUM_INTEGER,
PoolVec::new(vec![(PoolStr::new("range", pool), range)].into_iter(), pool), PoolVec::new(vec![(PoolStr::new("range", pool), range)].into_iter(), pool),
pool.add(alias_content), pool.add(alias_content),
@ -1983,19 +1916,9 @@ fn _num_integer(pool: &mut Pool, range: TypeId) -> Type2 {
fn num_num(pool: &mut Pool, type_id: TypeId) -> Type2 { fn num_num(pool: &mut Pool, type_id: TypeId) -> Type2 {
let range_type = pool.get(type_id); let range_type = pool.get(type_id);
let alias_content = Type2::TagUnion( let alias_content = range_type.shallow_clone();
PoolVec::new(
vec![(
TagName::Private(Symbol::NUM_AT_NUM),
PoolVec::new(vec![range_type.shallow_clone()].into_iter(), pool),
)]
.into_iter(),
pool,
),
pool.add(Type2::EmptyTagUnion),
);
Type2::Alias( Type2::Opaque(
Symbol::NUM_NUM, Symbol::NUM_NUM,
PoolVec::new( PoolVec::new(
vec![(PoolStr::new("range", pool), type_id)].into_iter(), vec![(PoolStr::new("range", pool), type_id)].into_iter(),
@ -2286,18 +2209,6 @@ pub mod test_constrain {
) )
} }
#[test]
fn constrain_private_tag() {
infer_eq(
indoc!(
r#"
@Foo
"#
),
"[ @Foo ]*",
)
}
#[test] #[test]
fn constrain_call_and_accessor() { fn constrain_call_and_accessor() {
infer_eq( infer_eq(

View file

@ -154,12 +154,6 @@ pub enum Expr2 {
ext_var: Variable, // 4B ext_var: Variable, // 4B
arguments: PoolVec<(Variable, ExprId)>, // 8B arguments: PoolVec<(Variable, ExprId)>, // 8B
}, },
PrivateTag {
name: Symbol, // 8B
variant_var: Variable, // 4B
ext_var: Variable, // 4B
arguments: PoolVec<(Variable, ExprId)>, // 8B
},
Blank, // Rendered as empty box in editor Blank, // Rendered as empty box in editor
// Compiles, but will crash if reached // Compiles, but will crash if reached

View file

@ -185,20 +185,6 @@ pub fn expr_to_expr2<'a>(
Output::default(), Output::default(),
) )
} }
PrivateTag(name) => {
// a private tag without any arguments
let ident_id = env.ident_ids.get_or_insert(&(*name).into());
let name = Symbol::new(env.home, ident_id);
(
Expr2::PrivateTag {
name,
variant_var: env.var_store.fresh(),
ext_var: env.var_store.fresh(),
arguments: PoolVec::empty(env.pool),
},
Output::default(),
)
}
RecordUpdate { RecordUpdate {
fields, fields,
@ -568,17 +554,6 @@ pub fn expr_to_expr2<'a>(
name, name,
arguments: args, arguments: args,
}, },
Expr2::PrivateTag {
variant_var,
ext_var,
name,
..
} => Expr2::PrivateTag {
variant_var,
ext_var,
name,
arguments: args,
},
_ => { _ => {
// This could be something like ((if True then fn1 else fn2) arg1 arg2). // This could be something like ((if True then fn1 else fn2) arg1 arg2).
let fn_expr_id = env.add(fn_expr, fn_region); let fn_expr_id = env.add(fn_expr, fn_region);

View file

@ -47,12 +47,6 @@ pub enum Pattern2 {
tag_name: PoolStr, // 8B tag_name: PoolStr, // 8B
arguments: PoolVec<(Variable, PatternId)>, // 8B arguments: PoolVec<(Variable, PatternId)>, // 8B
}, },
PrivateTag {
whole_var: Variable, // 4B
ext_var: Variable, // 4B
tag_name: Symbol, // 8B
arguments: PoolVec<(Variable, PatternId)>, // 8B
},
RecordDestructure { RecordDestructure {
whole_var: Variable, // 4B whole_var: Variable, // 4B
ext_var: Variable, // 4B ext_var: Variable, // 4B
@ -280,17 +274,6 @@ pub fn to_pattern2<'a>(
arguments: PoolVec::empty(env.pool), arguments: PoolVec::empty(env.pool),
} }
} }
PrivateTag(name) => {
let ident_id = env.ident_ids.get_or_insert(&(*name).into());
// Canonicalize the tag's name.
Pattern2::PrivateTag {
whole_var: env.var_store.fresh(),
ext_var: env.var_store.fresh(),
tag_name: Symbol::new(env.home, ident_id),
arguments: PoolVec::empty(env.pool),
}
}
OpaqueRef(..) => todo_opaques!(), OpaqueRef(..) => todo_opaques!(),
@ -319,16 +302,6 @@ pub fn to_pattern2<'a>(
tag_name: PoolStr::new(name, env.pool), tag_name: PoolStr::new(name, env.pool),
arguments: can_patterns, arguments: can_patterns,
}, },
PrivateTag(name) => {
let ident_id = env.ident_ids.get_or_insert(&name.into());
Pattern2::PrivateTag {
whole_var: env.var_store.fresh(),
ext_var: env.var_store.fresh(),
tag_name: Symbol::new(env.home, ident_id),
arguments: can_patterns,
}
}
_ => unreachable!("Other patterns cannot be applied"), _ => unreachable!("Other patterns cannot be applied"),
} }
} }
@ -506,7 +479,7 @@ pub fn symbols_from_pattern(pool: &Pool, initial: &Pattern2) -> Vec<Symbol> {
symbols.push(*symbol); symbols.push(*symbol);
} }
GlobalTag { arguments, .. } | PrivateTag { arguments, .. } => { GlobalTag { arguments, .. } => {
for (_, pat_id) in arguments.iter(pool) { for (_, pat_id) in arguments.iter(pool) {
let pat = pool.get(*pat_id); let pat = pool.get(*pat_id);
stack.push(pat); stack.push(pat);
@ -567,7 +540,7 @@ pub fn symbols_and_variables_from_pattern(
symbols.push((*symbol, variable)); symbols.push((*symbol, variable));
} }
GlobalTag { arguments, .. } | PrivateTag { arguments, .. } => { GlobalTag { arguments, .. } => {
for (var, pat_id) in arguments.iter(pool) { for (var, pat_id) in arguments.iter(pool) {
let pat = pool.get(*pat_id); let pat = pool.get(*pat_id);
stack.push((*var, pat)); stack.push((*var, pat));

View file

@ -24,6 +24,7 @@ pub enum Type2 {
Variable(Variable), // 4B Variable(Variable), // 4B
Alias(Symbol, PoolVec<(PoolStr, TypeId)>, TypeId), // 24B = 8B + 8B + 4B + pad Alias(Symbol, PoolVec<(PoolStr, TypeId)>, TypeId), // 24B = 8B + 8B + 4B + pad
Opaque(Symbol, PoolVec<(PoolStr, TypeId)>, TypeId), // 24B = 8B + 8B + 4B + pad
AsAlias(Symbol, PoolVec<(PoolStr, TypeId)>, TypeId), // 24B = 8B + 8B + 4B + pad AsAlias(Symbol, PoolVec<(PoolStr, TypeId)>, TypeId), // 24B = 8B + 8B + 4B + pad
// 24B // 24B
@ -74,6 +75,9 @@ impl ShallowClone for Type2 {
Self::Alias(symbol, args, alias_type_id) => { Self::Alias(symbol, args, alias_type_id) => {
Self::Alias(*symbol, args.shallow_clone(), alias_type_id.clone()) Self::Alias(*symbol, args.shallow_clone(), alias_type_id.clone())
} }
Self::Opaque(symbol, args, alias_type_id) => {
Self::Opaque(*symbol, args.shallow_clone(), alias_type_id.clone())
}
Self::Record(fields, ext_id) => Self::Record(fields.shallow_clone(), ext_id.clone()), Self::Record(fields, ext_id) => Self::Record(fields.shallow_clone(), ext_id.clone()),
Self::Function(args, closure_type_id, ret_type_id) => Self::Function( Self::Function(args, closure_type_id, ret_type_id) => Self::Function(
args.shallow_clone(), args.shallow_clone(),
@ -101,7 +105,7 @@ impl Type2 {
Variable(v) => { Variable(v) => {
result.insert(*v); result.insert(*v);
} }
Alias(_, _, actual) | AsAlias(_, _, actual) => { Alias(_, _, actual) | AsAlias(_, _, actual) | Opaque(_, _, actual) => {
stack.push(pool.get(*actual)); stack.push(pool.get(*actual));
} }
HostExposedAlias { HostExposedAlias {
@ -702,21 +706,6 @@ fn can_tags<'a>(
break 'inner tag_name; break 'inner tag_name;
} }
Tag::Private { name, args } => {
let ident_id = env.ident_ids.get_or_insert(&name.value.into());
let symbol = Symbol::new(env.home, ident_id);
let arg_types = PoolVec::with_capacity(args.len() as u32, env.pool);
for (type_id, loc_arg) in arg_types.iter_node_ids().zip(args.iter()) {
as_type_id(env, scope, rigids, type_id, &loc_arg.value, loc_arg.region);
}
let tag_name = TagName::Private(symbol);
tag_types.push((tag_name.clone(), arg_types));
break 'inner tag_name;
}
Tag::SpaceBefore(nested, _) | Tag::SpaceAfter(nested, _) => { Tag::SpaceBefore(nested, _) | Tag::SpaceAfter(nested, _) => {
// check the nested tag instead // check the nested tag instead
tag = nested; tag = nested;

View file

@ -3,6 +3,7 @@
use bumpalo::Bump; use bumpalo::Bump;
use roc_can::expected::{Expected, PExpected}; use roc_can::expected::{Expected, PExpected};
use roc_collections::all::{BumpMap, BumpMapDefault, MutMap}; use roc_collections::all::{BumpMap, BumpMapDefault, MutMap};
use roc_error_macros::internal_error;
use roc_module::ident::TagName; use roc_module::ident::TagName;
use roc_module::symbol::Symbol; use roc_module::symbol::Symbol;
use roc_region::all::{Loc, Region}; use roc_region::all::{Loc, Region};
@ -868,7 +869,7 @@ fn type_to_variable<'a>(
register(subs, rank, pools, content) register(subs, rank, pools, content)
} }
Alias(symbol, args, alias_type_id) => { Alias(symbol, args, alias_type_id) | Opaque(symbol, args, alias_type_id) => {
// TODO cache in uniqueness inference gives problems! all Int's get the same uniqueness var! // TODO cache in uniqueness inference gives problems! all Int's get the same uniqueness var!
// Cache aliases without type arguments. Commonly used aliases like `Int` would otherwise get O(n) // Cache aliases without type arguments. Commonly used aliases like `Int` would otherwise get O(n)
// different variables (once for each occurrence). The recursion restriction is required // different variables (once for each occurrence). The recursion restriction is required
@ -910,8 +911,12 @@ fn type_to_variable<'a>(
let alias_var = type_to_variable(arena, mempool, subs, rank, pools, cached, alias_type); let alias_var = type_to_variable(arena, mempool, subs, rank, pools, cached, alias_type);
// TODO(opaques): take opaques into account let kind = match typ {
let content = Content::Alias(*symbol, arg_vars, alias_var, AliasKind::Structural); Alias(..) => AliasKind::Structural,
Opaque(..) => AliasKind::Opaque,
_ => internal_error!(),
};
let content = Content::Alias(*symbol, arg_vars, alias_var, kind);
let result = register(subs, rank, pools, content); let result = register(subs, rank, pools, content);

View file

@ -66,7 +66,7 @@ const_format = "0.2.22"
bumpalo = { version = "3.8.0", features = ["collections"] } bumpalo = { version = "3.8.0", features = ["collections"] }
mimalloc = { version = "0.1.26", default-features = false } mimalloc = { version = "0.1.26", default-features = false }
target-lexicon = "0.12.2" target-lexicon = "0.12.3"
tempfile = "3.2.0" tempfile = "3.2.0"
wasmer-wasi = { version = "2.0.0", optional = true } wasmer-wasi = { version = "2.0.0", optional = true }

View file

@ -618,7 +618,6 @@ impl<'a> RemoveSpaces<'a> for Expr<'a> {
Expr::Var { module_name, ident } => Expr::Var { module_name, ident }, Expr::Var { module_name, ident } => Expr::Var { module_name, ident },
Expr::Underscore(a) => Expr::Underscore(a), Expr::Underscore(a) => Expr::Underscore(a),
Expr::GlobalTag(a) => Expr::GlobalTag(a), Expr::GlobalTag(a) => Expr::GlobalTag(a),
Expr::PrivateTag(a) => Expr::PrivateTag(a),
Expr::OpaqueRef(a) => Expr::OpaqueRef(a), Expr::OpaqueRef(a) => Expr::OpaqueRef(a),
Expr::Closure(a, b) => Expr::Closure( Expr::Closure(a, b) => Expr::Closure(
arena.alloc(a.remove_spaces(arena)), arena.alloc(a.remove_spaces(arena)),
@ -670,7 +669,6 @@ impl<'a> RemoveSpaces<'a> for Pattern<'a> {
match *self { match *self {
Pattern::Identifier(a) => Pattern::Identifier(a), Pattern::Identifier(a) => Pattern::Identifier(a),
Pattern::GlobalTag(a) => Pattern::GlobalTag(a), Pattern::GlobalTag(a) => Pattern::GlobalTag(a),
Pattern::PrivateTag(a) => Pattern::PrivateTag(a),
Pattern::OpaqueRef(a) => Pattern::OpaqueRef(a), Pattern::OpaqueRef(a) => Pattern::OpaqueRef(a),
Pattern::Apply(a, b) => Pattern::Apply( Pattern::Apply(a, b) => Pattern::Apply(
arena.alloc(a.remove_spaces(arena)), arena.alloc(a.remove_spaces(arena)),
@ -757,10 +755,6 @@ impl<'a> RemoveSpaces<'a> for Tag<'a> {
name: name.remove_spaces(arena), name: name.remove_spaces(arena),
args: args.remove_spaces(arena), args: args.remove_spaces(arena),
}, },
Tag::Private { name, args } => Tag::Private {
name: name.remove_spaces(arena),
args: args.remove_spaces(arena),
},
Tag::Malformed(a) => Tag::Malformed(a), Tag::Malformed(a) => Tag::Malformed(a),
Tag::SpaceBefore(a, _) => a.remove_spaces(arena), Tag::SpaceBefore(a, _) => a.remove_spaces(arena),
Tag::SpaceAfter(a, _) => a.remove_spaces(arena), Tag::SpaceAfter(a, _) => a.remove_spaces(arena),

View file

@ -256,8 +256,8 @@ mod cli_run {
return; return;
} }
} }
"hello-gui" => { "hello-gui" | "breakout" => {
// Since this one requires opening a window, we do `roc build` on it but don't run it. // Since these require opening a window, we do `roc build` on them but don't run them.
build_example(&file_name, &["--optimize"]); build_example(&file_name, &["--optimize"]);
return; return;
@ -394,6 +394,14 @@ mod cli_run {
expected_ending: "", expected_ending: "",
use_valgrind: false, use_valgrind: false,
}, },
breakout:"breakout" => Example {
filename: "breakout.roc",
executable_filename: "breakout",
stdin: &[],
input_file: None,
expected_ending: "",
use_valgrind: false,
},
quicksort:"algorithms" => Example { quicksort:"algorithms" => Example {
filename: "quicksort.roc", filename: "quicksort.roc",
executable_filename: "quicksort", executable_filename: "quicksort",

View file

@ -167,6 +167,20 @@ For a more detailed understanding of the compilation phases, see the `Phase`, `B
## Debugging intermediate representations ## Debugging intermediate representations
### Debugging the typechecker
Setting the following environment variables:
- `ROC_PRINT_UNIFICATIONS` prints all type unifications that are done,
before and after the unification.
- `ROC_PRINT_MISMATCHES` prints all type mismatches hit during unification.
- `ROC_PRETTY_PRINT_ALIAS_CONTENTS` expands the contents of aliases during
pretty-printing of types.
Note that this is only relevant during debug builds. Eventually we should have
some better debugging tools here, see https://github.com/rtfeldman/roc/issues/2486
for one.
### The mono IR ### The mono IR
If you observe a miscomplication, you may first want to check the generated mono If you observe a miscomplication, you may first want to check the generated mono

View file

@ -279,7 +279,8 @@ fn build_entry_point(
let block = builder.add_block(); let block = builder.add_block();
// to the modelling language, the arguments appear out of thin air // to the modelling language, the arguments appear out of thin air
let argument_type = build_tuple_type(&mut builder, layout.arguments)?; let argument_type =
build_tuple_type(&mut builder, layout.arguments, &WhenRecursive::Unreachable)?;
// does not make any assumptions about the input // does not make any assumptions about the input
// let argument = builder.add_unknown_with(block, &[], argument_type)?; // let argument = builder.add_unknown_with(block, &[], argument_type)?;
@ -308,7 +309,11 @@ fn build_entry_point(
let block = builder.add_block(); let block = builder.add_block();
let type_id = layout_spec(&mut builder, &Layout::struct_no_name_order(layouts))?; let type_id = layout_spec(
&mut builder,
&Layout::struct_no_name_order(layouts),
&WhenRecursive::Unreachable,
)?;
let argument = builder.add_unknown_with(block, &[], type_id)?; let argument = builder.add_unknown_with(block, &[], type_id)?;
@ -352,8 +357,9 @@ fn proc_spec<'a>(proc: &Proc<'a>) -> Result<(FuncDef, MutSet<UnionLayout<'a>>)>
let arg_type_id = layout_spec( let arg_type_id = layout_spec(
&mut builder, &mut builder,
&Layout::struct_no_name_order(&argument_layouts), &Layout::struct_no_name_order(&argument_layouts),
&WhenRecursive::Unreachable,
)?; )?;
let ret_type_id = layout_spec(&mut builder, &proc.ret_layout)?; let ret_type_id = layout_spec(&mut builder, &proc.ret_layout, &WhenRecursive::Unreachable)?;
let spec = builder.build(arg_type_id, ret_type_id, root)?; let spec = builder.build(arg_type_id, ret_type_id, root)?;
@ -457,10 +463,14 @@ fn stmt_spec<'a>(
let mut type_ids = Vec::new(); let mut type_ids = Vec::new();
for p in parameters.iter() { for p in parameters.iter() {
type_ids.push(layout_spec(builder, &p.layout)?); type_ids.push(layout_spec(
builder,
&p.layout,
&WhenRecursive::Unreachable,
)?);
} }
let ret_type_id = layout_spec(builder, layout)?; let ret_type_id = layout_spec(builder, layout, &WhenRecursive::Unreachable)?;
let jp_arg_type_id = builder.add_tuple_type(&type_ids)?; let jp_arg_type_id = builder.add_tuple_type(&type_ids)?;
@ -500,14 +510,14 @@ fn stmt_spec<'a>(
builder.add_sub_block(block, BlockExpr(cont_block, cont_value_id)) builder.add_sub_block(block, BlockExpr(cont_block, cont_value_id))
} }
Jump(id, symbols) => { Jump(id, symbols) => {
let ret_type_id = layout_spec(builder, layout)?; let ret_type_id = layout_spec(builder, layout, &WhenRecursive::Unreachable)?;
let argument = build_tuple_value(builder, env, block, symbols)?; let argument = build_tuple_value(builder, env, block, symbols)?;
let jpid = env.join_points[id]; let jpid = env.join_points[id];
builder.add_jump(block, jpid, argument, ret_type_id) builder.add_jump(block, jpid, argument, ret_type_id)
} }
RuntimeError(_) => { RuntimeError(_) => {
let type_id = layout_spec(builder, layout)?; let type_id = layout_spec(builder, layout, &WhenRecursive::Unreachable)?;
builder.add_terminate(block, type_id) builder.add_terminate(block, type_id)
} }
@ -556,11 +566,15 @@ fn build_recursive_tuple_type(
builder.add_tuple_type(&field_types) builder.add_tuple_type(&field_types)
} }
fn build_tuple_type(builder: &mut impl TypeContext, layouts: &[Layout]) -> Result<TypeId> { fn build_tuple_type(
builder: &mut impl TypeContext,
layouts: &[Layout],
when_recursive: &WhenRecursive,
) -> Result<TypeId> {
let mut field_types = Vec::new(); let mut field_types = Vec::new();
for field in layouts.iter() { for field in layouts.iter() {
field_types.push(layout_spec(builder, field)?); field_types.push(layout_spec(builder, field, when_recursive)?);
} }
builder.add_tuple_type(&field_types) builder.add_tuple_type(&field_types)
@ -691,7 +705,7 @@ fn call_spec(
.map(|symbol| env.symbols[symbol]) .map(|symbol| env.symbols[symbol])
.collect(); .collect();
let result_type = layout_spec(builder, ret_layout)?; let result_type = layout_spec(builder, ret_layout, &WhenRecursive::Unreachable)?;
builder.add_unknown_with(block, &arguments, result_type) builder.add_unknown_with(block, &arguments, result_type)
} }
@ -761,7 +775,8 @@ fn call_spec(
}; };
let state_layout = argument_layouts[0]; let state_layout = argument_layouts[0];
let state_type = layout_spec(builder, &state_layout)?; let state_type =
layout_spec(builder, &state_layout, &WhenRecursive::Unreachable)?;
let init_state = state; let init_state = state;
add_loop(builder, block, state_type, init_state, loop_body) add_loop(builder, block, state_type, init_state, loop_body)
@ -782,7 +797,8 @@ fn call_spec(
}; };
let state_layout = argument_layouts[0]; let state_layout = argument_layouts[0];
let state_type = layout_spec(builder, &state_layout)?; let state_type =
layout_spec(builder, &state_layout, &WhenRecursive::Unreachable)?;
let init_state = state; let init_state = state;
add_loop(builder, block, state_type, init_state, loop_body) add_loop(builder, block, state_type, init_state, loop_body)
@ -806,7 +822,8 @@ fn call_spec(
}; };
let state_layout = argument_layouts[0]; let state_layout = argument_layouts[0];
let state_type = layout_spec(builder, &state_layout)?; let state_type =
layout_spec(builder, &state_layout, &WhenRecursive::Unreachable)?;
let init_state = state; let init_state = state;
add_loop(builder, block, state_type, init_state, loop_body) add_loop(builder, block, state_type, init_state, loop_body)
@ -828,10 +845,12 @@ fn call_spec(
list_append(builder, block, update_mode_var, state, new_element) list_append(builder, block, update_mode_var, state, new_element)
}; };
let output_element_type = layout_spec(builder, return_layout)?; let output_element_type =
layout_spec(builder, return_layout, &WhenRecursive::Unreachable)?;
let state_layout = Layout::Builtin(Builtin::List(return_layout)); let state_layout = Layout::Builtin(Builtin::List(return_layout));
let state_type = layout_spec(builder, &state_layout)?; let state_type =
layout_spec(builder, &state_layout, &WhenRecursive::Unreachable)?;
let init_state = new_list(builder, block, output_element_type)?; let init_state = new_list(builder, block, output_element_type)?;
@ -851,10 +870,12 @@ fn call_spec(
list_append(builder, block, update_mode_var, state, new_element) list_append(builder, block, update_mode_var, state, new_element)
}; };
let output_element_type = layout_spec(builder, return_layout)?; let output_element_type =
layout_spec(builder, return_layout, &WhenRecursive::Unreachable)?;
let state_layout = Layout::Builtin(Builtin::List(return_layout)); let state_layout = Layout::Builtin(Builtin::List(return_layout));
let state_type = layout_spec(builder, &state_layout)?; let state_type =
layout_spec(builder, &state_layout, &WhenRecursive::Unreachable)?;
let init_state = new_list(builder, block, output_element_type)?; let init_state = new_list(builder, block, output_element_type)?;
@ -879,7 +900,8 @@ fn call_spec(
}; };
let state_layout = Layout::Builtin(Builtin::List(&argument_layouts[0])); let state_layout = Layout::Builtin(Builtin::List(&argument_layouts[0]));
let state_type = layout_spec(builder, &state_layout)?; let state_type =
layout_spec(builder, &state_layout, &WhenRecursive::Unreachable)?;
let init_state = list; let init_state = list;
add_loop(builder, block, state_type, init_state, loop_body) add_loop(builder, block, state_type, init_state, loop_body)
@ -903,10 +925,12 @@ fn call_spec(
list_append(builder, block, update_mode_var, state, new_element) list_append(builder, block, update_mode_var, state, new_element)
}; };
let output_element_type = layout_spec(builder, return_layout)?; let output_element_type =
layout_spec(builder, return_layout, &WhenRecursive::Unreachable)?;
let state_layout = Layout::Builtin(Builtin::List(return_layout)); let state_layout = Layout::Builtin(Builtin::List(return_layout));
let state_type = layout_spec(builder, &state_layout)?; let state_type =
layout_spec(builder, &state_layout, &WhenRecursive::Unreachable)?;
let init_state = new_list(builder, block, output_element_type)?; let init_state = new_list(builder, block, output_element_type)?;
@ -936,10 +960,12 @@ fn call_spec(
list_append(builder, block, update_mode_var, state, new_element) list_append(builder, block, update_mode_var, state, new_element)
}; };
let output_element_type = layout_spec(builder, return_layout)?; let output_element_type =
layout_spec(builder, return_layout, &WhenRecursive::Unreachable)?;
let state_layout = Layout::Builtin(Builtin::List(return_layout)); let state_layout = Layout::Builtin(Builtin::List(return_layout));
let state_type = layout_spec(builder, &state_layout)?; let state_type =
layout_spec(builder, &state_layout, &WhenRecursive::Unreachable)?;
let init_state = new_list(builder, block, output_element_type)?; let init_state = new_list(builder, block, output_element_type)?;
@ -975,10 +1001,12 @@ fn call_spec(
list_append(builder, block, update_mode_var, state, new_element) list_append(builder, block, update_mode_var, state, new_element)
}; };
let output_element_type = layout_spec(builder, return_layout)?; let output_element_type =
layout_spec(builder, return_layout, &WhenRecursive::Unreachable)?;
let state_layout = Layout::Builtin(Builtin::List(return_layout)); let state_layout = Layout::Builtin(Builtin::List(return_layout));
let state_type = layout_spec(builder, &state_layout)?; let state_type =
layout_spec(builder, &state_layout, &WhenRecursive::Unreachable)?;
let init_state = new_list(builder, block, output_element_type)?; let init_state = new_list(builder, block, output_element_type)?;
@ -1010,7 +1038,8 @@ fn call_spec(
}; };
let state_layout = Layout::Builtin(Builtin::List(&argument_layouts[0])); let state_layout = Layout::Builtin(Builtin::List(&argument_layouts[0]));
let state_type = layout_spec(builder, &state_layout)?; let state_type =
layout_spec(builder, &state_layout, &WhenRecursive::Unreachable)?;
let init_state = list; let init_state = list;
add_loop(builder, block, state_type, init_state, loop_body) add_loop(builder, block, state_type, init_state, loop_body)
@ -1087,11 +1116,13 @@ fn call_spec(
) )
}; };
let output_element_type = layout_spec(builder, &output_element_layout)?; let output_element_type =
layout_spec(builder, &output_element_layout, &WhenRecursive::Unreachable)?;
let init_state = new_list(builder, block, output_element_type)?; let init_state = new_list(builder, block, output_element_type)?;
let state_layout = Layout::Builtin(Builtin::List(&output_element_layout)); let state_layout = Layout::Builtin(Builtin::List(&output_element_layout));
let state_type = layout_spec(builder, &state_layout)?; let state_type =
layout_spec(builder, &state_layout, &WhenRecursive::Unreachable)?;
add_loop(builder, block, state_type, init_state, loop_body) add_loop(builder, block, state_type, init_state, loop_body)
} }
@ -1108,7 +1139,8 @@ fn call_spec(
}; };
let state_layout = Layout::Builtin(Builtin::Bool); let state_layout = Layout::Builtin(Builtin::Bool);
let state_type = layout_spec(builder, &state_layout)?; let state_type =
layout_spec(builder, &state_layout, &WhenRecursive::Unreachable)?;
let init_state = new_num(builder, block)?; let init_state = new_num(builder, block)?;
@ -1127,7 +1159,8 @@ fn call_spec(
}; };
let state_layout = Layout::Builtin(Builtin::Bool); let state_layout = Layout::Builtin(Builtin::Bool);
let state_type = layout_spec(builder, &state_layout)?; let state_type =
layout_spec(builder, &state_layout, &WhenRecursive::Unreachable)?;
let init_state = new_num(builder, block)?; let init_state = new_num(builder, block)?;
@ -1139,7 +1172,8 @@ fn call_spec(
// ListFindUnsafe returns { value: v, found: Bool=Int1 } // ListFindUnsafe returns { value: v, found: Bool=Int1 }
let output_layouts = vec![argument_layouts[0], Layout::Builtin(Builtin::Bool)]; let output_layouts = vec![argument_layouts[0], Layout::Builtin(Builtin::Bool)];
let output_layout = Layout::struct_no_name_order(&output_layouts); let output_layout = Layout::struct_no_name_order(&output_layouts);
let output_type = layout_spec(builder, &output_layout)?; let output_type =
layout_spec(builder, &output_layout, &WhenRecursive::Unreachable)?;
let loop_body = |builder: &mut FuncDefBuilder, block, output| { let loop_body = |builder: &mut FuncDefBuilder, block, output| {
let bag = builder.add_get_tuple_field(block, list, LIST_BAG_INDEX)?; let bag = builder.add_get_tuple_field(block, list, LIST_BAG_INDEX)?;
@ -1201,7 +1235,7 @@ fn lowlevel_spec(
) -> Result<ValueId> { ) -> Result<ValueId> {
use LowLevel::*; use LowLevel::*;
let type_id = layout_spec(builder, layout)?; let type_id = layout_spec(builder, layout, &WhenRecursive::Unreachable)?;
let mode = update_mode.to_bytes(); let mode = update_mode.to_bytes();
let update_mode_var = UpdateModeVar(&mode); let update_mode_var = UpdateModeVar(&mode);
@ -1323,8 +1357,8 @@ fn lowlevel_spec(
} }
DictEmpty => match layout { DictEmpty => match layout {
Layout::Builtin(Builtin::Dict(key_layout, value_layout)) => { Layout::Builtin(Builtin::Dict(key_layout, value_layout)) => {
let key_id = layout_spec(builder, key_layout)?; let key_id = layout_spec(builder, key_layout, &WhenRecursive::Unreachable)?;
let value_id = layout_spec(builder, value_layout)?; let value_id = layout_spec(builder, value_layout, &WhenRecursive::Unreachable)?;
new_dict(builder, block, key_id, value_id) new_dict(builder, block, key_id, value_id)
} }
_ => unreachable!("empty array does not have a list layout"), _ => unreachable!("empty array does not have a list layout"),
@ -1367,7 +1401,7 @@ fn lowlevel_spec(
// TODO overly pessimstic // TODO overly pessimstic
let arguments: Vec<_> = arguments.iter().map(|symbol| env.symbols[symbol]).collect(); let arguments: Vec<_> = arguments.iter().map(|symbol| env.symbols[symbol]).collect();
let result_type = layout_spec(builder, layout)?; let result_type = layout_spec(builder, layout, &WhenRecursive::Unreachable)?;
builder.add_unknown_with(block, &arguments, result_type) builder.add_unknown_with(block, &arguments, result_type)
} }
@ -1478,7 +1512,8 @@ fn expr_spec<'a>(
let value_id = match tag_layout { let value_id = match tag_layout {
UnionLayout::NonRecursive(tags) => { UnionLayout::NonRecursive(tags) => {
let variant_types = non_recursive_variant_types(builder, tags)?; let variant_types =
non_recursive_variant_types(builder, tags, &WhenRecursive::Unreachable)?;
let value_id = build_tuple_value(builder, env, block, arguments)?; let value_id = build_tuple_value(builder, env, block, arguments)?;
return builder.add_make_union(block, &variant_types, *tag_id as u32, value_id); return builder.add_make_union(block, &variant_types, *tag_id as u32, value_id);
} }
@ -1592,7 +1627,7 @@ fn expr_spec<'a>(
builder.add_get_tuple_field(block, value_id, *index as u32) builder.add_get_tuple_field(block, value_id, *index as u32)
} }
Array { elem_layout, elems } => { Array { elem_layout, elems } => {
let type_id = layout_spec(builder, elem_layout)?; let type_id = layout_spec(builder, elem_layout, &WhenRecursive::Unreachable)?;
let list = new_list(builder, block, type_id)?; let list = new_list(builder, block, type_id)?;
@ -1619,19 +1654,19 @@ fn expr_spec<'a>(
EmptyArray => match layout { EmptyArray => match layout {
Layout::Builtin(Builtin::List(element_layout)) => { Layout::Builtin(Builtin::List(element_layout)) => {
let type_id = layout_spec(builder, element_layout)?; let type_id = layout_spec(builder, element_layout, &WhenRecursive::Unreachable)?;
new_list(builder, block, type_id) new_list(builder, block, type_id)
} }
_ => unreachable!("empty array does not have a list layout"), _ => unreachable!("empty array does not have a list layout"),
}, },
Reset { symbol, .. } => { Reset { symbol, .. } => {
let type_id = layout_spec(builder, layout)?; let type_id = layout_spec(builder, layout, &WhenRecursive::Unreachable)?;
let value_id = env.symbols[symbol]; let value_id = env.symbols[symbol];
builder.add_unknown_with(block, &[value_id], type_id) builder.add_unknown_with(block, &[value_id], type_id)
} }
RuntimeErrorFunction(_) => { RuntimeErrorFunction(_) => {
let type_id = layout_spec(builder, layout)?; let type_id = layout_spec(builder, layout, &WhenRecursive::Unreachable)?;
builder.add_terminate(block, type_id) builder.add_terminate(block, type_id)
} }
@ -1658,18 +1693,24 @@ fn literal_spec(
} }
} }
fn layout_spec(builder: &mut impl TypeContext, layout: &Layout) -> Result<TypeId> { fn layout_spec(
layout_spec_help(builder, layout, &WhenRecursive::Unreachable) builder: &mut impl TypeContext,
layout: &Layout,
when_recursive: &WhenRecursive,
) -> Result<TypeId> {
layout_spec_help(builder, layout, when_recursive)
} }
fn non_recursive_variant_types( fn non_recursive_variant_types(
builder: &mut impl TypeContext, builder: &mut impl TypeContext,
tags: &[&[Layout]], tags: &[&[Layout]],
// If there is a recursive pointer latent within this layout, coming from a containing layout.
when_recursive: &WhenRecursive,
) -> Result<Vec<TypeId>> { ) -> Result<Vec<TypeId>> {
let mut result = Vec::with_capacity(tags.len()); let mut result = Vec::with_capacity(tags.len());
for tag in tags.iter() { for tag in tags.iter() {
result.push(build_tuple_type(builder, tag)?); result.push(build_tuple_type(builder, tag, when_recursive)?);
} }
Ok(result) Ok(result)
@ -1701,7 +1742,7 @@ fn layout_spec_help(
builder.add_tuple_type(&[]) builder.add_tuple_type(&[])
} }
UnionLayout::NonRecursive(tags) => { UnionLayout::NonRecursive(tags) => {
let variant_types = non_recursive_variant_types(builder, tags)?; let variant_types = non_recursive_variant_types(builder, tags, when_recursive)?;
builder.add_union_type(&variant_types) builder.add_union_type(&variant_types)
} }
UnionLayout::Recursive(_) UnionLayout::Recursive(_)

View file

@ -30,7 +30,7 @@ bumpalo = { version = "3.8.0", features = ["collections"] }
libloading = "0.7.1" libloading = "0.7.1"
tempfile = "3.2.0" tempfile = "3.2.0"
inkwell = { path = "../../vendor/inkwell", optional = true } inkwell = { path = "../../vendor/inkwell", optional = true }
target-lexicon = "0.12.2" target-lexicon = "0.12.3"
[target.'cfg(target_os = "macos")'.dependencies] [target.'cfg(target_os = "macos")'.dependencies]
serde_json = "1.0.69" serde_json = "1.0.69"

View file

@ -4,7 +4,7 @@ Builtins are the functions and modules that are implicitly imported into every m
### module/src/symbol.rs ### module/src/symbol.rs
Towards the bottom of `symbol.rs` there is a `define_builtins!` macro being used that takes many modules and function names. The first level (`List`, `Int` ..) is the module name, and the second level is the function or value name (`reverse`, `mod` ..). If you wanted to add a `Int` function called `addTwo` go to `2 Int: "Int" => {` and inside that case add to the bottom `38 INT_ADD_TWO: "addTwo"` (assuming there are 37 existing ones). Towards the bottom of `symbol.rs` there is a `define_builtins!` macro being used that takes many modules and function names. The first level (`List`, `Int` ..) is the module name, and the second level is the function or value name (`reverse`, `rem` ..). If you wanted to add a `Int` function called `addTwo` go to `2 Int: "Int" => {` and inside that case add to the bottom `38 INT_ADD_TWO: "addTwo"` (assuming there are 37 existing ones).
Some of these have `#` inside their name (`first#list`, `#lt` ..). This is a trick we are doing to hide implementation details from Roc programmers. To a Roc programmer, a name with `#` in it is invalid, because `#` means everything after it is parsed to a comment. We are constructing these functions manually, so we are circumventing the parsing step and dont have such restrictions. We get to make functions and values with `#` which as a consequence are not accessible to Roc programmers. Roc programmers simply cannot reference them. Some of these have `#` inside their name (`first#list`, `#lt` ..). This is a trick we are doing to hide implementation details from Roc programmers. To a Roc programmer, a name with `#` in it is invalid, because `#` means everything after it is parsed to a comment. We are constructing these functions manually, so we are circumventing the parsing step and dont have such restrictions. We get to make functions and values with `#` which as a consequence are not accessible to Roc programmers. Roc programmers simply cannot reference them.

View file

@ -70,10 +70,9 @@ xor : Bool, Bool -> Bool
## Structural equality works as follows: ## Structural equality works as follows:
## ##
## 1. Global tags are equal if they are the same tag, and also their contents (if any) are equal. ## 1. Global tags are equal if they are the same tag, and also their contents (if any) are equal.
## 2. Private tags are equal if they are the same tag, in the same module, and also their contents (if any) are equal. ## 2. Records are equal if all their fields are equal.
## 3. Records are equal if all their fields are equal. ## 3. Collections ([Str], [List], [Dict], and [Set]) are equal if they are the same length, and also all their corresponding elements are equal.
## 4. Collections ([Str], [List], [Dict], and [Set]) are equal if they are the same length, and also all their corresponding elements are equal. ## 4. [Num] values are equal if their numbers are equal, with one exception: if both arguments to `isEq` are *NaN*, then `isEq` returns `False`. See `Num.isNaN` for more about *NaN*.
## 5. [Num] values are equal if their numbers are equal, with one exception: if both arguments to `isEq` are *NaN*, then `isEq` returns `False`. See `Num.isNaN` for more about *NaN*.
## ##
## Note that `isEq` takes `'val` instead of `val`, which means `isEq` does not ## Note that `isEq` takes `'val` instead of `val`, which means `isEq` does not
## accept arguments whose types contain functions. ## accept arguments whose types contain functions.

View file

@ -93,7 +93,7 @@ interface Dict
## ##
## The [Dict.hasSameContents] function gives an alternative to `==` which ignores ordering ## The [Dict.hasSameContents] function gives an alternative to `==` which ignores ordering
## and returns `True` if both dictionaries have the same keys and associated values. ## and returns `True` if both dictionaries have the same keys and associated values.
Dict k v : [ @Dict k v ] # TODO k should require a hashing and equating constraint Dict k v := [ Dict k v ] # TODO k should require a hashing and equating constraint
## An empty dictionary. ## An empty dictionary.
empty : Dict * * empty : Dict * *

View file

@ -187,7 +187,7 @@ interface List
## * Even when copying is faster, other list operations may still be slightly slower with persistent data structures. For example, even if it were a persistent data structure, [List.map], [List.walk], and [List.keepIf] would all need to traverse every element in the list and build up the result from scratch. These operations are all ## * Even when copying is faster, other list operations may still be slightly slower with persistent data structures. For example, even if it were a persistent data structure, [List.map], [List.walk], and [List.keepIf] would all need to traverse every element in the list and build up the result from scratch. These operations are all
## * Roc's compiler optimizes many list operations into in-place mutations behind the scenes, depending on how the list is being used. For example, [List.map], [List.keepIf], and [List.set] can all be optimized to perform in-place mutations. ## * Roc's compiler optimizes many list operations into in-place mutations behind the scenes, depending on how the list is being used. For example, [List.map], [List.keepIf], and [List.set] can all be optimized to perform in-place mutations.
## * If possible, it is usually best for performance to use large lists in a way where the optimizer can turn them into in-place mutations. If this is not possible, a persistent data structure might be faster - but this is a rare enough scenario that it would not be good for the average Roc program's performance if this were the way [List] worked by default. Instead, you can look outside Roc's standard modules for an implementation of a persistent data structure - likely built using [List] under the hood! ## * If possible, it is usually best for performance to use large lists in a way where the optimizer can turn them into in-place mutations. If this is not possible, a persistent data structure might be faster - but this is a rare enough scenario that it would not be good for the average Roc program's performance if this were the way [List] worked by default. Instead, you can look outside Roc's standard modules for an implementation of a persistent data structure - likely built using [List] under the hood!
List elem : [ @List elem ] List elem := [ List elem ]
## Initialize ## Initialize

View file

@ -61,6 +61,7 @@ interface Num
isPositive, isPositive,
isZero, isZero,
log, log,
logChecked,
maxFloat, maxFloat,
maxI8, maxI8,
maxU8, maxU8,
@ -88,6 +89,7 @@ interface Num
pow, pow,
powInt, powInt,
rem, rem,
remChecked,
round, round,
shiftLeftBy, shiftLeftBy,
shiftRightBy, shiftRightBy,
@ -97,6 +99,7 @@ interface Num
subChecked, subChecked,
subWrap, subWrap,
sqrt, sqrt,
sqrtChecked,
tan, tan,
toI8, toI8,
toI8Checked, toI8Checked,
@ -186,7 +189,7 @@ interface Num
## ##
## In practice, these are rarely needed. It's most common to write ## In practice, these are rarely needed. It's most common to write
## number literals without any suffix. ## number literals without any suffix.
Num a : [ @Num a ] Num a := a
## A decimal number. ## A decimal number.
## ##
@ -220,7 +223,7 @@ Num a : [ @Num a ]
## [Dec] typically takes slightly less time than [F64] to perform addition and ## [Dec] typically takes slightly less time than [F64] to perform addition and
## subtraction, but 10-20 times longer to perform multiplication and division. ## subtraction, but 10-20 times longer to perform multiplication and division.
## [sqrt] and trigonometry are massively slower with [Dec] than with [F64]. ## [sqrt] and trigonometry are massively slower with [Dec] than with [F64].
Dec : Float [ @Decimal128 ] Dec : Num (FloatingPoint Decimal)
## A fixed-size number with a fractional component. ## A fixed-size number with a fractional component.
## ##
@ -289,7 +292,7 @@ Dec : Float [ @Decimal128 ]
## loops and conditionals. If you need to do performance-critical trigonometry ## loops and conditionals. If you need to do performance-critical trigonometry
## or square roots, either [F64] or [F32] is probably a better choice than the ## or square roots, either [F64] or [F32] is probably a better choice than the
## usual default choice of [Dec], despite the precision problems they bring. ## usual default choice of [Dec], despite the precision problems they bring.
Float a : Num [ @Fraction a ] Float range : Num (FloatingPoint range)
## A fixed-size integer - that is, a number with no fractional component. ## A fixed-size integer - that is, a number with no fractional component.
## ##
@ -340,19 +343,19 @@ Float a : Num [ @Fraction a ]
## * Start by deciding if this integer should allow negative numbers, and choose signed or unsigned accordingly. ## * Start by deciding if this integer should allow negative numbers, and choose signed or unsigned accordingly.
## * Next, think about the range of numbers you expect this number to hold. Choose the smallest size you will never expect to overflow, no matter the inputs your program receives. (Validating inputs for size, and presenting the user with an error if they are too big, can help guard against overflow.) ## * Next, think about the range of numbers you expect this number to hold. Choose the smallest size you will never expect to overflow, no matter the inputs your program receives. (Validating inputs for size, and presenting the user with an error if they are too big, can help guard against overflow.)
## * Finally, if a particular numeric calculation is running too slowly, you can try experimenting with other number sizes. This rarely makes a meaningful difference, but some processors can operate on different number sizes at different speeds. ## * Finally, if a particular numeric calculation is running too slowly, you can try experimenting with other number sizes. This rarely makes a meaningful difference, but some processors can operate on different number sizes at different speeds.
Int size : Num [ @Integer size ] Int range : Num (Integer range)
## A signed 8-bit integer, ranging from -128 to 127 ## A signed 8-bit integer, ranging from -128 to 127
I8 : Int [ @Signed8 ] I8 : Int Signed8
U8 : Int [ @Unsigned8 ] U8 : Int Unsigned8
I16 : Int [ @Signed16 ] I16 : Int Signed16
U16 : Int [ @Unsigned16 ] U16 : Int Unsigned16
I32 : Int [ @Signed32 ] I32 : Int Signed32
U32 : Int [ @Unsigned32 ] U32 : Int Unsigned32
I64 : Int [ @Signed64 ] I64 : Int Signed64
U64 : Int [ @Unsigned64 ] U64 : Int Unsigned64
I128 : Int [ @Signed128 ] I128 : Int Signed128
U128 : Int [ @Unsigned128 ] U128 : Int Unsigned128
## A [natural number](https://en.wikipedia.org/wiki/Natural_number) represented ## A [natural number](https://en.wikipedia.org/wiki/Natural_number) represented
## as a 64-bit unsigned integer on 64-bit systems, a 32-bit unsigned integer ## as a 64-bit unsigned integer on 64-bit systems, a 32-bit unsigned integer
@ -364,7 +367,7 @@ U128 : Int [ @Unsigned128 ]
## a [List] can hold on a 64-bit system fits in a 64-bit unsigned integer, and ## a [List] can hold on a 64-bit system fits in a 64-bit unsigned integer, and
## on a 32-bit system it fits in 32-bit unsigned integer. This makes [Nat] a ## on a 32-bit system it fits in 32-bit unsigned integer. This makes [Nat] a
## good fit for [List.len] regardless of system. ## good fit for [List.len] regardless of system.
Nat : Int [ @Natural ] Nat : Num (Integer Natural)
## A 64-bit signed integer. All number literals without decimal points are compatible with #Int values. ## A 64-bit signed integer. All number literals without decimal points are compatible with #Int values.
## ##
@ -440,7 +443,7 @@ Nat : Int [ @Natural ]
## ##
## As such, it's very important to design your code not to exceed these bounds! ## As such, it's very important to design your code not to exceed these bounds!
## If you need to do math outside these bounds, consider using a larger numeric size. ## If you need to do math outside these bounds, consider using a larger numeric size.
Int size : Num [ @Int size ] Int range : Num (Integer range)
## Convert ## Convert
@ -802,27 +805,18 @@ toDec : Num * -> Dec
## This is the same as the #// operator. ## This is the same as the #// operator.
divTrunc : Int a, Int a -> Int a divTrunc : Int a, Int a -> Int a
## Perform flooring modulo on two integers. ## Obtain the remainder (truncating modulo) from the division of two integers.
## ##
## Modulo is the same as remainder when working with positive numbers, ## `a % b` is shorthand for `Num.rem a b`.
## but if either number is negative, then modulo works differently.
## ##
## Additionally, flooring modulo uses [Float].floor on the result. ## >>> 5 % 7
## ##
## (Use [Float].mod for non-flooring modulo.) ## >>> Num.rem 5 7
## ##
## Return `Err DivByZero` if the second integer is zero, because division by zero is undefined in mathematics. ## >>> -8 % -3
## ##
## `a %% b` is shorthand for `Int.modFloor a b`. ## >>> Num.rem -8 -3
## rem : Int a, Int a -> Int a
## >>> 5 %% 7
##
## >>> Int.modFloor 5 7
##
## >>> -8 %% -3
##
## >>> Int.modFloor -8 -3
#modFloor : Int a, Int a -> Result (Int a) [ DivByZero ]*
## Bitwise ## Bitwise
@ -1094,31 +1088,6 @@ atan : Float a -> Float a
## >>> |> Num.div 2.0 ## >>> |> Num.div 2.0
div : Float a, Float a -> Float a div : Float a, Float a -> Float a
## Perform modulo on two [Float]s.
##
## Modulo is the same as remainder when working with positive numbers,
## but if either number is negative, then modulo works differently.
##
## `a % b` is shorthand for `Num.mod a b`.
##
## [Division by zero is undefined in mathematics](https://en.wikipedia.org/wiki/Division_by_zero),
## and as such, so is modulo by zero. Because of this, you should make sure never
## to pass zero for the second argument to this function!
##
## Passing [mod] a [Dec] value of zero for its second argument will cause a panic.
## Passing [mod] a [F32] and [F64] value for its second argument will cause it
## to return [*NaN*](Num.isNaN).
##
## >>> 5.0 % 7.0
##
## >>> Num.mod 5 7
##
## `Num.mod` can be convenient in pipelines.
##
## >>> Num.pi
## >>> |> Num.mod 2.0
mod : Float a, Float a -> Float a
## Raises a [Float] to the power of another [Float]. ## Raises a [Float] to the power of another [Float].
## ##
## ` ## `
@ -1314,7 +1283,7 @@ isInfinite : Float * -> Bool
## ##
## >>> Num.isNaN 12.3 ## >>> Num.isNaN 12.3
## ##
## >>> Num.isNaN (Num.sqrt -2) ## >>> Num.isNaN (Num.pow -1 0.5)
## ##
## *NaN* is unusual from other numberic values in that: ## *NaN* is unusual from other numberic values in that:
## * *NaN* is not equal to any other number, even itself. [Bool.isEq] always returns `False` if either argument is *NaN*. ## * *NaN* is not equal to any other number, even itself. [Bool.isEq] always returns `False` if either argument is *NaN*.

View file

@ -13,7 +13,7 @@ interface Result
## The result of an operation that could fail: either the operation went ## The result of an operation that could fail: either the operation went
## okay, or else there was an error of some sort. ## okay, or else there was an error of some sort.
Result ok err : [ @Result ok err ] Result ok err : [ Ok ok, Err err ]
## Return True if the result indicates a success, else return False ## Return True if the result indicates a success, else return False
## ##

View file

@ -18,7 +18,7 @@ interface Set
imports [] imports []
## A Set is an unordered collection of unique elements. ## A Set is an unordered collection of unique elements.
Set elem : [ @Set elem ] Set elem := [ Set elem ]
## An empty set. ## An empty set.
empty : Set * empty : Set *

View file

@ -116,7 +116,7 @@ interface Str
## It has many more tools than this module does! ## It has many more tools than this module does!
## A [Unicode](https://unicode.org) text value. ## A [Unicode](https://unicode.org) text value.
Str : [ @Str ] Str := [ Str ]
## Convert ## Convert

View file

@ -66,10 +66,13 @@ interface Num
isPositive, isPositive,
isNegative, isNegative,
rem, rem,
remChecked,
div, div,
divChecked, divChecked,
sqrt, sqrt,
sqrtChecked,
log, log,
logChecked,
round, round,
ceiling, ceiling,
floor, floor,
@ -155,25 +158,25 @@ interface Num
Bool.{ Bool } Bool.{ Bool }
] ]
Num range : [ @Num range ] Num range := range
Int range : Num (Integer range) Int range : Num (Integer range)
Float range : Num (FloatingPoint range) Float range : Num (FloatingPoint range)
Signed128 : [ @Signed128 ] Signed128 := []
Signed64 : [ @Signed64 ] Signed64 := []
Signed32 : [ @Signed32 ] Signed32 := []
Signed16 : [ @Signed16 ] Signed16 := []
Signed8 : [ @Signed8 ] Signed8 := []
Unsigned128 : [ @Unsigned128 ] Unsigned128 := []
Unsigned64 : [ @Unsigned64 ] Unsigned64 := []
Unsigned32 : [ @Unsigned32 ] Unsigned32 := []
Unsigned16 : [ @Unsigned16 ] Unsigned16 := []
Unsigned8 : [ @Unsigned8 ] Unsigned8 := []
Natural : [ @Natural ] Natural := []
Integer range : [ @Integer range ] Integer range := range
I128 : Num (Integer Signed128) I128 : Num (Integer Signed128)
I64 : Num (Integer Signed64) I64 : Num (Integer Signed64)
@ -189,11 +192,11 @@ U8 : Num (Integer Unsigned8)
Nat : Num (Integer Natural) Nat : Num (Integer Natural)
Decimal : [ @Decimal ] Decimal := []
Binary64 : [ @Binary64 ] Binary64 := []
Binary32 : [ @Binary32 ] Binary32 := []
FloatingPoint range : [ @FloatingPoint range ] FloatingPoint range := range
F64 : Num (FloatingPoint Binary64) F64 : Num (FloatingPoint Binary64)
F32 : Num (FloatingPoint Binary32) F32 : Num (FloatingPoint Binary32)
@ -239,19 +242,22 @@ asin : Float a -> Float a
acos : Float a -> Float a acos : Float a -> Float a
atan : Float a -> Float a atan : Float a -> Float a
sqrt : Float a -> Result (Float a) [ SqrtOfNegative ]* sqrt : Float a -> Float a
log : Float a -> Result (Float a) [ LogNeedsPositive ]* sqrtChecked : Float a -> Result (Float a) [ SqrtOfNegative ]*
log : Float a -> Float a
logChecked : Float a -> Result (Float a) [ LogNeedsPositive ]*
div : Float a, Float a -> Float a div : Float a, Float a -> Float a
divChecked : Float a, Float a -> Result (Float a) [ DivByZero ]* divChecked : Float a, Float a -> Result (Float a) [ DivByZero ]*
divCeil : Int a, Int a -> Int a divCeil : Int a, Int a -> Int a
divCeilChecked : Int a, Int a -> Result (Int a) [ DivByZero ]* divCeilChecked : Int a, Int a -> Result (Int a) [ DivByZero ]*
divTrunc : Int a, Int a -> Int a divTrunc : Int a, Int a -> Int a
divTruncChecked : Int a, Int a -> Result (Int a) [ DivByZero ]* divTruncChecked : Int a, Int a -> Result (Int a) [ DivByZero ]*
# mod : Float a, Float a -> Result (Float a) [ DivByZero ]*
rem : Int a, Int a -> Result (Int a) [ DivByZero ]* rem : Int a, Int a -> Int a
# mod : Int a, Int a -> Result (Int a) [ DivByZero ]* remChecked : Int a, Int a -> Result (Int a) [ DivByZero ]*
isMultipleOf : Int a, Int a -> Bool isMultipleOf : Int a, Int a -> Bool
bitwiseAnd : Int a, Int a -> Int a bitwiseAnd : Int a, Int a -> Int a

View file

@ -68,13 +68,9 @@ impl FloatWidth {
pub const fn try_from_symbol(symbol: Symbol) -> Option<Self> { pub const fn try_from_symbol(symbol: Symbol) -> Option<Self> {
match symbol { match symbol {
Symbol::NUM_F64 | Symbol::NUM_BINARY64 | Symbol::NUM_AT_BINARY64 => { Symbol::NUM_F64 | Symbol::NUM_BINARY64 => Some(FloatWidth::F64),
Some(FloatWidth::F64)
}
Symbol::NUM_F32 | Symbol::NUM_BINARY32 | Symbol::NUM_AT_BINARY32 => { Symbol::NUM_F32 | Symbol::NUM_BINARY32 => Some(FloatWidth::F32),
Some(FloatWidth::F32)
}
_ => None, _ => None,
} }
@ -136,26 +132,16 @@ impl IntWidth {
pub const fn try_from_symbol(symbol: Symbol) -> Option<Self> { pub const fn try_from_symbol(symbol: Symbol) -> Option<Self> {
match symbol { match symbol {
Symbol::NUM_I128 | Symbol::NUM_SIGNED128 | Symbol::NUM_AT_SIGNED128 => { Symbol::NUM_I128 | Symbol::NUM_SIGNED128 => Some(IntWidth::I128),
Some(IntWidth::I128) Symbol::NUM_I64 | Symbol::NUM_SIGNED64 => Some(IntWidth::I64),
} Symbol::NUM_I32 | Symbol::NUM_SIGNED32 => Some(IntWidth::I32),
Symbol::NUM_I64 | Symbol::NUM_SIGNED64 | Symbol::NUM_AT_SIGNED64 => Some(IntWidth::I64), Symbol::NUM_I16 | Symbol::NUM_SIGNED16 => Some(IntWidth::I16),
Symbol::NUM_I32 | Symbol::NUM_SIGNED32 | Symbol::NUM_AT_SIGNED32 => Some(IntWidth::I32), Symbol::NUM_I8 | Symbol::NUM_SIGNED8 => Some(IntWidth::I8),
Symbol::NUM_I16 | Symbol::NUM_SIGNED16 | Symbol::NUM_AT_SIGNED16 => Some(IntWidth::I16), Symbol::NUM_U128 | Symbol::NUM_UNSIGNED128 => Some(IntWidth::U128),
Symbol::NUM_I8 | Symbol::NUM_SIGNED8 | Symbol::NUM_AT_SIGNED8 => Some(IntWidth::I8), Symbol::NUM_U64 | Symbol::NUM_UNSIGNED64 => Some(IntWidth::U64),
Symbol::NUM_U128 | Symbol::NUM_UNSIGNED128 | Symbol::NUM_AT_UNSIGNED128 => { Symbol::NUM_U32 | Symbol::NUM_UNSIGNED32 => Some(IntWidth::U32),
Some(IntWidth::U128) Symbol::NUM_U16 | Symbol::NUM_UNSIGNED16 => Some(IntWidth::U16),
} Symbol::NUM_U8 | Symbol::NUM_UNSIGNED8 => Some(IntWidth::U8),
Symbol::NUM_U64 | Symbol::NUM_UNSIGNED64 | Symbol::NUM_AT_UNSIGNED64 => {
Some(IntWidth::U64)
}
Symbol::NUM_U32 | Symbol::NUM_UNSIGNED32 | Symbol::NUM_AT_UNSIGNED32 => {
Some(IntWidth::U32)
}
Symbol::NUM_U16 | Symbol::NUM_UNSIGNED16 | Symbol::NUM_AT_UNSIGNED16 => {
Some(IntWidth::U16)
}
Symbol::NUM_U8 | Symbol::NUM_UNSIGNED8 | Symbol::NUM_AT_UNSIGNED8 => Some(IntWidth::U8),
_ => None, _ => None,
} }
} }

View file

@ -393,16 +393,16 @@ pub fn types() -> MutMap<Symbol, (SolvedType, Region)> {
Box::new(int_type(flex(TVAR2))) Box::new(int_type(flex(TVAR2)))
); );
// rem : Int a, Int a -> Result (Int a) [ DivByZero ]* // rem : Int a, Int a -> Int a
add_top_level_function_type!( add_top_level_function_type!(
Symbol::NUM_REM, Symbol::NUM_REM,
vec![int_type(flex(TVAR1)), int_type(flex(TVAR1))], vec![int_type(flex(TVAR1)), int_type(flex(TVAR1))],
Box::new(result_type(int_type(flex(TVAR1)), div_by_zero.clone())), Box::new(int_type(flex(TVAR1))),
); );
// mod : Int a, Int a -> Result (Int a) [ DivByZero ]* // remChecked : Int a, Int a -> Result (Int a) [ DivByZero ]*
add_top_level_function_type!( add_top_level_function_type!(
Symbol::NUM_MOD_INT, Symbol::NUM_REM_CHECKED,
vec![int_type(flex(TVAR1)), int_type(flex(TVAR1))], vec![int_type(flex(TVAR1)), int_type(flex(TVAR1))],
Box::new(result_type(int_type(flex(TVAR1)), div_by_zero.clone())), Box::new(result_type(int_type(flex(TVAR1)), div_by_zero.clone())),
); );
@ -680,36 +680,43 @@ pub fn types() -> MutMap<Symbol, (SolvedType, Region)> {
add_top_level_function_type!( add_top_level_function_type!(
Symbol::NUM_DIV_FLOAT_CHECKED, Symbol::NUM_DIV_FLOAT_CHECKED,
vec![float_type(flex(TVAR1)), float_type(flex(TVAR1))], vec![float_type(flex(TVAR1)), float_type(flex(TVAR1))],
Box::new(result_type(float_type(flex(TVAR1)), div_by_zero.clone())),
);
// mod : Float a, Float a -> Result (Float a) [ DivByZero ]*
add_top_level_function_type!(
Symbol::NUM_MOD_FLOAT,
vec![float_type(flex(TVAR1)), float_type(flex(TVAR1))],
Box::new(result_type(float_type(flex(TVAR1)), div_by_zero)), Box::new(result_type(float_type(flex(TVAR1)), div_by_zero)),
); );
// sqrt : Float a -> Float a // sqrt : Float a -> Float a
add_top_level_function_type!(
Symbol::NUM_SQRT,
vec![float_type(flex(TVAR1))],
Box::new(float_type(flex(TVAR1))),
);
// sqrtChecked : Float a -> Result (Float a) [ SqrtOfNegative ]*
let sqrt_of_negative = SolvedType::TagUnion( let sqrt_of_negative = SolvedType::TagUnion(
vec![(TagName::Global("SqrtOfNegative".into()), vec![])], vec![(TagName::Global("SqrtOfNegative".into()), vec![])],
Box::new(SolvedType::Wildcard), Box::new(SolvedType::Wildcard),
); );
add_top_level_function_type!( add_top_level_function_type!(
Symbol::NUM_SQRT, Symbol::NUM_SQRT_CHECKED,
vec![float_type(flex(TVAR1))], vec![float_type(flex(TVAR1))],
Box::new(result_type(float_type(flex(TVAR1)), sqrt_of_negative)), Box::new(result_type(float_type(flex(TVAR1)), sqrt_of_negative)),
); );
// log : Float a -> Float a // log : Float a -> Float a
add_top_level_function_type!(
Symbol::NUM_LOG,
vec![float_type(flex(TVAR1))],
Box::new(float_type(flex(TVAR1))),
);
// logChecked : Float a -> Result (Float a) [ LogNeedsPositive ]*
let log_needs_positive = SolvedType::TagUnion( let log_needs_positive = SolvedType::TagUnion(
vec![(TagName::Global("LogNeedsPositive".into()), vec![])], vec![(TagName::Global("LogNeedsPositive".into()), vec![])],
Box::new(SolvedType::Wildcard), Box::new(SolvedType::Wildcard),
); );
add_top_level_function_type!( add_top_level_function_type!(
Symbol::NUM_LOG, Symbol::NUM_LOG_CHECKED,
vec![float_type(flex(TVAR1))], vec![float_type(flex(TVAR1))],
Box::new(result_type(float_type(flex(TVAR1)), log_needs_positive)), Box::new(result_type(float_type(flex(TVAR1)), log_needs_positive)),
); );

View file

@ -365,7 +365,7 @@ pub fn find_type_def_symbols(
while let Some(tag) = inner_stack.pop() { while let Some(tag) = inner_stack.pop() {
match tag { match tag {
Tag::Global { args, .. } | Tag::Private { args, .. } => { Tag::Global { args, .. } => {
for t in args.iter() { for t in args.iter() {
stack.push(&t.value); stack.push(&t.value);
} }
@ -1253,31 +1253,6 @@ fn can_tags<'a>(
break 'inner tag_name; break 'inner tag_name;
} }
Tag::Private { name, args } => {
let ident_id = env.ident_ids.get_or_insert(&name.value.into());
let symbol = Symbol::new(env.home, ident_id);
let mut arg_types = Vec::with_capacity(args.len());
for arg in args.iter() {
let ann = can_annotation_help(
env,
&arg.value,
arg.region,
scope,
var_store,
introduced_variables,
local_aliases,
references,
);
arg_types.push(ann);
}
let tag_name = TagName::Private(symbol);
tag_types.push((tag_name.clone(), arg_types));
break 'inner tag_name;
}
Tag::SpaceBefore(nested, _) | Tag::SpaceAfter(nested, _) => { Tag::SpaceBefore(nested, _) | Tag::SpaceAfter(nested, _) => {
// check the nested tag instead // check the nested tag instead
tag = nested; tag = nested;

View file

@ -204,9 +204,12 @@ pub fn builtin_defs_map(symbol: Symbol, var_store: &mut VarStore) -> Option<Def>
NUM_ABS => num_abs, NUM_ABS => num_abs,
NUM_NEG => num_neg, NUM_NEG => num_neg,
NUM_REM => num_rem, NUM_REM => num_rem,
NUM_REM_CHECKED => num_rem_checked,
NUM_IS_MULTIPLE_OF => num_is_multiple_of, NUM_IS_MULTIPLE_OF => num_is_multiple_of,
NUM_SQRT => num_sqrt, NUM_SQRT => num_sqrt,
NUM_SQRT_CHECKED => num_sqrt_checked,
NUM_LOG => num_log, NUM_LOG => num_log,
NUM_LOG_CHECKED => num_log_checked,
NUM_ROUND => num_round, NUM_ROUND => num_round,
NUM_IS_ODD => num_is_odd, NUM_IS_ODD => num_is_odd,
NUM_IS_EVEN => num_is_even, NUM_IS_EVEN => num_is_even,
@ -713,6 +716,23 @@ fn bool_and(symbol: Symbol, var_store: &mut VarStore) -> Def {
) )
} }
fn num_unaryop(symbol: Symbol, var_store: &mut VarStore, op: LowLevel) -> Def {
let num_var = var_store.fresh();
let body = RunLowLevel {
op,
args: vec![(num_var, Var(Symbol::ARG_1))],
ret_var: num_var,
};
defn(
symbol,
vec![(num_var, Symbol::ARG_1)],
var_store,
body,
num_var,
)
}
/// Num a, Num a -> Num a /// Num a, Num a -> Num a
fn num_binop(symbol: Symbol, var_store: &mut VarStore, op: LowLevel) -> Def { fn num_binop(symbol: Symbol, var_store: &mut VarStore, op: LowLevel) -> Def {
let num_var = var_store.fresh(); let num_var = var_store.fresh();
@ -1152,8 +1172,13 @@ fn num_to_float(symbol: Symbol, var_store: &mut VarStore) -> Def {
) )
} }
/// Num.sqrt : Float -> Result Float [ SqrtOfNegative ]* /// Num.sqrt : Float a -> Float a
fn num_sqrt(symbol: Symbol, var_store: &mut VarStore) -> Def { fn num_sqrt(symbol: Symbol, var_store: &mut VarStore) -> Def {
num_unaryop(symbol, var_store, LowLevel::NumSqrtUnchecked)
}
/// Num.sqrtChecked : Float a -> Result (Float a) [ SqrtOfNegative ]*
fn num_sqrt_checked(symbol: Symbol, var_store: &mut VarStore) -> Def {
let bool_var = var_store.fresh(); let bool_var = var_store.fresh();
let float_var = var_store.fresh(); let float_var = var_store.fresh();
let unbound_zero_var = var_store.fresh(); let unbound_zero_var = var_store.fresh();
@ -1201,8 +1226,13 @@ fn num_sqrt(symbol: Symbol, var_store: &mut VarStore) -> Def {
) )
} }
/// Num.log : Float -> Result Float [ LogNeedsPositive ]* /// Num.log : Float a -> Float a
fn num_log(symbol: Symbol, var_store: &mut VarStore) -> Def { fn num_log(symbol: Symbol, var_store: &mut VarStore) -> Def {
num_unaryop(symbol, var_store, LowLevel::NumLogUnchecked)
}
/// Num.logChecked : Float a -> Result (Float a) [ LogNeedsPositive ]*
fn num_log_checked(symbol: Symbol, var_store: &mut VarStore) -> Def {
let bool_var = var_store.fresh(); let bool_var = var_store.fresh();
let float_var = var_store.fresh(); let float_var = var_store.fresh();
let unbound_zero_var = var_store.fresh(); let unbound_zero_var = var_store.fresh();
@ -4084,8 +4114,13 @@ fn set_walk(symbol: Symbol, var_store: &mut VarStore) -> Def {
) )
} }
/// Num.rem : Int a, Int a -> Result (Int a) [ DivByZero ]* /// Num.rem : Int a, Int a -> Int a
fn num_rem(symbol: Symbol, var_store: &mut VarStore) -> Def { fn num_rem(symbol: Symbol, var_store: &mut VarStore) -> Def {
num_binop(symbol, var_store, LowLevel::NumRemUnchecked)
}
/// Num.remChecked : Int a, Int a -> Result (Int a) [ DivByZero ]*
fn num_rem_checked(symbol: Symbol, var_store: &mut VarStore) -> Def {
let num_var = var_store.fresh(); let num_var = var_store.fresh();
let unbound_zero_var = var_store.fresh(); let unbound_zero_var = var_store.fresh();
let bool_var = var_store.fresh(); let bool_var = var_store.fresh();

File diff suppressed because it is too large Load diff

View file

@ -6,11 +6,11 @@ use crate::pattern::Pattern;
use crate::scope::Scope; use crate::scope::Scope;
use roc_collections::{SendMap, VecSet}; use roc_collections::{SendMap, VecSet};
use roc_module::called_via::CalledVia; use roc_module::called_via::CalledVia;
use roc_module::ident::TagName; use roc_module::ident::{Lowercase, TagName};
use roc_module::symbol::Symbol; use roc_module::symbol::Symbol;
use roc_region::all::{Loc, Region}; use roc_region::all::{Loc, Region};
use roc_types::subs::{VarStore, Variable}; use roc_types::subs::{VarStore, Variable};
use roc_types::types::{AliasKind, Type, TypeExtension}; use roc_types::types::{AliasKind, LambdaSet, Type, TypeExtension};
#[derive(Debug, Default, Clone, Copy)] #[derive(Debug, Default, Clone, Copy)]
pub(crate) struct HostedGeneratedFunctions { pub(crate) struct HostedGeneratedFunctions {
@ -30,7 +30,7 @@ pub(crate) struct HostedGeneratedFunctions {
/// ///
/// The effect alias is implemented as /// The effect alias is implemented as
/// ///
/// Effect a : [ @Effect ({} -> a) ] /// Effect a := {} -> a
/// ///
/// For this alias we implement the functions specified in HostedGeneratedFunctions with the /// For this alias we implement the functions specified in HostedGeneratedFunctions with the
/// standard implementation. /// standard implementation.
@ -45,13 +45,7 @@ pub(crate) fn build_effect_builtins(
) { ) {
macro_rules! helper { macro_rules! helper {
($f:expr) => {{ ($f:expr) => {{
let (symbol, def) = $f( let (symbol, def) = $f(env, scope, effect_symbol, var_store);
env,
scope,
effect_symbol,
TagName::Private(effect_symbol),
var_store,
);
// make the outside world know this symbol exists // make the outside world know this symbol exists
exposed_symbols.insert(symbol); exposed_symbols.insert(symbol);
@ -114,7 +108,6 @@ fn build_effect_always(
env: &mut Env, env: &mut Env,
scope: &mut Scope, scope: &mut Scope,
effect_symbol: Symbol, effect_symbol: Symbol,
effect_tag_name: TagName,
var_store: &mut VarStore, var_store: &mut VarStore,
) -> (Symbol, Def) { ) -> (Symbol, Def) {
// Effect.always = \value -> @Effect \{} -> value // Effect.always = \value -> @Effect \{} -> value
@ -177,11 +170,15 @@ fn build_effect_always(
// \value -> @Effect \{} -> value // \value -> @Effect \{} -> value
let (function_var, always_closure) = { let (function_var, always_closure) = {
// `@Effect \{} -> value` // `@Effect \{} -> value`
let body = Expr::Tag { let (specialized_def_type, type_arguments, lambda_set_variables) =
variant_var: var_store.fresh(), build_fresh_opaque_variables(var_store);
ext_var: var_store.fresh(), let body = Expr::OpaqueRef {
name: effect_tag_name.clone(), opaque_var: var_store.fresh(),
arguments: vec![(var_store.fresh(), Loc::at_zero(const_closure))], name: effect_symbol,
argument: Box::new((var_store.fresh(), Loc::at_zero(const_closure))),
specialized_def_type,
type_arguments,
lambda_set_variables,
}; };
let arguments = vec![( let arguments = vec![(
@ -212,9 +209,8 @@ fn build_effect_always(
let var_a = var_store.fresh(); let var_a = var_store.fresh();
introduced_variables.insert_named("a".into(), Loc::at_zero(var_a)); introduced_variables.insert_named("a".into(), Loc::at_zero(var_a));
let effect_a = build_effect_alias( let effect_a = build_effect_opaque(
effect_symbol, effect_symbol,
effect_tag_name,
"a", "a",
var_a, var_a,
Type::Variable(var_a), Type::Variable(var_a),
@ -257,7 +253,6 @@ fn build_effect_map(
env: &mut Env, env: &mut Env,
scope: &mut Scope, scope: &mut Scope,
effect_symbol: Symbol, effect_symbol: Symbol,
effect_tag_name: TagName,
var_store: &mut VarStore, var_store: &mut VarStore,
) -> (Symbol, Def) { ) -> (Symbol, Def) {
// Effect.map = \@Effect thunk, mapper -> @Effect \{} -> mapper (thunk {}) // Effect.map = \@Effect thunk, mapper -> @Effect \{} -> mapper (thunk {})
@ -355,17 +350,22 @@ fn build_effect_map(
}) })
}; };
// \@Effect thunk, mapper
let (specialized_def_type, type_arguments, lambda_set_variables) =
build_fresh_opaque_variables(var_store);
let arguments = vec![ let arguments = vec![
( (
var_store.fresh(), var_store.fresh(),
Loc::at_zero(Pattern::AppliedTag { Loc::at_zero(Pattern::UnwrappedOpaque {
opaque: effect_symbol,
whole_var: var_store.fresh(), whole_var: var_store.fresh(),
ext_var: var_store.fresh(), argument: Box::new((
tag_name: effect_tag_name.clone(),
arguments: vec![(
var_store.fresh(), var_store.fresh(),
Loc::at_zero(Pattern::Identifier(thunk_symbol)), Loc::at_zero(Pattern::Identifier(thunk_symbol)),
)], )),
specialized_def_type,
type_arguments,
lambda_set_variables,
}), }),
), ),
( (
@ -375,11 +375,15 @@ fn build_effect_map(
]; ];
// `@Effect \{} -> (mapper (thunk {}))` // `@Effect \{} -> (mapper (thunk {}))`
let body = Expr::Tag { let (specialized_def_type, type_arguments, lambda_set_variables) =
variant_var: var_store.fresh(), build_fresh_opaque_variables(var_store);
ext_var: var_store.fresh(), let body = Expr::OpaqueRef {
name: effect_tag_name.clone(), opaque_var: var_store.fresh(),
arguments: vec![(var_store.fresh(), Loc::at_zero(inner_closure))], name: effect_symbol,
argument: Box::new((var_store.fresh(), Loc::at_zero(inner_closure))),
specialized_def_type,
type_arguments,
lambda_set_variables,
}; };
let function_var = var_store.fresh(); let function_var = var_store.fresh();
@ -405,9 +409,8 @@ fn build_effect_map(
introduced_variables.insert_named("a".into(), Loc::at_zero(var_a)); introduced_variables.insert_named("a".into(), Loc::at_zero(var_a));
introduced_variables.insert_named("b".into(), Loc::at_zero(var_b)); introduced_variables.insert_named("b".into(), Loc::at_zero(var_b));
let effect_a = build_effect_alias( let effect_a = build_effect_opaque(
effect_symbol, effect_symbol,
effect_tag_name.clone(),
"a", "a",
var_a, var_a,
Type::Variable(var_a), Type::Variable(var_a),
@ -415,9 +418,8 @@ fn build_effect_map(
&mut introduced_variables, &mut introduced_variables,
); );
let effect_b = build_effect_alias( let effect_b = build_effect_opaque(
effect_symbol, effect_symbol,
effect_tag_name,
"b", "b",
var_b, var_b,
Type::Variable(var_b), Type::Variable(var_b),
@ -469,7 +471,6 @@ fn build_effect_after(
env: &mut Env, env: &mut Env,
scope: &mut Scope, scope: &mut Scope,
effect_symbol: Symbol, effect_symbol: Symbol,
effect_tag_name: TagName,
var_store: &mut VarStore, var_store: &mut VarStore,
) -> (Symbol, Def) { ) -> (Symbol, Def) {
// Effect.after = \@Effect effect, toEffect -> toEffect (effect {}) // Effect.after = \@Effect effect, toEffect -> toEffect (effect {})
@ -533,17 +534,22 @@ fn build_effect_after(
Expr::Call(Box::new(boxed), arguments, CalledVia::Space) Expr::Call(Box::new(boxed), arguments, CalledVia::Space)
}; };
let (specialized_def_type, type_arguments, lambda_set_variables) =
build_fresh_opaque_variables(var_store);
let arguments = vec![ let arguments = vec![
( (
var_store.fresh(), var_store.fresh(),
Loc::at_zero(Pattern::AppliedTag { Loc::at_zero(Pattern::UnwrappedOpaque {
opaque: effect_symbol,
whole_var: var_store.fresh(), whole_var: var_store.fresh(),
ext_var: var_store.fresh(), argument: Box::new((
tag_name: effect_tag_name.clone(),
arguments: vec![(
var_store.fresh(), var_store.fresh(),
Loc::at_zero(Pattern::Identifier(thunk_symbol)), Loc::at_zero(Pattern::Identifier(thunk_symbol)),
)], )),
specialized_def_type,
type_arguments,
lambda_set_variables,
}), }),
), ),
( (
@ -574,9 +580,8 @@ fn build_effect_after(
introduced_variables.insert_named("a".into(), Loc::at_zero(var_a)); introduced_variables.insert_named("a".into(), Loc::at_zero(var_a));
introduced_variables.insert_named("b".into(), Loc::at_zero(var_b)); introduced_variables.insert_named("b".into(), Loc::at_zero(var_b));
let effect_a = build_effect_alias( let effect_a = build_effect_opaque(
effect_symbol, effect_symbol,
effect_tag_name.clone(),
"a", "a",
var_a, var_a,
Type::Variable(var_a), Type::Variable(var_a),
@ -584,9 +589,8 @@ fn build_effect_after(
&mut introduced_variables, &mut introduced_variables,
); );
let effect_b = build_effect_alias( let effect_b = build_effect_opaque(
effect_symbol, effect_symbol,
effect_tag_name,
"b", "b",
var_b, var_b,
Type::Variable(var_b), Type::Variable(var_b),
@ -635,7 +639,7 @@ fn build_effect_after(
/// turn `value` into `@Effect \{} -> value` /// turn `value` into `@Effect \{} -> value`
fn wrap_in_effect_thunk( fn wrap_in_effect_thunk(
body: Expr, body: Expr,
effect_tag_name: TagName, effect_symbol: Symbol,
closure_name: Symbol, closure_name: Symbol,
captured_symbols: Vec<Symbol>, captured_symbols: Vec<Symbol>,
var_store: &mut VarStore, var_store: &mut VarStore,
@ -667,31 +671,38 @@ fn wrap_in_effect_thunk(
}; };
// `@Effect \{} -> value` // `@Effect \{} -> value`
Expr::Tag { let (specialized_def_type, type_arguments, lambda_set_variables) =
variant_var: var_store.fresh(), build_fresh_opaque_variables(var_store);
ext_var: var_store.fresh(), Expr::OpaqueRef {
name: effect_tag_name, opaque_var: var_store.fresh(),
arguments: vec![(var_store.fresh(), Loc::at_zero(const_closure))], name: effect_symbol,
argument: Box::new((var_store.fresh(), Loc::at_zero(const_closure))),
specialized_def_type,
type_arguments,
lambda_set_variables,
} }
} }
/// given `effect : Effect a`, unwrap the thunk and force it, giving a value of type `a` /// given `effect : Effect a`, unwrap the thunk and force it, giving a value of type `a`
fn force_effect( fn force_effect(
effect: Expr, effect: Expr,
effect_tag_name: TagName, effect_symbol: Symbol,
thunk_symbol: Symbol, thunk_symbol: Symbol,
var_store: &mut VarStore, var_store: &mut VarStore,
) -> Expr { ) -> Expr {
let whole_var = var_store.fresh(); let whole_var = var_store.fresh();
let ext_var = var_store.fresh();
let thunk_var = var_store.fresh(); let thunk_var = var_store.fresh();
let pattern = Pattern::AppliedTag { let (specialized_def_type, type_arguments, lambda_set_variables) =
ext_var, build_fresh_opaque_variables(var_store);
let pattern = Pattern::UnwrappedOpaque {
whole_var, whole_var,
tag_name: effect_tag_name, opaque: effect_symbol,
arguments: vec![(thunk_var, Loc::at_zero(Pattern::Identifier(thunk_symbol)))], argument: Box::new((thunk_var, Loc::at_zero(Pattern::Identifier(thunk_symbol)))),
specialized_def_type,
type_arguments,
lambda_set_variables,
}; };
let pattern_vars = SendMap::default(); let pattern_vars = SendMap::default();
@ -728,7 +739,6 @@ fn build_effect_forever(
env: &mut Env, env: &mut Env,
scope: &mut Scope, scope: &mut Scope,
effect_symbol: Symbol, effect_symbol: Symbol,
effect_tag_name: TagName,
var_store: &mut VarStore, var_store: &mut VarStore,
) -> (Symbol, Def) { ) -> (Symbol, Def) {
// morally // morally
@ -801,14 +811,8 @@ fn build_effect_forever(
.unwrap() .unwrap()
}; };
let body = build_effect_forever_body( let body =
env, build_effect_forever_body(env, scope, effect_symbol, forever_symbol, effect, var_store);
scope,
effect_tag_name.clone(),
forever_symbol,
effect,
var_store,
);
let arguments = vec![(var_store.fresh(), Loc::at_zero(Pattern::Identifier(effect)))]; let arguments = vec![(var_store.fresh(), Loc::at_zero(Pattern::Identifier(effect)))];
@ -834,9 +838,8 @@ fn build_effect_forever(
introduced_variables.insert_named("a".into(), Loc::at_zero(var_a)); introduced_variables.insert_named("a".into(), Loc::at_zero(var_a));
introduced_variables.insert_named("b".into(), Loc::at_zero(var_b)); introduced_variables.insert_named("b".into(), Loc::at_zero(var_b));
let effect_a = build_effect_alias( let effect_a = build_effect_opaque(
effect_symbol, effect_symbol,
effect_tag_name.clone(),
"a", "a",
var_a, var_a,
Type::Variable(var_a), Type::Variable(var_a),
@ -844,9 +847,8 @@ fn build_effect_forever(
&mut introduced_variables, &mut introduced_variables,
); );
let effect_b = build_effect_alias( let effect_b = build_effect_opaque(
effect_symbol, effect_symbol,
effect_tag_name,
"b", "b",
var_b, var_b,
Type::Variable(var_b), Type::Variable(var_b),
@ -888,7 +890,7 @@ fn build_effect_forever(
fn build_effect_forever_body( fn build_effect_forever_body(
env: &mut Env, env: &mut Env,
scope: &mut Scope, scope: &mut Scope,
effect_tag_name: TagName, effect_symbol: Symbol,
forever_symbol: Symbol, forever_symbol: Symbol,
effect: Symbol, effect: Symbol,
var_store: &mut VarStore, var_store: &mut VarStore,
@ -907,7 +909,7 @@ fn build_effect_forever_body(
let inner_body = build_effect_forever_inner_body( let inner_body = build_effect_forever_inner_body(
env, env,
scope, scope,
effect_tag_name.clone(), effect_symbol,
forever_symbol, forever_symbol,
effect, effect,
var_store, var_store,
@ -916,7 +918,7 @@ fn build_effect_forever_body(
let captured_symbols = vec![effect]; let captured_symbols = vec![effect];
wrap_in_effect_thunk( wrap_in_effect_thunk(
inner_body, inner_body,
effect_tag_name, effect_symbol,
closure_name, closure_name,
captured_symbols, captured_symbols,
var_store, var_store,
@ -926,7 +928,7 @@ fn build_effect_forever_body(
fn build_effect_forever_inner_body( fn build_effect_forever_inner_body(
env: &mut Env, env: &mut Env,
scope: &mut Scope, scope: &mut Scope,
effect_tag_name: TagName, effect_symbol: Symbol,
forever_symbol: Symbol, forever_symbol: Symbol,
effect: Symbol, effect: Symbol,
var_store: &mut VarStore, var_store: &mut VarStore,
@ -953,18 +955,21 @@ fn build_effect_forever_inner_body(
.unwrap() .unwrap()
}; };
// Effect thunk1 = effect // @Effect thunk1 = effect
let thunk_from_effect = { let thunk_from_effect = {
let whole_var = var_store.fresh(); let whole_var = var_store.fresh();
let ext_var = var_store.fresh();
let thunk_var = var_store.fresh(); let thunk_var = var_store.fresh();
let pattern = Pattern::AppliedTag { let (specialized_def_type, type_arguments, lambda_set_variables) =
ext_var, build_fresh_opaque_variables(var_store);
let pattern = Pattern::UnwrappedOpaque {
whole_var, whole_var,
tag_name: effect_tag_name.clone(), opaque: effect_symbol,
arguments: vec![(thunk_var, Loc::at_zero(Pattern::Identifier(thunk1_symbol)))], argument: Box::new((thunk_var, Loc::at_zero(Pattern::Identifier(thunk1_symbol)))),
specialized_def_type,
type_arguments,
lambda_set_variables,
}; };
let pattern_vars = SendMap::default(); let pattern_vars = SendMap::default();
@ -1017,12 +1022,12 @@ fn build_effect_forever_inner_body(
}; };
// ``` // ```
// Effect thunk2 = forever effect // @Effect thunk2 = forever effect
// thunk2 {} // thunk2 {}
// ``` // ```
let force_thunk2 = Loc::at_zero(force_effect( let force_thunk2 = Loc::at_zero(force_effect(
forever_effect, forever_effect,
effect_tag_name, effect_symbol,
thunk2_symbol, thunk2_symbol,
var_store, var_store,
)); ));
@ -1042,7 +1047,6 @@ fn build_effect_loop(
env: &mut Env, env: &mut Env,
scope: &mut Scope, scope: &mut Scope,
effect_symbol: Symbol, effect_symbol: Symbol,
effect_tag_name: TagName,
var_store: &mut VarStore, var_store: &mut VarStore,
) -> (Symbol, Def) { ) -> (Symbol, Def) {
let loop_symbol = new_symbol!(scope, env, "loop"); let loop_symbol = new_symbol!(scope, env, "loop");
@ -1052,7 +1056,7 @@ fn build_effect_loop(
let body = build_effect_loop_body( let body = build_effect_loop_body(
env, env,
scope, scope,
effect_tag_name.clone(), effect_symbol,
loop_symbol, loop_symbol,
state_symbol, state_symbol,
step_symbol, step_symbol,
@ -1092,9 +1096,8 @@ fn build_effect_loop(
introduced_variables.insert_named("a".into(), Loc::at_zero(var_a)); introduced_variables.insert_named("a".into(), Loc::at_zero(var_a));
introduced_variables.insert_named("b".into(), Loc::at_zero(var_b)); introduced_variables.insert_named("b".into(), Loc::at_zero(var_b));
let effect_b = build_effect_alias( let effect_b = build_effect_opaque(
effect_symbol, effect_symbol,
effect_tag_name.clone(),
"b", "b",
var_b, var_b,
Type::Variable(var_b), Type::Variable(var_b),
@ -1119,19 +1122,11 @@ fn build_effect_loop(
let closure_var = var_store.fresh(); let closure_var = var_store.fresh();
introduced_variables.insert_wildcard(Loc::at_zero(closure_var)); introduced_variables.insert_wildcard(Loc::at_zero(closure_var));
let actual = { let actual = Type::Function(
Type::TagUnion( vec![Type::EmptyRec],
vec![( Box::new(Type::Variable(closure_var)),
effect_tag_name, Box::new(state_type.clone()),
vec![Type::Function( );
vec![Type::EmptyRec],
Box::new(Type::Variable(closure_var)),
Box::new(state_type.clone()),
)],
)],
TypeExtension::Closed,
)
};
Type::Alias { Type::Alias {
symbol: effect_symbol, symbol: effect_symbol,
@ -1140,7 +1135,7 @@ fn build_effect_loop(
closure_var, closure_var,
))], ))],
actual: Box::new(actual), actual: Box::new(actual),
kind: AliasKind::Structural, kind: AliasKind::Opaque,
} }
}; };
@ -1187,7 +1182,7 @@ fn build_effect_loop(
fn build_effect_loop_body( fn build_effect_loop_body(
env: &mut Env, env: &mut Env,
scope: &mut Scope, scope: &mut Scope,
effect_tag_name: TagName, effect_symbol: Symbol,
loop_symbol: Symbol, loop_symbol: Symbol,
state_symbol: Symbol, state_symbol: Symbol,
step_symbol: Symbol, step_symbol: Symbol,
@ -1207,7 +1202,7 @@ fn build_effect_loop_body(
let inner_body = build_effect_loop_inner_body( let inner_body = build_effect_loop_inner_body(
env, env,
scope, scope,
effect_tag_name.clone(), effect_symbol,
loop_symbol, loop_symbol,
state_symbol, state_symbol,
step_symbol, step_symbol,
@ -1217,7 +1212,7 @@ fn build_effect_loop_body(
let captured_symbols = vec![state_symbol, step_symbol]; let captured_symbols = vec![state_symbol, step_symbol];
wrap_in_effect_thunk( wrap_in_effect_thunk(
inner_body, inner_body,
effect_tag_name, effect_symbol,
closure_name, closure_name,
captured_symbols, captured_symbols,
var_store, var_store,
@ -1249,7 +1244,7 @@ fn applied_tag_pattern(
fn build_effect_loop_inner_body( fn build_effect_loop_inner_body(
env: &mut Env, env: &mut Env,
scope: &mut Scope, scope: &mut Scope,
effect_tag_name: TagName, effect_symbol: Symbol,
loop_symbol: Symbol, loop_symbol: Symbol,
state_symbol: Symbol, state_symbol: Symbol,
step_symbol: Symbol, step_symbol: Symbol,
@ -1264,15 +1259,18 @@ fn build_effect_loop_inner_body(
// Effect thunk1 = step state // Effect thunk1 = step state
let thunk_from_effect = { let thunk_from_effect = {
let whole_var = var_store.fresh(); let whole_var = var_store.fresh();
let ext_var = var_store.fresh();
let thunk_var = var_store.fresh(); let thunk_var = var_store.fresh();
let pattern = Pattern::AppliedTag { let (specialized_def_type, type_arguments, lambda_set_variables) =
ext_var, build_fresh_opaque_variables(var_store);
let pattern = Pattern::UnwrappedOpaque {
whole_var, whole_var,
tag_name: effect_tag_name.clone(), opaque: effect_symbol,
arguments: vec![(thunk_var, Loc::at_zero(Pattern::Identifier(thunk1_symbol)))], argument: Box::new((thunk_var, Loc::at_zero(Pattern::Identifier(thunk1_symbol)))),
specialized_def_type,
type_arguments,
lambda_set_variables,
}; };
let pattern_vars = SendMap::default(); let pattern_vars = SendMap::default();
@ -1332,15 +1330,10 @@ fn build_effect_loop_inner_body(
}; };
// ``` // ```
// Effect thunk2 = loop effect // @Effect thunk2 = loop effect
// thunk2 {} // thunk2 {}
// ``` // ```
let force_thunk2 = force_effect( let force_thunk2 = force_effect(loop_new_state_step, effect_symbol, thunk2_symbol, var_store);
loop_new_state_step,
effect_tag_name,
thunk2_symbol,
var_store,
);
let step_branch = { let step_branch = {
let step_tag_name = TagName::Global("Step".into()); let step_tag_name = TagName::Global("Step".into());
@ -1387,7 +1380,7 @@ pub fn build_host_exposed_def(
scope: &mut Scope, scope: &mut Scope,
symbol: Symbol, symbol: Symbol,
ident: &str, ident: &str,
effect_tag_name: TagName, effect_symbol: Symbol,
var_store: &mut VarStore, var_store: &mut VarStore,
annotation: crate::annotation::Annotation, annotation: crate::annotation::Annotation,
) -> Def { ) -> Def {
@ -1400,8 +1393,15 @@ pub fn build_host_exposed_def(
let mut linked_symbol_arguments: Vec<(Variable, Expr)> = Vec::new(); let mut linked_symbol_arguments: Vec<(Variable, Expr)> = Vec::new();
let mut captured_symbols: Vec<(Symbol, Variable)> = Vec::new(); let mut captured_symbols: Vec<(Symbol, Variable)> = Vec::new();
let crate::annotation::Annotation {
introduced_variables,
typ,
aliases,
..
} = annotation;
let def_body = { let def_body = {
match annotation.typ.shallow_dealias() { match typ.shallow_structural_dealias() {
Type::Function(args, _, _) => { Type::Function(args, _, _) => {
for i in 0..args.len() { for i in 0..args.len() {
let name = format!("closure_arg_{}_{}", ident, i); let name = format!("closure_arg_{}_{}", ident, i);
@ -1462,11 +1462,15 @@ pub fn build_host_exposed_def(
loc_body: Box::new(Loc::at_zero(low_level_call)), loc_body: Box::new(Loc::at_zero(low_level_call)),
}); });
let body = Expr::Tag { let (specialized_def_type, type_arguments, lambda_set_variables) =
variant_var: var_store.fresh(), build_fresh_opaque_variables(var_store);
ext_var: var_store.fresh(), let body = Expr::OpaqueRef {
name: effect_tag_name, opaque_var: var_store.fresh(),
arguments: vec![(var_store.fresh(), Loc::at_zero(effect_closure))], name: effect_symbol,
argument: Box::new((var_store.fresh(), Loc::at_zero(effect_closure))),
specialized_def_type,
type_arguments,
lambda_set_variables,
}; };
Expr::Closure(ClosureData { Expr::Closure(ClosureData {
@ -1523,20 +1527,24 @@ pub fn build_host_exposed_def(
loc_body: Box::new(Loc::at_zero(low_level_call)), loc_body: Box::new(Loc::at_zero(low_level_call)),
}); });
Expr::Tag { let (specialized_def_type, type_arguments, lambda_set_variables) =
variant_var: var_store.fresh(), build_fresh_opaque_variables(var_store);
ext_var: var_store.fresh(), Expr::OpaqueRef {
name: effect_tag_name, opaque_var: var_store.fresh(),
arguments: vec![(var_store.fresh(), Loc::at_zero(effect_closure))], name: effect_symbol,
argument: Box::new((var_store.fresh(), Loc::at_zero(effect_closure))),
specialized_def_type,
type_arguments,
lambda_set_variables,
} }
} }
} }
}; };
let def_annotation = crate::def::Annotation { let def_annotation = crate::def::Annotation {
signature: annotation.typ, signature: typ,
introduced_variables: annotation.introduced_variables, introduced_variables,
aliases: annotation.aliases, aliases,
region: Region::zero(), region: Region::zero(),
}; };
@ -1549,9 +1557,19 @@ pub fn build_host_exposed_def(
} }
} }
fn build_effect_alias( pub fn build_effect_actual(a_type: Type, var_store: &mut VarStore) -> Type {
let closure_var = var_store.fresh();
Type::Function(
vec![Type::EmptyRec],
Box::new(Type::Variable(closure_var)),
Box::new(a_type),
)
}
/// Effect a := {} -> a
fn build_effect_opaque(
effect_symbol: Symbol, effect_symbol: Symbol,
effect_tag_name: TagName,
a_name: &str, a_name: &str,
a_var: Variable, a_var: Variable,
a_type: Type, a_type: Type,
@ -1561,47 +1579,39 @@ fn build_effect_alias(
let closure_var = var_store.fresh(); let closure_var = var_store.fresh();
introduced_variables.insert_wildcard(Loc::at_zero(closure_var)); introduced_variables.insert_wildcard(Loc::at_zero(closure_var));
let actual = { let actual = Type::Function(
Type::TagUnion( vec![Type::EmptyRec],
vec![( Box::new(Type::Variable(closure_var)),
effect_tag_name, Box::new(a_type),
vec![Type::Function( );
vec![Type::EmptyRec],
Box::new(Type::Variable(closure_var)),
Box::new(a_type),
)],
)],
TypeExtension::Closed,
)
};
Type::Alias { Type::Alias {
symbol: effect_symbol, symbol: effect_symbol,
type_arguments: vec![(a_name.into(), Type::Variable(a_var))], type_arguments: vec![(a_name.into(), Type::Variable(a_var))],
lambda_set_variables: vec![roc_types::types::LambdaSet(Type::Variable(closure_var))], lambda_set_variables: vec![roc_types::types::LambdaSet(Type::Variable(closure_var))],
actual: Box::new(actual), actual: Box::new(actual),
kind: AliasKind::Structural, kind: AliasKind::Opaque,
} }
} }
pub fn build_effect_actual( fn build_fresh_opaque_variables(
effect_tag_name: TagName,
a_type: Type,
var_store: &mut VarStore, var_store: &mut VarStore,
) -> Type { ) -> (Box<Type>, Vec<(Lowercase, Type)>, Vec<LambdaSet>) {
let closure_var = var_store.fresh(); let closure_var = var_store.fresh();
Type::TagUnion( // NB: if there are bugs, check whether not introducing variables is a problem!
vec![( // introduced_variables.insert_wildcard(Loc::at_zero(closure_var));
effect_tag_name,
vec![Type::Function( let a_var = var_store.fresh();
vec![Type::EmptyRec], let actual = Type::Function(
Box::new(Type::Variable(closure_var)), vec![Type::EmptyRec],
Box::new(a_type), Box::new(Type::Variable(closure_var)),
)], Box::new(Type::Variable(a_var)),
)], );
TypeExtension::Closed, let type_arguments = vec![("a".into(), Type::Variable(a_var))];
) let lambda_set_variables = vec![roc_types::types::LambdaSet(Type::Variable(closure_var))];
(Box::new(actual), type_arguments, lambda_set_variables)
} }
#[inline(always)] #[inline(always)]

View file

@ -7,7 +7,7 @@ use roc_region::all::{Loc, Region};
/// The canonicalization environment for a particular module. /// The canonicalization environment for a particular module.
pub struct Env<'a> { pub struct Env<'a> {
/// The module's path. Private tags and unqualified references to identifiers /// The module's path. Opaques and unqualified references to identifiers
/// are assumed to be relative to this path. /// are assumed to be relative to this path.
pub home: ModuleId, pub home: ModuleId,
@ -182,8 +182,4 @@ impl<'a> Env<'a> {
pub fn problem(&mut self, problem: Problem) { pub fn problem(&mut self, problem: Problem) {
self.problems.push(problem) self.problems.push(problem)
} }
pub fn register_closure(&mut self, symbol: Symbol, references: References) {
self.closures.insert(symbol, references);
}
} }

View file

@ -163,8 +163,7 @@ pub enum Expr {
name: TagName, name: TagName,
}, },
/// A wrapping of an opaque type, like `$Age 21` /// A wrapping of an opaque type, like `@Age 21`
// TODO(opaques): $->@ above when opaques land
OpaqueRef { OpaqueRef {
opaque_var: Variable, opaque_var: Variable,
name: Symbol, name: Symbol,
@ -293,6 +292,23 @@ pub struct WhenBranch {
pub guard: Option<Loc<Expr>>, pub guard: Option<Loc<Expr>>,
} }
impl WhenBranch {
pub fn pattern_region(&self) -> Region {
Region::span_across(
&self
.patterns
.first()
.expect("when branch has no pattern?")
.region,
&self
.patterns
.last()
.expect("when branch has no pattern?")
.region,
)
}
}
pub fn canonicalize_expr<'a>( pub fn canonicalize_expr<'a>(
env: &mut Env<'a>, env: &mut Env<'a>,
var_store: &mut VarStore, var_store: &mut VarStore,
@ -699,7 +715,9 @@ pub fn canonicalize_expr<'a>(
} }
} }
env.register_closure(symbol, output.references.clone()); // store the references of this function in the Env. This information is used
// when we canonicalize a surrounding def (if it exists)
env.closures.insert(symbol, output.references.clone());
let mut captured_symbols: Vec<_> = captured_symbols let mut captured_symbols: Vec<_> = captured_symbols
.into_iter() .into_iter()
@ -811,23 +829,6 @@ pub fn canonicalize_expr<'a>(
Output::default(), Output::default(),
) )
} }
ast::Expr::PrivateTag(tag) => {
let variant_var = var_store.fresh();
let ext_var = var_store.fresh();
let tag_ident = env.ident_ids.get_or_insert(&(*tag).into());
let symbol = Symbol::new(env.home, tag_ident);
let lambda_set_symbol = env.gen_unique_symbol();
(
ZeroArgumentTag {
name: TagName::Private(symbol),
variant_var,
ext_var,
closure_name: lambda_set_symbol,
},
Output::default(),
)
}
ast::Expr::OpaqueRef(opaque_ref) => { ast::Expr::OpaqueRef(opaque_ref) => {
// If we're here, the opaque reference is definitely not wrapping an argument - wrapped // If we're here, the opaque reference is definitely not wrapping an argument - wrapped
// arguments are handled in the Apply branch. // arguments are handled in the Apply branch.

View file

@ -18,3 +18,4 @@ pub mod procedure;
mod reference_matrix; mod reference_matrix;
pub mod scope; pub mod scope;
pub mod string; pub mod string;
pub mod traverse;

View file

@ -8,8 +8,8 @@ use crate::pattern::Pattern;
use crate::scope::Scope; use crate::scope::Scope;
use bumpalo::Bump; use bumpalo::Bump;
use roc_collections::{MutMap, SendMap, VecSet}; use roc_collections::{MutMap, SendMap, VecSet};
use roc_module::ident::Ident;
use roc_module::ident::Lowercase; use roc_module::ident::Lowercase;
use roc_module::ident::{Ident, TagName};
use roc_module::symbol::{IdentIds, ModuleId, ModuleIds, Symbol}; use roc_module::symbol::{IdentIds, ModuleId, ModuleIds, Symbol};
use roc_parse::ast; use roc_parse::ast;
use roc_parse::header::HeaderFor; use roc_parse::header::HeaderFor;
@ -116,23 +116,18 @@ impl GeneratedInfo {
) )
.unwrap(); .unwrap();
let effect_tag_name = TagName::Private(effect_symbol);
{ {
let a_var = var_store.fresh(); let a_var = var_store.fresh();
let actual = crate::effect_module::build_effect_actual( let actual =
effect_tag_name, crate::effect_module::build_effect_actual(Type::Variable(a_var), var_store);
Type::Variable(a_var),
var_store,
);
scope.add_alias( scope.add_alias(
effect_symbol, effect_symbol,
Region::zero(), Region::zero(),
vec![Loc::at_zero(("a".into(), a_var))], vec![Loc::at_zero(("a".into(), a_var))],
actual, actual,
AliasKind::Structural, AliasKind::Opaque,
); );
} }
@ -433,7 +428,7 @@ pub fn canonicalize_module_defs<'a>(
&mut scope, &mut scope,
*symbol, *symbol,
&ident, &ident,
TagName::Private(effect_symbol), effect_symbol,
var_store, var_store,
annotation, annotation,
); );

View file

@ -151,7 +151,6 @@ pub fn desugar_expr<'a>(arena: &'a Bump, loc_expr: &'a Loc<Expr<'a>>) -> &'a Loc
| MalformedClosure | MalformedClosure
| PrecedenceConflict { .. } | PrecedenceConflict { .. }
| GlobalTag(_) | GlobalTag(_)
| PrivateTag(_)
| OpaqueRef(_) => loc_expr, | OpaqueRef(_) => loc_expr,
Access(sub_expr, paths) => { Access(sub_expr, paths) => {
@ -425,7 +424,6 @@ fn binop_to_function(binop: BinOp) -> (&'static str, &'static str) {
Slash => (ModuleName::NUM, "div"), Slash => (ModuleName::NUM, "div"),
DoubleSlash => (ModuleName::NUM, "divTrunc"), DoubleSlash => (ModuleName::NUM, "divTrunc"),
Percent => (ModuleName::NUM, "rem"), Percent => (ModuleName::NUM, "rem"),
DoublePercent => (ModuleName::NUM, "mod"),
Plus => (ModuleName::NUM, "add"), Plus => (ModuleName::NUM, "add"),
Minus => (ModuleName::NUM, "sub"), Minus => (ModuleName::NUM, "sub"),
Equals => (ModuleName::BOOL, "isEq"), Equals => (ModuleName::BOOL, "isEq"),

View file

@ -82,6 +82,31 @@ pub enum Pattern {
MalformedPattern(MalformedPatternProblem, Region), MalformedPattern(MalformedPatternProblem, Region),
} }
impl Pattern {
pub fn opt_var(&self) -> Option<Variable> {
use Pattern::*;
match self {
Identifier(_) => None,
AppliedTag { whole_var, .. } => Some(*whole_var),
UnwrappedOpaque { whole_var, .. } => Some(*whole_var),
RecordDestructure { whole_var, .. } => Some(*whole_var),
NumLiteral(var, ..) => Some(*var),
IntLiteral(var, ..) => Some(*var),
FloatLiteral(var, ..) => Some(*var),
StrLiteral(_) => None,
SingleQuote(_) => None,
Underscore => None,
AbilityMemberSpecialization { .. } => None,
Shadowed(..) | OpaqueNotInScope(..) | UnsupportedPattern(..) | MalformedPattern(..) => {
None
}
}
}
}
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
pub struct RecordDestruct { pub struct RecordDestruct {
pub var: Variable, pub var: Variable,
@ -244,17 +269,6 @@ pub fn canonicalize_pattern<'a>(
arguments: vec![], arguments: vec![],
} }
} }
PrivateTag(name) => {
let ident_id = env.ident_ids.get_or_insert(&(*name).into());
// Canonicalize the tag's name.
Pattern::AppliedTag {
whole_var: var_store.fresh(),
ext_var: var_store.fresh(),
tag_name: TagName::Private(Symbol::new(env.home, ident_id)),
arguments: vec![],
}
}
OpaqueRef(name) => { OpaqueRef(name) => {
// If this opaque ref had an argument, we would be in the "Apply" branch. // If this opaque ref had an argument, we would be in the "Apply" branch.
let loc_name = Loc::at(region, (*name).into()); let loc_name = Loc::at(region, (*name).into());
@ -289,17 +303,6 @@ pub fn canonicalize_pattern<'a>(
arguments: can_patterns, arguments: can_patterns,
} }
} }
PrivateTag(name) => {
let ident_id = env.ident_ids.get_or_insert(&name.into());
let tag_name = TagName::Private(Symbol::new(env.home, ident_id));
Pattern::AppliedTag {
whole_var: var_store.fresh(),
ext_var: var_store.fresh(),
tag_name,
arguments: can_patterns,
}
}
OpaqueRef(name) => match scope.lookup_opaque_ref(name, tag.region) { OpaqueRef(name) => match scope.lookup_opaque_ref(name, tag.region) {
Ok((opaque, opaque_def)) => { Ok((opaque, opaque_def)) => {

View file

@ -1,7 +1,8 @@
// see if we get better performance with different integer types // see if we get better performance with different integer types
pub(crate) type Element = usize; type Order = bitvec::order::Lsb0;
pub(crate) type BitVec = bitvec::vec::BitVec<Element>; type Element = usize;
pub(crate) type BitSlice = bitvec::prelude::BitSlice<Element>; type BitVec = bitvec::vec::BitVec<Element, Order>;
type BitSlice = bitvec::prelude::BitSlice<Element, Order>;
/// A square boolean matrix used to store relations /// A square boolean matrix used to store relations
/// ///
@ -36,8 +37,8 @@ impl ReferenceMatrix {
} }
#[inline(always)] #[inline(always)]
pub fn get(&self, index: usize) -> bool { pub fn get_row_col(&self, row: usize, col: usize) -> bool {
self.bitvec[index] self.bitvec[row * self.length + col]
} }
} }
@ -50,6 +51,7 @@ impl ReferenceMatrix {
// //
// Thank you, Samuel! // Thank you, Samuel!
impl ReferenceMatrix { impl ReferenceMatrix {
#[allow(dead_code)]
pub fn topological_sort_into_groups(&self) -> TopologicalSort { pub fn topological_sort_into_groups(&self) -> TopologicalSort {
if self.length == 0 { if self.length == 0 {
return TopologicalSort::Groups { groups: Vec::new() }; return TopologicalSort::Groups { groups: Vec::new() };
@ -128,7 +130,7 @@ impl ReferenceMatrix {
} }
/// Get the strongly-connected components of the set of input nodes. /// Get the strongly-connected components of the set of input nodes.
pub fn strongly_connected_components(&self, nodes: &[u32]) -> Vec<Vec<u32>> { pub fn strongly_connected_components(&self, nodes: &[u32]) -> Sccs {
let mut params = Params::new(self.length, nodes); let mut params = Params::new(self.length, nodes);
'outer: loop { 'outer: loop {
@ -147,6 +149,7 @@ impl ReferenceMatrix {
} }
} }
#[allow(dead_code)]
pub(crate) enum TopologicalSort { pub(crate) enum TopologicalSort {
/// There were no cycles, all nodes have been partitioned into groups /// There were no cycles, all nodes have been partitioned into groups
Groups { groups: Vec<Vec<u32>> }, Groups { groups: Vec<Vec<u32>> },
@ -172,7 +175,7 @@ struct Params {
c: usize, c: usize,
p: Vec<u32>, p: Vec<u32>,
s: Vec<u32>, s: Vec<u32>,
scc: Vec<Vec<u32>>, scc: Sccs,
scca: Vec<u32>, scca: Vec<u32>,
} }
@ -189,7 +192,10 @@ impl Params {
c: 0, c: 0,
s: Vec::new(), s: Vec::new(),
p: Vec::new(), p: Vec::new(),
scc: Vec::new(), scc: Sccs {
matrix: ReferenceMatrix::new(length),
components: 0,
},
scca: Vec::new(), scca: Vec::new(),
} }
} }
@ -230,15 +236,47 @@ fn recurse_onto(length: usize, bitvec: &BitVec, v: usize, params: &mut Params) {
if params.p.last() == Some(&(v as u32)) { if params.p.last() == Some(&(v as u32)) {
params.p.pop(); params.p.pop();
let mut component = Vec::new();
while let Some(node) = params.s.pop() { while let Some(node) = params.s.pop() {
component.push(node); params
.scc
.matrix
.set_row_col(params.scc.components, node as usize, true);
params.scca.push(node); params.scca.push(node);
params.preorders[node as usize] = Preorder::Removed; params.preorders[node as usize] = Preorder::Removed;
if node as usize == v { if node as usize == v {
break; break;
} }
} }
params.scc.push(component);
params.scc.components += 1;
}
}
#[derive(Debug)]
pub(crate) struct Sccs {
components: usize,
matrix: ReferenceMatrix,
}
impl Sccs {
/// Iterate over the individual components. Each component is represented as a bit vector where
/// a one indicates that the node is part of the group and a zero that it is not.
///
/// A good way to get the actual nodes is the `.iter_ones()` method.
///
/// It is guaranteed that a group is non-empty, and that flattening the groups gives a valid
/// topological ordering.
pub fn groups(&self) -> std::iter::Take<bitvec::slice::Chunks<'_, Element, Order>> {
// work around a panic when requesting a chunk size of 0
let length = if self.matrix.length == 0 {
// the `.take(self.components)` ensures the resulting iterator will be empty
assert!(self.components == 0);
1
} else {
self.matrix.length
};
self.matrix.bitvec.chunks(length).take(self.components)
} }
} }

View file

@ -134,7 +134,12 @@ fn add_aliases(var_store: &mut VarStore) -> SendMap<Symbol, Alias> {
let mut aliases = SendMap::default(); let mut aliases = SendMap::default();
for (symbol, builtin_alias) in solved_aliases { for (symbol, builtin_alias) in solved_aliases {
let BuiltinAlias { region, vars, typ } = builtin_alias; let BuiltinAlias {
region,
vars,
typ,
kind,
} = builtin_alias;
let mut free_vars = FreeVars::default(); let mut free_vars = FreeVars::default();
let typ = roc_types::solved_types::to_type(&typ, &mut free_vars, var_store); let typ = roc_types::solved_types::to_type(&typ, &mut free_vars, var_store);
@ -153,8 +158,7 @@ fn add_aliases(var_store: &mut VarStore) -> SendMap<Symbol, Alias> {
lambda_set_variables: Vec::new(), lambda_set_variables: Vec::new(),
recursion_variables: MutSet::default(), recursion_variables: MutSet::default(),
type_variables: variables, type_variables: variables,
// TODO(opaques): replace when opaques are included in the stdlib kind,
kind: AliasKind::Structural,
}; };
aliases.insert(symbol, alias); aliases.insert(symbol, alias);
@ -201,11 +205,6 @@ impl Scope {
} }
pub fn lookup(&self, ident: &Ident, region: Region) -> Result<Symbol, RuntimeError> { pub fn lookup(&self, ident: &Ident, region: Region) -> Result<Symbol, RuntimeError> {
println!(
"stats: string length: {}, ident len {}",
self.idents.string.len(),
self.idents.len()
);
match self.idents.get_symbol(ident) { match self.idents.get_symbol(ident) {
Some(symbol) => Ok(symbol), Some(symbol) => Ok(symbol),
None => { None => {
@ -230,15 +229,14 @@ impl Scope {
} }
/// Check if there is an opaque type alias referenced by `opaque_ref` referenced in the /// Check if there is an opaque type alias referenced by `opaque_ref` referenced in the
/// current scope. E.g. `$Age` must reference an opaque `Age` declared in this module, not any /// current scope. E.g. `@Age` must reference an opaque `Age` declared in this module, not any
/// other! /// other!
// TODO(opaques): $->@ in the above comment
pub fn lookup_opaque_ref( pub fn lookup_opaque_ref(
&self, &self,
opaque_ref: &str, opaque_ref: &str,
lookup_region: Region, lookup_region: Region,
) -> Result<(Symbol, &Alias), RuntimeError> { ) -> Result<(Symbol, &Alias), RuntimeError> {
debug_assert!(opaque_ref.starts_with('$')); debug_assert!(opaque_ref.starts_with('@'));
let opaque = opaque_ref[1..].into(); let opaque = opaque_ref[1..].into();
match self.idents.get_symbol_and_region(&opaque) { match self.idents.get_symbol_and_region(&opaque) {

View 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
}

View file

@ -87,6 +87,18 @@ impl<K: PartialEq, V> VecMap<K, V> {
pub fn values(&self) -> impl Iterator<Item = &V> { pub fn values(&self) -> impl Iterator<Item = &V> {
self.values.iter() self.values.iter()
} }
pub fn unzip(self) -> (Vec<K>, Vec<V>) {
(self.keys, self.values)
}
/// # Safety
///
/// keys and values must have the same length, and there must not
/// be any duplicates in the keys vector
pub unsafe fn zip(keys: Vec<K>, values: Vec<V>) -> Self {
Self { keys, values }
}
} }
impl<K: Ord, V> Extend<(K, V)> for VecMap<K, V> { impl<K: Ord, V> Extend<(K, V)> for VecMap<K, V> {

View file

@ -2,13 +2,13 @@ use arrayvec::ArrayVec;
use roc_can::constraint::{Constraint, Constraints}; use roc_can::constraint::{Constraint, Constraints};
use roc_can::expected::Expected::{self, *}; use roc_can::expected::Expected::{self, *};
use roc_can::num::{FloatBound, FloatWidth, IntBound, IntWidth, NumericBound, SignDemand}; use roc_can::num::{FloatBound, FloatWidth, IntBound, IntWidth, NumericBound, SignDemand};
use roc_module::ident::{Lowercase, TagName}; use roc_module::ident::Lowercase;
use roc_module::symbol::Symbol; use roc_module::symbol::Symbol;
use roc_region::all::Region; use roc_region::all::Region;
use roc_types::subs::Variable; use roc_types::subs::Variable;
use roc_types::types::Reason;
use roc_types::types::Type::{self, *}; use roc_types::types::Type::{self, *};
use roc_types::types::{AliasKind, Category}; use roc_types::types::{AliasKind, Category};
use roc_types::types::{Reason, TypeExtension};
#[must_use] #[must_use]
#[inline(always)] #[inline(always)]
@ -163,14 +163,14 @@ fn builtin_alias(
symbol: Symbol, symbol: Symbol,
type_arguments: Vec<(Lowercase, Type)>, type_arguments: Vec<(Lowercase, Type)>,
actual: Box<Type>, actual: Box<Type>,
kind: AliasKind,
) -> Type { ) -> Type {
Type::Alias { Type::Alias {
symbol, symbol,
type_arguments, type_arguments,
actual, actual,
lambda_set_variables: vec![], lambda_set_variables: vec![],
// TODO(opaques): revisit later kind,
kind: AliasKind::Structural,
} }
} }
@ -180,49 +180,48 @@ pub fn num_float(range: Type) -> Type {
Symbol::NUM_FLOAT, Symbol::NUM_FLOAT,
vec![("range".into(), range.clone())], vec![("range".into(), range.clone())],
Box::new(num_num(num_floatingpoint(range))), Box::new(num_num(num_floatingpoint(range))),
AliasKind::Structural,
) )
} }
#[inline(always)] #[inline(always)]
pub fn num_floatingpoint(range: Type) -> Type { pub fn num_floatingpoint(range: Type) -> Type {
let alias_content = Type::TagUnion(
vec![(
TagName::Private(Symbol::NUM_AT_FLOATINGPOINT),
vec![range.clone()],
)],
TypeExtension::Closed,
);
builtin_alias( builtin_alias(
Symbol::NUM_FLOATINGPOINT, Symbol::NUM_FLOATINGPOINT,
vec![("range".into(), range)], vec![("range".into(), range.clone())],
Box::new(alias_content), Box::new(range),
AliasKind::Opaque,
) )
} }
#[inline(always)] #[inline(always)]
pub fn num_u32() -> Type { pub fn num_u32() -> Type {
builtin_alias(Symbol::NUM_U32, vec![], Box::new(num_int(num_unsigned32()))) builtin_alias(
Symbol::NUM_U32,
vec![],
Box::new(num_int(num_unsigned32())),
AliasKind::Structural,
)
} }
#[inline(always)] #[inline(always)]
fn num_unsigned32() -> Type { fn num_unsigned32() -> Type {
let alias_content = Type::TagUnion( builtin_alias(
vec![(TagName::Private(Symbol::NUM_AT_UNSIGNED32), vec![])], Symbol::NUM_UNSIGNED32,
TypeExtension::Closed, vec![],
); Box::new(Type::EmptyTagUnion),
AliasKind::Opaque,
builtin_alias(Symbol::NUM_UNSIGNED32, vec![], Box::new(alias_content)) )
} }
#[inline(always)] #[inline(always)]
pub fn num_binary64() -> Type { pub fn num_binary64() -> Type {
let alias_content = Type::TagUnion( builtin_alias(
vec![(TagName::Private(Symbol::NUM_AT_BINARY64), vec![])], Symbol::NUM_BINARY64,
TypeExtension::Closed, vec![],
); Box::new(Type::EmptyTagUnion),
AliasKind::Opaque,
builtin_alias(Symbol::NUM_BINARY64, vec![], Box::new(alias_content)) )
} }
#[inline(always)] #[inline(always)]
@ -231,47 +230,37 @@ pub fn num_int(range: Type) -> Type {
Symbol::NUM_INT, Symbol::NUM_INT,
vec![("range".into(), range.clone())], vec![("range".into(), range.clone())],
Box::new(num_num(num_integer(range))), Box::new(num_num(num_integer(range))),
AliasKind::Structural,
) )
} }
#[inline(always)] #[inline(always)]
pub fn num_signed64() -> Type { pub fn num_signed64() -> Type {
let alias_content = Type::TagUnion( builtin_alias(
vec![(TagName::Private(Symbol::NUM_AT_SIGNED64), vec![])], Symbol::NUM_SIGNED64,
TypeExtension::Closed, vec![],
); Box::new(Type::EmptyTagUnion),
AliasKind::Opaque,
builtin_alias(Symbol::NUM_SIGNED64, vec![], Box::new(alias_content)) )
} }
#[inline(always)] #[inline(always)]
pub fn num_integer(range: Type) -> Type { pub fn num_integer(range: Type) -> Type {
let alias_content = Type::TagUnion(
vec![(
TagName::Private(Symbol::NUM_AT_INTEGER),
vec![range.clone()],
)],
TypeExtension::Closed,
);
builtin_alias( builtin_alias(
Symbol::NUM_INTEGER, Symbol::NUM_INTEGER,
vec![("range".into(), range)], vec![("range".into(), range.clone())],
Box::new(alias_content), Box::new(range),
AliasKind::Opaque,
) )
} }
#[inline(always)] #[inline(always)]
pub fn num_num(typ: Type) -> Type { pub fn num_num(typ: Type) -> Type {
let alias_content = Type::TagUnion(
vec![(TagName::Private(Symbol::NUM_AT_NUM), vec![typ.clone()])],
TypeExtension::Closed,
);
builtin_alias( builtin_alias(
Symbol::NUM_NUM, Symbol::NUM_NUM,
vec![("range".into(), typ)], vec![("range".into(), typ.clone())],
Box::new(alias_content), Box::new(typ),
AliasKind::Opaque,
) )
} }

View file

@ -585,20 +585,21 @@ pub fn constrain_expr(
branches, branches,
.. ..
} => { } => {
// Infer the condition expression's type.
let cond_var = *cond_var; let cond_var = *cond_var;
let cond_type = Variable(cond_var); let cond_type = Variable(cond_var);
let expr_con = constrain_expr(
constraints,
env,
region,
&loc_cond.value,
NoExpectation(cond_type.clone()),
);
let branch_var = *expr_var; let branch_var = *expr_var;
let branch_type = Variable(branch_var); let branch_type = Variable(branch_var);
let branches_region = {
debug_assert!(!branches.is_empty());
Region::span_across(
&loc_cond.region,
// &branches.first().unwrap().region(),
&branches.last().unwrap().pattern_region(),
)
};
let branch_expr_reason = let branch_expr_reason =
|expected: &Expected<Type>, index, branch_region| match expected { |expected: &Expected<Type>, index, branch_region| match expected {
FromAnnotation(name, arity, ann_source, _typ) => { FromAnnotation(name, arity, ann_source, _typ) => {
@ -647,12 +648,20 @@ pub fn constrain_expr(
// constraints. // constraints.
let mut pattern_vars = Vec::with_capacity(branches.len()); let mut pattern_vars = Vec::with_capacity(branches.len());
let mut pattern_headers = SendMap::default(); let mut pattern_headers = SendMap::default();
let mut pattern_cons = Vec::with_capacity(branches.len()); let mut pattern_cons = Vec::with_capacity(branches.len() + 1);
let mut branch_cons = Vec::with_capacity(branches.len()); let mut branch_cons = Vec::with_capacity(branches.len());
for (index, when_branch) in branches.iter().enumerate() { for (index, when_branch) in branches.iter().enumerate() {
let pattern_region = let expected_pattern = |sub_pattern, sub_region| {
Region::across_all(when_branch.patterns.iter().map(|v| &v.region)); PExpected::ForReason(
PReason::WhenMatch {
index: HumanIndex::zero_based(index),
sub_pattern,
},
cond_type.clone(),
sub_region,
)
};
let (new_pattern_vars, new_pattern_headers, pattern_con, branch_con) = let (new_pattern_vars, new_pattern_headers, pattern_con, branch_con) =
constrain_when_branch_help( constrain_when_branch_help(
@ -660,13 +669,7 @@ pub fn constrain_expr(
env, env,
region, region,
when_branch, when_branch,
PExpected::ForReason( expected_pattern,
PReason::WhenMatch {
index: HumanIndex::zero_based(index),
},
cond_type.clone(),
pattern_region,
),
branch_expr_reason( branch_expr_reason(
&expected, &expected,
HumanIndex::zero_based(index), HumanIndex::zero_based(index),
@ -696,10 +699,20 @@ pub fn constrain_expr(
// //
// The return type of each branch must equal the return type of // The return type of each branch must equal the return type of
// the entire when-expression. // the entire when-expression.
// branch_cons.extend(pattern_cons);
// branch_constraints.push(constraints.and_constraint(pattern_cons)); // After solving the condition variable with what's expected from the branch patterns,
let mut total_cons = Vec::with_capacity(1 + 2 * branches.len() + 1); // check it against the condition expression.
total_cons.push(expr_con); // TODO: when we have exhaustiveness checking during the typechecking phase, perform
// exhaustiveness checking when this expectation fails. That will produce better error
// messages.
let cond_constraint = constrain_expr(
constraints,
env,
loc_cond.region,
&loc_cond.value,
Expected::ForReason(Reason::WhenBranches, cond_type, branches_region),
);
pattern_cons.push(cond_constraint);
// Solve all the pattern constraints together, introducing variables in the pattern as // Solve all the pattern constraints together, introducing variables in the pattern as
// need be before solving the bodies. // need be before solving the bodies.
@ -712,15 +725,11 @@ pub fn constrain_expr(
pattern_constraints, pattern_constraints,
body_constraints, body_constraints,
); );
total_cons.push(when_body_con);
total_cons.push(constraints.equal_types_var( let result_con =
branch_var, constraints.equal_types_var(branch_var, expected, Category::When, region);
expected,
Category::When,
region,
));
let total_cons = [when_body_con, result_con];
let branch_constraints = constraints.and_constraint(total_cons); let branch_constraints = constraints.and_constraint(total_cons);
// exhautiveness checking happens when converting to mono::Expr // exhautiveness checking happens when converting to mono::Expr
@ -1122,7 +1131,7 @@ fn constrain_when_branch_help(
env: &Env, env: &Env,
region: Region, region: Region,
when_branch: &WhenBranch, when_branch: &WhenBranch,
pattern_expected: PExpected<Type>, pattern_expected: impl Fn(HumanIndex, Region) -> PExpected<Type>,
expr_expected: Expected<Type>, expr_expected: Expected<Type>,
) -> ( ) -> (
Vec<Variable>, Vec<Variable>,
@ -1142,17 +1151,20 @@ fn constrain_when_branch_help(
headers: SendMap::default(), headers: SendMap::default(),
vars: Vec::with_capacity(1), vars: Vec::with_capacity(1),
constraints: Vec::with_capacity(1), constraints: Vec::with_capacity(1),
delayed_is_open_constraints: Vec::new(),
}; };
// TODO investigate for error messages, is it better to unify all branches with a variable, // TODO investigate for error messages, is it better to unify all branches with a variable,
// then unify that variable with the expectation? // then unify that variable with the expectation?
for loc_pattern in &when_branch.patterns { for (i, loc_pattern) in when_branch.patterns.iter().enumerate() {
let pattern_expected = pattern_expected(HumanIndex::zero_based(i), loc_pattern.region);
constrain_pattern( constrain_pattern(
constraints, constraints,
env, env,
&loc_pattern.value, &loc_pattern.value,
loc_pattern.region, loc_pattern.region,
pattern_expected.clone(), pattern_expected,
&mut state, &mut state,
); );
} }
@ -1171,11 +1183,17 @@ fn constrain_when_branch_help(
); );
// must introduce the headers from the pattern before constraining the guard // must introduce the headers from the pattern before constraining the guard
state
.constraints
.append(&mut state.delayed_is_open_constraints);
let state_constraints = constraints.and_constraint(state.constraints); let state_constraints = constraints.and_constraint(state.constraints);
let inner = constraints.let_constraint([], [], [], guard_constraint, ret_constraint); let inner = constraints.let_constraint([], [], [], guard_constraint, ret_constraint);
(state_constraints, inner) (state_constraints, inner)
} else { } else {
state
.constraints
.append(&mut state.delayed_is_open_constraints);
let state_constraints = constraints.and_constraint(state.constraints); let state_constraints = constraints.and_constraint(state.constraints);
(state_constraints, ret_constraint) (state_constraints, ret_constraint)
}; };
@ -1267,6 +1285,7 @@ fn constrain_def_pattern(
headers: SendMap::default(), headers: SendMap::default(),
vars: Vec::with_capacity(1), vars: Vec::with_capacity(1),
constraints: Vec::with_capacity(1), constraints: Vec::with_capacity(1),
delayed_is_open_constraints: vec![],
}; };
constrain_pattern( constrain_pattern(
@ -1364,6 +1383,7 @@ fn constrain_typed_def(
headers: SendMap::default(), headers: SendMap::default(),
vars: Vec::with_capacity(arguments.len()), vars: Vec::with_capacity(arguments.len()),
constraints: Vec::with_capacity(1), constraints: Vec::with_capacity(1),
delayed_is_open_constraints: vec![],
}; };
let mut vars = Vec::with_capacity(argument_pattern_state.vars.capacity() + 1); let mut vars = Vec::with_capacity(argument_pattern_state.vars.capacity() + 1);
let ret_var = *ret_var; let ret_var = *ret_var;
@ -1843,6 +1863,7 @@ pub fn rec_defs_help(
headers: SendMap::default(), headers: SendMap::default(),
vars: Vec::with_capacity(arguments.len()), vars: Vec::with_capacity(arguments.len()),
constraints: Vec::with_capacity(1), constraints: Vec::with_capacity(1),
delayed_is_open_constraints: vec![],
}; };
let mut vars = Vec::with_capacity(state.vars.capacity() + 1); let mut vars = Vec::with_capacity(state.vars.capacity() + 1);
let mut pattern_types = Vec::with_capacity(state.vars.capacity()); let mut pattern_types = Vec::with_capacity(state.vars.capacity());

View file

@ -18,6 +18,7 @@ pub struct PatternState {
pub headers: SendMap<Symbol, Loc<Type>>, pub headers: SendMap<Symbol, Loc<Type>>,
pub vars: Vec<Variable>, pub vars: Vec<Variable>,
pub constraints: Vec<Constraint>, pub constraints: Vec<Constraint>,
pub delayed_is_open_constraints: Vec<Constraint>,
} }
/// If there is a type annotation, the pattern state headers can be optimized by putting the /// If there is a type annotation, the pattern state headers can be optimized by putting the
@ -180,7 +181,7 @@ pub fn constrain_pattern(
// so, we know that "x" (in this case, a tag union) must be open. // so, we know that "x" (in this case, a tag union) must be open.
if could_be_a_tag_union(expected.get_type_ref()) { if could_be_a_tag_union(expected.get_type_ref()) {
state state
.constraints .delayed_is_open_constraints
.push(constraints.is_open_type(expected.get_type())); .push(constraints.is_open_type(expected.get_type()));
} }
} }
@ -191,7 +192,7 @@ pub fn constrain_pattern(
Identifier(symbol) | Shadowed(_, _, symbol) => { Identifier(symbol) | Shadowed(_, _, symbol) => {
if could_be_a_tag_union(expected.get_type_ref()) { if could_be_a_tag_union(expected.get_type_ref()) {
state state
.constraints .delayed_is_open_constraints
.push(constraints.is_open_type(expected.get_type_ref().clone())); .push(constraints.is_open_type(expected.get_type_ref().clone()));
} }
@ -494,6 +495,9 @@ pub fn constrain_pattern(
state.vars.push(*ext_var); state.vars.push(*ext_var);
state.constraints.push(whole_con); state.constraints.push(whole_con);
state.constraints.push(tag_con); state.constraints.push(tag_con);
state
.constraints
.append(&mut state.delayed_is_open_constraints);
} }
UnwrappedOpaque { UnwrappedOpaque {

View file

@ -2,7 +2,10 @@
//! http://moscova.inria.fr/~maranget/papers/warn/warn.pdf //! http://moscova.inria.fr/~maranget/papers/warn/warn.pdf
use roc_collections::all::{HumanIndex, MutMap}; use roc_collections::all::{HumanIndex, MutMap};
use roc_module::ident::{Lowercase, TagIdIntType, TagName}; use roc_module::{
ident::{Lowercase, TagIdIntType, TagName},
symbol::Symbol,
};
use roc_region::all::Region; use roc_region::all::Region;
use roc_std::RocDec; use roc_std::RocDec;
@ -15,9 +18,9 @@ pub struct Union {
} }
impl Union { impl Union {
pub fn newtype_wrapper(tag_name: TagName, arity: usize) -> Self { pub fn newtype_wrapper(name: CtorName, arity: usize) -> Self {
let alternatives = vec![Ctor { let alternatives = vec![Ctor {
name: tag_name, name,
tag_id: TagId(0), tag_id: TagId(0),
arity, arity,
}]; }];
@ -40,9 +43,24 @@ pub enum RenderAs {
#[derive(Clone, Debug, PartialEq, Eq, Hash, Copy)] #[derive(Clone, Debug, PartialEq, Eq, Hash, Copy)]
pub struct TagId(pub TagIdIntType); pub struct TagId(pub TagIdIntType);
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub enum CtorName {
Tag(TagName),
Opaque(Symbol),
}
impl CtorName {
pub fn is_tag(&self, tag_name: &TagName) -> bool {
match self {
Self::Tag(test) => test == tag_name,
_ => false,
}
}
}
#[derive(Clone, Debug, PartialEq, Eq, Hash)] #[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub struct Ctor { pub struct Ctor {
pub name: TagName, pub name: CtorName,
pub tag_id: TagId, pub tag_id: TagId,
pub arity: usize, pub arity: usize,
} }

View file

@ -468,9 +468,7 @@ impl<'a> Formattable for Tag<'a> {
use self::Tag::*; use self::Tag::*;
match self { match self {
Global { args, .. } | Private { args, .. } => { Global { args, .. } => args.iter().any(|arg| (&arg.value).is_multiline()),
args.iter().any(|arg| (&arg.value).is_multiline())
}
Tag::SpaceBefore(_, _) | Tag::SpaceAfter(_, _) => true, Tag::SpaceBefore(_, _) | Tag::SpaceAfter(_, _) => true,
Malformed(text) => text.chars().any(|c| c == '\n'), Malformed(text) => text.chars().any(|c| c == '\n'),
} }
@ -503,24 +501,6 @@ impl<'a> Formattable for Tag<'a> {
} }
} }
} }
Tag::Private { name, args } => {
debug_assert!(name.value.starts_with('@'));
buf.indent(indent);
buf.push_str(name.value);
if is_multiline {
let arg_indent = indent + INDENT;
for arg in *args {
buf.newline();
arg.format_with_options(buf, Parens::InApply, Newlines::No, arg_indent);
}
} else {
for arg in *args {
buf.spaces(1);
arg.format_with_options(buf, Parens::InApply, Newlines::No, indent);
}
}
}
Tag::SpaceBefore(_, _) | Tag::SpaceAfter(_, _) => unreachable!(), Tag::SpaceBefore(_, _) | Tag::SpaceAfter(_, _) => unreachable!(),
Tag::Malformed(raw) => { Tag::Malformed(raw) => {
buf.indent(indent); buf.indent(indent);

View file

@ -191,12 +191,26 @@ pub fn fmt_body<'a, 'buf>(
buf.push_str(" ="); buf.push_str(" =");
if body.is_multiline() { if body.is_multiline() {
match body { match body {
Expr::SpaceBefore(_, _) => { Expr::SpaceBefore(sub_def, spaces) => {
body.format_with_options(buf, Parens::NotNeeded, Newlines::Yes, indent + INDENT); let should_outdent = match sub_def {
} Expr::Record { .. } | Expr::List { .. } => {
Expr::Record { .. } | Expr::List { .. } => { let is_only_newlines = spaces.iter().all(|s| s.is_newline());
buf.newline(); is_only_newlines && sub_def.is_multiline()
body.format_with_options(buf, Parens::NotNeeded, Newlines::Yes, indent + INDENT); }
_ => false,
};
if should_outdent {
buf.spaces(1);
sub_def.format_with_options(buf, Parens::NotNeeded, Newlines::Yes, indent);
} else {
body.format_with_options(
buf,
Parens::NotNeeded,
Newlines::Yes,
indent + INDENT,
);
}
} }
_ => { _ => {
buf.spaces(1); buf.spaces(1);

View file

@ -38,7 +38,6 @@ impl<'a> Formattable for Expr<'a> {
| MalformedIdent(_, _) | MalformedIdent(_, _)
| MalformedClosure | MalformedClosure
| GlobalTag(_) | GlobalTag(_)
| PrivateTag(_)
| OpaqueRef(_) => false, | OpaqueRef(_) => false,
// These expressions always have newlines // These expressions always have newlines
@ -187,13 +186,73 @@ impl<'a> Formattable for Expr<'a> {
let multiline_args = loc_args.iter().any(|loc_arg| loc_arg.is_multiline()); let multiline_args = loc_args.iter().any(|loc_arg| loc_arg.is_multiline());
if multiline_args { let mut found_multiline_expr = false;
let mut iter = loc_args.iter().peekable();
while let Some(loc_arg) = iter.next() {
if iter.peek().is_none() {
found_multiline_expr = match loc_arg.value {
SpaceBefore(sub_expr, spaces) => match sub_expr {
Record { .. } | List { .. } => {
let is_only_newlines = spaces.iter().all(|s| s.is_newline());
is_only_newlines
&& !found_multiline_expr
&& sub_expr.is_multiline()
}
_ => false,
},
Record { .. } | List { .. } | Closure { .. } => {
!found_multiline_expr && loc_arg.is_multiline()
}
_ => false,
}
} else {
found_multiline_expr = loc_arg.is_multiline();
}
}
let should_outdent_last_arg = found_multiline_expr;
if multiline_args && !should_outdent_last_arg {
let arg_indent = indent + INDENT; let arg_indent = indent + INDENT;
for loc_arg in loc_args.iter() { for loc_arg in loc_args.iter() {
buf.newline(); buf.newline();
loc_arg.format_with_options(buf, Parens::InApply, Newlines::No, arg_indent); loc_arg.format_with_options(buf, Parens::InApply, Newlines::No, arg_indent);
} }
} else if multiline_args && should_outdent_last_arg {
let mut iter = loc_args.iter().peekable();
while let Some(loc_arg) = iter.next() {
buf.spaces(1);
if iter.peek().is_none() {
match loc_arg.value {
SpaceBefore(sub_expr, _) => {
sub_expr.format_with_options(
buf,
Parens::InApply,
Newlines::Yes,
indent,
);
}
_ => {
loc_arg.format_with_options(
buf,
Parens::InApply,
Newlines::Yes,
indent,
);
}
}
} else {
loc_arg.format_with_options(
buf,
Parens::InApply,
Newlines::Yes,
indent,
);
}
}
} else { } else {
for loc_arg in loc_args.iter() { for loc_arg in loc_args.iter() {
buf.spaces(1); buf.spaces(1);
@ -213,7 +272,7 @@ impl<'a> Formattable for Expr<'a> {
buf.indent(indent); buf.indent(indent);
buf.push_str(string); buf.push_str(string);
} }
GlobalTag(string) | PrivateTag(string) | OpaqueRef(string) => { GlobalTag(string) | OpaqueRef(string) => {
buf.indent(indent); buf.indent(indent);
buf.push_str(string) buf.push_str(string)
} }
@ -372,7 +431,6 @@ fn push_op(buf: &mut Buf, op: BinOp) {
called_via::BinOp::Slash => buf.push('/'), called_via::BinOp::Slash => buf.push('/'),
called_via::BinOp::DoubleSlash => buf.push_str("//"), called_via::BinOp::DoubleSlash => buf.push_str("//"),
called_via::BinOp::Percent => buf.push('%'), called_via::BinOp::Percent => buf.push('%'),
called_via::BinOp::DoublePercent => buf.push_str("%%"),
called_via::BinOp::Plus => buf.push('+'), called_via::BinOp::Plus => buf.push('+'),
called_via::BinOp::Minus => buf.push('-'), called_via::BinOp::Minus => buf.push('-'),
called_via::BinOp::Equals => buf.push_str("=="), called_via::BinOp::Equals => buf.push_str("=="),
@ -847,7 +905,34 @@ fn fmt_closure<'a, 'buf>(
} }
}; };
loc_ret.format_with_options(buf, Parens::NotNeeded, Newlines::Yes, body_indent); if is_multiline {
match &loc_ret.value {
SpaceBefore(sub_expr, spaces) => {
let should_outdent = match sub_expr {
Record { .. } | List { .. } => {
let is_only_newlines = spaces.iter().all(|s| s.is_newline());
is_only_newlines && sub_expr.is_multiline()
}
_ => false,
};
if should_outdent {
buf.spaces(1);
sub_expr.format_with_options(buf, Parens::NotNeeded, Newlines::Yes, indent);
} else {
loc_ret.format_with_options(buf, Parens::NotNeeded, Newlines::Yes, body_indent);
}
}
Record { .. } | List { .. } => {
loc_ret.format_with_options(buf, Parens::NotNeeded, Newlines::Yes, indent);
}
_ => {
loc_ret.format_with_options(buf, Parens::NotNeeded, Newlines::Yes, body_indent);
}
}
} else {
loc_ret.format_with_options(buf, Parens::NotNeeded, Newlines::Yes, body_indent);
}
} }
fn fmt_backpassing<'a, 'buf>( fn fmt_backpassing<'a, 'buf>(
@ -1104,7 +1189,6 @@ fn sub_expr_requests_parens(expr: &Expr<'_>) -> bool {
| BinOp::Slash | BinOp::Slash
| BinOp::DoubleSlash | BinOp::DoubleSlash
| BinOp::Percent | BinOp::Percent
| BinOp::DoublePercent
| BinOp::Plus | BinOp::Plus
| BinOp::Minus | BinOp::Minus
| BinOp::Equals | BinOp::Equals

View file

@ -29,7 +29,6 @@ impl<'a> Formattable for Pattern<'a> {
Pattern::Identifier(_) Pattern::Identifier(_)
| Pattern::GlobalTag(_) | Pattern::GlobalTag(_)
| Pattern::PrivateTag(_)
| Pattern::OpaqueRef(_) | Pattern::OpaqueRef(_)
| Pattern::Apply(_, _) | Pattern::Apply(_, _)
| Pattern::NumLiteral(..) | Pattern::NumLiteral(..)
@ -58,7 +57,7 @@ impl<'a> Formattable for Pattern<'a> {
buf.indent(indent); buf.indent(indent);
buf.push_str(string) buf.push_str(string)
} }
GlobalTag(name) | PrivateTag(name) | OpaqueRef(name) => { GlobalTag(name) | OpaqueRef(name) => {
buf.indent(indent); buf.indent(indent);
buf.push_str(name); buf.push_str(name);
} }

View file

@ -626,6 +626,392 @@ mod test_fmt {
)); ));
} }
#[test]
fn lambda_returns_record() {
expr_formats_same(indoc!(
r#"
toRecord = \_ -> {
x: 1,
y: 2,
z: 3,
}
toRecord
"#
));
expr_formats_same(indoc!(
r#"
func = \_ ->
{ x: 1, y: 2, z: 3 }
func
"#
));
expr_formats_same(indoc!(
r#"
toRecord = \_ ->
val = 0
{
x: 1,
y: 2,
z: 3,
}
toRecord
"#
));
expr_formats_to(
indoc!(
r#"
toRecord = \_ ->
{
x: 1,
y: 2,
z: 3,
}
toRecord
"#
),
indoc!(
r#"
toRecord = \_ -> {
x: 1,
y: 2,
z: 3,
}
toRecord
"#
),
);
}
#[test]
fn lambda_returns_list() {
expr_formats_same(indoc!(
r#"
toList = \_ -> [
1,
2,
3,
]
toList
"#
));
expr_formats_same(indoc!(
r#"
func = \_ ->
[ 1, 2, 3 ]
func
"#
));
expr_formats_same(indoc!(
r#"
toList = \_ ->
val = 0
[
1,
2,
3,
]
toList
"#
));
expr_formats_to(
indoc!(
r#"
toList = \_ ->
[
1,
2,
3,
]
toList
"#
),
indoc!(
r#"
toList = \_ -> [
1,
2,
3,
]
toList
"#
),
);
}
#[test]
fn multiline_list_func_arg() {
expr_formats_same(indoc!(
r#"
result = func arg [
1,
2,
3,
]
result
"#
));
expr_formats_to(
indoc!(
r#"
result = func arg
[ 1, 2, 3 ]
result
"#
),
indoc!(
r#"
result = func
arg
[ 1, 2, 3 ]
result
"#
),
);
expr_formats_to(
indoc!(
r#"
result = func arg [
1,
2,
3,
]
result
"#
),
indoc!(
r#"
result = func arg [
1,
2,
3,
]
result
"#
),
);
expr_formats_to(
indoc!(
r#"
result = func [
1,
2,
3,
]
arg
result
"#
),
indoc!(
r#"
result = func
[
1,
2,
3,
]
arg
result
"#
),
);
expr_formats_to(
indoc!(
r#"
result = func arg
[
1,
2,
3,
]
result
"#
),
indoc!(
r#"
result = func arg [
1,
2,
3,
]
result
"#
),
);
expr_formats_same(indoc!(
r#"
result = func
arg
[
1,
2,
3,
]
result
"#
));
}
#[test]
fn multiline_record_func_arg() {
expr_formats_same(indoc!(
r#"
result = func arg {
x: 1,
y: 2,
z: 3,
}
result
"#
));
expr_formats_to(
indoc!(
r#"
result = func arg
{ x: 1, y: 2, z: 3 }
result
"#
),
indoc!(
r#"
result = func
arg
{ x: 1, y: 2, z: 3 }
result
"#
),
);
expr_formats_to(
indoc!(
r#"
result = func arg {
x: 1,
y: 2,
z: 3,
}
result
"#
),
indoc!(
r#"
result = func arg {
x: 1,
y: 2,
z: 3,
}
result
"#
),
);
expr_formats_to(
indoc!(
r#"
result = func {
x: 1,
y: 2,
z: 3,
}
arg
result
"#
),
indoc!(
r#"
result = func
{
x: 1,
y: 2,
z: 3,
}
arg
result
"#
),
);
expr_formats_to(
indoc!(
r#"
result = func arg
{
x: 1,
y: 2,
z: 3,
}
result
"#
),
indoc!(
r#"
result = func arg {
x: 1,
y: 2,
z: 3,
}
result
"#
),
);
expr_formats_same(indoc!(
r#"
result = func
arg
{
x: 1,
y: 2,
z: 3,
}
result
"#
));
}
#[test] #[test]
fn record_updating() { fn record_updating() {
expr_formats_same(indoc!( expr_formats_same(indoc!(
@ -1301,6 +1687,27 @@ mod test_fmt {
fn multi_line_list_def() { fn multi_line_list_def() {
expr_formats_same(indoc!( expr_formats_same(indoc!(
r#" r#"
l = [
1,
2,
]
l
"#
));
expr_formats_same(indoc!(
r#"
l =
[ 1, 2 ]
l
"#
));
expr_formats_to(
indoc!(
r#"
l = l =
[ [
1, 1,
@ -1308,8 +1715,19 @@ mod test_fmt {
] ]
l l
"# "#
)); ),
indoc!(
r#"
l = [
1,
2,
]
l
"#
),
);
expr_formats_to( expr_formats_to(
indoc!( indoc!(
@ -1324,11 +1742,10 @@ mod test_fmt {
), ),
indoc!( indoc!(
r#" r#"
results = results = [
[ Ok 4,
Ok 4, Ok 5,
Ok 5, ]
]
allOks results allOks results
"# "#
@ -1417,18 +1834,69 @@ mod test_fmt {
#[test] #[test]
fn multi_line_record_def() { fn multi_line_record_def() {
expr_formats_same(indoc!(
r#"
pos = {
x: 4,
y: 11,
z: 16,
}
pos
"#
));
expr_formats_same(indoc!( expr_formats_same(indoc!(
r#" r#"
pos = pos =
{ x: 4, y: 11, z: 16 }
pos
"#
));
expr_formats_same(indoc!(
r#"
myDef =
list = [
a,
b,
]
{ {
c,
d,
}
myDef
"#
));
expr_formats_to(
indoc!(
r#"
pos =
{
x: 4,
y: 11,
z: 16,
}
pos
"#
),
indoc!(
r#"
pos = {
x: 4, x: 4,
y: 11, y: 11,
z: 16, z: 16,
} }
pos pos
"# "#
)); ),
);
expr_formats_to( expr_formats_to(
indoc!( indoc!(
@ -1443,11 +1911,10 @@ mod test_fmt {
), ),
indoc!( indoc!(
r#" r#"
pos = pos = {
{ x: 5,
x: 5, y: 10,
y: 10, }
}
pos pos
"# "#
@ -2537,7 +3004,7 @@ mod test_fmt {
indoc!( indoc!(
r#" r#"
2 % 3 2 % 3
%% 5 // 5
+ 7 + 7
"# "#
), ),
@ -2545,7 +3012,7 @@ mod test_fmt {
r#" r#"
2 2
% 3 % 3
%% 5 // 5
+ 7 + 7
"# "#
), ),
@ -2619,6 +3086,18 @@ mod test_fmt {
)); ));
} }
#[test]
fn func_call_trailing_multiline_lambda() {
expr_formats_same(indoc!(
r#"
list = List.map [ 1, 2, 3 ] \x ->
x + 1
list
"#
));
}
// MODULES // MODULES
#[test] #[test]

View file

@ -19,7 +19,7 @@ roc_mono = { path = "../mono" }
roc_target = { path = "../roc_target" } roc_target = { path = "../roc_target" }
roc_error_macros = { path = "../../error_macros" } roc_error_macros = { path = "../../error_macros" }
bumpalo = { version = "3.8.0", features = ["collections"] } bumpalo = { version = "3.8.0", features = ["collections"] }
target-lexicon = "0.12.2" target-lexicon = "0.12.3"
# TODO: Deal with the update of object to 0.27. # TODO: Deal with the update of object to 0.27.
# It looks like it breaks linking the generated objects. # It looks like it breaks linking the generated objects.
# Probably just need to specify an extra field that used to be implicit or something. # Probably just need to specify an extra field that used to be implicit or something.

View file

@ -913,9 +913,6 @@ trait Backend<'a> {
TagName::Closure(sym) => { TagName::Closure(sym) => {
self.set_last_seen(*sym, stmt); self.set_last_seen(*sym, stmt);
} }
TagName::Private(sym) => {
self.set_last_seen(*sym, stmt);
}
TagName::Global(_) => {} TagName::Global(_) => {}
} }
for sym in *arguments { for sym in *arguments {

View file

@ -18,4 +18,4 @@ roc_std = { path = "../../roc_std", default-features = false }
morphic_lib = { path = "../../vendor/morphic_lib" } morphic_lib = { path = "../../vendor/morphic_lib" }
bumpalo = { version = "3.8.0", features = ["collections"] } bumpalo = { version = "3.8.0", features = ["collections"] }
inkwell = { path = "../../vendor/inkwell" } inkwell = { path = "../../vendor/inkwell" }
target-lexicon = "0.12.2" target-lexicon = "0.12.3"

View file

@ -6915,7 +6915,6 @@ fn build_float_binop<'a, 'ctx, 'env>(
NumGte => bd.build_float_compare(OGE, lhs, rhs, "float_gte").into(), NumGte => bd.build_float_compare(OGE, lhs, rhs, "float_gte").into(),
NumLt => bd.build_float_compare(OLT, lhs, rhs, "float_lt").into(), NumLt => bd.build_float_compare(OLT, lhs, rhs, "float_lt").into(),
NumLte => bd.build_float_compare(OLE, lhs, rhs, "float_lte").into(), NumLte => bd.build_float_compare(OLE, lhs, rhs, "float_lte").into(),
NumRemUnchecked => bd.build_float_rem(lhs, rhs, "rem_float").into(),
NumDivUnchecked => bd.build_float_div(lhs, rhs, "div_float").into(), NumDivUnchecked => bd.build_float_div(lhs, rhs, "div_float").into(),
NumPow => env.call_intrinsic(&LLVM_POW[float_width], &[lhs.into(), rhs.into()]), NumPow => env.call_intrinsic(&LLVM_POW[float_width], &[lhs.into(), rhs.into()]),
_ => { _ => {

View file

@ -1809,7 +1809,39 @@ fn modify_refcount_union_help<'a, 'ctx, 'env>(
for (i, field_layout) in field_layouts.iter().enumerate() { for (i, field_layout) in field_layouts.iter().enumerate() {
if let Layout::RecursivePointer = field_layout { if let Layout::RecursivePointer = field_layout {
panic!("non-recursive tag unions cannot contain naked recursion pointers!"); let recursive_union_layout = match when_recursive {
WhenRecursive::Unreachable => {
panic!("non-recursive tag unions cannot contain naked recursion pointers!");
}
WhenRecursive::Loop(recursive_union_layout) => recursive_union_layout,
};
// This field is a pointer to the recursive pointer.
let field_ptr = env
.builder
.build_struct_gep(cast_tag_data_pointer, i as u32, "modify_tag_field")
.unwrap();
// This is the actual pointer to the recursive data.
let field_value = env.builder.build_load(field_ptr, "load_recursive_pointer");
debug_assert!(field_value.is_pointer_value());
// therefore we must cast it to our desired type
let union_type =
basic_type_from_layout(env, &Layout::Union(*recursive_union_layout));
let recursive_ptr_field_value =
cast_basic_basic(env.builder, field_value, union_type);
modify_refcount_layout_help(
env,
parent,
layout_ids,
mode.to_call_mode(fn_val),
when_recursive,
recursive_ptr_field_value,
&Layout::RecursivePointer,
)
} else if field_layout.contains_refcounted() { } else if field_layout.contains_refcounted() {
let field_ptr = env let field_ptr = env
.builder .builder

View file

@ -1,7 +1,5 @@
use crate::docs::DocEntry::DetachedDoc; use crate::docs::DocEntry::DetachedDoc;
use crate::docs::TypeAnnotation::{ use crate::docs::TypeAnnotation::{Apply, BoundVariable, Function, NoTypeAnn, Record, TagUnion};
Apply, BoundVariable, Function, NoTypeAnn, ObscuredRecord, ObscuredTagUnion, Record, TagUnion,
};
use crate::file::LoadedModule; use crate::file::LoadedModule;
use roc_can::scope::Scope; use roc_can::scope::Scope;
use roc_error_macros::todo_abilities; use roc_error_macros::todo_abilities;
@ -274,36 +272,20 @@ fn type_to_docs(in_func_type_ann: bool, type_annotation: ast::TypeAnnotation) ->
ast::TypeAnnotation::TagUnion { tags, ext } => { ast::TypeAnnotation::TagUnion { tags, ext } => {
let mut tags_to_render: Vec<Tag> = Vec::new(); let mut tags_to_render: Vec<Tag> = Vec::new();
let mut any_tags_are_private = false;
for tag in tags.iter() { for tag in tags.iter() {
match tag_to_doc(in_func_type_ann, tag.value) { if let Some(tag_ann) = tag_to_doc(in_func_type_ann, tag.value) {
None => { tags_to_render.push(tag_ann);
any_tags_are_private = true;
break;
}
Some(tag_ann) => {
tags_to_render.push(tag_ann);
}
} }
} }
if any_tags_are_private { let extension = match ext {
if in_func_type_ann { None => NoTypeAnn,
ObscuredTagUnion Some(ext_type_ann) => type_to_docs(in_func_type_ann, ext_type_ann.value),
} else { };
NoTypeAnn
}
} else {
let extension = match ext {
None => NoTypeAnn,
Some(ext_type_ann) => type_to_docs(in_func_type_ann, ext_type_ann.value),
};
TagUnion { TagUnion {
tags: tags_to_render, tags: tags_to_render,
extension: Box::new(extension), extension: Box::new(extension),
}
} }
} }
ast::TypeAnnotation::BoundVariable(var_name) => BoundVariable(var_name.to_string()), ast::TypeAnnotation::BoundVariable(var_name) => BoundVariable(var_name.to_string()),
@ -328,35 +310,19 @@ fn type_to_docs(in_func_type_ann: bool, type_annotation: ast::TypeAnnotation) ->
ast::TypeAnnotation::Record { fields, ext } => { ast::TypeAnnotation::Record { fields, ext } => {
let mut doc_fields = Vec::new(); let mut doc_fields = Vec::new();
let mut any_fields_include_private_tags = false;
for field in fields.items { for field in fields.items {
match record_field_to_doc(in_func_type_ann, field.value) { if let Some(doc_field) = record_field_to_doc(in_func_type_ann, field.value) {
None => { doc_fields.push(doc_field);
any_fields_include_private_tags = true;
break;
}
Some(doc_field) => {
doc_fields.push(doc_field);
}
} }
} }
if any_fields_include_private_tags { let extension = match ext {
if in_func_type_ann { None => NoTypeAnn,
ObscuredRecord Some(ext_type_ann) => type_to_docs(in_func_type_ann, ext_type_ann.value),
} else { };
NoTypeAnn
}
} else {
let extension = match ext {
None => NoTypeAnn,
Some(ext_type_ann) => type_to_docs(in_func_type_ann, ext_type_ann.value),
};
Record { Record {
fields: doc_fields, fields: doc_fields,
extension: Box::new(extension), extension: Box::new(extension),
}
} }
} }
ast::TypeAnnotation::SpaceBefore(&sub_type_ann, _) => { ast::TypeAnnotation::SpaceBefore(&sub_type_ann, _) => {
@ -404,8 +370,7 @@ fn record_field_to_doc(
} }
} }
// The Option here represents if it is private. Private tags // The Option here represents if it is malformed.
// evaluate to `None`.
fn tag_to_doc(in_func_ann: bool, tag: ast::Tag) -> Option<Tag> { fn tag_to_doc(in_func_ann: bool, tag: ast::Tag) -> Option<Tag> {
match tag { match tag {
ast::Tag::Global { name, args } => Some(Tag { ast::Tag::Global { name, args } => Some(Tag {
@ -420,7 +385,6 @@ fn tag_to_doc(in_func_ann: bool, tag: ast::Tag) -> Option<Tag> {
type_vars type_vars
}, },
}), }),
ast::Tag::Private { .. } => None,
ast::Tag::SpaceBefore(&sub_tag, _) => tag_to_doc(in_func_ann, sub_tag), ast::Tag::SpaceBefore(&sub_tag, _) => tag_to_doc(in_func_ann, sub_tag),
ast::Tag::SpaceAfter(&sub_tag, _) => tag_to_doc(in_func_ann, sub_tag), ast::Tag::SpaceAfter(&sub_tag, _) => tag_to_doc(in_func_ann, sub_tag),
ast::Tag::Malformed(_) => None, ast::Tag::Malformed(_) => None,

View file

@ -39,7 +39,7 @@ use roc_solve::solve;
use roc_target::TargetInfo; use roc_target::TargetInfo;
use roc_types::solved_types::Solved; use roc_types::solved_types::Solved;
use roc_types::subs::{Subs, VarStore, Variable}; use roc_types::subs::{Subs, VarStore, Variable};
use roc_types::types::{Alias, AliasCommon, TypeExtension}; use roc_types::types::{Alias, AliasCommon, AliasKind, TypeExtension};
use std::collections::hash_map::Entry::{Occupied, Vacant}; use std::collections::hash_map::Entry::{Occupied, Vacant};
use std::collections::HashMap; use std::collections::HashMap;
use std::io; use std::io;
@ -4715,50 +4715,35 @@ fn default_aliases() -> roc_solve::solve::Aliases {
let mut var_store = VarStore::default(); let mut var_store = VarStore::default();
// Num range := range
{ {
let symbol = Symbol::NUM_NUM; let symbol = Symbol::NUM_NUM;
let tvar = var_store.fresh(); let tvar = var_store.fresh();
let typ = Type::TagUnion(
vec![(
TagName::Private(Symbol::NUM_AT_NUM),
vec![Type::Variable(tvar)],
)],
TypeExtension::Closed,
);
let alias = Alias { let alias = Alias {
region: Region::zero(), region: Region::zero(),
type_variables: vec![Loc::at_zero(("range".into(), tvar))], type_variables: vec![Loc::at_zero(("range".into(), tvar))],
lambda_set_variables: Default::default(), lambda_set_variables: Default::default(),
recursion_variables: Default::default(), recursion_variables: Default::default(),
typ, typ: Type::Variable(tvar),
kind: roc_types::types::AliasKind::Structural, kind: roc_types::types::AliasKind::Structural,
}; };
solve_aliases.insert(symbol, alias); solve_aliases.insert(symbol, alias);
} }
// FloatingPoint range : [ @FloatingPoint range ] // FloatingPoint range := []
{ {
let symbol = Symbol::NUM_FLOATINGPOINT; let symbol = Symbol::NUM_FLOATINGPOINT;
let tvar = var_store.fresh(); let tvar = var_store.fresh();
let typ = Type::TagUnion(
vec![(
TagName::Private(Symbol::NUM_AT_FLOATINGPOINT),
vec![Type::Variable(tvar)],
)],
TypeExtension::Closed,
);
let alias = Alias { let alias = Alias {
region: Region::zero(), region: Region::zero(),
type_variables: vec![Loc::at_zero(("range".into(), tvar))], type_variables: vec![Loc::at_zero(("range".into(), tvar))],
lambda_set_variables: Default::default(), lambda_set_variables: Default::default(),
recursion_variables: Default::default(), recursion_variables: Default::default(),
typ, typ: Type::Variable(tvar),
kind: roc_types::types::AliasKind::Structural, kind: roc_types::types::AliasKind::Opaque,
}; };
solve_aliases.insert(symbol, alias); solve_aliases.insert(symbol, alias);
@ -4773,11 +4758,13 @@ fn default_aliases() -> roc_solve::solve::Aliases {
symbol: Symbol::NUM_NUM, symbol: Symbol::NUM_NUM,
type_arguments: vec![( type_arguments: vec![(
"range".into(), "range".into(),
Type::DelayedAlias(AliasCommon { Type::Alias {
symbol: Symbol::NUM_INTEGER, symbol: Symbol::NUM_INTEGER,
type_arguments: vec![("range".into(), Type::Variable(tvar))], type_arguments: vec![("range".into(), Type::Variable(tvar))],
lambda_set_variables: vec![], lambda_set_variables: vec![],
}), actual: Box::new(Type::Variable(tvar)),
kind: AliasKind::Opaque,
},
)], )],
lambda_set_variables: vec![], lambda_set_variables: vec![],
}); });
@ -4794,6 +4781,7 @@ fn default_aliases() -> roc_solve::solve::Aliases {
solve_aliases.insert(symbol, alias); solve_aliases.insert(symbol, alias);
} }
// Float range : Num (FloatingPoint range)
{ {
let symbol = Symbol::NUM_FLOAT; let symbol = Symbol::NUM_FLOAT;
let tvar = var_store.fresh(); let tvar = var_store.fresh();
@ -4802,11 +4790,13 @@ fn default_aliases() -> roc_solve::solve::Aliases {
symbol: Symbol::NUM_NUM, symbol: Symbol::NUM_NUM,
type_arguments: vec![( type_arguments: vec![(
"range".into(), "range".into(),
Type::DelayedAlias(AliasCommon { Type::Alias {
symbol: Symbol::NUM_FLOATINGPOINT, symbol: Symbol::NUM_FLOATINGPOINT,
type_arguments: vec![("range".into(), Type::Variable(tvar))], type_arguments: vec![("range".into(), Type::Variable(tvar))],
lambda_set_variables: vec![], lambda_set_variables: vec![],
}), actual: Box::new(Type::Variable(tvar)),
kind: AliasKind::Opaque,
},
)], )],
lambda_set_variables: vec![], lambda_set_variables: vec![],
}); });
@ -4823,24 +4813,17 @@ fn default_aliases() -> roc_solve::solve::Aliases {
solve_aliases.insert(symbol, alias); solve_aliases.insert(symbol, alias);
} }
// Integer range := range
{ {
let symbol = Symbol::NUM_INTEGER; let symbol = Symbol::NUM_INTEGER;
let tvar = var_store.fresh(); let tvar = var_store.fresh();
let typ = Type::TagUnion(
vec![(
TagName::Private(Symbol::NUM_AT_INTEGER),
vec![Type::Variable(tvar)],
)],
TypeExtension::Closed,
);
let alias = Alias { let alias = Alias {
region: Region::zero(), region: Region::zero(),
type_variables: vec![Loc::at_zero(("range".into(), tvar))], type_variables: vec![Loc::at_zero(("range".into(), tvar))],
lambda_set_variables: Default::default(), lambda_set_variables: Default::default(),
recursion_variables: Default::default(), recursion_variables: Default::default(),
typ, typ: Type::Variable(tvar),
kind: roc_types::types::AliasKind::Structural, kind: roc_types::types::AliasKind::Structural,
}; };
@ -4875,38 +4858,33 @@ fn default_aliases() -> roc_solve::solve::Aliases {
solve_aliases.insert(symbol, alias); solve_aliases.insert(symbol, alias);
} }
let mut unit_function = |alias_name: Symbol, at_tag_name: Symbol| { let mut zero_opaque = |alias_name: Symbol| {
let typ = Type::TagUnion(
vec![(TagName::Private(at_tag_name), vec![])],
TypeExtension::Closed,
);
let alias = Alias { let alias = Alias {
region: Region::zero(), region: Region::zero(),
type_variables: vec![], type_variables: vec![],
lambda_set_variables: Default::default(), lambda_set_variables: Default::default(),
recursion_variables: Default::default(), recursion_variables: Default::default(),
typ, typ: Type::EmptyTagUnion,
kind: roc_types::types::AliasKind::Structural, kind: AliasKind::Opaque,
}; };
solve_aliases.insert(alias_name, alias); solve_aliases.insert(alias_name, alias);
}; };
unit_function(Symbol::NUM_SIGNED8, Symbol::NUM_AT_SIGNED8); zero_opaque(Symbol::NUM_SIGNED8);
unit_function(Symbol::NUM_SIGNED16, Symbol::NUM_AT_SIGNED16); zero_opaque(Symbol::NUM_SIGNED16);
unit_function(Symbol::NUM_SIGNED32, Symbol::NUM_AT_SIGNED32); zero_opaque(Symbol::NUM_SIGNED32);
unit_function(Symbol::NUM_SIGNED64, Symbol::NUM_AT_SIGNED64); zero_opaque(Symbol::NUM_SIGNED64);
unit_function(Symbol::NUM_SIGNED128, Symbol::NUM_AT_SIGNED128); zero_opaque(Symbol::NUM_SIGNED128);
unit_function(Symbol::NUM_UNSIGNED8, Symbol::NUM_AT_UNSIGNED8); zero_opaque(Symbol::NUM_UNSIGNED8);
unit_function(Symbol::NUM_UNSIGNED16, Symbol::NUM_AT_UNSIGNED16); zero_opaque(Symbol::NUM_UNSIGNED16);
unit_function(Symbol::NUM_UNSIGNED32, Symbol::NUM_AT_UNSIGNED32); zero_opaque(Symbol::NUM_UNSIGNED32);
unit_function(Symbol::NUM_UNSIGNED64, Symbol::NUM_AT_UNSIGNED64); zero_opaque(Symbol::NUM_UNSIGNED64);
unit_function(Symbol::NUM_UNSIGNED128, Symbol::NUM_AT_UNSIGNED128); zero_opaque(Symbol::NUM_UNSIGNED128);
unit_function(Symbol::NUM_BINARY32, Symbol::NUM_AT_BINARY32); zero_opaque(Symbol::NUM_BINARY32);
unit_function(Symbol::NUM_BINARY64, Symbol::NUM_AT_BINARY64); zero_opaque(Symbol::NUM_BINARY64);
solve_aliases solve_aliases
} }

View file

@ -757,9 +757,9 @@ mod test_load {
r#" r#"
interface Main exposes [ twenty, readAge ] imports [ Age.{ Age } ] interface Main exposes [ twenty, readAge ] imports [ Age.{ Age } ]
twenty = $Age 20 twenty = @Age 20
readAge = \$Age n -> n readAge = \@Age n -> n
"# "#
), ),
), ),
@ -775,7 +775,7 @@ mod test_load {
The unwrapped opaque type Age referenced here: The unwrapped opaque type Age referenced here:
3 twenty = $Age 20 3 twenty = @Age 20
^^^^ ^^^^
is imported from another module: is imported from another module:
@ -789,7 +789,7 @@ mod test_load {
The unwrapped opaque type Age referenced here: The unwrapped opaque type Age referenced here:
5 readAge = \$Age n -> n 5 readAge = \@Age n -> n
^^^^ ^^^^
is imported from another module: is imported from another module:

View file

@ -34,7 +34,6 @@ pub enum BinOp {
Slash, Slash,
DoubleSlash, DoubleSlash,
Percent, Percent,
DoublePercent,
Plus, Plus,
Minus, Minus,
Equals, Equals,
@ -58,8 +57,8 @@ impl BinOp {
pub fn width(self) -> u16 { pub fn width(self) -> u16 {
match self { match self {
Caret | Star | Slash | Percent | Plus | Minus | LessThan | GreaterThan => 1, Caret | Star | Slash | Percent | Plus | Minus | LessThan | GreaterThan => 1,
DoubleSlash | DoublePercent | Equals | NotEquals | LessThanOrEq | GreaterThanOrEq DoubleSlash | Equals | NotEquals | LessThanOrEq | GreaterThanOrEq | And | Or
| And | Or | Pizza => 2, | Pizza => 2,
Assignment | IsAliasType | IsOpaqueType | Backpassing => unreachable!(), Assignment | IsAliasType | IsOpaqueType | Backpassing => unreachable!(),
} }
} }
@ -97,9 +96,7 @@ impl BinOp {
use self::Associativity::*; use self::Associativity::*;
match self { match self {
Pizza | Star | Slash | DoubleSlash | DoublePercent | Percent | Plus | Minus => { Pizza | Star | Slash | DoubleSlash | Percent | Plus | Minus => LeftAssociative,
LeftAssociative
}
And | Or | Caret => RightAssociative, And | Or | Caret => RightAssociative,
Equals | NotEquals | LessThan | GreaterThan | LessThanOrEq | GreaterThanOrEq => { Equals | NotEquals | LessThan | GreaterThan | LessThanOrEq | GreaterThanOrEq => {
NonAssociative NonAssociative
@ -111,7 +108,7 @@ impl BinOp {
fn precedence(self) -> u8 { fn precedence(self) -> u8 {
match self { match self {
Caret => 7, Caret => 7,
Star | Slash | DoubleSlash | DoublePercent | Percent => 6, Star | Slash | DoubleSlash | Percent => 6,
Plus | Minus => 5, Plus | Minus => 5,
Equals | NotEquals | LessThan | GreaterThan | LessThanOrEq | GreaterThanOrEq => 4, Equals | NotEquals | LessThan | GreaterThan | LessThanOrEq | GreaterThanOrEq => 4,
And => 3, And => 3,
@ -142,7 +139,6 @@ impl std::fmt::Display for BinOp {
Slash => "/", Slash => "/",
DoubleSlash => "//", DoubleSlash => "//",
Percent => "%", Percent => "%",
DoublePercent => "%%",
Plus => "+", Plus => "+",
Minus => "-", Minus => "-",
Equals => "==", Equals => "==",

View file

@ -53,10 +53,6 @@ pub enum TagName {
/// into integers. (Record field labels work the same way, for the same reason.) /// into integers. (Record field labels work the same way, for the same reason.)
Global(Uppercase), Global(Uppercase),
/// Private tags are associated with a specific module, and as such use a
/// Symbol just like all other module-specific identifiers.
Private(Symbol),
/// Used to connect the closure size to the function it corresponds to /// Used to connect the closure size to the function it corresponds to
Closure(Symbol), Closure(Symbol),
} }
@ -69,9 +65,6 @@ impl TagName {
pub fn as_ident_str(&self, interns: &Interns, home: ModuleId) -> IdentStr { pub fn as_ident_str(&self, interns: &Interns, home: ModuleId) -> IdentStr {
match self { match self {
TagName::Global(uppercase) => uppercase.as_ident_str().clone(), TagName::Global(uppercase) => uppercase.as_ident_str().clone(),
TagName::Private(symbol) => {
symbol.fully_qualified(interns, home).as_ident_str().clone()
}
TagName::Closure(symbol) => { TagName::Closure(symbol) => {
symbol.fully_qualified(interns, home).as_ident_str().clone() symbol.fully_qualified(interns, home).as_ident_str().clone()
} }

View file

@ -294,14 +294,17 @@ impl LowLevelWrapperType {
Symbol::NUM_DIV_FLOAT_CHECKED => WrapperIsRequired, Symbol::NUM_DIV_FLOAT_CHECKED => WrapperIsRequired,
Symbol::NUM_DIV_CEIL => CanBeReplacedBy(NumDivCeilUnchecked), Symbol::NUM_DIV_CEIL => CanBeReplacedBy(NumDivCeilUnchecked),
Symbol::NUM_DIV_CEIL_CHECKED => WrapperIsRequired, Symbol::NUM_DIV_CEIL_CHECKED => WrapperIsRequired,
Symbol::NUM_REM => WrapperIsRequired, Symbol::NUM_REM => CanBeReplacedBy(NumRemUnchecked),
Symbol::NUM_REM_CHECKED => WrapperIsRequired,
Symbol::NUM_IS_MULTIPLE_OF => CanBeReplacedBy(NumIsMultipleOf), Symbol::NUM_IS_MULTIPLE_OF => CanBeReplacedBy(NumIsMultipleOf),
Symbol::NUM_ABS => CanBeReplacedBy(NumAbs), Symbol::NUM_ABS => CanBeReplacedBy(NumAbs),
Symbol::NUM_NEG => CanBeReplacedBy(NumNeg), Symbol::NUM_NEG => CanBeReplacedBy(NumNeg),
Symbol::NUM_SIN => CanBeReplacedBy(NumSin), Symbol::NUM_SIN => CanBeReplacedBy(NumSin),
Symbol::NUM_COS => CanBeReplacedBy(NumCos), Symbol::NUM_COS => CanBeReplacedBy(NumCos),
Symbol::NUM_SQRT => WrapperIsRequired, Symbol::NUM_SQRT => CanBeReplacedBy(NumSqrtUnchecked),
Symbol::NUM_LOG => WrapperIsRequired, Symbol::NUM_SQRT_CHECKED => WrapperIsRequired,
Symbol::NUM_LOG => CanBeReplacedBy(NumLogUnchecked),
Symbol::NUM_LOG_CHECKED => WrapperIsRequired,
Symbol::NUM_ROUND => CanBeReplacedBy(NumRound), Symbol::NUM_ROUND => CanBeReplacedBy(NumRound),
Symbol::NUM_TO_FLOAT => CanBeReplacedBy(NumToFloat), Symbol::NUM_TO_FLOAT => CanBeReplacedBy(NumToFloat),
Symbol::NUM_POW => CanBeReplacedBy(NumPow), Symbol::NUM_POW => CanBeReplacedBy(NumPow),

View file

@ -907,164 +907,143 @@ define_builtins! {
} }
1 NUM: "Num" => { 1 NUM: "Num" => {
0 NUM_NUM: "Num" // the Num.Num type alias 0 NUM_NUM: "Num" // the Num.Num type alias
1 NUM_AT_NUM: "@Num" // the Num.@Num private tag 1 NUM_I128: "I128" // the Num.I128 type alias
2 NUM_I128: "I128" // the Num.I128 type alias 2 NUM_U128: "U128" // the Num.U128 type alias
3 NUM_U128: "U128" // the Num.U128 type alias 3 NUM_I64: "I64" // the Num.I64 type alias
4 NUM_I64: "I64" // the Num.I64 type alias 4 NUM_U64: "U64" // the Num.U64 type alias
5 NUM_U64: "U64" // the Num.U64 type alias 5 NUM_I32: "I32" // the Num.I32 type alias
6 NUM_I32: "I32" // the Num.I32 type alias 6 NUM_U32: "U32" // the Num.U32 type alias
7 NUM_U32: "U32" // the Num.U32 type alias 7 NUM_I16: "I16" // the Num.I16 type alias
8 NUM_I16: "I16" // the Num.I16 type alias 8 NUM_U16: "U16" // the Num.U16 type alias
9 NUM_U16: "U16" // the Num.U16 type alias 9 NUM_I8: "I8" // the Num.I8 type alias
10 NUM_I8: "I8" // the Num.I8 type alias 10 NUM_U8: "U8" // the Num.U8 type alias
11 NUM_U8: "U8" // the Num.U8 type alias 11 NUM_INTEGER: "Integer" // Int : Num Integer
12 NUM_INTEGER: "Integer" // Int : Num Integer 12 NUM_F64: "F64" // the Num.F64 type alias
13 NUM_AT_INTEGER: "@Integer" // the Int.@Integer private tag 13 NUM_F32: "F32" // the Num.F32 type alias
14 NUM_F64: "F64" // the Num.F64 type alias 14 NUM_FLOATINGPOINT: "FloatingPoint" // Float : Num FloatingPoint
15 NUM_F32: "F32" // the Num.F32 type alias 15 NUM_MAX_FLOAT: "maxFloat"
16 NUM_FLOATINGPOINT: "FloatingPoint" // Float : Num FloatingPoint 16 NUM_MIN_FLOAT: "minFloat"
17 NUM_AT_FLOATINGPOINT: "@FloatingPoint" // the Float.@FloatingPoint private tag 17 NUM_ABS: "abs"
18 NUM_MAX_FLOAT: "maxFloat" 18 NUM_NEG: "neg"
19 NUM_MIN_FLOAT: "minFloat" 19 NUM_ADD: "add"
20 NUM_ABS: "abs" 20 NUM_SUB: "sub"
21 NUM_NEG: "neg" 21 NUM_MUL: "mul"
22 NUM_ADD: "add" 22 NUM_LT: "isLt"
23 NUM_SUB: "sub" 23 NUM_LTE: "isLte"
24 NUM_MUL: "mul" 24 NUM_GT: "isGt"
25 NUM_LT: "isLt" 25 NUM_GTE: "isGte"
26 NUM_LTE: "isLte" 26 NUM_TO_FLOAT: "toFloat"
27 NUM_GT: "isGt" 27 NUM_SIN: "sin"
28 NUM_GTE: "isGte" 28 NUM_COS: "cos"
29 NUM_TO_FLOAT: "toFloat" 29 NUM_TAN: "tan"
30 NUM_SIN: "sin" 30 NUM_IS_ZERO: "isZero"
31 NUM_COS: "cos" 31 NUM_IS_EVEN: "isEven"
32 NUM_TAN: "tan" 32 NUM_IS_ODD: "isOdd"
33 NUM_IS_ZERO: "isZero" 33 NUM_IS_POSITIVE: "isPositive"
34 NUM_IS_EVEN: "isEven" 34 NUM_IS_NEGATIVE: "isNegative"
35 NUM_IS_ODD: "isOdd" 35 NUM_REM: "rem"
36 NUM_IS_POSITIVE: "isPositive" 36 NUM_REM_CHECKED: "remChecked"
37 NUM_IS_NEGATIVE: "isNegative" 37 NUM_DIV_FLOAT: "div"
38 NUM_REM: "rem" 38 NUM_DIV_FLOAT_CHECKED: "divChecked"
39 NUM_REM_CHECKED: "remChecked" 39 NUM_DIV_TRUNC: "divTrunc"
40 NUM_DIV_FLOAT: "div" 40 NUM_DIV_TRUNC_CHECKED: "divTruncChecked"
41 NUM_DIV_FLOAT_CHECKED: "divChecked" 41 NUM_SQRT: "sqrt"
42 NUM_DIV_TRUNC: "divTrunc" 42 NUM_SQRT_CHECKED: "sqrtChecked"
43 NUM_DIV_TRUNC_CHECKED: "divTruncChecked" 43 NUM_LOG: "log"
44 NUM_MOD_INT: "modInt" 44 NUM_LOG_CHECKED: "logChecked"
45 NUM_MOD_INT_CHECKED: "modIntChecked" 45 NUM_ROUND: "round"
46 NUM_MOD_FLOAT: "modFloat" 46 NUM_COMPARE: "compare"
47 NUM_MOD_FLOAT_CHECKED: "modFloatChecked" 47 NUM_POW: "pow"
48 NUM_SQRT: "sqrt" 48 NUM_CEILING: "ceiling"
49 NUM_SQRT_CHECKED: "sqrtChecked" 49 NUM_POW_INT: "powInt"
50 NUM_LOG: "log" 50 NUM_FLOOR: "floor"
51 NUM_LOG_CHECKED: "logChecked" 51 NUM_ADD_WRAP: "addWrap"
52 NUM_ROUND: "round" 52 NUM_ADD_CHECKED: "addChecked"
53 NUM_COMPARE: "compare" 53 NUM_ADD_SATURATED: "addSaturated"
54 NUM_POW: "pow" 54 NUM_ATAN: "atan"
55 NUM_CEILING: "ceiling" 55 NUM_ACOS: "acos"
56 NUM_POW_INT: "powInt" 56 NUM_ASIN: "asin"
57 NUM_FLOOR: "floor" 57 NUM_SIGNED128: "Signed128"
58 NUM_ADD_WRAP: "addWrap" 58 NUM_SIGNED64: "Signed64"
59 NUM_ADD_CHECKED: "addChecked" 59 NUM_SIGNED32: "Signed32"
60 NUM_ADD_SATURATED: "addSaturated" 60 NUM_SIGNED16: "Signed16"
61 NUM_ATAN: "atan" 61 NUM_SIGNED8: "Signed8"
62 NUM_ACOS: "acos" 62 NUM_UNSIGNED128: "Unsigned128"
63 NUM_ASIN: "asin" 63 NUM_UNSIGNED64: "Unsigned64"
64 NUM_AT_SIGNED128: "@Signed128" 64 NUM_UNSIGNED32: "Unsigned32"
65 NUM_SIGNED128: "Signed128" 65 NUM_UNSIGNED16: "Unsigned16"
66 NUM_AT_SIGNED64: "@Signed64" 66 NUM_UNSIGNED8: "Unsigned8"
67 NUM_SIGNED64: "Signed64" 67 NUM_BINARY64: "Binary64"
68 NUM_AT_SIGNED32: "@Signed32" 68 NUM_BINARY32: "Binary32"
69 NUM_SIGNED32: "Signed32" 69 NUM_BITWISE_AND: "bitwiseAnd"
70 NUM_AT_SIGNED16: "@Signed16" 70 NUM_BITWISE_XOR: "bitwiseXor"
71 NUM_SIGNED16: "Signed16" 71 NUM_BITWISE_OR: "bitwiseOr"
72 NUM_AT_SIGNED8: "@Signed8" 72 NUM_SHIFT_LEFT: "shiftLeftBy"
73 NUM_SIGNED8: "Signed8" 73 NUM_SHIFT_RIGHT: "shiftRightBy"
74 NUM_AT_UNSIGNED128: "@Unsigned128" 74 NUM_SHIFT_RIGHT_ZERO_FILL: "shiftRightZfBy"
75 NUM_UNSIGNED128: "Unsigned128" 75 NUM_SUB_WRAP: "subWrap"
76 NUM_AT_UNSIGNED64: "@Unsigned64" 76 NUM_SUB_CHECKED: "subChecked"
77 NUM_UNSIGNED64: "Unsigned64" 77 NUM_SUB_SATURATED: "subSaturated"
78 NUM_AT_UNSIGNED32: "@Unsigned32" 78 NUM_MUL_WRAP: "mulWrap"
79 NUM_UNSIGNED32: "Unsigned32" 79 NUM_MUL_CHECKED: "mulChecked"
80 NUM_AT_UNSIGNED16: "@Unsigned16" 80 NUM_INT: "Int"
81 NUM_UNSIGNED16: "Unsigned16" 81 NUM_FLOAT: "Float"
82 NUM_AT_UNSIGNED8: "@Unsigned8" 82 NUM_NATURAL: "Natural"
83 NUM_UNSIGNED8: "Unsigned8" 83 NUM_NAT: "Nat"
84 NUM_AT_BINARY64: "@Binary64" 84 NUM_INT_CAST: "intCast"
85 NUM_BINARY64: "Binary64" 85 NUM_IS_MULTIPLE_OF: "isMultipleOf"
86 NUM_AT_BINARY32: "@Binary32" 86 NUM_DECIMAL: "Decimal"
87 NUM_BINARY32: "Binary32" 87 NUM_DEC: "Dec" // the Num.Dectype alias
88 NUM_BITWISE_AND: "bitwiseAnd" 88 NUM_BYTES_TO_U16: "bytesToU16"
89 NUM_BITWISE_XOR: "bitwiseXor" 89 NUM_BYTES_TO_U32: "bytesToU32"
90 NUM_BITWISE_OR: "bitwiseOr" 90 NUM_CAST_TO_NAT: "#castToNat"
91 NUM_SHIFT_LEFT: "shiftLeftBy" 91 NUM_DIV_CEIL: "divCeil"
92 NUM_SHIFT_RIGHT: "shiftRightBy" 92 NUM_DIV_CEIL_CHECKED: "divCeilChecked"
93 NUM_SHIFT_RIGHT_ZERO_FILL: "shiftRightZfBy" 93 NUM_TO_STR: "toStr"
94 NUM_SUB_WRAP: "subWrap" 94 NUM_MIN_I8: "minI8"
95 NUM_SUB_CHECKED: "subChecked" 95 NUM_MAX_I8: "maxI8"
96 NUM_SUB_SATURATED: "subSaturated" 96 NUM_MIN_U8: "minU8"
97 NUM_MUL_WRAP: "mulWrap" 97 NUM_MAX_U8: "maxU8"
98 NUM_MUL_CHECKED: "mulChecked" 98 NUM_MIN_I16: "minI16"
99 NUM_INT: "Int" 99 NUM_MAX_I16: "maxI16"
100 NUM_FLOAT: "Float" 100 NUM_MIN_U16: "minU16"
101 NUM_AT_NATURAL: "@Natural" 101 NUM_MAX_U16: "maxU16"
102 NUM_NATURAL: "Natural" 102 NUM_MIN_I32: "minI32"
103 NUM_NAT: "Nat" 103 NUM_MAX_I32: "maxI32"
104 NUM_INT_CAST: "intCast" 104 NUM_MIN_U32: "minU32"
105 NUM_IS_MULTIPLE_OF: "isMultipleOf" 105 NUM_MAX_U32: "maxU32"
106 NUM_AT_DECIMAL: "@Decimal" 106 NUM_MIN_I64: "minI64"
107 NUM_DECIMAL: "Decimal" 107 NUM_MAX_I64: "maxI64"
108 NUM_DEC: "Dec" // the Num.Dectype alias 108 NUM_MIN_U64: "minU64"
109 NUM_BYTES_TO_U16: "bytesToU16" 109 NUM_MAX_U64: "maxU64"
110 NUM_BYTES_TO_U32: "bytesToU32" 110 NUM_MIN_I128: "minI128"
111 NUM_CAST_TO_NAT: "#castToNat" 111 NUM_MAX_I128: "maxI128"
112 NUM_DIV_CEIL: "divCeil" 112 NUM_TO_I8: "toI8"
113 NUM_DIV_CEIL_CHECKED: "divCeilChecked" 113 NUM_TO_I8_CHECKED: "toI8Checked"
114 NUM_TO_STR: "toStr" 114 NUM_TO_I16: "toI16"
115 NUM_MIN_I8: "minI8" 115 NUM_TO_I16_CHECKED: "toI16Checked"
116 NUM_MAX_I8: "maxI8" 116 NUM_TO_I32: "toI32"
117 NUM_MIN_U8: "minU8" 117 NUM_TO_I32_CHECKED: "toI32Checked"
118 NUM_MAX_U8: "maxU8" 118 NUM_TO_I64: "toI64"
119 NUM_MIN_I16: "minI16" 119 NUM_TO_I64_CHECKED: "toI64Checked"
120 NUM_MAX_I16: "maxI16" 120 NUM_TO_I128: "toI128"
121 NUM_MIN_U16: "minU16" 121 NUM_TO_I128_CHECKED: "toI128Checked"
122 NUM_MAX_U16: "maxU16" 122 NUM_TO_U8: "toU8"
123 NUM_MIN_I32: "minI32" 123 NUM_TO_U8_CHECKED: "toU8Checked"
124 NUM_MAX_I32: "maxI32" 124 NUM_TO_U16: "toU16"
125 NUM_MIN_U32: "minU32" 125 NUM_TO_U16_CHECKED: "toU16Checked"
126 NUM_MAX_U32: "maxU32" 126 NUM_TO_U32: "toU32"
127 NUM_MIN_I64: "minI64" 127 NUM_TO_U32_CHECKED: "toU32Checked"
128 NUM_MAX_I64: "maxI64" 128 NUM_TO_U64: "toU64"
129 NUM_MIN_U64: "minU64" 129 NUM_TO_U64_CHECKED: "toU64Checked"
130 NUM_MAX_U64: "maxU64" 130 NUM_TO_U128: "toU128"
131 NUM_MIN_I128: "minI128" 131 NUM_TO_U128_CHECKED: "toU128Checked"
132 NUM_MAX_I128: "maxI128" 132 NUM_TO_NAT: "toNat"
133 NUM_TO_I8: "toI8" 133 NUM_TO_NAT_CHECKED: "toNatChecked"
134 NUM_TO_I8_CHECKED: "toI8Checked" 134 NUM_TO_F32: "toF32"
135 NUM_TO_I16: "toI16" 135 NUM_TO_F32_CHECKED: "toF32Checked"
136 NUM_TO_I16_CHECKED: "toI16Checked" 136 NUM_TO_F64: "toF64"
137 NUM_TO_I32: "toI32" 137 NUM_TO_F64_CHECKED: "toF64Checked"
138 NUM_TO_I32_CHECKED: "toI32Checked"
139 NUM_TO_I64: "toI64"
140 NUM_TO_I64_CHECKED: "toI64Checked"
141 NUM_TO_I128: "toI128"
142 NUM_TO_I128_CHECKED: "toI128Checked"
143 NUM_TO_U8: "toU8"
144 NUM_TO_U8_CHECKED: "toU8Checked"
145 NUM_TO_U16: "toU16"
146 NUM_TO_U16_CHECKED: "toU16Checked"
147 NUM_TO_U32: "toU32"
148 NUM_TO_U32_CHECKED: "toU32Checked"
149 NUM_TO_U64: "toU64"
150 NUM_TO_U64_CHECKED: "toU64Checked"
151 NUM_TO_U128: "toU128"
152 NUM_TO_U128_CHECKED: "toU128Checked"
153 NUM_TO_NAT: "toNat"
154 NUM_TO_NAT_CHECKED: "toNatChecked"
155 NUM_TO_F32: "toF32"
156 NUM_TO_F32_CHECKED: "toF32Checked"
157 NUM_TO_F64: "toF64"
158 NUM_TO_F64_CHECKED: "toF64Checked"
} }
2 BOOL: "Bool" => { 2 BOOL: "Bool" => {
0 BOOL_BOOL: "Bool" // the Bool.Bool type alias 0 BOOL_BOOL: "Bool" // the Bool.Bool type alias
@ -1081,101 +1060,99 @@ define_builtins! {
} }
3 STR: "Str" => { 3 STR: "Str" => {
0 STR_STR: "Str" imported // the Str.Str type alias 0 STR_STR: "Str" imported // the Str.Str type alias
1 STR_AT_STR: "@Str" // the Str.@Str private tag 1 STR_IS_EMPTY: "isEmpty"
2 STR_IS_EMPTY: "isEmpty" 2 STR_APPEND: "#append" // unused
3 STR_APPEND: "#append" // unused 3 STR_CONCAT: "concat"
4 STR_CONCAT: "concat" 4 STR_JOIN_WITH: "joinWith"
5 STR_JOIN_WITH: "joinWith" 5 STR_SPLIT: "split"
6 STR_SPLIT: "split" 6 STR_COUNT_GRAPHEMES: "countGraphemes"
7 STR_COUNT_GRAPHEMES: "countGraphemes" 7 STR_STARTS_WITH: "startsWith"
8 STR_STARTS_WITH: "startsWith" 8 STR_ENDS_WITH: "endsWith"
9 STR_ENDS_WITH: "endsWith" 9 STR_FROM_UTF8: "fromUtf8"
10 STR_FROM_UTF8: "fromUtf8" 10 STR_UT8_PROBLEM: "Utf8Problem" // the Utf8Problem type alias
11 STR_UT8_PROBLEM: "Utf8Problem" // the Utf8Problem type alias 11 STR_UT8_BYTE_PROBLEM: "Utf8ByteProblem" // the Utf8ByteProblem type alias
12 STR_UT8_BYTE_PROBLEM: "Utf8ByteProblem" // the Utf8ByteProblem type alias 12 STR_TO_UTF8: "toUtf8"
13 STR_TO_UTF8: "toUtf8" 13 STR_STARTS_WITH_CODE_PT: "startsWithCodePt"
14 STR_STARTS_WITH_CODE_PT: "startsWithCodePt" 14 STR_ALIAS_ANALYSIS_STATIC: "#aliasAnalysisStatic" // string with the static lifetime
15 STR_ALIAS_ANALYSIS_STATIC: "#aliasAnalysisStatic" // string with the static lifetime 15 STR_FROM_UTF8_RANGE: "fromUtf8Range"
16 STR_FROM_UTF8_RANGE: "fromUtf8Range" 16 STR_REPEAT: "repeat"
17 STR_REPEAT: "repeat" 17 STR_TRIM: "trim"
18 STR_TRIM: "trim" 18 STR_TRIM_LEFT: "trimLeft"
19 STR_TRIM_LEFT: "trimLeft" 19 STR_TRIM_RIGHT: "trimRight"
20 STR_TRIM_RIGHT: "trimRight" 20 STR_TO_DEC: "toDec"
21 STR_TO_DEC: "toDec" 21 STR_TO_F64: "toF64"
22 STR_TO_F64: "toF64" 22 STR_TO_F32: "toF32"
23 STR_TO_F32: "toF32" 23 STR_TO_NAT: "toNat"
24 STR_TO_NAT: "toNat" 24 STR_TO_U128: "toU128"
25 STR_TO_U128: "toU128" 25 STR_TO_I128: "toI128"
26 STR_TO_I128: "toI128" 26 STR_TO_U64: "toU64"
27 STR_TO_U64: "toU64" 27 STR_TO_I64: "toI64"
28 STR_TO_I64: "toI64" 28 STR_TO_U32: "toU32"
29 STR_TO_U32: "toU32" 29 STR_TO_I32: "toI32"
30 STR_TO_I32: "toI32" 30 STR_TO_U16: "toU16"
31 STR_TO_U16: "toU16" 31 STR_TO_I16: "toI16"
32 STR_TO_I16: "toI16" 32 STR_TO_U8: "toU8"
33 STR_TO_U8: "toU8" 33 STR_TO_I8: "toI8"
34 STR_TO_I8: "toI8"
} }
4 LIST: "List" => { 4 LIST: "List" => {
0 LIST_LIST: "List" imported // the List.List type alias 0 LIST_LIST: "List" imported // the List.List type alias
1 LIST_AT_LIST: "@List" // the List.@List private tag 1 LIST_IS_EMPTY: "isEmpty"
2 LIST_IS_EMPTY: "isEmpty" 2 LIST_GET: "get"
3 LIST_GET: "get" 3 LIST_SET: "set"
4 LIST_SET: "set" 4 LIST_APPEND: "append"
5 LIST_APPEND: "append" 5 LIST_MAP: "map"
6 LIST_MAP: "map" 6 LIST_LEN: "len"
7 LIST_LEN: "len" 7 LIST_WALK_BACKWARDS: "walkBackwards"
8 LIST_WALK_BACKWARDS: "walkBackwards" 8 LIST_CONCAT: "concat"
9 LIST_CONCAT: "concat" 9 LIST_FIRST: "first"
10 LIST_FIRST: "first" 10 LIST_SINGLE: "single"
11 LIST_SINGLE: "single" 11 LIST_REPEAT: "repeat"
12 LIST_REPEAT: "repeat" 12 LIST_REVERSE: "reverse"
13 LIST_REVERSE: "reverse" 13 LIST_PREPEND: "prepend"
14 LIST_PREPEND: "prepend" 14 LIST_JOIN: "join"
15 LIST_JOIN: "join" 15 LIST_KEEP_IF: "keepIf"
16 LIST_KEEP_IF: "keepIf" 16 LIST_CONTAINS: "contains"
17 LIST_CONTAINS: "contains" 17 LIST_SUM: "sum"
18 LIST_SUM: "sum" 18 LIST_WALK: "walk"
19 LIST_WALK: "walk" 19 LIST_LAST: "last"
20 LIST_LAST: "last" 20 LIST_KEEP_OKS: "keepOks"
21 LIST_KEEP_OKS: "keepOks" 21 LIST_KEEP_ERRS: "keepErrs"
22 LIST_KEEP_ERRS: "keepErrs" 22 LIST_MAP_WITH_INDEX: "mapWithIndex"
23 LIST_MAP_WITH_INDEX: "mapWithIndex" 23 LIST_MAP2: "map2"
24 LIST_MAP2: "map2" 24 LIST_MAP3: "map3"
25 LIST_MAP3: "map3" 25 LIST_PRODUCT: "product"
26 LIST_PRODUCT: "product" 26 LIST_WALK_UNTIL: "walkUntil"
27 LIST_WALK_UNTIL: "walkUntil" 27 LIST_RANGE: "range"
28 LIST_RANGE: "range" 28 LIST_SORT_WITH: "sortWith"
29 LIST_SORT_WITH: "sortWith" 29 LIST_DROP: "drop"
30 LIST_DROP: "drop" 30 LIST_SWAP: "swap"
31 LIST_SWAP: "swap" 31 LIST_DROP_AT: "dropAt"
32 LIST_DROP_AT: "dropAt" 32 LIST_DROP_LAST: "dropLast"
33 LIST_DROP_LAST: "dropLast" 33 LIST_MIN: "min"
34 LIST_MIN: "min" 34 LIST_MIN_LT: "#minlt"
35 LIST_MIN_LT: "#minlt" 35 LIST_MAX: "max"
36 LIST_MAX: "max" 36 LIST_MAX_GT: "#maxGt"
37 LIST_MAX_GT: "#maxGt" 37 LIST_MAP4: "map4"
38 LIST_MAP4: "map4" 38 LIST_DROP_FIRST: "dropFirst"
39 LIST_DROP_FIRST: "dropFirst" 39 LIST_JOIN_MAP: "joinMap"
40 LIST_JOIN_MAP: "joinMap" 40 LIST_JOIN_MAP_CONCAT: "#joinMapConcat"
41 LIST_JOIN_MAP_CONCAT: "#joinMapConcat" 41 LIST_ANY: "any"
42 LIST_ANY: "any" 42 LIST_TAKE_FIRST: "takeFirst"
43 LIST_TAKE_FIRST: "takeFirst" 43 LIST_TAKE_LAST: "takeLast"
44 LIST_TAKE_LAST: "takeLast" 44 LIST_FIND: "find"
45 LIST_FIND: "find" 45 LIST_FIND_RESULT: "#find_result" // symbol used in the definition of List.find
46 LIST_FIND_RESULT: "#find_result" // symbol used in the definition of List.find 46 LIST_SUBLIST: "sublist"
47 LIST_SUBLIST: "sublist" 47 LIST_INTERSPERSE: "intersperse"
48 LIST_INTERSPERSE: "intersperse" 48 LIST_INTERSPERSE_CLOS: "#intersperseClos"
49 LIST_INTERSPERSE_CLOS: "#intersperseClos" 49 LIST_SPLIT: "split"
50 LIST_SPLIT: "split" 50 LIST_SPLIT_CLOS: "#splitClos"
51 LIST_SPLIT_CLOS: "#splitClos" 51 LIST_ALL: "all"
52 LIST_ALL: "all" 52 LIST_DROP_IF: "dropIf"
53 LIST_DROP_IF: "dropIf" 53 LIST_DROP_IF_PREDICATE: "#dropIfPred"
54 LIST_DROP_IF_PREDICATE: "#dropIfPred" 54 LIST_SORT_ASC: "sortAsc"
55 LIST_SORT_ASC: "sortAsc" 55 LIST_SORT_DESC: "sortDesc"
56 LIST_SORT_DESC: "sortDesc" 56 LIST_SORT_DESC_COMPARE: "#sortDescCompare"
57 LIST_SORT_DESC_COMPARE: "#sortDescCompare" 57 LIST_REPLACE: "replace"
58 LIST_REPLACE: "replace"
} }
5 RESULT: "Result" => { 5 RESULT: "Result" => {
0 RESULT_RESULT: "Result" // the Result.Result type alias 0 RESULT_RESULT: "Result" // the Result.Result type alias
@ -1192,41 +1169,39 @@ define_builtins! {
} }
6 DICT: "Dict" => { 6 DICT: "Dict" => {
0 DICT_DICT: "Dict" imported // the Dict.Dict type alias 0 DICT_DICT: "Dict" imported // the Dict.Dict type alias
1 DICT_AT_DICT: "@Dict" // the Dict.@Dict private tag 1 DICT_EMPTY: "empty"
2 DICT_EMPTY: "empty" 2 DICT_SINGLE: "single"
3 DICT_SINGLE: "single" 3 DICT_GET: "get"
4 DICT_GET: "get" 4 DICT_GET_RESULT: "#get_result" // symbol used in the definition of Dict.get
5 DICT_GET_RESULT: "#get_result" // symbol used in the definition of Dict.get 5 DICT_WALK: "walk"
6 DICT_WALK: "walk" 6 DICT_INSERT: "insert"
7 DICT_INSERT: "insert" 7 DICT_LEN: "len"
8 DICT_LEN: "len"
9 DICT_REMOVE: "remove" 8 DICT_REMOVE: "remove"
10 DICT_CONTAINS: "contains" 9 DICT_CONTAINS: "contains"
11 DICT_KEYS: "keys" 10 DICT_KEYS: "keys"
12 DICT_VALUES: "values" 11 DICT_VALUES: "values"
13 DICT_UNION: "union" 12 DICT_UNION: "union"
14 DICT_INTERSECTION: "intersection" 13 DICT_INTERSECTION: "intersection"
15 DICT_DIFFERENCE: "difference" 14 DICT_DIFFERENCE: "difference"
} }
7 SET: "Set" => { 7 SET: "Set" => {
0 SET_SET: "Set" imported // the Set.Set type alias 0 SET_SET: "Set" imported // the Set.Set type alias
1 SET_AT_SET: "@Set" // the Set.@Set private tag 1 SET_EMPTY: "empty"
2 SET_EMPTY: "empty" 2 SET_SINGLE: "single"
3 SET_SINGLE: "single" 3 SET_LEN: "len"
4 SET_LEN: "len" 4 SET_INSERT: "insert"
5 SET_INSERT: "insert" 5 SET_REMOVE: "remove"
6 SET_REMOVE: "remove" 6 SET_UNION: "union"
7 SET_UNION: "union" 7 SET_DIFFERENCE: "difference"
8 SET_DIFFERENCE: "difference" 8 SET_INTERSECTION: "intersection"
9 SET_INTERSECTION: "intersection" 9 SET_TO_LIST: "toList"
10 SET_TO_LIST: "toList" 10 SET_FROM_LIST: "fromList"
11 SET_FROM_LIST: "fromList" 11 SET_WALK: "walk"
12 SET_WALK: "walk" 12 SET_WALK_USER_FUNCTION: "#walk_user_function"
13 SET_WALK_USER_FUNCTION: "#walk_user_function" 13 SET_CONTAINS: "contains"
14 SET_CONTAINS: "contains" 14 SET_TO_DICT: "toDict"
15 SET_TO_DICT: "toDict"
} }
8 BOX: "Box" => { 8 BOX: "Box" => {
0 BOX_BOX_TYPE: "Box" imported // the Box.Box opaque type 0 BOX_BOX_TYPE: "Box" imported // the Box.Box opaque type

View file

@ -4,7 +4,7 @@ use crate::ir::{
use crate::layout::{Builtin, Layout, LayoutCache, TagIdIntType, UnionLayout}; use crate::layout::{Builtin, Layout, LayoutCache, TagIdIntType, UnionLayout};
use roc_builtins::bitcode::{FloatWidth, IntWidth}; use roc_builtins::bitcode::{FloatWidth, IntWidth};
use roc_collections::all::{MutMap, MutSet}; use roc_collections::all::{MutMap, MutSet};
use roc_exhaustive::{Ctor, RenderAs, TagId, Union}; use roc_exhaustive::{Ctor, CtorName, RenderAs, TagId, Union};
use roc_module::ident::TagName; use roc_module::ident::TagName;
use roc_module::low_level::LowLevel; use roc_module::low_level::LowLevel;
use roc_module::symbol::Symbol; use roc_module::symbol::Symbol;
@ -82,7 +82,7 @@ enum GuardedTest<'a> {
enum Test<'a> { enum Test<'a> {
IsCtor { IsCtor {
tag_id: TagIdIntType, tag_id: TagIdIntType,
tag_name: TagName, ctor_name: CtorName,
union: roc_exhaustive::Union, union: roc_exhaustive::Union,
arguments: Vec<(Pattern<'a>, Layout<'a>)>, arguments: Vec<(Pattern<'a>, Layout<'a>)>,
}, },
@ -512,7 +512,7 @@ fn test_at_path<'a>(
render_as: RenderAs::Tag, render_as: RenderAs::Tag,
alternatives: vec![Ctor { alternatives: vec![Ctor {
tag_id: TagId(0), tag_id: TagId(0),
name: TagName::Global(RECORD_TAG_NAME.into()), name: CtorName::Tag(TagName::Global(RECORD_TAG_NAME.into())),
arity: destructs.len(), arity: destructs.len(),
}], }],
}; };
@ -532,7 +532,7 @@ fn test_at_path<'a>(
IsCtor { IsCtor {
tag_id: 0, tag_id: 0,
tag_name: TagName::Global(RECORD_TAG_NAME.into()), ctor_name: CtorName::Tag(TagName::Global(RECORD_TAG_NAME.into())),
union, union,
arguments, arguments,
} }
@ -543,11 +543,12 @@ fn test_at_path<'a>(
arguments, arguments,
} => { } => {
let tag_id = 0; let tag_id = 0;
let union = Union::newtype_wrapper(tag_name.clone(), arguments.len()); let union =
Union::newtype_wrapper(CtorName::Tag(tag_name.clone()), arguments.len());
IsCtor { IsCtor {
tag_id, tag_id,
tag_name: tag_name.clone(), ctor_name: CtorName::Tag(tag_name.clone()),
union, union,
arguments: arguments.to_vec(), arguments: arguments.to_vec(),
} }
@ -561,7 +562,7 @@ fn test_at_path<'a>(
.. ..
} => IsCtor { } => IsCtor {
tag_id: *tag_id, tag_id: *tag_id,
tag_name: tag_name.clone(), ctor_name: CtorName::Tag(tag_name.clone()),
union: union.clone(), union: union.clone(),
arguments: arguments.to_vec(), arguments: arguments.to_vec(),
}, },
@ -571,14 +572,14 @@ fn test_at_path<'a>(
render_as: RenderAs::Tag, render_as: RenderAs::Tag,
alternatives: vec![Ctor { alternatives: vec![Ctor {
tag_id: TagId(0), tag_id: TagId(0),
name: TagName::Private(*opaque), name: CtorName::Opaque(*opaque),
arity: 1, arity: 1,
}], }],
}; };
IsCtor { IsCtor {
tag_id: 0, tag_id: 0,
tag_name: TagName::Private(*opaque), ctor_name: CtorName::Opaque(*opaque),
union, union,
arguments: vec![(**argument).clone()], arguments: vec![(**argument).clone()],
} }
@ -680,11 +681,11 @@ fn to_relevant_branch_help<'a>(
RecordDestructure(destructs, _) => match test { RecordDestructure(destructs, _) => match test {
IsCtor { IsCtor {
tag_name: test_name, ctor_name: test_name,
tag_id, tag_id,
.. ..
} => { } => {
debug_assert!(test_name == &TagName::Global(RECORD_TAG_NAME.into())); debug_assert!(test_name == &CtorName::Tag(TagName::Global(RECORD_TAG_NAME.into())));
let sub_positions = destructs.into_iter().enumerate().map(|(index, destruct)| { let sub_positions = destructs.into_iter().enumerate().map(|(index, destruct)| {
let pattern = match destruct.typ { let pattern = match destruct.typ {
DestructType::Guard(guard) => guard.clone(), DestructType::Guard(guard) => guard.clone(),
@ -713,11 +714,11 @@ fn to_relevant_branch_help<'a>(
OpaqueUnwrap { opaque, argument } => match test { OpaqueUnwrap { opaque, argument } => match test {
IsCtor { IsCtor {
tag_name: test_opaque_tag_name, ctor_name: test_opaque_tag_name,
tag_id, tag_id,
.. ..
} => { } => {
debug_assert_eq!(test_opaque_tag_name, &TagName::Private(opaque)); debug_assert_eq!(test_opaque_tag_name, &CtorName::Opaque(opaque));
let (argument, _) = *argument; let (argument, _) = *argument;
@ -744,10 +745,10 @@ fn to_relevant_branch_help<'a>(
.. ..
} => match test { } => match test {
IsCtor { IsCtor {
tag_name: test_name, ctor_name: test_name,
tag_id: test_id, tag_id: test_id,
.. ..
} if &tag_name == test_name => { } if test_name.is_tag(&tag_name) => {
let tag_id = 0; let tag_id = 0;
debug_assert_eq!(tag_id, *test_id); debug_assert_eq!(tag_id, *test_id);
@ -785,10 +786,10 @@ fn to_relevant_branch_help<'a>(
} => { } => {
match test { match test {
IsCtor { IsCtor {
tag_name: test_name, ctor_name: test_name,
tag_id: test_id, tag_id: test_id,
.. ..
} if &tag_name == test_name => { } if test_name.is_tag(&tag_name) => {
debug_assert_eq!(tag_id, *test_id); debug_assert_eq!(tag_id, *test_id);
// the test matches the constructor of this pattern // the test matches the constructor of this pattern

View file

@ -1,7 +1,7 @@
use crate::ir::DestructType; use crate::ir::DestructType;
use roc_collections::all::HumanIndex; use roc_collections::all::HumanIndex;
use roc_exhaustive::{ use roc_exhaustive::{
is_useful, Context, Ctor, Error, Guard, Literal, Pattern, RenderAs, TagId, Union, is_useful, Context, Ctor, CtorName, Error, Guard, Literal, Pattern, RenderAs, TagId, Union,
}; };
use roc_module::ident::{TagIdIntType, TagName}; use roc_module::ident::{TagIdIntType, TagName};
use roc_region::all::{Loc, Region}; use roc_region::all::{Loc, Region};
@ -45,7 +45,7 @@ fn simplify(pattern: &crate::ir::Pattern) -> Pattern {
let union = Union { let union = Union {
render_as: RenderAs::Record(field_names), render_as: RenderAs::Record(field_names),
alternatives: vec![Ctor { alternatives: vec![Ctor {
name: TagName::Global("#Record".into()), name: CtorName::Tag(TagName::Global("#Record".into())),
tag_id, tag_id,
arity: destructures.len(), arity: destructures.len(),
}], }],
@ -62,7 +62,7 @@ fn simplify(pattern: &crate::ir::Pattern) -> Pattern {
let simplified_args: std::vec::Vec<_> = let simplified_args: std::vec::Vec<_> =
arguments.iter().map(|v| simplify(&v.0)).collect(); arguments.iter().map(|v| simplify(&v.0)).collect();
Ctor( Ctor(
Union::newtype_wrapper(tag_name.clone(), arguments.len()), Union::newtype_wrapper(CtorName::Tag(tag_name.clone()), arguments.len()),
TagId(tag_id), TagId(tag_id),
simplified_args, simplified_args,
) )
@ -87,7 +87,7 @@ fn simplify(pattern: &crate::ir::Pattern) -> Pattern {
let union = Union { let union = Union {
render_as: RenderAs::Opaque, render_as: RenderAs::Opaque,
alternatives: vec![Ctor { alternatives: vec![Ctor {
name: TagName::Private(*opaque), name: CtorName::Opaque(*opaque),
tag_id, tag_id,
arity: 1, arity: 1,
}], }],
@ -169,7 +169,7 @@ fn to_nonredundant_rows(
render_as: RenderAs::Guard, render_as: RenderAs::Guard,
alternatives: vec![Ctor { alternatives: vec![Ctor {
tag_id, tag_id,
name: TagName::Global("#Guard".into()), name: CtorName::Tag(TagName::Global("#Guard".into())),
arity: 2, arity: 2,
}], }],
}; };

View file

@ -10,7 +10,7 @@ use roc_builtins::bitcode::{FloatWidth, IntWidth};
use roc_can::abilities::AbilitiesStore; use roc_can::abilities::AbilitiesStore;
use roc_can::expr::{ClosureData, IntValue}; use roc_can::expr::{ClosureData, IntValue};
use roc_collections::all::{default_hasher, BumpMap, BumpMapDefault, MutMap}; use roc_collections::all::{default_hasher, BumpMap, BumpMapDefault, MutMap};
use roc_exhaustive::{Ctor, Guard, RenderAs, TagId}; use roc_exhaustive::{Ctor, CtorName, Guard, RenderAs, TagId};
use roc_module::ident::{ForeignSymbol, Lowercase, TagName}; use roc_module::ident::{ForeignSymbol, Lowercase, TagName};
use roc_module::low_level::LowLevel; use roc_module::low_level::LowLevel;
use roc_module::symbol::{IdentIds, ModuleId, Symbol}; use roc_module::symbol::{IdentIds, ModuleId, Symbol};
@ -1656,7 +1656,6 @@ impl<'a> Expr<'a> {
} => { } => {
let doc_tag = match tag_name { let doc_tag = match tag_name {
TagName::Global(s) => alloc.text(s.as_str()), TagName::Global(s) => alloc.text(s.as_str()),
TagName::Private(s) => symbol_to_doc(alloc, *s),
TagName::Closure(s) => alloc TagName::Closure(s) => alloc
.text("ClosureTag(") .text("ClosureTag(")
.append(symbol_to_doc(alloc, *s)) .append(symbol_to_doc(alloc, *s))
@ -1678,7 +1677,6 @@ impl<'a> Expr<'a> {
} => { } => {
let doc_tag = match tag_name { let doc_tag = match tag_name {
TagName::Global(s) => alloc.text(s.as_str()), TagName::Global(s) => alloc.text(s.as_str()),
TagName::Private(s) => alloc.text(format!("{}", s)),
TagName::Closure(s) => alloc TagName::Closure(s) => alloc
.text("ClosureTag(") .text("ClosureTag(")
.append(symbol_to_doc(alloc, *s)) .append(symbol_to_doc(alloc, *s))
@ -3499,15 +3497,24 @@ pub fn with_hole<'a>(
OpaqueRef { argument, .. } => { OpaqueRef { argument, .. } => {
let (arg_var, loc_arg_expr) = *argument; let (arg_var, loc_arg_expr) = *argument;
with_hole(
env, match can_reuse_symbol(env, procs, &loc_arg_expr.value) {
loc_arg_expr.value, // Opaques decay to their argument.
arg_var, ReuseSymbol::Value(real_name) => {
procs, let mut result = hole.clone();
layout_cache, substitute_in_exprs(arena, &mut result, assigned, real_name);
assigned, result
hole, }
) _ => with_hole(
env,
loc_arg_expr.value,
arg_var,
procs,
layout_cache,
assigned,
hole,
),
}
} }
Record { Record {
@ -8030,7 +8037,7 @@ fn from_can_pattern_help<'a>(
render_as: RenderAs::Tag, render_as: RenderAs::Tag,
alternatives: vec![Ctor { alternatives: vec![Ctor {
tag_id: TagId(0), tag_id: TagId(0),
name: tag_name.clone(), name: CtorName::Tag(tag_name.clone()),
arity: 0, arity: 0,
}], }],
}, },
@ -8043,12 +8050,12 @@ fn from_can_pattern_help<'a>(
alternatives: vec![ alternatives: vec![
Ctor { Ctor {
tag_id: TagId(0), tag_id: TagId(0),
name: ffalse, name: CtorName::Tag(ffalse),
arity: 0, arity: 0,
}, },
Ctor { Ctor {
tag_id: TagId(1), tag_id: TagId(1),
name: ttrue, name: CtorName::Tag(ttrue),
arity: 0, arity: 0,
}, },
], ],
@ -8064,7 +8071,7 @@ fn from_can_pattern_help<'a>(
for (i, tag_name) in tag_names.into_iter().enumerate() { for (i, tag_name) in tag_names.into_iter().enumerate() {
ctors.push(Ctor { ctors.push(Ctor {
tag_id: TagId(i as _), tag_id: TagId(i as _),
name: tag_name, name: CtorName::Tag(tag_name),
arity: 0, arity: 0,
}) })
} }
@ -8155,7 +8162,7 @@ fn from_can_pattern_help<'a>(
for (i, (tag_name, args)) in tags.iter().enumerate() { for (i, (tag_name, args)) in tags.iter().enumerate() {
ctors.push(Ctor { ctors.push(Ctor {
tag_id: TagId(i as _), tag_id: TagId(i as _),
name: tag_name.clone(), name: CtorName::Tag(tag_name.clone()),
arity: args.len(), arity: args.len(),
}) })
} }
@ -8206,7 +8213,7 @@ fn from_can_pattern_help<'a>(
for (i, (tag_name, args)) in tags.iter().enumerate() { for (i, (tag_name, args)) in tags.iter().enumerate() {
ctors.push(Ctor { ctors.push(Ctor {
tag_id: TagId(i as _), tag_id: TagId(i as _),
name: tag_name.clone(), name: CtorName::Tag(tag_name.clone()),
// don't include tag discriminant in arity // don't include tag discriminant in arity
arity: args.len() - 1, arity: args.len() - 1,
}) })
@ -8251,7 +8258,7 @@ fn from_can_pattern_help<'a>(
ctors.push(Ctor { ctors.push(Ctor {
tag_id: TagId(0), tag_id: TagId(0),
name: tag_name.clone(), name: CtorName::Tag(tag_name.clone()),
arity: fields.len(), arity: fields.len(),
}); });
@ -8298,7 +8305,7 @@ fn from_can_pattern_help<'a>(
if i == nullable_id as usize { if i == nullable_id as usize {
ctors.push(Ctor { ctors.push(Ctor {
tag_id: TagId(i as _), tag_id: TagId(i as _),
name: nullable_name.clone(), name: CtorName::Tag(nullable_name.clone()),
// don't include tag discriminant in arity // don't include tag discriminant in arity
arity: 0, arity: 0,
}); });
@ -8308,7 +8315,7 @@ fn from_can_pattern_help<'a>(
ctors.push(Ctor { ctors.push(Ctor {
tag_id: TagId(i as _), tag_id: TagId(i as _),
name: tag_name.clone(), name: CtorName::Tag(tag_name.clone()),
// don't include tag discriminant in arity // don't include tag discriminant in arity
arity: args.len() - 1, arity: args.len() - 1,
}); });
@ -8319,7 +8326,7 @@ fn from_can_pattern_help<'a>(
if i == nullable_id as usize { if i == nullable_id as usize {
ctors.push(Ctor { ctors.push(Ctor {
tag_id: TagId(i as _), tag_id: TagId(i as _),
name: nullable_name.clone(), name: CtorName::Tag(nullable_name.clone()),
// don't include tag discriminant in arity // don't include tag discriminant in arity
arity: 0, arity: 0,
}); });
@ -8369,13 +8376,13 @@ fn from_can_pattern_help<'a>(
ctors.push(Ctor { ctors.push(Ctor {
tag_id: TagId(nullable_id as _), tag_id: TagId(nullable_id as _),
name: nullable_name.clone(), name: CtorName::Tag(nullable_name.clone()),
arity: 0, arity: 0,
}); });
ctors.push(Ctor { ctors.push(Ctor {
tag_id: TagId(!nullable_id as _), tag_id: TagId(!nullable_id as _),
name: nullable_name.clone(), name: CtorName::Tag(nullable_name.clone()),
// FIXME drop tag // FIXME drop tag
arity: other_fields.len() - 1, arity: other_fields.len() - 1,
}); });
@ -8631,9 +8638,9 @@ pub fn num_argument_to_int_or_float(
num_argument_to_int_or_float(subs, target_info, var, true) num_argument_to_int_or_float(subs, target_info, var, true)
} }
Symbol::NUM_DECIMAL | Symbol::NUM_AT_DECIMAL => IntOrFloat::DecimalFloatType, Symbol::NUM_DECIMAL => IntOrFloat::DecimalFloatType,
Symbol::NUM_NAT | Symbol::NUM_NATURAL | Symbol::NUM_AT_NATURAL => { Symbol::NUM_NAT | Symbol::NUM_NATURAL => {
let int_width = match target_info.ptr_width() { let int_width = match target_info.ptr_width() {
roc_target::PtrWidth::Bytes4 => IntWidth::U32, roc_target::PtrWidth::Bytes4 => IntWidth::U32,
roc_target::PtrWidth::Bytes8 => IntWidth::U64, roc_target::PtrWidth::Bytes8 => IntWidth::U64,

View file

@ -311,6 +311,50 @@ impl<'a> UnionLayout<'a> {
.append(alloc.intersperse(tags_doc, ", ")) .append(alloc.intersperse(tags_doc, ", "))
.append(alloc.text("]")) .append(alloc.text("]"))
} }
Recursive(tags) => {
let tags_doc = tags.iter().map(|fields| {
alloc.text("C ").append(alloc.intersperse(
fields.iter().map(|x| x.to_doc(alloc, Parens::InTypeParam)),
" ",
))
});
alloc
.text("[<r>")
.append(alloc.intersperse(tags_doc, ", "))
.append(alloc.text("]"))
}
NonNullableUnwrapped(fields) => {
let fields_doc = alloc.text("C ").append(alloc.intersperse(
fields.iter().map(|x| x.to_doc(alloc, Parens::InTypeParam)),
" ",
));
alloc
.text("[<rnnu>")
.append(fields_doc)
.append(alloc.text("]"))
}
NullableUnwrapped {
nullable_id,
other_fields,
} => {
let fields_doc = alloc.text("C ").append(
alloc.intersperse(
other_fields
.iter()
.map(|x| x.to_doc(alloc, Parens::InTypeParam)),
" ",
),
);
let tags_doc = if nullable_id {
alloc.concat(vec![alloc.text("<null>, "), fields_doc])
} else {
alloc.concat(vec![fields_doc, alloc.text(", <null>")])
};
alloc
.text("[<rnu>")
.append(tags_doc)
.append(alloc.text("]"))
}
_ => alloc.text("TODO"), _ => alloc.text("TODO"),
} }
} }
@ -939,6 +983,16 @@ pub const fn round_up_to_alignment(width: u32, alignment: u32) -> u32 {
} }
} }
#[inline(always)]
pub fn is_unresolved_var(subs: &Subs, var: Variable) -> bool {
use Content::*;
let content = subs.get_content_without_compacting(var);
matches!(
content,
FlexVar(..) | RigidVar(..) | FlexAbleVar(..) | RigidAbleVar(..),
)
}
impl<'a> Layout<'a> { impl<'a> Layout<'a> {
pub const VOID: Self = Layout::Union(UnionLayout::NonRecursive(&[])); pub const VOID: Self = Layout::Union(UnionLayout::NonRecursive(&[]));
pub const UNIT: Self = Layout::Struct { pub const UNIT: Self = Layout::Struct {
@ -971,12 +1025,24 @@ impl<'a> Layout<'a> {
} }
match symbol { match symbol {
Symbol::NUM_DECIMAL | Symbol::NUM_AT_DECIMAL => { Symbol::NUM_DECIMAL => return Ok(Layout::Builtin(Builtin::Decimal)),
return Ok(Layout::Builtin(Builtin::Decimal))
Symbol::NUM_NAT | Symbol::NUM_NATURAL => {
return Ok(Layout::usize(env.target_info))
} }
Symbol::NUM_NAT | Symbol::NUM_NATURAL | Symbol::NUM_AT_NATURAL => { Symbol::NUM_NUM | Symbol::NUM_INT | Symbol::NUM_INTEGER
return Ok(Layout::usize(env.target_info)) if is_unresolved_var(env.subs, actual_var) =>
{
// default to i64
return Ok(Layout::i64());
}
Symbol::NUM_FLOAT | Symbol::NUM_FLOATINGPOINT
if is_unresolved_var(env.subs, actual_var) =>
{
// default to f64
return Ok(Layout::f64());
} }
_ => Self::from_var(env, actual_var), _ => Self::from_var(env, actual_var),
@ -1645,7 +1711,7 @@ fn layout_from_flat_type<'a>(
Ok(Layout::f32()) Ok(Layout::f32())
} }
Symbol::NUM_NUM | Symbol::NUM_AT_NUM => { Symbol::NUM_NUM => {
// Num.Num should only ever have 1 argument, e.g. Num.Num Int.Integer // Num.Num should only ever have 1 argument, e.g. Num.Num Int.Integer
debug_assert_eq!(args.len(), 1); debug_assert_eq!(args.len(), 1);
@ -1731,7 +1797,7 @@ fn layout_from_flat_type<'a>(
debug_assert!(ext_var_is_empty_tag_union(subs, ext_var)); debug_assert!(ext_var_is_empty_tag_union(subs, ext_var));
Ok(layout_from_tag_union(arena, &tags, subs, env.target_info)) Ok(layout_from_tag_union(env, &tags))
} }
FunctionOrTagUnion(tag_name, _, ext_var) => { FunctionOrTagUnion(tag_name, _, ext_var) => {
debug_assert!( debug_assert!(
@ -1742,7 +1808,7 @@ fn layout_from_flat_type<'a>(
let union_tags = UnionTags::from_tag_name_index(tag_name); let union_tags = UnionTags::from_tag_name_index(tag_name);
let (tags, _) = union_tags.unsorted_tags_and_ext(subs, ext_var); let (tags, _) = union_tags.unsorted_tags_and_ext(subs, ext_var);
Ok(layout_from_tag_union(arena, &tags, subs, env.target_info)) Ok(layout_from_tag_union(env, &tags))
} }
RecursiveTagUnion(rec_var, tags, ext_var) => { RecursiveTagUnion(rec_var, tags, ext_var) => {
let (tags, ext_var) = tags.unsorted_tags_and_ext(subs, ext_var); let (tags, ext_var) = tags.unsorted_tags_and_ext(subs, ext_var);
@ -2071,23 +2137,14 @@ fn is_recursive_tag_union(layout: &Layout) -> bool {
} }
fn union_sorted_tags_help_new<'a>( fn union_sorted_tags_help_new<'a>(
arena: &'a Bump, env: &mut Env<'a, '_>,
tags_list: &[(&'_ TagName, &[Variable])], tags_list: &[(&'_ TagName, &[Variable])],
opt_rec_var: Option<Variable>, opt_rec_var: Option<Variable>,
subs: &Subs,
target_info: TargetInfo,
) -> UnionVariant<'a> { ) -> UnionVariant<'a> {
// sort up front; make sure the ordering stays intact! // sort up front; make sure the ordering stays intact!
let mut tags_list = Vec::from_iter_in(tags_list.iter(), arena); let mut tags_list = Vec::from_iter_in(tags_list.iter(), env.arena);
tags_list.sort_unstable_by(|(a, _), (b, _)| a.cmp(b)); tags_list.sort_unstable_by(|(a, _), (b, _)| a.cmp(b));
let mut env = Env {
arena,
subs,
seen: Vec::new_in(arena),
target_info,
};
match tags_list.len() { match tags_list.len() {
0 => { 0 => {
// trying to instantiate a type with no values // trying to instantiate a type with no values
@ -2098,39 +2155,29 @@ fn union_sorted_tags_help_new<'a>(
let tag_name = tag_name.clone(); let tag_name = tag_name.clone();
// just one tag in the union (but with arguments) can be a struct // just one tag in the union (but with arguments) can be a struct
let mut layouts = Vec::with_capacity_in(tags_list.len(), arena); let mut layouts = Vec::with_capacity_in(tags_list.len(), env.arena);
// special-case NUM_AT_NUM: if its argument is a FlexVar, make it Int for &var in arguments {
match tag_name { match Layout::from_var(env, var) {
TagName::Private(Symbol::NUM_AT_NUM) => { Ok(layout) => {
let var = arguments[0]; layouts.push(layout);
layouts }
.push(unwrap_num_tag(subs, var, target_info).expect("invalid num layout")); Err(LayoutProblem::UnresolvedTypeVar(_)) => {
} // If we encounter an unbound type var (e.g. `Ok *`)
_ => { // then it's zero-sized; In the future we may drop this argument
for &var in arguments { // completely, but for now we represent it with the empty tag union
match Layout::from_var(&mut env, var) { layouts.push(Layout::VOID)
Ok(layout) => { }
layouts.push(layout); Err(LayoutProblem::Erroneous) => {
} // An erroneous type var will code gen to a runtime
Err(LayoutProblem::UnresolvedTypeVar(_)) => { // error, so we don't need to store any data for it.
// If we encounter an unbound type var (e.g. `Ok *`)
// then it's zero-sized; In the future we may drop this argument
// completely, but for now we represent it with the empty tag union
layouts.push(Layout::VOID)
}
Err(LayoutProblem::Erroneous) => {
// An erroneous type var will code gen to a runtime
// error, so we don't need to store any data for it.
}
}
} }
} }
} }
layouts.sort_by(|layout1, layout2| { layouts.sort_by(|layout1, layout2| {
let size1 = layout1.alignment_bytes(target_info); let size1 = layout1.alignment_bytes(env.target_info);
let size2 = layout2.alignment_bytes(target_info); let size2 = layout2.alignment_bytes(env.target_info);
size2.cmp(&size1) size2.cmp(&size1)
}); });
@ -2151,7 +2198,7 @@ fn union_sorted_tags_help_new<'a>(
} }
num_tags => { num_tags => {
// default path // default path
let mut answer = Vec::with_capacity_in(tags_list.len(), arena); let mut answer = Vec::with_capacity_in(tags_list.len(), env.arena);
let mut has_any_arguments = false; let mut has_any_arguments = false;
let mut nullable: Option<(TagIdIntType, TagName)> = None; let mut nullable: Option<(TagIdIntType, TagName)> = None;
@ -2174,17 +2221,19 @@ fn union_sorted_tags_help_new<'a>(
continue; continue;
} }
let mut arg_layouts = Vec::with_capacity_in(arguments.len() + 1, arena); let mut arg_layouts = Vec::with_capacity_in(arguments.len() + 1, env.arena);
for &var in arguments { for &var in arguments {
match Layout::from_var(&mut env, var) { match Layout::from_var(env, var) {
Ok(layout) => { Ok(layout) => {
has_any_arguments = true; has_any_arguments = true;
// make sure to not unroll recursive types! // make sure to not unroll recursive types!
let self_recursion = opt_rec_var.is_some() let self_recursion = opt_rec_var.is_some()
&& subs.get_root_key_without_compacting(var) && env.subs.get_root_key_without_compacting(var)
== subs.get_root_key_without_compacting(opt_rec_var.unwrap()) == env
.subs
.get_root_key_without_compacting(opt_rec_var.unwrap())
&& is_recursive_tag_union(&layout); && is_recursive_tag_union(&layout);
if self_recursion { if self_recursion {
@ -2207,8 +2256,8 @@ fn union_sorted_tags_help_new<'a>(
} }
arg_layouts.sort_by(|layout1, layout2| { arg_layouts.sort_by(|layout1, layout2| {
let size1 = layout1.alignment_bytes(target_info); let size1 = layout1.alignment_bytes(env.target_info);
let size2 = layout2.alignment_bytes(target_info); let size2 = layout2.alignment_bytes(env.target_info);
size2.cmp(&size1) size2.cmp(&size1)
}); });
@ -2229,7 +2278,7 @@ fn union_sorted_tags_help_new<'a>(
3..=MAX_ENUM_SIZE if !has_any_arguments => { 3..=MAX_ENUM_SIZE if !has_any_arguments => {
// type can be stored in a byte // type can be stored in a byte
// needs the sorted tag names to determine the tag_id // needs the sorted tag names to determine the tag_id
let mut tag_names = Vec::with_capacity_in(answer.len(), arena); let mut tag_names = Vec::with_capacity_in(answer.len(), env.arena);
for (tag_name, _) in answer { for (tag_name, _) in answer {
tag_names.push(tag_name); tag_names.push(tag_name);
@ -2303,37 +2352,26 @@ pub fn union_sorted_tags_help<'a>(
let mut layouts = Vec::with_capacity_in(tags_vec.len(), arena); let mut layouts = Vec::with_capacity_in(tags_vec.len(), arena);
let mut contains_zero_sized = false; let mut contains_zero_sized = false;
// special-case NUM_AT_NUM: if its argument is a FlexVar, make it Int for var in arguments {
match tag_name { match Layout::from_var(&mut env, var) {
TagName::Private(Symbol::NUM_AT_NUM) => { Ok(layout) => {
layouts.push( // Drop any zero-sized arguments like {}
unwrap_num_tag(subs, arguments[0], target_info) if !layout.is_dropped_because_empty() {
.expect("invalid num layout"), layouts.push(layout);
); } else {
} contains_zero_sized = true;
_ => {
for var in arguments {
match Layout::from_var(&mut env, var) {
Ok(layout) => {
// Drop any zero-sized arguments like {}
if !layout.is_dropped_because_empty() {
layouts.push(layout);
} else {
contains_zero_sized = true;
}
}
Err(LayoutProblem::UnresolvedTypeVar(_)) => {
// If we encounter an unbound type var (e.g. `Ok *`)
// then it's zero-sized; In the future we may drop this argument
// completely, but for now we represent it with the empty tag union
layouts.push(Layout::VOID)
}
Err(LayoutProblem::Erroneous) => {
// An erroneous type var will code gen to a runtime
// error, so we don't need to store any data for it.
}
} }
} }
Err(LayoutProblem::UnresolvedTypeVar(_)) => {
// If we encounter an unbound type var (e.g. `Ok *`)
// then it's zero-sized; In the future we may drop this argument
// completely, but for now we represent it with the empty tag union
layouts.push(Layout::VOID)
}
Err(LayoutProblem::Erroneous) => {
// An erroneous type var will code gen to a runtime
// error, so we don't need to store any data for it.
}
} }
} }
@ -2488,128 +2526,95 @@ pub fn union_sorted_tags_help<'a>(
} }
} }
fn layout_from_newtype<'a>( fn layout_from_newtype<'a>(env: &mut Env<'a, '_>, tags: &UnsortedUnionTags) -> Layout<'a> {
arena: &'a Bump, debug_assert!(tags.is_newtype_wrapper(env.subs));
tags: &UnsortedUnionTags,
subs: &Subs,
target_info: TargetInfo,
) -> Layout<'a> {
debug_assert!(tags.is_newtype_wrapper(subs));
let (tag_name, var) = tags.get_newtype(subs); let (_tag_name, var) = tags.get_newtype(env.subs);
if tag_name == &TagName::Private(Symbol::NUM_AT_NUM) { match Layout::from_var(env, var) {
unwrap_num_tag(subs, var, target_info).expect("invalid Num argument") Ok(layout) => layout,
} else { Err(LayoutProblem::UnresolvedTypeVar(_)) => {
let mut env = Env { // If we encounter an unbound type var (e.g. `Ok *`)
arena, // then it's zero-sized; In the future we may drop this argument
subs, // completely, but for now we represent it with the empty tag union
seen: Vec::new_in(arena), Layout::VOID
target_info, }
}; Err(LayoutProblem::Erroneous) => {
// An erroneous type var will code gen to a runtime
match Layout::from_var(&mut env, var) { // error, so we don't need to store any data for it.
Ok(layout) => layout, todo!()
Err(LayoutProblem::UnresolvedTypeVar(_)) => {
// If we encounter an unbound type var (e.g. `Ok *`)
// then it's zero-sized; In the future we may drop this argument
// completely, but for now we represent it with the empty tag union
Layout::VOID
}
Err(LayoutProblem::Erroneous) => {
// An erroneous type var will code gen to a runtime
// error, so we don't need to store any data for it.
todo!()
}
} }
} }
} }
fn layout_from_tag_union<'a>( fn layout_from_tag_union<'a>(env: &mut Env<'a, '_>, tags: &UnsortedUnionTags) -> Layout<'a> {
arena: &'a Bump,
tags: &UnsortedUnionTags,
subs: &Subs,
target_info: TargetInfo,
) -> Layout<'a> {
use UnionVariant::*; use UnionVariant::*;
if tags.is_newtype_wrapper(subs) { if tags.is_newtype_wrapper(env.subs) {
return layout_from_newtype(arena, tags, subs, target_info); return layout_from_newtype(env, tags);
} }
let tags_vec = &tags.tags; let tags_vec = &tags.tags;
match tags_vec.get(0) { let opt_rec_var = None;
Some((tag_name, arguments)) if *tag_name == &TagName::Private(Symbol::NUM_AT_NUM) => { let variant = union_sorted_tags_help_new(env, tags_vec, opt_rec_var);
debug_assert_eq!(arguments.len(), 1);
let &var = arguments.iter().next().unwrap(); match variant {
Never => Layout::VOID,
Unit | UnitWithArguments => Layout::UNIT,
BoolUnion { .. } => Layout::bool(),
ByteUnion(_) => Layout::u8(),
Newtype {
arguments: field_layouts,
..
} => {
let answer1 = if field_layouts.len() == 1 {
field_layouts[0]
} else {
Layout::struct_no_name_order(field_layouts.into_bump_slice())
};
unwrap_num_tag(subs, var, target_info).expect("invalid Num argument") answer1
} }
_ => { Wrapped(variant) => {
let opt_rec_var = None; use WrappedVariant::*;
let variant =
union_sorted_tags_help_new(arena, tags_vec, opt_rec_var, subs, target_info);
match variant { match variant {
Never => Layout::VOID, NonRecursive {
Unit | UnitWithArguments => Layout::UNIT, sorted_tag_layouts: tags,
BoolUnion { .. } => Layout::bool(),
ByteUnion(_) => Layout::u8(),
Newtype {
arguments: field_layouts,
..
} => { } => {
let answer1 = if field_layouts.len() == 1 { let mut tag_layouts = Vec::with_capacity_in(tags.len(), env.arena);
field_layouts[0] tag_layouts.extend(tags.iter().map(|r| r.1));
} else {
Layout::struct_no_name_order(field_layouts.into_bump_slice())
};
answer1 Layout::Union(UnionLayout::NonRecursive(tag_layouts.into_bump_slice()))
} }
Wrapped(variant) => {
use WrappedVariant::*;
match variant { Recursive {
NonRecursive { sorted_tag_layouts: tags,
sorted_tag_layouts: tags, } => {
} => { let mut tag_layouts = Vec::with_capacity_in(tags.len(), env.arena);
let mut tag_layouts = Vec::with_capacity_in(tags.len(), arena); tag_layouts.extend(tags.iter().map(|r| r.1));
tag_layouts.extend(tags.iter().map(|r| r.1));
Layout::Union(UnionLayout::NonRecursive(tag_layouts.into_bump_slice())) debug_assert!(tag_layouts.len() > 1);
} Layout::Union(UnionLayout::Recursive(tag_layouts.into_bump_slice()))
Recursive {
sorted_tag_layouts: tags,
} => {
let mut tag_layouts = Vec::with_capacity_in(tags.len(), arena);
tag_layouts.extend(tags.iter().map(|r| r.1));
debug_assert!(tag_layouts.len() > 1);
Layout::Union(UnionLayout::Recursive(tag_layouts.into_bump_slice()))
}
NullableWrapped {
nullable_id,
nullable_name: _,
sorted_tag_layouts: tags,
} => {
let mut tag_layouts = Vec::with_capacity_in(tags.len(), arena);
tag_layouts.extend(tags.iter().map(|r| r.1));
Layout::Union(UnionLayout::NullableWrapped {
nullable_id,
other_tags: tag_layouts.into_bump_slice(),
})
}
NullableUnwrapped { .. } => todo!(),
NonNullableUnwrapped { .. } => todo!(),
}
} }
NullableWrapped {
nullable_id,
nullable_name: _,
sorted_tag_layouts: tags,
} => {
let mut tag_layouts = Vec::with_capacity_in(tags.len(), env.arena);
tag_layouts.extend(tags.iter().map(|r| r.1));
Layout::Union(UnionLayout::NullableWrapped {
nullable_id,
other_tags: tag_layouts.into_bump_slice(),
})
}
NullableUnwrapped { .. } => todo!(),
NonNullableUnwrapped { .. } => todo!(),
} }
} }
} }
@ -2707,88 +2712,6 @@ fn layout_from_num_content<'a>(
} }
} }
fn unwrap_num_tag<'a>(
subs: &Subs,
var: Variable,
target_info: TargetInfo,
) -> Result<Layout<'a>, LayoutProblem> {
match subs.get_content_without_compacting(var) {
Content::Alias(Symbol::NUM_INTEGER, args, _, _) => {
debug_assert!(args.len() == 1);
let precision_var = subs[args.all_variables().into_iter().next().unwrap()];
let precision = subs.get_content_without_compacting(precision_var);
match precision {
Content::Alias(symbol, args, _, _) => {
debug_assert!(args.is_empty());
let layout = match *symbol {
Symbol::NUM_SIGNED128 => Layout::i128(),
Symbol::NUM_SIGNED64 => Layout::i64(),
Symbol::NUM_SIGNED32 => Layout::i32(),
Symbol::NUM_SIGNED16 => Layout::i16(),
Symbol::NUM_SIGNED8 => Layout::i8(),
Symbol::NUM_UNSIGNED128 => Layout::u128(),
Symbol::NUM_UNSIGNED64 => Layout::u64(),
Symbol::NUM_UNSIGNED32 => Layout::u32(),
Symbol::NUM_UNSIGNED16 => Layout::u16(),
Symbol::NUM_UNSIGNED8 => Layout::u8(),
Symbol::NUM_NATURAL => Layout::usize(target_info),
_ => unreachable!("not a valid int variant: {:?} {:?}", symbol, args),
};
Ok(layout)
}
Content::FlexVar(_) | Content::RigidVar(_) => {
// default to i64
Ok(Layout::i64())
}
_ => unreachable!("not a valid int variant: {:?}", precision),
}
}
Content::Alias(Symbol::NUM_FLOATINGPOINT, args, _, _) => {
debug_assert!(args.len() == 1);
let precision_var = subs[args.all_variables().into_iter().next().unwrap()];
let precision = subs.get_content_without_compacting(precision_var);
match precision {
Content::Alias(Symbol::NUM_BINARY32, args, _, _) => {
debug_assert!(args.is_empty());
Ok(Layout::f32())
}
Content::Alias(Symbol::NUM_BINARY64, args, _, _) => {
debug_assert!(args.is_empty());
Ok(Layout::f64())
}
Content::Alias(Symbol::NUM_DECIMAL, args, _, _) => {
debug_assert!(args.is_empty());
Ok(Layout::Builtin(Builtin::Decimal))
}
Content::FlexVar(_) | Content::RigidVar(_) => {
// default to f64
Ok(Layout::f64())
}
_ => unreachable!("not a valid float variant: {:?}", precision),
}
}
Content::FlexVar(_) | Content::RigidVar(_) => {
// If this was still a (Num *) then default to compiling it to i64
Ok(Layout::default_integer())
}
other => {
todo!("TODO non structure Num.@Num flat_type {:?}", other);
}
}
}
fn dict_layout_from_key_value<'a>( fn dict_layout_from_key_value<'a>(
env: &mut Env<'a, '_>, env: &mut Env<'a, '_>,
key_var: Variable, key_var: Variable,

View file

@ -344,7 +344,6 @@ impl LambdaSet {
layouts.symbols.push(*symbol); layouts.symbols.push(*symbol);
} }
TagName::Global(_) => unreachable!("lambda set tags must be closure tags"), TagName::Global(_) => unreachable!("lambda set tags must be closure tags"),
TagName::Private(_) => unreachable!("lambda set tags must be closure tags"),
} }
} }
@ -678,11 +677,9 @@ impl Layout {
} }
match symbol { match symbol {
Symbol::NUM_DECIMAL | Symbol::NUM_AT_DECIMAL => Ok(Layout::Decimal), Symbol::NUM_DECIMAL => Ok(Layout::Decimal),
Symbol::NUM_NAT | Symbol::NUM_NATURAL | Symbol::NUM_AT_NATURAL => { Symbol::NUM_NAT | Symbol::NUM_NATURAL => Ok(layouts.usize()),
Ok(layouts.usize())
}
_ => { _ => {
// at this point we throw away alias information // at this point we throw away alias information

View file

@ -30,7 +30,6 @@
">=" ">="
">" ">"
"^" "^"
"%%"
"%" "%"
"->" "->"

View file

@ -189,10 +189,8 @@ pub enum Expr<'a> {
// Tags // Tags
GlobalTag(&'a str), GlobalTag(&'a str),
PrivateTag(&'a str),
// Reference to an opaque type, e.g. $Opaq // Reference to an opaque type, e.g. @Opaq
// TODO(opaques): $->@ in the above comment
OpaqueRef(&'a str), OpaqueRef(&'a str),
// Pattern Matching // Pattern Matching
@ -446,11 +444,6 @@ pub enum Tag<'a> {
args: &'a [Loc<TypeAnnotation<'a>>], args: &'a [Loc<TypeAnnotation<'a>>],
}, },
Private {
name: Loc<&'a str>,
args: &'a [Loc<TypeAnnotation<'a>>],
},
// We preserve this for the formatter; canonicalization ignores it. // We preserve this for the formatter; canonicalization ignores it.
SpaceBefore(&'a Tag<'a>, &'a [CommentOrNewline<'a>]), SpaceBefore(&'a Tag<'a>, &'a [CommentOrNewline<'a>]),
SpaceAfter(&'a Tag<'a>, &'a [CommentOrNewline<'a>]), SpaceAfter(&'a Tag<'a>, &'a [CommentOrNewline<'a>]),
@ -523,7 +516,6 @@ pub enum Pattern<'a> {
Identifier(&'a str), Identifier(&'a str),
GlobalTag(&'a str), GlobalTag(&'a str),
PrivateTag(&'a str),
OpaqueRef(&'a str), OpaqueRef(&'a str),
@ -579,7 +571,6 @@ impl<'a> Pattern<'a> {
pub fn from_ident(arena: &'a Bump, ident: Ident<'a>) -> Pattern<'a> { pub fn from_ident(arena: &'a Bump, ident: Ident<'a>) -> Pattern<'a> {
match ident { match ident {
Ident::GlobalTag(string) => Pattern::GlobalTag(string), Ident::GlobalTag(string) => Pattern::GlobalTag(string),
Ident::PrivateTag(string) => Pattern::PrivateTag(string),
Ident::OpaqueRef(string) => Pattern::OpaqueRef(string), Ident::OpaqueRef(string) => Pattern::OpaqueRef(string),
Ident::Access { module_name, parts } => { Ident::Access { module_name, parts } => {
if parts.len() == 1 { if parts.len() == 1 {
@ -629,7 +620,6 @@ impl<'a> Pattern<'a> {
match (self, other) { match (self, other) {
(Identifier(x), Identifier(y)) => x == y, (Identifier(x), Identifier(y)) => x == y,
(GlobalTag(x), GlobalTag(y)) => x == y, (GlobalTag(x), GlobalTag(y)) => x == y,
(PrivateTag(x), PrivateTag(y)) => x == y,
(Apply(constructor_x, args_x), Apply(constructor_y, args_y)) => { (Apply(constructor_x, args_x), Apply(constructor_y, args_y)) => {
let equivalent_args = args_x let equivalent_args = args_x
.iter() .iter()
@ -927,7 +917,7 @@ impl<'a> Expr<'a> {
} }
pub fn is_tag(&self) -> bool { pub fn is_tag(&self) -> bool {
matches!(self, Expr::GlobalTag(_) | Expr::PrivateTag(_)) matches!(self, Expr::GlobalTag(_))
} }
} }

View file

@ -196,6 +196,7 @@ fn parse_loc_term_or_underscore<'a>(
) -> ParseResult<'a, Loc<Expr<'a>>, EExpr<'a>> { ) -> ParseResult<'a, Loc<Expr<'a>>, EExpr<'a>> {
one_of!( one_of!(
loc_expr_in_parens_etc_help(min_indent), loc_expr_in_parens_etc_help(min_indent),
loc!(specialize(EExpr::If, if_expr_help(min_indent, options))),
loc!(specialize(EExpr::Str, string_literal_help())), loc!(specialize(EExpr::Str, string_literal_help())),
loc!(specialize(EExpr::SingleQuote, single_quote_literal_help())), loc!(specialize(EExpr::SingleQuote, single_quote_literal_help())),
loc!(specialize(EExpr::Number, positive_number_literal_help())), loc!(specialize(EExpr::Number, positive_number_literal_help())),
@ -1509,8 +1510,8 @@ fn parse_expr_operator<'a>(
} }
} }
} }
Err((NoProgress, _, _)) => { Err((NoProgress, expr, e)) => {
todo!() todo!("{:?} {:?}", expr, e)
} }
}, },
} }
@ -1763,7 +1764,6 @@ fn expr_to_pattern_help<'a>(arena: &'a Bump, expr: &Expr<'a>) -> Result<Pattern<
} }
Expr::Underscore(opt_name) => Ok(Pattern::Underscore(opt_name)), Expr::Underscore(opt_name) => Ok(Pattern::Underscore(opt_name)),
Expr::GlobalTag(value) => Ok(Pattern::GlobalTag(value)), Expr::GlobalTag(value) => Ok(Pattern::GlobalTag(value)),
Expr::PrivateTag(value) => Ok(Pattern::PrivateTag(value)),
Expr::OpaqueRef(value) => Ok(Pattern::OpaqueRef(value)), Expr::OpaqueRef(value) => Ok(Pattern::OpaqueRef(value)),
Expr::Apply(loc_val, loc_args, _) => { Expr::Apply(loc_val, loc_args, _) => {
let region = loc_val.region; let region = loc_val.region;
@ -2437,7 +2437,6 @@ where
fn ident_to_expr<'a>(arena: &'a Bump, src: Ident<'a>) -> Expr<'a> { fn ident_to_expr<'a>(arena: &'a Bump, src: Ident<'a>) -> Expr<'a> {
match src { match src {
Ident::GlobalTag(string) => Expr::GlobalTag(string), Ident::GlobalTag(string) => Expr::GlobalTag(string),
Ident::PrivateTag(string) => Expr::PrivateTag(string),
Ident::OpaqueRef(string) => Expr::OpaqueRef(string), Ident::OpaqueRef(string) => Expr::OpaqueRef(string),
Ident::Access { module_name, parts } => { Ident::Access { module_name, parts } => {
let mut iter = parts.iter(); let mut iter = parts.iter();
@ -2762,7 +2761,6 @@ where
"&&" => good!(BinOp::And, 2), "&&" => good!(BinOp::And, 2),
"||" => good!(BinOp::Or, 2), "||" => good!(BinOp::Or, 2),
"//" => good!(BinOp::DoubleSlash, 2), "//" => good!(BinOp::DoubleSlash, 2),
"%%" => good!(BinOp::DoublePercent, 2),
"->" => { "->" => {
// makes no progress, so it does not interfere with `_ if isGood -> ...` // makes no progress, so it does not interfere with `_ if isGood -> ...`
Err((NoProgress, to_error("->", state.pos()), state)) Err((NoProgress, to_error("->", state.pos()), state))

View file

@ -37,9 +37,6 @@ pub enum Ident<'a> {
/// Foo or Bar /// Foo or Bar
GlobalTag(&'a str), GlobalTag(&'a str),
/// @Foo or @Bar /// @Foo or @Bar
PrivateTag(&'a str),
/// $Foo or $Bar
// TODO(opaques): $->@ in the above comment
OpaqueRef(&'a str), OpaqueRef(&'a str),
/// foo or foo.bar or Foo.Bar.baz.qux /// foo or foo.bar or Foo.Bar.baz.qux
Access { Access {
@ -57,7 +54,7 @@ impl<'a> Ident<'a> {
use self::Ident::*; use self::Ident::*;
match self { match self {
GlobalTag(string) | PrivateTag(string) | OpaqueRef(string) => string.len(), GlobalTag(string) | OpaqueRef(string) => string.len(),
Access { module_name, parts } => { Access { module_name, parts } => {
let mut len = if module_name.is_empty() { let mut len = if module_name.is_empty() {
0 0
@ -101,24 +98,7 @@ pub fn lowercase_ident<'a>() -> impl Parser<'a, &'a str, ()> {
} }
pub fn tag_name<'a>() -> impl Parser<'a, &'a str, ()> { pub fn tag_name<'a>() -> impl Parser<'a, &'a str, ()> {
move |arena, state: State<'a>| { move |arena, state: State<'a>| uppercase_ident().parse(arena, state)
if state.bytes().starts_with(b"@") {
match chomp_private_tag_or_opaque(
/* private tag */ true,
state.bytes(),
state.pos(),
) {
Err(BadIdent::Start(_)) => Err((NoProgress, (), state)),
Err(_) => Err((MadeProgress, (), state)),
Ok(ident) => {
let width = ident.len();
Ok((MadeProgress, ident, state.advance(width)))
}
}
} else {
uppercase_ident().parse(arena, state)
}
}
} }
/// This could be: /// This could be:
@ -242,7 +222,6 @@ pub enum BadIdent {
WeirdDotAccess(Position), WeirdDotAccess(Position),
WeirdDotQualified(Position), WeirdDotQualified(Position),
StrayDot(Position), StrayDot(Position),
BadPrivateTag(Position),
BadOpaqueRef(Position), BadOpaqueRef(Position),
} }
@ -311,21 +290,13 @@ fn chomp_accessor(buffer: &[u8], pos: Position) -> Result<&str, BadIdent> {
} }
} }
/// a `@Token` private tag /// a `@Token` opaque
fn chomp_private_tag_or_opaque( fn chomp_opaque_ref(buffer: &[u8], pos: Position) -> Result<&str, BadIdent> {
private_tag: bool, // If false, opaque
buffer: &[u8],
pos: Position,
) -> Result<&str, BadIdent> {
// assumes the leading `@` has NOT been chomped already // assumes the leading `@` has NOT been chomped already
debug_assert_eq!(buffer.get(0), Some(if private_tag { &b'@' } else { &b'$' })); debug_assert_eq!(buffer.get(0), Some(&b'@'));
use encode_unicode::CharExt; use encode_unicode::CharExt;
let bad_ident = if private_tag { let bad_ident = BadIdent::BadOpaqueRef;
BadIdent::BadPrivateTag
} else {
BadIdent::BadOpaqueRef
};
match chomp_uppercase_part(&buffer[1..]) { match chomp_uppercase_part(&buffer[1..]) {
Ok(name) => { Ok(name) => {
@ -362,15 +333,11 @@ fn chomp_identifier_chain<'a>(
} }
Err(fail) => return Err((1, fail)), Err(fail) => return Err((1, fail)),
}, },
c @ ('@' | '$') => match chomp_private_tag_or_opaque(c == '@', buffer, pos) { '@' => match chomp_opaque_ref(buffer, pos) {
Ok(tagname) => { Ok(tagname) => {
let bytes_parsed = tagname.len(); let bytes_parsed = tagname.len();
let ident = if c == '@' { let ident = Ident::OpaqueRef;
Ident::PrivateTag
} else {
Ident::OpaqueRef
};
return Ok((bytes_parsed as u32, ident(tagname))); return Ok((bytes_parsed as u32, ident(tagname)));
} }

View file

@ -240,14 +240,10 @@ fn loc_ident_pattern_help<'a>(
Ok((MadeProgress, loc_tag, state)) Ok((MadeProgress, loc_tag, state))
} }
} }
Ident::PrivateTag(name) | Ident::OpaqueRef(name) => { Ident::OpaqueRef(name) => {
let loc_pat = Loc { let loc_pat = Loc {
region: loc_ident.region, region: loc_ident.region,
value: if matches!(loc_ident.value, Ident::PrivateTag(..)) { value: Pattern::OpaqueRef(name),
Pattern::PrivateTag(name)
} else {
Pattern::OpaqueRef(name)
},
}; };
// Make sure `@Foo Bar 1` is parsed as `@Foo (Bar) 1`, and not `@Foo (Bar 1)` // Make sure `@Foo Bar 1` is parsed as `@Foo (Bar) 1`, and not `@Foo (Bar 1)`

View file

@ -214,16 +214,9 @@ fn tag_type<'a>(min_indent: u32) -> impl Parser<'a, Tag<'a>, ETypeTagUnion<'a>>
let (_, args, state) = specialize_ref(ETypeTagUnion::Type, loc_applied_args_e(min_indent)) let (_, args, state) = specialize_ref(ETypeTagUnion::Type, loc_applied_args_e(min_indent))
.parse(arena, state)?; .parse(arena, state)?;
let result = if name.value.starts_with('@') { let result = Tag::Global {
Tag::Private { name,
name, args: args.into_bump_slice(),
args: args.into_bump_slice(),
}
} else {
Tag::Global {
name,
args: args.into_bump_slice(),
}
}; };
Ok((MadeProgress, result, state)) Ok((MadeProgress, result, state))

View file

@ -1,14 +0,0 @@
Apply(
@0-5 PrivateTag(
"@Whee",
),
[
@6-8 Num(
"12",
),
@9-11 Num(
"34",
),
],
Space,
)

View file

@ -1,3 +0,0 @@
PrivateTag(
"@Whee",
)

View file

@ -1,3 +1,3 @@
OpaqueRef( OpaqueRef(
"$Age", "@Age",
) )

View file

@ -1,6 +1,6 @@
Apply( Apply(
@0-4 OpaqueRef( @0-4 OpaqueRef(
"$Age", "@Age",
), ),
[ [
@5-6 Var { @5-6 Var {

View file

@ -8,7 +8,7 @@ When(
patterns: [ patterns: [
@12-16 SpaceBefore( @12-16 SpaceBefore(
OpaqueRef( OpaqueRef(
"$Age", "@Age",
), ),
[ [
Newline, Newline,

View file

@ -1,2 +1,2 @@
when n is when n is
$Age -> 1 @Age -> 1

View file

@ -9,7 +9,7 @@ When(
@12-20 SpaceBefore( @12-20 SpaceBefore(
Apply( Apply(
@12-16 OpaqueRef( @12-16 OpaqueRef(
"$Add", "@Add",
), ),
[ [
@17-18 Identifier( @17-18 Identifier(

View file

@ -1,2 +1,2 @@
when n is when n is
$Add n m -> n + m @Add n m -> n + m

View 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",
),
),
)

View file

@ -0,0 +1 @@
1 * if True then 1 else 1

View file

@ -1,6 +0,0 @@
MalformedIdent(
"@One.Two.Whee",
BadPrivateTag(
@4,
),
)

View file

@ -122,6 +122,7 @@ mod test_parse {
snapshot_tests! { snapshot_tests! {
fail/type_argument_no_arrow.expr, fail/type_argument_no_arrow.expr,
fail/type_double_comma.expr, fail/type_double_comma.expr,
pass/plus_if.expr,
pass/list_closing_indent_not_enough.expr, pass/list_closing_indent_not_enough.expr,
pass/ability_single_line.expr, pass/ability_single_line.expr,
pass/ability_multi_line.expr, pass/ability_multi_line.expr,
@ -133,7 +134,6 @@ mod test_parse {
pass/annotated_tag_destructure.expr, pass/annotated_tag_destructure.expr,
pass/apply_global_tag.expr, pass/apply_global_tag.expr,
pass/apply_parenthetical_global_tag_args.expr, pass/apply_parenthetical_global_tag_args.expr,
pass/apply_private_tag.expr,
pass/apply_three_args.expr, pass/apply_three_args.expr,
pass/apply_two_args.expr, pass/apply_two_args.expr,
pass/apply_unary_negation.expr, pass/apply_unary_negation.expr,
@ -142,7 +142,6 @@ mod test_parse {
pass/basic_docs.expr, pass/basic_docs.expr,
pass/basic_field.expr, pass/basic_field.expr,
pass/basic_global_tag.expr, pass/basic_global_tag.expr,
pass/basic_private_tag.expr,
pass/basic_var.expr, pass/basic_var.expr,
pass/closure_with_underscores.expr, pass/closure_with_underscores.expr,
pass/comment_after_def.module, pass/comment_after_def.module,
@ -232,7 +231,6 @@ mod test_parse {
pass/pos_inf_float.expr, pass/pos_inf_float.expr,
pass/positive_float.expr, pass/positive_float.expr,
pass/positive_int.expr, pass/positive_int.expr,
pass/private_qualified_tag.expr,
pass/provides_type.header, pass/provides_type.header,
pass/qualified_field.expr, pass/qualified_field.expr,
pass/qualified_global_tag.expr, pass/qualified_global_tag.expr,
@ -316,7 +314,17 @@ mod test_parse {
if std::env::var("ROC_PARSER_SNAPSHOT_TEST_OVERWRITE").is_ok() { if std::env::var("ROC_PARSER_SNAPSHOT_TEST_OVERWRITE").is_ok() {
std::fs::write(&result_path, actual_result).unwrap(); std::fs::write(&result_path, actual_result).unwrap();
} else { } else {
let expected_result = std::fs::read_to_string(&result_path).unwrap(); let expected_result = std::fs::read_to_string(&result_path).unwrap_or_else(|e| {
panic!(
"Error opening test output file {}:\n\
{:?}
Supposing the file is missing, consider running the tests with:\n\
`env ROC_PARSER_SNAPSHOT_TEST_OVERWRITE=1 cargo test ...`\n\
and committing the file that creates.",
result_path.display(),
e
);
});
assert_multiline_str_eq!(expected_result, actual_result); assert_multiline_str_eq!(expected_result, actual_result);
} }
@ -645,114 +653,6 @@ mod test_parse {
// ); // );
// } // }
// #[test]
// fn ann_private_open_union() {
// let arena = Bump::new();
// let newline = bumpalo::vec![in &arena; Newline];
// let newlines = bumpalo::vec![in &arena; Newline, Newline];
// let tag1 = Tag::Private {
// name: Located::new(0, 0, 8, 13, "@True"),
// args: &[],
// };
// let tag2arg1 = Located::new(0, 0, 24, 27, TypeAnnotation::Apply("", "Two", &[]));
// let tag2arg2 = Located::new(0, 0, 28, 34, TypeAnnotation::Apply("", "Things", &[]));
// let tag2args = bumpalo::vec![in &arena; tag2arg1, tag2arg2];
// let tag2 = Tag::Private {
// name: Located::new(0, 0, 15, 23, "@Perhaps"),
// args: tag2args.into_bump_slice(),
// };
// let tags = bumpalo::vec![in &arena;
// Located::new(0, 0, 8, 13, tag1),
// Located::new(0, 0, 15, 34, tag2)
// ];
// let loc_wildcard = Located::new(0, 0, 36, 37, TypeAnnotation::Wildcard);
// let applied_ann = TypeAnnotation::TagUnion {
// tags: tags.into_bump_slice(),
// ext: Some(arena.alloc(loc_wildcard)),
// };
// let signature = Def::Annotation(
// Located::new(0, 0, 0, 3, Identifier("foo")),
// Located::new(0, 0, 6, 37, applied_ann),
// );
// let def = Def::Body(
// arena.alloc(Located::new(1, 1, 0, 3, Identifier("foo"))),
// arena.alloc(Located::new(1, 1, 6, 10, Expr::GlobalTag("True"))),
// );
// let spaced_def = Def::SpaceBefore(arena.alloc(def), newline.into_bump_slice());
// let loc_def = &*arena.alloc(Located::new(1, 1, 0, 10, spaced_def));
// let loc_ann = &*arena.alloc(Located::new(0, 0, 0, 3, signature));
// let defs = &[loc_ann, loc_def];
// let ret = Expr::SpaceBefore(arena.alloc(Num("42")), newlines.into_bump_slice());
// let loc_ret = Located::new(3, 3, 0, 2, ret);
// let expected = Defs(defs, arena.alloc(loc_ret));
// assert_parses_to(
// indoc!(
// r#"
// foo : [ @True, @Perhaps Two Things ]*
// foo = True
// 42
// "#
// ),
// expected,
// );
// }
// #[test]
// fn ann_private_closed_union() {
// let arena = Bump::new();
// let newline = bumpalo::vec![in &arena; Newline];
// let newlines = bumpalo::vec![in &arena; Newline, Newline];
// let tag1 = Tag::Private {
// name: Located::new(0, 0, 8, 13, "@True"),
// args: &[],
// };
// let tag2arg = Located::new(0, 0, 24, 29, TypeAnnotation::Apply("", "Thing", &[]));
// let tag2args = bumpalo::vec![in &arena; tag2arg];
// let tag2 = Tag::Private {
// name: Located::new(0, 0, 15, 23, "@Perhaps"),
// args: tag2args.into_bump_slice(),
// };
// let tags = bumpalo::vec![in &arena;
// Located::new(0, 0, 8, 13, tag1),
// Located::new(0, 0, 15, 29, tag2)
// ];
// let applied_ann = TypeAnnotation::TagUnion {
// tags: tags.into_bump_slice(),
// ext: None,
// };
// let signature = Def::Annotation(
// Located::new(0, 0, 0, 3, Identifier("foo")),
// Located::new(0, 0, 6, 31, applied_ann),
// );
// let def = Def::Body(
// arena.alloc(Located::new(1, 1, 0, 3, Identifier("foo"))),
// arena.alloc(Located::new(1, 1, 6, 10, Expr::GlobalTag("True"))),
// );
// let spaced_def = Def::SpaceBefore(arena.alloc(def), newline.into_bump_slice());
// let loc_def = &*arena.alloc(Located::new(1, 1, 0, 10, spaced_def));
// let loc_ann = &*arena.alloc(Located::new(0, 0, 0, 3, signature));
// let defs = &[loc_ann, loc_def];
// let ret = Expr::SpaceBefore(arena.alloc(Num("42")), newlines.into_bump_slice());
// let loc_ret = Located::new(3, 3, 0, 2, ret);
// let expected = Defs(defs, arena.alloc(loc_ret));
// assert_parses_to(
// indoc!(
// r#"
// foo : [ @True, @Perhaps Thing ]
// foo = True
// 42
// "#
// ),
// expected,
// );
// }
// #[test] // #[test]
// fn ann_global_open_union() { // fn ann_global_open_union() {
// let arena = Bump::new(); // let arena = Bump::new();

View file

@ -377,6 +377,17 @@ impl LineInfo {
end: self.convert_pos(region.end()), end: self.convert_pos(region.end()),
} }
} }
pub fn convert_line_column(&self, lc: LineColumn) -> Position {
let offset = self.line_offsets[lc.line as usize] + lc.column;
Position::new(offset)
}
pub fn convert_line_column_region(&self, lc_region: LineColumnRegion) -> Region {
let start = self.convert_line_column(lc_region.start);
let end = self.convert_line_column(lc_region.end);
Region::new(start, end)
}
} }
#[test] #[test]

View file

@ -6,4 +6,4 @@ license = "UPL-1.0"
edition = "2018" edition = "2018"
[dependencies] [dependencies]
target-lexicon = "0.12.2" target-lexicon = "0.12.3"

View file

@ -28,3 +28,5 @@ pretty_assertions = "1.0.0"
indoc = "1.0.3" indoc = "1.0.3"
tempfile = "3.2.0" tempfile = "3.2.0"
bumpalo = { version = "3.8.0", features = ["collections"] } bumpalo = { version = "3.8.0", features = ["collections"] }
regex = "1.5.5"
lazy_static = "1.4.0"

View file

@ -5,6 +5,7 @@ use roc_can::constraint::Constraint::{self, *};
use roc_can::constraint::{Constraints, LetConstraint}; use roc_can::constraint::{Constraints, LetConstraint};
use roc_can::expected::{Expected, PExpected}; use roc_can::expected::{Expected, PExpected};
use roc_collections::all::MutMap; use roc_collections::all::MutMap;
use roc_error_macros::internal_error;
use roc_module::ident::TagName; use roc_module::ident::TagName;
use roc_module::symbol::Symbol; use roc_module::symbol::Symbol;
use roc_region::all::{Loc, Region}; use roc_region::all::{Loc, Region};
@ -195,20 +196,20 @@ impl Aliases {
register(subs, rank, pools, content) register(subs, rank, pools, content)
} }
/// Instantiate an alias of the form `Foo a : [ @Foo a ]` /// Build an alias of the form `Num range := range`
fn instantiate_num_at_alias( fn build_num_opaque(
subs: &mut Subs, subs: &mut Subs,
rank: Rank, rank: Rank,
pools: &mut Pools, pools: &mut Pools,
tag_name_slice: SubsSlice<TagName>, symbol: Symbol,
range_slice: SubsSlice<Variable>, range_var: Variable,
) -> Variable { ) -> Variable {
let variable_slices = SubsSlice::extend_new(&mut subs.variable_slices, [range_slice]); let content = Content::Alias(
symbol,
let union_tags = UnionTags::from_slices(tag_name_slice, variable_slices); AliasVariables::insert_into_subs(subs, [range_var], []),
let ext_var = Variable::EMPTY_TAG_UNION; range_var,
let flat_type = FlatType::TagUnion(union_tags, ext_var); AliasKind::Opaque,
let content = Content::Structure(flat_type); );
register(subs, rank, pools, content) register(subs, rank, pools, content)
} }
@ -227,126 +228,46 @@ impl Aliases {
Some(var) Some(var)
} }
Symbol::NUM_NUM => { Symbol::NUM_NUM | Symbol::NUM_FLOATINGPOINT | Symbol::NUM_INTEGER => {
let var = Self::instantiate_num_at_alias( // These are opaque types Num range := range (respectively for FloatingPoint and
subs, // Integer). They should not have been built as DelayedAliases!
rank, internal_error!("Attempting to build delayed instantiation of opaque num");
pools,
Subs::NUM_AT_NUM,
SubsSlice::new(alias_variables.variables_start, 1),
);
Some(var)
}
Symbol::NUM_FLOATINGPOINT => {
let var = Self::instantiate_num_at_alias(
subs,
rank,
pools,
Subs::NUM_AT_FLOATINGPOINT,
SubsSlice::new(alias_variables.variables_start, 1),
);
Some(var)
}
Symbol::NUM_INTEGER => {
let var = Self::instantiate_num_at_alias(
subs,
rank,
pools,
Subs::NUM_AT_INTEGER,
SubsSlice::new(alias_variables.variables_start, 1),
);
Some(var)
} }
Symbol::NUM_INT => { Symbol::NUM_INT => {
// [ @Integer range ] // Int range : Num (Integer range)
let integer_content_var = Self::instantiate_builtin_aliases( //
self, // build `Integer range := range`
let integer_content_var = Self::build_num_opaque(
subs, subs,
rank, rank,
pools, pools,
Symbol::NUM_INTEGER, Symbol::NUM_INTEGER,
alias_variables, subs.variables[alias_variables.variables_start as usize],
)
.unwrap();
// Integer range (alias variable is the same as `Int range`)
let integer_alias_variables = alias_variables;
let integer_content = Content::Alias(
Symbol::NUM_INTEGER,
integer_alias_variables,
integer_content_var,
AliasKind::Structural,
);
let integer_alias_var = register(subs, rank, pools, integer_content);
// [ @Num (Integer range) ]
let num_alias_variables =
AliasVariables::insert_into_subs(subs, [integer_alias_var], []);
let num_content_var = Self::instantiate_builtin_aliases(
self,
subs,
rank,
pools,
Symbol::NUM_NUM,
num_alias_variables,
)
.unwrap();
let num_content = Content::Alias(
Symbol::NUM_NUM,
num_alias_variables,
num_content_var,
AliasKind::Structural,
); );
Some(register(subs, rank, pools, num_content)) // build `Num (Integer range) := Integer range`
let num_content_var =
Self::build_num_opaque(subs, rank, pools, Symbol::NUM_NUM, integer_content_var);
Some(num_content_var)
} }
Symbol::NUM_FLOAT => { Symbol::NUM_FLOAT => {
// [ @FloatingPoint range ] // Float range : Num (FloatingPoint range)
let fpoint_content_var = Self::instantiate_builtin_aliases( //
self, // build `FloatingPoint range := range`
let fpoint_content_var = Self::build_num_opaque(
subs, subs,
rank, rank,
pools, pools,
Symbol::NUM_FLOATINGPOINT, Symbol::NUM_FLOATINGPOINT,
alias_variables, subs.variables[alias_variables.variables_start as usize],
)
.unwrap();
// FloatingPoint range (alias variable is the same as `Float range`)
let fpoint_alias_variables = alias_variables;
let fpoint_content = Content::Alias(
Symbol::NUM_FLOATINGPOINT,
fpoint_alias_variables,
fpoint_content_var,
AliasKind::Structural,
);
let fpoint_alias_var = register(subs, rank, pools, fpoint_content);
// [ @Num (FloatingPoint range) ]
let num_alias_variables =
AliasVariables::insert_into_subs(subs, [fpoint_alias_var], []);
let num_content_var = Self::instantiate_builtin_aliases(
self,
subs,
rank,
pools,
Symbol::NUM_NUM,
num_alias_variables,
)
.unwrap();
let num_content = Content::Alias(
Symbol::NUM_NUM,
num_alias_variables,
num_content_var,
AliasKind::Structural,
); );
Some(register(subs, rank, pools, num_content)) // build `Num (FloatingPoint range) := FloatingPoint range`
let num_content_var =
Self::build_num_opaque(subs, rank, pools, Symbol::NUM_NUM, fpoint_content_var);
Some(num_content_var)
} }
_ => None, _ => None,
} }
@ -1183,26 +1104,34 @@ fn solve(
let actual = let actual =
either_type_index_to_var(constraints, subs, rank, pools, aliases, *type_index); either_type_index_to_var(constraints, subs, rank, pools, aliases, *type_index);
let mut new_desc = subs.get(actual); let mut stack = vec![actual];
match new_desc.content { while let Some(var) = stack.pop() {
Content::Structure(FlatType::TagUnion(tags, _)) => { use {Content::*, FlatType::*};
let new_ext = subs.fresh_unnamed_flex_var();
subs.set_rank(new_ext, new_desc.rank); let mut desc = subs.get(var);
let new_union = Content::Structure(FlatType::TagUnion(tags, new_ext)); if let Structure(TagUnion(tags, ext)) = desc.content {
new_desc.content = new_union; if let Structure(EmptyTagUnion) = subs.get_content_without_compacting(ext) {
subs.set(actual, new_desc); let new_ext = subs.fresh_unnamed_flex_var();
state subs.set_rank(new_ext, desc.rank);
} let new_union = Structure(TagUnion(tags, new_ext));
_ => { desc.content = new_union;
// Today, an "open" constraint doesn't affect any types subs.set(var, desc);
// other than tag unions. Recursive tag unions are constructed }
// at a later time (during occurs checks after tag unions are
// resolved), so that's not handled here either. // Also open up all nested tag unions.
// NB: Handle record types here if we add presence constraints let all_vars = tags.variables().into_iter();
// to their type inference as well. stack.extend(all_vars.flat_map(|slice| subs[slice]).map(|var| subs[var]));
state
} }
// Today, an "open" constraint doesn't affect any types
// other than tag unions. Recursive tag unions are constructed
// at a later time (during occurs checks after tag unions are
// resolved), so that's not handled here either.
// NB: Handle record types here if we add presence constraints
// to their type inference as well.
} }
state
} }
IncludesTag(index) => { IncludesTag(index) => {
let includes_tag = &constraints.includes_tags[index.index()]; let includes_tag = &constraints.includes_tags[index.index()];

View file

@ -10,14 +10,54 @@ mod helpers;
#[cfg(test)] #[cfg(test)]
mod solve_expr { mod solve_expr {
use crate::helpers::with_larger_debug_stack; use crate::helpers::with_larger_debug_stack;
use lazy_static::lazy_static;
use regex::Regex;
use roc_can::traverse::find_type_at;
use roc_load::LoadedModule; use roc_load::LoadedModule;
use roc_module::symbol::{Interns, ModuleId};
use roc_problem::can::Problem;
use roc_region::all::{LineColumn, LineColumnRegion, LineInfo, Region};
use roc_reporting::report::{can_problem, type_problem, RocDocAllocator};
use roc_solve::solve::TypeError;
use roc_types::pretty_print::{content_to_string, name_all_type_vars}; use roc_types::pretty_print::{content_to_string, name_all_type_vars};
use std::path::PathBuf;
// HELPERS // HELPERS
fn run_load_and_infer(src: &str) -> Result<LoadedModule, std::io::Error> { lazy_static! {
static ref RE_TYPE_QUERY: Regex = Regex::new(r#"^\s*#\s*(?P<where>\^+)\s*$"#).unwrap();
}
#[derive(Debug, Clone, Copy)]
struct TypeQuery(Region);
fn parse_queries(src: &str) -> Vec<TypeQuery> {
let line_info = LineInfo::new(src);
let mut queries = vec![];
for (i, line) in src.lines().enumerate() {
if let Some(capture) = RE_TYPE_QUERY.captures(line) {
let wher = capture.name("where").unwrap();
let (start, end) = (wher.start() as u32, wher.end() as u32);
let last_line = i as u32 - 1;
let start_lc = LineColumn {
line: last_line,
column: start,
};
let end_lc = LineColumn {
line: last_line,
column: end,
};
let lc_region = LineColumnRegion::new(start_lc, end_lc);
let region = line_info.convert_line_column_region(lc_region);
queries.push(TypeQuery(region));
}
}
queries
}
fn run_load_and_infer(src: &str) -> Result<(LoadedModule, String), std::io::Error> {
use bumpalo::Bump; use bumpalo::Bump;
use std::path::PathBuf;
use tempfile::tempdir; use tempfile::tempdir;
let arena = &Bump::new(); let arena = &Bump::new();
@ -54,51 +94,78 @@ mod solve_expr {
}; };
let loaded = loaded.expect("failed to load module"); let loaded = loaded.expect("failed to load module");
Ok(loaded) Ok((loaded, module_src.to_string()))
} }
fn infer_eq_help( fn format_problems(
src: &str, src: &str,
) -> Result< home: ModuleId,
( interns: &Interns,
Vec<roc_solve::solve::TypeError>, can_problems: Vec<Problem>,
Vec<roc_problem::can::Problem>, type_problems: Vec<TypeError>,
String, ) -> (String, String) {
), let filename = PathBuf::from("test.roc");
std::io::Error, let src_lines: Vec<&str> = src.split('\n').collect();
> { let lines = LineInfo::new(src);
let LoadedModule { let alloc = RocDocAllocator::new(&src_lines, home, &interns);
module_id: home,
mut can_problems, let mut can_reports = vec![];
mut type_problems, let mut type_reports = vec![];
interns,
mut solved, for problem in can_problems {
exposed_to_host, let report = can_problem(&alloc, &lines, filename.clone(), problem.clone());
.. can_reports.push(report.pretty(&alloc));
} = run_load_and_infer(src)?; }
for problem in type_problems {
if let Some(report) = type_problem(&alloc, &lines, filename.clone(), problem.clone()) {
type_reports.push(report.pretty(&alloc));
}
}
let mut can_reports_buf = String::new();
let mut type_reports_buf = String::new();
use roc_reporting::report::CiWrite;
alloc
.stack(can_reports)
.1
.render_raw(70, &mut CiWrite::new(&mut can_reports_buf))
.unwrap();
alloc
.stack(type_reports)
.1
.render_raw(70, &mut CiWrite::new(&mut type_reports_buf))
.unwrap();
(can_reports_buf, type_reports_buf)
}
fn infer_eq_help(src: &str) -> Result<(String, String, String), std::io::Error> {
let (
LoadedModule {
module_id: home,
mut can_problems,
mut type_problems,
interns,
mut solved,
exposed_to_host,
..
},
src,
) = run_load_and_infer(src)?;
let mut can_problems = can_problems.remove(&home).unwrap_or_default(); let mut can_problems = can_problems.remove(&home).unwrap_or_default();
let type_problems = type_problems.remove(&home).unwrap_or_default(); let type_problems = type_problems.remove(&home).unwrap_or_default();
// Disregard UnusedDef problems, because those are unavoidable when
// returning a function from the test expression.
can_problems.retain(|prob| !matches!(prob, roc_problem::can::Problem::UnusedDef(_, _)));
let (can_problems, type_problems) =
format_problems(&src, home, &interns, can_problems, type_problems);
let subs = solved.inner_mut(); let subs = solved.inner_mut();
// assert!(can_problems.is_empty());
// assert!(type_problems.is_empty());
// let CanExprOut {
// output,
// var_store,
// var,
// constraint,
// home,
// interns,
// problems: mut can_problems,
// ..
// } = can_expr(src);
// let mut subs = Subs::new(var_store.into());
// TODO fix this
// assert_correct_variable_usage(&constraint);
// name type vars // name type vars
for var in exposed_to_host.values() { for var in exposed_to_host.values() {
name_all_type_vars(*var, subs); name_all_type_vars(*var, subs);
@ -112,10 +179,6 @@ mod solve_expr {
let actual_str = content_to_string(content, subs, home, &interns); let actual_str = content_to_string(content, subs, home, &interns);
// Disregard UnusedDef problems, because those are unavoidable when
// returning a function from the test expression.
can_problems.retain(|prob| !matches!(prob, roc_problem::can::Problem::UnusedDef(_, _)));
Ok((type_problems, can_problems, actual_str)) Ok((type_problems, can_problems, actual_str))
} }
@ -143,7 +206,11 @@ mod solve_expr {
fn infer_eq(src: &str, expected: &str) { fn infer_eq(src: &str, expected: &str) {
let (_, can_problems, actual) = infer_eq_help(src).unwrap(); let (_, can_problems, actual) = infer_eq_help(src).unwrap();
assert_eq!(can_problems, Vec::new(), "Canonicalization problems: "); assert!(
can_problems.is_empty(),
"Canonicalization problems: {}",
can_problems
);
assert_eq!(actual, expected.to_string()); assert_eq!(actual, expected.to_string());
} }
@ -151,16 +218,73 @@ mod solve_expr {
fn infer_eq_without_problem(src: &str, expected: &str) { fn infer_eq_without_problem(src: &str, expected: &str) {
let (type_problems, can_problems, actual) = infer_eq_help(src).unwrap(); let (type_problems, can_problems, actual) = infer_eq_help(src).unwrap();
assert_eq!(can_problems, Vec::new(), "Canonicalization problems: "); assert!(
can_problems.is_empty(),
"Canonicalization problems: {}",
can_problems
);
if !type_problems.is_empty() { if !type_problems.is_empty() {
// fail with an assert, but print the problems normally so rust doesn't try to diff // fail with an assert, but print the problems normally so rust doesn't try to diff
// an empty vec with the problems. // an empty vec with the problems.
panic!("expected:\n{:?}\ninferred:\n{:?}", expected, actual); panic!(
"expected:\n{:?}\ninferred:\n{:?}\nproblems:\n{}",
expected, actual, type_problems,
);
} }
assert_eq!(actual, expected.to_string()); assert_eq!(actual, expected.to_string());
} }
fn infer_queries(src: &str, expected: &[&'static str]) {
let (
LoadedModule {
module_id: home,
mut can_problems,
mut type_problems,
mut declarations_by_id,
mut solved,
interns,
..
},
src,
) = run_load_and_infer(src).unwrap();
let decls = declarations_by_id.remove(&home).unwrap();
let subs = solved.inner_mut();
let can_problems = can_problems.remove(&home).unwrap_or_default();
let type_problems = type_problems.remove(&home).unwrap_or_default();
let (can_problems, type_problems) =
format_problems(&src, home, &interns, can_problems, type_problems);
assert!(
can_problems.is_empty(),
"Canonicalization problems: {}",
can_problems
);
assert!(type_problems.is_empty(), "Type problems: {}", type_problems);
let queries = parse_queries(&src);
assert!(!queries.is_empty(), "No queries provided!");
let mut solved_queries = Vec::with_capacity(queries.len());
for TypeQuery(region) in queries.into_iter() {
let start = region.start().offset;
let end = region.end().offset;
let text = &src[start as usize..end as usize];
let var = find_type_at(region, &decls).expect(&format!("No type for {}!", &text));
name_all_type_vars(var, subs);
let content = subs.get_content_without_compacting(var);
let actual_str = content_to_string(content, subs, home, &interns);
solved_queries.push(format!("{} : {}", text, actual_str));
}
assert_eq!(solved_queries, expected)
}
fn check_inferred_abilities<'a, I>(src: &'a str, expected_specializations: I) fn check_inferred_abilities<'a, I>(src: &'a str, expected_specializations: I)
where where
I: IntoIterator<Item = (&'a str, &'a str)>, I: IntoIterator<Item = (&'a str, &'a str)>,
@ -172,7 +296,7 @@ mod solve_expr {
interns, interns,
abilities_store, abilities_store,
.. ..
} = run_load_and_infer(src).unwrap(); } = run_load_and_infer(src).unwrap().0;
let can_problems = can_problems.remove(&home).unwrap_or_default(); let can_problems = can_problems.remove(&home).unwrap_or_default();
let type_problems = type_problems.remove(&home).unwrap_or_default(); let type_problems = type_problems.remove(&home).unwrap_or_default();
@ -1383,18 +1507,6 @@ mod solve_expr {
); );
} }
#[test]
fn single_private_tag_pattern() {
infer_eq(
indoc!(
r#"
\@Foo -> 42
"#
),
"[ @Foo ] -> Num *",
);
}
#[test] #[test]
fn two_tag_pattern() { fn two_tag_pattern() {
infer_eq( infer_eq(
@ -1422,18 +1534,6 @@ mod solve_expr {
); );
} }
#[test]
fn private_tag_application() {
infer_eq(
indoc!(
r#"
@Foo "happy" 2020
"#
),
"[ @Foo Str (Num *) ]*",
);
}
#[test] #[test]
fn record_extraction() { fn record_extraction() {
infer_eq( infer_eq(
@ -1500,19 +1600,6 @@ mod solve_expr {
); );
} }
#[test]
fn private_tag_with_field() {
infer_eq(
indoc!(
r#"
when @Foo "blah" is
@Foo x -> x
"#
),
"Str",
);
}
#[test] #[test]
fn qualified_annotation_num_integer() { fn qualified_annotation_num_integer() {
infer_eq( infer_eq(
@ -4115,31 +4202,6 @@ mod solve_expr {
); );
} }
#[test]
fn double_tag_application_pattern_private() {
infer_eq_without_problem(
indoc!(
r#"
app "test" provides [ main ] to "./platform"
Foo : [ @Foo [ @Bar ] I64, @Empty ]
foo : Foo
foo = @Foo @Bar 1
main =
when foo is
@Foo @Bar 1 ->
@Foo @Bar 2
x ->
x
"#
),
"[ @Empty, @Foo [ @Bar ] I64 ]",
);
}
#[test] #[test]
fn recursive_function_with_rigid() { fn recursive_function_with_rigid() {
infer_eq_without_problem( infer_eq_without_problem(
@ -5313,6 +5375,24 @@ mod solve_expr {
) )
} }
#[test]
fn issue_2458_swapped_order() {
infer_eq_without_problem(
indoc!(
r#"
Bar a : Foo a
Foo a : [ Blah (Result (Bar a) { val: a }) ]
v : Bar U8
v = Blah (Ok (Blah (Err { val: 1 })))
v
"#
),
"Bar U8",
)
}
// https://github.com/rtfeldman/roc/issues/2379 // https://github.com/rtfeldman/roc/issues/2379
#[test] #[test]
fn copy_vars_referencing_copied_vars() { fn copy_vars_referencing_copied_vars() {
@ -5400,7 +5480,7 @@ mod solve_expr {
r#" r#"
Age := U32 Age := U32
$Age 21 @Age 21
"# "#
), ),
r#"Age"#, r#"Age"#,
@ -5415,7 +5495,7 @@ mod solve_expr {
Age := U32 Age := U32
a : Age a : Age
a = $Age 21 a = @Age 21
a a
"# "#
@ -5431,7 +5511,7 @@ mod solve_expr {
r#" r#"
Id n := [ Id U32 n ] Id n := [ Id U32 n ]
$Id (Id 21 "sasha") @Id (Id 21 "sasha")
"# "#
), ),
r#"Id Str"#, r#"Id Str"#,
@ -5446,7 +5526,7 @@ mod solve_expr {
Id n := [ Id U32 n ] Id n := [ Id U32 n ]
a : Id Str a : Id Str
a = $Id (Id 21 "sasha") a = @Id (Id 21 "sasha")
a a
"# "#
@ -5464,8 +5544,8 @@ mod solve_expr {
condition : Bool condition : Bool
if condition if condition
then $Id (Id 21 (Y "sasha")) then @Id (Id 21 (Y "sasha"))
else $Id (Id 21 (Z "felix")) else @Id (Id 21 (Z "felix"))
"# "#
), ),
r#"Id [ Y Str, Z Str ]*"#, r#"Id [ Y Str, Z Str ]*"#,
@ -5483,8 +5563,8 @@ mod solve_expr {
v : Id [ Y Str, Z Str ] v : Id [ Y Str, Z Str ]
v = v =
if condition if condition
then $Id (Id 21 (Y "sasha")) then @Id (Id 21 (Y "sasha"))
else $Id (Id 21 (Z "felix")) else @Id (Id 21 (Z "felix"))
v v
"# "#
@ -5500,7 +5580,7 @@ mod solve_expr {
r#" r#"
Age := U32 Age := U32
\$Age n -> n \@Age n -> n
"# "#
), ),
r#"Age -> U32"#, r#"Age -> U32"#,
@ -5515,7 +5595,7 @@ mod solve_expr {
Age := U32 Age := U32
v : Age -> U32 v : Age -> U32
v = \$Age n -> n v = \@Age n -> n
v v
"# "#
), ),
@ -5530,7 +5610,7 @@ mod solve_expr {
r#" r#"
Id n := [ Id U32 n ] Id n := [ Id U32 n ]
\$Id (Id _ n) -> n \@Id (Id _ n) -> n
"# "#
), ),
r#"Id a -> a"#, r#"Id a -> a"#,
@ -5545,7 +5625,7 @@ mod solve_expr {
Id n := [ Id U32 n ] Id n := [ Id U32 n ]
v : Id a -> a v : Id a -> a
v = \$Id (Id _ n) -> n v = \@Id (Id _ n) -> n
v v
"# "#
@ -5563,7 +5643,7 @@ mod solve_expr {
strToBool : Str -> Bool strToBool : Str -> Bool
\$Id (Id _ n) -> strToBool n \@Id (Id _ n) -> strToBool n
"# "#
), ),
r#"Id Str -> Bool"#, r#"Id Str -> Bool"#,
@ -5580,7 +5660,7 @@ mod solve_expr {
strToBool : Str -> Bool strToBool : Str -> Bool
v : Id Str -> Bool v : Id Str -> Bool
v = \$Id (Id _ n) -> strToBool n v = \@Id (Id _ n) -> strToBool n
v v
"# "#
@ -5598,9 +5678,9 @@ mod solve_expr {
\id -> \id ->
when id is when id is
$Id (Id _ A) -> "" @Id (Id _ A) -> ""
$Id (Id _ B) -> "" @Id (Id _ B) -> ""
$Id (Id _ (C { a: "" })) -> "" @Id (Id _ (C { a: "" })) -> ""
"# "#
), ),
r#"Id [ A, B, C { a : Str }* ] -> Str"#, r#"Id [ A, B, C { a : Str }* ] -> Str"#,
@ -5617,9 +5697,9 @@ mod solve_expr {
f : Id [ A, B, C { a : Str }e ] -> Str f : Id [ A, B, C { a : Str }e ] -> Str
f = \id -> f = \id ->
when id is when id is
$Id (Id _ A) -> "" @Id (Id _ A) -> ""
$Id (Id _ B) -> "" @Id (Id _ B) -> ""
$Id (Id _ (C { a: "" })) -> "" @Id (Id _ (C { a: "" })) -> ""
f f
"# "#
@ -5635,7 +5715,7 @@ mod solve_expr {
r#" r#"
app "test" provides [ effectAlways ] to "./platform" app "test" provides [ effectAlways ] to "./platform"
Effect a : [ @Effect ({} -> a) ] Effect a := {} -> a
effectAlways : a -> Effect a effectAlways : a -> Effect a
effectAlways = \x -> effectAlways = \x ->
@ -5703,8 +5783,8 @@ mod solve_expr {
insert : Outer k, k -> Outer k insert : Outer k, k -> Outer k
insert = \m, var -> insert = \m, var ->
when m is when m is
$Outer Empty -> $Outer (Wrapped var) @Outer Empty -> @Outer (Wrapped var)
$Outer (Wrapped _) -> $Outer (Wrapped var) @Outer (Wrapped _) -> @Outer (Wrapped var)
insert insert
"# "#
@ -5720,9 +5800,9 @@ mod solve_expr {
r#" r#"
Outer k := [ Empty, Wrapped k ] Outer k := [ Empty, Wrapped k ]
when ($Outer Empty) is when (@Outer Empty) is
$Outer Empty -> $Outer (Wrapped "") @Outer Empty -> @Outer (Wrapped "")
$Outer (Wrapped k) -> $Outer (Wrapped k) @Outer (Wrapped k) -> @Outer (Wrapped k)
"# "#
), ),
r#"Outer Str"#, r#"Outer Str"#,
@ -5736,9 +5816,9 @@ mod solve_expr {
r#" r#"
Outer := [ A, B ] Outer := [ A, B ]
when ($Outer A) is when (@Outer A) is
$Outer A -> $Outer A @Outer A -> @Outer A
$Outer B -> $Outer B @Outer B -> @Outer B
"# "#
), ),
r#"Outer"#, r#"Outer"#,
@ -5795,7 +5875,7 @@ mod solve_expr {
Id := U64 Id := U64
hash = \$Id n -> n hash = \@Id n -> n
"# "#
), ),
[("Hash:hash", "Id")], [("Hash:hash", "Id")],
@ -5815,8 +5895,8 @@ mod solve_expr {
Id := U64 Id := U64
hash = \$Id n -> n hash = \@Id n -> n
hash32 = \$Id n -> Num.toU32 n hash32 = \@Id n -> Num.toU32 n
"# "#
), ),
[("Hash:hash", "Id"), ("Hash:hash32", "Id")], [("Hash:hash", "Id"), ("Hash:hash32", "Id")],
@ -5840,11 +5920,11 @@ mod solve_expr {
Id := U64 Id := U64
hash = \$Id n -> n hash = \@Id n -> n
hash32 = \$Id n -> Num.toU32 n hash32 = \@Id n -> Num.toU32 n
eq = \$Id m, $Id n -> m == n eq = \@Id m, @Id n -> m == n
le = \$Id m, $Id n -> m < n le = \@Id m, @Id n -> m < n
"# "#
), ),
[ [
@ -5869,7 +5949,7 @@ mod solve_expr {
Id := U64 Id := U64
hash : Id -> U64 hash : Id -> U64
hash = \$Id n -> n hash = \@Id n -> n
"# "#
), ),
[("Hash:hash", "Id")], [("Hash:hash", "Id")],
@ -5907,9 +5987,9 @@ mod solve_expr {
Id := U64 Id := U64
hash = \$Id n -> n hash = \@Id n -> n
zero = hash ($Id 0) zero = hash (@Id 0)
"# "#
), ),
"U64", "U64",
@ -6000,9 +6080,9 @@ mod solve_expr {
hashEq = \x, y -> hash x == hash y hashEq = \x, y -> hash x == hash y
Id := U64 Id := U64
hash = \$Id n -> n hash = \@Id n -> n
result = hashEq ($Id 100) ($Id 101) result = hashEq (@Id 100) (@Id 101)
"# "#
), ),
"Bool", "Bool",
@ -6022,15 +6102,88 @@ mod solve_expr {
mulHashes = \x, y -> hash x * hash y mulHashes = \x, y -> hash x * hash y
Id := U64 Id := U64
hash = \$Id n -> n hash = \@Id n -> n
Three := {} Three := {}
hash = \$Three _ -> 3 hash = \@Three _ -> 3
result = mulHashes ($Id 100) ($Three {}) result = mulHashes (@Id 100) (@Three {})
"# "#
), ),
"U64", "U64",
) )
} }
#[test]
fn intermediate_branch_types() {
infer_queries(
indoc!(
r#"
app "test" provides [ foo ] to "./platform"
foo : Bool -> Str
foo = \ob ->
# ^^
when ob is
# ^^
True -> "A"
# ^^^^
False -> "B"
# ^^^^^
"#
),
&[
"ob : Bool",
"ob : [ False, True ]",
"True : [ False, True ]",
"False : [ False, True ]",
],
)
}
#[test]
fn nested_open_tag_union() {
infer_eq_without_problem(
indoc!(
r#"
app "test" provides [ go ] to "./platform"
Expr : [
Wrap Expr,
Val I64,
]
go : Expr -> Expr
go = \e ->
when P e is
P (Wrap (Val _)) -> Wrap e
# This branch should force the first argument to `P` and
# the first argument to `Wrap` to be an open tag union.
# This tests checks that we don't regress on that.
P y1 -> Wrap y1
"#
),
indoc!(r#"Expr -> Expr"#),
)
}
#[test]
fn opaque_and_alias_unify() {
infer_eq_without_problem(
indoc!(
r#"
app "test" provides [ always ] to "./platform"
Effect a := {} -> a
Task a err : Effect (Result a err)
always : a -> Task a *
always = \x -> @Effect (\{} -> Ok x)
"#
),
"a -> Task a *",
);
}
} }

View file

@ -37,7 +37,7 @@ bumpalo = { version = "3.8.0", features = ["collections"] }
either = "1.6.1" either = "1.6.1"
libc = "0.2.106" libc = "0.2.106"
inkwell = { path = "../../vendor/inkwell" } inkwell = { path = "../../vendor/inkwell" }
target-lexicon = "0.12.2" target-lexicon = "0.12.3"
libloading = "0.7.1" libloading = "0.7.1"
wasmer-wasi = "2.0.0" wasmer-wasi = "2.0.0"
tempfile = "3.2.0" tempfile = "3.2.0"

View file

@ -23,9 +23,9 @@ fn hash_specialization() {
Id := U64 Id := U64
hash = \$Id n -> n hash = \@Id n -> n
main = hash ($Id 1234) main = hash (@Id 1234)
"# "#
), ),
1234, 1234,
@ -46,13 +46,13 @@ fn hash_specialization_multiple_add() {
Id := U64 Id := U64
hash = \$Id n -> n hash = \@Id n -> n
One := {} One := {}
hash = \$One _ -> 1 hash = \@One _ -> 1
main = hash ($Id 1234) + hash ($One {}) main = hash (@Id 1234) + hash (@One {})
"# "#
), ),
1235, 1235,
@ -73,11 +73,11 @@ fn alias_member_specialization() {
Id := U64 Id := U64
hash = \$Id n -> n hash = \@Id n -> n
main = main =
aliasedHash = hash aliasedHash = hash
aliasedHash ($Id 1234) aliasedHash (@Id 1234)
"# "#
), ),
1234, 1234,
@ -100,9 +100,9 @@ fn ability_constrained_in_non_member_usage() {
mulHashes = \x, y -> hash x * hash y mulHashes = \x, y -> hash x * hash y
Id := U64 Id := U64
hash = \$Id n -> n hash = \@Id n -> n
result = mulHashes ($Id 5) ($Id 7) result = mulHashes (@Id 5) (@Id 7)
"# "#
), ),
35, 35,
@ -124,9 +124,9 @@ fn ability_constrained_in_non_member_usage_inferred() {
mulHashes = \x, y -> hash x * hash y mulHashes = \x, y -> hash x * hash y
Id := U64 Id := U64
hash = \$Id n -> n hash = \@Id n -> n
result = mulHashes ($Id 5) ($Id 7) result = mulHashes (@Id 5) (@Id 7)
"# "#
), ),
35, 35,
@ -149,12 +149,12 @@ fn ability_constrained_in_non_member_multiple_specializations() {
mulHashes = \x, y -> hash x * hash y mulHashes = \x, y -> hash x * hash y
Id := U64 Id := U64
hash = \$Id n -> n hash = \@Id n -> n
Three := {} Three := {}
hash = \$Three _ -> 3 hash = \@Three _ -> 3
result = mulHashes ($Id 100) ($Three {}) result = mulHashes (@Id 100) (@Three {})
"# "#
), ),
300, 300,
@ -176,12 +176,12 @@ fn ability_constrained_in_non_member_multiple_specializations_inferred() {
mulHashes = \x, y -> hash x * hash y mulHashes = \x, y -> hash x * hash y
Id := U64 Id := U64
hash = \$Id n -> n hash = \@Id n -> n
Three := {} Three := {}
hash = \$Three _ -> 3 hash = \@Three _ -> 3
result = mulHashes ($Id 100) ($Three {}) result = mulHashes (@Id 100) (@Three {})
"# "#
), ),
300, 300,
@ -204,12 +204,12 @@ fn ability_used_as_type_still_compiles() {
mulHashes = \x, y -> hash x * hash y mulHashes = \x, y -> hash x * hash y
Id := U64 Id := U64
hash = \$Id n -> n hash = \@Id n -> n
Three := {} Three := {}
hash = \$Three _ -> 3 hash = \@Three _ -> 3
result = mulHashes ($Id 100) ($Three {}) result = mulHashes (@Id 100) (@Three {})
"# "#
), ),
300, 300,

View file

@ -2534,7 +2534,7 @@ fn list_keep_oks() {
RocList<i64> RocList<i64>
); );
assert_evals_to!( assert_evals_to!(
"List.keepOks [1,2] (\\x -> x % 2)", "List.keepOks [1,2] (\\x -> Num.remChecked x 2)",
RocList::from_slice(&[1, 0]), RocList::from_slice(&[1, 0]),
RocList<i64> RocList<i64>
); );
@ -2561,7 +2561,7 @@ fn list_keep_errs() {
assert_evals_to!( assert_evals_to!(
indoc!( indoc!(
r#" r#"
List.keepErrs [0,1,2] (\x -> x % 0 |> Result.mapErr (\_ -> 32)) List.keepErrs [0,1,2] (\x -> Num.remChecked x 0 |> Result.mapErr (\_ -> 32))
"# "#
), ),
RocList::from_slice(&[32, 32, 32]), RocList::from_slice(&[32, 32, 32]),

View file

@ -473,7 +473,7 @@ fn f64_sqrt() {
assert_evals_to!( assert_evals_to!(
indoc!( indoc!(
r#" r#"
when Num.sqrt 100 is when Num.sqrtChecked 100 is
Ok val -> val Ok val -> val
Err _ -> -1 Err _ -> -1
"# "#
@ -489,9 +489,7 @@ fn f64_log() {
assert_evals_to!( assert_evals_to!(
indoc!( indoc!(
r#" r#"
when Num.log 7.38905609893 is Num.log 7.38905609893
Ok val -> val
Err _ -> -1
"# "#
), ),
1.999999999999912, 1.999999999999912,
@ -501,11 +499,11 @@ fn f64_log() {
#[test] #[test]
#[cfg(any(feature = "gen-llvm"))] #[cfg(any(feature = "gen-llvm"))]
fn f64_log_one() { fn f64_log_checked_one() {
assert_evals_to!( assert_evals_to!(
indoc!( indoc!(
r#" r#"
when Num.log 1 is when Num.logChecked 1 is
Ok val -> val Ok val -> val
Err _ -> -1 Err _ -> -1
"# "#
@ -521,7 +519,7 @@ fn f64_sqrt_zero() {
assert_evals_to!( assert_evals_to!(
indoc!( indoc!(
r#" r#"
when Num.sqrt 0 is when Num.sqrtChecked 0 is
Ok val -> val Ok val -> val
Err _ -> -1 Err _ -> -1
"# "#
@ -533,11 +531,11 @@ fn f64_sqrt_zero() {
#[test] #[test]
#[cfg(any(feature = "gen-llvm"))] #[cfg(any(feature = "gen-llvm"))]
fn f64_sqrt_negative() { fn f64_sqrt_checked_negative() {
assert_evals_to!( assert_evals_to!(
indoc!( indoc!(
r#" r#"
when Num.sqrt -1 is when Num.sqrtChecked -1 is
Err _ -> 42 Err _ -> 42
Ok val -> val Ok val -> val
"# "#
@ -549,11 +547,11 @@ fn f64_sqrt_negative() {
#[test] #[test]
#[cfg(any(feature = "gen-llvm"))] #[cfg(any(feature = "gen-llvm"))]
fn f64_log_zero() { fn f64_log_checked_zero() {
assert_evals_to!( assert_evals_to!(
indoc!( indoc!(
r#" r#"
when Num.log 0 is when Num.logChecked 0 is
Err _ -> 42 Err _ -> 42
Ok val -> val Ok val -> val
"# "#
@ -569,13 +567,12 @@ fn f64_log_negative() {
assert_evals_to!( assert_evals_to!(
indoc!( indoc!(
r#" r#"
when Num.log -1 is Num.log -1
Err _ -> 42
Ok val -> val
"# "#
), ),
42.0, true,
f64 f64,
|f: f64| f.is_nan()
); );
} }
@ -1082,9 +1079,7 @@ fn gen_rem_i64() {
assert_evals_to!( assert_evals_to!(
indoc!( indoc!(
r#" r#"
when Num.rem 8 3 is Num.rem 8 3
Ok val -> val
Err _ -> -1
"# "#
), ),
2, 2,
@ -1094,11 +1089,11 @@ fn gen_rem_i64() {
#[test] #[test]
#[cfg(any(feature = "gen-llvm"))] #[cfg(any(feature = "gen-llvm"))]
fn gen_rem_div_by_zero_i64() { fn gen_rem_checked_div_by_zero_i64() {
assert_evals_to!( assert_evals_to!(
indoc!( indoc!(
r#" r#"
when Num.rem 8 0 is when Num.remChecked 8 0 is
Err DivByZero -> 4 Err DivByZero -> 4
Ok _ -> -23 Ok _ -> -23
"# "#

View file

@ -593,6 +593,27 @@ fn top_level_constant() {
); );
} }
#[test]
#[ignore]
#[cfg(any(feature = "gen-llvm", feature = "gen-dev", feature = "gen-wasm"))]
fn top_level_destructure() {
assert_evals_to!(
indoc!(
r#"
app "test" provides [ main ] to "./platform"
{a, b} = { a: 1, b: 2 }
main =
a + b
"#
),
3,
i64
);
}
#[test] #[test]
#[cfg(any(feature = "gen-llvm"))] #[cfg(any(feature = "gen-llvm"))]
fn linked_list_len_0() { fn linked_list_len_0() {
@ -1113,7 +1134,7 @@ fn io_poc_effect() {
r#" r#"
app "test" provides [ main ] to "./platform" app "test" provides [ main ] to "./platform"
Effect a : [ @Effect ({} -> a) ] Effect a := {} -> a
succeed : a -> Effect a succeed : a -> Effect a
succeed = \x -> @Effect \{} -> x succeed = \x -> @Effect \{} -> x
@ -1172,7 +1193,7 @@ fn return_wrapped_function_pointer() {
r#" r#"
app "test" provides [ main ] to "./platform" app "test" provides [ main ] to "./platform"
Effect a : [ @Effect ({} -> a) ] Effect a := {} -> a
foo : Effect {} foo : Effect {}
foo = @Effect \{} -> {} foo = @Effect \{} -> {}
@ -1217,7 +1238,7 @@ fn return_wrapped_closure() {
r#" r#"
app "test" provides [ main ] to "./platform" app "test" provides [ main ] to "./platform"
Effect a : [ @Effect ({} -> a) ] Effect a := {} -> a
foo : Effect {} foo : Effect {}
foo = foo =
@ -1843,7 +1864,7 @@ fn task_always_twice() {
r#" r#"
app "test" provides [ main ] to "./platform" app "test" provides [ main ] to "./platform"
Effect a : [ @Effect ({} -> a) ] Effect a := {} -> a
effectAlways : a -> Effect a effectAlways : a -> Effect a
effectAlways = \x -> effectAlways = \x ->
@ -1888,7 +1909,7 @@ fn wildcard_rigid() {
r#" r#"
app "test" provides [ main ] to "./platform" app "test" provides [ main ] to "./platform"
Effect a : [ @Effect ({} -> a) ] Effect a := {} -> a
Task a err : Effect (Result a err) Task a err : Effect (Result a err)
@ -1918,7 +1939,7 @@ fn alias_of_alias_with_type_arguments() {
r#" r#"
app "test" provides [ main ] to "./platform" app "test" provides [ main ] to "./platform"
Effect a : [ @Effect a ] Effect a := a
Task a err : Effect (Result a err) Task a err : Effect (Result a err)
@ -1948,7 +1969,7 @@ fn todo_bad_error_message() {
r#" r#"
app "test" provides [ main ] to "./platform" app "test" provides [ main ] to "./platform"
Effect a : [ @Effect ({} -> a) ] Effect a := {} -> a
effectAlways : a -> Effect a effectAlways : a -> Effect a
effectAlways = \x -> effectAlways = \x ->
@ -2972,6 +2993,7 @@ fn mix_function_and_closure_level_of_indirection() {
#[test] #[test]
#[cfg(any(feature = "gen-llvm"))] #[cfg(any(feature = "gen-llvm"))]
#[cfg_attr(debug_assertions, ignore)] // this test stack-overflows the compiler in debug mode
fn do_pass_bool_byte_closure_layout() { fn do_pass_bool_byte_closure_layout() {
// see https://github.com/rtfeldman/roc/pull/1706 // see https://github.com/rtfeldman/roc/pull/1706
// the distinction is actually important, dropping that info means some functions just get // the distinction is actually important, dropping that info means some functions just get
@ -3079,7 +3101,7 @@ fn nested_rigid_alias() {
r#" r#"
app "test" provides [ main ] to "./platform" app "test" provides [ main ] to "./platform"
Identity a : [ @Identity a ] Identity a := a
foo : Identity a -> Identity a foo : Identity a -> Identity a
foo = \list -> foo = \list ->
@ -3106,15 +3128,15 @@ fn nested_rigid_tag_union() {
r#" r#"
app "test" provides [ main ] to "./platform" app "test" provides [ main ] to "./platform"
foo : [ @Identity a ] -> [ @Identity a ] foo : [ Identity a ] -> [ Identity a ]
foo = \list -> foo = \list ->
p2 : [ @Identity a ] p2 : [ Identity a ]
p2 = list p2 = list
p2 p2
main = main =
when foo (@Identity "foo") is when foo (Identity "foo") is
_ -> "hello world" _ -> "hello world"
"# "#
), ),
@ -3200,7 +3222,7 @@ fn recursively_build_effect() {
always {} |> after \_ -> nestHelp (m - 1) always {} |> after \_ -> nestHelp (m - 1)
XEffect a : [ @XEffect ({} -> a) ] XEffect a := {} -> a
always : a -> XEffect a always : a -> XEffect a
always = \x -> @XEffect (\{} -> x) always = \x -> @XEffect (\{} -> x)

View file

@ -1055,10 +1055,10 @@ fn phantom_polymorphic() {
r"# r"#
Point coordinate : [ Point coordinate I64 I64 ] Point coordinate : [ Point coordinate I64 I64 ]
World : [ @World ] World := {}
zero : Point World zero : Point World
zero = Point @World 0 0 zero = Point (@World {}) 0 0
add : Point a -> Point a add : Point a -> Point a
add = \(Point c x y) -> (Point c x y) add = \(Point c x y) -> (Point c x y)
@ -1580,3 +1580,28 @@ fn issue_2725_alias_polymorphic_lambda() {
i64 i64
) )
} }
#[test]
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
fn opaque_assign_to_symbol() {
assert_evals_to!(
indoc!(
r#"
app "test" provides [ out ] to "./platform"
Variable := U8
fromUtf8 : U8 -> Result Variable [ InvalidVariableUtf8 ]
fromUtf8 = \char ->
Ok (@Variable char)
out =
when fromUtf8 98 is
Ok (@Variable n) -> n
_ -> 1
"#
),
98,
u8
)
}

Some files were not shown because too many files have changed in this diff Show more