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>
Sean Hagstrom <sean@seanhagstrom.com>
Kas Buunk <kasbuunk@icloud.com>
Oskar Hahn <mail@oshahn.de>

10
Cargo.lock generated
View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

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
## * Roc's compiler optimizes many list operations into in-place mutations behind the scenes, depending on how the list is being used. For example, [List.map], [List.keepIf], and [List.set] can all be optimized to perform in-place mutations.
## * If possible, it is usually best for performance to use large lists in a way where the optimizer can turn them into in-place mutations. If this is not possible, a persistent data structure might be faster - but this is a rare enough scenario that it would not be good for the average Roc program's performance if this were the way [List] worked by default. Instead, you can look outside Roc's standard modules for an implementation of a persistent data structure - likely built using [List] under the hood!
List elem : [ @List elem ]
List elem := [ List elem ]
## Initialize

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

File diff suppressed because it is too large Load diff

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

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> {
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> {

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

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

View file

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

View file

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

View file

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

View file

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

View file

@ -1809,7 +1809,39 @@ fn modify_refcount_union_help<'a, 'ctx, 'env>(
for (i, field_layout) in field_layouts.iter().enumerate() {
if let Layout::RecursivePointer = field_layout {
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() {
let field_ptr = env
.builder

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -311,6 +311,50 @@ impl<'a> UnionLayout<'a> {
.append(alloc.intersperse(tags_doc, ", "))
.append(alloc.text("]"))
}
Recursive(tags) => {
let tags_doc = tags.iter().map(|fields| {
alloc.text("C ").append(alloc.intersperse(
fields.iter().map(|x| x.to_doc(alloc, Parens::InTypeParam)),
" ",
))
});
alloc
.text("[<r>")
.append(alloc.intersperse(tags_doc, ", "))
.append(alloc.text("]"))
}
NonNullableUnwrapped(fields) => {
let fields_doc = alloc.text("C ").append(alloc.intersperse(
fields.iter().map(|x| x.to_doc(alloc, Parens::InTypeParam)),
" ",
));
alloc
.text("[<rnnu>")
.append(fields_doc)
.append(alloc.text("]"))
}
NullableUnwrapped {
nullable_id,
other_fields,
} => {
let fields_doc = alloc.text("C ").append(
alloc.intersperse(
other_fields
.iter()
.map(|x| x.to_doc(alloc, Parens::InTypeParam)),
" ",
),
);
let tags_doc = if nullable_id {
alloc.concat(vec![alloc.text("<null>, "), fields_doc])
} else {
alloc.concat(vec![fields_doc, alloc.text(", <null>")])
};
alloc
.text("[<rnu>")
.append(tags_doc)
.append(alloc.text("]"))
}
_ => alloc.text("TODO"),
}
}
@ -939,6 +983,16 @@ pub const fn round_up_to_alignment(width: u32, alignment: u32) -> u32 {
}
}
#[inline(always)]
pub fn is_unresolved_var(subs: &Subs, var: Variable) -> bool {
use Content::*;
let content = subs.get_content_without_compacting(var);
matches!(
content,
FlexVar(..) | RigidVar(..) | FlexAbleVar(..) | RigidAbleVar(..),
)
}
impl<'a> Layout<'a> {
pub const VOID: Self = Layout::Union(UnionLayout::NonRecursive(&[]));
pub const UNIT: Self = Layout::Struct {
@ -971,12 +1025,24 @@ impl<'a> Layout<'a> {
}
match symbol {
Symbol::NUM_DECIMAL | Symbol::NUM_AT_DECIMAL => {
return Ok(Layout::Builtin(Builtin::Decimal))
Symbol::NUM_DECIMAL => return Ok(Layout::Builtin(Builtin::Decimal)),
Symbol::NUM_NAT | Symbol::NUM_NATURAL => {
return Ok(Layout::usize(env.target_info))
}
Symbol::NUM_NAT | Symbol::NUM_NATURAL | Symbol::NUM_AT_NATURAL => {
return Ok(Layout::usize(env.target_info))
Symbol::NUM_NUM | Symbol::NUM_INT | Symbol::NUM_INTEGER
if is_unresolved_var(env.subs, actual_var) =>
{
// default to i64
return Ok(Layout::i64());
}
Symbol::NUM_FLOAT | Symbol::NUM_FLOATINGPOINT
if is_unresolved_var(env.subs, actual_var) =>
{
// default to f64
return Ok(Layout::f64());
}
_ => Self::from_var(env, actual_var),
@ -1645,7 +1711,7 @@ fn layout_from_flat_type<'a>(
Ok(Layout::f32())
}
Symbol::NUM_NUM | Symbol::NUM_AT_NUM => {
Symbol::NUM_NUM => {
// Num.Num should only ever have 1 argument, e.g. Num.Num Int.Integer
debug_assert_eq!(args.len(), 1);
@ -1731,7 +1797,7 @@ fn layout_from_flat_type<'a>(
debug_assert!(ext_var_is_empty_tag_union(subs, ext_var));
Ok(layout_from_tag_union(arena, &tags, subs, env.target_info))
Ok(layout_from_tag_union(env, &tags))
}
FunctionOrTagUnion(tag_name, _, ext_var) => {
debug_assert!(
@ -1742,7 +1808,7 @@ fn layout_from_flat_type<'a>(
let union_tags = UnionTags::from_tag_name_index(tag_name);
let (tags, _) = union_tags.unsorted_tags_and_ext(subs, ext_var);
Ok(layout_from_tag_union(arena, &tags, subs, env.target_info))
Ok(layout_from_tag_union(env, &tags))
}
RecursiveTagUnion(rec_var, tags, ext_var) => {
let (tags, ext_var) = tags.unsorted_tags_and_ext(subs, ext_var);
@ -2071,23 +2137,14 @@ fn is_recursive_tag_union(layout: &Layout) -> bool {
}
fn union_sorted_tags_help_new<'a>(
arena: &'a Bump,
env: &mut Env<'a, '_>,
tags_list: &[(&'_ TagName, &[Variable])],
opt_rec_var: Option<Variable>,
subs: &Subs,
target_info: TargetInfo,
) -> UnionVariant<'a> {
// sort up front; make sure the ordering stays intact!
let mut tags_list = Vec::from_iter_in(tags_list.iter(), arena);
let mut tags_list = Vec::from_iter_in(tags_list.iter(), env.arena);
tags_list.sort_unstable_by(|(a, _), (b, _)| a.cmp(b));
let mut env = Env {
arena,
subs,
seen: Vec::new_in(arena),
target_info,
};
match tags_list.len() {
0 => {
// trying to instantiate a type with no values
@ -2098,39 +2155,29 @@ fn union_sorted_tags_help_new<'a>(
let tag_name = tag_name.clone();
// just one tag in the union (but with arguments) can be a struct
let mut layouts = Vec::with_capacity_in(tags_list.len(), arena);
let mut layouts = Vec::with_capacity_in(tags_list.len(), env.arena);
// special-case NUM_AT_NUM: if its argument is a FlexVar, make it Int
match tag_name {
TagName::Private(Symbol::NUM_AT_NUM) => {
let var = arguments[0];
layouts
.push(unwrap_num_tag(subs, var, target_info).expect("invalid num layout"));
}
_ => {
for &var in arguments {
match Layout::from_var(&mut env, var) {
Ok(layout) => {
layouts.push(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
// 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.
}
}
for &var in arguments {
match Layout::from_var(env, var) {
Ok(layout) => {
layouts.push(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
// 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| {
let size1 = layout1.alignment_bytes(target_info);
let size2 = layout2.alignment_bytes(target_info);
let size1 = layout1.alignment_bytes(env.target_info);
let size2 = layout2.alignment_bytes(env.target_info);
size2.cmp(&size1)
});
@ -2151,7 +2198,7 @@ fn union_sorted_tags_help_new<'a>(
}
num_tags => {
// default path
let mut answer = Vec::with_capacity_in(tags_list.len(), arena);
let mut answer = Vec::with_capacity_in(tags_list.len(), env.arena);
let mut has_any_arguments = false;
let mut nullable: Option<(TagIdIntType, TagName)> = None;
@ -2174,17 +2221,19 @@ fn union_sorted_tags_help_new<'a>(
continue;
}
let mut arg_layouts = Vec::with_capacity_in(arguments.len() + 1, arena);
let mut arg_layouts = Vec::with_capacity_in(arguments.len() + 1, env.arena);
for &var in arguments {
match Layout::from_var(&mut env, var) {
match Layout::from_var(env, var) {
Ok(layout) => {
has_any_arguments = true;
// make sure to not unroll recursive types!
let self_recursion = opt_rec_var.is_some()
&& subs.get_root_key_without_compacting(var)
== subs.get_root_key_without_compacting(opt_rec_var.unwrap())
&& env.subs.get_root_key_without_compacting(var)
== env
.subs
.get_root_key_without_compacting(opt_rec_var.unwrap())
&& is_recursive_tag_union(&layout);
if self_recursion {
@ -2207,8 +2256,8 @@ fn union_sorted_tags_help_new<'a>(
}
arg_layouts.sort_by(|layout1, layout2| {
let size1 = layout1.alignment_bytes(target_info);
let size2 = layout2.alignment_bytes(target_info);
let size1 = layout1.alignment_bytes(env.target_info);
let size2 = layout2.alignment_bytes(env.target_info);
size2.cmp(&size1)
});
@ -2229,7 +2278,7 @@ fn union_sorted_tags_help_new<'a>(
3..=MAX_ENUM_SIZE if !has_any_arguments => {
// type can be stored in a byte
// needs the sorted tag names to determine the tag_id
let mut tag_names = Vec::with_capacity_in(answer.len(), arena);
let mut tag_names = Vec::with_capacity_in(answer.len(), env.arena);
for (tag_name, _) in answer {
tag_names.push(tag_name);
@ -2303,37 +2352,26 @@ pub fn union_sorted_tags_help<'a>(
let mut layouts = Vec::with_capacity_in(tags_vec.len(), arena);
let mut contains_zero_sized = false;
// special-case NUM_AT_NUM: if its argument is a FlexVar, make it Int
match tag_name {
TagName::Private(Symbol::NUM_AT_NUM) => {
layouts.push(
unwrap_num_tag(subs, arguments[0], target_info)
.expect("invalid num layout"),
);
}
_ => {
for var in arguments {
match Layout::from_var(&mut env, var) {
Ok(layout) => {
// 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.
}
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.
}
}
}
@ -2488,128 +2526,95 @@ pub fn union_sorted_tags_help<'a>(
}
}
fn layout_from_newtype<'a>(
arena: &'a Bump,
tags: &UnsortedUnionTags,
subs: &Subs,
target_info: TargetInfo,
) -> Layout<'a> {
debug_assert!(tags.is_newtype_wrapper(subs));
fn layout_from_newtype<'a>(env: &mut Env<'a, '_>, tags: &UnsortedUnionTags) -> Layout<'a> {
debug_assert!(tags.is_newtype_wrapper(env.subs));
let (tag_name, var) = tags.get_newtype(subs);
let (_tag_name, var) = tags.get_newtype(env.subs);
if tag_name == &TagName::Private(Symbol::NUM_AT_NUM) {
unwrap_num_tag(subs, var, target_info).expect("invalid Num argument")
} else {
let mut env = Env {
arena,
subs,
seen: Vec::new_in(arena),
target_info,
};
match Layout::from_var(&mut env, var) {
Ok(layout) => 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
// 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!()
}
match Layout::from_var(env, var) {
Ok(layout) => 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
// 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>(
arena: &'a Bump,
tags: &UnsortedUnionTags,
subs: &Subs,
target_info: TargetInfo,
) -> Layout<'a> {
fn layout_from_tag_union<'a>(env: &mut Env<'a, '_>, tags: &UnsortedUnionTags) -> Layout<'a> {
use UnionVariant::*;
if tags.is_newtype_wrapper(subs) {
return layout_from_newtype(arena, tags, subs, target_info);
if tags.is_newtype_wrapper(env.subs) {
return layout_from_newtype(env, tags);
}
let tags_vec = &tags.tags;
match tags_vec.get(0) {
Some((tag_name, arguments)) if *tag_name == &TagName::Private(Symbol::NUM_AT_NUM) => {
debug_assert_eq!(arguments.len(), 1);
let opt_rec_var = None;
let variant = union_sorted_tags_help_new(env, tags_vec, opt_rec_var);
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
}
_ => {
let opt_rec_var = None;
let variant =
union_sorted_tags_help_new(arena, tags_vec, opt_rec_var, subs, target_info);
Wrapped(variant) => {
use WrappedVariant::*;
match variant {
Never => Layout::VOID,
Unit | UnitWithArguments => Layout::UNIT,
BoolUnion { .. } => Layout::bool(),
ByteUnion(_) => Layout::u8(),
Newtype {
arguments: field_layouts,
..
NonRecursive {
sorted_tag_layouts: tags,
} => {
let answer1 = if field_layouts.len() == 1 {
field_layouts[0]
} else {
Layout::struct_no_name_order(field_layouts.into_bump_slice())
};
let mut tag_layouts = Vec::with_capacity_in(tags.len(), env.arena);
tag_layouts.extend(tags.iter().map(|r| r.1));
answer1
Layout::Union(UnionLayout::NonRecursive(tag_layouts.into_bump_slice()))
}
Wrapped(variant) => {
use WrappedVariant::*;
match variant {
NonRecursive {
sorted_tag_layouts: tags,
} => {
let mut tag_layouts = Vec::with_capacity_in(tags.len(), arena);
tag_layouts.extend(tags.iter().map(|r| r.1));
Recursive {
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::NonRecursive(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!(),
}
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(), 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>(
env: &mut Env<'a, '_>,
key_var: Variable,

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

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))
.parse(arena, state)?;
let result = if name.value.starts_with('@') {
Tag::Private {
name,
args: args.into_bump_slice(),
}
} else {
Tag::Global {
name,
args: args.into_bump_slice(),
}
let result = Tag::Global {
name,
args: args.into_bump_slice(),
};
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(
"$Age",
"@Age",
)

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

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

View file

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

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