mirror of
https://github.com/roc-lang/roc.git
synced 2024-11-13 09:49:11 +03:00
Merge remote-tracking branch 'origin/trunk' into breakout
This commit is contained in:
commit
b4512c9bac
4
.github/workflows/benchmarks.yml
vendored
4
.github/workflows/benchmarks.yml
vendored
@ -2,6 +2,10 @@ on: [pull_request]
|
||||
|
||||
name: Benchmarks
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.ref }}
|
||||
cancel-in-progress: true
|
||||
|
||||
env:
|
||||
RUST_BACKTRACE: 1
|
||||
ROC_NUM_WORKERS: 1
|
||||
|
4
.github/workflows/ci.yml
vendored
4
.github/workflows/ci.yml
vendored
@ -2,6 +2,10 @@ on: [pull_request]
|
||||
|
||||
name: CI
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.ref }}
|
||||
cancel-in-progress: true
|
||||
|
||||
env:
|
||||
RUST_BACKTRACE: 1
|
||||
|
||||
|
4
.github/workflows/spellcheck.yml
vendored
4
.github/workflows/spellcheck.yml
vendored
@ -2,6 +2,10 @@ on: [pull_request]
|
||||
|
||||
name: SpellCheck
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.ref }}
|
||||
cancel-in-progress: true
|
||||
|
||||
env:
|
||||
RUST_BACKTRACE: 1
|
||||
|
||||
|
1
AUTHORS
1
AUTHORS
@ -76,3 +76,4 @@ Nikita Mounier <36044205+nikitamounier@users.noreply.github.com>
|
||||
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>
|
||||
|
@ -107,7 +107,7 @@ To run the test suite (via `cargo test`), you additionally need to install:
|
||||
* [`valgrind`](https://www.valgrind.org/) (needs special treatment to [install on macOS](https://stackoverflow.com/a/61359781)
|
||||
Alternatively, you can use `cargo test --no-fail-fast` or `cargo test -p specific_tests` to skip over the valgrind failures & tests.
|
||||
|
||||
For debugging LLVM IR, we use [DebugIR](https://github.com/vaivaswatha/debugir). This dependency is only required to build with the `--debug` flag, and for normal developtment you should be fine without it.
|
||||
For debugging LLVM IR, we use [DebugIR](https://github.com/vaivaswatha/debugir). This dependency is only required to build with the `--debug` flag, and for normal development you should be fine without it.
|
||||
|
||||
### libxcb libraries
|
||||
|
||||
|
@ -1941,7 +1941,7 @@ Here are various Roc expressions involving operators, and what they desugar to.
|
||||
| `a - b` | `Num.sub a b` |
|
||||
| `a * b` | `Num.mul a b` |
|
||||
| `a / b` | `Num.div a b` |
|
||||
| `a // b` | `Num.divFloor a b` |
|
||||
| `a // b` | `Num.divTrunc a b` |
|
||||
| `a ^ b` | `Num.pow a b` |
|
||||
| `a % b` | `Num.rem a b` |
|
||||
| `a %% b` | `Num.mod a b` |
|
||||
|
@ -128,7 +128,6 @@ pub fn build_app<'a>() -> App<'a> {
|
||||
.long(FLAG_PRECOMPILED)
|
||||
.about("Assumes the host has been precompiled and skips recompiling the host. (Enabled by default when using a --target other than `--target host`)")
|
||||
.possible_values(["true", "false"])
|
||||
.default_value("false")
|
||||
.required(false),
|
||||
)
|
||||
.arg(
|
||||
@ -234,7 +233,6 @@ pub fn build_app<'a>() -> App<'a> {
|
||||
.long(FLAG_PRECOMPILED)
|
||||
.about("Assumes the host has been precompiled and skips recompiling the host. (Enabled by default when using a --target other than `--target host`)")
|
||||
.possible_values(["true", "false"])
|
||||
.default_value("false")
|
||||
.required(false),
|
||||
)
|
||||
.arg(
|
||||
|
@ -47,7 +47,7 @@ interface Num
|
||||
compare,
|
||||
cos,
|
||||
div,
|
||||
divFloor,
|
||||
divTrunc,
|
||||
floor,
|
||||
intCast,
|
||||
isEven,
|
||||
@ -81,8 +81,6 @@ interface Num
|
||||
minI64,
|
||||
minU64,
|
||||
minI128,
|
||||
modInt,
|
||||
modFloat,
|
||||
mul,
|
||||
mulChecked,
|
||||
mulWrap,
|
||||
@ -781,7 +779,7 @@ toU128 : Int * -> U128
|
||||
## there will be a loss of precision.
|
||||
toDec : Num * -> Dec
|
||||
|
||||
## Divide two integers and #Num.round the resulut.
|
||||
## Divide two integers, truncating the result towards zero.
|
||||
##
|
||||
## Division by zero is undefined in mathematics. As such, you should make
|
||||
## sure never to pass zero as the denomaintor to this function!
|
||||
@ -791,18 +789,18 @@ toDec : Num * -> Dec
|
||||
## * In a development build, you'll get an assertion failure.
|
||||
## * In an optimized build, the function will return 0.
|
||||
##
|
||||
## `a // b` is shorthand for `Num.divRound a b`.
|
||||
## `a // b` is shorthand for `Num.divTrunc a b`.
|
||||
##
|
||||
## >>> 5 // 7
|
||||
##
|
||||
## >>> Num.divRound 5 7
|
||||
## >>> Num.divTrunc 5 7
|
||||
##
|
||||
## >>> 8 // -3
|
||||
##
|
||||
## >>> Num.divRound 8 -3
|
||||
## >>> Num.divTrunc 8 -3
|
||||
##
|
||||
## This is the same as the #// operator.
|
||||
divRound : Int a, Int a -> Int a
|
||||
divTrunc : Int a, Int a -> Int a
|
||||
|
||||
## Perform flooring modulo on two integers.
|
||||
##
|
||||
|
@ -1,6 +1,6 @@
|
||||
interface Dict
|
||||
exposes
|
||||
[
|
||||
exposes
|
||||
[
|
||||
empty,
|
||||
single,
|
||||
get,
|
||||
@ -15,7 +15,10 @@ interface Dict
|
||||
intersection,
|
||||
difference,
|
||||
]
|
||||
imports [ ]
|
||||
imports
|
||||
[
|
||||
Bool.{ Bool }
|
||||
]
|
||||
|
||||
empty : Dict k v
|
||||
single : k, v -> Dict k v
|
||||
|
@ -1,60 +1,72 @@
|
||||
isEmpty,
|
||||
get,
|
||||
set,
|
||||
replace,
|
||||
append,
|
||||
map,
|
||||
len,
|
||||
walkBackwards,
|
||||
concat,
|
||||
first,
|
||||
single,
|
||||
repeat,
|
||||
reverse,
|
||||
prepend,
|
||||
join,
|
||||
keepIf,
|
||||
contains,
|
||||
sum,
|
||||
walk,
|
||||
last,
|
||||
keepOks,
|
||||
keepErrs,
|
||||
mapWithIndex,
|
||||
map2,
|
||||
map3,
|
||||
product,
|
||||
walkUntil,
|
||||
range,
|
||||
sortWith,
|
||||
drop,
|
||||
swap,
|
||||
dropAt,
|
||||
dropLast,
|
||||
min,
|
||||
max,
|
||||
map4,
|
||||
dropFirst,
|
||||
joinMap,
|
||||
any,
|
||||
takeFirst,
|
||||
takeLast,
|
||||
find,
|
||||
sublist,
|
||||
intersperse,
|
||||
split,
|
||||
all,
|
||||
dropIf,
|
||||
sortAsc,
|
||||
sortDesc,
|
||||
interface List
|
||||
exposes
|
||||
[
|
||||
isEmpty,
|
||||
get,
|
||||
set,
|
||||
replace,
|
||||
append,
|
||||
map,
|
||||
len,
|
||||
walkBackwards,
|
||||
concat,
|
||||
first,
|
||||
single,
|
||||
repeat,
|
||||
reverse,
|
||||
prepend,
|
||||
join,
|
||||
keepIf,
|
||||
contains,
|
||||
sum,
|
||||
walk,
|
||||
last,
|
||||
keepOks,
|
||||
keepErrs,
|
||||
mapWithIndex,
|
||||
map2,
|
||||
map3,
|
||||
product,
|
||||
walkUntil,
|
||||
range,
|
||||
sortWith,
|
||||
drop,
|
||||
swap,
|
||||
dropAt,
|
||||
dropLast,
|
||||
min,
|
||||
max,
|
||||
map4,
|
||||
dropFirst,
|
||||
joinMap,
|
||||
any,
|
||||
takeFirst,
|
||||
takeLast,
|
||||
find,
|
||||
sublist,
|
||||
intersperse,
|
||||
split,
|
||||
all,
|
||||
dropIf,
|
||||
sortAsc,
|
||||
sortDesc,
|
||||
]
|
||||
imports
|
||||
[
|
||||
Bool.{ Bool }
|
||||
]
|
||||
|
||||
isEmpty : List a -> Bool
|
||||
isEmpty = \list ->
|
||||
List.len list == 0
|
||||
|
||||
get : List a, Nat -> Result a [ OutOfBounds ]*
|
||||
set : List a, Nat, a -> List a
|
||||
replace : List a, Nat, a -> { list : List a, value : a }
|
||||
|
||||
set : List a, Nat, a -> List a
|
||||
set = \list, index, value ->
|
||||
(List.replace list index value).list
|
||||
|
||||
append : List a, a -> List a
|
||||
prepend : List a, a -> List a
|
||||
len : List a -> Nat
|
||||
@ -70,11 +82,11 @@ walkBackwards : List elem, state, (state, elem -> state) -> state
|
||||
walkUntil : List elem, state, (state, elem -> [ Continue state, Stop state ]) -> state
|
||||
|
||||
sum : List (Num a) -> Num a
|
||||
sum = \list ->
|
||||
sum = \list ->
|
||||
List.walk list 0 Num.add
|
||||
|
||||
product : List (Num a) -> Num a
|
||||
product = \list ->
|
||||
product = \list ->
|
||||
List.walk list 1 Num.mul
|
||||
|
||||
any : List a, (a -> Bool) -> Bool
|
||||
@ -82,6 +94,8 @@ all : List a, (a -> Bool) -> Bool
|
||||
|
||||
keepIf : List a, (a -> Bool) -> List a
|
||||
dropIf : List a, (a -> Bool) -> List a
|
||||
dropIf = \list, predicate ->
|
||||
List.keepIf list (\e -> Bool.not (predicate e))
|
||||
|
||||
keepOks : List before, (before -> Result after *) -> List after
|
||||
keepErrs: List before, (before -> Result * after) -> List after
|
||||
@ -89,7 +103,7 @@ map : List a, (a -> b) -> List b
|
||||
map2 : List a, List b, (a, b -> c) -> List c
|
||||
map3 : List a, List b, List c, (a, b, c -> d) -> List d
|
||||
map4 : List a, List b, List c, List d, (a, b, c, d -> e) -> List e
|
||||
mapWithIndex : List a, (a -> b) -> List b
|
||||
mapWithIndex : List a, (a, Nat -> b) -> List b
|
||||
range : Int a, Int a -> List (Int a)
|
||||
sortWith : List a, (a, a -> [ LT, EQ, GT ] ) -> List a
|
||||
sortAsc : List (Num a) -> List (Num a)
|
||||
@ -112,9 +126,47 @@ drop : List elem, Nat -> List elem
|
||||
dropAt : List elem, Nat -> List elem
|
||||
|
||||
min : List (Num a) -> Result (Num a) [ ListWasEmpty ]*
|
||||
min = \list ->
|
||||
when List.first list is
|
||||
Ok initial ->
|
||||
Ok (minHelp list initial)
|
||||
|
||||
Err ListWasEmpty ->
|
||||
Err ListWasEmpty
|
||||
|
||||
|
||||
minHelp : List (Num a), Num a -> Num a
|
||||
minHelp = \list, initial ->
|
||||
List.walk list initial \bestSoFar, current ->
|
||||
if current < bestSoFar then
|
||||
current
|
||||
|
||||
else
|
||||
bestSoFar
|
||||
|
||||
max : List (Num a) -> Result (Num a) [ ListWasEmpty ]*
|
||||
max = \list ->
|
||||
when List.first list is
|
||||
Ok initial ->
|
||||
Ok (maxHelp list initial)
|
||||
|
||||
Err ListWasEmpty ->
|
||||
Err ListWasEmpty
|
||||
|
||||
|
||||
maxHelp : List (Num a), Num a -> Num a
|
||||
maxHelp = \list, initial ->
|
||||
List.walk list initial \bestSoFar, current ->
|
||||
if current > bestSoFar then
|
||||
current
|
||||
|
||||
else
|
||||
bestSoFar
|
||||
|
||||
joinMap : List a, (a -> List b) -> List b
|
||||
joinMap = \list, mapper ->
|
||||
List.walk list [] (\state, elem -> List.concat state (mapper elem))
|
||||
|
||||
find : List elem, (elem -> Bool) -> Result elem [ NotFound ]*
|
||||
sublist : List elem, { start : Nat, len : Nat } -> List elem
|
||||
intersperse : List elem, elem -> List elem
|
||||
|
@ -44,8 +44,6 @@ interface Num
|
||||
Binary32,
|
||||
Binary64,
|
||||
|
||||
maxFloat,
|
||||
minFloat,
|
||||
abs,
|
||||
neg,
|
||||
add,
|
||||
@ -70,8 +68,6 @@ interface Num
|
||||
rem,
|
||||
div,
|
||||
divChecked,
|
||||
modInt,
|
||||
modFloat,
|
||||
sqrt,
|
||||
log,
|
||||
round,
|
||||
@ -99,8 +95,8 @@ interface Num
|
||||
bytesToU32,
|
||||
divCeil,
|
||||
divCeilChecked,
|
||||
divFloor,
|
||||
divFloorChecked,
|
||||
divTrunc,
|
||||
divTruncChecked,
|
||||
toStr,
|
||||
isMultipleOf,
|
||||
minI8,
|
||||
@ -123,6 +119,10 @@ interface Num
|
||||
maxI128,
|
||||
minU128,
|
||||
maxU128,
|
||||
minF32,
|
||||
maxF32,
|
||||
minF64,
|
||||
maxF64,
|
||||
toI8,
|
||||
toI8Checked,
|
||||
toI16,
|
||||
@ -143,8 +143,17 @@ interface Num
|
||||
toU64Checked,
|
||||
toU128,
|
||||
toU128Checked,
|
||||
toNat,
|
||||
toNatChecked,
|
||||
toF32,
|
||||
toF32Checked,
|
||||
toF64,
|
||||
toF64Checked,
|
||||
]
|
||||
imports
|
||||
[
|
||||
Bool.{ Bool }
|
||||
]
|
||||
imports [ ]
|
||||
|
||||
Num range : [ @Num range ]
|
||||
Int range : Num (Integer range)
|
||||
@ -237,8 +246,8 @@ 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 ]*
|
||||
divFloor : Int a, Int a -> Int a
|
||||
divFloorChecked : 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 ]*
|
||||
@ -331,6 +340,18 @@ minU128 = 0
|
||||
maxU128 : U128
|
||||
maxU128 = 0340282366920938463463374607431768211455
|
||||
|
||||
minF32 : F32
|
||||
minF32 = -3.40282347e38
|
||||
|
||||
maxF32 : F32
|
||||
maxF32 = 3.40282347e38
|
||||
|
||||
minF64 : F64
|
||||
minF64 = -1.7976931348623157e308
|
||||
|
||||
maxF64 : F64
|
||||
maxF64 = 1.7976931348623157e308
|
||||
|
||||
toI8 : Int * -> I8
|
||||
toI16 : Int * -> I16
|
||||
toI32 : Int * -> I32
|
||||
@ -341,6 +362,7 @@ toU16 : Int * -> U16
|
||||
toU32 : Int * -> U32
|
||||
toU64 : Int * -> U64
|
||||
toU128 : Int * -> U128
|
||||
toNat : Int * -> Nat
|
||||
|
||||
toF32 : Num * -> F32
|
||||
toF64 : Num * -> F64
|
||||
@ -355,5 +377,6 @@ toU16Checked : Int * -> Result U16 [ OutOfBounds ]*
|
||||
toU32Checked : Int * -> Result U32 [ OutOfBounds ]*
|
||||
toU64Checked : Int * -> Result U64 [ OutOfBounds ]*
|
||||
toU128Checked : Int * -> Result U128 [ OutOfBounds ]*
|
||||
toNatChecked : Int * -> Result Nat [ OutOfBounds ]*
|
||||
toF32Checked : Num * -> Result F32 [ OutOfBounds ]*
|
||||
toF64Checked : Num * -> Result F64 [ OutOfBounds ]*
|
||||
|
@ -1,6 +1,6 @@
|
||||
interface Result
|
||||
exposes [ Result, isOk, isErr, map, mapErr, after, withDefault ]
|
||||
imports [ ]
|
||||
imports [ Bool.{ Bool } ]
|
||||
|
||||
Result ok err : [ Ok ok, Err err ]
|
||||
|
||||
|
@ -1,6 +1,6 @@
|
||||
interface Dict
|
||||
exposes
|
||||
[
|
||||
interface Set
|
||||
exposes
|
||||
[
|
||||
empty,
|
||||
single,
|
||||
walk,
|
||||
@ -14,7 +14,7 @@ interface Dict
|
||||
intersection,
|
||||
difference,
|
||||
]
|
||||
imports [ ]
|
||||
imports [ List, Bool.{ Bool }, Dict.{ values } ]
|
||||
|
||||
empty : Set k
|
||||
single : k -> Set k
|
||||
@ -35,4 +35,4 @@ toDict : Set k -> Dict k {}
|
||||
|
||||
walk : Set k, state, (state, k -> state) -> state
|
||||
walk = \set, state, step ->
|
||||
Dict.walk (toDict set) state (\s, k, _ -> step s k)
|
||||
Dict.walk (Set.toDict set) state (\s, k, _ -> step s k)
|
||||
|
@ -34,7 +34,7 @@ interface Str
|
||||
toU8,
|
||||
toI8,
|
||||
]
|
||||
imports [ ]
|
||||
imports [ Bool.{ Bool }, Result.{ Result } ]
|
||||
|
||||
|
||||
|
||||
|
@ -316,16 +316,16 @@ pub fn types() -> MutMap<Symbol, (SolvedType, Region)> {
|
||||
Box::new(SolvedType::Wildcard),
|
||||
);
|
||||
|
||||
// divFloor : Int a, Int a -> Int a
|
||||
// divTrunc : Int a, Int a -> Int a
|
||||
add_top_level_function_type!(
|
||||
Symbol::NUM_DIV_FLOOR,
|
||||
Symbol::NUM_DIV_TRUNC,
|
||||
vec![int_type(flex(TVAR1)), int_type(flex(TVAR1))],
|
||||
Box::new(int_type(flex(TVAR1)))
|
||||
);
|
||||
|
||||
// divFloorChecked : Int a, Int a -> Result (Int a) [ DivByZero ]*
|
||||
// divTruncChecked : Int a, Int a -> Result (Int a) [ DivByZero ]*
|
||||
add_top_level_function_type!(
|
||||
Symbol::NUM_DIV_FLOOR_CHECKED,
|
||||
Symbol::NUM_DIV_TRUNC_CHECKED,
|
||||
vec![int_type(flex(TVAR1)), int_type(flex(TVAR1))],
|
||||
Box::new(result_type(int_type(flex(TVAR1)), div_by_zero.clone())),
|
||||
);
|
||||
@ -948,7 +948,7 @@ pub fn types() -> MutMap<Symbol, (SolvedType, Region)> {
|
||||
);
|
||||
}
|
||||
|
||||
// fromUtf8Range : List U8 -> Result Str [ BadUtf8 Utf8Problem, OutOfBounds ]*
|
||||
// fromUtf8Range : List U8, { start : Nat, count : Nat } -> Result Str [ BadUtf8 Utf8Problem, OutOfBounds ]*
|
||||
{
|
||||
let bad_utf8 = SolvedType::TagUnion(
|
||||
vec![
|
||||
|
@ -16,6 +16,7 @@ pub struct MemberVariables {
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub struct AbilityMemberData {
|
||||
pub parent_ability: Symbol,
|
||||
pub signature_var: Variable,
|
||||
pub signature: Type,
|
||||
pub variables: MemberVariables,
|
||||
pub region: Region,
|
||||
@ -60,15 +61,16 @@ impl AbilitiesStore {
|
||||
pub fn register_ability(
|
||||
&mut self,
|
||||
ability: Symbol,
|
||||
members: Vec<(Symbol, Region, Type, MemberVariables)>,
|
||||
members: Vec<(Symbol, Region, Variable, Type, MemberVariables)>,
|
||||
) {
|
||||
let mut members_vec = Vec::with_capacity(members.len());
|
||||
for (member, region, signature, variables) in members.into_iter() {
|
||||
for (member, region, signature_var, signature, variables) in members.into_iter() {
|
||||
members_vec.push(member);
|
||||
let old_member = self.ability_members.insert(
|
||||
member,
|
||||
AbilityMemberData {
|
||||
parent_ability: ability,
|
||||
signature_var,
|
||||
signature,
|
||||
region,
|
||||
variables,
|
||||
|
@ -1,6 +1,6 @@
|
||||
use crate::env::Env;
|
||||
use crate::scope::Scope;
|
||||
use roc_collections::all::{ImMap, MutMap, MutSet, SendMap};
|
||||
use roc_collections::all::{ImMap, MutMap, MutSet, SendMap, VecSet};
|
||||
use roc_module::ident::{Ident, Lowercase, TagName};
|
||||
use roc_module::symbol::{IdentIds, ModuleId, Symbol};
|
||||
use roc_parse::ast::{AssignedField, ExtractSpaces, Pattern, Tag, TypeAnnotation, TypeHeader};
|
||||
@ -15,7 +15,7 @@ use roc_types::types::{
|
||||
pub struct Annotation {
|
||||
pub typ: Type,
|
||||
pub introduced_variables: IntroducedVariables,
|
||||
pub references: MutSet<Symbol>,
|
||||
pub references: VecSet<Symbol>,
|
||||
pub aliases: SendMap<Symbol, Alias>,
|
||||
}
|
||||
|
||||
@ -58,8 +58,8 @@ pub struct IntroducedVariables {
|
||||
pub wildcards: Vec<Loc<Variable>>,
|
||||
pub lambda_sets: Vec<Variable>,
|
||||
pub inferred: Vec<Loc<Variable>>,
|
||||
pub named: Vec<NamedVariable>,
|
||||
pub able: Vec<AbleVariable>,
|
||||
pub named: VecSet<NamedVariable>,
|
||||
pub able: VecSet<AbleVariable>,
|
||||
pub host_exposed_aliases: MutMap<Symbol, Variable>,
|
||||
}
|
||||
|
||||
@ -84,7 +84,7 @@ impl IntroducedVariables {
|
||||
first_seen: var.region,
|
||||
};
|
||||
|
||||
self.named.push(named_variable);
|
||||
self.named.insert(named_variable);
|
||||
}
|
||||
|
||||
pub fn insert_able(&mut self, name: Lowercase, var: Loc<Variable>, ability: Symbol) {
|
||||
@ -97,7 +97,7 @@ impl IntroducedVariables {
|
||||
first_seen: var.region,
|
||||
};
|
||||
|
||||
self.able.push(able_variable);
|
||||
self.able.insert(able_variable);
|
||||
}
|
||||
|
||||
pub fn insert_wildcard(&mut self, var: Loc<Variable>) {
|
||||
@ -128,12 +128,7 @@ impl IntroducedVariables {
|
||||
.extend(other.host_exposed_aliases.clone());
|
||||
|
||||
self.named.extend(other.named.iter().cloned());
|
||||
self.named.sort();
|
||||
self.named.dedup();
|
||||
|
||||
self.able.extend(other.able.iter().cloned());
|
||||
self.able.sort();
|
||||
self.able.dedup();
|
||||
}
|
||||
|
||||
pub fn union_owned(&mut self, other: Self) {
|
||||
@ -143,8 +138,7 @@ impl IntroducedVariables {
|
||||
self.host_exposed_aliases.extend(other.host_exposed_aliases);
|
||||
|
||||
self.named.extend(other.named);
|
||||
self.named.sort();
|
||||
self.named.dedup();
|
||||
self.able.extend(other.able.iter().cloned());
|
||||
}
|
||||
|
||||
pub fn var_by_name(&self, name: &Lowercase) -> Option<Variable> {
|
||||
@ -201,7 +195,7 @@ pub fn canonicalize_annotation(
|
||||
var_store: &mut VarStore,
|
||||
) -> Annotation {
|
||||
let mut introduced_variables = IntroducedVariables::default();
|
||||
let mut references = MutSet::default();
|
||||
let mut references = VecSet::default();
|
||||
let mut aliases = SendMap::default();
|
||||
|
||||
let typ = can_annotation_help(
|
||||
@ -232,7 +226,7 @@ pub fn canonicalize_annotation_with_possible_clauses(
|
||||
abilities_in_scope: &[Symbol],
|
||||
) -> Annotation {
|
||||
let mut introduced_variables = IntroducedVariables::default();
|
||||
let mut references = MutSet::default();
|
||||
let mut references = VecSet::default();
|
||||
let mut aliases = SendMap::default();
|
||||
|
||||
let (annotation, region) = match annotation {
|
||||
@ -433,7 +427,7 @@ fn can_annotation_help(
|
||||
var_store: &mut VarStore,
|
||||
introduced_variables: &mut IntroducedVariables,
|
||||
local_aliases: &mut SendMap<Symbol, Alias>,
|
||||
references: &mut MutSet<Symbol>,
|
||||
references: &mut VecSet<Symbol>,
|
||||
) -> Type {
|
||||
use roc_parse::ast::TypeAnnotation::*;
|
||||
|
||||
@ -861,7 +855,7 @@ fn canonicalize_has_clause(
|
||||
introduced_variables: &mut IntroducedVariables,
|
||||
clause: &Loc<roc_parse::ast::HasClause<'_>>,
|
||||
abilities_in_scope: &[Symbol],
|
||||
references: &mut MutSet<Symbol>,
|
||||
references: &mut VecSet<Symbol>,
|
||||
) -> Result<(), Type> {
|
||||
let Loc {
|
||||
region,
|
||||
@ -923,7 +917,7 @@ fn can_extension_type<'a>(
|
||||
var_store: &mut VarStore,
|
||||
introduced_variables: &mut IntroducedVariables,
|
||||
local_aliases: &mut SendMap<Symbol, Alias>,
|
||||
references: &mut MutSet<Symbol>,
|
||||
references: &mut VecSet<Symbol>,
|
||||
opt_ext: &Option<&Loc<TypeAnnotation<'a>>>,
|
||||
ext_problem_kind: roc_problem::can::ExtensionTypeKind,
|
||||
) -> Type {
|
||||
@ -1105,7 +1099,7 @@ fn can_assigned_fields<'a>(
|
||||
var_store: &mut VarStore,
|
||||
introduced_variables: &mut IntroducedVariables,
|
||||
local_aliases: &mut SendMap<Symbol, Alias>,
|
||||
references: &mut MutSet<Symbol>,
|
||||
references: &mut VecSet<Symbol>,
|
||||
) -> SendMap<Lowercase, RecordField<Type>> {
|
||||
use roc_parse::ast::AssignedField::*;
|
||||
use roc_types::types::RecordField::*;
|
||||
@ -1218,7 +1212,7 @@ fn can_tags<'a>(
|
||||
var_store: &mut VarStore,
|
||||
introduced_variables: &mut IntroducedVariables,
|
||||
local_aliases: &mut SendMap<Symbol, Alias>,
|
||||
references: &mut MutSet<Symbol>,
|
||||
references: &mut VecSet<Symbol>,
|
||||
) -> Vec<(TagName, Vec<Type>)> {
|
||||
let mut tag_types = Vec::with_capacity(tags.len());
|
||||
|
||||
|
@ -171,6 +171,7 @@ pub fn builtin_defs_map(symbol: Symbol, var_store: &mut VarStore) -> Option<Def>
|
||||
SET_DIFFERENCE => set_difference,
|
||||
SET_TO_LIST => set_to_list,
|
||||
SET_FROM_LIST => set_from_list,
|
||||
SET_TO_DICT=> set_to_dict,
|
||||
SET_INSERT => set_insert,
|
||||
SET_REMOVE => set_remove,
|
||||
SET_CONTAINS => set_contains,
|
||||
@ -196,8 +197,8 @@ pub fn builtin_defs_map(symbol: Symbol, var_store: &mut VarStore) -> Option<Def>
|
||||
NUM_TAN => num_tan,
|
||||
NUM_DIV_FLOAT => num_div_float,
|
||||
NUM_DIV_FLOAT_CHECKED => num_div_float_checked,
|
||||
NUM_DIV_FLOOR => num_div_floor,
|
||||
NUM_DIV_FLOOR_CHECKED => num_div_floor_checked,
|
||||
NUM_DIV_TRUNC => num_div_trunc,
|
||||
NUM_DIV_TRUNC_CHECKED => num_div_trunc_checked,
|
||||
NUM_DIV_CEIL => num_div_ceil,
|
||||
NUM_DIV_CEIL_CHECKED => num_div_ceil_checked,
|
||||
NUM_ABS => num_abs,
|
||||
@ -229,24 +230,6 @@ pub fn builtin_defs_map(symbol: Symbol, var_store: &mut VarStore) -> Option<Def>
|
||||
NUM_SHIFT_RIGHT => num_shift_right_by,
|
||||
NUM_SHIFT_RIGHT_ZERO_FILL => num_shift_right_zf_by,
|
||||
NUM_INT_CAST=> num_int_cast,
|
||||
NUM_MIN_I8=> num_min_i8,
|
||||
NUM_MAX_I8=> num_max_i8,
|
||||
NUM_MIN_U8=> num_min_u8,
|
||||
NUM_MAX_U8=> num_max_u8,
|
||||
NUM_MIN_I16=> num_min_i16,
|
||||
NUM_MAX_I16=> num_max_i16,
|
||||
NUM_MIN_U16=> num_min_u16,
|
||||
NUM_MAX_U16=> num_max_u16,
|
||||
NUM_MIN_I32=> num_min_i32,
|
||||
NUM_MAX_I32=> num_max_i32,
|
||||
NUM_MIN_U32=> num_min_u32,
|
||||
NUM_MAX_U32=> num_max_u32,
|
||||
NUM_MIN_I64=> num_min_i64,
|
||||
NUM_MAX_I64=> num_max_i64,
|
||||
NUM_MIN_U64=> num_min_u64,
|
||||
NUM_MAX_U64=> num_max_u64,
|
||||
NUM_MIN_I128=> num_min_i128,
|
||||
NUM_MAX_I128=> num_max_i128,
|
||||
NUM_TO_I8 => num_to_i8,
|
||||
NUM_TO_I8_CHECKED => num_to_i8_checked,
|
||||
NUM_TO_I16 => num_to_i16,
|
||||
@ -1473,106 +1456,6 @@ fn num_int_cast(symbol: Symbol, var_store: &mut VarStore) -> Def {
|
||||
lowlevel_1(symbol, LowLevel::NumIntCast, var_store)
|
||||
}
|
||||
|
||||
/// Num.minI8: I8
|
||||
fn num_min_i8(symbol: Symbol, var_store: &mut VarStore) -> Def {
|
||||
int_min_or_max::<i8>(symbol, var_store, i8::MIN, IntBound::Exact(IntWidth::I8))
|
||||
}
|
||||
|
||||
/// Num.maxI8: I8
|
||||
fn num_max_i8(symbol: Symbol, var_store: &mut VarStore) -> Def {
|
||||
int_min_or_max::<i8>(symbol, var_store, i8::MAX, IntBound::Exact(IntWidth::I8))
|
||||
}
|
||||
|
||||
/// Num.minU8: U8
|
||||
fn num_min_u8(symbol: Symbol, var_store: &mut VarStore) -> Def {
|
||||
int_min_or_max::<u8>(symbol, var_store, u8::MIN, IntBound::Exact(IntWidth::U8))
|
||||
}
|
||||
|
||||
/// Num.maxU8: U8
|
||||
fn num_max_u8(symbol: Symbol, var_store: &mut VarStore) -> Def {
|
||||
int_min_or_max::<u8>(symbol, var_store, u8::MAX, IntBound::Exact(IntWidth::U8))
|
||||
}
|
||||
|
||||
/// Num.minI16: I16
|
||||
fn num_min_i16(symbol: Symbol, var_store: &mut VarStore) -> Def {
|
||||
int_min_or_max::<i16>(symbol, var_store, i16::MIN, IntBound::Exact(IntWidth::I16))
|
||||
}
|
||||
|
||||
/// Num.maxI16: I16
|
||||
fn num_max_i16(symbol: Symbol, var_store: &mut VarStore) -> Def {
|
||||
int_min_or_max::<i16>(symbol, var_store, i16::MAX, IntBound::Exact(IntWidth::I16))
|
||||
}
|
||||
|
||||
/// Num.minU16: U16
|
||||
fn num_min_u16(symbol: Symbol, var_store: &mut VarStore) -> Def {
|
||||
int_min_or_max::<u16>(symbol, var_store, u16::MIN, IntBound::Exact(IntWidth::U16))
|
||||
}
|
||||
|
||||
/// Num.maxU16: U16
|
||||
fn num_max_u16(symbol: Symbol, var_store: &mut VarStore) -> Def {
|
||||
int_min_or_max::<u16>(symbol, var_store, u16::MAX, IntBound::Exact(IntWidth::U16))
|
||||
}
|
||||
|
||||
/// Num.minI32: I32
|
||||
fn num_min_i32(symbol: Symbol, var_store: &mut VarStore) -> Def {
|
||||
int_min_or_max::<i32>(symbol, var_store, i32::MIN, IntBound::Exact(IntWidth::I32))
|
||||
}
|
||||
|
||||
/// Num.maxI32: I32
|
||||
fn num_max_i32(symbol: Symbol, var_store: &mut VarStore) -> Def {
|
||||
int_min_or_max::<i32>(symbol, var_store, i32::MAX, IntBound::Exact(IntWidth::I32))
|
||||
}
|
||||
|
||||
/// Num.minU32: U32
|
||||
fn num_min_u32(symbol: Symbol, var_store: &mut VarStore) -> Def {
|
||||
int_min_or_max::<u32>(symbol, var_store, u32::MIN, IntBound::Exact(IntWidth::U32))
|
||||
}
|
||||
|
||||
/// Num.maxU32: U32
|
||||
fn num_max_u32(symbol: Symbol, var_store: &mut VarStore) -> Def {
|
||||
int_min_or_max::<u32>(symbol, var_store, u32::MAX, IntBound::Exact(IntWidth::U32))
|
||||
}
|
||||
|
||||
/// Num.minI64: I64
|
||||
fn num_min_i64(symbol: Symbol, var_store: &mut VarStore) -> Def {
|
||||
int_min_or_max::<i64>(symbol, var_store, i64::MIN, IntBound::Exact(IntWidth::I64))
|
||||
}
|
||||
|
||||
/// Num.maxI64: I64
|
||||
fn num_max_i64(symbol: Symbol, var_store: &mut VarStore) -> Def {
|
||||
int_min_or_max::<i64>(symbol, var_store, i64::MAX, IntBound::Exact(IntWidth::I64))
|
||||
}
|
||||
|
||||
/// Num.minU64: U64
|
||||
fn num_min_u64(symbol: Symbol, var_store: &mut VarStore) -> Def {
|
||||
int_min_or_max::<u64>(symbol, var_store, u64::MIN, IntBound::Exact(IntWidth::U64))
|
||||
}
|
||||
|
||||
/// Num.maxU64: U64
|
||||
fn num_max_u64(symbol: Symbol, var_store: &mut VarStore) -> Def {
|
||||
int_min_or_max::<u64>(symbol, var_store, u64::MAX, IntBound::Exact(IntWidth::U64))
|
||||
}
|
||||
|
||||
/// Num.minI128: I128
|
||||
fn num_min_i128(symbol: Symbol, var_store: &mut VarStore) -> Def {
|
||||
int_min_or_max::<i128>(
|
||||
symbol,
|
||||
var_store,
|
||||
i128::MIN,
|
||||
IntBound::Exact(IntWidth::I128),
|
||||
)
|
||||
}
|
||||
|
||||
/// Num.maxI128: I128
|
||||
fn num_max_i128(symbol: Symbol, var_store: &mut VarStore) -> Def {
|
||||
int_min_or_max::<i128>(
|
||||
symbol,
|
||||
var_store,
|
||||
i128::MAX,
|
||||
IntBound::Exact(IntWidth::I128),
|
||||
)
|
||||
}
|
||||
|
||||
/// List.isEmpty : List * -> Bool
|
||||
fn list_is_empty(symbol: Symbol, var_store: &mut VarStore) -> Def {
|
||||
let list_var = var_store.fresh();
|
||||
@ -4098,6 +3981,11 @@ fn set_from_list(symbol: Symbol, var_store: &mut VarStore) -> Def {
|
||||
lowlevel_1(symbol, LowLevel::SetFromList, var_store)
|
||||
}
|
||||
|
||||
/// Set.toDict : Set k -> Dict k {}
|
||||
fn set_to_dict(symbol: Symbol, var_store: &mut VarStore) -> Def {
|
||||
lowlevel_1(symbol, LowLevel::SetToDict, var_store)
|
||||
}
|
||||
|
||||
/// Set.insert : Set k, k -> Set k
|
||||
fn set_insert(symbol: Symbol, var_store: &mut VarStore) -> Def {
|
||||
let dict_var = var_store.fresh();
|
||||
@ -4369,13 +4257,13 @@ fn num_div_float_checked(symbol: Symbol, var_store: &mut VarStore) -> Def {
|
||||
)
|
||||
}
|
||||
|
||||
/// Num.divFloor : Int a, Int a -> Int a
|
||||
fn num_div_floor(symbol: Symbol, var_store: &mut VarStore) -> Def {
|
||||
/// Num.divTrunc : Int a, Int a -> Int a
|
||||
fn num_div_trunc(symbol: Symbol, var_store: &mut VarStore) -> Def {
|
||||
num_binop(symbol, var_store, LowLevel::NumDivUnchecked)
|
||||
}
|
||||
|
||||
/// Num.divFloorChecked : Int a , Int a -> Result (Int a) [ DivByZero ]*
|
||||
fn num_div_floor_checked(symbol: Symbol, var_store: &mut VarStore) -> Def {
|
||||
/// Num.divTruncChecked : Int a , Int a -> Result (Int a) [ DivByZero ]*
|
||||
fn num_div_trunc_checked(symbol: Symbol, var_store: &mut VarStore) -> Def {
|
||||
let bool_var = var_store.fresh();
|
||||
let num_var = var_store.fresh();
|
||||
let unbound_zero_var = var_store.fresh();
|
||||
@ -5411,36 +5299,6 @@ fn defn_help(
|
||||
})
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn int_min_or_max<I128>(symbol: Symbol, var_store: &mut VarStore, i: I128, bound: IntBound) -> Def
|
||||
where
|
||||
I128: Into<i128>,
|
||||
{
|
||||
let int_var = var_store.fresh();
|
||||
let int_precision_var = var_store.fresh();
|
||||
let body = int::<I128>(int_var, int_precision_var, i, bound);
|
||||
|
||||
let std = roc_builtins::std::types();
|
||||
let solved = std.get(&symbol).unwrap();
|
||||
let mut free_vars = roc_types::solved_types::FreeVars::default();
|
||||
let signature = roc_types::solved_types::to_type(&solved.0, &mut free_vars, var_store);
|
||||
|
||||
let annotation = crate::def::Annotation {
|
||||
signature,
|
||||
introduced_variables: Default::default(),
|
||||
region: Region::zero(),
|
||||
aliases: Default::default(),
|
||||
};
|
||||
|
||||
Def {
|
||||
annotation: Some(annotation),
|
||||
expr_var: int_var,
|
||||
loc_expr: Loc::at_zero(body),
|
||||
loc_pattern: Loc::at_zero(Pattern::Identifier(symbol)),
|
||||
pattern_vars: SendMap::default(),
|
||||
}
|
||||
}
|
||||
|
||||
fn num_no_bound() -> NumericBound {
|
||||
NumericBound::None
|
||||
}
|
||||
|
@ -241,10 +241,12 @@ pub fn canonicalize_defs<'a>(
|
||||
let pending_type_defs = type_defs
|
||||
.into_iter()
|
||||
.filter_map(|loc_def| {
|
||||
to_pending_type_def(env, loc_def.value, &mut scope).map(|(new_output, pending_def)| {
|
||||
output.union(new_output);
|
||||
pending_def
|
||||
})
|
||||
to_pending_type_def(env, loc_def.value, &mut scope, pattern_type).map(
|
||||
|(new_output, pending_def)| {
|
||||
output.union(new_output);
|
||||
pending_def
|
||||
},
|
||||
)
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
@ -344,7 +346,8 @@ pub fn canonicalize_defs<'a>(
|
||||
|
||||
let mut named = can_ann.introduced_variables.named;
|
||||
for loc_lowercase in vars.iter() {
|
||||
match named.iter().position(|nv| nv.name == loc_lowercase.value) {
|
||||
let opt_index = named.iter().position(|nv| nv.name == loc_lowercase.value);
|
||||
match opt_index {
|
||||
Some(index) => {
|
||||
// This is a valid lowercase rigid var for the type def.
|
||||
let named_variable = named.swap_remove(index);
|
||||
@ -542,7 +545,13 @@ pub fn canonicalize_defs<'a>(
|
||||
flex_vars: iv.collect_flex(),
|
||||
};
|
||||
|
||||
can_members.push((member_sym, name_region, member_annot.typ, variables));
|
||||
can_members.push((
|
||||
member_sym,
|
||||
name_region,
|
||||
var_store.fresh(),
|
||||
member_annot.typ,
|
||||
variables,
|
||||
));
|
||||
}
|
||||
|
||||
// Store what symbols a type must define implementations for to have this ability.
|
||||
@ -690,7 +699,7 @@ pub fn sort_can_defs(
|
||||
if let Some(References { value_lookups, .. }) = env.closures.get(symbol) {
|
||||
let home = env.home;
|
||||
|
||||
for lookup in value_lookups {
|
||||
for lookup in value_lookups.iter() {
|
||||
if lookup != symbol && lookup.module_id() == home {
|
||||
// DO NOT register a self-call behind a lambda!
|
||||
//
|
||||
@ -741,7 +750,7 @@ pub fn sort_can_defs(
|
||||
|
||||
// if the current symbol is a closure, peek into its body
|
||||
if let Some(References { value_lookups, .. }) = env.closures.get(symbol) {
|
||||
for lookup in value_lookups {
|
||||
for lookup in value_lookups.iter() {
|
||||
loc_succ.push(*lookup);
|
||||
}
|
||||
}
|
||||
@ -1306,7 +1315,7 @@ fn canonicalize_pending_value_def<'a>(
|
||||
let (mut loc_can_expr, can_output) =
|
||||
canonicalize_expr(env, var_store, scope, loc_expr.region, &loc_expr.value);
|
||||
|
||||
output.references = output.references.union(can_output.references.clone());
|
||||
output.references.union_mut(&can_output.references);
|
||||
|
||||
// reset the tailcallable_symbol
|
||||
env.tailcallable_symbol = outer_identifier;
|
||||
@ -1356,7 +1365,7 @@ fn canonicalize_pending_value_def<'a>(
|
||||
// Recursion doesn't count as referencing. (If it did, all recursive functions
|
||||
// would result in circular def errors!)
|
||||
refs_by_symbol.entry(symbol).and_modify(|(_, refs)| {
|
||||
refs.value_lookups = refs.value_lookups.without(&symbol);
|
||||
refs.value_lookups.remove(&symbol);
|
||||
});
|
||||
|
||||
// renamed_closure_def = Some(&symbol);
|
||||
@ -1496,7 +1505,7 @@ fn canonicalize_pending_value_def<'a>(
|
||||
// Recursion doesn't count as referencing. (If it did, all recursive functions
|
||||
// would result in circular def errors!)
|
||||
refs_by_symbol.entry(symbol).and_modify(|(_, refs)| {
|
||||
refs.value_lookups = refs.value_lookups.without(&symbol);
|
||||
refs.value_lookups.remove(&symbol);
|
||||
});
|
||||
|
||||
loc_can_expr.value = Closure(ClosureData {
|
||||
@ -1586,7 +1595,7 @@ pub fn can_defs_with_return<'a>(
|
||||
output
|
||||
.introduced_variables
|
||||
.union(&defs_output.introduced_variables);
|
||||
output.references = output.references.union(defs_output.references);
|
||||
output.references.union_mut(&defs_output.references);
|
||||
|
||||
// Now that we've collected all the references, check to see if any of the new idents
|
||||
// we defined went unused by the return expression. If any were unused, report it.
|
||||
@ -1640,7 +1649,7 @@ fn closure_recursivity(symbol: Symbol, closures: &MutMap<Symbol, References>) ->
|
||||
let mut stack = Vec::new();
|
||||
|
||||
if let Some(references) = closures.get(&symbol) {
|
||||
for v in &references.calls {
|
||||
for v in references.calls.iter() {
|
||||
stack.push(*v);
|
||||
}
|
||||
|
||||
@ -1656,7 +1665,7 @@ fn closure_recursivity(symbol: Symbol, closures: &MutMap<Symbol, References>) ->
|
||||
// if it calls any functions
|
||||
if let Some(nested_references) = closures.get(&nested_symbol) {
|
||||
// add its called to the stack
|
||||
for v in &nested_references.calls {
|
||||
for v in nested_references.calls.iter() {
|
||||
stack.push(*v);
|
||||
}
|
||||
}
|
||||
@ -1672,6 +1681,7 @@ fn to_pending_type_def<'a>(
|
||||
env: &mut Env<'a>,
|
||||
def: &'a ast::TypeDef<'a>,
|
||||
scope: &mut Scope,
|
||||
pattern_type: PatternType,
|
||||
) -> Option<(Output, PendingTypeDef<'a>)> {
|
||||
use ast::TypeDef::*;
|
||||
|
||||
@ -1755,6 +1765,19 @@ fn to_pending_type_def<'a>(
|
||||
}
|
||||
}
|
||||
|
||||
Ability {
|
||||
header, members, ..
|
||||
} if pattern_type != PatternType::TopLevelDef => {
|
||||
let header_region = header.region();
|
||||
let region = Region::span_across(
|
||||
&header_region,
|
||||
&members.last().map(|m| m.region()).unwrap_or(header_region),
|
||||
);
|
||||
env.problem(Problem::AbilityNotOnToplevel { region });
|
||||
|
||||
Some((Output::default(), PendingTypeDef::InvalidAbility))
|
||||
}
|
||||
|
||||
Ability {
|
||||
header: TypeHeader { name, vars },
|
||||
members,
|
||||
|
@ -4,7 +4,7 @@ use crate::env::Env;
|
||||
use crate::expr::{ClosureData, Expr, Recursive};
|
||||
use crate::pattern::Pattern;
|
||||
use crate::scope::Scope;
|
||||
use roc_collections::all::{MutSet, SendMap};
|
||||
use roc_collections::all::{SendMap, VecSet};
|
||||
use roc_module::called_via::CalledVia;
|
||||
use roc_module::ident::TagName;
|
||||
use roc_module::symbol::Symbol;
|
||||
@ -12,7 +12,7 @@ use roc_region::all::{Loc, Region};
|
||||
use roc_types::subs::{VarStore, Variable};
|
||||
use roc_types::types::{AliasKind, Type, TypeExtension};
|
||||
|
||||
#[derive(Default, Clone, Copy)]
|
||||
#[derive(Debug, Default, Clone, Copy)]
|
||||
pub(crate) struct HostedGeneratedFunctions {
|
||||
pub(crate) after: bool,
|
||||
pub(crate) map: bool,
|
||||
@ -39,7 +39,7 @@ pub(crate) fn build_effect_builtins(
|
||||
scope: &mut Scope,
|
||||
effect_symbol: Symbol,
|
||||
var_store: &mut VarStore,
|
||||
exposed_symbols: &mut MutSet<Symbol>,
|
||||
exposed_symbols: &mut VecSet<Symbol>,
|
||||
declarations: &mut Vec<Declaration>,
|
||||
generated_functions: HostedGeneratedFunctions,
|
||||
) {
|
||||
|
@ -1,5 +1,5 @@
|
||||
use crate::procedure::References;
|
||||
use roc_collections::all::{MutMap, MutSet};
|
||||
use roc_collections::all::{MutMap, VecSet};
|
||||
use roc_module::ident::{Ident, Lowercase, ModuleName};
|
||||
use roc_module::symbol::{IdentIds, ModuleId, ModuleIds, Symbol};
|
||||
use roc_problem::can::{Problem, RuntimeError};
|
||||
@ -28,12 +28,12 @@ pub struct Env<'a> {
|
||||
pub closure_name_symbol: Option<Symbol>,
|
||||
|
||||
/// Symbols of values/functions which were referenced by qualified lookups.
|
||||
pub qualified_value_lookups: MutSet<Symbol>,
|
||||
pub qualified_value_lookups: VecSet<Symbol>,
|
||||
|
||||
/// Symbols of types which were referenced by qualified lookups.
|
||||
pub qualified_type_lookups: MutSet<Symbol>,
|
||||
pub qualified_type_lookups: VecSet<Symbol>,
|
||||
|
||||
pub top_level_symbols: MutSet<Symbol>,
|
||||
pub top_level_symbols: VecSet<Symbol>,
|
||||
|
||||
pub ident_ids: IdentIds,
|
||||
pub exposed_ident_ids: IdentIds,
|
||||
@ -54,11 +54,11 @@ impl<'a> Env<'a> {
|
||||
exposed_ident_ids,
|
||||
problems: Vec::new(),
|
||||
closures: MutMap::default(),
|
||||
qualified_value_lookups: MutSet::default(),
|
||||
qualified_type_lookups: MutSet::default(),
|
||||
qualified_value_lookups: VecSet::default(),
|
||||
qualified_type_lookups: VecSet::default(),
|
||||
tailcallable_symbol: None,
|
||||
closure_name_symbol: None,
|
||||
top_level_symbols: MutSet::default(),
|
||||
top_level_symbols: VecSet::default(),
|
||||
}
|
||||
}
|
||||
|
||||
@ -97,16 +97,19 @@ impl<'a> Env<'a> {
|
||||
|
||||
Ok(symbol)
|
||||
}
|
||||
None => Err(RuntimeError::LookupNotInScope(
|
||||
Loc {
|
||||
value: ident,
|
||||
region,
|
||||
},
|
||||
self.ident_ids
|
||||
.idents()
|
||||
.map(|(_, string)| string.as_ref().into())
|
||||
.collect(),
|
||||
)),
|
||||
None => {
|
||||
let error = RuntimeError::LookupNotInScope(
|
||||
Loc {
|
||||
value: ident,
|
||||
region,
|
||||
},
|
||||
self.ident_ids
|
||||
.idents()
|
||||
.map(|(_, string)| string.as_ref().into())
|
||||
.collect(),
|
||||
);
|
||||
Err(error)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
match self.dep_idents.get(&module_id) {
|
||||
|
@ -9,7 +9,7 @@ use crate::num::{
|
||||
use crate::pattern::{canonicalize_pattern, Pattern};
|
||||
use crate::procedure::References;
|
||||
use crate::scope::Scope;
|
||||
use roc_collections::all::{MutMap, MutSet, SendMap};
|
||||
use roc_collections::all::{MutMap, MutSet, SendMap, VecSet};
|
||||
use roc_module::called_via::CalledVia;
|
||||
use roc_module::ident::{ForeignSymbol, Lowercase, TagName};
|
||||
use roc_module::low_level::LowLevel;
|
||||
@ -29,12 +29,12 @@ pub struct Output {
|
||||
pub tail_call: Option<Symbol>,
|
||||
pub introduced_variables: IntroducedVariables,
|
||||
pub aliases: SendMap<Symbol, Alias>,
|
||||
pub non_closures: MutSet<Symbol>,
|
||||
pub non_closures: VecSet<Symbol>,
|
||||
}
|
||||
|
||||
impl Output {
|
||||
pub fn union(&mut self, other: Self) {
|
||||
self.references.union_mut(other.references);
|
||||
self.references.union_mut(&other.references);
|
||||
|
||||
if let (None, Some(later)) = (self.tail_call, other.tail_call) {
|
||||
self.tail_call = Some(later);
|
||||
@ -354,7 +354,7 @@ pub fn canonicalize_expr<'a>(
|
||||
if let Var(symbol) = &can_update.value {
|
||||
match canonicalize_fields(env, var_store, scope, region, fields.items) {
|
||||
Ok((can_fields, mut output)) => {
|
||||
output.references = output.references.union(update_out.references);
|
||||
output.references.union_mut(&update_out.references);
|
||||
|
||||
let answer = Update {
|
||||
record_var: var_store.fresh(),
|
||||
@ -432,7 +432,7 @@ pub fn canonicalize_expr<'a>(
|
||||
let (can_expr, elem_out) =
|
||||
canonicalize_expr(env, var_store, scope, loc_elem.region, &loc_elem.value);
|
||||
|
||||
references = references.union(elem_out.references);
|
||||
references.union_mut(&elem_out.references);
|
||||
|
||||
can_elems.push(can_expr);
|
||||
}
|
||||
@ -466,7 +466,7 @@ pub fn canonicalize_expr<'a>(
|
||||
canonicalize_expr(env, var_store, scope, loc_arg.region, &loc_arg.value);
|
||||
|
||||
args.push((var_store.fresh(), arg_expr));
|
||||
output.references = output.references.union(arg_out.references);
|
||||
output.references.union_mut(&arg_out.references);
|
||||
}
|
||||
|
||||
if let ast::Expr::OpaqueRef(name) = loc_fn.value {
|
||||
@ -753,7 +753,7 @@ pub fn canonicalize_expr<'a>(
|
||||
let (can_when_branch, branch_references) =
|
||||
canonicalize_when_branch(env, var_store, scope, region, *branch, &mut output);
|
||||
|
||||
output.references = output.references.union(branch_references);
|
||||
output.references.union_mut(&branch_references);
|
||||
|
||||
can_branches.push(can_when_branch);
|
||||
}
|
||||
@ -886,8 +886,8 @@ pub fn canonicalize_expr<'a>(
|
||||
|
||||
branches.push((loc_cond, loc_then));
|
||||
|
||||
output.references = output.references.union(cond_output.references);
|
||||
output.references = output.references.union(then_output.references);
|
||||
output.references.union_mut(&cond_output.references);
|
||||
output.references.union_mut(&then_output.references);
|
||||
}
|
||||
|
||||
let (loc_else, else_output) = canonicalize_expr(
|
||||
@ -898,7 +898,7 @@ pub fn canonicalize_expr<'a>(
|
||||
&final_else_branch.value,
|
||||
);
|
||||
|
||||
output.references = output.references.union(else_output.references);
|
||||
output.references.union_mut(&else_output.references);
|
||||
|
||||
(
|
||||
If {
|
||||
@ -1167,7 +1167,7 @@ fn canonicalize_fields<'a>(
|
||||
});
|
||||
}
|
||||
|
||||
output.references = output.references.union(field_out.references);
|
||||
output.references.union_mut(&field_out.references);
|
||||
}
|
||||
Err(CanonicalizeFieldProblem::InvalidOptionalValue {
|
||||
field_name,
|
||||
|
@ -7,7 +7,7 @@ use crate::operator::desugar_def;
|
||||
use crate::pattern::Pattern;
|
||||
use crate::scope::Scope;
|
||||
use bumpalo::Bump;
|
||||
use roc_collections::all::{MutMap, MutSet, SendMap};
|
||||
use roc_collections::all::{MutMap, SendMap, VecSet};
|
||||
use roc_module::ident::Lowercase;
|
||||
use roc_module::ident::{Ident, TagName};
|
||||
use roc_module::symbol::{IdentIds, ModuleId, ModuleIds, Symbol};
|
||||
@ -23,9 +23,9 @@ use roc_types::types::{Alias, AliasKind, Type};
|
||||
pub struct Module {
|
||||
pub module_id: ModuleId,
|
||||
pub exposed_imports: MutMap<Symbol, Variable>,
|
||||
pub exposed_symbols: MutSet<Symbol>,
|
||||
pub referenced_values: MutSet<Symbol>,
|
||||
pub referenced_types: MutSet<Symbol>,
|
||||
pub exposed_symbols: VecSet<Symbol>,
|
||||
pub referenced_values: VecSet<Symbol>,
|
||||
pub referenced_types: VecSet<Symbol>,
|
||||
/// all aliases. `bool` indicates whether it is exposed
|
||||
pub aliases: MutMap<Symbol, (bool, Alias)>,
|
||||
pub rigid_variables: RigidVariables,
|
||||
@ -36,7 +36,7 @@ pub struct Module {
|
||||
pub struct RigidVariables {
|
||||
pub named: MutMap<Variable, Lowercase>,
|
||||
pub able: MutMap<Variable, (Lowercase, Symbol)>,
|
||||
pub wildcards: MutSet<Variable>,
|
||||
pub wildcards: VecSet<Variable>,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
@ -48,8 +48,8 @@ pub struct ModuleOutput {
|
||||
pub lookups: Vec<(Symbol, Variable, Region)>,
|
||||
pub problems: Vec<Problem>,
|
||||
pub ident_ids: IdentIds,
|
||||
pub referenced_values: MutSet<Symbol>,
|
||||
pub referenced_types: MutSet<Symbol>,
|
||||
pub referenced_values: VecSet<Symbol>,
|
||||
pub referenced_types: VecSet<Symbol>,
|
||||
pub scope: Scope,
|
||||
}
|
||||
|
||||
@ -77,6 +77,95 @@ fn validate_generate_with<'a>(
|
||||
(functions, unknown)
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
enum GeneratedInfo {
|
||||
Hosted {
|
||||
effect_symbol: Symbol,
|
||||
generated_functions: HostedGeneratedFunctions,
|
||||
},
|
||||
Builtin,
|
||||
NotSpecial,
|
||||
}
|
||||
|
||||
impl GeneratedInfo {
|
||||
fn from_header_for<'a>(
|
||||
env: &mut Env,
|
||||
scope: &mut Scope,
|
||||
var_store: &mut VarStore,
|
||||
header_for: &HeaderFor<'a>,
|
||||
) -> Self {
|
||||
match header_for {
|
||||
HeaderFor::Hosted {
|
||||
generates,
|
||||
generates_with,
|
||||
} => {
|
||||
let name: &str = generates.into();
|
||||
let (generated_functions, unknown_generated) =
|
||||
validate_generate_with(generates_with);
|
||||
|
||||
for unknown in unknown_generated {
|
||||
env.problem(Problem::UnknownGeneratesWith(unknown));
|
||||
}
|
||||
|
||||
let effect_symbol = scope
|
||||
.introduce(
|
||||
name.into(),
|
||||
&env.exposed_ident_ids,
|
||||
&mut env.ident_ids,
|
||||
Region::zero(),
|
||||
)
|
||||
.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,
|
||||
);
|
||||
|
||||
scope.add_alias(
|
||||
effect_symbol,
|
||||
Region::zero(),
|
||||
vec![Loc::at_zero(("a".into(), a_var))],
|
||||
actual,
|
||||
AliasKind::Structural,
|
||||
);
|
||||
}
|
||||
|
||||
GeneratedInfo::Hosted {
|
||||
effect_symbol,
|
||||
generated_functions,
|
||||
}
|
||||
}
|
||||
HeaderFor::Builtin { generates_with } => {
|
||||
debug_assert!(generates_with.is_empty());
|
||||
GeneratedInfo::Builtin
|
||||
}
|
||||
_ => GeneratedInfo::NotSpecial,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn has_no_implementation(expr: &Expr) -> bool {
|
||||
match expr {
|
||||
Expr::RuntimeError(RuntimeError::NoImplementationNamed { .. }) => true,
|
||||
Expr::Closure(closure_data)
|
||||
if matches!(
|
||||
closure_data.loc_body.value,
|
||||
Expr::RuntimeError(RuntimeError::NoImplementationNamed { .. })
|
||||
) =>
|
||||
{
|
||||
true
|
||||
}
|
||||
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
// TODO trim these down
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
pub fn canonicalize_module_defs<'a>(
|
||||
@ -89,7 +178,7 @@ pub fn canonicalize_module_defs<'a>(
|
||||
dep_idents: &'a MutMap<ModuleId, IdentIds>,
|
||||
aliases: MutMap<Symbol, Alias>,
|
||||
exposed_imports: MutMap<Ident, (Symbol, Region)>,
|
||||
exposed_symbols: &MutSet<Symbol>,
|
||||
exposed_symbols: &VecSet<Symbol>,
|
||||
var_store: &mut VarStore,
|
||||
) -> Result<ModuleOutput, RuntimeError> {
|
||||
let mut can_exposed_imports = MutMap::default();
|
||||
@ -107,59 +196,8 @@ pub fn canonicalize_module_defs<'a>(
|
||||
);
|
||||
}
|
||||
|
||||
struct Hosted {
|
||||
effect_symbol: Symbol,
|
||||
generated_functions: HostedGeneratedFunctions,
|
||||
}
|
||||
|
||||
let hosted_info = if let HeaderFor::Hosted {
|
||||
generates,
|
||||
generates_with,
|
||||
} = header_for
|
||||
{
|
||||
let name: &str = generates.into();
|
||||
let (generated_functions, unknown_generated) = validate_generate_with(generates_with);
|
||||
|
||||
for unknown in unknown_generated {
|
||||
env.problem(Problem::UnknownGeneratesWith(unknown));
|
||||
}
|
||||
|
||||
let effect_symbol = scope
|
||||
.introduce(
|
||||
name.into(),
|
||||
&env.exposed_ident_ids,
|
||||
&mut env.ident_ids,
|
||||
Region::zero(),
|
||||
)
|
||||
.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,
|
||||
);
|
||||
|
||||
scope.add_alias(
|
||||
effect_symbol,
|
||||
Region::zero(),
|
||||
vec![Loc::at_zero(("a".into(), a_var))],
|
||||
actual,
|
||||
AliasKind::Structural,
|
||||
);
|
||||
}
|
||||
|
||||
Some(Hosted {
|
||||
effect_symbol,
|
||||
generated_functions,
|
||||
})
|
||||
} else {
|
||||
None
|
||||
};
|
||||
let generated_info =
|
||||
GeneratedInfo::from_header_for(&mut env, &mut scope, var_store, header_for);
|
||||
|
||||
// Desugar operators (convert them to Apply calls, taking into account
|
||||
// operator precedence and associativity rules), before doing other canonicalization.
|
||||
@ -215,14 +253,25 @@ pub fn canonicalize_module_defs<'a>(
|
||||
panic!("TODO gracefully handle shadowing in imports.")
|
||||
}
|
||||
}
|
||||
} else if [
|
||||
Symbol::LIST_LIST,
|
||||
Symbol::STR_STR,
|
||||
Symbol::DICT_DICT,
|
||||
Symbol::SET_SET,
|
||||
Symbol::BOX_BOX_TYPE,
|
||||
]
|
||||
.contains(&symbol)
|
||||
{
|
||||
// These are not aliases but Apply's and we make sure they are always in scope
|
||||
} else {
|
||||
// This is a type alias
|
||||
|
||||
// the symbol should already be added to the scope when this module is canonicalized
|
||||
debug_assert!(
|
||||
scope.contains_alias(symbol),
|
||||
"apparently, {:?} is not actually a type alias",
|
||||
symbol
|
||||
"The {:?} is not a type alias known in {:?}",
|
||||
symbol,
|
||||
home
|
||||
);
|
||||
|
||||
// but now we know this symbol by a different identifier, so we still need to add it to
|
||||
@ -231,8 +280,11 @@ pub fn canonicalize_module_defs<'a>(
|
||||
Ok(()) => {
|
||||
// here we do nothing special
|
||||
}
|
||||
Err((_shadowed_symbol, _region)) => {
|
||||
panic!("TODO gracefully handle shadowing in imports.")
|
||||
Err((shadowed_symbol, _region)) => {
|
||||
panic!(
|
||||
"TODO gracefully handle shadowing in imports, {:?} is shadowed.",
|
||||
shadowed_symbol
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -273,8 +325,8 @@ pub fn canonicalize_module_defs<'a>(
|
||||
rigid_variables.wildcards.insert(var.value);
|
||||
}
|
||||
|
||||
let mut referenced_values = MutSet::default();
|
||||
let mut referenced_types = MutSet::default();
|
||||
let mut referenced_values = VecSet::default();
|
||||
let mut referenced_types = VecSet::default();
|
||||
|
||||
// Gather up all the symbols that were referenced across all the defs' lookups.
|
||||
referenced_values.extend(output.references.value_lookups);
|
||||
@ -314,12 +366,12 @@ pub fn canonicalize_module_defs<'a>(
|
||||
(Ok(mut declarations), output) => {
|
||||
use crate::def::Declaration::*;
|
||||
|
||||
if let Some(Hosted {
|
||||
if let GeneratedInfo::Hosted {
|
||||
effect_symbol,
|
||||
generated_functions,
|
||||
}) = hosted_info
|
||||
} = generated_info
|
||||
{
|
||||
let mut exposed_symbols = MutSet::default();
|
||||
let mut exposed_symbols = VecSet::default();
|
||||
|
||||
// NOTE this currently builds all functions, not just the ones that the user requested
|
||||
crate::effect_module::build_effect_builtins(
|
||||
@ -350,9 +402,21 @@ pub fn canonicalize_module_defs<'a>(
|
||||
// Temporary hack: we don't know exactly what symbols are hosted symbols,
|
||||
// and which are meant to be normal definitions without a body. So for now
|
||||
// we just assume they are hosted functions (meant to be provided by the platform)
|
||||
if let Some(Hosted { effect_symbol, .. }) = hosted_info {
|
||||
macro_rules! make_hosted_def {
|
||||
() => {
|
||||
if has_no_implementation(&def.loc_expr.value) {
|
||||
match generated_info {
|
||||
GeneratedInfo::Builtin => {
|
||||
let symbol = def.pattern_vars.iter().next().unwrap().0;
|
||||
match crate::builtins::builtin_defs_map(*symbol, var_store) {
|
||||
None => {
|
||||
panic!("A builtin module contains a signature without implementation for {:?}", symbol)
|
||||
}
|
||||
Some(mut replacement_def) => {
|
||||
replacement_def.annotation = def.annotation.take();
|
||||
*def = replacement_def;
|
||||
}
|
||||
}
|
||||
}
|
||||
GeneratedInfo::Hosted { effect_symbol, .. } => {
|
||||
let symbol = def.pattern_vars.iter().next().unwrap().0;
|
||||
let ident_id = symbol.ident_id();
|
||||
let ident =
|
||||
@ -376,27 +440,8 @@ pub fn canonicalize_module_defs<'a>(
|
||||
);
|
||||
|
||||
*def = hosted_def;
|
||||
};
|
||||
}
|
||||
|
||||
match &def.loc_expr.value {
|
||||
Expr::RuntimeError(RuntimeError::NoImplementationNamed {
|
||||
..
|
||||
}) => {
|
||||
make_hosted_def!();
|
||||
}
|
||||
Expr::Closure(closure_data)
|
||||
if matches!(
|
||||
closure_data.loc_body.value,
|
||||
Expr::RuntimeError(
|
||||
RuntimeError::NoImplementationNamed { .. }
|
||||
)
|
||||
) =>
|
||||
{
|
||||
make_hosted_def!();
|
||||
}
|
||||
|
||||
_ => {}
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -431,7 +476,7 @@ pub fn canonicalize_module_defs<'a>(
|
||||
|
||||
let mut aliases = MutMap::default();
|
||||
|
||||
if let Some(Hosted { effect_symbol, .. }) = hosted_info {
|
||||
if let GeneratedInfo::Hosted { effect_symbol, .. } = generated_info {
|
||||
// Remove this from exposed_symbols,
|
||||
// so that at the end of the process,
|
||||
// we can see if there were any
|
||||
@ -495,25 +540,14 @@ pub fn canonicalize_module_defs<'a>(
|
||||
|
||||
for declaration in declarations.iter_mut() {
|
||||
match declaration {
|
||||
Declare(def) => fix_values_captured_in_closure_def(def, &mut MutSet::default()),
|
||||
Declare(def) => fix_values_captured_in_closure_def(def, &mut VecSet::default()),
|
||||
DeclareRec(defs) => {
|
||||
fix_values_captured_in_closure_defs(defs, &mut MutSet::default())
|
||||
fix_values_captured_in_closure_defs(defs, &mut VecSet::default())
|
||||
}
|
||||
InvalidCycle(_) | Builtin(_) => {}
|
||||
}
|
||||
}
|
||||
|
||||
// TODO this loops over all symbols in the module, we can speed it up by having an
|
||||
// iterator over all builtin symbols
|
||||
for symbol in referenced_values.iter() {
|
||||
if symbol.is_builtin() {
|
||||
// this can fail when the symbol is for builtin types, or has no implementation yet
|
||||
if let Some(def) = crate::builtins::builtin_defs_map(*symbol, var_store) {
|
||||
declarations.push(Declaration::Builtin(def));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let output = ModuleOutput {
|
||||
scope,
|
||||
aliases,
|
||||
@ -535,7 +569,7 @@ pub fn canonicalize_module_defs<'a>(
|
||||
|
||||
fn fix_values_captured_in_closure_def(
|
||||
def: &mut crate::def::Def,
|
||||
no_capture_symbols: &mut MutSet<Symbol>,
|
||||
no_capture_symbols: &mut VecSet<Symbol>,
|
||||
) {
|
||||
// patterns can contain default expressions, so much go over them too!
|
||||
fix_values_captured_in_closure_pattern(&mut def.loc_pattern.value, no_capture_symbols);
|
||||
@ -545,7 +579,7 @@ fn fix_values_captured_in_closure_def(
|
||||
|
||||
fn fix_values_captured_in_closure_defs(
|
||||
defs: &mut Vec<crate::def::Def>,
|
||||
no_capture_symbols: &mut MutSet<Symbol>,
|
||||
no_capture_symbols: &mut VecSet<Symbol>,
|
||||
) {
|
||||
// recursive defs cannot capture each other
|
||||
for def in defs.iter() {
|
||||
@ -561,7 +595,7 @@ fn fix_values_captured_in_closure_defs(
|
||||
|
||||
fn fix_values_captured_in_closure_pattern(
|
||||
pattern: &mut crate::pattern::Pattern,
|
||||
no_capture_symbols: &mut MutSet<Symbol>,
|
||||
no_capture_symbols: &mut VecSet<Symbol>,
|
||||
) {
|
||||
use crate::pattern::Pattern::*;
|
||||
|
||||
@ -610,7 +644,7 @@ fn fix_values_captured_in_closure_pattern(
|
||||
|
||||
fn fix_values_captured_in_closure_expr(
|
||||
expr: &mut crate::expr::Expr,
|
||||
no_capture_symbols: &mut MutSet<Symbol>,
|
||||
no_capture_symbols: &mut VecSet<Symbol>,
|
||||
) {
|
||||
use crate::expr::Expr::*;
|
||||
|
||||
|
@ -423,7 +423,7 @@ fn binop_to_function(binop: BinOp) -> (&'static str, &'static str) {
|
||||
Caret => (ModuleName::NUM, "pow"),
|
||||
Star => (ModuleName::NUM, "mul"),
|
||||
Slash => (ModuleName::NUM, "div"),
|
||||
DoubleSlash => (ModuleName::NUM, "divFloor"),
|
||||
DoubleSlash => (ModuleName::NUM, "divTrunc"),
|
||||
Percent => (ModuleName::NUM, "rem"),
|
||||
DoublePercent => (ModuleName::NUM, "mod"),
|
||||
Plus => (ModuleName::NUM, "add"),
|
||||
|
@ -1,6 +1,6 @@
|
||||
use crate::expr::Expr;
|
||||
use crate::pattern::Pattern;
|
||||
use roc_collections::all::ImSet;
|
||||
use roc_collections::all::VecSet;
|
||||
use roc_module::symbol::Symbol;
|
||||
use roc_region::all::{Loc, Region};
|
||||
use roc_types::subs::Variable;
|
||||
@ -44,12 +44,12 @@ impl Procedure {
|
||||
/// so it's important that building the same code gives the same order every time!
|
||||
#[derive(Clone, Debug, Default, PartialEq)]
|
||||
pub struct References {
|
||||
pub bound_symbols: ImSet<Symbol>,
|
||||
pub type_lookups: ImSet<Symbol>,
|
||||
pub value_lookups: ImSet<Symbol>,
|
||||
pub bound_symbols: VecSet<Symbol>,
|
||||
pub type_lookups: VecSet<Symbol>,
|
||||
pub value_lookups: VecSet<Symbol>,
|
||||
/// Aliases or opaque types referenced
|
||||
pub referenced_type_defs: ImSet<Symbol>,
|
||||
pub calls: ImSet<Symbol>,
|
||||
pub referenced_type_defs: VecSet<Symbol>,
|
||||
pub calls: VecSet<Symbol>,
|
||||
}
|
||||
|
||||
impl References {
|
||||
@ -57,22 +57,15 @@ impl References {
|
||||
Self::default()
|
||||
}
|
||||
|
||||
pub fn union(mut self, other: References) -> Self {
|
||||
self.value_lookups = self.value_lookups.union(other.value_lookups);
|
||||
self.type_lookups = self.type_lookups.union(other.type_lookups);
|
||||
self.calls = self.calls.union(other.calls);
|
||||
self.bound_symbols = self.bound_symbols.union(other.bound_symbols);
|
||||
self.referenced_type_defs = self.referenced_type_defs.union(other.referenced_type_defs);
|
||||
|
||||
self
|
||||
}
|
||||
|
||||
pub fn union_mut(&mut self, other: References) {
|
||||
self.value_lookups.extend(other.value_lookups);
|
||||
self.type_lookups.extend(other.type_lookups);
|
||||
self.calls.extend(other.calls);
|
||||
self.bound_symbols.extend(other.bound_symbols);
|
||||
self.referenced_type_defs.extend(other.referenced_type_defs);
|
||||
pub fn union_mut(&mut self, other: &References) {
|
||||
self.value_lookups
|
||||
.extend(other.value_lookups.iter().copied());
|
||||
self.type_lookups.extend(other.type_lookups.iter().copied());
|
||||
self.calls.extend(other.calls.iter().copied());
|
||||
self.bound_symbols
|
||||
.extend(other.bound_symbols.iter().copied());
|
||||
self.referenced_type_defs
|
||||
.extend(other.referenced_type_defs.iter().copied());
|
||||
}
|
||||
|
||||
pub fn has_value_lookup(&self, symbol: Symbol) -> bool {
|
||||
|
@ -95,13 +95,17 @@ impl Scope {
|
||||
pub fn lookup(&self, ident: &Ident, region: Region) -> Result<Symbol, RuntimeError> {
|
||||
match self.idents.get(ident) {
|
||||
Some((symbol, _)) => Ok(*symbol),
|
||||
None => Err(RuntimeError::LookupNotInScope(
|
||||
Loc {
|
||||
region,
|
||||
value: ident.clone(),
|
||||
},
|
||||
self.idents.keys().map(|v| v.as_ref().into()).collect(),
|
||||
)),
|
||||
None => {
|
||||
let error = RuntimeError::LookupNotInScope(
|
||||
Loc {
|
||||
region,
|
||||
value: ident.clone(),
|
||||
},
|
||||
self.idents.keys().map(|v| v.as_ref().into()).collect(),
|
||||
);
|
||||
|
||||
Err(error)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -7,10 +7,11 @@ use roc_can::expr::{canonicalize_expr, Expr};
|
||||
use roc_can::operator;
|
||||
use roc_can::scope::Scope;
|
||||
use roc_collections::all::MutMap;
|
||||
use roc_module::symbol::{IdentIds, Interns, ModuleId, ModuleIds};
|
||||
use roc_module::symbol::{IdentIds, Interns, ModuleId, ModuleIds, Symbol};
|
||||
use roc_problem::can::Problem;
|
||||
use roc_region::all::{Loc, Region};
|
||||
use roc_types::subs::{VarStore, Variable};
|
||||
use roc_types::types::Type;
|
||||
use std::hash::Hash;
|
||||
|
||||
pub fn test_home() -> ModuleId {
|
||||
@ -55,6 +56,14 @@ pub fn can_expr_with(arena: &Bump, home: ModuleId, expr_str: &str) -> CanExprOut
|
||||
let loc_expr = operator::desugar_expr(arena, &loc_expr);
|
||||
|
||||
let mut scope = Scope::new(home, &mut var_store);
|
||||
scope.add_alias(
|
||||
Symbol::NUM_INT,
|
||||
Region::zero(),
|
||||
vec![Loc::at_zero(("a".into(), Variable::EMPTY_RECORD))],
|
||||
Type::EmptyRec,
|
||||
roc_types::types::AliasKind::Structural,
|
||||
);
|
||||
|
||||
let dep_idents = IdentIds::exposed_builtins(0);
|
||||
let mut env = Env::new(home, &dep_idents, &module_ids, IdentIds::default());
|
||||
let (loc_expr, output) = canonicalize_expr(
|
||||
|
@ -50,7 +50,7 @@ mod test_can {
|
||||
assert_eq!(IntValue::I128(expected), actual);
|
||||
}
|
||||
actual => {
|
||||
panic!("Expected an Int *, but got: {:?}", actual);
|
||||
panic!("Expected an Num.Int *, but got: {:?}", actual);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -274,7 +274,7 @@ mod test_can {
|
||||
fn correct_annotated_body() {
|
||||
let src = indoc!(
|
||||
r#"
|
||||
f : Int * -> Int *
|
||||
f : Num.Int * -> Num.Int *
|
||||
f = \ a -> a
|
||||
|
||||
f
|
||||
@ -290,7 +290,7 @@ mod test_can {
|
||||
fn correct_annotated_body_with_comments() {
|
||||
let src = indoc!(
|
||||
r#"
|
||||
f : Int * -> Int * # comment
|
||||
f : Num.Int * -> Num.Int * # comment
|
||||
f = \ a -> a
|
||||
|
||||
f
|
||||
@ -306,7 +306,7 @@ mod test_can {
|
||||
fn name_mismatch_annotated_body() {
|
||||
let src = indoc!(
|
||||
r#"
|
||||
f : Int * -> Int *
|
||||
f : Num.Int * -> Num.Int *
|
||||
g = \ a -> a
|
||||
|
||||
g
|
||||
@ -332,7 +332,7 @@ mod test_can {
|
||||
fn name_mismatch_annotated_body_with_comment() {
|
||||
let src = indoc!(
|
||||
r#"
|
||||
f : Int * -> Int * # comment
|
||||
f : Num.Int * -> Num.Int * # comment
|
||||
g = \ a -> a
|
||||
|
||||
g
|
||||
@ -358,7 +358,7 @@ mod test_can {
|
||||
fn separated_annotated_body() {
|
||||
let src = indoc!(
|
||||
r#"
|
||||
f : Int * -> Int *
|
||||
f : Num.Int * -> Num.Int *
|
||||
|
||||
f = \ a -> a
|
||||
|
||||
@ -381,7 +381,7 @@ mod test_can {
|
||||
fn separated_annotated_body_with_comment() {
|
||||
let src = indoc!(
|
||||
r#"
|
||||
f : Int * -> Int *
|
||||
f : Num.Int * -> Num.Int *
|
||||
# comment
|
||||
f = \ a -> a
|
||||
|
||||
@ -404,9 +404,9 @@ mod test_can {
|
||||
fn shadowed_annotation() {
|
||||
let src = indoc!(
|
||||
r#"
|
||||
f : Int * -> Int *
|
||||
f : Num.Int * -> Num.Int *
|
||||
|
||||
f : Int * -> Int *
|
||||
f : Num.Int * -> Num.Int *
|
||||
|
||||
f
|
||||
"#
|
||||
@ -428,7 +428,7 @@ mod test_can {
|
||||
fn correct_nested_unannotated_body() {
|
||||
let src = indoc!(
|
||||
r#"
|
||||
f : Int *
|
||||
f : Num.Int *
|
||||
f =
|
||||
g = 42
|
||||
|
||||
@ -447,9 +447,9 @@ mod test_can {
|
||||
fn correct_nested_annotated_body() {
|
||||
let src = indoc!(
|
||||
r#"
|
||||
f : Int *
|
||||
f : Num.Int *
|
||||
f =
|
||||
g : Int *
|
||||
g : Num.Int *
|
||||
g = 42
|
||||
|
||||
g + 1
|
||||
@ -467,11 +467,11 @@ mod test_can {
|
||||
fn correct_nested_body_annotated_multiple_lines() {
|
||||
let src = indoc!(
|
||||
r#"
|
||||
f : Int *
|
||||
f : Num.Int *
|
||||
f =
|
||||
g : Int *
|
||||
g : Num.Int *
|
||||
g = 42
|
||||
h : Int *
|
||||
h : Num.Int *
|
||||
h = 5
|
||||
z = 4
|
||||
g + h + z
|
||||
@ -489,10 +489,10 @@ mod test_can {
|
||||
fn correct_nested_body_unannotated_multiple_lines() {
|
||||
let src = indoc!(
|
||||
r#"
|
||||
f : Int *
|
||||
f : Num.Int *
|
||||
f =
|
||||
g = 42
|
||||
h : Int *
|
||||
h : Num.Int *
|
||||
h = 5
|
||||
z = 4
|
||||
g + h + z
|
||||
@ -509,7 +509,7 @@ mod test_can {
|
||||
fn correct_double_nested_body() {
|
||||
let src = indoc!(
|
||||
r#"
|
||||
f : Int *
|
||||
f : Num.Int *
|
||||
f =
|
||||
g =
|
||||
h = 42
|
||||
|
@ -220,3 +220,99 @@ macro_rules! mut_map {
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub struct VecSet<T> {
|
||||
elements: Vec<T>,
|
||||
}
|
||||
|
||||
impl<T> Default for VecSet<T> {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
elements: Vec::new(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: PartialEq> VecSet<T> {
|
||||
pub fn with_capacity(capacity: usize) -> Self {
|
||||
Self {
|
||||
elements: Vec::with_capacity(capacity),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn len(&self) -> usize {
|
||||
self.elements.len()
|
||||
}
|
||||
|
||||
pub fn is_empty(&self) -> bool {
|
||||
self.elements.is_empty()
|
||||
}
|
||||
|
||||
pub fn swap_remove(&mut self, index: usize) -> T {
|
||||
self.elements.swap_remove(index)
|
||||
}
|
||||
|
||||
pub fn insert(&mut self, value: T) -> bool {
|
||||
if self.elements.contains(&value) {
|
||||
true
|
||||
} else {
|
||||
self.elements.push(value);
|
||||
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
pub fn contains(&self, value: &T) -> bool {
|
||||
self.elements.contains(value)
|
||||
}
|
||||
|
||||
pub fn remove(&mut self, value: &T) {
|
||||
match self.elements.iter().position(|x| x == value) {
|
||||
None => {
|
||||
// just do nothing
|
||||
}
|
||||
Some(index) => {
|
||||
self.elements.swap_remove(index);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn iter(&self) -> impl Iterator<Item = &T> {
|
||||
self.elements.iter()
|
||||
}
|
||||
}
|
||||
|
||||
impl<A: Ord> Extend<A> for VecSet<A> {
|
||||
fn extend<T: IntoIterator<Item = A>>(&mut self, iter: T) {
|
||||
let it = iter.into_iter();
|
||||
let hint = it.size_hint();
|
||||
|
||||
match hint {
|
||||
(0, Some(0)) => {
|
||||
// done, do nothing
|
||||
}
|
||||
(1, Some(1)) | (2, Some(2)) => {
|
||||
for value in it {
|
||||
self.insert(value);
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
self.elements.extend(it);
|
||||
|
||||
self.elements.sort();
|
||||
self.elements.dedup();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> IntoIterator for VecSet<T> {
|
||||
type Item = T;
|
||||
|
||||
type IntoIter = std::vec::IntoIter<T>;
|
||||
|
||||
fn into_iter(self) -> Self::IntoIter {
|
||||
self.elements.into_iter()
|
||||
}
|
||||
}
|
||||
|
@ -596,109 +596,136 @@ pub fn constrain_expr(
|
||||
NoExpectation(cond_type.clone()),
|
||||
);
|
||||
|
||||
let mut branch_constraints = Vec::with_capacity(branches.len() + 1);
|
||||
branch_constraints.push(expr_con);
|
||||
let branch_var = *expr_var;
|
||||
let branch_type = Variable(branch_var);
|
||||
|
||||
match &expected {
|
||||
FromAnnotation(name, arity, ann_source, _typ) => {
|
||||
// NOTE deviation from elm.
|
||||
//
|
||||
// in elm, `_typ` is used, but because we have this `expr_var` too
|
||||
// and need to constrain it, this is what works and gives better error messages
|
||||
let typ = Type::Variable(*expr_var);
|
||||
let branch_expr_reason =
|
||||
|expected: &Expected<Type>, index, branch_region| match expected {
|
||||
FromAnnotation(name, arity, ann_source, _typ) => {
|
||||
// NOTE deviation from elm.
|
||||
//
|
||||
// in elm, `_typ` is used, but because we have this `expr_var` too
|
||||
// and need to constrain it, this is what works and gives better error messages
|
||||
FromAnnotation(
|
||||
name.clone(),
|
||||
*arity,
|
||||
AnnotationSource::TypedWhenBranch {
|
||||
index,
|
||||
region: ann_source.region(),
|
||||
},
|
||||
branch_type.clone(),
|
||||
)
|
||||
}
|
||||
|
||||
for (index, when_branch) in branches.iter().enumerate() {
|
||||
let pattern_region =
|
||||
Region::across_all(when_branch.patterns.iter().map(|v| &v.region));
|
||||
_ => ForReason(
|
||||
Reason::WhenBranch { index },
|
||||
branch_type.clone(),
|
||||
branch_region,
|
||||
),
|
||||
};
|
||||
|
||||
let branch_con = constrain_when_branch(
|
||||
constraints,
|
||||
env,
|
||||
// Our goal is to constrain and introduce variables in all pattern when branch patterns before
|
||||
// looking at their bodies.
|
||||
//
|
||||
// pat1 -> body1
|
||||
// *^^^ +~~~~
|
||||
// pat2 -> body2
|
||||
// *^^^ +~~~~
|
||||
//
|
||||
// * solve first
|
||||
// + solve second
|
||||
//
|
||||
// For a single pattern/body pair, we must introduce variables and symbols defined in the
|
||||
// pattern before solving the body, since those definitions are effectively let-bound.
|
||||
//
|
||||
// But also, we'd like to solve all branch pattern constraints in one swoop before looking at
|
||||
// the bodies, because the patterns may have presence constraints that expect to be built up
|
||||
// together.
|
||||
//
|
||||
// For this reason, we distinguish the two - and introduce variables in the branch patterns
|
||||
// as part of the pattern constraint, solving all of those at once, and then solving the body
|
||||
// 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 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 (new_pattern_vars, new_pattern_headers, pattern_con, branch_con) =
|
||||
constrain_when_branch_help(
|
||||
constraints,
|
||||
env,
|
||||
region,
|
||||
when_branch,
|
||||
PExpected::ForReason(
|
||||
PReason::WhenMatch {
|
||||
index: HumanIndex::zero_based(index),
|
||||
},
|
||||
cond_type.clone(),
|
||||
pattern_region,
|
||||
),
|
||||
branch_expr_reason(
|
||||
&expected,
|
||||
HumanIndex::zero_based(index),
|
||||
when_branch.value.region,
|
||||
when_branch,
|
||||
PExpected::ForReason(
|
||||
PReason::WhenMatch {
|
||||
index: HumanIndex::zero_based(index),
|
||||
},
|
||||
cond_type.clone(),
|
||||
pattern_region,
|
||||
),
|
||||
FromAnnotation(
|
||||
name.clone(),
|
||||
*arity,
|
||||
AnnotationSource::TypedWhenBranch {
|
||||
index: HumanIndex::zero_based(index),
|
||||
region: ann_source.region(),
|
||||
},
|
||||
typ.clone(),
|
||||
),
|
||||
);
|
||||
),
|
||||
);
|
||||
|
||||
branch_constraints.push(branch_con);
|
||||
}
|
||||
pattern_vars.extend(new_pattern_vars);
|
||||
debug_assert!(
|
||||
pattern_headers
|
||||
.clone()
|
||||
.intersection(new_pattern_headers.clone())
|
||||
.is_empty(),
|
||||
"Two patterns introduce the same symbols - that's a bug!"
|
||||
);
|
||||
pattern_headers.extend(new_pattern_headers);
|
||||
pattern_cons.push(pattern_con);
|
||||
|
||||
branch_constraints.push(constraints.equal_types_var(
|
||||
*expr_var,
|
||||
expected,
|
||||
Category::When,
|
||||
region,
|
||||
));
|
||||
|
||||
return constraints.exists_many([cond_var, *expr_var], branch_constraints);
|
||||
}
|
||||
|
||||
_ => {
|
||||
let branch_var = *expr_var;
|
||||
let branch_type = Variable(branch_var);
|
||||
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 branch_con = constrain_when_branch(
|
||||
constraints,
|
||||
env,
|
||||
region,
|
||||
when_branch,
|
||||
PExpected::ForReason(
|
||||
PReason::WhenMatch {
|
||||
index: HumanIndex::zero_based(index),
|
||||
},
|
||||
cond_type.clone(),
|
||||
pattern_region,
|
||||
),
|
||||
ForReason(
|
||||
Reason::WhenBranch {
|
||||
index: HumanIndex::zero_based(index),
|
||||
},
|
||||
branch_type.clone(),
|
||||
when_branch.value.region,
|
||||
),
|
||||
);
|
||||
|
||||
branch_cons.push(branch_con);
|
||||
}
|
||||
|
||||
// Deviation: elm adds another layer of And nesting
|
||||
//
|
||||
// Record the original conditional expression's constraint.
|
||||
// Each branch's pattern must have the same type
|
||||
// as the condition expression did.
|
||||
//
|
||||
// The return type of each branch must equal the return type of
|
||||
// the entire when-expression.
|
||||
branch_cons.push(constraints.equal_types_var(
|
||||
branch_var,
|
||||
expected,
|
||||
Category::When,
|
||||
region,
|
||||
));
|
||||
branch_constraints.push(constraints.and_constraint(branch_cons));
|
||||
}
|
||||
branch_cons.push(branch_con);
|
||||
}
|
||||
|
||||
// Deviation: elm adds another layer of And nesting
|
||||
//
|
||||
// Record the original conditional expression's constraint.
|
||||
// Each branch's pattern must have the same type
|
||||
// as the condition expression did.
|
||||
//
|
||||
// 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);
|
||||
|
||||
// Solve all the pattern constraints together, introducing variables in the pattern as
|
||||
// need be before solving the bodies.
|
||||
let pattern_constraints = constraints.and_constraint(pattern_cons);
|
||||
let body_constraints = constraints.and_constraint(branch_cons);
|
||||
let when_body_con = constraints.let_constraint(
|
||||
[],
|
||||
pattern_vars,
|
||||
pattern_headers,
|
||||
pattern_constraints,
|
||||
body_constraints,
|
||||
);
|
||||
total_cons.push(when_body_con);
|
||||
|
||||
total_cons.push(constraints.equal_types_var(
|
||||
branch_var,
|
||||
expected,
|
||||
Category::When,
|
||||
region,
|
||||
));
|
||||
|
||||
let branch_constraints = constraints.and_constraint(total_cons);
|
||||
|
||||
// exhautiveness checking happens when converting to mono::Expr
|
||||
constraints.exists_many([cond_var, *expr_var], branch_constraints)
|
||||
// ...for now
|
||||
constraints.exists([cond_var, *expr_var], branch_constraints)
|
||||
}
|
||||
Access {
|
||||
record_var,
|
||||
@ -1087,15 +1114,22 @@ pub fn constrain_expr(
|
||||
}
|
||||
}
|
||||
|
||||
/// Constrain a when branch, returning (variables in pattern, symbols introduced in pattern, pattern constraint, body constraint).
|
||||
/// We want to constraint all pattern constraints in a "when" before body constraints.
|
||||
#[inline(always)]
|
||||
fn constrain_when_branch(
|
||||
fn constrain_when_branch_help(
|
||||
constraints: &mut Constraints,
|
||||
env: &Env,
|
||||
region: Region,
|
||||
when_branch: &WhenBranch,
|
||||
pattern_expected: PExpected<Type>,
|
||||
expr_expected: Expected<Type>,
|
||||
) -> Constraint {
|
||||
) -> (
|
||||
Vec<Variable>,
|
||||
SendMap<Symbol, Loc<Type>>,
|
||||
Constraint,
|
||||
Constraint,
|
||||
) {
|
||||
let ret_constraint = constrain_expr(
|
||||
constraints,
|
||||
env,
|
||||
@ -1123,7 +1157,7 @@ fn constrain_when_branch(
|
||||
);
|
||||
}
|
||||
|
||||
if let Some(loc_guard) = &when_branch.guard {
|
||||
let (pattern_constraints, body_constraints) = if let Some(loc_guard) = &when_branch.guard {
|
||||
let guard_constraint = constrain_expr(
|
||||
constraints,
|
||||
env,
|
||||
@ -1140,17 +1174,18 @@ fn constrain_when_branch(
|
||||
let state_constraints = constraints.and_constraint(state.constraints);
|
||||
let inner = constraints.let_constraint([], [], [], guard_constraint, ret_constraint);
|
||||
|
||||
constraints.let_constraint([], state.vars, state.headers, state_constraints, inner)
|
||||
(state_constraints, inner)
|
||||
} else {
|
||||
let state_constraints = constraints.and_constraint(state.constraints);
|
||||
constraints.let_constraint(
|
||||
[],
|
||||
state.vars,
|
||||
state.headers,
|
||||
state_constraints,
|
||||
ret_constraint,
|
||||
)
|
||||
}
|
||||
(state_constraints, ret_constraint)
|
||||
};
|
||||
|
||||
(
|
||||
state.vars,
|
||||
state.headers,
|
||||
pattern_constraints,
|
||||
body_constraints,
|
||||
)
|
||||
}
|
||||
|
||||
fn constrain_field(
|
||||
|
@ -2,12 +2,14 @@ use roc_builtins::std::StdLib;
|
||||
use roc_can::abilities::AbilitiesStore;
|
||||
use roc_can::constraint::{Constraint, Constraints};
|
||||
use roc_can::def::Declaration;
|
||||
use roc_can::expected::Expected;
|
||||
use roc_collections::all::MutMap;
|
||||
use roc_error_macros::internal_error;
|
||||
use roc_module::symbol::{ModuleId, Symbol};
|
||||
use roc_region::all::Loc;
|
||||
use roc_region::all::{Loc, Region};
|
||||
use roc_types::solved_types::{FreeVars, SolvedType};
|
||||
use roc_types::subs::{VarStore, Variable};
|
||||
use roc_types::types::{Category, Type};
|
||||
|
||||
/// The types of all exposed values/functions of a collection of modules
|
||||
#[derive(Clone, Debug, Default)]
|
||||
@ -65,17 +67,8 @@ impl ExposedForModule {
|
||||
let mut imported_values = Vec::new();
|
||||
|
||||
for symbol in it {
|
||||
// Today, builtins are not actually imported,
|
||||
// but generated in each module that uses them
|
||||
//
|
||||
// This will change when we write builtins in roc
|
||||
if symbol.is_builtin() {
|
||||
continue;
|
||||
}
|
||||
|
||||
if let Some(ExposedModuleTypes::Valid { .. }) =
|
||||
exposed_by_module.exposed.get(&symbol.module_id())
|
||||
{
|
||||
let module = exposed_by_module.exposed.get(&symbol.module_id());
|
||||
if let Some(ExposedModuleTypes::Valid { .. }) = module {
|
||||
imported_values.push(*symbol);
|
||||
} else {
|
||||
continue;
|
||||
@ -105,27 +98,53 @@ pub fn constrain_module(
|
||||
declarations: &[Declaration],
|
||||
home: ModuleId,
|
||||
) -> Constraint {
|
||||
let mut constraint = crate::expr::constrain_decls(constraints, home, declarations);
|
||||
let constraint = crate::expr::constrain_decls(constraints, home, declarations);
|
||||
|
||||
let constraint = frontload_ability_constraints(constraints, abilities_store, constraint);
|
||||
|
||||
// The module constraint should always save the environment at the end.
|
||||
debug_assert!(constraints.contains_save_the_environment(&constraint));
|
||||
|
||||
constraint
|
||||
}
|
||||
|
||||
pub fn frontload_ability_constraints(
|
||||
constraints: &mut Constraints,
|
||||
abilities_store: &AbilitiesStore,
|
||||
mut constraint: Constraint,
|
||||
) -> Constraint {
|
||||
for (member_name, member_data) in abilities_store.root_ability_members().iter() {
|
||||
// 1. Attach the type of member signature to the reserved signature_var. This is
|
||||
// infallible.
|
||||
let unify_with_signature_var = constraints.equal_types_var(
|
||||
member_data.signature_var,
|
||||
Expected::NoExpectation(member_data.signature.clone()),
|
||||
Category::Storage(std::file!(), std::column!()),
|
||||
Region::zero(),
|
||||
);
|
||||
|
||||
// 2. Store the member signature on the member symbol. This makes sure we generalize it on
|
||||
// the toplevel, as appropriate.
|
||||
let vars = &member_data.variables;
|
||||
let rigids = (vars.rigid_vars.iter())
|
||||
// For our purposes, in the let constraint, able vars are treated like rigids.
|
||||
.chain(vars.able_vars.iter())
|
||||
.copied();
|
||||
let flex = vars.flex_vars.iter().copied();
|
||||
constraint = constraints.let_constraint(
|
||||
|
||||
let let_constr = constraints.let_constraint(
|
||||
rigids,
|
||||
flex,
|
||||
[(*member_name, Loc::at_zero(member_data.signature.clone()))],
|
||||
[(
|
||||
*member_name,
|
||||
Loc::at_zero(Type::Variable(member_data.signature_var)),
|
||||
)],
|
||||
Constraint::True,
|
||||
constraint,
|
||||
);
|
||||
|
||||
constraint = constraints.and_constraint([unify_with_signature_var, let_constr]);
|
||||
}
|
||||
|
||||
// The module constraint should always save the environment at the end.
|
||||
debug_assert!(constraints.contains_save_the_environment(&constraint));
|
||||
|
||||
constraint
|
||||
}
|
||||
|
||||
@ -170,17 +189,6 @@ pub fn constrain_builtin_imports(
|
||||
}
|
||||
}
|
||||
None => {
|
||||
let is_valid_alias = stdlib.applies.contains(&symbol)
|
||||
// This wasn't a builtin value or Apply; maybe it was a builtin alias.
|
||||
|| roc_types::builtin_aliases::aliases().contains_key(&symbol);
|
||||
|
||||
if !is_valid_alias {
|
||||
panic!(
|
||||
"Could not find {:?} in builtin types {:?} or builtin aliases",
|
||||
symbol, stdlib.types,
|
||||
);
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
};
|
||||
|
@ -6070,6 +6070,13 @@ fn run_low_level<'a, 'ctx, 'env>(
|
||||
let key_layout = list_element_layout!(list_layout);
|
||||
set_from_list(env, layout_ids, list, key_layout)
|
||||
}
|
||||
SetToDict => {
|
||||
debug_assert_eq!(args.len(), 1);
|
||||
|
||||
let (set, _set_layout) = load_symbol_and_layout(scope, &args[0]);
|
||||
|
||||
set
|
||||
}
|
||||
ExpectTrue => {
|
||||
debug_assert_eq!(args.len(), 1);
|
||||
|
||||
|
@ -7,7 +7,7 @@ use roc_collections::all::MutMap;
|
||||
use roc_module::ident::Ident;
|
||||
use roc_module::low_level::{LowLevel, LowLevelWrapperType};
|
||||
use roc_module::symbol::{Interns, Symbol};
|
||||
use roc_mono::code_gen_help::{CodeGenHelp, REFCOUNT_MAX};
|
||||
use roc_mono::code_gen_help::{CodeGenHelp, HelperOp, REFCOUNT_MAX};
|
||||
use roc_mono::ir::{
|
||||
BranchInfo, CallType, Expr, JoinPointId, ListLiteralElement, Literal, ModifyRc, Param, Proc,
|
||||
ProcLayout, Stmt,
|
||||
@ -270,6 +270,13 @@ impl<'a> WasmBackend<'a> {
|
||||
self.storage.stack_frame_size,
|
||||
self.storage.stack_frame_pointer,
|
||||
);
|
||||
|
||||
if DEBUG_LOG_SETTINGS.storage_map {
|
||||
println!("\nStorage:");
|
||||
for (sym, storage) in self.storage.symbol_storage_map.iter() {
|
||||
println!("{:?} => {:?}", sym, storage);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn append_proc_debug_name(&mut self, sym: Symbol) {
|
||||
@ -1609,8 +1616,9 @@ impl<'a> WasmBackend<'a> {
|
||||
);
|
||||
}
|
||||
|
||||
/// Generate a refcount increment procedure and return its Wasm function index
|
||||
pub fn gen_refcount_inc_for_zig(&mut self, layout: Layout<'a>) -> u32 {
|
||||
/// Generate a refcount helper procedure and return a pointer (table index) to it
|
||||
/// This allows it to be indirectly called from Zig code
|
||||
pub fn get_refcount_fn_ptr(&mut self, layout: Layout<'a>, op: HelperOp) -> i32 {
|
||||
let ident_ids = self
|
||||
.interns
|
||||
.all_ident_ids
|
||||
@ -1619,7 +1627,7 @@ impl<'a> WasmBackend<'a> {
|
||||
|
||||
let (proc_symbol, new_specializations) = self
|
||||
.helper_proc_gen
|
||||
.gen_refcount_inc_proc(ident_ids, layout);
|
||||
.gen_refcount_proc(ident_ids, layout, op);
|
||||
|
||||
// If any new specializations were created, register their symbol data
|
||||
for (spec_sym, spec_layout) in new_specializations.into_iter() {
|
||||
@ -1632,6 +1640,7 @@ impl<'a> WasmBackend<'a> {
|
||||
.position(|lookup| lookup.name == proc_symbol && lookup.layout.arguments[0] == layout)
|
||||
.unwrap();
|
||||
|
||||
self.fn_index_offset + proc_index as u32
|
||||
let wasm_fn_index = self.fn_index_offset + proc_index as u32;
|
||||
self.get_fn_table_index(wasm_fn_index)
|
||||
}
|
||||
}
|
||||
|
@ -253,6 +253,7 @@ pub struct WasmDebugLogSettings {
|
||||
helper_procs_ir: bool,
|
||||
let_stmt_ir: bool,
|
||||
instructions: bool,
|
||||
storage_map: bool,
|
||||
pub keep_test_binary: bool,
|
||||
}
|
||||
|
||||
@ -262,5 +263,6 @@ pub const DEBUG_LOG_SETTINGS: WasmDebugLogSettings = WasmDebugLogSettings {
|
||||
helper_procs_ir: false && cfg!(debug_assertions),
|
||||
let_stmt_ir: false && cfg!(debug_assertions),
|
||||
instructions: false && cfg!(debug_assertions),
|
||||
storage_map: false && cfg!(debug_assertions),
|
||||
keep_test_binary: false && cfg!(debug_assertions),
|
||||
};
|
||||
|
@ -3,6 +3,7 @@ use roc_builtins::bitcode::{self, FloatWidth, IntWidth};
|
||||
use roc_error_macros::internal_error;
|
||||
use roc_module::low_level::LowLevel;
|
||||
use roc_module::symbol::Symbol;
|
||||
use roc_mono::code_gen_help::HelperOp;
|
||||
use roc_mono::ir::{HigherOrderLowLevel, PassedFunction, ProcLayout};
|
||||
use roc_mono::layout::{Builtin, Layout, UnionLayout};
|
||||
use roc_mono::low_level::HigherOrder;
|
||||
@ -283,7 +284,7 @@ impl<'a> LowLevelCall<'a> {
|
||||
|
||||
DictSize | DictEmpty | DictInsert | DictRemove | DictContains | DictGetUnsafe
|
||||
| DictKeys | DictValues | DictUnion | DictIntersection | DictDifference
|
||||
| SetFromList => {
|
||||
| SetFromList | SetToDict => {
|
||||
todo!("{:?}", self.lowlevel);
|
||||
}
|
||||
|
||||
@ -1014,58 +1015,73 @@ pub fn call_higher_order_lowlevel<'a>(
|
||||
};
|
||||
|
||||
let wrapper_fn_idx = backend.register_helper_proc(wrapper_sym, wrapper_layout, source);
|
||||
let inc_fn_idx = backend.gen_refcount_inc_for_zig(closure_data_layout);
|
||||
|
||||
let wrapper_fn_ptr = backend.get_fn_table_index(wrapper_fn_idx);
|
||||
let inc_fn_ptr = backend.get_fn_table_index(inc_fn_idx);
|
||||
let inc_fn_ptr = match closure_data_layout {
|
||||
Layout::Struct {
|
||||
field_layouts: &[], ..
|
||||
} => {
|
||||
// Our code gen would ignore the Unit arg, but the Zig builtin passes a pointer for it!
|
||||
// That results in an exception (type signature mismatch in indirect call).
|
||||
// The workaround is to use I32 layout, treating the (ignored) pointer as an integer.
|
||||
backend.get_refcount_fn_ptr(Layout::Builtin(Builtin::Int(IntWidth::I32)), HelperOp::Inc)
|
||||
}
|
||||
_ => backend.get_refcount_fn_ptr(closure_data_layout, HelperOp::Inc),
|
||||
};
|
||||
|
||||
match op {
|
||||
// List.map : List elem_x, (elem_x -> elem_ret) -> List elem_ret
|
||||
ListMap { xs } => {
|
||||
let list_layout_in = backend.storage.symbol_layouts[xs];
|
||||
ListMap { xs } => list_map_n(
|
||||
bitcode::LIST_MAP,
|
||||
backend,
|
||||
&[*xs],
|
||||
return_sym,
|
||||
*return_layout,
|
||||
wrapper_fn_ptr,
|
||||
inc_fn_ptr,
|
||||
closure_data_exists,
|
||||
*captured_environment,
|
||||
*owns_captured_environment,
|
||||
),
|
||||
|
||||
let (elem_x, elem_ret) = match (list_layout_in, return_layout) {
|
||||
(
|
||||
Layout::Builtin(Builtin::List(elem_x)),
|
||||
Layout::Builtin(Builtin::List(elem_ret)),
|
||||
) => (elem_x, elem_ret),
|
||||
_ => unreachable!("invalid layout for List.map arguments"),
|
||||
};
|
||||
let elem_x_size = elem_x.stack_size(TARGET_INFO);
|
||||
let (elem_ret_size, elem_ret_align) = elem_ret.stack_size_and_alignment(TARGET_INFO);
|
||||
ListMap2 { xs, ys } => list_map_n(
|
||||
bitcode::LIST_MAP2,
|
||||
backend,
|
||||
&[*xs, *ys],
|
||||
return_sym,
|
||||
*return_layout,
|
||||
wrapper_fn_ptr,
|
||||
inc_fn_ptr,
|
||||
closure_data_exists,
|
||||
*captured_environment,
|
||||
*owns_captured_environment,
|
||||
),
|
||||
|
||||
let cb = &mut backend.code_builder;
|
||||
ListMap3 { xs, ys, zs } => list_map_n(
|
||||
bitcode::LIST_MAP3,
|
||||
backend,
|
||||
&[*xs, *ys, *zs],
|
||||
return_sym,
|
||||
*return_layout,
|
||||
wrapper_fn_ptr,
|
||||
inc_fn_ptr,
|
||||
closure_data_exists,
|
||||
*captured_environment,
|
||||
*owns_captured_environment,
|
||||
),
|
||||
|
||||
// Load return pointer & argument values
|
||||
// Wasm signature: (i32, i64, i64, i32, i32, i32, i32, i32, i32, i32) -> nil
|
||||
backend.storage.load_symbols(cb, &[return_sym]);
|
||||
backend.storage.load_symbol_zig(cb, *xs); // list with capacity = 2 x i64 args
|
||||
cb.i32_const(wrapper_fn_ptr);
|
||||
if closure_data_exists {
|
||||
backend.storage.load_symbols(cb, &[*captured_environment]);
|
||||
} else {
|
||||
// Normally, a zero-size arg would be eliminated in code gen, but Zig expects one!
|
||||
cb.i32_const(0); // null pointer
|
||||
}
|
||||
cb.i32_const(inc_fn_ptr);
|
||||
cb.i32_const(*owns_captured_environment as i32);
|
||||
cb.i32_const(elem_ret_align as i32); // used for allocating the new list
|
||||
cb.i32_const(elem_x_size as i32);
|
||||
cb.i32_const(elem_ret_size as i32);
|
||||
ListMap4 { xs, ys, zs, ws } => list_map_n(
|
||||
bitcode::LIST_MAP4,
|
||||
backend,
|
||||
&[*xs, *ys, *zs, *ws],
|
||||
return_sym,
|
||||
*return_layout,
|
||||
wrapper_fn_ptr,
|
||||
inc_fn_ptr,
|
||||
closure_data_exists,
|
||||
*captured_environment,
|
||||
*owns_captured_environment,
|
||||
),
|
||||
|
||||
let num_wasm_args = 10; // 1 return pointer + 8 Zig args + list 2nd i64
|
||||
let has_return_val = false;
|
||||
backend.call_zig_builtin_after_loading_args(
|
||||
bitcode::LIST_MAP,
|
||||
num_wasm_args,
|
||||
has_return_val,
|
||||
);
|
||||
}
|
||||
|
||||
ListMap2 { .. }
|
||||
| ListMap3 { .. }
|
||||
| ListMap4 { .. }
|
||||
| ListMapWithIndex { .. }
|
||||
ListMapWithIndex { .. }
|
||||
| ListKeepIf { .. }
|
||||
| ListWalk { .. }
|
||||
| ListWalkUntil { .. }
|
||||
@ -1079,3 +1095,71 @@ pub fn call_higher_order_lowlevel<'a>(
|
||||
| DictWalk { .. } => todo!("{:?}", op),
|
||||
}
|
||||
}
|
||||
|
||||
fn unwrap_list_elem_layout(list_layout: Layout<'_>) -> &Layout<'_> {
|
||||
match list_layout {
|
||||
Layout::Builtin(Builtin::List(x)) => x,
|
||||
e => internal_error!("expected List layout, got {:?}", e),
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
fn list_map_n<'a>(
|
||||
zig_fn_name: &'static str,
|
||||
backend: &mut WasmBackend<'a>,
|
||||
arg_symbols: &[Symbol],
|
||||
return_sym: Symbol,
|
||||
return_layout: Layout<'a>,
|
||||
wrapper_fn_ptr: i32,
|
||||
inc_fn_ptr: i32,
|
||||
closure_data_exists: bool,
|
||||
captured_environment: Symbol,
|
||||
owns_captured_environment: bool,
|
||||
) {
|
||||
let arg_elem_layouts = Vec::from_iter_in(
|
||||
arg_symbols
|
||||
.iter()
|
||||
.map(|sym| *unwrap_list_elem_layout(backend.storage.symbol_layouts[sym])),
|
||||
backend.env.arena,
|
||||
);
|
||||
|
||||
let elem_ret = unwrap_list_elem_layout(return_layout);
|
||||
let (elem_ret_size, elem_ret_align) = elem_ret.stack_size_and_alignment(TARGET_INFO);
|
||||
|
||||
let cb = &mut backend.code_builder;
|
||||
|
||||
backend.storage.load_symbols(cb, &[return_sym]);
|
||||
|
||||
for s in arg_symbols {
|
||||
backend.storage.load_symbol_zig(cb, *s);
|
||||
}
|
||||
cb.i32_const(wrapper_fn_ptr);
|
||||
if closure_data_exists {
|
||||
backend.storage.load_symbols(cb, &[captured_environment]);
|
||||
} else {
|
||||
// load_symbols assumes that a zero-size arg should be eliminated in code gen,
|
||||
// but that's a specialization that our Zig code doesn't have! Pass a null pointer.
|
||||
cb.i32_const(0);
|
||||
}
|
||||
cb.i32_const(inc_fn_ptr);
|
||||
cb.i32_const(owns_captured_environment as i32);
|
||||
cb.i32_const(elem_ret_align as i32);
|
||||
for el in arg_elem_layouts.iter() {
|
||||
cb.i32_const(el.stack_size(TARGET_INFO) as i32);
|
||||
}
|
||||
cb.i32_const(elem_ret_size as i32);
|
||||
|
||||
// If we have lists of different lengths, we may need to decrement
|
||||
let num_wasm_args = if arg_elem_layouts.len() > 1 {
|
||||
for el in arg_elem_layouts.iter() {
|
||||
let ptr = backend.get_refcount_fn_ptr(*el, HelperOp::Dec);
|
||||
backend.code_builder.i32_const(ptr);
|
||||
}
|
||||
7 + arg_elem_layouts.len() * 4
|
||||
} else {
|
||||
7 + arg_elem_layouts.len() * 3
|
||||
};
|
||||
|
||||
let has_return_val = false;
|
||||
backend.call_zig_builtin_after_loading_args(zig_fn_name, num_wasm_args, has_return_val);
|
||||
}
|
||||
|
@ -62,7 +62,7 @@ struct VmBlock<'a> {
|
||||
|
||||
impl std::fmt::Debug for VmBlock<'_> {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
f.write_fmt(format_args!("{:?}", self.opcode))
|
||||
f.write_fmt(format_args!("{:?} {:?}", self.opcode, self.value_stack))
|
||||
}
|
||||
}
|
||||
|
||||
@ -608,7 +608,7 @@ impl<'a> CodeBuilder<'a> {
|
||||
log_instruction!(
|
||||
"{:10}\t\t{:?}",
|
||||
format!("{:?}", opcode),
|
||||
self.current_stack()
|
||||
self.vm_block_stack
|
||||
);
|
||||
}
|
||||
|
||||
@ -635,7 +635,7 @@ impl<'a> CodeBuilder<'a> {
|
||||
"{:10}\t{}\t{:?}",
|
||||
format!("{:?}", opcode),
|
||||
immediate,
|
||||
self.current_stack()
|
||||
self.vm_block_stack
|
||||
);
|
||||
}
|
||||
|
||||
@ -648,7 +648,7 @@ impl<'a> CodeBuilder<'a> {
|
||||
format!("{:?}", opcode),
|
||||
align,
|
||||
offset,
|
||||
self.current_stack()
|
||||
self.vm_block_stack
|
||||
);
|
||||
}
|
||||
|
||||
@ -752,7 +752,7 @@ impl<'a> CodeBuilder<'a> {
|
||||
"{:10}\t{}\t{:?}",
|
||||
format!("{:?}", CALL),
|
||||
function_index,
|
||||
self.current_stack()
|
||||
self.vm_block_stack
|
||||
);
|
||||
}
|
||||
|
||||
@ -823,7 +823,7 @@ impl<'a> CodeBuilder<'a> {
|
||||
"{:10}\t{}\t{:?}",
|
||||
format!("{:?}", opcode),
|
||||
x,
|
||||
self.current_stack()
|
||||
self.vm_block_stack
|
||||
);
|
||||
}
|
||||
pub fn i32_const(&mut self, x: i32) {
|
||||
|
@ -30,7 +30,8 @@ impl IdentStr {
|
||||
// Reserve 1 byte for the discriminant
|
||||
const SMALL_STR_BYTES: usize = std::mem::size_of::<Self>() - 1;
|
||||
|
||||
pub fn len(&self) -> usize {
|
||||
#[inline(always)]
|
||||
pub const fn len(&self) -> usize {
|
||||
let bytes = self.length.to_ne_bytes();
|
||||
let last_byte = bytes[mem::size_of::<usize>() - 1];
|
||||
|
||||
@ -55,11 +56,11 @@ impl IdentStr {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn is_empty(&self) -> bool {
|
||||
pub const fn is_empty(&self) -> bool {
|
||||
self.length == 0
|
||||
}
|
||||
|
||||
pub fn is_small_str(&self) -> bool {
|
||||
pub const fn is_small_str(&self) -> bool {
|
||||
let bytes = self.length.to_ne_bytes();
|
||||
let last_byte = bytes[mem::size_of::<usize>() - 1];
|
||||
|
||||
|
@ -5,13 +5,13 @@ use roc_module::symbol::ModuleId;
|
||||
|
||||
const MODULES: &[(ModuleId, &str)] = &[
|
||||
(ModuleId::BOOL, "Bool.roc"),
|
||||
// (ModuleId::RESULT, "Result.roc"),
|
||||
// (ModuleId::LIST, "List.roc"),
|
||||
// (ModuleId::STR, "Str.roc"),
|
||||
// (ModuleId::DICT, "Dict.roc"),
|
||||
// (ModuleId::SET, "Set.roc"),
|
||||
// (ModuleId::BOX, "Box.roc"),
|
||||
// (ModuleId::NUM, "Num.roc"),
|
||||
(ModuleId::RESULT, "Result.roc"),
|
||||
(ModuleId::NUM, "Num.roc"),
|
||||
(ModuleId::LIST, "List.roc"),
|
||||
(ModuleId::STR, "Str.roc"),
|
||||
(ModuleId::DICT, "Dict.roc"),
|
||||
(ModuleId::SET, "Set.roc"),
|
||||
(ModuleId::BOX, "Box.roc"),
|
||||
];
|
||||
|
||||
fn main() {
|
||||
|
@ -35,6 +35,30 @@ fn load<'a>(
|
||||
)
|
||||
}
|
||||
|
||||
/// Load using only a single thread; used when compiling to webassembly
|
||||
pub fn load_single_threaded<'a>(
|
||||
arena: &'a Bump,
|
||||
load_start: LoadStart<'a>,
|
||||
src_dir: &Path,
|
||||
exposed_types: ExposedByModule,
|
||||
goal_phase: Phase,
|
||||
target_info: TargetInfo,
|
||||
render: RenderTarget,
|
||||
) -> Result<LoadResult<'a>, LoadingProblem<'a>> {
|
||||
let cached_subs = read_cached_subs();
|
||||
|
||||
roc_load_internal::file::load_single_threaded(
|
||||
arena,
|
||||
load_start,
|
||||
src_dir,
|
||||
exposed_types,
|
||||
goal_phase,
|
||||
target_info,
|
||||
cached_subs,
|
||||
render,
|
||||
)
|
||||
}
|
||||
|
||||
pub fn load_and_monomorphize_from_str<'a>(
|
||||
arena: &'a Bump,
|
||||
filename: PathBuf,
|
||||
@ -114,14 +138,44 @@ pub fn load_and_typecheck<'a>(
|
||||
}
|
||||
}
|
||||
|
||||
pub fn load_and_typecheck_str<'a>(
|
||||
arena: &'a Bump,
|
||||
filename: PathBuf,
|
||||
source: &'a str,
|
||||
src_dir: &Path,
|
||||
exposed_types: ExposedByModule,
|
||||
target_info: TargetInfo,
|
||||
render: RenderTarget,
|
||||
) -> Result<LoadedModule, LoadingProblem<'a>> {
|
||||
use LoadResult::*;
|
||||
|
||||
let load_start = LoadStart::from_str(arena, filename, source)?;
|
||||
|
||||
// NOTE: this function is meant for tests, and so we use single-threaded
|
||||
// solving so we don't use too many threads per-test. That gives higher
|
||||
// throughput for the test run overall
|
||||
match load_single_threaded(
|
||||
arena,
|
||||
load_start,
|
||||
src_dir,
|
||||
exposed_types,
|
||||
Phase::SolveTypes,
|
||||
target_info,
|
||||
render,
|
||||
)? {
|
||||
Monomorphized(_) => unreachable!(""),
|
||||
TypeChecked(module) => Ok(module),
|
||||
}
|
||||
}
|
||||
|
||||
const BOOL: &[u8] = include_bytes!(concat!(env!("OUT_DIR"), "/Bool.dat")) as &[_];
|
||||
// const RESULT: &[u8] = include_bytes!(concat!(env!("OUT_DIR"), "/Result.dat")) as &[_];
|
||||
// const LIST: &[u8] = include_bytes!(concat!(env!("OUT_DIR"), "/List.dat")) as &[_];
|
||||
// const STR: &[u8] = include_bytes!(concat!(env!("OUT_DIR"), "/Str.dat")) as &[_];
|
||||
// const DICT: &[u8] = include_bytes!(concat!(env!("OUT_DIR"), "/Dict.dat")) as &[_];
|
||||
// const SET: &[u8] = include_bytes!(concat!(env!("OUT_DIR"), "/Set.dat")) as &[_];
|
||||
// const BOX: &[u8] = include_bytes!(concat!(env!("OUT_DIR"), "/Box.dat")) as &[_];
|
||||
// const NUM: &[u8] = include_bytes!(concat!(env!("OUT_DIR"), "/Num.dat")) as &[_];
|
||||
const RESULT: &[u8] = include_bytes!(concat!(env!("OUT_DIR"), "/Result.dat")) as &[_];
|
||||
const LIST: &[u8] = include_bytes!(concat!(env!("OUT_DIR"), "/List.dat")) as &[_];
|
||||
const STR: &[u8] = include_bytes!(concat!(env!("OUT_DIR"), "/Str.dat")) as &[_];
|
||||
const DICT: &[u8] = include_bytes!(concat!(env!("OUT_DIR"), "/Dict.dat")) as &[_];
|
||||
const SET: &[u8] = include_bytes!(concat!(env!("OUT_DIR"), "/Set.dat")) as &[_];
|
||||
const BOX: &[u8] = include_bytes!(concat!(env!("OUT_DIR"), "/Box.dat")) as &[_];
|
||||
const NUM: &[u8] = include_bytes!(concat!(env!("OUT_DIR"), "/Num.dat")) as &[_];
|
||||
|
||||
fn deserialize_help(bytes: &[u8]) -> (Subs, Vec<(Symbol, Variable)>) {
|
||||
let (subs, slice) = Subs::deserialize(bytes);
|
||||
@ -132,14 +186,20 @@ fn deserialize_help(bytes: &[u8]) -> (Subs, Vec<(Symbol, Variable)>) {
|
||||
fn read_cached_subs() -> MutMap<ModuleId, (Subs, Vec<(Symbol, Variable)>)> {
|
||||
let mut output = MutMap::default();
|
||||
|
||||
output.insert(ModuleId::BOOL, deserialize_help(BOOL));
|
||||
// output.insert(ModuleId::RESULT, deserialize_help(RESULT));
|
||||
// output.insert(ModuleId::LIST, deserialize_help(LIST));
|
||||
// output.insert(ModuleId::STR, deserialize_help(STR));
|
||||
// output.insert(ModuleId::DICT, deserialize_help(DICT));
|
||||
// output.insert(ModuleId::SET, deserialize_help(SET));
|
||||
// output.insert(ModuleId::BOX, deserialize_help(BOX));
|
||||
// output.insert(ModuleId::NUM, deserialize_help(NUM));
|
||||
// Wasm seems to re-order definitions between build time and runtime, but only in release mode.
|
||||
// That is very strange, but we can solve it separately
|
||||
if !cfg!(target_family = "wasm") {
|
||||
output.insert(ModuleId::BOOL, deserialize_help(BOOL));
|
||||
output.insert(ModuleId::RESULT, deserialize_help(RESULT));
|
||||
output.insert(ModuleId::NUM, deserialize_help(NUM));
|
||||
|
||||
output.insert(ModuleId::LIST, deserialize_help(LIST));
|
||||
output.insert(ModuleId::STR, deserialize_help(STR));
|
||||
output.insert(ModuleId::DICT, deserialize_help(DICT));
|
||||
|
||||
output.insert(ModuleId::SET, deserialize_help(SET));
|
||||
output.insert(ModuleId::BOX, deserialize_help(BOX));
|
||||
}
|
||||
|
||||
output
|
||||
}
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -90,6 +90,7 @@ impl ModuleName {
|
||||
pub const DICT: &'static str = "Dict";
|
||||
pub const SET: &'static str = "Set";
|
||||
pub const RESULT: &'static str = "Result";
|
||||
pub const BOX: &'static str = "Box";
|
||||
|
||||
pub fn as_str(&self) -> &str {
|
||||
self.0.as_str()
|
||||
|
@ -66,6 +66,7 @@ pub enum LowLevel {
|
||||
DictDifference,
|
||||
DictWalk,
|
||||
SetFromList,
|
||||
SetToDict,
|
||||
NumAdd,
|
||||
NumAddWrap,
|
||||
NumAddChecked,
|
||||
@ -243,22 +244,22 @@ impl LowLevelWrapperType {
|
||||
Symbol::LIST_JOIN => CanBeReplacedBy(ListJoin),
|
||||
Symbol::LIST_RANGE => CanBeReplacedBy(ListRange),
|
||||
Symbol::LIST_MAP => WrapperIsRequired,
|
||||
Symbol::LIST_MAP2 => CanBeReplacedBy(ListMap2),
|
||||
Symbol::LIST_MAP3 => CanBeReplacedBy(ListMap3),
|
||||
Symbol::LIST_MAP4 => CanBeReplacedBy(ListMap4),
|
||||
Symbol::LIST_MAP_WITH_INDEX => CanBeReplacedBy(ListMapWithIndex),
|
||||
Symbol::LIST_KEEP_IF => CanBeReplacedBy(ListKeepIf),
|
||||
Symbol::LIST_WALK => CanBeReplacedBy(ListWalk),
|
||||
Symbol::LIST_WALK_UNTIL => CanBeReplacedBy(ListWalkUntil),
|
||||
Symbol::LIST_WALK_BACKWARDS => CanBeReplacedBy(ListWalkBackwards),
|
||||
Symbol::LIST_KEEP_OKS => CanBeReplacedBy(ListKeepOks),
|
||||
Symbol::LIST_KEEP_ERRS => CanBeReplacedBy(ListKeepErrs),
|
||||
Symbol::LIST_SORT_WITH => CanBeReplacedBy(ListSortWith),
|
||||
Symbol::LIST_MAP2 => WrapperIsRequired,
|
||||
Symbol::LIST_MAP3 => WrapperIsRequired,
|
||||
Symbol::LIST_MAP4 => WrapperIsRequired,
|
||||
Symbol::LIST_MAP_WITH_INDEX => WrapperIsRequired,
|
||||
Symbol::LIST_KEEP_IF => WrapperIsRequired,
|
||||
Symbol::LIST_WALK => WrapperIsRequired,
|
||||
Symbol::LIST_WALK_UNTIL => WrapperIsRequired,
|
||||
Symbol::LIST_WALK_BACKWARDS => WrapperIsRequired,
|
||||
Symbol::LIST_KEEP_OKS => WrapperIsRequired,
|
||||
Symbol::LIST_KEEP_ERRS => WrapperIsRequired,
|
||||
Symbol::LIST_SORT_WITH => WrapperIsRequired,
|
||||
Symbol::LIST_SUBLIST => CanBeReplacedBy(ListSublist),
|
||||
Symbol::LIST_DROP_AT => CanBeReplacedBy(ListDropAt),
|
||||
Symbol::LIST_SWAP => CanBeReplacedBy(ListSwap),
|
||||
Symbol::LIST_ANY => CanBeReplacedBy(ListAny),
|
||||
Symbol::LIST_ALL => CanBeReplacedBy(ListAll),
|
||||
Symbol::LIST_ANY => WrapperIsRequired,
|
||||
Symbol::LIST_ALL => WrapperIsRequired,
|
||||
Symbol::LIST_FIND => WrapperIsRequired,
|
||||
Symbol::DICT_LEN => CanBeReplacedBy(DictSize),
|
||||
Symbol::DICT_EMPTY => CanBeReplacedBy(DictEmpty),
|
||||
@ -271,7 +272,7 @@ impl LowLevelWrapperType {
|
||||
Symbol::DICT_UNION => CanBeReplacedBy(DictUnion),
|
||||
Symbol::DICT_INTERSECTION => CanBeReplacedBy(DictIntersection),
|
||||
Symbol::DICT_DIFFERENCE => CanBeReplacedBy(DictDifference),
|
||||
Symbol::DICT_WALK => CanBeReplacedBy(DictWalk),
|
||||
Symbol::DICT_WALK => WrapperIsRequired,
|
||||
Symbol::SET_FROM_LIST => CanBeReplacedBy(SetFromList),
|
||||
Symbol::NUM_ADD => CanBeReplacedBy(NumAdd),
|
||||
Symbol::NUM_ADD_WRAP => CanBeReplacedBy(NumAddWrap),
|
||||
|
@ -906,23 +906,23 @@ define_builtins! {
|
||||
30 DEV_TMP5: "#dev_tmp5"
|
||||
}
|
||||
1 NUM: "Num" => {
|
||||
0 NUM_NUM: "Num" imported // the Num.Num type alias
|
||||
0 NUM_NUM: "Num" // the Num.Num type alias
|
||||
1 NUM_AT_NUM: "@Num" // the Num.@Num private tag
|
||||
2 NUM_I128: "I128" imported // the Num.I128 type alias
|
||||
3 NUM_U128: "U128" imported // the Num.U128 type alias
|
||||
4 NUM_I64: "I64" imported // the Num.I64 type alias
|
||||
5 NUM_U64: "U64" imported // the Num.U64 type alias
|
||||
6 NUM_I32: "I32" imported // the Num.I32 type alias
|
||||
7 NUM_U32: "U32" imported // the Num.U32 type alias
|
||||
8 NUM_I16: "I16" imported // the Num.I16 type alias
|
||||
9 NUM_U16: "U16" imported // the Num.U16 type alias
|
||||
10 NUM_I8: "I8" imported // the Num.I8 type alias
|
||||
11 NUM_U8: "U8" imported // the Num.U8 type alias
|
||||
12 NUM_INTEGER: "Integer" imported // Int : Num Integer
|
||||
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" imported // the Num.F64 type alias
|
||||
15 NUM_F32: "F32" imported // the Num.F32 type alias
|
||||
16 NUM_FLOATINGPOINT: "FloatingPoint" imported // Float : Num FloatingPoint
|
||||
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"
|
||||
@ -948,8 +948,8 @@ define_builtins! {
|
||||
39 NUM_REM_CHECKED: "remChecked"
|
||||
40 NUM_DIV_FLOAT: "div"
|
||||
41 NUM_DIV_FLOAT_CHECKED: "divChecked"
|
||||
42 NUM_DIV_FLOOR: "divFloor"
|
||||
43 NUM_DIV_FLOOR_CHECKED: "divFloorChecked"
|
||||
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"
|
||||
@ -971,29 +971,29 @@ define_builtins! {
|
||||
62 NUM_ACOS: "acos"
|
||||
63 NUM_ASIN: "asin"
|
||||
64 NUM_AT_SIGNED128: "@Signed128"
|
||||
65 NUM_SIGNED128: "Signed128" imported
|
||||
65 NUM_SIGNED128: "Signed128"
|
||||
66 NUM_AT_SIGNED64: "@Signed64"
|
||||
67 NUM_SIGNED64: "Signed64" imported
|
||||
67 NUM_SIGNED64: "Signed64"
|
||||
68 NUM_AT_SIGNED32: "@Signed32"
|
||||
69 NUM_SIGNED32: "Signed32" imported
|
||||
69 NUM_SIGNED32: "Signed32"
|
||||
70 NUM_AT_SIGNED16: "@Signed16"
|
||||
71 NUM_SIGNED16: "Signed16" imported
|
||||
71 NUM_SIGNED16: "Signed16"
|
||||
72 NUM_AT_SIGNED8: "@Signed8"
|
||||
73 NUM_SIGNED8: "Signed8" imported
|
||||
73 NUM_SIGNED8: "Signed8"
|
||||
74 NUM_AT_UNSIGNED128: "@Unsigned128"
|
||||
75 NUM_UNSIGNED128: "Unsigned128" imported
|
||||
75 NUM_UNSIGNED128: "Unsigned128"
|
||||
76 NUM_AT_UNSIGNED64: "@Unsigned64"
|
||||
77 NUM_UNSIGNED64: "Unsigned64" imported
|
||||
77 NUM_UNSIGNED64: "Unsigned64"
|
||||
78 NUM_AT_UNSIGNED32: "@Unsigned32"
|
||||
79 NUM_UNSIGNED32: "Unsigned32" imported
|
||||
79 NUM_UNSIGNED32: "Unsigned32"
|
||||
80 NUM_AT_UNSIGNED16: "@Unsigned16"
|
||||
81 NUM_UNSIGNED16: "Unsigned16" imported
|
||||
81 NUM_UNSIGNED16: "Unsigned16"
|
||||
82 NUM_AT_UNSIGNED8: "@Unsigned8"
|
||||
83 NUM_UNSIGNED8: "Unsigned8" imported
|
||||
83 NUM_UNSIGNED8: "Unsigned8"
|
||||
84 NUM_AT_BINARY64: "@Binary64"
|
||||
85 NUM_BINARY64: "Binary64" imported
|
||||
85 NUM_BINARY64: "Binary64"
|
||||
86 NUM_AT_BINARY32: "@Binary32"
|
||||
87 NUM_BINARY32: "Binary32" imported
|
||||
87 NUM_BINARY32: "Binary32"
|
||||
88 NUM_BITWISE_AND: "bitwiseAnd"
|
||||
89 NUM_BITWISE_XOR: "bitwiseXor"
|
||||
90 NUM_BITWISE_OR: "bitwiseOr"
|
||||
@ -1005,16 +1005,16 @@ define_builtins! {
|
||||
96 NUM_SUB_SATURATED: "subSaturated"
|
||||
97 NUM_MUL_WRAP: "mulWrap"
|
||||
98 NUM_MUL_CHECKED: "mulChecked"
|
||||
99 NUM_INT: "Int" imported
|
||||
100 NUM_FLOAT: "Float" imported
|
||||
99 NUM_INT: "Int"
|
||||
100 NUM_FLOAT: "Float"
|
||||
101 NUM_AT_NATURAL: "@Natural"
|
||||
102 NUM_NATURAL: "Natural" imported
|
||||
103 NUM_NAT: "Nat" imported
|
||||
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" imported
|
||||
108 NUM_DEC: "Dec" imported // the Num.Dectype alias
|
||||
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"
|
||||
@ -1067,7 +1067,7 @@ define_builtins! {
|
||||
158 NUM_TO_F64_CHECKED: "toF64Checked"
|
||||
}
|
||||
2 BOOL: "Bool" => {
|
||||
0 BOOL_BOOL: "Bool" imported // the Bool.Bool type alias
|
||||
0 BOOL_BOOL: "Bool" // the Bool.Bool type alias
|
||||
1 BOOL_FALSE: "False" imported // Bool.Bool = [ False, True ]
|
||||
// NB: not strictly needed; used for finding global tag names in error suggestions
|
||||
2 BOOL_TRUE: "True" imported // Bool.Bool = [ False, True ]
|
||||
@ -1083,7 +1083,7 @@ define_builtins! {
|
||||
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"
|
||||
3 STR_APPEND: "#append" // unused
|
||||
4 STR_CONCAT: "concat"
|
||||
5 STR_JOIN_WITH: "joinWith"
|
||||
6 STR_SPLIT: "split"
|
||||
@ -1178,7 +1178,7 @@ define_builtins! {
|
||||
58 LIST_REPLACE: "replace"
|
||||
}
|
||||
5 RESULT: "Result" => {
|
||||
0 RESULT_RESULT: "Result" imported // the Result.Result type alias
|
||||
0 RESULT_RESULT: "Result" // the Result.Result type alias
|
||||
1 RESULT_OK: "Ok" imported // Result.Result a e = [ Ok a, Err e ]
|
||||
// NB: not strictly needed; used for finding global tag names in error suggestions
|
||||
2 RESULT_ERR: "Err" imported // Result.Result a e = [ Ok a, Err e ]
|
||||
@ -1226,6 +1226,7 @@ define_builtins! {
|
||||
12 SET_WALK: "walk"
|
||||
13 SET_WALK_USER_FUNCTION: "#walk_user_function"
|
||||
14 SET_CONTAINS: "contains"
|
||||
15 SET_TO_DICT: "toDict"
|
||||
}
|
||||
8 BOX: "Box" => {
|
||||
0 BOX_BOX_TYPE: "Box" imported // the Box.Box opaque type
|
||||
|
@ -1027,6 +1027,7 @@ pub fn lowlevel_borrow_signature(arena: &Bump, op: LowLevel) -> &[bool] {
|
||||
DictWalk => arena.alloc_slice_copy(&[owned, owned, function, closure_data]),
|
||||
|
||||
SetFromList => arena.alloc_slice_copy(&[owned]),
|
||||
SetToDict => arena.alloc_slice_copy(&[owned]),
|
||||
|
||||
ExpectTrue => arena.alloc_slice_copy(&[irrelevant]),
|
||||
|
||||
|
@ -25,7 +25,7 @@ const ARG_2: Symbol = Symbol::ARG_2;
|
||||
pub const REFCOUNT_MAX: usize = 0;
|
||||
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||
enum HelperOp {
|
||||
pub enum HelperOp {
|
||||
Inc,
|
||||
Dec,
|
||||
DecRef(JoinPointId),
|
||||
@ -185,16 +185,16 @@ impl<'a> CodeGenHelp<'a> {
|
||||
/// Generate a refcount increment procedure, *without* a Call expression.
|
||||
/// *This method should be rarely used* - only when the proc is to be called from Zig.
|
||||
/// Otherwise you want to generate the Proc and the Call together, using another method.
|
||||
/// This only supports the 'inc' operation, as it's the only real use case we have.
|
||||
pub fn gen_refcount_inc_proc(
|
||||
pub fn gen_refcount_proc(
|
||||
&mut self,
|
||||
ident_ids: &mut IdentIds,
|
||||
layout: Layout<'a>,
|
||||
op: HelperOp,
|
||||
) -> (Symbol, Vec<'a, (Symbol, ProcLayout<'a>)>) {
|
||||
let mut ctx = Context {
|
||||
new_linker_data: Vec::new_in(self.arena),
|
||||
recursive_union: None,
|
||||
op: HelperOp::Inc,
|
||||
op,
|
||||
};
|
||||
|
||||
let proc_name = self.find_or_create_proc(ident_ids, &mut ctx, layout);
|
||||
|
@ -107,7 +107,9 @@ pub fn refcount_generic<'a>(
|
||||
|
||||
match layout {
|
||||
Layout::Builtin(Builtin::Int(_) | Builtin::Float(_) | Builtin::Bool | Builtin::Decimal) => {
|
||||
unreachable!("Not refcounted: {:?}", layout)
|
||||
// Generate a dummy function that immediately returns Unit
|
||||
// Some higher-order Zig builtins *always* call an RC function on List elements.
|
||||
rc_return_stmt(root, ident_ids, ctx)
|
||||
}
|
||||
Layout::Builtin(Builtin::Str) => refcount_str(root, ident_ids, ctx),
|
||||
Layout::Builtin(Builtin::List(elem_layout)) => {
|
||||
|
@ -7,6 +7,7 @@ use crate::layout::{
|
||||
use bumpalo::collections::Vec;
|
||||
use bumpalo::Bump;
|
||||
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};
|
||||
@ -251,11 +252,22 @@ impl<'a> PartialProc<'a> {
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
enum PartialExprLink {
|
||||
Aliases(Symbol),
|
||||
enum PolymorphicExpr {
|
||||
/// A root ability member, which must be specialized at a call site, for example
|
||||
/// "hash" which must be specialized to an exact symbol implementing "hash" for a type.
|
||||
AbilityMember(Symbol),
|
||||
/// A polymorphic expression we inline at the usage site.
|
||||
Expr(roc_can::expr::Expr, Variable),
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
enum PartialExprLink {
|
||||
/// The root polymorphic expression
|
||||
Sink(PolymorphicExpr),
|
||||
/// A hop in a partial expression alias chain
|
||||
Aliases(Symbol),
|
||||
}
|
||||
|
||||
/// A table of symbols to polymorphic expressions. For example, in the program
|
||||
///
|
||||
/// n = 1
|
||||
@ -281,8 +293,8 @@ impl PartialExprs {
|
||||
Self(BumpMap::new_in(arena))
|
||||
}
|
||||
|
||||
fn insert(&mut self, symbol: Symbol, expr: roc_can::expr::Expr, expr_var: Variable) {
|
||||
self.0.insert(symbol, PartialExprLink::Expr(expr, expr_var));
|
||||
fn insert(&mut self, symbol: Symbol, expr: PolymorphicExpr) {
|
||||
self.0.insert(symbol, PartialExprLink::Sink(expr));
|
||||
}
|
||||
|
||||
fn insert_alias(&mut self, symbol: Symbol, aliases: Symbol) {
|
||||
@ -293,7 +305,7 @@ impl PartialExprs {
|
||||
self.0.contains_key(&symbol)
|
||||
}
|
||||
|
||||
fn get(&mut self, mut symbol: Symbol) -> Option<(&roc_can::expr::Expr, Variable)> {
|
||||
fn get(&mut self, mut symbol: Symbol) -> Option<&PolymorphicExpr> {
|
||||
// In practice the alias chain is very short
|
||||
loop {
|
||||
match self.0.get(&symbol) {
|
||||
@ -303,8 +315,8 @@ impl PartialExprs {
|
||||
Some(&PartialExprLink::Aliases(real_symbol)) => {
|
||||
symbol = real_symbol;
|
||||
}
|
||||
Some(PartialExprLink::Expr(expr, var)) => {
|
||||
return Some((expr, *var));
|
||||
Some(PartialExprLink::Sink(expr)) => {
|
||||
return Some(expr);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1119,6 +1131,7 @@ pub struct Env<'a, 'i> {
|
||||
pub target_info: TargetInfo,
|
||||
pub update_mode_ids: &'i mut UpdateModeIds,
|
||||
pub call_specialization_counter: u32,
|
||||
pub abilities_store: &'i AbilitiesStore,
|
||||
}
|
||||
|
||||
impl<'a, 'i> Env<'a, 'i> {
|
||||
@ -1143,7 +1156,7 @@ impl<'a, 'i> Env<'a, 'i> {
|
||||
}
|
||||
|
||||
pub fn is_imported_symbol(&self, symbol: Symbol) -> bool {
|
||||
symbol.module_id() != self.home && !symbol.is_builtin()
|
||||
symbol.module_id() != self.home
|
||||
}
|
||||
}
|
||||
|
||||
@ -4218,10 +4231,14 @@ pub fn with_hole<'a>(
|
||||
// a proc in this module, or an imported symbol
|
||||
procs.partial_procs.contains_key(key)
|
||||
|| (env.is_imported_symbol(key) && !procs.is_imported_module_thunk(key))
|
||||
|| env.abilities_store.is_ability_member_name(key)
|
||||
};
|
||||
|
||||
match loc_expr.value {
|
||||
roc_can::expr::Expr::Var(proc_name) if is_known(proc_name) => {
|
||||
// This might be an ability member - if so, use the appropriate specialization.
|
||||
let proc_name = get_specialization(env, fn_var, proc_name).unwrap_or(proc_name);
|
||||
|
||||
// a call by a known name
|
||||
call_by_name(
|
||||
env,
|
||||
@ -4330,47 +4347,68 @@ pub fn with_hole<'a>(
|
||||
unreachable!("calling a non-closure layout")
|
||||
}
|
||||
},
|
||||
UnspecializedExpr(symbol) => match full_layout {
|
||||
RawFunctionLayout::Function(arg_layouts, lambda_set, ret_layout) => {
|
||||
let closure_data_symbol = env.unique_symbol();
|
||||
UnspecializedExpr(symbol) => match procs.partial_exprs.get(symbol).unwrap()
|
||||
{
|
||||
&PolymorphicExpr::AbilityMember(member) => {
|
||||
let proc_name = get_specialization(env, fn_var, member).expect("Recorded as an ability member, but it doesn't have a specialization");
|
||||
|
||||
result = match_on_lambda_set(
|
||||
// a call by a known name
|
||||
return call_by_name(
|
||||
env,
|
||||
lambda_set,
|
||||
closure_data_symbol,
|
||||
arg_symbols,
|
||||
arg_layouts,
|
||||
ret_layout,
|
||||
procs,
|
||||
fn_var,
|
||||
proc_name,
|
||||
loc_args,
|
||||
layout_cache,
|
||||
assigned,
|
||||
hole,
|
||||
);
|
||||
|
||||
let (lambda_expr, lambda_expr_var) =
|
||||
procs.partial_exprs.get(symbol).unwrap();
|
||||
|
||||
let snapshot = env.subs.snapshot();
|
||||
let cache_snapshot = layout_cache.snapshot();
|
||||
let _unified = roc_unify::unify::unify(
|
||||
env.subs,
|
||||
fn_var,
|
||||
lambda_expr_var,
|
||||
roc_unify::unify::Mode::EQ,
|
||||
);
|
||||
|
||||
result = with_hole(
|
||||
env,
|
||||
lambda_expr.clone(),
|
||||
fn_var,
|
||||
procs,
|
||||
layout_cache,
|
||||
closure_data_symbol,
|
||||
env.arena.alloc(result),
|
||||
);
|
||||
env.subs.rollback_to(snapshot);
|
||||
layout_cache.rollback_to(cache_snapshot);
|
||||
}
|
||||
RawFunctionLayout::ZeroArgumentThunk(_) => {
|
||||
unreachable!("calling a non-closure layout")
|
||||
PolymorphicExpr::Expr(lambda_expr, lambda_expr_var) => {
|
||||
match full_layout {
|
||||
RawFunctionLayout::Function(
|
||||
arg_layouts,
|
||||
lambda_set,
|
||||
ret_layout,
|
||||
) => {
|
||||
let closure_data_symbol = env.unique_symbol();
|
||||
|
||||
result = match_on_lambda_set(
|
||||
env,
|
||||
lambda_set,
|
||||
closure_data_symbol,
|
||||
arg_symbols,
|
||||
arg_layouts,
|
||||
ret_layout,
|
||||
assigned,
|
||||
hole,
|
||||
);
|
||||
|
||||
let snapshot = env.subs.snapshot();
|
||||
let cache_snapshot = layout_cache.snapshot();
|
||||
let _unified = roc_unify::unify::unify(
|
||||
env.subs,
|
||||
fn_var,
|
||||
*lambda_expr_var,
|
||||
roc_unify::unify::Mode::EQ,
|
||||
);
|
||||
|
||||
result = with_hole(
|
||||
env,
|
||||
lambda_expr.clone(),
|
||||
fn_var,
|
||||
procs,
|
||||
layout_cache,
|
||||
closure_data_symbol,
|
||||
env.arena.alloc(result),
|
||||
);
|
||||
env.subs.rollback_to(snapshot);
|
||||
layout_cache.rollback_to(cache_snapshot);
|
||||
}
|
||||
RawFunctionLayout::ZeroArgumentThunk(_) => {
|
||||
unreachable!("calling a non-closure layout")
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
NotASymbol => {
|
||||
@ -4705,6 +4743,43 @@ pub fn with_hole<'a>(
|
||||
}
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn get_specialization<'a>(
|
||||
env: &mut Env<'a, '_>,
|
||||
symbol_var: Variable,
|
||||
symbol: Symbol,
|
||||
) -> Option<Symbol> {
|
||||
use roc_solve::ability::type_implementing_member;
|
||||
use roc_unify::unify::unify;
|
||||
|
||||
match env.abilities_store.member_def(symbol) {
|
||||
None => {
|
||||
// This is not an ability member, it doesn't need specialization.
|
||||
None
|
||||
}
|
||||
Some(member) => {
|
||||
let snapshot = env.subs.snapshot();
|
||||
let (_, must_implement_ability) = unify(
|
||||
env.subs,
|
||||
symbol_var,
|
||||
member.signature_var,
|
||||
roc_unify::unify::Mode::EQ,
|
||||
)
|
||||
.expect_success("This typechecked previously");
|
||||
env.subs.rollback_to(snapshot);
|
||||
let specializing_type =
|
||||
type_implementing_member(&must_implement_ability, member.parent_ability);
|
||||
|
||||
let specialization = env
|
||||
.abilities_store
|
||||
.get_specialization(symbol, specializing_type)
|
||||
.expect("No specialization is recorded - I thought there would only be a type error here.");
|
||||
|
||||
Some(specialization.symbol)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
fn construct_closure_data<'a>(
|
||||
env: &mut Env<'a, '_>,
|
||||
@ -5621,9 +5696,10 @@ pub fn from_can<'a>(
|
||||
// At the definition site `n = 1` we only know `1` to have the type `[Int *]`,
|
||||
// which won't be refined until the call `asU8 n`. Add it as a partial expression
|
||||
// that will be specialized at each concrete usage site.
|
||||
procs
|
||||
.partial_exprs
|
||||
.insert(*symbol, def.loc_expr.value, def.expr_var);
|
||||
procs.partial_exprs.insert(
|
||||
*symbol,
|
||||
PolymorphicExpr::Expr(def.loc_expr.value, def.expr_var),
|
||||
);
|
||||
|
||||
let result = from_can(env, variable, cont.value, procs, layout_cache);
|
||||
|
||||
@ -6329,7 +6405,7 @@ fn store_pattern_help<'a>(
|
||||
|
||||
match can_pat {
|
||||
Identifier(symbol) => {
|
||||
if let Some((_, var)) = procs.partial_exprs.get(outer_symbol) {
|
||||
if let Some(&PolymorphicExpr::Expr(_, var)) = procs.partial_exprs.get(outer_symbol) {
|
||||
// It might be the case that symbol we're storing hasn't been reified to a value
|
||||
// yet, if it's polymorphic. Do that now.
|
||||
stmt = specialize_symbol(
|
||||
@ -6690,7 +6766,19 @@ fn can_reuse_symbol<'a>(
|
||||
if let roc_can::expr::Expr::Var(symbol) = expr {
|
||||
let symbol = *symbol;
|
||||
|
||||
if env.is_imported_symbol(symbol) {
|
||||
let arguments = [
|
||||
Symbol::ARG_1,
|
||||
Symbol::ARG_2,
|
||||
Symbol::ARG_3,
|
||||
Symbol::ARG_4,
|
||||
Symbol::ARG_5,
|
||||
Symbol::ARG_6,
|
||||
Symbol::ARG_7,
|
||||
];
|
||||
|
||||
if arguments.contains(&symbol) {
|
||||
Value(symbol)
|
||||
} else if env.is_imported_symbol(symbol) {
|
||||
Imported(symbol)
|
||||
} else if procs.partial_procs.contains_key(symbol) {
|
||||
LocalFunction(symbol)
|
||||
@ -6727,6 +6815,13 @@ fn handle_variable_aliasing<'a, BuildRest>(
|
||||
where
|
||||
BuildRest: FnOnce(&mut Env<'a, '_>, &mut Procs<'a>, &mut LayoutCache<'a>) -> Stmt<'a>,
|
||||
{
|
||||
if env.abilities_store.is_ability_member_name(right) {
|
||||
procs
|
||||
.partial_exprs
|
||||
.insert(left, PolymorphicExpr::AbilityMember(right));
|
||||
return build_rest(env, procs, layout_cache);
|
||||
}
|
||||
|
||||
if procs.partial_exprs.contains(right) {
|
||||
// If `right` links to a partial expression, make sure we link `left` to it as well, so
|
||||
// that usages of it will be specialized when building the rest of the program.
|
||||
@ -6804,7 +6899,7 @@ fn specialize_symbol<'a>(
|
||||
result: Stmt<'a>,
|
||||
original: Symbol,
|
||||
) -> Stmt<'a> {
|
||||
if let Some((expr, expr_var)) = procs.partial_exprs.get(original) {
|
||||
if let Some(PolymorphicExpr::Expr(expr, expr_var)) = procs.partial_exprs.get(original) {
|
||||
// Specialize the expression type now, based off the `arg_var` we've been given.
|
||||
// TODO: cache the specialized result
|
||||
let snapshot = env.subs.snapshot();
|
||||
@ -6812,14 +6907,14 @@ fn specialize_symbol<'a>(
|
||||
let _unified = roc_unify::unify::unify(
|
||||
env.subs,
|
||||
arg_var.unwrap(),
|
||||
expr_var,
|
||||
*expr_var,
|
||||
roc_unify::unify::Mode::EQ,
|
||||
);
|
||||
|
||||
let result = with_hole(
|
||||
env,
|
||||
expr.clone(),
|
||||
expr_var,
|
||||
*expr_var,
|
||||
procs,
|
||||
layout_cache,
|
||||
symbol,
|
||||
@ -6837,9 +6932,10 @@ fn specialize_symbol<'a>(
|
||||
None => {
|
||||
match arg_var {
|
||||
Some(arg_var) if env.is_imported_symbol(original) => {
|
||||
let raw = layout_cache
|
||||
.raw_from_var(env.arena, arg_var, env.subs)
|
||||
.expect("creating layout does not fail");
|
||||
let raw = match layout_cache.raw_from_var(env.arena, arg_var, env.subs) {
|
||||
Ok(v) => v,
|
||||
Err(e) => return_on_layout_error_help!(env, e),
|
||||
};
|
||||
|
||||
if procs.is_imported_module_thunk(original) {
|
||||
let layout = match raw {
|
||||
@ -7315,6 +7411,7 @@ fn call_by_name_help<'a>(
|
||||
add_needed_external(procs, env, original_fn_var, proc_name);
|
||||
|
||||
debug_assert_ne!(proc_name.module_id(), ModuleId::ATTR);
|
||||
|
||||
if procs.is_imported_module_thunk(proc_name) {
|
||||
force_thunk(
|
||||
env,
|
||||
@ -7323,13 +7420,23 @@ fn call_by_name_help<'a>(
|
||||
assigned,
|
||||
hole,
|
||||
)
|
||||
} else {
|
||||
debug_assert!(
|
||||
!field_symbols.is_empty(),
|
||||
"{} should be in the list of imported_module_thunks",
|
||||
proc_name
|
||||
);
|
||||
} else if field_symbols.is_empty() {
|
||||
// this is a case like `Str.concat`, an imported standard function, applied to zero arguments
|
||||
|
||||
// imported symbols cannot capture anything
|
||||
let captured = &[];
|
||||
|
||||
construct_closure_data(
|
||||
env,
|
||||
procs,
|
||||
layout_cache,
|
||||
lambda_set,
|
||||
proc_name,
|
||||
captured,
|
||||
assigned,
|
||||
hole,
|
||||
)
|
||||
} else {
|
||||
debug_assert_eq!(
|
||||
argument_layouts.len(),
|
||||
field_symbols.len(),
|
||||
@ -7484,8 +7591,6 @@ fn call_by_name_module_thunk<'a>(
|
||||
assigned: Symbol,
|
||||
hole: &'a Stmt<'a>,
|
||||
) -> Stmt<'a> {
|
||||
debug_assert!(!env.is_imported_symbol(proc_name));
|
||||
|
||||
let top_level_layout = ProcLayout::new(env.arena, &[], *ret_layout);
|
||||
|
||||
let inner_layout = *ret_layout;
|
||||
|
@ -278,6 +278,12 @@ pub struct AbilityMember<'a> {
|
||||
pub typ: Loc<TypeAnnotation<'a>>,
|
||||
}
|
||||
|
||||
impl AbilityMember<'_> {
|
||||
pub fn region(&self) -> Region {
|
||||
Region::across_all([self.name.region, self.typ.region].iter())
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq)]
|
||||
pub enum TypeDef<'a> {
|
||||
/// A type alias. This is like a standalone annotation, except the pattern
|
||||
@ -705,7 +711,7 @@ impl<'a, T> Collection<'a, T> {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn with_items(items: &'a [T]) -> Collection<'a, T> {
|
||||
pub const fn with_items(items: &'a [T]) -> Collection<'a, T> {
|
||||
Collection {
|
||||
items,
|
||||
final_comments: None,
|
||||
|
@ -6,6 +6,7 @@ use crate::parser::{specialize, word1, EPackageEntry, EPackageName, Parser};
|
||||
use crate::state::State;
|
||||
use crate::string_literal;
|
||||
use bumpalo::collections::Vec;
|
||||
use roc_module::symbol::Symbol;
|
||||
use roc_region::all::Loc;
|
||||
|
||||
#[derive(Debug)]
|
||||
@ -17,6 +18,10 @@ pub enum HeaderFor<'a> {
|
||||
generates: UppercaseIdent<'a>,
|
||||
generates_with: &'a [Loc<ExposedName<'a>>],
|
||||
},
|
||||
/// Only created during canonicalization, never actually parsed from source
|
||||
Builtin {
|
||||
generates_with: &'a [Symbol],
|
||||
},
|
||||
PkgConfig {
|
||||
/// usually `pf`
|
||||
config_shorthand: &'a str,
|
||||
@ -60,11 +65,11 @@ impl<'a> From<ModuleName<'a>> for &'a str {
|
||||
}
|
||||
|
||||
impl<'a> ModuleName<'a> {
|
||||
pub fn new(name: &'a str) -> Self {
|
||||
pub const fn new(name: &'a str) -> Self {
|
||||
ModuleName(name)
|
||||
}
|
||||
|
||||
pub fn as_str(&'a self) -> &'a str {
|
||||
pub const fn as_str(&'a self) -> &'a str {
|
||||
self.0
|
||||
}
|
||||
}
|
||||
@ -88,7 +93,7 @@ impl<'a> From<ExposedName<'a>> for &'a str {
|
||||
}
|
||||
|
||||
impl<'a> ExposedName<'a> {
|
||||
pub fn new(name: &'a str) -> Self {
|
||||
pub const fn new(name: &'a str) -> Self {
|
||||
ExposedName(name)
|
||||
}
|
||||
|
||||
|
@ -135,6 +135,9 @@ pub enum Problem {
|
||||
loc_name: Loc<Symbol>,
|
||||
ability: Symbol,
|
||||
},
|
||||
AbilityNotOnToplevel {
|
||||
region: Region,
|
||||
},
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
|
@ -279,16 +279,16 @@ pub struct Loc<T> {
|
||||
}
|
||||
|
||||
impl<T> Loc<T> {
|
||||
pub fn new(start: u32, end: u32, value: T) -> Loc<T> {
|
||||
pub const fn new(start: u32, end: u32, value: T) -> Loc<T> {
|
||||
let region = Region::new(Position::new(start), Position::new(end));
|
||||
Loc { region, value }
|
||||
}
|
||||
|
||||
pub fn at(region: Region, value: T) -> Loc<T> {
|
||||
pub const fn at(region: Region, value: T) -> Loc<T> {
|
||||
Loc { region, value }
|
||||
}
|
||||
|
||||
pub fn at_zero(value: T) -> Loc<T> {
|
||||
pub const fn at_zero(value: T) -> Loc<T> {
|
||||
let region = Region::zero();
|
||||
Loc { region, value }
|
||||
}
|
||||
|
@ -1,9 +1,11 @@
|
||||
use roc_can::abilities::AbilitiesStore;
|
||||
use roc_module::symbol::Symbol;
|
||||
use roc_region::all::{Loc, Region};
|
||||
use roc_types::subs::Subs;
|
||||
use roc_types::subs::Variable;
|
||||
use roc_types::types::{Category, PatternCategory};
|
||||
use roc_unify::unify::MustImplementAbility;
|
||||
use roc_unify::unify::MustImplementConstraints;
|
||||
|
||||
use crate::solve::{IncompleteAbilityImplementation, TypeError};
|
||||
|
||||
@ -18,17 +20,14 @@ pub enum AbilityImplError {
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct DeferredMustImplementAbility(Vec<(Vec<MustImplementAbility>, AbilityImplError)>);
|
||||
pub struct DeferredMustImplementAbility(Vec<(MustImplementConstraints, AbilityImplError)>);
|
||||
|
||||
impl DeferredMustImplementAbility {
|
||||
pub fn add(&mut self, must_implement: Vec<MustImplementAbility>, on_error: AbilityImplError) {
|
||||
pub fn add(&mut self, must_implement: MustImplementConstraints, on_error: AbilityImplError) {
|
||||
self.0.push((must_implement, on_error));
|
||||
}
|
||||
|
||||
pub fn check(self, subs: &mut Subs, abilities_store: &AbilitiesStore) -> Vec<TypeError> {
|
||||
// Two passes here. First up let's build up records of what types fully implement
|
||||
// abilities, and what specializations are available/missing for the ones that don't.
|
||||
// Use a vec since these lists should usually be pretty small.
|
||||
let mut good = vec![];
|
||||
let mut bad = vec![];
|
||||
|
||||
@ -45,8 +44,21 @@ impl DeferredMustImplementAbility {
|
||||
};
|
||||
}
|
||||
|
||||
for (mias, _) in self.0.iter() {
|
||||
for &mia @ MustImplementAbility { typ, ability } in mias {
|
||||
let mut problems = vec![];
|
||||
|
||||
// Keep track of which types that have an incomplete ability were reported as part of
|
||||
// another type error (from an expression or pattern). If we reported an error for a type
|
||||
// that doesn't implement an ability in that context, we don't want to repeat the error
|
||||
// message.
|
||||
let mut reported_in_context = vec![];
|
||||
let mut incomplete_not_in_context = vec![];
|
||||
|
||||
for (constraints, on_error) in self.0.into_iter() {
|
||||
let must_implement = constraints.get_unique();
|
||||
|
||||
// First off, make sure we populate information about which of the "must implement"
|
||||
// constraints are met, and which aren't.
|
||||
for &mia @ MustImplementAbility { typ, ability } in must_implement.iter() {
|
||||
if is_good!(&mia) || get_bad!(mia).is_some() {
|
||||
continue;
|
||||
}
|
||||
@ -80,22 +92,16 @@ impl DeferredMustImplementAbility {
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Now figure out what errors we need to report.
|
||||
let mut problems = vec![];
|
||||
|
||||
// Keep track of which types that have an incomplete ability were reported as part of
|
||||
// another type error (from an expression or pattern). If we reported an error for a type
|
||||
// that doesn't implement an ability in that context, we don't want to repeat the error
|
||||
// message.
|
||||
let mut reported_in_context = vec![];
|
||||
let mut incomplete_not_in_context = vec![];
|
||||
|
||||
for (must_implement, on_error) in self.0.into_iter() {
|
||||
// Now, figure out what errors we need to report.
|
||||
use AbilityImplError::*;
|
||||
match on_error {
|
||||
IncompleteAbility => {
|
||||
// These aren't attached to another type error, so if these must_implement
|
||||
// constraints aren't met, we'll emit a generic "this type doesn't implement an
|
||||
// ability" error message at the end. We only want to do this if it turns out
|
||||
// the "must implement" constraint indeed wasn't part of a more specific type
|
||||
// error.
|
||||
incomplete_not_in_context.extend(must_implement);
|
||||
}
|
||||
BadExpr(region, category, var) => {
|
||||
@ -138,9 +144,11 @@ impl DeferredMustImplementAbility {
|
||||
reported_in_context.extend(must_implement);
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
// Go through and attach generic "type does not implement ability" errors, if they were not
|
||||
// part of a larger context.
|
||||
for mia in incomplete_not_in_context.into_iter() {
|
||||
if let Some(must_implement) = get_bad!(mia) {
|
||||
if !reported_in_context.contains(&mia) {
|
||||
@ -154,3 +162,29 @@ impl DeferredMustImplementAbility {
|
||||
problems
|
||||
}
|
||||
}
|
||||
|
||||
/// Determines what type implements an ability member of a specialized signature, given the
|
||||
/// [MustImplementAbility] constraints of the signature.
|
||||
pub fn type_implementing_member(
|
||||
specialization_must_implement_constraints: &MustImplementConstraints,
|
||||
ability: Symbol,
|
||||
) -> Symbol {
|
||||
debug_assert_eq!({
|
||||
let ability_implementations_for_specialization =
|
||||
specialization_must_implement_constraints
|
||||
.clone()
|
||||
.get_unique();
|
||||
|
||||
ability_implementations_for_specialization.len()
|
||||
},
|
||||
1,
|
||||
"Multiple variables bound to an ability - this is ambiguous and should have been caught in canonicalization: {:?}",
|
||||
specialization_must_implement_constraints
|
||||
);
|
||||
|
||||
specialization_must_implement_constraints
|
||||
.iter_for_ability(ability)
|
||||
.next()
|
||||
.unwrap()
|
||||
.typ
|
||||
}
|
||||
|
@ -2,6 +2,6 @@
|
||||
// See github.com/rtfeldman/roc/issues/800 for discussion of the large_enum_variant check.
|
||||
#![allow(clippy::large_enum_variant)]
|
||||
|
||||
mod ability;
|
||||
pub mod ability;
|
||||
pub mod module;
|
||||
pub mod solve;
|
||||
|
@ -1,4 +1,4 @@
|
||||
use crate::ability::{AbilityImplError, DeferredMustImplementAbility};
|
||||
use crate::ability::{type_implementing_member, AbilityImplError, DeferredMustImplementAbility};
|
||||
use bumpalo::Bump;
|
||||
use roc_can::abilities::{AbilitiesStore, MemberSpecialization};
|
||||
use roc_can::constraint::Constraint::{self, *};
|
||||
@ -703,7 +703,6 @@ fn solve(
|
||||
check_ability_specialization(
|
||||
arena,
|
||||
subs,
|
||||
&new_env,
|
||||
pools,
|
||||
rank,
|
||||
abilities_store,
|
||||
@ -812,7 +811,6 @@ fn solve(
|
||||
check_ability_specialization(
|
||||
arena,
|
||||
subs,
|
||||
&new_env,
|
||||
pools,
|
||||
rank,
|
||||
abilities_store,
|
||||
@ -1189,6 +1187,7 @@ fn solve(
|
||||
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);
|
||||
@ -1282,7 +1281,6 @@ fn solve(
|
||||
fn check_ability_specialization(
|
||||
arena: &Bump,
|
||||
subs: &mut Subs,
|
||||
env: &Env,
|
||||
pools: &mut Pools,
|
||||
rank: Rank,
|
||||
abilities_store: &mut AbilitiesStore,
|
||||
@ -1295,9 +1293,7 @@ fn check_ability_specialization(
|
||||
// inferred type for the specialization actually aligns with the expected
|
||||
// implementation.
|
||||
if let Some((root_symbol, root_data)) = abilities_store.root_name_and_def(symbol) {
|
||||
let root_signature_var = env
|
||||
.get_var_by_symbol(&root_symbol)
|
||||
.expect("Ability should be registered in env by now!");
|
||||
let root_signature_var = root_data.signature_var;
|
||||
|
||||
// Check if they unify - if they don't, then the claimed specialization isn't really one,
|
||||
// and that's a type error!
|
||||
@ -1351,16 +1347,8 @@ fn check_ability_specialization(
|
||||
|
||||
// First, figure out and register for what type does this symbol specialize
|
||||
// the ability member.
|
||||
let mut ability_implementations_for_specialization = must_implement_ability
|
||||
.iter()
|
||||
.filter(|mia| mia.ability == root_data.parent_ability)
|
||||
.collect::<Vec<_>>();
|
||||
ability_implementations_for_specialization.dedup();
|
||||
|
||||
debug_assert!(ability_implementations_for_specialization.len() == 1, "Multiple variables bound to an ability - this is ambiguous and should have been caught in canonicalization");
|
||||
|
||||
// This is a valid specialization! Record it.
|
||||
let specialization_type = ability_implementations_for_specialization[0].typ;
|
||||
let specialization_type =
|
||||
type_implementing_member(&must_implement_ability, root_data.parent_ability);
|
||||
let specialization = MemberSpecialization {
|
||||
symbol,
|
||||
region: symbol_loc_var.region,
|
||||
|
@ -17,8 +17,6 @@ mod solve_expr {
|
||||
|
||||
fn run_load_and_infer(src: &str) -> Result<LoadedModule, std::io::Error> {
|
||||
use bumpalo::Bump;
|
||||
use std::fs::File;
|
||||
use std::io::Write;
|
||||
use std::path::PathBuf;
|
||||
use tempfile::tempdir;
|
||||
|
||||
@ -40,13 +38,10 @@ mod solve_expr {
|
||||
let dir = tempdir()?;
|
||||
let filename = PathBuf::from("Test.roc");
|
||||
let file_path = dir.path().join(filename);
|
||||
let full_file_path = file_path.clone();
|
||||
let mut file = File::create(file_path)?;
|
||||
writeln!(file, "{}", module_src)?;
|
||||
drop(file);
|
||||
let result = roc_load::load_and_typecheck(
|
||||
let result = roc_load::load_and_typecheck_str(
|
||||
arena,
|
||||
full_file_path,
|
||||
file_path,
|
||||
module_src,
|
||||
dir.path(),
|
||||
exposed_types,
|
||||
roc_target::TargetInfo::default_x86_64(),
|
||||
@ -125,8 +120,15 @@ mod solve_expr {
|
||||
}
|
||||
|
||||
fn promote_expr_to_module(src: &str) -> String {
|
||||
let mut buffer =
|
||||
String::from("app \"test\" provides [ main ] to \"./platform\"\n\nmain =\n");
|
||||
let mut buffer = String::from(indoc!(
|
||||
r#"
|
||||
app "test"
|
||||
imports []
|
||||
provides [ main ] to "./platform"
|
||||
|
||||
main =
|
||||
"#
|
||||
));
|
||||
|
||||
for line in src.lines() {
|
||||
// indent the body!
|
||||
@ -3402,11 +3404,11 @@ mod solve_expr {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn div_floor() {
|
||||
fn div_trunc() {
|
||||
infer_eq_without_problem(
|
||||
indoc!(
|
||||
r#"
|
||||
Num.divFloor
|
||||
Num.divTrunc
|
||||
"#
|
||||
),
|
||||
"Int a, Int a -> Int a",
|
||||
@ -3414,11 +3416,11 @@ mod solve_expr {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn div_floor_checked() {
|
||||
fn div_trunc_checked() {
|
||||
infer_eq_without_problem(
|
||||
indoc!(
|
||||
r#"
|
||||
Num.divFloorChecked
|
||||
Num.divTruncChecked
|
||||
"#
|
||||
),
|
||||
"Int a, Int a -> Result (Int a) [ DivByZero ]*",
|
||||
@ -3603,7 +3605,7 @@ mod solve_expr {
|
||||
infer_eq_without_problem(
|
||||
indoc!(
|
||||
r#"
|
||||
app "test" provides [ main ] to "./platform"
|
||||
app "test" imports [ Result.{ Result } ] provides [ main ] to "./platform"
|
||||
|
||||
boom = \_ -> boom {}
|
||||
|
||||
@ -4828,7 +4830,7 @@ mod solve_expr {
|
||||
infer_eq_without_problem(
|
||||
indoc!(
|
||||
r#"
|
||||
canIGo : _ -> Result _ _
|
||||
canIGo : _ -> Result.Result _ _
|
||||
canIGo = \color ->
|
||||
when color is
|
||||
"green" -> Ok "go!"
|
||||
@ -5913,4 +5915,40 @@ mod solve_expr {
|
||||
"U64",
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn alias_ability_member() {
|
||||
infer_eq_without_problem(
|
||||
indoc!(
|
||||
r#"
|
||||
app "test" provides [ thething ] to "./platform"
|
||||
|
||||
Hash has
|
||||
hash : a -> U64 | a has Hash
|
||||
|
||||
thething =
|
||||
itis = hash
|
||||
itis
|
||||
"#
|
||||
),
|
||||
"a -> U64 | a has Hash",
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn when_branch_and_body_flipflop() {
|
||||
infer_eq_without_problem(
|
||||
indoc!(
|
||||
r#"
|
||||
func = \record ->
|
||||
when record.tag is
|
||||
A -> { record & tag: B }
|
||||
B -> { record & tag: A }
|
||||
|
||||
func
|
||||
"#
|
||||
),
|
||||
"{ tag : [ A, B ] }a -> { tag : [ A, B ] }a",
|
||||
)
|
||||
}
|
||||
}
|
||||
|
86
compiler/test_gen/src/gen_abilities.rs
Normal file
86
compiler/test_gen/src/gen_abilities.rs
Normal file
@ -0,0 +1,86 @@
|
||||
#[cfg(feature = "gen-llvm")]
|
||||
use crate::helpers::llvm::assert_evals_to;
|
||||
|
||||
#[cfg(feature = "gen-dev")]
|
||||
use crate::helpers::dev::assert_evals_to;
|
||||
|
||||
#[cfg(feature = "gen-wasm")]
|
||||
use crate::helpers::wasm::assert_evals_to;
|
||||
|
||||
#[cfg(test)]
|
||||
use indoc::indoc;
|
||||
|
||||
#[test]
|
||||
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
|
||||
fn hash_specialization() {
|
||||
assert_evals_to!(
|
||||
indoc!(
|
||||
r#"
|
||||
app "test" provides [ main ] to "./platform"
|
||||
|
||||
Hash has
|
||||
hash : a -> U64 | a has Hash
|
||||
|
||||
Id := U64
|
||||
|
||||
hash = \$Id n -> n
|
||||
|
||||
main = hash ($Id 1234)
|
||||
"#
|
||||
),
|
||||
1234,
|
||||
u64
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
|
||||
fn hash_specialization_multiple_add() {
|
||||
assert_evals_to!(
|
||||
indoc!(
|
||||
r#"
|
||||
app "test" provides [ main ] to "./platform"
|
||||
|
||||
Hash has
|
||||
hash : a -> U64 | a has Hash
|
||||
|
||||
Id := U64
|
||||
|
||||
hash = \$Id n -> n
|
||||
|
||||
One := {}
|
||||
|
||||
hash = \$One _ -> 1
|
||||
|
||||
main = hash ($Id 1234) + hash ($One {})
|
||||
"#
|
||||
),
|
||||
1235,
|
||||
u64
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
|
||||
fn alias_member_specialization() {
|
||||
assert_evals_to!(
|
||||
indoc!(
|
||||
r#"
|
||||
app "test" provides [ main ] to "./platform"
|
||||
|
||||
Hash has
|
||||
hash : a -> U64 | a has Hash
|
||||
|
||||
Id := U64
|
||||
|
||||
hash = \$Id n -> n
|
||||
|
||||
main =
|
||||
aliasedHash = hash
|
||||
aliasedHash ($Id 1234)
|
||||
"#
|
||||
),
|
||||
1234,
|
||||
u64
|
||||
);
|
||||
}
|
@ -1104,7 +1104,7 @@ fn list_map_closure() {
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(any(feature = "gen-llvm"))]
|
||||
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
|
||||
fn list_map4_group() {
|
||||
assert_evals_to!(
|
||||
indoc!(
|
||||
@ -1112,13 +1112,13 @@ fn list_map4_group() {
|
||||
List.map4 [1,2,3] [3,2,1] [2,1,3] [3,1,2] (\a, b, c, d -> Group a b c d)
|
||||
"#
|
||||
),
|
||||
RocList::from_slice(&[(1, 3, 2, 3), (2, 2, 1, 1), (3, 1, 3, 2)]),
|
||||
RocList<(i64, i64, i64, i64)>
|
||||
RocList::from_slice(&[[1, 3, 2, 3], [2, 2, 1, 1], [3, 1, 3, 2]]),
|
||||
RocList<[i64; 4]>
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(any(feature = "gen-llvm"))]
|
||||
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
|
||||
fn list_map4_different_length() {
|
||||
assert_evals_to!(
|
||||
indoc!(
|
||||
@ -1137,7 +1137,7 @@ fn list_map4_different_length() {
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(any(feature = "gen-llvm"))]
|
||||
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
|
||||
fn list_map3_group() {
|
||||
assert_evals_to!(
|
||||
indoc!(
|
||||
@ -1151,7 +1151,7 @@ fn list_map3_group() {
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(any(feature = "gen-llvm"))]
|
||||
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
|
||||
fn list_map3_different_length() {
|
||||
assert_evals_to!(
|
||||
indoc!(
|
||||
@ -1169,7 +1169,7 @@ fn list_map3_different_length() {
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(any(feature = "gen-llvm"))]
|
||||
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
|
||||
fn list_map2_pair() {
|
||||
assert_evals_to!(
|
||||
indoc!(
|
||||
@ -1184,13 +1184,13 @@ fn list_map2_pair() {
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(any(feature = "gen-llvm"))]
|
||||
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
|
||||
fn list_map2_different_lengths() {
|
||||
assert_evals_to!(
|
||||
indoc!(
|
||||
r#"
|
||||
List.map2
|
||||
["a", "b", "lllllllllllllongnggg" ]
|
||||
["a", "b", "lllllllllllllooooooooongnggg" ]
|
||||
["b"]
|
||||
(\a, b -> Str.concat a b)
|
||||
"#
|
||||
|
@ -19,7 +19,7 @@ fn nat_alias() {
|
||||
assert_evals_to!(
|
||||
indoc!(
|
||||
r#"
|
||||
i : Nat
|
||||
i : Num.Nat
|
||||
i = 1
|
||||
|
||||
i
|
||||
@ -1050,7 +1050,7 @@ fn gen_div_checked_i64() {
|
||||
assert_evals_to!(
|
||||
indoc!(
|
||||
r#"
|
||||
when Num.divFloorChecked 1000 10 is
|
||||
when Num.divTruncChecked 1000 10 is
|
||||
Ok val -> val
|
||||
Err _ -> -1
|
||||
"#
|
||||
@ -1066,7 +1066,7 @@ fn gen_div_checked_by_zero_i64() {
|
||||
assert_evals_to!(
|
||||
indoc!(
|
||||
r#"
|
||||
when Num.divFloorChecked 1000 0 is
|
||||
when Num.divTruncChecked 1000 0 is
|
||||
Err DivByZero -> 99
|
||||
_ -> -24
|
||||
"#
|
||||
|
@ -3266,7 +3266,9 @@ fn box_and_unbox_string() {
|
||||
assert_evals_to!(
|
||||
indoc!(
|
||||
r#"
|
||||
Box.unbox (Box.box (Str.concat "Leverage " "agile frameworks to provide a robust synopsis for high level overviews"))
|
||||
Str.concat "Leverage " "agile frameworks to provide a robust synopsis for high level overviews"
|
||||
|> Box.box
|
||||
|> Box.unbox
|
||||
"#
|
||||
),
|
||||
RocStr::from(
|
||||
@ -3312,6 +3314,7 @@ fn box_and_unbox_tag_union() {
|
||||
r#"
|
||||
v : [ A U8, B U8 ] # usually stack allocated
|
||||
v = B 27u8
|
||||
|
||||
Box.unbox (Box.box v)
|
||||
"#
|
||||
),
|
||||
|
@ -4,6 +4,7 @@
|
||||
// we actually want to compare against the literal float bits
|
||||
#![allow(clippy::float_cmp)]
|
||||
|
||||
pub mod gen_abilities;
|
||||
pub mod gen_compare;
|
||||
pub mod gen_dict;
|
||||
pub mod gen_list;
|
||||
|
7
compiler/test_mono/generated/specialize_ability_call.txt
Normal file
7
compiler/test_mono/generated/specialize_ability_call.txt
Normal file
@ -0,0 +1,7 @@
|
||||
procedure Test.5 (Test.8):
|
||||
ret Test.8;
|
||||
|
||||
procedure Test.0 ():
|
||||
let Test.10 : U64 = 1234i64;
|
||||
let Test.9 : U64 = CallByName Test.5 Test.10;
|
||||
ret Test.9;
|
@ -275,7 +275,7 @@ fn ir_round() {
|
||||
#[mono_test]
|
||||
fn ir_when_idiv() {
|
||||
r#"
|
||||
when Num.divFloorChecked 1000 10 is
|
||||
when Num.divTruncChecked 1000 10 is
|
||||
Ok val -> val
|
||||
Err _ -> -1
|
||||
"#
|
||||
@ -1294,6 +1294,25 @@ fn issue_2811() {
|
||||
)
|
||||
}
|
||||
|
||||
#[mono_test]
|
||||
fn specialize_ability_call() {
|
||||
indoc!(
|
||||
r#"
|
||||
app "test" provides [ main ] to "./platform"
|
||||
|
||||
Hash has
|
||||
hash : a -> U64 | a has Hash
|
||||
|
||||
Id := U64
|
||||
|
||||
hash : Id -> U64
|
||||
hash = \$Id n -> n
|
||||
|
||||
main = hash ($Id 1234)
|
||||
"#
|
||||
)
|
||||
}
|
||||
|
||||
// #[ignore]
|
||||
// #[mono_test]
|
||||
// fn static_str_closure() {
|
||||
|
@ -11,7 +11,6 @@ const NUM_BUILTIN_IMPORTS: usize = 8;
|
||||
|
||||
/// These can be shared between definitions, they will get instantiated when converted to Type
|
||||
const TVAR1: VarId = VarId::from_u32(1);
|
||||
const TVAR2: VarId = VarId::from_u32(2);
|
||||
|
||||
pub fn aliases() -> MutMap<Symbol, BuiltinAlias> {
|
||||
let mut aliases = HashMap::with_capacity_and_hasher(NUM_BUILTIN_IMPORTS, default_hasher());
|
||||
@ -319,19 +318,6 @@ pub fn aliases() -> MutMap<Symbol, BuiltinAlias> {
|
||||
},
|
||||
);
|
||||
|
||||
// Result ok err : [ Ok ok, Err err ]
|
||||
add_alias(
|
||||
Symbol::RESULT_RESULT,
|
||||
BuiltinAlias {
|
||||
region: Region::zero(),
|
||||
vars: vec![
|
||||
Loc::at(Region::zero(), "ok".into()),
|
||||
Loc::at(Region::zero(), "err".into()),
|
||||
],
|
||||
typ: result_alias_content(flex(TVAR1), flex(TVAR2)),
|
||||
},
|
||||
);
|
||||
|
||||
// Utf8ByteProblem : [ InvalidStartByte, UnexpectedEndOfSequence, ExpectedContinuation, OverlongEncoding, CodepointTooLarge, EncodesSurrogateHalf ]
|
||||
add_alias(
|
||||
Symbol::STR_UT8_BYTE_PROBLEM,
|
||||
|
@ -1,5 +1,5 @@
|
||||
use bitflags::bitflags;
|
||||
use roc_error_macros::todo_abilities;
|
||||
use roc_error_macros::{internal_error, todo_abilities};
|
||||
use roc_module::ident::{Lowercase, TagName};
|
||||
use roc_module::symbol::Symbol;
|
||||
use roc_types::subs::Content::{self, *};
|
||||
@ -134,14 +134,26 @@ pub struct Context {
|
||||
pub enum Unified {
|
||||
Success {
|
||||
vars: Pool,
|
||||
must_implement_ability: Vec<MustImplementAbility>,
|
||||
must_implement_ability: MustImplementConstraints,
|
||||
},
|
||||
Failure(Pool, ErrorType, ErrorType, DoesNotImplementAbility),
|
||||
BadType(Pool, roc_types::types::Problem),
|
||||
}
|
||||
|
||||
impl Unified {
|
||||
pub fn expect_success(self, err_msg: &'static str) -> (Pool, MustImplementConstraints) {
|
||||
match self {
|
||||
Unified::Success {
|
||||
vars,
|
||||
must_implement_ability,
|
||||
} => (vars, must_implement_ability),
|
||||
_ => internal_error!("{}", err_msg),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Specifies that `type` must implement the ability `ability`.
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
|
||||
pub struct MustImplementAbility {
|
||||
// This only points to opaque type names currently.
|
||||
// TODO(abilities) support structural types in general
|
||||
@ -149,12 +161,39 @@ pub struct MustImplementAbility {
|
||||
pub ability: Symbol,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Default)]
|
||||
pub struct MustImplementConstraints(Vec<MustImplementAbility>);
|
||||
|
||||
impl MustImplementConstraints {
|
||||
pub fn push(&mut self, must_implement: MustImplementAbility) {
|
||||
self.0.push(must_implement)
|
||||
}
|
||||
|
||||
pub fn extend(&mut self, other: Self) {
|
||||
self.0.extend(other.0)
|
||||
}
|
||||
|
||||
pub fn is_empty(&self) -> bool {
|
||||
self.0.is_empty()
|
||||
}
|
||||
|
||||
pub fn get_unique(mut self) -> Vec<MustImplementAbility> {
|
||||
self.0.sort();
|
||||
self.0.dedup();
|
||||
self.0
|
||||
}
|
||||
|
||||
pub fn iter_for_ability(&self, ability: Symbol) -> impl Iterator<Item = &MustImplementAbility> {
|
||||
self.0.iter().filter(move |mia| mia.ability == ability)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Default)]
|
||||
pub struct Outcome {
|
||||
mismatches: Vec<Mismatch>,
|
||||
/// We defer these checks until the end of a solving phase.
|
||||
/// NOTE: this vector is almost always empty!
|
||||
must_implement_ability: Vec<MustImplementAbility>,
|
||||
must_implement_ability: MustImplementConstraints,
|
||||
}
|
||||
|
||||
impl Outcome {
|
||||
@ -310,7 +349,7 @@ fn unify_context(subs: &mut Subs, pool: &mut Pool, ctx: Context) -> Outcome {
|
||||
};
|
||||
|
||||
#[cfg(debug_assertions)]
|
||||
debug_print_unified_types(subs, &ctx, true);
|
||||
debug_print_unified_types(subs, &ctx, false);
|
||||
|
||||
result
|
||||
}
|
||||
|
@ -434,7 +434,7 @@ stepExecCtx = \ctx, char ->
|
||||
(
|
||||
(T popCtx1 numR) <- Result.after (popNumber ctx)
|
||||
(T popCtx2 numL) <- Result.after (popNumber popCtx1)
|
||||
res <- Result.after (Num.divFloorChecked numL numR)
|
||||
res <- Result.after (Num.divTruncChecked numL numR)
|
||||
Ok (Context.pushStack popCtx2 (Number res))
|
||||
)
|
||||
|
||||
|
@ -62,14 +62,14 @@ fn num_rem() {
|
||||
#[cfg(not(feature = "wasm"))]
|
||||
#[test]
|
||||
fn num_floor_division() {
|
||||
expect_success("Num.divFloor 4 3", "1 : Int *");
|
||||
expect_success("Num.divTrunc 4 3", "1 : Int *");
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "wasm"))]
|
||||
#[test]
|
||||
fn num_floor_checked_division_success() {
|
||||
expect_success(
|
||||
"Num.divFloorChecked 4 3",
|
||||
"Num.divTruncChecked 4 3",
|
||||
"Ok 1 : Result (Int *) [ DivByZero ]*",
|
||||
);
|
||||
}
|
||||
@ -78,7 +78,7 @@ fn num_floor_checked_division_success() {
|
||||
#[test]
|
||||
fn num_floor_checked_division_divby_zero() {
|
||||
expect_success(
|
||||
"Num.divFloorChecked 4 0",
|
||||
"Num.divTruncChecked 4 0",
|
||||
"Err DivByZero : Result (Int *) [ DivByZero ]*",
|
||||
);
|
||||
}
|
||||
|
@ -45,6 +45,7 @@ const ILLEGAL_HAS_CLAUSE: &str = "ILLEGAL HAS CLAUSE";
|
||||
const ABILITY_MEMBER_MISSING_HAS_CLAUSE: &str = "ABILITY MEMBER MISSING HAS CLAUSE";
|
||||
const ABILITY_MEMBER_HAS_EXTRANEOUS_HAS_CLAUSE: &str = "ABILITY MEMBER HAS EXTRANEOUS HAS CLAUSE";
|
||||
const ABILITY_MEMBER_BINDS_MULTIPLE_VARIABLES: &str = "ABILITY MEMBER BINDS MULTIPLE VARIABLES";
|
||||
const ABILITY_NOT_ON_TOPLEVEL: &str = "ABILITY NOT ON TOP-LEVEL";
|
||||
|
||||
pub fn can_problem<'b>(
|
||||
alloc: &'b RocDocAllocator<'b>,
|
||||
@ -61,7 +62,7 @@ pub fn can_problem<'b>(
|
||||
let line =
|
||||
r#" then remove it so future readers of your code don't wonder why it is there."#;
|
||||
|
||||
doc = alloc.stack(vec![
|
||||
doc = alloc.stack([
|
||||
alloc
|
||||
.symbol_unqualified(symbol)
|
||||
.append(alloc.reflow(" is not used anywhere in your code.")),
|
||||
@ -76,14 +77,14 @@ pub fn can_problem<'b>(
|
||||
severity = Severity::Warning;
|
||||
}
|
||||
Problem::UnusedImport(module_id, region) => {
|
||||
doc = alloc.stack(vec![
|
||||
alloc.concat(vec![
|
||||
doc = alloc.stack([
|
||||
alloc.concat([
|
||||
alloc.reflow("Nothing from "),
|
||||
alloc.module(module_id),
|
||||
alloc.reflow(" is used in this module."),
|
||||
]),
|
||||
alloc.region(lines.convert_region(region)),
|
||||
alloc.concat(vec![
|
||||
alloc.concat([
|
||||
alloc.reflow("Since "),
|
||||
alloc.module(module_id),
|
||||
alloc.reflow(" isn't used, you don't need to import it."),
|
||||
@ -94,7 +95,7 @@ pub fn can_problem<'b>(
|
||||
severity = Severity::Warning;
|
||||
}
|
||||
Problem::ExposedButNotDefined(symbol) => {
|
||||
doc = alloc.stack(vec![
|
||||
doc = alloc.stack([
|
||||
alloc.symbol_unqualified(symbol).append(
|
||||
alloc.reflow(" is listed as exposed, but it isn't defined in this module."),
|
||||
),
|
||||
@ -110,7 +111,7 @@ pub fn can_problem<'b>(
|
||||
severity = Severity::RuntimeError;
|
||||
}
|
||||
Problem::UnknownGeneratesWith(loc_ident) => {
|
||||
doc = alloc.stack(vec![
|
||||
doc = alloc.stack([
|
||||
alloc
|
||||
.reflow("I don't know how to generate the ")
|
||||
.append(alloc.ident(loc_ident.value))
|
||||
@ -127,15 +128,15 @@ pub fn can_problem<'b>(
|
||||
Problem::UnusedArgument(closure_symbol, argument_symbol, region) => {
|
||||
let line = "\". Adding an underscore at the start of a variable name is a way of saying that the variable is not used.";
|
||||
|
||||
doc = alloc.stack(vec![
|
||||
alloc.concat(vec![
|
||||
doc = alloc.stack([
|
||||
alloc.concat([
|
||||
alloc.symbol_unqualified(closure_symbol),
|
||||
alloc.reflow(" doesn't use "),
|
||||
alloc.symbol_unqualified(argument_symbol),
|
||||
alloc.text("."),
|
||||
]),
|
||||
alloc.region(lines.convert_region(region)),
|
||||
alloc.concat(vec![
|
||||
alloc.concat([
|
||||
alloc.reflow("If you don't need "),
|
||||
alloc.symbol_unqualified(argument_symbol),
|
||||
alloc.reflow(", then you can just remove it. However, if you really do need "),
|
||||
@ -152,9 +153,9 @@ pub fn can_problem<'b>(
|
||||
severity = Severity::Warning;
|
||||
}
|
||||
Problem::PrecedenceProblem(BothNonAssociative(region, left_bin_op, right_bin_op)) => {
|
||||
doc = alloc.stack(vec![
|
||||
doc = alloc.stack([
|
||||
if left_bin_op.value == right_bin_op.value {
|
||||
alloc.concat(vec![
|
||||
alloc.concat([
|
||||
alloc.reflow("Using more than one "),
|
||||
alloc.binop(left_bin_op.value),
|
||||
alloc.reflow(concat!(
|
||||
@ -163,7 +164,7 @@ pub fn can_problem<'b>(
|
||||
)),
|
||||
])
|
||||
} else {
|
||||
alloc.concat(vec![
|
||||
alloc.concat([
|
||||
alloc.reflow("Using "),
|
||||
alloc.binop(left_bin_op.value),
|
||||
alloc.reflow(" and "),
|
||||
@ -181,7 +182,7 @@ pub fn can_problem<'b>(
|
||||
severity = Severity::RuntimeError;
|
||||
}
|
||||
Problem::UnsupportedPattern(BadPattern::UnderscoreInDef, region) => {
|
||||
doc = alloc.stack(vec![
|
||||
doc = alloc.stack([
|
||||
alloc.reflow("Underscore patterns are not allowed in definitions"),
|
||||
alloc.region(lines.convert_region(region)),
|
||||
]);
|
||||
@ -209,7 +210,7 @@ pub fn can_problem<'b>(
|
||||
alloc.reflow(" instead."),
|
||||
];
|
||||
|
||||
doc = alloc.stack(vec![
|
||||
doc = alloc.stack([
|
||||
alloc
|
||||
.reflow("This pattern is not allowed in ")
|
||||
.append(alloc.reflow(this_thing)),
|
||||
@ -242,8 +243,8 @@ pub fn can_problem<'b>(
|
||||
variable_region,
|
||||
variable_name,
|
||||
} => {
|
||||
doc = alloc.stack(vec![
|
||||
alloc.concat(vec![
|
||||
doc = alloc.stack([
|
||||
alloc.concat([
|
||||
alloc.reflow("The "),
|
||||
alloc.type_variable(variable_name),
|
||||
alloc.reflow(" type parameter is not used in the "),
|
||||
@ -270,13 +271,13 @@ pub fn can_problem<'b>(
|
||||
} => {
|
||||
let mut stack = Vec::with_capacity(4);
|
||||
if num_unbound == 1 {
|
||||
stack.push(alloc.concat(vec![
|
||||
stack.push(alloc.concat([
|
||||
alloc.reflow("The definition of "),
|
||||
alloc.symbol_unqualified(alias),
|
||||
alloc.reflow(" has an unbound type variable:"),
|
||||
]));
|
||||
} else {
|
||||
stack.push(alloc.concat(vec![
|
||||
stack.push(alloc.concat([
|
||||
alloc.reflow("The definition of "),
|
||||
alloc.symbol_unqualified(alias),
|
||||
alloc.reflow(" has "),
|
||||
@ -286,7 +287,7 @@ pub fn can_problem<'b>(
|
||||
stack.push(alloc.reflow("Here is one occurrence:"));
|
||||
}
|
||||
stack.push(alloc.region(lines.convert_region(one_occurrence)));
|
||||
stack.push(alloc.tip().append(alloc.concat(vec![
|
||||
stack.push(alloc.tip().append(alloc.concat([
|
||||
alloc.reflow("Type variables must be bound before the "),
|
||||
alloc.keyword(match kind {
|
||||
AliasKind::Structural => ":",
|
||||
@ -310,8 +311,8 @@ pub fn can_problem<'b>(
|
||||
record_region,
|
||||
replaced_region,
|
||||
} => {
|
||||
doc = alloc.stack(vec![
|
||||
alloc.concat(vec![
|
||||
doc = alloc.stack([
|
||||
alloc.concat([
|
||||
alloc.reflow("This record defines the "),
|
||||
alloc.record_field(field_name.clone()),
|
||||
alloc.reflow(" field twice!"),
|
||||
@ -329,7 +330,7 @@ pub fn can_problem<'b>(
|
||||
lines.convert_region(field_region),
|
||||
Annotation::TypoSuggestion,
|
||||
),
|
||||
alloc.concat(vec![
|
||||
alloc.concat([
|
||||
alloc.reflow("For clarity, remove the previous "),
|
||||
alloc.record_field(field_name),
|
||||
alloc.reflow(" definitions from this record."),
|
||||
@ -359,8 +360,8 @@ pub fn can_problem<'b>(
|
||||
record_region,
|
||||
replaced_region,
|
||||
} => {
|
||||
doc = alloc.stack(vec![
|
||||
alloc.concat(vec![
|
||||
doc = alloc.stack([
|
||||
alloc.concat([
|
||||
alloc.reflow("This record type defines the "),
|
||||
alloc.record_field(field_name.clone()),
|
||||
alloc.reflow(" field twice!"),
|
||||
@ -378,7 +379,7 @@ pub fn can_problem<'b>(
|
||||
lines.convert_region(field_region),
|
||||
Annotation::TypoSuggestion,
|
||||
),
|
||||
alloc.concat(vec![
|
||||
alloc.concat([
|
||||
alloc.reflow("For clarity, remove the previous "),
|
||||
alloc.record_field(field_name),
|
||||
alloc.reflow(" definitions from this record type."),
|
||||
@ -394,8 +395,8 @@ pub fn can_problem<'b>(
|
||||
tag_region,
|
||||
replaced_region,
|
||||
} => {
|
||||
doc = alloc.stack(vec![
|
||||
alloc.concat(vec![
|
||||
doc = alloc.stack([
|
||||
alloc.concat([
|
||||
alloc.reflow("This tag union type defines the "),
|
||||
alloc.tag_name(tag_name.clone()),
|
||||
alloc.reflow(" tag twice!"),
|
||||
@ -413,7 +414,7 @@ pub fn can_problem<'b>(
|
||||
lines.convert_region(tag_region),
|
||||
Annotation::TypoSuggestion,
|
||||
),
|
||||
alloc.concat(vec![
|
||||
alloc.concat([
|
||||
alloc.reflow("For clarity, remove the previous "),
|
||||
alloc.tag_name(tag_name),
|
||||
alloc.reflow(" definitions from this tag union type."),
|
||||
@ -427,7 +428,7 @@ pub fn can_problem<'b>(
|
||||
ref annotation_pattern,
|
||||
ref def_pattern,
|
||||
} => {
|
||||
doc = alloc.stack(vec![
|
||||
doc = alloc.stack([
|
||||
alloc.reflow(
|
||||
"This annotation does not match the definition immediately following it:",
|
||||
),
|
||||
@ -444,14 +445,14 @@ pub fn can_problem<'b>(
|
||||
alias_name: type_name,
|
||||
region,
|
||||
} => {
|
||||
doc = alloc.stack(vec![
|
||||
alloc.concat(vec![
|
||||
doc = alloc.stack([
|
||||
alloc.concat([
|
||||
alloc.reflow("This pattern in the definition of "),
|
||||
alloc.symbol_unqualified(type_name),
|
||||
alloc.reflow(" is not what I expect:"),
|
||||
]),
|
||||
alloc.region(lines.convert_region(region)),
|
||||
alloc.concat(vec![
|
||||
alloc.concat([
|
||||
alloc.reflow("Only type variables like "),
|
||||
alloc.type_variable("a".into()),
|
||||
alloc.reflow(" or "),
|
||||
@ -464,10 +465,10 @@ pub fn can_problem<'b>(
|
||||
severity = Severity::RuntimeError;
|
||||
}
|
||||
Problem::InvalidHexadecimal(region) => {
|
||||
doc = alloc.stack(vec![
|
||||
doc = alloc.stack([
|
||||
alloc.reflow("This unicode code point is invalid:"),
|
||||
alloc.region(lines.convert_region(region)),
|
||||
alloc.concat(vec![
|
||||
alloc.concat([
|
||||
alloc.reflow(r"I was expecting a hexadecimal number, like "),
|
||||
alloc.parser_suggestion("\\u(1100)"),
|
||||
alloc.reflow(" or "),
|
||||
@ -481,7 +482,7 @@ pub fn can_problem<'b>(
|
||||
severity = Severity::RuntimeError;
|
||||
}
|
||||
Problem::InvalidUnicodeCodePt(region) => {
|
||||
doc = alloc.stack(vec![
|
||||
doc = alloc.stack([
|
||||
alloc.reflow("This unicode code point is invalid:"),
|
||||
alloc.region(lines.convert_region(region)),
|
||||
alloc.reflow("Learn more about working with unicode in roc at TODO"),
|
||||
@ -491,10 +492,10 @@ pub fn can_problem<'b>(
|
||||
severity = Severity::RuntimeError;
|
||||
}
|
||||
Problem::InvalidInterpolation(region) => {
|
||||
doc = alloc.stack(vec![
|
||||
doc = alloc.stack([
|
||||
alloc.reflow("This string interpolation is invalid:"),
|
||||
alloc.region(lines.convert_region(region)),
|
||||
alloc.concat(vec![
|
||||
alloc.concat([
|
||||
alloc.reflow(r"I was expecting an identifier, like "),
|
||||
alloc.parser_suggestion("\\u(message)"),
|
||||
alloc.reflow(" or "),
|
||||
@ -519,20 +520,20 @@ pub fn can_problem<'b>(
|
||||
def_region,
|
||||
differing_recursion_region,
|
||||
} => {
|
||||
doc = alloc.stack(vec![
|
||||
alloc.concat(vec![
|
||||
doc = alloc.stack([
|
||||
alloc.concat([
|
||||
alloc.symbol_unqualified(alias),
|
||||
alloc.reflow(" is a nested datatype. Here is one recursive usage of it:"),
|
||||
]),
|
||||
alloc.region(lines.convert_region(differing_recursion_region)),
|
||||
alloc.concat(vec![
|
||||
alloc.concat([
|
||||
alloc.reflow("But recursive usages of "),
|
||||
alloc.symbol_unqualified(alias),
|
||||
alloc.reflow(" must match its definition:"),
|
||||
]),
|
||||
alloc.region(lines.convert_region(def_region)),
|
||||
alloc.reflow("Nested datatypes are not supported in Roc."),
|
||||
alloc.concat(vec![
|
||||
alloc.concat([
|
||||
alloc.hint("Consider rewriting the definition of "),
|
||||
alloc.symbol_unqualified(alias),
|
||||
alloc.text(" to use the recursive type with the same arguments."),
|
||||
@ -551,14 +552,14 @@ pub fn can_problem<'b>(
|
||||
}
|
||||
};
|
||||
|
||||
doc = alloc.stack(vec![
|
||||
alloc.concat(vec![
|
||||
doc = alloc.stack([
|
||||
alloc.concat([
|
||||
alloc.reflow("This "),
|
||||
alloc.text(kind_str),
|
||||
alloc.reflow(" extension type is invalid:"),
|
||||
]),
|
||||
alloc.region(lines.convert_region(region)),
|
||||
alloc.concat(vec![
|
||||
alloc.concat([
|
||||
alloc.note("A "),
|
||||
alloc.reflow(kind_str),
|
||||
alloc.reflow(" extension variable can only contain "),
|
||||
@ -575,8 +576,8 @@ pub fn can_problem<'b>(
|
||||
name,
|
||||
variables_region,
|
||||
} => {
|
||||
doc = alloc.stack(vec![
|
||||
alloc.concat(vec![
|
||||
doc = alloc.stack([
|
||||
alloc.concat([
|
||||
alloc.reflow("The definition of the "),
|
||||
alloc.symbol_unqualified(name),
|
||||
alloc.reflow(" ability includes type variables:"),
|
||||
@ -593,7 +594,7 @@ pub fn can_problem<'b>(
|
||||
Problem::HasClauseIsNotAbility {
|
||||
region: clause_region,
|
||||
} => {
|
||||
doc = alloc.stack(vec![
|
||||
doc = alloc.stack([
|
||||
alloc.reflow(r#"The type referenced in this "has" clause is not an ability:"#),
|
||||
alloc.region(lines.convert_region(clause_region)),
|
||||
]);
|
||||
@ -608,8 +609,8 @@ pub fn can_problem<'b>(
|
||||
},
|
||||
ability,
|
||||
} => {
|
||||
doc = alloc.stack(vec![
|
||||
alloc.concat(vec![
|
||||
doc = alloc.stack([
|
||||
alloc.concat([
|
||||
alloc.reflow("The definition of the "),
|
||||
alloc.symbol_unqualified(name),
|
||||
alloc.reflow(" aliases references the ability "),
|
||||
@ -617,12 +618,12 @@ pub fn can_problem<'b>(
|
||||
alloc.reflow(":"),
|
||||
]),
|
||||
alloc.region(lines.convert_region(region)),
|
||||
alloc.concat(vec![
|
||||
alloc.concat([
|
||||
alloc.reflow("Abilities are not types, but you can add an ability constraint to a type variable "),
|
||||
alloc.type_variable("a".into()),
|
||||
alloc.reflow(" by writing"),
|
||||
]),
|
||||
alloc.type_block(alloc.concat(vec![
|
||||
alloc.type_block(alloc.concat([
|
||||
alloc.reflow("| a has "),
|
||||
alloc.symbol_unqualified(ability),
|
||||
])),
|
||||
@ -633,14 +634,14 @@ pub fn can_problem<'b>(
|
||||
}
|
||||
|
||||
Problem::IllegalHasClause { region } => {
|
||||
doc = alloc.stack(vec![
|
||||
alloc.concat(vec![
|
||||
doc = alloc.stack([
|
||||
alloc.concat([
|
||||
alloc.reflow("A "),
|
||||
alloc.keyword("has"),
|
||||
alloc.reflow(" clause is not allowed here:"),
|
||||
]),
|
||||
alloc.region(lines.convert_region(region)),
|
||||
alloc.concat(vec![
|
||||
alloc.concat([
|
||||
alloc.keyword("has"),
|
||||
alloc.reflow(" clauses can only be specified on the top-level type annotation of an ability member."),
|
||||
]),
|
||||
@ -654,8 +655,8 @@ pub fn can_problem<'b>(
|
||||
ability,
|
||||
region,
|
||||
} => {
|
||||
doc = alloc.stack(vec![
|
||||
alloc.concat(vec![
|
||||
doc = alloc.stack([
|
||||
alloc.concat([
|
||||
alloc.reflow("The definition of the ability member "),
|
||||
alloc.symbol_unqualified(member),
|
||||
alloc.reflow(" does not include a "),
|
||||
@ -665,21 +666,20 @@ pub fn can_problem<'b>(
|
||||
alloc.reflow(":"),
|
||||
]),
|
||||
alloc.region(lines.convert_region(region)),
|
||||
alloc.concat(vec![
|
||||
alloc.concat([
|
||||
alloc.reflow("Ability members must include a "),
|
||||
alloc.keyword("has"),
|
||||
alloc.reflow(" clause binding a type variable to an ability, like"),
|
||||
]),
|
||||
alloc.type_block(alloc.concat(vec![
|
||||
alloc.type_block(alloc.concat([
|
||||
alloc.type_variable("a".into()),
|
||||
alloc.space(),
|
||||
alloc.keyword("has"),
|
||||
alloc.space(),
|
||||
alloc.symbol_unqualified(ability),
|
||||
])),
|
||||
alloc.concat(vec![alloc.reflow(
|
||||
"Otherwise, the function does not need to be part of the ability!",
|
||||
)]),
|
||||
alloc.concat([alloc
|
||||
.reflow("Otherwise, the function does not need to be part of the ability!")]),
|
||||
]);
|
||||
title = ABILITY_MEMBER_MISSING_HAS_CLAUSE.to_string();
|
||||
severity = Severity::RuntimeError;
|
||||
@ -691,8 +691,8 @@ pub fn can_problem<'b>(
|
||||
span_has_clauses,
|
||||
mut bound_var_names,
|
||||
} => {
|
||||
doc = alloc.stack(vec![
|
||||
alloc.concat(vec![
|
||||
doc = alloc.stack([
|
||||
alloc.concat([
|
||||
alloc.reflow("The definition of the ability member "),
|
||||
alloc.symbol_unqualified(member),
|
||||
alloc.reflow(" includes multiple variables bound to the "),
|
||||
@ -701,7 +701,7 @@ pub fn can_problem<'b>(
|
||||
]),
|
||||
alloc.region(lines.convert_region(span_has_clauses)),
|
||||
alloc.reflow("Ability members can only bind one type variable to their parent ability. Otherwise, I wouldn't know what type implements an ability by looking at specializations!"),
|
||||
alloc.concat(vec![
|
||||
alloc.concat([
|
||||
alloc.hint("Did you mean to only bind "),
|
||||
alloc.type_variable(bound_var_names.swap_remove(0)),
|
||||
alloc.reflow(" to "),
|
||||
@ -718,15 +718,15 @@ pub fn can_problem<'b>(
|
||||
ability,
|
||||
region,
|
||||
} => {
|
||||
doc = alloc.stack(vec![
|
||||
alloc.concat(vec![
|
||||
doc = alloc.stack([
|
||||
alloc.concat([
|
||||
alloc.reflow("The definition of the ability member "),
|
||||
alloc.symbol_unqualified(member),
|
||||
alloc.reflow(" includes a has clause binding an ability it is not a part of:"),
|
||||
]),
|
||||
alloc.region(lines.convert_region(region)),
|
||||
alloc.reflow("Currently, ability members can only bind variables to the ability they are a part of."),
|
||||
alloc.concat(vec![
|
||||
alloc.concat([
|
||||
alloc.hint(""),
|
||||
alloc.reflow("Did you mean to bind the "),
|
||||
alloc.symbol_unqualified(ability),
|
||||
@ -736,6 +736,18 @@ pub fn can_problem<'b>(
|
||||
title = ABILITY_MEMBER_HAS_EXTRANEOUS_HAS_CLAUSE.to_string();
|
||||
severity = Severity::RuntimeError;
|
||||
}
|
||||
|
||||
Problem::AbilityNotOnToplevel { region } => {
|
||||
doc = alloc.stack(vec![
|
||||
alloc.concat(vec![alloc.reflow(
|
||||
"This ability definition is not on the top-level of a module:",
|
||||
)]),
|
||||
alloc.region(lines.convert_region(region)),
|
||||
alloc.reflow("Abilities can only be defined on the top-level of a Roc module."),
|
||||
]);
|
||||
title = ABILITY_NOT_ON_TOPLEVEL.to_string();
|
||||
severity = Severity::RuntimeError;
|
||||
}
|
||||
};
|
||||
|
||||
Report {
|
||||
@ -777,8 +789,8 @@ fn to_invalid_optional_value_report_help<'b>(
|
||||
field_region: Region,
|
||||
record_region: Region,
|
||||
) -> RocDocBuilder<'b> {
|
||||
alloc.stack(vec![
|
||||
alloc.concat(vec![
|
||||
alloc.stack([
|
||||
alloc.concat([
|
||||
alloc.reflow("This record uses an optional value for the "),
|
||||
alloc.record_field(field_name),
|
||||
alloc.reflow(" field in an incorrect context!"),
|
||||
@ -809,10 +821,10 @@ fn to_bad_ident_expr_report<'b>(
|
||||
WeirdDotAccess(pos) | StrayDot(pos) => {
|
||||
let region = LineColumnRegion::from_pos(lines.convert_pos(pos));
|
||||
|
||||
alloc.stack(vec![
|
||||
alloc.stack([
|
||||
alloc.reflow(r"I trying to parse a record field access here:"),
|
||||
alloc.region_with_subregion(lines.convert_region(surroundings), region),
|
||||
alloc.concat(vec![
|
||||
alloc.concat([
|
||||
alloc.reflow("So I expect to see a lowercase letter next, like "),
|
||||
alloc.parser_suggestion(".name"),
|
||||
alloc.reflow(" or "),
|
||||
@ -822,10 +834,10 @@ fn to_bad_ident_expr_report<'b>(
|
||||
])
|
||||
}
|
||||
|
||||
WeirdAccessor(_pos) => alloc.stack(vec![
|
||||
WeirdAccessor(_pos) => alloc.stack([
|
||||
alloc.reflow("I am very confused by this field access"),
|
||||
alloc.region(lines.convert_region(surroundings)),
|
||||
alloc.concat(vec![
|
||||
alloc.concat([
|
||||
alloc.reflow("It looks like a field access on an accessor. I parse"),
|
||||
alloc.parser_suggestion(".client.name"),
|
||||
alloc.reflow(" as "),
|
||||
@ -840,10 +852,10 @@ fn to_bad_ident_expr_report<'b>(
|
||||
WeirdDotQualified(pos) => {
|
||||
let region = LineColumnRegion::from_pos(lines.convert_pos(pos));
|
||||
|
||||
alloc.stack(vec![
|
||||
alloc.stack([
|
||||
alloc.reflow("I am trying to parse a qualified name here:"),
|
||||
alloc.region_with_subregion(lines.convert_region(surroundings), region),
|
||||
alloc.concat(vec![
|
||||
alloc.concat([
|
||||
alloc.reflow("I was expecting to see an identifier next, like "),
|
||||
alloc.parser_suggestion("height"),
|
||||
alloc.reflow(". A complete qualified name looks something like "),
|
||||
@ -855,10 +867,10 @@ fn to_bad_ident_expr_report<'b>(
|
||||
QualifiedTag(pos) => {
|
||||
let region = LineColumnRegion::from_pos(lines.convert_pos(pos));
|
||||
|
||||
alloc.stack(vec![
|
||||
alloc.stack([
|
||||
alloc.reflow("I am trying to parse a qualified name here:"),
|
||||
alloc.region_with_subregion(lines.convert_region(surroundings), region),
|
||||
alloc.concat(vec![
|
||||
alloc.concat([
|
||||
alloc.reflow(r"This looks like a qualified tag name to me, "),
|
||||
alloc.reflow(r"but tags cannot be qualified! "),
|
||||
alloc.reflow(r"Maybe you wanted a qualified name, something like "),
|
||||
@ -870,13 +882,13 @@ fn to_bad_ident_expr_report<'b>(
|
||||
|
||||
Underscore(pos) => {
|
||||
let region = Region::new(surroundings.start(), pos);
|
||||
alloc.stack(vec![
|
||||
alloc.stack([
|
||||
alloc.reflow("Underscores are not allowed in identifier names:"),
|
||||
alloc.region_with_subregion(
|
||||
lines.convert_region(surroundings),
|
||||
lines.convert_region(region),
|
||||
),
|
||||
alloc.concat(vec![alloc.reflow(
|
||||
alloc.concat([alloc.reflow(
|
||||
r"I recommend using camelCase, it is the standard in the Roc ecosystem.",
|
||||
)]),
|
||||
])
|
||||
@ -892,13 +904,13 @@ fn to_bad_ident_expr_report<'b>(
|
||||
match what_is_next(alloc.src_lines, lines.convert_pos(pos)) {
|
||||
LowercaseAccess(width) => {
|
||||
let region = Region::new(pos, pos.bump_column(width));
|
||||
alloc.stack(vec![
|
||||
alloc.stack([
|
||||
alloc.reflow("I am very confused by this field access:"),
|
||||
alloc.region_with_subregion(
|
||||
lines.convert_region(surroundings),
|
||||
lines.convert_region(region),
|
||||
),
|
||||
alloc.concat(vec![
|
||||
alloc.concat([
|
||||
alloc.reflow(r"It looks like a record field access on "),
|
||||
alloc.reflow(kind),
|
||||
alloc.text("."),
|
||||
@ -907,13 +919,13 @@ fn to_bad_ident_expr_report<'b>(
|
||||
}
|
||||
UppercaseAccess(width) => {
|
||||
let region = Region::new(pos, pos.bump_column(width));
|
||||
alloc.stack(vec![
|
||||
alloc.stack([
|
||||
alloc.reflow("I am very confused by this expression:"),
|
||||
alloc.region_with_subregion(
|
||||
lines.convert_region(surroundings),
|
||||
lines.convert_region(region),
|
||||
),
|
||||
alloc.concat(vec![
|
||||
alloc.concat([
|
||||
alloc.reflow(r"Looks like "),
|
||||
alloc.reflow(kind),
|
||||
alloc.reflow(" is treated like a module name. "),
|
||||
@ -926,8 +938,8 @@ fn to_bad_ident_expr_report<'b>(
|
||||
Other(Some(c)) if c.is_lowercase() => {
|
||||
let region =
|
||||
Region::new(surroundings.start().bump_column(1), pos.bump_column(1));
|
||||
alloc.stack(vec![
|
||||
alloc.concat(vec![
|
||||
alloc.stack([
|
||||
alloc.concat([
|
||||
alloc.reflow("I am trying to parse "),
|
||||
alloc.reflow(kind),
|
||||
alloc.reflow(" here:"),
|
||||
@ -936,7 +948,7 @@ fn to_bad_ident_expr_report<'b>(
|
||||
lines.convert_region(surroundings),
|
||||
lines.convert_region(region),
|
||||
),
|
||||
alloc.concat(vec![
|
||||
alloc.concat([
|
||||
alloc.reflow(r"But after the "),
|
||||
alloc.keyword("@"),
|
||||
alloc.reflow(r" symbol I found a lowercase letter. "),
|
||||
@ -968,10 +980,10 @@ fn to_bad_ident_pattern_report<'b>(
|
||||
WeirdDotAccess(pos) | StrayDot(pos) => {
|
||||
let region = LineColumnRegion::from_pos(lines.convert_pos(pos));
|
||||
|
||||
alloc.stack(vec![
|
||||
alloc.stack([
|
||||
alloc.reflow(r"I trying to parse a record field accessor here:"),
|
||||
alloc.region_with_subregion(lines.convert_region(surroundings), region),
|
||||
alloc.concat(vec![
|
||||
alloc.concat([
|
||||
alloc.reflow("Something like "),
|
||||
alloc.parser_suggestion(".name"),
|
||||
alloc.reflow(" or "),
|
||||
@ -981,10 +993,10 @@ fn to_bad_ident_pattern_report<'b>(
|
||||
])
|
||||
}
|
||||
|
||||
WeirdAccessor(_pos) => alloc.stack(vec![
|
||||
WeirdAccessor(_pos) => alloc.stack([
|
||||
alloc.reflow("I am very confused by this field access"),
|
||||
alloc.region(lines.convert_region(surroundings)),
|
||||
alloc.concat(vec![
|
||||
alloc.concat([
|
||||
alloc.reflow("It looks like a field access on an accessor. I parse"),
|
||||
alloc.parser_suggestion(".client.name"),
|
||||
alloc.reflow(" as "),
|
||||
@ -999,10 +1011,10 @@ fn to_bad_ident_pattern_report<'b>(
|
||||
WeirdDotQualified(pos) => {
|
||||
let region = LineColumnRegion::from_pos(lines.convert_pos(pos));
|
||||
|
||||
alloc.stack(vec![
|
||||
alloc.stack([
|
||||
alloc.reflow("I am trying to parse a qualified name here:"),
|
||||
alloc.region_with_subregion(lines.convert_region(surroundings), region),
|
||||
alloc.concat(vec![
|
||||
alloc.concat([
|
||||
alloc.reflow("I was expecting to see an identifier next, like "),
|
||||
alloc.parser_suggestion("height"),
|
||||
alloc.reflow(". A complete qualified name looks something like "),
|
||||
@ -1014,10 +1026,10 @@ fn to_bad_ident_pattern_report<'b>(
|
||||
QualifiedTag(pos) => {
|
||||
let region = LineColumnRegion::from_pos(lines.convert_pos(pos));
|
||||
|
||||
alloc.stack(vec![
|
||||
alloc.stack([
|
||||
alloc.reflow("I am trying to parse a qualified name here:"),
|
||||
alloc.region_with_subregion(lines.convert_region(surroundings), region),
|
||||
alloc.concat(vec![
|
||||
alloc.concat([
|
||||
alloc.reflow(r"This looks like a qualified tag name to me, "),
|
||||
alloc.reflow(r"but tags cannot be qualified! "),
|
||||
alloc.reflow(r"Maybe you wanted a qualified name, something like "),
|
||||
@ -1030,13 +1042,13 @@ fn to_bad_ident_pattern_report<'b>(
|
||||
Underscore(pos) => {
|
||||
let region = Region::from_pos(pos.sub(1));
|
||||
|
||||
alloc.stack(vec![
|
||||
alloc.stack([
|
||||
alloc.reflow("I am trying to parse an identifier here:"),
|
||||
alloc.region_with_subregion(
|
||||
lines.convert_region(surroundings),
|
||||
lines.convert_region(region),
|
||||
),
|
||||
alloc.concat(vec![alloc.reflow(
|
||||
alloc.concat([alloc.reflow(
|
||||
r"Underscores are not allowed in identifiers. Use camelCase instead!",
|
||||
)]),
|
||||
])
|
||||
@ -1123,7 +1135,7 @@ fn report_shadowing<'b>(
|
||||
ShadowKind::Ability => "abilities",
|
||||
};
|
||||
|
||||
alloc.stack(vec![
|
||||
alloc.stack([
|
||||
alloc
|
||||
.text("The ")
|
||||
.append(alloc.ident(shadow.value))
|
||||
@ -1131,7 +1143,7 @@ fn report_shadowing<'b>(
|
||||
alloc.region(lines.convert_region(original_region)),
|
||||
alloc.reflow("But then it's defined a second time here:"),
|
||||
alloc.region(lines.convert_region(shadow.region)),
|
||||
alloc.concat(vec![
|
||||
alloc.concat([
|
||||
alloc.reflow("Since these "),
|
||||
alloc.reflow(what),
|
||||
alloc.reflow(" have the same name, it's easy to use the wrong one on accident. Give one of them a new name."),
|
||||
@ -1218,8 +1230,8 @@ fn pretty_runtime_error<'b>(
|
||||
),
|
||||
};
|
||||
|
||||
doc = alloc.stack(vec![
|
||||
alloc.concat(vec![
|
||||
doc = alloc.stack([
|
||||
alloc.concat([
|
||||
alloc.reflow("This"),
|
||||
alloc.text(name),
|
||||
alloc.reflow("pattern is malformed:"),
|
||||
@ -1243,7 +1255,7 @@ fn pretty_runtime_error<'b>(
|
||||
suggestions.truncate(4);
|
||||
|
||||
let did_you_mean = if suggestions.is_empty() {
|
||||
alloc.concat(vec![
|
||||
alloc.concat([
|
||||
alloc.reflow("In fact, it looks like "),
|
||||
alloc.module_name(module_name.clone()),
|
||||
alloc.reflow(" doesn't expose any values!"),
|
||||
@ -1252,13 +1264,13 @@ fn pretty_runtime_error<'b>(
|
||||
let qualified_suggestions = suggestions
|
||||
.into_iter()
|
||||
.map(|v| alloc.string(module_name.to_string() + "." + v.as_str()));
|
||||
alloc.stack(vec![
|
||||
alloc.stack([
|
||||
alloc.reflow("Did you mean one of these?"),
|
||||
alloc.vcat(qualified_suggestions).indent(4),
|
||||
])
|
||||
};
|
||||
doc = alloc.stack(vec![
|
||||
alloc.concat(vec![
|
||||
doc = alloc.stack([
|
||||
alloc.concat([
|
||||
alloc.reflow("The "),
|
||||
alloc.module_name(module_name),
|
||||
alloc.reflow(" module does not expose `"),
|
||||
@ -1299,10 +1311,10 @@ fn pretty_runtime_error<'b>(
|
||||
title = SYNTAX_PROBLEM;
|
||||
}
|
||||
RuntimeError::MalformedTypeName(_box_str, surroundings) => {
|
||||
doc = alloc.stack(vec![
|
||||
doc = alloc.stack([
|
||||
alloc.reflow(r"I am confused by this type name:"),
|
||||
alloc.region(lines.convert_region(surroundings)),
|
||||
alloc.concat(vec![
|
||||
alloc.concat([
|
||||
alloc.reflow("Type names start with an uppercase letter, "),
|
||||
alloc.reflow("and can optionally be qualified by a module name, like "),
|
||||
alloc.parser_suggestion("Bool"),
|
||||
@ -1329,14 +1341,14 @@ fn pretty_runtime_error<'b>(
|
||||
"small"
|
||||
};
|
||||
|
||||
doc = alloc.stack(vec![
|
||||
alloc.concat(vec![
|
||||
doc = alloc.stack([
|
||||
alloc.concat([
|
||||
alloc.reflow("This float literal is too "),
|
||||
alloc.text(big_or_small),
|
||||
alloc.reflow(":"),
|
||||
]),
|
||||
alloc.region(lines.convert_region(region)),
|
||||
alloc.concat(vec![
|
||||
alloc.concat([
|
||||
alloc
|
||||
.reflow("Roc uses signed 64-bit floating points, allowing values between "),
|
||||
alloc.text(format!("{:e}", f64::MIN)),
|
||||
@ -1353,12 +1365,12 @@ fn pretty_runtime_error<'b>(
|
||||
.tip()
|
||||
.append(alloc.reflow("Learn more about number literals at TODO"));
|
||||
|
||||
doc = alloc.stack(vec![
|
||||
alloc.concat(vec![
|
||||
doc = alloc.stack([
|
||||
alloc.concat([
|
||||
alloc.reflow("This float literal contains an invalid digit:"),
|
||||
]),
|
||||
alloc.region(lines.convert_region(region)),
|
||||
alloc.concat(vec![
|
||||
alloc.concat([
|
||||
alloc.reflow("Floating point literals can only contain the digits 0-9, or use scientific notation 10e4, or have a float suffix."),
|
||||
]),
|
||||
tip,
|
||||
@ -1367,10 +1379,10 @@ fn pretty_runtime_error<'b>(
|
||||
title = SYNTAX_PROBLEM;
|
||||
}
|
||||
RuntimeError::InvalidFloat(FloatErrorKind::IntSuffix, region, _raw_str) => {
|
||||
doc = alloc.stack(vec![
|
||||
alloc.concat(vec![alloc.reflow(
|
||||
"This number literal is a float, but it has an integer suffix:",
|
||||
)]),
|
||||
doc = alloc.stack([
|
||||
alloc
|
||||
.concat([alloc
|
||||
.reflow("This number literal is a float, but it has an integer suffix:")]),
|
||||
alloc.region(lines.convert_region(region)),
|
||||
]);
|
||||
|
||||
@ -1417,8 +1429,8 @@ fn pretty_runtime_error<'b>(
|
||||
.tip()
|
||||
.append(alloc.reflow("Learn more about number literals at TODO"));
|
||||
|
||||
doc = alloc.stack(vec![
|
||||
alloc.concat(vec![
|
||||
doc = alloc.stack([
|
||||
alloc.concat([
|
||||
alloc.reflow("This "),
|
||||
alloc.text(name),
|
||||
alloc.reflow(" literal contains "),
|
||||
@ -1426,7 +1438,7 @@ fn pretty_runtime_error<'b>(
|
||||
alloc.text(":"),
|
||||
]),
|
||||
alloc.region(lines.convert_region(region)),
|
||||
alloc.concat(vec![
|
||||
alloc.concat([
|
||||
alloc.text(plurals),
|
||||
contains,
|
||||
alloc.text(charset),
|
||||
@ -1442,7 +1454,7 @@ fn pretty_runtime_error<'b>(
|
||||
let (big_or_small, info) = if let IntErrorKind::Underflow = error_kind {
|
||||
(
|
||||
"small",
|
||||
alloc.concat(vec![
|
||||
alloc.concat([
|
||||
alloc.reflow(
|
||||
"The smallest number representable in Roc is the minimum I128 value, ",
|
||||
),
|
||||
@ -1453,7 +1465,7 @@ fn pretty_runtime_error<'b>(
|
||||
} else {
|
||||
(
|
||||
"big",
|
||||
alloc.concat(vec![
|
||||
alloc.concat([
|
||||
alloc.reflow(
|
||||
"The largest number representable in Roc is the maximum U128 value, ",
|
||||
),
|
||||
@ -1467,8 +1479,8 @@ fn pretty_runtime_error<'b>(
|
||||
.tip()
|
||||
.append(alloc.reflow("Learn more about number literals at TODO"));
|
||||
|
||||
doc = alloc.stack(vec![
|
||||
alloc.concat(vec![
|
||||
doc = alloc.stack([
|
||||
alloc.concat([
|
||||
alloc.reflow("This integer literal is too "),
|
||||
alloc.text(big_or_small),
|
||||
alloc.reflow(":"),
|
||||
@ -1481,10 +1493,10 @@ fn pretty_runtime_error<'b>(
|
||||
title = SYNTAX_PROBLEM;
|
||||
}
|
||||
RuntimeError::InvalidInt(IntErrorKind::FloatSuffix, _base, region, _raw_str) => {
|
||||
doc = alloc.stack(vec![
|
||||
alloc.concat(vec![alloc.reflow(
|
||||
"This number literal is an integer, but it has a float suffix:",
|
||||
)]),
|
||||
doc = alloc.stack([
|
||||
alloc
|
||||
.concat([alloc
|
||||
.reflow("This number literal is an integer, but it has a float suffix:")]),
|
||||
alloc.region(lines.convert_region(region)),
|
||||
]);
|
||||
|
||||
@ -1499,12 +1511,11 @@ fn pretty_runtime_error<'b>(
|
||||
region,
|
||||
_raw_str,
|
||||
) => {
|
||||
doc = alloc.stack(vec![
|
||||
alloc.concat(vec![alloc.reflow(
|
||||
"This integer literal overflows the type indicated by its suffix:",
|
||||
)]),
|
||||
doc = alloc.stack([
|
||||
alloc.concat([alloc
|
||||
.reflow("This integer literal overflows the type indicated by its suffix:")]),
|
||||
alloc.region(lines.convert_region(region)),
|
||||
alloc.tip().append(alloc.concat(vec![
|
||||
alloc.tip().append(alloc.concat([
|
||||
alloc.reflow("The suffix indicates this integer is a "),
|
||||
alloc.type_str(suffix_type),
|
||||
alloc.reflow(", whose maximum value is "),
|
||||
@ -1524,12 +1535,11 @@ fn pretty_runtime_error<'b>(
|
||||
region,
|
||||
_raw_str,
|
||||
) => {
|
||||
doc = alloc.stack(vec![
|
||||
alloc.concat(vec![alloc.reflow(
|
||||
"This integer literal underflows the type indicated by its suffix:",
|
||||
)]),
|
||||
doc = alloc.stack([
|
||||
alloc.concat([alloc
|
||||
.reflow("This integer literal underflows the type indicated by its suffix:")]),
|
||||
alloc.region(lines.convert_region(region)),
|
||||
alloc.tip().append(alloc.concat(vec![
|
||||
alloc.tip().append(alloc.concat([
|
||||
alloc.reflow("The suffix indicates this integer is a "),
|
||||
alloc.type_str(suffix_type),
|
||||
alloc.reflow(", whose minimum value is "),
|
||||
@ -1556,8 +1566,8 @@ fn pretty_runtime_error<'b>(
|
||||
title = SYNTAX_PROBLEM;
|
||||
}
|
||||
RuntimeError::InvalidRecordUpdate { region } => {
|
||||
doc = alloc.stack(vec![
|
||||
alloc.concat(vec![
|
||||
doc = alloc.stack([
|
||||
alloc.concat([
|
||||
alloc.reflow("This expression cannot be updated"),
|
||||
alloc.reflow(":"),
|
||||
]),
|
||||
@ -1592,13 +1602,11 @@ fn pretty_runtime_error<'b>(
|
||||
unreachable!("not currently reported (but can blow up at runtime)")
|
||||
}
|
||||
RuntimeError::ExposedButNotDefined(symbol) => {
|
||||
doc = alloc.stack(vec![alloc
|
||||
doc = alloc.stack([alloc
|
||||
.symbol_unqualified(symbol)
|
||||
.append(alloc.reflow(" was listed as exposed in "))
|
||||
.append(alloc.module(symbol.module_id()))
|
||||
.append(
|
||||
alloc.reflow(", but it was not defined anywhere in that module."),
|
||||
)]);
|
||||
.append(alloc.reflow(", but it was not defined anywhere in that module."))]);
|
||||
|
||||
title = MISSING_DEFINITION;
|
||||
}
|
||||
@ -1607,8 +1615,8 @@ fn pretty_runtime_error<'b>(
|
||||
.tip()
|
||||
.append(alloc.reflow("Learn more about character literals at TODO"));
|
||||
|
||||
doc = alloc.stack(vec![
|
||||
alloc.concat(vec![alloc.reflow("This character literal is empty.")]),
|
||||
doc = alloc.stack([
|
||||
alloc.concat([alloc.reflow("This character literal is empty.")]),
|
||||
alloc.region(lines.convert_region(region)),
|
||||
tip,
|
||||
]);
|
||||
@ -1620,14 +1628,12 @@ fn pretty_runtime_error<'b>(
|
||||
.tip()
|
||||
.append(alloc.reflow("Learn more about character literals at TODO"));
|
||||
|
||||
doc = alloc.stack(vec![
|
||||
alloc.concat(vec![
|
||||
doc = alloc.stack([
|
||||
alloc.concat([
|
||||
alloc.reflow("This character literal contains more than one code point.")
|
||||
]),
|
||||
alloc.region(lines.convert_region(region)),
|
||||
alloc.concat(vec![
|
||||
alloc.reflow("Character literals can only contain one code point.")
|
||||
]),
|
||||
alloc.concat([alloc.reflow("Character literals can only contain one code point.")]),
|
||||
tip,
|
||||
]);
|
||||
|
||||
@ -1653,7 +1659,7 @@ fn pretty_runtime_error<'b>(
|
||||
} else {
|
||||
let qualified_suggestions =
|
||||
suggestions.into_iter().map(|v| alloc.string(v.to_string()));
|
||||
alloc.stack(vec![
|
||||
alloc.stack([
|
||||
alloc
|
||||
.tip()
|
||||
.append(alloc.reflow("Did you mean one of these opaque types?")),
|
||||
@ -1662,7 +1668,7 @@ fn pretty_runtime_error<'b>(
|
||||
};
|
||||
|
||||
let mut stack = vec![
|
||||
alloc.concat(vec![
|
||||
alloc.concat([
|
||||
alloc.reflow("The opaque type "),
|
||||
alloc.type_str(opaque.as_inline_str().as_str()),
|
||||
alloc.reflow(" referenced here is not defined:"),
|
||||
@ -1671,7 +1677,7 @@ fn pretty_runtime_error<'b>(
|
||||
];
|
||||
|
||||
if let Some(defined_alias_region) = opt_defined_alias {
|
||||
stack.push(alloc.stack(vec![
|
||||
stack.push(alloc.stack([
|
||||
alloc.note("There is an alias of the same name:"),
|
||||
alloc.region(lines.convert_region(defined_alias_region)),
|
||||
]));
|
||||
@ -1688,8 +1694,8 @@ fn pretty_runtime_error<'b>(
|
||||
referenced_region,
|
||||
imported_region,
|
||||
} => {
|
||||
doc = alloc.stack(vec![
|
||||
alloc.concat(vec![
|
||||
doc = alloc.stack([
|
||||
alloc.concat([
|
||||
alloc.reflow("The unwrapped opaque type "),
|
||||
alloc.type_str(opaque.as_inline_str().as_str()),
|
||||
alloc.reflow(" referenced here:"),
|
||||
@ -1705,7 +1711,7 @@ fn pretty_runtime_error<'b>(
|
||||
title = OPAQUE_DECLARED_OUTSIDE_SCOPE;
|
||||
}
|
||||
RuntimeError::OpaqueNotApplied(loc_ident) => {
|
||||
doc = alloc.stack(vec![
|
||||
doc = alloc.stack([
|
||||
alloc.reflow("This opaque type is not applied to an argument:"),
|
||||
alloc.region(lines.convert_region(loc_ident.region)),
|
||||
alloc.note("Opaque types always wrap exactly one argument!"),
|
||||
@ -1714,7 +1720,7 @@ fn pretty_runtime_error<'b>(
|
||||
title = OPAQUE_NOT_APPLIED;
|
||||
}
|
||||
RuntimeError::OpaqueAppliedToMultipleArgs(region) => {
|
||||
doc = alloc.stack(vec![
|
||||
doc = alloc.stack([
|
||||
alloc.reflow("This opaque type is applied to multiple arguments:"),
|
||||
alloc.region(lines.convert_region(region)),
|
||||
alloc.note("Opaque types always wrap exactly one argument!"),
|
||||
@ -1743,7 +1749,7 @@ fn to_circular_def_doc<'b>(
|
||||
" value is defined directly in terms of itself, causing an infinite loop.",
|
||||
)),
|
||||
[first, others @ ..] => {
|
||||
alloc.stack(vec![
|
||||
alloc.stack([
|
||||
alloc
|
||||
.reflow("The ")
|
||||
.append(alloc.symbol_unqualified(first.symbol))
|
||||
@ -1784,7 +1790,7 @@ fn not_found<'b>(
|
||||
);
|
||||
suggestions.truncate(4);
|
||||
|
||||
let default_no = alloc.concat(vec![
|
||||
let default_no = alloc.concat([
|
||||
alloc.reflow("Is there an "),
|
||||
alloc.keyword("import"),
|
||||
alloc.reflow(" or "),
|
||||
@ -1798,7 +1804,7 @@ fn not_found<'b>(
|
||||
if suggestions.is_empty() {
|
||||
no_suggestion_details
|
||||
} else {
|
||||
alloc.stack(vec![
|
||||
alloc.stack([
|
||||
yes_suggestion_details,
|
||||
alloc
|
||||
.vcat(suggestions.into_iter().map(|v| alloc.string(v.to_string())))
|
||||
@ -1807,8 +1813,8 @@ fn not_found<'b>(
|
||||
}
|
||||
};
|
||||
|
||||
alloc.stack(vec![
|
||||
alloc.concat(vec![
|
||||
alloc.stack([
|
||||
alloc.concat([
|
||||
alloc.reflow("I cannot find a `"),
|
||||
alloc.string(name.to_string()),
|
||||
alloc.reflow("` "),
|
||||
@ -1842,7 +1848,7 @@ fn module_not_found<'b>(
|
||||
|
||||
if suggestions.is_empty() {
|
||||
// We don't have any recommended spelling corrections
|
||||
alloc.concat(vec![
|
||||
alloc.concat([
|
||||
alloc.reflow("Is there an "),
|
||||
alloc.keyword("import"),
|
||||
alloc.reflow(" or "),
|
||||
@ -1850,7 +1856,7 @@ fn module_not_found<'b>(
|
||||
alloc.reflow(" missing up-top"),
|
||||
])
|
||||
} else {
|
||||
alloc.stack(vec![
|
||||
alloc.stack([
|
||||
alloc.reflow("Is there an import missing? Perhaps there is a typo. Did you mean one of these?"),
|
||||
alloc
|
||||
.vcat(suggestions.into_iter().map(|v| alloc.string(v.to_string())))
|
||||
@ -1859,8 +1865,8 @@ fn module_not_found<'b>(
|
||||
}
|
||||
};
|
||||
|
||||
alloc.stack(vec![
|
||||
alloc.concat(vec![
|
||||
alloc.stack([
|
||||
alloc.concat([
|
||||
alloc.reflow("The `"),
|
||||
alloc.string(name.to_string()),
|
||||
alloc.reflow("` module is not imported:"),
|
||||
|
@ -17,12 +17,12 @@ pub fn mono_problem<'b>(
|
||||
match problem {
|
||||
PatternProblem(Incomplete(region, context, missing)) => match context {
|
||||
BadArg => {
|
||||
let doc = alloc.stack(vec![
|
||||
let doc = alloc.stack([
|
||||
alloc.reflow("This pattern does not cover all the possibilities:"),
|
||||
alloc.region(lines.convert_region(region)),
|
||||
alloc.reflow("Other possibilities include:"),
|
||||
unhandled_patterns_to_doc_block(alloc, missing),
|
||||
alloc.concat(vec![
|
||||
alloc.concat([
|
||||
alloc.reflow(
|
||||
"I would have to crash if I saw one of those! \
|
||||
So rather than pattern matching in function arguments, put a ",
|
||||
@ -40,12 +40,12 @@ pub fn mono_problem<'b>(
|
||||
}
|
||||
}
|
||||
BadDestruct => {
|
||||
let doc = alloc.stack(vec![
|
||||
let doc = alloc.stack([
|
||||
alloc.reflow("This pattern does not cover all the possibilities:"),
|
||||
alloc.region(lines.convert_region(region)),
|
||||
alloc.reflow("Other possibilities include:"),
|
||||
unhandled_patterns_to_doc_block(alloc, missing),
|
||||
alloc.concat(vec![
|
||||
alloc.concat([
|
||||
alloc.reflow(
|
||||
"I would have to crash if I saw one of those! \
|
||||
You can use a binding to deconstruct a value if there is only ONE possibility. \
|
||||
@ -64,8 +64,8 @@ pub fn mono_problem<'b>(
|
||||
}
|
||||
}
|
||||
BadCase => {
|
||||
let doc = alloc.stack(vec![
|
||||
alloc.concat(vec![
|
||||
let doc = alloc.stack([
|
||||
alloc.concat([
|
||||
alloc.reflow("This "),
|
||||
alloc.keyword("when"),
|
||||
alloc.reflow(" does not cover all the possibilities:"),
|
||||
@ -93,8 +93,8 @@ pub fn mono_problem<'b>(
|
||||
branch_region,
|
||||
index,
|
||||
}) => {
|
||||
let doc = alloc.stack(vec![
|
||||
alloc.concat(vec![
|
||||
let doc = alloc.stack([
|
||||
alloc.concat([
|
||||
alloc.reflow("The "),
|
||||
alloc.string(index.ordinal()),
|
||||
alloc.reflow(" pattern is redundant:"),
|
||||
@ -169,7 +169,7 @@ fn pattern_to_doc_help<'b>(
|
||||
);
|
||||
debug_assert!(args.len() == 2);
|
||||
let tag = pattern_to_doc_help(alloc, args[1].clone(), in_type_param);
|
||||
alloc.concat(vec![
|
||||
alloc.concat([
|
||||
tag,
|
||||
alloc.text(AFTER_TAG_INDENT),
|
||||
alloc.text("(note the lack of an "),
|
||||
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@ -135,10 +135,7 @@ impl<'b> Report<'b> {
|
||||
"─".repeat(80 - (self.title.len() + 4))
|
||||
);
|
||||
|
||||
alloc.stack(vec![
|
||||
alloc.text(header).annotate(Annotation::Header),
|
||||
self.doc,
|
||||
])
|
||||
alloc.stack([alloc.text(header).annotate(Annotation::Header), self.doc])
|
||||
}
|
||||
}
|
||||
|
||||
@ -194,7 +191,7 @@ const fn default_palette_from_style_codes(codes: StyleCodes) -> Palette {
|
||||
module_name: codes.green,
|
||||
binop: codes.green,
|
||||
typo: codes.yellow,
|
||||
typo_suggestion: codes.green,
|
||||
typo_suggestion: codes.yellow,
|
||||
parser_suggestion: codes.yellow,
|
||||
bold: codes.bold,
|
||||
underline: codes.underline,
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -1294,7 +1294,7 @@ Here are various Roc expressions involving operators, and what they desugar to.
|
||||
| `a - b` | `Num.sub a b` |
|
||||
| `a * b` | `Num.mul a b` |
|
||||
| `a / b` | `Num.div a b` |
|
||||
| `a // b` | `Num.divFloor a b` |
|
||||
| `a // b` | `Num.divTrunc a b` |
|
||||
| `a ^ b` | `Num.pow a b` |
|
||||
| `a % b` | `Num.rem a b` |
|
||||
| `a %% b` | `Num.mod a b` |
|
||||
|
Loading…
Reference in New Issue
Block a user