Merge branch 'main' into ayaz/optimize-list-literal-alloc

This commit is contained in:
Luke Boswell 2024-09-17 19:44:02 +10:00 committed by GitHub
commit 49dedf0b7b
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
424 changed files with 15945 additions and 11035 deletions

View file

@ -1159,7 +1159,7 @@ fn link_macos(
Architecture::Aarch64 => {
ld_child.wait()?;
let mut codesign_cmd = Command::new("codesign");
let mut codesign_cmd = Command::new("/usr/bin/codesign");
codesign_cmd.args(["-s", "-", output_path.to_str().unwrap()]);
debug_print_command(&codesign_cmd);
let codesign_child = codesign_cmd.spawn()?;

View file

@ -129,8 +129,8 @@ hashDict = \hasher, dict -> Hash.hashUnordered hasher (toList dict) List.walk
toInspectorDict : Dict k v -> Inspector f where k implements Inspect & Hash & Eq, v implements Inspect, f implements InspectFormatter
toInspectorDict = \dict ->
fmt <- Inspect.custom
Inspect.apply (Inspect.dict dict walk Inspect.toInspector Inspect.toInspector) fmt
Inspect.custom \fmt ->
Inspect.apply (Inspect.dict dict walk Inspect.toInspector Inspect.toInspector) fmt
## Return an empty dictionary.
## ```roc
@ -894,9 +894,9 @@ calcNumBuckets = \shifts ->
maxBucketCount
fillBucketsFromData = \buckets0, data, shifts ->
buckets1, (key, _), dataIndex <- List.walkWithIndex data buckets0
(bucketIndex, distAndFingerprint) = nextWhileLess buckets1 key shifts
placeAndShiftUp buckets1 { distAndFingerprint, dataIndex: Num.toU32 dataIndex } bucketIndex
List.walkWithIndex data buckets0 \buckets1, (key, _), dataIndex ->
(bucketIndex, distAndFingerprint) = nextWhileLess buckets1 key shifts
placeAndShiftUp buckets1 { distAndFingerprint, dataIndex: Num.toU32 dataIndex } bucketIndex
nextWhileLess : List Bucket, k, U8 -> (U64, U32) where k implements Hash & Eq
nextWhileLess = \buckets, key, shifts ->
@ -1213,15 +1213,15 @@ expect
]
dict =
acc, k <- List.walk badKeys (Dict.empty {})
Dict.update acc k \val ->
when val is
Present p -> Present (p |> Num.addWrap 1)
Missing -> Present 0
List.walk badKeys (Dict.empty {}) \acc, k ->
Dict.update acc k \val ->
when val is
Present p -> Present (p |> Num.addWrap 1)
Missing -> Present 0
allInsertedCorrectly =
acc, k <- List.walk badKeys Bool.true
acc && Dict.contains dict k
List.walk badKeys Bool.true \acc, k ->
acc && Dict.contains dict k
allInsertedCorrectly

View file

@ -138,203 +138,203 @@ dbgInit = \{} -> @DbgFormatter { data: "" }
dbgList : list, ElemWalker (DbgFormatter, Bool) list elem, (elem -> Inspector DbgFormatter) -> Inspector DbgFormatter
dbgList = \content, walkFn, toDbgInspector ->
f0 <- custom
dbgWrite f0 "["
|> \f1 ->
(f2, prependSep), elem <- walkFn content (f1, Bool.false)
f3 =
if prependSep then
dbgWrite f2 ", "
else
f2
custom \f0 ->
dbgWrite f0 "["
|> \f1 ->
walkFn content (f1, Bool.false) \(f2, prependSep), elem ->
f3 =
if prependSep then
dbgWrite f2 ", "
else
f2
elem
|> toDbgInspector
|> apply f3
|> \f4 -> (f4, Bool.true)
|> .0
|> dbgWrite "]"
elem
|> toDbgInspector
|> apply f3
|> \f4 -> (f4, Bool.true)
|> .0
|> dbgWrite "]"
dbgSet : set, ElemWalker (DbgFormatter, Bool) set elem, (elem -> Inspector DbgFormatter) -> Inspector DbgFormatter
dbgSet = \content, walkFn, toDbgInspector ->
f0 <- custom
dbgWrite f0 "{"
|> \f1 ->
(f2, prependSep), elem <- walkFn content (f1, Bool.false)
f3 =
if prependSep then
dbgWrite f2 ", "
else
f2
custom \f0 ->
dbgWrite f0 "{"
|> \f1 ->
walkFn content (f1, Bool.false) \(f2, prependSep), elem ->
f3 =
if prependSep then
dbgWrite f2 ", "
else
f2
elem
|> toDbgInspector
|> apply f3
|> \f4 -> (f4, Bool.true)
|> .0
|> dbgWrite "}"
elem
|> toDbgInspector
|> apply f3
|> \f4 -> (f4, Bool.true)
|> .0
|> dbgWrite "}"
dbgDict : dict, KeyValWalker (DbgFormatter, Bool) dict key value, (key -> Inspector DbgFormatter), (value -> Inspector DbgFormatter) -> Inspector DbgFormatter
dbgDict = \d, walkFn, keyToInspector, valueToInspector ->
f0 <- custom
dbgWrite f0 "{"
|> \f1 ->
(f2, prependSep), key, value <- walkFn d (f1, Bool.false)
f3 =
if prependSep then
dbgWrite f2 ", "
else
f2
custom \f0 ->
dbgWrite f0 "{"
|> \f1 ->
walkFn d (f1, Bool.false) \(f2, prependSep), key, value ->
f3 =
if prependSep then
dbgWrite f2 ", "
else
f2
apply (keyToInspector key) f3
|> dbgWrite ": "
|> \x -> apply (valueToInspector value) x
|> \f4 -> (f4, Bool.true)
|> .0
|> dbgWrite "}"
apply (keyToInspector key) f3
|> dbgWrite ": "
|> \x -> apply (valueToInspector value) x
|> \f4 -> (f4, Bool.true)
|> .0
|> dbgWrite "}"
dbgTag : Str, List (Inspector DbgFormatter) -> Inspector DbgFormatter
dbgTag = \name, fields ->
if List.isEmpty fields then
f0 <- custom
dbgWrite f0 name
custom \f0 ->
dbgWrite f0 name
else
f0 <- custom
dbgWrite f0 "("
|> dbgWrite name
|> \f1 ->
f2, inspector <- List.walk fields f1
dbgWrite f2 " "
|> \x -> apply inspector x
|> dbgWrite ")"
custom \f0 ->
dbgWrite f0 "("
|> dbgWrite name
|> \f1 ->
List.walk fields f1 \f2, inspector ->
dbgWrite f2 " "
|> \x -> apply inspector x
|> dbgWrite ")"
dbgTuple : List (Inspector DbgFormatter) -> Inspector DbgFormatter
dbgTuple = \fields ->
f0 <- custom
dbgWrite f0 "("
|> \f1 ->
(f2, prependSep), inspector <- List.walk fields (f1, Bool.false)
f3 =
if prependSep then
dbgWrite f2 ", "
else
f2
custom \f0 ->
dbgWrite f0 "("
|> \f1 ->
List.walk fields (f1, Bool.false) \(f2, prependSep), inspector ->
f3 =
if prependSep then
dbgWrite f2 ", "
else
f2
apply inspector f3
|> \f4 -> (f4, Bool.true)
|> .0
|> dbgWrite ")"
apply inspector f3
|> \f4 -> (f4, Bool.true)
|> .0
|> dbgWrite ")"
dbgRecord : List { key : Str, value : Inspector DbgFormatter } -> Inspector DbgFormatter
dbgRecord = \fields ->
f0 <- custom
dbgWrite f0 "{"
|> \f1 ->
(f2, prependSep), { key, value } <- List.walk fields (f1, Bool.false)
f3 =
if prependSep then
dbgWrite f2 ", "
else
f2
custom \f0 ->
dbgWrite f0 "{"
|> \f1 ->
List.walk fields (f1, Bool.false) \(f2, prependSep), { key, value } ->
f3 =
if prependSep then
dbgWrite f2 ", "
else
f2
dbgWrite f3 key
|> dbgWrite ": "
|> \x -> apply value x
|> \f4 -> (f4, Bool.true)
|> .0
|> dbgWrite "}"
dbgWrite f3 key
|> dbgWrite ": "
|> \x -> apply value x
|> \f4 -> (f4, Bool.true)
|> .0
|> dbgWrite "}"
dbgBool : Bool -> Inspector DbgFormatter
dbgBool = \b ->
if b then
f0 <- custom
dbgWrite f0 "Bool.true"
custom \f0 ->
dbgWrite f0 "Bool.true"
else
f0 <- custom
dbgWrite f0 "Bool.false"
custom \f0 ->
dbgWrite f0 "Bool.false"
dbgStr : Str -> Inspector DbgFormatter
dbgStr = \s ->
f0 <- custom
f0
|> dbgWrite "\""
|> dbgWrite s # TODO: Should we be escaping strings for dbg/logging?
|> dbgWrite "\""
custom \f0 ->
f0
|> dbgWrite "\""
|> dbgWrite s # TODO: Should we be escaping strings for dbg/logging?
|> dbgWrite "\""
dbgOpaque : * -> Inspector DbgFormatter
dbgOpaque = \_ ->
f0 <- custom
dbgWrite f0 "<opaque>"
custom \f0 ->
dbgWrite f0 "<opaque>"
dbgFunction : * -> Inspector DbgFormatter
dbgFunction = \_ ->
f0 <- custom
dbgWrite f0 "<function>"
custom \f0 ->
dbgWrite f0 "<function>"
dbgU8 : U8 -> Inspector DbgFormatter
dbgU8 = \num ->
f0 <- custom
dbgWrite f0 (num |> Num.toStr)
custom \f0 ->
dbgWrite f0 (num |> Num.toStr)
dbgI8 : I8 -> Inspector DbgFormatter
dbgI8 = \num ->
f0 <- custom
dbgWrite f0 (num |> Num.toStr)
custom \f0 ->
dbgWrite f0 (num |> Num.toStr)
dbgU16 : U16 -> Inspector DbgFormatter
dbgU16 = \num ->
f0 <- custom
dbgWrite f0 (num |> Num.toStr)
custom \f0 ->
dbgWrite f0 (num |> Num.toStr)
dbgI16 : I16 -> Inspector DbgFormatter
dbgI16 = \num ->
f0 <- custom
dbgWrite f0 (num |> Num.toStr)
custom \f0 ->
dbgWrite f0 (num |> Num.toStr)
dbgU32 : U32 -> Inspector DbgFormatter
dbgU32 = \num ->
f0 <- custom
dbgWrite f0 (num |> Num.toStr)
custom \f0 ->
dbgWrite f0 (num |> Num.toStr)
dbgI32 : I32 -> Inspector DbgFormatter
dbgI32 = \num ->
f0 <- custom
dbgWrite f0 (num |> Num.toStr)
custom \f0 ->
dbgWrite f0 (num |> Num.toStr)
dbgU64 : U64 -> Inspector DbgFormatter
dbgU64 = \num ->
f0 <- custom
dbgWrite f0 (num |> Num.toStr)
custom \f0 ->
dbgWrite f0 (num |> Num.toStr)
dbgI64 : I64 -> Inspector DbgFormatter
dbgI64 = \num ->
f0 <- custom
dbgWrite f0 (num |> Num.toStr)
custom \f0 ->
dbgWrite f0 (num |> Num.toStr)
dbgU128 : U128 -> Inspector DbgFormatter
dbgU128 = \num ->
f0 <- custom
dbgWrite f0 (num |> Num.toStr)
custom \f0 ->
dbgWrite f0 (num |> Num.toStr)
dbgI128 : I128 -> Inspector DbgFormatter
dbgI128 = \num ->
f0 <- custom
dbgWrite f0 (num |> Num.toStr)
custom \f0 ->
dbgWrite f0 (num |> Num.toStr)
dbgF32 : F32 -> Inspector DbgFormatter
dbgF32 = \num ->
f0 <- custom
dbgWrite f0 (num |> Num.toStr)
custom \f0 ->
dbgWrite f0 (num |> Num.toStr)
dbgF64 : F64 -> Inspector DbgFormatter
dbgF64 = \num ->
f0 <- custom
dbgWrite f0 (num |> Num.toStr)
custom \f0 ->
dbgWrite f0 (num |> Num.toStr)
dbgDec : Dec -> Inspector DbgFormatter
dbgDec = \num ->
f0 <- custom
dbgWrite f0 (num |> Num.toStr)
custom \f0 ->
dbgWrite f0 (num |> Num.toStr)
dbgWrite : DbgFormatter, Str -> DbgFormatter
dbgWrite = \@DbgFormatter { data }, added ->

View file

@ -708,6 +708,9 @@ absDiff = \a, b ->
##
## Num.neg 0.0
## ```
## !! Num.neg is not completely implemented for all types in all contexts, see github.com/roc-lang/roc/issues/6959
## You can use `\someNum -> 0 - someNum` as a workaround.
##
## This is safe to use with any [Frac], but it can cause overflow when used with certain [Int] values.
##
## For example, calling #Num.neg on the lowest value of a signed integer (such as [Num.minI64] or [Num.minI32]) will cause overflow.

View file

@ -1,4 +1,15 @@
module [Result, isOk, isErr, map, mapErr, try, onErr, withDefault]
module [
Result,
isOk,
isErr,
map,
mapErr,
mapBoth,
map2,
try,
onErr,
withDefault,
]
import Bool exposing [Bool]
@ -67,6 +78,22 @@ mapErr = \result, transform ->
Ok v -> Ok v
Err e -> Err (transform e)
## Maps both the `Ok` and `Err` values of a `Result` to new values.
mapBoth : Result ok1 err1, (ok1 -> ok2), (err1 -> err2) -> Result ok2 err2
mapBoth = \result, okTransform, errTransform ->
when result is
Ok val -> Ok (okTransform val)
Err err -> Err (errTransform err)
## Maps the `Ok` values of two `Result`s to a new value using a given transformation,
## or returns the first `Err` value encountered.
map2 : Result a err, Result b err, (a, b -> c) -> Result c err
map2 = \firstResult, secondResult, transform ->
when (firstResult, secondResult) is
(Ok first, Ok second) -> Ok (transform first second)
(Err err, _) -> Err err
(_, Err err) -> Err err
## If the result is `Ok`, transforms the entire result by running a conversion
## function on the value the `Ok` holds. Then returns that new result. If the
## result is `Err`, this has no effect. Use `onErr` to transform an `Err`.

View file

@ -62,8 +62,8 @@ hashSet = \hasher, @Set inner -> Hash.hash hasher inner
toInspectorSet : Set k -> Inspector f where k implements Inspect & Hash & Eq, f implements InspectFormatter
toInspectorSet = \set ->
fmt <- Inspect.custom
Inspect.apply (Inspect.set set walk Inspect.toInspector) fmt
Inspect.custom \fmt ->
Inspect.apply (Inspect.set set walk Inspect.toInspector) fmt
## Creates a new empty `Set`.
## ```roc

View file

@ -0,0 +1,265 @@
module [
Task,
ok,
err,
await,
map,
mapErr,
onErr,
attempt,
forever,
loop,
fromResult,
batch,
sequence,
forEach,
result,
]
import List
import Result exposing [Result]
## A Task represents an effect; an interaction with state outside your Roc
## program, such as the terminal's standard output, or a file.
Task ok err := {} -> Result ok err
## Run a task repeatedly, until it fails with `err`. Note that this task does not return a success value.
forever : Task a err -> Task * err
forever = \@Task task ->
looper = \{} ->
when task {} is
Err e -> Err e
Ok _ -> looper {}
@Task \{} -> looper {}
## Run a task repeatedly, until it fails with `err` or completes with `done`.
##
## ```
## sum =
## Task.loop! 0 \total ->
## numResult =
## Stdin.line
## |> Task.result!
## |> Result.try Str.toU64
##
## when numResult is
## Ok num -> Task.ok (Step (total + num))
## Err (StdinErr EndOfFile) -> Task.ok (Done total)
## Err InvalidNumStr -> Task.err NonNumberGiven
## ```
loop : state, (state -> Task [Step state, Done done] err) -> Task done err
loop = \state, step ->
looper = \current ->
(@Task next) = step current
when next {} is
Err e -> Err e
Ok (Done newResult) -> Ok newResult
Ok (Step newState) -> looper (newState)
@Task \{} -> looper state
## Create a task that always succeeds with the value provided.
##
## ```
## # Always succeeds with "Louis"
## getName : Task.Task Str *
## getName = Task.ok "Louis"
## ```
##
ok : a -> Task a *
ok = \a -> @Task \{} -> Ok a
## Create a task that always fails with the error provided.
##
## ```
## # Always fails with the tag `CustomError Str`
## customError : Str -> Task.Task {} [CustomError Str]
## customError = \err -> Task.err (CustomError err)
## ```
##
err : a -> Task * a
err = \a -> @Task \{} -> Err a
## Transform a given Task with a function that handles the success or error case
## and returns another task based on that. This is useful for chaining tasks
## together or performing error handling and recovery.
##
## Consider the following task:
##
## `canFail : Task {} [Failure, AnotherFail, YetAnotherFail]`
##
## We can use [attempt] to handle the failure cases using the following:
##
## ```
## Task.attempt canFail \result ->
## when result is
## Ok Success -> Stdout.line "Success!"
## Err Failure -> Stdout.line "Oops, failed!"
## Err AnotherFail -> Stdout.line "Ooooops, another failure!"
## Err YetAnotherFail -> Stdout.line "Really big oooooops, yet again!"
## ```
##
## Here we know that the `canFail` task may fail, and so we use
## `Task.attempt` to convert the task to a `Result` and then use pattern
## matching to handle the success and possible failure cases.
attempt : Task a b, (Result a b -> Task c d) -> Task c d
attempt = \@Task task, transform ->
@Task \{} ->
(@Task transformed) = transform (task {})
transformed {}
## Take the success value from a given [Task] and use that to generate a new [Task].
##
## We can [await] Task results with callbacks:
##
## ```
## Task.await (Stdin.line "What's your name?") \name ->
## Stdout.line "Your name is: $(name)"
## ```
##
## Or we can more succinctly use the `!` bang operator, which desugars to [await]:
##
## ```
## name = Stdin.line! "What's your name?"
## Stdout.line "Your name is: $(name)"
## ```
await : Task a b, (a -> Task c b) -> Task c b
await = \@Task task, transform ->
@Task \{} ->
when task {} is
Ok a ->
(@Task transformed) = transform a
transformed {}
Err b ->
Err b
## Take the error value from a given [Task] and use that to generate a new [Task].
##
## ```
## # Prints "Something went wrong!" to standard error if `canFail` fails.
## canFail
## |> Task.onErr \_ -> Stderr.line "Something went wrong!"
## ```
onErr : Task a b, (b -> Task a c) -> Task a c
onErr = \@Task task, transform ->
@Task \{} ->
when task {} is
Ok a ->
Ok a
Err b ->
(@Task transformed) = transform b
transformed {}
## Transform the success value of a given [Task] with a given function.
##
## ```
## # Succeeds with a value of "Bonjour Louis!"
## Task.ok "Louis"
## |> Task.map (\name -> "Bonjour $(name)!")
## ```
map : Task a c, (a -> b) -> Task b c
map = \@Task task, transform ->
@Task \{} ->
when task {} is
Ok a -> Ok (transform a)
Err b -> Err b
## Transform the error value of a given [Task] with a given function.
##
## ```
## # Ignore the fail value, and map it to the tag `CustomError`
## canFail
## |> Task.mapErr \_ -> CustomError
## ```
mapErr : Task c a, (a -> b) -> Task c b
mapErr = \@Task task, transform ->
@Task \{} ->
when task {} is
Ok a -> Ok a
Err b -> Err (transform b)
## Use a Result among other Tasks by converting it into a [Task].
fromResult : Result a b -> Task a b
fromResult = \res ->
@Task \{} -> res
## Apply a task to another task applicatively. This can be used with
## [ok] to build a [Task] that returns a record.
##
## The following example returns a Record with two fields, `apples` and
## `oranges`, each of which is a `List Str`. If it fails it returns the tag
## `NoFruitAvailable`.
##
## ```
## getFruitBasket : Task { apples : List Str, oranges : List Str } [NoFruitAvailable]
## getFruitBasket = Task.ok {
## apples: <- getFruit Apples |> Task.batch,
## oranges: <- getFruit Oranges |> Task.batch,
## }
## ```
batch : Task a c -> (Task (a -> b) c -> Task b c)
batch = \current ->
\next ->
await next \f ->
map current f
## Apply each task in a list sequentially, and return a list of the resulting values.
## Each task will be awaited before beginning the next task.
##
## ```
## fetchAuthorTasks : List (Task Author [DbError])
##
## getAuthors : Task (List Author) [DbError]
## getAuthors = Task.sequence fetchAuthorTasks
## ```
##
sequence : List (Task ok err) -> Task (List ok) err
sequence = \taskList ->
Task.loop (taskList, List.withCapacity (List.len taskList)) \(tasks, values) ->
when tasks is
[task, .. as rest] ->
value = task!
Task.ok (Step (rest, List.append values value))
[] ->
Task.ok (Done values)
## Apply a task repeatedly for each item in a list
##
## ```
## authors : List Author
## saveAuthor : Author -> Task {} [DbError]
##
## saveAuthors : Task (List Author) [DbError]
## saveAuthors = Task.forEach authors saveAuthor
## ```
##
forEach : List a, (a -> Task {} b) -> Task {} b
forEach = \items, fn ->
List.walk items (ok {}) \state, item ->
state |> await \_ -> fn item
## Transform a task that can either succeed with `ok`, or fail with `err`, into
## a task that succeeds with `Result ok err`.
##
## This is useful when chaining tasks using the `!` suffix. For example:
##
## ```
## # Path.roc
## checkFile : Str -> Task [Good, Bad] [IOError]
##
## # main.roc
## when checkFile "/usr/local/bin/roc" |> Task.result! is
## Ok Good -> "..."
## Ok Bad -> "..."
## Err IOError -> "..."
## ```
##
result : Task ok err -> Task (Result ok err) *
result = \@Task task ->
@Task \{} ->
Ok (task {})

View file

@ -11,4 +11,5 @@ package [
Hash,
Box,
Inspect,
Task,
] {}

View file

@ -16,6 +16,7 @@ pub fn module_source(module_id: ModuleId) -> &'static str {
ModuleId::DECODE => DECODE,
ModuleId::HASH => HASH,
ModuleId::INSPECT => INSPECT,
ModuleId::TASK => TASK,
_ => internal_error!(
"ModuleId {:?} is not part of the standard library",
module_id
@ -35,3 +36,4 @@ const ENCODE: &str = include_str!("../roc/Encode.roc");
const DECODE: &str = include_str!("../roc/Decode.roc");
const HASH: &str = include_str!("../roc/Hash.roc");
const INSPECT: &str = include_str!("../roc/Inspect.roc");
const TASK: &str = include_str!("../roc/Task.roc");

View file

@ -1,6 +1,6 @@
use crate::env::Env;
use crate::procedure::{QualifiedReference, References};
use crate::scope::{PendingAbilitiesInScope, Scope};
use crate::scope::{PendingAbilitiesInScope, Scope, SymbolLookup};
use roc_collections::{ImMap, MutSet, SendMap, VecMap, VecSet};
use roc_module::ident::{Ident, Lowercase, TagName};
use roc_module::symbol::Symbol;
@ -386,7 +386,10 @@ pub(crate) fn make_apply_symbol(
// Look it up in scope!
match scope.lookup_str(ident, region) {
Ok(symbol) => {
Ok(SymbolLookup {
symbol,
module_params: _,
}) => {
references.insert_type_lookup(symbol, QualifiedReference::Unqualified);
Ok(symbol)
}
@ -398,7 +401,10 @@ pub(crate) fn make_apply_symbol(
}
} else {
match env.qualified_lookup(scope, module_name, ident, region) {
Ok(symbol) => {
Ok(SymbolLookup {
symbol,
module_params: _,
}) => {
references.insert_type_lookup(symbol, QualifiedReference::Qualified);
Ok(symbol)
}
@ -467,7 +473,8 @@ pub fn find_type_def_symbols(
while let Some(assigned_field) = inner_stack.pop() {
match assigned_field {
AssignedField::RequiredValue(_, _, t)
| AssignedField::OptionalValue(_, _, t) => {
| AssignedField::OptionalValue(_, _, t)
| AssignedField::IgnoredValue(_, _, t) => {
stack.push(&t.value);
}
AssignedField::LabelOnly(_) => {}
@ -1386,6 +1393,7 @@ fn can_assigned_fields<'a>(
break 'inner label;
}
IgnoredValue(_, _, _) => unreachable!(),
LabelOnly(loc_field_name) => {
// Interpret { a, b } as { a : a, b : b }
let field_name = Lowercase::from(loc_field_name.value);

View file

@ -602,7 +602,8 @@ impl Constraints {
| Constraint::Exhaustive { .. }
| Constraint::Resolve(..)
| Constraint::IngestedFile(..)
| Constraint::CheckCycle(..) => false,
| Constraint::CheckCycle(..)
| Constraint::ImportParams(..) => false,
}
}
@ -685,6 +686,15 @@ impl Constraints {
) -> Constraint {
Constraint::IngestedFile(type_index, file_path, bytes)
}
pub fn import_params(
&mut self,
opt_type_index: Option<TypeOrVar>,
module_id: ModuleId,
region: Region,
) -> Constraint {
Constraint::ImportParams(opt_type_index, module_id, region)
}
}
roc_error_macros::assert_sizeof_default!(Constraint, 3 * 8);
@ -787,6 +797,7 @@ pub enum Constraint {
CheckCycle(Index<Cycle>, IllegalCycleMark),
IngestedFile(TypeOrVar, Box<PathBuf>, Arc<Vec<u8>>),
ImportParams(Option<TypeOrVar>, ModuleId, Region),
}
#[derive(Debug, Clone, Copy, Default)]
@ -865,6 +876,9 @@ impl std::fmt::Debug for Constraint {
Self::IngestedFile(arg0, arg1, arg2) => {
write!(f, "IngestedFile({arg0:?}, {arg1:?}, {arg2:?})")
}
Self::ImportParams(arg0, arg1, arg2) => {
write!(f, "ImportParams({arg0:?}, {arg1:?}, {arg2:?})")
}
}
}
}

View file

@ -288,6 +288,24 @@ fn deep_copy_expr_help<C: CopyEnv>(env: &mut C, copied: &mut Vec<Variable>, expr
loc_elems: loc_elems.iter().map(|le| le.map(|e| go_help!(e))).collect(),
},
Var(sym, var) => Var(*sym, sub!(*var)),
ParamsVar {
symbol,
var,
params_symbol,
params_var,
} => ParamsVar {
symbol: *symbol,
var: sub!(*var),
params_symbol: *params_symbol,
params_var: sub!(*params_var),
},
ImportParams(module_id, region, opt_provided) => ImportParams(
*module_id,
*region,
opt_provided
.as_ref()
.map(|(var, expr)| (sub!(*var), Box::new(go_help!(&expr)))),
),
&AbilityMember(sym, specialization, specialization_var) => {
AbilityMember(sym, specialization, sub!(specialization_var))
}

View file

@ -3,12 +3,14 @@
use crate::def::Def;
use crate::expr::Expr::{self, *};
use crate::expr::{
ClosureData, DeclarationTag, Declarations, FunctionDef, OpaqueWrapFunctionData, WhenBranch,
ClosureData, DeclarationTag, Declarations, FunctionDef, OpaqueWrapFunctionData,
StructAccessorData, WhenBranch,
};
use crate::pattern::{Pattern, RecordDestruct, TupleDestruct};
use roc_module::symbol::{Interns, ModuleId, Symbol};
use roc_types::types::IndexOrField;
use ven_pretty::{text, Arena, DocAllocator, DocBuilder};
pub struct Ctx<'a> {
@ -206,7 +208,13 @@ fn expr<'a>(c: &Ctx, p: EPrec, f: &'a Arena<'a>, e: &'a Expr) -> DocBuilder<'a,
.append("]")
.group(),
),
Var(sym, _) | AbilityMember(sym, _, _) => pp_sym(c, f, *sym),
Var(sym, _) | ParamsVar { symbol: sym, .. } | AbilityMember(sym, _, _) => {
pp_sym(c, f, *sym)
}
ImportParams(_, _, Some((_, params_expr))) => expr(c, p, f, params_expr),
ImportParams(module_id, _, None) => {
text!(f, "<no params for {:?}>", module_id)
}
When {
loc_cond, branches, ..
} => maybe_paren!(
@ -375,7 +383,10 @@ fn expr<'a>(c: &Ctx, p: EPrec, f: &'a Arena<'a>, e: &'a Expr) -> DocBuilder<'a,
OpaqueWrapFunction(OpaqueWrapFunctionData { opaque_name, .. }) => {
text!(f, "@{}", opaque_name.as_str(c.interns))
}
RecordAccessor(_) => todo!(),
RecordAccessor(StructAccessorData { field, .. }) => match field {
IndexOrField::Index(index) => text!(f, ".{}", index),
IndexOrField::Field(name) => text!(f, ".{}", name),
},
RecordUpdate {
symbol, updates, ..
} => f

View file

@ -10,6 +10,7 @@ use crate::annotation::IntroducedVariables;
use crate::annotation::OwnedNamedOrAble;
use crate::derive;
use crate::env::Env;
use crate::expr::canonicalize_record;
use crate::expr::get_lookup_symbols;
use crate::expr::AnnotatedMark;
use crate::expr::ClosureData;
@ -18,8 +19,10 @@ use crate::expr::Expr::{self, *};
use crate::expr::StructAccessorData;
use crate::expr::{canonicalize_expr, Output, Recursive};
use crate::pattern::{canonicalize_def_header_pattern, BindingsFromPattern, Pattern};
use crate::procedure::QualifiedReference;
use crate::procedure::References;
use crate::scope::create_alias;
use crate::scope::SymbolLookup;
use crate::scope::{PendingAbilitiesInScope, Scope};
use roc_collections::ReferenceMatrix;
use roc_collections::VecMap;
@ -109,6 +112,23 @@ impl Annotation {
self
}
pub fn convert_to_fn(&mut self, argument_count: usize, var_store: &mut VarStore) {
let mut arg_types = Vec::with_capacity(argument_count);
for _ in 0..argument_count {
let var = var_store.fresh();
self.introduced_variables.insert_inferred(Loc::at_zero(var));
arg_types.push(Type::Variable(var));
}
self.signature = Type::Function(
arg_types,
Box::new(Type::Variable(var_store.fresh())),
Box::new(self.signature.clone()),
);
}
}
#[derive(Debug)]
@ -160,6 +180,14 @@ enum PendingValueDef<'a> {
&'a Loc<ast::TypeAnnotation<'a>>,
&'a Loc<ast::Expr<'a>>,
),
/// Module params from an import
ImportParams {
symbol: Symbol,
variable: Variable,
loc_pattern: Loc<Pattern>,
module_id: ModuleId,
opt_provided: Option<ast::Collection<'a, Loc<AssignedField<'a, ast::Expr<'a>>>>>,
},
/// Ingested file
IngestedFile(
Loc<Pattern>,
@ -174,6 +202,13 @@ impl PendingValueDef<'_> {
PendingValueDef::AnnotationOnly(loc_pattern, _) => loc_pattern,
PendingValueDef::Body(loc_pattern, _) => loc_pattern,
PendingValueDef::TypedBody(_, loc_pattern, _, _) => loc_pattern,
PendingValueDef::ImportParams {
loc_pattern,
symbol: _,
variable: _,
module_id: _,
opt_provided: _,
} => loc_pattern,
PendingValueDef::IngestedFile(loc_pattern, _, _) => loc_pattern,
}
}
@ -485,6 +520,16 @@ fn canonicalize_alias<'a>(
}
}
#[macro_export]
macro_rules! params_in_abilities_unimplemented {
($lookup:expr) => {
match $lookup.module_params {
None => $lookup.symbol,
Some(_) => unimplemented!("params in abilities"),
}
};
}
/// Canonicalizes a claimed ability implementation like `{ eq }` or `{ eq: myEq }`.
/// Returns a mapping of the ability member to the implementation symbol.
/// If there was an error, a problem will be recorded and nothing is returned.
@ -503,7 +548,7 @@ fn canonicalize_claimed_ability_impl<'a>(
let member_symbol =
match env.qualified_lookup_with_module_id(scope, ability_home, label_str, region) {
Ok(symbol) => symbol,
Ok(lookup) => params_in_abilities_unimplemented!(lookup),
Err(_) => {
env.problem(Problem::NotAnAbilityMember {
ability,
@ -546,8 +591,13 @@ fn canonicalize_claimed_ability_impl<'a>(
// To handle both cases, try checking for a shadow first, then check for a direct
// reference. We want to check for a direct reference second so that if there is a
// shadow, we won't accidentally grab the imported symbol.
let opt_impl_symbol = (scope.lookup_ability_member_shadow(member_symbol))
.or_else(|| scope.lookup_str(label_str, region).ok());
let opt_impl_symbol =
(scope.lookup_ability_member_shadow(member_symbol)).or_else(|| {
scope
.lookup_str(label_str, region)
.map(|s| params_in_abilities_unimplemented!(s))
.ok()
});
match opt_impl_symbol {
// It's possible that even if we find a symbol it is still only the member
@ -599,7 +649,7 @@ fn canonicalize_claimed_ability_impl<'a>(
label.value,
label.region,
) {
Ok(symbol) => symbol,
Ok(lookup) => params_in_abilities_unimplemented!(lookup),
Err(_) => {
env.problem(Problem::NotAnAbilityMember {
ability,
@ -611,7 +661,7 @@ fn canonicalize_claimed_ability_impl<'a>(
};
let impl_symbol = match scope.lookup(&impl_ident.into(), impl_region) {
Ok(symbol) => symbol,
Ok(symbol) => params_in_abilities_unimplemented!(symbol),
Err(err) => {
env.problem(Problem::RuntimeError(err));
return Err(());
@ -631,7 +681,9 @@ fn canonicalize_claimed_ability_impl<'a>(
// An error will already have been reported
Err(())
}
AssignedField::SpaceBefore(_, _) | AssignedField::SpaceAfter(_, _) => {
AssignedField::SpaceBefore(_, _)
| AssignedField::SpaceAfter(_, _)
| AssignedField::IgnoredValue(_, _, _) => {
internal_error!("unreachable")
}
}
@ -1107,8 +1159,25 @@ fn canonicalize_value_defs<'a>(
PendingValue::ExpectFx(pending_expect) => {
pending_expect_fx.push(pending_expect);
}
PendingValue::ModuleImport(introduced_import) => {
imports_introduced.push(introduced_import);
PendingValue::ModuleImport(PendingModuleImport {
module_id,
region,
exposed_symbols,
params,
}) => {
imports_introduced.push(IntroducedImport {
module_id,
region,
exposed_symbols,
});
pending_value_defs.push(PendingValueDef::ImportParams {
symbol: params.symbol,
variable: params.variable,
loc_pattern: params.loc_pattern,
opt_provided: params.opt_provided,
module_id,
});
}
PendingValue::InvalidIngestedFile => { /* skip */ }
PendingValue::ImportNameConflict => { /* skip */ }
@ -1556,7 +1625,7 @@ impl DefOrdering {
fn insert_symbol_references(&mut self, def_id: u32, def_references: &DefReferences) {
match def_references {
DefReferences::Value(references) => {
let it = references.value_lookups().chain(references.calls());
let it = references.value_lookups();
for referenced in it {
if let Some(ref_id) = self.get_id(*referenced) {
@ -2350,6 +2419,52 @@ fn canonicalize_pending_value_def<'a>(
None,
)
}
ImportParams {
symbol,
variable,
loc_pattern,
module_id,
opt_provided,
} => {
// Insert a reference to the record so that we don't report it as unused
// If the whole module is unused, we'll report that separately
output.references.insert_value_lookup(
SymbolLookup::no_params(symbol),
QualifiedReference::Unqualified,
);
let (opt_var_record, references) = match opt_provided {
Some(params) => {
let (record, can_output) =
canonicalize_record(env, var_store, scope, loc_pattern.region, params);
let references = can_output.references.clone();
output.union(can_output);
(Some((variable, Box::new(record))), references)
}
None => (None, References::new()),
};
let loc_expr = Loc::at(
loc_pattern.region,
Expr::ImportParams(module_id, loc_pattern.region, opt_var_record),
);
let def = single_can_def(
loc_pattern,
loc_expr,
var_store.fresh(),
None,
SendMap::default(),
);
DefOutput {
output,
references: DefReferences::Value(references),
def,
}
}
IngestedFile(loc_pattern, opt_loc_ann, path_literal) => {
let relative_path =
if let ast::StrLiteral::PlainLine(ingested_path) = path_literal.value {
@ -2633,12 +2748,11 @@ pub fn report_unused_imports(
for (symbol, region) in &import.exposed_symbols {
if !references.has_unqualified_type_or_value_lookup(*symbol)
&& !scope.abilities_store.is_specialization_name(*symbol)
&& !import.is_task(env)
{
env.problem(Problem::UnusedImport(*symbol, *region));
}
}
} else if !import.is_task(env) {
} else {
env.problem(Problem::UnusedModuleImport(import.module_id, import.region));
}
}
@ -2891,7 +3005,7 @@ enum PendingValue<'a> {
Dbg(PendingExpectOrDbg<'a>),
Expect(PendingExpectOrDbg<'a>),
ExpectFx(PendingExpectOrDbg<'a>),
ModuleImport(IntroducedImport),
ModuleImport(PendingModuleImport<'a>),
SignatureDefMismatch,
InvalidIngestedFile,
ImportNameConflict,
@ -2902,22 +3016,26 @@ struct PendingExpectOrDbg<'a> {
preceding_comment: Region,
}
struct PendingModuleImport<'a> {
module_id: ModuleId,
region: Region,
exposed_symbols: Vec<(Symbol, Region)>,
params: PendingModuleImportParams<'a>,
}
struct PendingModuleImportParams<'a> {
symbol: Symbol,
variable: Variable,
loc_pattern: Loc<Pattern>,
opt_provided: Option<ast::Collection<'a, Loc<AssignedField<'a, ast::Expr<'a>>>>>,
}
pub struct IntroducedImport {
module_id: ModuleId,
region: Region,
exposed_symbols: Vec<(Symbol, Region)>,
}
impl IntroducedImport {
pub fn is_task(&self, env: &Env<'_>) -> bool {
// Temporarily needed for `!` convenience. Can be removed when Task becomes a builtin.
match env.qualified_module_ids.get_name(self.module_id) {
Some(name) => name.as_inner().as_str() == "Task",
None => false,
}
}
}
#[allow(clippy::too_many_arguments)]
fn to_pending_value_def<'a>(
env: &mut Env<'a>,
@ -3051,10 +3169,29 @@ fn to_pending_value_def<'a>(
None => module_name.clone(),
};
// Generate a symbol for the module params def
// We do this even if params weren't provided so that solve can report if they are missing
let params_sym = scope.gen_unique_symbol();
let params_region = module_import.params.map(|p| p.params.region).unwrap_or(region);
let params_var = var_store.fresh();
let params =
PendingModuleImportParams {
symbol: params_sym,
variable: params_var,
loc_pattern: Loc::at(params_region, Pattern::Identifier(params_sym)),
opt_provided: module_import.params.map(|p| p.params.value),
};
let provided_params = if module_import.params.is_some() {
// Only add params to scope if they are provided
Some((params_var, params_sym))
} else {
None
};
if let Err(existing_import) =
scope
.modules
.insert(name_with_alias.clone(), module_id, region)
.insert(name_with_alias.clone(), module_id, provided_params, region)
{
env.problems.push(Problem::ImportNameConflict {
name: name_with_alias,
@ -3065,7 +3202,7 @@ fn to_pending_value_def<'a>(
});
return PendingValue::ImportNameConflict;
}
};
let exposed_names = module_import
.exposed
@ -3117,13 +3254,13 @@ fn to_pending_value_def<'a>(
}))
}
}
}
PendingValue::ModuleImport(IntroducedImport {
PendingValue::ModuleImport(PendingModuleImport {
module_id,
region,
exposed_symbols,
params,
})
}
IngestedFileImport(ingested_file) => {

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -1,13 +1,14 @@
use std::path::Path;
use crate::procedure::References;
use crate::scope::Scope;
use crate::scope::{ModuleLookup, Scope, SymbolLookup};
use bumpalo::Bump;
use roc_collections::{MutMap, VecSet};
use roc_module::ident::{Ident, ModuleName};
use roc_module::symbol::{IdentIdsByModule, ModuleId, PQModuleName, PackageModuleIds, Symbol};
use roc_problem::can::{Problem, RuntimeError};
use roc_region::all::{Loc, Region};
use roc_region::all::{LineInfo, Loc, Region};
use roc_types::subs::Variable;
/// The canonicalization environment for a particular module.
pub struct Env<'a> {
@ -38,14 +39,24 @@ pub struct Env<'a> {
pub top_level_symbols: VecSet<Symbol>,
pub home_params_record: Option<(Symbol, Variable)>,
pub arena: &'a Bump,
pub opt_shorthand: Option<&'a str>,
pub src: &'a str,
/// Lazily calculated line info. This data is only needed if the code contains calls to `dbg`,
/// otherwise we can leave it as `None` and never pay the cost of scanning the source an extra
/// time.
line_info: &'a mut Option<LineInfo>,
}
impl<'a> Env<'a> {
pub fn new(
arena: &'a Bump,
src: &'a str,
home: ModuleId,
module_path: &'a Path,
dep_idents: &'a IdentIdsByModule,
@ -54,6 +65,7 @@ impl<'a> Env<'a> {
) -> Env<'a> {
Env {
arena,
src,
home,
module_path,
dep_idents,
@ -64,7 +76,9 @@ impl<'a> Env<'a> {
qualified_type_lookups: VecSet::default(),
tailcallable_symbol: None,
top_level_symbols: VecSet::default(),
home_params_record: None,
opt_shorthand,
line_info: arena.alloc(None),
}
}
@ -74,7 +88,7 @@ impl<'a> Env<'a> {
module_name_str: &str,
ident: &str,
region: Region,
) -> Result<Symbol, RuntimeError> {
) -> Result<SymbolLookup, RuntimeError> {
debug_assert!(
!module_name_str.is_empty(),
"Called env.qualified_lookup with an unqualified ident: {ident:?}"
@ -82,8 +96,10 @@ impl<'a> Env<'a> {
let module_name = ModuleName::from(module_name_str);
match scope.modules.get_id(&module_name) {
Some(module_id) => self.qualified_lookup_help(scope, module_id, ident, region),
match scope.modules.lookup(&module_name) {
Some(lookedup_module) => {
self.qualified_lookup_help(scope, lookedup_module, ident, region)
}
None => Err(RuntimeError::ModuleNotImported {
module_name: module_name.clone(),
imported_modules: scope
@ -106,11 +122,11 @@ impl<'a> Env<'a> {
module_id: ModuleId,
ident: &str,
region: Region,
) -> Result<Symbol, RuntimeError> {
if !scope.modules.has_id(module_id) {
Err(self.module_exists_but_not_imported(scope, module_id, region))
) -> Result<SymbolLookup, RuntimeError> {
if let Some(module) = scope.modules.lookup_by_id(&module_id) {
self.qualified_lookup_help(scope, module, ident, region)
} else {
self.qualified_lookup_help(scope, module_id, ident, region)
Err(self.module_exists_but_not_imported(scope, module_id, region))
}
}
@ -118,18 +134,18 @@ impl<'a> Env<'a> {
fn qualified_lookup_help(
&mut self,
scope: &Scope,
module_id: ModuleId,
module: ModuleLookup,
ident: &str,
region: Region,
) -> Result<Symbol, RuntimeError> {
) -> Result<SymbolLookup, RuntimeError> {
let is_type_name = ident.starts_with(|c: char| c.is_uppercase());
// You can do qualified lookups on your own module, e.g.
// if I'm in the Foo module, I can do a `Foo.bar` lookup.
if module_id == self.home {
if module.id == self.home {
match scope.locals.ident_ids.get_id(ident) {
Some(ident_id) => {
let symbol = Symbol::new(module_id, ident_id);
let symbol = Symbol::new(module.id, ident_id);
if is_type_name {
self.qualified_type_lookups.insert(symbol);
@ -137,7 +153,7 @@ impl<'a> Env<'a> {
self.qualified_value_lookups.insert(symbol);
}
Ok(symbol)
Ok(SymbolLookup::no_params(symbol))
}
None => {
let error = RuntimeError::LookupNotInScope {
@ -157,10 +173,10 @@ impl<'a> Env<'a> {
}
}
} else {
match self.dep_idents.get(&module_id) {
match self.dep_idents.get(&module.id) {
Some(exposed_ids) => match exposed_ids.get_id(ident) {
Some(ident_id) => {
let symbol = Symbol::new(module_id, ident_id);
let symbol = Symbol::new(module.id, ident_id);
if is_type_name {
self.qualified_type_lookups.insert(symbol);
@ -168,12 +184,12 @@ impl<'a> Env<'a> {
self.qualified_value_lookups.insert(symbol);
}
Ok(symbol)
Ok(module.into_symbol(symbol))
}
None => Err(RuntimeError::ValueNotExposed {
module_name: self
.qualified_module_ids
.get_name(module_id)
.get_name(module.id)
.expect("Module ID known, but not in the module IDs somehow")
.as_inner()
.clone(),
@ -182,7 +198,7 @@ impl<'a> Env<'a> {
exposed_values: exposed_ids.exposed_values(),
}),
},
_ => Err(self.module_exists_but_not_imported(scope, module_id, region)),
_ => Err(self.module_exists_but_not_imported(scope, module.id, region)),
}
}
}
@ -213,4 +229,11 @@ impl<'a> Env<'a> {
pub fn problem(&mut self, problem: Problem) {
self.problems.push(problem)
}
pub fn line_info(&mut self) -> &LineInfo {
if self.line_info.is_none() {
*self.line_info = Some(LineInfo::new(self.src));
}
self.line_info.as_ref().unwrap()
}
}

View file

@ -7,9 +7,10 @@ use crate::num::{
finish_parsing_base, finish_parsing_float, finish_parsing_num, float_expr_from_result,
int_expr_from_result, num_expr_from_result, FloatBound, IntBound, NumBound,
};
use crate::params_in_abilities_unimplemented;
use crate::pattern::{canonicalize_pattern, BindingsFromPattern, Pattern, PermitShadows};
use crate::procedure::{QualifiedReference, References};
use crate::scope::Scope;
use crate::scope::{Scope, SymbolLookup};
use crate::traverse::{walk_expr, Visitor};
use roc_collections::soa::Index;
use roc_collections::{SendMap, VecMap, VecSet};
@ -17,7 +18,7 @@ use roc_error_macros::internal_error;
use roc_module::called_via::CalledVia;
use roc_module::ident::{ForeignSymbol, Lowercase, TagName};
use roc_module::low_level::LowLevel;
use roc_module::symbol::Symbol;
use roc_module::symbol::{IdentId, ModuleId, Symbol};
use roc_parse::ast::{self, Defs, PrecedenceConflict, StrLiteral};
use roc_parse::ident::Accessor;
use roc_parse::pattern::PatternType::*;
@ -107,6 +108,13 @@ pub enum Expr {
// Lookups
Var(Symbol, Variable),
/// Like Var, but from a module with params
ParamsVar {
symbol: Symbol,
var: Variable,
params_symbol: Symbol,
params_var: Variable,
},
AbilityMember(
/// Actual member name
Symbol,
@ -177,6 +185,9 @@ pub enum Expr {
elems: Vec<(Variable, Box<Loc<Expr>>)>,
},
/// Module params expression in import
ImportParams(ModuleId, Region, Option<(Variable, Box<Expr>)>),
/// The "crash" keyword
Crash {
msg: Box<Loc<Expr>>,
@ -308,6 +319,12 @@ impl Expr {
Self::SingleQuote(..) => Category::Character,
Self::List { .. } => Category::List,
&Self::Var(sym, _) => Category::Lookup(sym),
&Self::ParamsVar {
symbol,
var: _,
params_symbol: _,
params_var: _,
} => Category::Lookup(symbol),
&Self::AbilityMember(sym, _, _) => Category::Lookup(sym),
Self::When { .. } => Category::When,
Self::If { .. } => Category::If,
@ -324,6 +341,8 @@ impl Expr {
Self::RecordAccessor(data) => Category::Accessor(data.field.clone()),
Self::TupleAccess { index, .. } => Category::TupleAccess(*index),
Self::RecordUpdate { .. } => Category::Record,
Self::ImportParams(_, _, Some((_, expr))) => expr.category(),
Self::ImportParams(_, _, None) => Category::Unknown,
Self::Tag {
name, arguments, ..
} => Category::TagApply {
@ -631,34 +650,7 @@ pub fn canonicalize_expr<'a>(
(answer, Output::default())
}
ast::Expr::Record(fields) => {
if fields.is_empty() {
(EmptyRecord, Output::default())
} else {
match canonicalize_fields(env, var_store, scope, region, fields.items) {
Ok((can_fields, output)) => (
Record {
record_var: var_store.fresh(),
fields: can_fields,
},
output,
),
Err(CanonicalizeRecordProblem::InvalidOptionalValue {
field_name,
field_region,
record_region,
}) => (
Expr::RuntimeError(roc_problem::can::RuntimeError::InvalidOptionalValue {
field_name,
field_region,
record_region,
}),
Output::default(),
),
}
}
}
ast::Expr::Record(fields) => canonicalize_record(env, var_store, scope, region, *fields),
ast::Expr::RecordUpdate {
fields,
update: loc_update,
@ -1030,6 +1022,9 @@ pub fn canonicalize_expr<'a>(
ast::Expr::Backpassing(_, _, _) => {
internal_error!("Backpassing should have been desugared by now")
}
ast::Expr::RecordUpdater(_) => {
internal_error!("Record updater should have been desugared by now")
}
ast::Expr::Closure(loc_arg_patterns, loc_body_expr) => {
let (closure_data, output) =
canonicalize_closure(env, var_store, scope, loc_arg_patterns, loc_body_expr, None);
@ -1127,7 +1122,9 @@ pub fn canonicalize_expr<'a>(
output,
)
}
ast::Expr::TaskAwaitBang(..) => internal_error!("a Expr::TaskAwaitBang expression was not completely removed in desugar_value_def_suffixed"),
ast::Expr::TrySuffix { .. } => internal_error!(
"a Expr::TrySuffix expression was not completely removed in desugar_value_def_suffixed"
),
ast::Expr::Tag(tag) => {
let variant_var = var_store.fresh();
let ext_var = var_store.fresh();
@ -1212,8 +1209,20 @@ pub fn canonicalize_expr<'a>(
output,
)
}
ast::Expr::Dbg(_, _) => {
internal_error!("Dbg should have been desugared by now")
ast::Expr::Dbg => {
// Dbg was not desugared as either part of an `Apply` or a `Pizza` binop, so it's
// invalid.
env.problem(Problem::UnappliedDbg { region });
let invalid_dbg_expr = crate::desugar::desugar_invalid_dbg_expr(env, scope, region);
let (loc_expr, output) =
canonicalize_expr(env, var_store, scope, region, invalid_dbg_expr);
(loc_expr.value, output)
}
ast::Expr::DbgStmt(_, _) => {
internal_error!("DbgStmt should have been desugared by now")
}
ast::Expr::LowLevelDbg((source_location, source), message, continuation) => {
let mut output = Output::default();
@ -1252,7 +1261,11 @@ pub fn canonicalize_expr<'a>(
output,
)
}
ast::Expr::If(if_thens, final_else_branch) => {
ast::Expr::If {
if_thens,
final_else: final_else_branch,
..
} => {
let mut branches = Vec::with_capacity(if_thens.len());
let mut output = Output::default();
@ -1379,7 +1392,10 @@ pub fn canonicalize_expr<'a>(
use roc_problem::can::RuntimeError::*;
let sub_region = Region::span_across(&loc_name.region, &loc_value.region);
let problem = OptionalFieldInRecordBuilder {record: region, field: sub_region };
let problem = OptionalFieldInRecordBuilder {
record: region,
field: sub_region,
};
env.problem(Problem::RuntimeError(problem.clone()));
(RuntimeError(problem), Output::default())
@ -1452,6 +1468,42 @@ pub fn canonicalize_expr<'a>(
)
}
pub fn canonicalize_record<'a>(
env: &mut Env<'a>,
var_store: &mut VarStore,
scope: &mut Scope,
region: Region,
fields: ast::Collection<'a, Loc<ast::AssignedField<'a, ast::Expr<'a>>>>,
) -> (Expr, Output) {
use Expr::*;
if fields.is_empty() {
(EmptyRecord, Output::default())
} else {
match canonicalize_fields(env, var_store, scope, region, fields.items) {
Ok((can_fields, output)) => (
Record {
record_var: var_store.fresh(),
fields: can_fields,
},
output,
),
Err(CanonicalizeRecordProblem::InvalidOptionalValue {
field_name,
field_region,
record_region,
}) => (
Expr::RuntimeError(roc_problem::can::RuntimeError::InvalidOptionalValue {
field_name,
field_region,
record_region,
}),
Output::default(),
),
}
}
}
pub fn canonicalize_closure<'a>(
env: &mut Env<'a>,
var_store: &mut VarStore,
@ -1520,6 +1572,8 @@ fn canonicalize_closure_body<'a>(
&loc_body_expr.value,
);
let mut references_top_level = false;
let mut captured_symbols: Vec<_> = new_output
.references
.value_lookups()
@ -1530,7 +1584,11 @@ fn canonicalize_closure_body<'a>(
.filter(|s| !new_output.references.bound_symbols().any(|x| x == s))
.filter(|s| bound_by_argument_patterns.iter().all(|(k, _)| s != k))
// filter out top-level symbols those will be globally available, and don't need to be captured
.filter(|s| !env.top_level_symbols.contains(s))
.filter(|s| {
let is_top_level = env.top_level_symbols.contains(s);
references_top_level = references_top_level || is_top_level;
!is_top_level
})
// filter out imported symbols those will be globally available, and don't need to be captured
.filter(|s| s.module_id() == env.home)
// filter out functions that don't close over anything
@ -1539,6 +1597,15 @@ fn canonicalize_closure_body<'a>(
.map(|s| (s, var_store.fresh()))
.collect();
if references_top_level {
if let Some(params_record) = env.home_params_record {
// If this module has params and the closure references top-level symbols,
// we need to capture the whole record so we can pass it.
// The lower_params pass will take care of removing the captures for top-level fns.
captured_symbols.push(params_record);
}
}
output.union(new_output);
// Now that we've collected all the references, check to see if any of the args we defined
@ -1846,6 +1913,11 @@ fn canonicalize_field<'a>(
field_region: Region::span_across(&label.region, &loc_expr.region),
}),
// An ignored value, e.g. `{ _name: 123 }`
IgnoredValue(_, _, _) => {
internal_error!("Somehow an IgnoredValue record field was not desugared!");
}
// A label with no value, e.g. `{ name }` (this is sugar for { name: name })
LabelOnly(_) => {
internal_error!("Somehow a LabelOnly record field was not desugared!");
@ -1876,19 +1948,19 @@ fn canonicalize_var_lookup(
// Since module_name was empty, this is an unqualified var.
// Look it up in scope!
match scope.lookup_str(ident, region) {
Ok(symbol) => {
Ok(lookup) => {
output
.references
.insert_value_lookup(symbol, QualifiedReference::Unqualified);
.insert_value_lookup(lookup, QualifiedReference::Unqualified);
if scope.abilities_store.is_ability_member_name(symbol) {
if scope.abilities_store.is_ability_member_name(lookup.symbol) {
AbilityMember(
symbol,
params_in_abilities_unimplemented!(lookup),
Some(scope.abilities_store.fresh_specialization_id()),
var_store.fresh(),
)
} else {
Var(symbol, var_store.fresh())
lookup_to_expr(var_store, lookup)
}
}
Err(problem) => {
@ -1901,19 +1973,19 @@ fn canonicalize_var_lookup(
// Since module_name was nonempty, this is a qualified var.
// Look it up in the env!
match env.qualified_lookup(scope, module_name, ident, region) {
Ok(symbol) => {
Ok(lookup) => {
output
.references
.insert_value_lookup(symbol, QualifiedReference::Qualified);
.insert_value_lookup(lookup, QualifiedReference::Qualified);
if scope.abilities_store.is_ability_member_name(symbol) {
if scope.abilities_store.is_ability_member_name(lookup.symbol) {
AbilityMember(
symbol,
params_in_abilities_unimplemented!(lookup),
Some(scope.abilities_store.fresh_specialization_id()),
var_store.fresh(),
)
} else {
Var(symbol, var_store.fresh())
lookup_to_expr(var_store, lookup)
}
}
Err(problem) => {
@ -1931,6 +2003,25 @@ fn canonicalize_var_lookup(
(can_expr, output)
}
fn lookup_to_expr(
var_store: &mut VarStore,
SymbolLookup {
symbol,
module_params,
}: SymbolLookup,
) -> Expr {
if let Some((params_var, params_symbol)) = module_params {
Expr::ParamsVar {
symbol,
var: var_store.fresh(),
params_symbol,
params_var,
}
} else {
Expr::Var(symbol, var_store.fresh())
}
}
/// Currently uses the heuristic of "only inline if it's a builtin"
pub fn inline_calls(var_store: &mut VarStore, expr: Expr) -> Expr {
use Expr::*;
@ -1949,6 +2040,7 @@ pub fn inline_calls(var_store: &mut VarStore, expr: Expr) -> Expr {
| other @ RecordAccessor { .. }
| other @ RecordUpdate { .. }
| other @ Var(..)
| other @ ParamsVar { .. }
| other @ AbilityMember(..)
| other @ RunLowLevel { .. }
| other @ TypedHole { .. }
@ -2212,6 +2304,14 @@ pub fn inline_calls(var_store: &mut VarStore, expr: Expr) -> Expr {
);
}
ImportParams(module_id, region, Some((var, expr))) => ImportParams(
module_id,
region,
Some((var, Box::new(inline_calls(var_store, *expr)))),
),
ImportParams(module_id, region, None) => ImportParams(module_id, region, None),
RecordAccess {
record_var,
ext_var,
@ -2402,22 +2502,30 @@ pub fn is_valid_interpolation(expr: &ast::Expr<'_>) -> bool {
| ast::Expr::Num(_)
| ast::Expr::NonBase10Int { .. }
| ast::Expr::AccessorFunction(_)
| ast::Expr::RecordUpdater(_)
| ast::Expr::Crash
| ast::Expr::Dbg
| ast::Expr::Underscore(_)
| ast::Expr::MalformedIdent(_, _)
| ast::Expr::Tag(_)
| ast::Expr::OpaqueRef(_)
| ast::Expr::MalformedClosure => true,
// Newlines are disallowed inside interpolation, and these all require newlines
ast::Expr::Dbg(_, _)
ast::Expr::DbgStmt(_, _)
| ast::Expr::LowLevelDbg(_, _, _)
| ast::Expr::Defs(_, _)
| ast::Expr::Expect(_, _)
| ast::Expr::When(_, _)
| ast::Expr::Backpassing(_, _, _)
| ast::Expr::SpaceBefore(_, _)
| ast::Expr::Str(StrLiteral::Block(_))
| ast::Expr::SpaceAfter(_, _) => false,
// Desugared dbg expression
ast::Expr::Defs(_, loc_ret) => match loc_ret.value {
ast::Expr::LowLevelDbg(_, _, continuation) => {
is_valid_interpolation(&continuation.value)
}
_ => false,
},
// These can contain subexpressions, so we need to recursively check those
ast::Expr::Str(StrLiteral::Line(segments)) => {
segments.iter().all(|segment| match segment {
@ -2433,7 +2541,8 @@ pub fn is_valid_interpolation(expr: &ast::Expr<'_>) -> bool {
}
ast::Expr::Record(fields) => fields.iter().all(|loc_field| match loc_field.value {
ast::AssignedField::RequiredValue(_label, loc_comments, loc_val)
| ast::AssignedField::OptionalValue(_label, loc_comments, loc_val) => {
| ast::AssignedField::OptionalValue(_label, loc_comments, loc_val)
| ast::AssignedField::IgnoredValue(_label, loc_comments, loc_val) => {
loc_comments.is_empty() && is_valid_interpolation(&loc_val.value)
}
ast::AssignedField::Malformed(_) | ast::AssignedField::LabelOnly(_) => true,
@ -2454,7 +2563,7 @@ pub fn is_valid_interpolation(expr: &ast::Expr<'_>) -> bool {
ast::Expr::TupleAccess(sub_expr, _)
| ast::Expr::ParensAround(sub_expr)
| ast::Expr::RecordAccess(sub_expr, _)
| ast::Expr::TaskAwaitBang(sub_expr) => is_valid_interpolation(sub_expr),
| ast::Expr::TrySuffix { expr: sub_expr, .. } => is_valid_interpolation(sub_expr),
ast::Expr::Apply(loc_expr, args, _called_via) => {
is_valid_interpolation(&loc_expr.value)
&& args
@ -2467,7 +2576,11 @@ pub fn is_valid_interpolation(expr: &ast::Expr<'_>) -> bool {
.iter()
.all(|(loc_expr, _binop)| is_valid_interpolation(&loc_expr.value))
}
ast::Expr::If(branches, final_branch) => {
ast::Expr::If {
if_thens: branches,
final_else: final_branch,
..
} => {
is_valid_interpolation(&final_branch.value)
&& branches.iter().all(|(loc_before, loc_after)| {
is_valid_interpolation(&loc_before.value)
@ -2481,7 +2594,8 @@ pub fn is_valid_interpolation(expr: &ast::Expr<'_>) -> bool {
is_valid_interpolation(&update.value)
&& fields.iter().all(|loc_field| match loc_field.value {
ast::AssignedField::RequiredValue(_label, loc_comments, loc_val)
| ast::AssignedField::OptionalValue(_label, loc_comments, loc_val) => {
| ast::AssignedField::OptionalValue(_label, loc_comments, loc_val)
| ast::AssignedField::IgnoredValue(_label, loc_comments, loc_val) => {
loc_comments.is_empty() && is_valid_interpolation(&loc_val.value)
}
ast::AssignedField::Malformed(_) | ast::AssignedField::LabelOnly(_) => true,
@ -2514,7 +2628,8 @@ pub fn is_valid_interpolation(expr: &ast::Expr<'_>) -> bool {
is_valid_interpolation(&mapper.value)
&& fields.iter().all(|loc_field| match loc_field.value {
ast::AssignedField::RequiredValue(_label, loc_comments, loc_val)
| ast::AssignedField::OptionalValue(_label, loc_comments, loc_val) => {
| ast::AssignedField::OptionalValue(_label, loc_comments, loc_val)
| ast::AssignedField::IgnoredValue(_label, loc_comments, loc_val) => {
loc_comments.is_empty() && is_valid_interpolation(&loc_val.value)
}
ast::AssignedField::Malformed(_) | ast::AssignedField::LabelOnly(_) => true,
@ -2704,6 +2819,9 @@ pub struct Declarations {
// used for ability member specializatons.
pub specializes: VecMap<usize, Symbol>,
// used while lowering params.
arity_by_name: VecMap<IdentId, usize>,
pub host_exposed_annotations: VecMap<usize, (Variable, crate::def::Annotation)>,
pub function_bodies: Vec<Loc<FunctionDef>>,
@ -2732,6 +2850,7 @@ impl Declarations {
expressions: Vec::with_capacity(capacity),
specializes: VecMap::default(), // number of specializations is probably low
destructs: Vec::new(), // number of destructs is probably low
arity_by_name: VecMap::with_capacity(capacity),
}
}
@ -2770,6 +2889,9 @@ impl Declarations {
arguments: loc_closure_data.value.arguments,
};
self.arity_by_name
.insert(symbol.value.ident_id(), function_def.arguments.len());
let loc_function_def = Loc::at(loc_closure_data.region, function_def);
let function_def_index = Index::push_new(&mut self.function_bodies, loc_function_def);
@ -2818,6 +2940,9 @@ impl Declarations {
arguments: loc_closure_data.value.arguments,
};
self.arity_by_name
.insert(symbol.value.ident_id(), function_def.arguments.len());
let loc_function_def = Loc::at(loc_closure_data.region, function_def);
let function_def_index = Index::push_new(&mut self.function_bodies, loc_function_def);
@ -2894,6 +3019,8 @@ impl Declarations {
.insert(self.declarations.len(), annotation);
}
self.arity_by_name.insert(symbol.value.ident_id(), 0);
self.declarations.push(DeclarationTag::Value);
self.variables.push(expr_var);
self.symbols.push(symbol);
@ -3010,6 +3137,60 @@ impl Declarations {
}
}
/// Convert a value def to a function def with the given arguments
/// Currently used in lower_params
pub fn convert_value_to_function(
&mut self,
index: usize,
new_arguments: Vec<(Variable, AnnotatedMark, Loc<Pattern>)>,
var_store: &mut VarStore,
) {
match self.declarations[index] {
DeclarationTag::Value => {
let new_args_len = new_arguments.len();
let loc_body = self.expressions[index].clone();
let region = loc_body.region;
let closure_data = ClosureData {
function_type: var_store.fresh(),
closure_type: var_store.fresh(),
return_type: var_store.fresh(),
name: self.symbols[index].value,
captured_symbols: vec![],
recursive: Recursive::NotRecursive,
arguments: new_arguments,
loc_body: Box::new(loc_body),
};
let loc_closure_data = Loc::at(region, closure_data);
let function_def = FunctionDef {
closure_type: loc_closure_data.value.closure_type,
return_type: loc_closure_data.value.return_type,
captured_symbols: loc_closure_data.value.captured_symbols,
arguments: loc_closure_data.value.arguments,
};
let loc_function_def = Loc::at(region, function_def);
let function_def_index =
Index::push_new(&mut self.function_bodies, loc_function_def);
if let Some(annotation) = &mut self.annotations[index] {
annotation.convert_to_fn(new_args_len, var_store);
}
if let Some((_var, annotation)) = self.host_exposed_annotations.get_mut(&index) {
annotation.convert_to_fn(new_args_len, var_store);
}
self.declarations[index] = DeclarationTag::Function(function_def_index);
}
_ => internal_error!("Expected value declaration"),
};
}
pub fn len(&self) -> usize {
self.declarations.len()
}
@ -3079,6 +3260,11 @@ impl Declarations {
collector
}
pub(crate) fn take_arity_by_name(&mut self) -> VecMap<IdentId, usize> {
// `arity_by_name` is only needed for lowering module params
std::mem::take(&mut self.arity_by_name)
}
}
roc_error_macros::assert_sizeof_default!(DeclarationTag, 8);
@ -3133,6 +3319,12 @@ pub(crate) fn get_lookup_symbols(expr: &Expr) -> Vec<ExpectLookup> {
while let Some(expr) = stack.pop() {
match expr {
Expr::Var(symbol, var)
| Expr::ParamsVar {
symbol,
var,
params_symbol: _,
params_var: _,
}
| Expr::RecordUpdate {
symbol,
record_var: var,
@ -3237,6 +3429,9 @@ pub(crate) fn get_lookup_symbols(expr: &Expr) -> Vec<ExpectLookup> {
Expr::Tuple { elems, .. } => {
stack.extend(elems.iter().map(|(_, elem)| &elem.value));
}
Expr::ImportParams(_, _, Some((_, expr))) => {
stack.push(expr);
}
Expr::Expect {
loc_continuation, ..
}
@ -3263,6 +3458,7 @@ pub(crate) fn get_lookup_symbols(expr: &Expr) -> Vec<ExpectLookup> {
| Expr::EmptyRecord
| Expr::TypedHole(_)
| Expr::RuntimeError(_)
| Expr::ImportParams(_, _, None)
| Expr::OpaqueWrapFunction(_) => {}
}
}

View file

@ -14,7 +14,6 @@ pub mod copy;
pub mod def;
mod derive;
pub mod desugar;
pub mod effect_module;
pub mod env;
pub mod exhaustive;
pub mod expected;
@ -26,6 +25,7 @@ pub mod procedure;
pub mod scope;
pub mod string;
pub mod suffixed;
pub mod task_module;
pub mod traverse;
pub use derive::DERIVED_REGION;

View file

@ -3,12 +3,13 @@ use std::path::Path;
use crate::abilities::{AbilitiesStore, ImplKey, PendingAbilitiesStore, ResolvedImpl};
use crate::annotation::{canonicalize_annotation, AnnotationFor};
use crate::def::{canonicalize_defs, report_unused_imports, Def};
use crate::effect_module::HostedGeneratedFunctions;
use crate::env::Env;
use crate::expr::{
ClosureData, DbgLookup, Declarations, ExpectLookup, Expr, Output, PendingDerives,
};
use crate::pattern::{BindingsFromPattern, Pattern};
use crate::pattern::{
canonicalize_record_destructs, BindingsFromPattern, Pattern, PermitShadows, RecordDestruct,
};
use crate::procedure::References;
use crate::scope::Scope;
use bumpalo::Bump;
@ -16,14 +17,14 @@ use roc_collections::{MutMap, SendMap, VecMap, VecSet};
use roc_error_macros::internal_error;
use roc_module::ident::Ident;
use roc_module::ident::Lowercase;
use roc_module::symbol::{IdentIds, IdentIdsByModule, ModuleId, PackageModuleIds, Symbol};
use roc_module::symbol::{IdentId, IdentIds, IdentIdsByModule, ModuleId, PackageModuleIds, Symbol};
use roc_parse::ast::{Defs, TypeAnnotation};
use roc_parse::header::HeaderType;
use roc_parse::pattern::PatternType;
use roc_problem::can::{Problem, RuntimeError};
use roc_region::all::{Loc, Region};
use roc_types::subs::{ExposedTypesStorageSubs, Subs, VarStore, Variable};
use roc_types::types::{AbilitySet, Alias, AliasKind, AliasVar, Type};
use roc_types::types::{AbilitySet, Alias, Type};
/// The types of all exposed values/functions of a collection of modules
#[derive(Clone, Debug, Default)]
@ -136,6 +137,33 @@ pub struct Module {
pub abilities_store: PendingAbilitiesStore,
pub loc_expects: VecMap<Region, Vec<ExpectLookup>>,
pub loc_dbgs: VecMap<Symbol, DbgLookup>,
pub module_params: Option<ModuleParams>,
}
#[derive(Debug, Clone)]
pub struct ModuleParams {
pub region: Region,
pub whole_symbol: Symbol,
pub whole_var: Variable,
pub record_var: Variable,
pub record_ext_var: Variable,
pub destructs: Vec<Loc<RecordDestruct>>,
// used while lowering passed functions
pub arity_by_name: VecMap<IdentId, usize>,
}
impl ModuleParams {
pub fn pattern(&self) -> Loc<Pattern> {
let record_pattern = Pattern::RecordDestructure {
whole_var: self.record_var,
ext_var: self.record_ext_var,
destructs: self.destructs.clone(),
};
let loc_record_pattern = Loc::at(self.region, record_pattern);
let as_pattern = Pattern::As(Box::new(loc_record_pattern), self.whole_symbol);
Loc::at(self.region, as_pattern)
}
}
#[derive(Debug, Default)]
@ -149,6 +177,7 @@ pub struct RigidVariables {
pub struct ModuleOutput {
pub aliases: MutMap<Symbol, Alias>,
pub rigid_variables: RigidVariables,
pub module_params: Option<ModuleParams>,
pub declarations: Declarations,
pub exposed_imports: MutMap<Symbol, Region>,
pub exposed_symbols: VecSet<Symbol>,
@ -161,98 +190,6 @@ pub struct ModuleOutput {
pub loc_dbgs: VecMap<Symbol, DbgLookup>,
}
fn validate_generate_with<'a>(
generate_with: &'a [Loc<roc_parse::header::ExposedName<'a>>],
) -> (HostedGeneratedFunctions, Vec<Loc<Ident>>) {
let mut functions = HostedGeneratedFunctions::default();
let mut unknown = Vec::new();
for generated in generate_with {
match generated.value.as_str() {
"after" => functions.after = true,
"map" => functions.map = true,
"always" => functions.always = true,
"loop" => functions.loop_ = true,
"forever" => functions.forever = true,
other => {
// we don't know how to generate this function
let ident = Ident::from(other);
unknown.push(Loc::at(generated.region, ident));
}
}
}
(functions, unknown)
}
#[derive(Debug)]
enum GeneratedInfo {
Hosted {
effect_symbol: Symbol,
generated_functions: HostedGeneratedFunctions,
},
Builtin,
NotSpecial,
}
impl GeneratedInfo {
fn from_header_type(
env: &mut Env,
scope: &mut Scope,
var_store: &mut VarStore,
header_type: &HeaderType,
) -> Self {
match header_type {
HeaderType::Hosted {
generates,
generates_with,
name: _,
exposes: _,
} => {
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(), Region::zero()).unwrap();
{
let a_var = var_store.fresh();
let actual =
crate::effect_module::build_effect_actual(Type::Variable(a_var), var_store);
scope.add_alias(
effect_symbol,
Region::zero(),
vec![Loc::at_zero(AliasVar::unbound("a".into(), a_var))],
vec![],
actual,
AliasKind::Opaque,
);
}
GeneratedInfo::Hosted {
effect_symbol,
generated_functions,
}
}
HeaderType::Builtin {
generates_with,
name: _,
exposes: _,
} => {
debug_assert!(generates_with.is_empty());
GeneratedInfo::Builtin
}
_ => GeneratedInfo::NotSpecial,
}
}
}
fn has_no_implementation(expr: &Expr) -> bool {
match expr {
Expr::RuntimeError(RuntimeError::NoImplementationNamed { .. }) => true,
@ -274,7 +211,7 @@ fn has_no_implementation(expr: &Expr) -> bool {
pub fn canonicalize_module_defs<'a>(
arena: &'a Bump,
loc_defs: &'a mut Defs<'a>,
header_type: &roc_parse::header::HeaderType,
header_type: &'a roc_parse::header::HeaderType,
home: ModuleId,
module_path: &'a str,
src: &'a str,
@ -290,6 +227,7 @@ pub fn canonicalize_module_defs<'a>(
opt_shorthand: Option<&'a str>,
) -> ModuleOutput {
let mut can_exposed_imports = MutMap::default();
let mut scope = Scope::new(
home,
qualified_module_ids
@ -302,6 +240,7 @@ pub fn canonicalize_module_defs<'a>(
);
let mut env = Env::new(
arena,
src,
home,
arena.alloc(Path::new(module_path)),
dep_idents,
@ -320,9 +259,6 @@ pub fn canonicalize_module_defs<'a>(
);
}
let generated_info =
GeneratedInfo::from_header_type(&mut env, &mut scope, var_store, header_type);
// Desugar operators (convert them to Apply calls, taking into account
// operator precedence and associativity rules), before doing other canonicalization.
//
@ -331,11 +267,11 @@ pub fn canonicalize_module_defs<'a>(
// operators, and then again on *their* nested operators, ultimately applying the
// rules multiple times unnecessarily.
crate::desugar::desugar_defs_node_values(arena, loc_defs, src, &mut None, module_path, true);
crate::desugar::desugar_defs_node_values(&mut env, &mut scope, loc_defs, true);
let mut rigid_variables = RigidVariables::default();
// Iniital scope values are treated like defs that appear before any others.
// Initial scope values are treated like defs that appear before any others.
// They include builtin types that are automatically imported, and for a platform
// package, the required values from the app.
//
@ -382,9 +318,47 @@ pub fn canonicalize_module_defs<'a>(
}
}
let mut output = Output::default();
let module_params = header_type.get_params().as_ref().map(
|roc_parse::header::ModuleParams {
pattern,
before_arrow: _,
after_arrow: _,
}| {
let (destructs, _) = canonicalize_record_destructs(
&mut env,
var_store,
&mut scope,
&mut output,
PatternType::ModuleParams,
&pattern.value,
pattern.region,
PermitShadows(false),
);
let whole_symbol = scope.gen_unique_symbol();
env.top_level_symbols.insert(whole_symbol);
let whole_var = var_store.fresh();
env.home_params_record = Some((whole_symbol, whole_var));
ModuleParams {
region: pattern.region,
whole_var,
whole_symbol,
record_var: var_store.fresh(),
record_ext_var: var_store.fresh(),
destructs,
arity_by_name: Default::default(),
}
},
);
let (defs, output, symbols_introduced, imports_introduced) = canonicalize_defs(
&mut env,
Output::default(),
output,
var_store,
&mut scope,
loc_defs,
@ -456,6 +430,11 @@ pub fn canonicalize_module_defs<'a>(
&exposed_symbols,
);
let module_params = module_params.map(|params| ModuleParams {
arity_by_name: declarations.take_arity_by_name(),
..params
});
debug_assert!(
output.pending_derives.is_empty(),
"I thought pending derives are only found during def introduction"
@ -495,24 +474,6 @@ pub fn canonicalize_module_defs<'a>(
report_unused_imports(imports_introduced, &output.references, &mut env, &mut scope);
if let GeneratedInfo::Hosted {
effect_symbol,
generated_functions,
} = generated_info
{
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(
&mut scope,
effect_symbol,
var_store,
&mut exposed_symbols,
&mut declarations,
generated_functions,
);
}
for index in 0..declarations.len() {
use crate::expr::DeclarationTag::*;
@ -533,8 +494,8 @@ pub fn canonicalize_module_defs<'a>(
// 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 has_no_implementation(&declarations.expressions[index].value) {
match generated_info {
GeneratedInfo::Builtin => {
match header_type {
HeaderType::Builtin { .. } => {
match crate::builtins::builtin_defs_map(*symbol, var_store) {
None => {
internal_error!("A builtin module contains a signature without implementation for {:?}", symbol)
@ -544,7 +505,7 @@ pub fn canonicalize_module_defs<'a>(
}
}
}
GeneratedInfo::Hosted { effect_symbol, .. } => {
HeaderType::Hosted { .. } => {
let ident_id = symbol.ident_id();
let ident = scope
.locals
@ -562,13 +523,8 @@ pub fn canonicalize_module_defs<'a>(
aliases: Default::default(),
};
let hosted_def = crate::effect_module::build_host_exposed_def(
&mut scope,
*symbol,
&ident,
effect_symbol,
var_store,
annotation,
let hosted_def = crate::task_module::build_host_exposed_def(
&mut scope, *symbol, &ident, var_store, annotation,
);
declarations.update_builtin_def(index, hosted_def);
@ -591,8 +547,8 @@ pub fn canonicalize_module_defs<'a>(
// 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 has_no_implementation(&declarations.expressions[index].value) {
match generated_info {
GeneratedInfo::Builtin => {
match header_type {
HeaderType::Builtin { .. } => {
match crate::builtins::builtin_defs_map(*symbol, var_store) {
None => {
internal_error!("A builtin module contains a signature without implementation for {:?}", symbol)
@ -602,7 +558,7 @@ pub fn canonicalize_module_defs<'a>(
}
}
}
GeneratedInfo::Hosted { effect_symbol, .. } => {
HeaderType::Hosted { .. } => {
let ident_id = symbol.ident_id();
let ident = scope
.locals
@ -620,13 +576,8 @@ pub fn canonicalize_module_defs<'a>(
aliases: Default::default(),
};
let hosted_def = crate::effect_module::build_host_exposed_def(
&mut scope,
*symbol,
&ident,
effect_symbol,
var_store,
annotation,
let hosted_def = crate::task_module::build_host_exposed_def(
&mut scope, *symbol, &ident, var_store, annotation,
);
declarations.update_builtin_def(index, hosted_def);
@ -652,18 +603,6 @@ pub fn canonicalize_module_defs<'a>(
let mut aliases = MutMap::default();
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
// exposed symbols which did not have
// corresponding defs.
exposed_but_not_defined.remove(&effect_symbol);
let hosted_alias = scope.lookup_alias(effect_symbol).unwrap().clone();
aliases.insert(effect_symbol, hosted_alias);
}
for (symbol, alias) in output.aliases {
// Remove this from exposed_symbols,
// so that at the end of the process,
@ -812,6 +751,7 @@ pub fn canonicalize_module_defs<'a>(
scope,
aliases,
rigid_variables,
module_params,
declarations,
referenced_values,
exposed_imports: can_exposed_imports,
@ -1105,6 +1045,7 @@ fn fix_values_captured_in_closure_expr(
| SingleQuote(..)
| IngestedFile(..)
| Var(..)
| ParamsVar { .. }
| AbilityMember(..)
| EmptyRecord
| TypedHole { .. }
@ -1216,6 +1157,12 @@ fn fix_values_captured_in_closure_expr(
}
}
ImportParams(_, _, Some((_, expr))) => {
fix_values_captured_in_closure_expr(expr, no_capture_symbols, closure_captures);
}
ImportParams(_, _, None) => {}
Tuple { elems, .. } => {
for (_var, expr) in elems.iter_mut() {
fix_values_captured_in_closure_expr(

View file

@ -626,120 +626,17 @@ pub fn canonicalize_pattern<'a>(
RecordDestructure(patterns) => {
let ext_var = var_store.fresh();
let whole_var = var_store.fresh();
let mut destructs = Vec::with_capacity(patterns.len());
let mut opt_erroneous = None;
for loc_pattern in patterns.iter() {
match loc_pattern.value {
Identifier { ident: label } => {
match scope.introduce(label.into(), region) {
Ok(symbol) => {
output.references.insert_bound(symbol);
destructs.push(Loc {
region: loc_pattern.region,
value: RecordDestruct {
var: var_store.fresh(),
label: Lowercase::from(label),
symbol,
typ: DestructType::Required,
},
});
}
Err((shadowed_symbol, shadow, new_symbol)) => {
env.problem(Problem::RuntimeError(RuntimeError::Shadowing {
original_region: shadowed_symbol.region,
shadow: shadow.clone(),
kind: ShadowKind::Variable,
}));
// No matter what the other patterns
// are, we're definitely shadowed and will
// get a runtime exception as soon as we
// encounter the first bad pattern.
opt_erroneous = Some(Pattern::Shadowed(
shadowed_symbol.region,
shadow,
new_symbol,
));
}
};
}
RequiredField(label, loc_guard) => {
// a guard does not introduce the label into scope!
let symbol =
scope.scopeless_symbol(&Ident::from(label), loc_pattern.region);
let can_guard = canonicalize_pattern(
env,
var_store,
scope,
output,
pattern_type,
&loc_guard.value,
loc_guard.region,
permit_shadows,
);
destructs.push(Loc {
region: loc_pattern.region,
value: RecordDestruct {
var: var_store.fresh(),
label: Lowercase::from(label),
symbol,
typ: DestructType::Guard(var_store.fresh(), can_guard),
},
});
}
OptionalField(label, loc_default) => {
// an optional DOES introduce the label into scope!
match scope.introduce(label.into(), region) {
Ok(symbol) => {
let (can_default, expr_output) = canonicalize_expr(
env,
var_store,
scope,
loc_default.region,
&loc_default.value,
);
// an optional field binds the symbol!
output.references.insert_bound(symbol);
output.union(expr_output);
destructs.push(Loc {
region: loc_pattern.region,
value: RecordDestruct {
var: var_store.fresh(),
label: Lowercase::from(label),
symbol,
typ: DestructType::Optional(var_store.fresh(), can_default),
},
});
}
Err((shadowed_symbol, shadow, new_symbol)) => {
env.problem(Problem::RuntimeError(RuntimeError::Shadowing {
original_region: shadowed_symbol.region,
shadow: shadow.clone(),
kind: ShadowKind::Variable,
}));
// No matter what the other patterns
// are, we're definitely shadowed and will
// get a runtime exception as soon as we
// encounter the first bad pattern.
opt_erroneous = Some(Pattern::Shadowed(
shadowed_symbol.region,
shadow,
new_symbol,
));
}
};
}
_ => unreachable!("Any other pattern should have given a parse error"),
}
}
let (destructs, opt_erroneous) = canonicalize_record_destructs(
env,
var_store,
scope,
output,
pattern_type,
patterns,
region,
permit_shadows,
);
// If we encountered an erroneous pattern (e.g. one with shadowing),
// use the resulting RuntimeError. Otherwise, return a successful record destructure.
@ -894,6 +791,136 @@ pub fn canonicalize_pattern<'a>(
}
}
#[allow(clippy::too_many_arguments)]
pub fn canonicalize_record_destructs<'a>(
env: &mut Env<'a>,
var_store: &mut VarStore,
scope: &mut Scope,
output: &mut Output,
pattern_type: PatternType,
patterns: &ast::Collection<Loc<ast::Pattern<'a>>>,
region: Region,
permit_shadows: PermitShadows,
) -> (Vec<Loc<RecordDestruct>>, Option<Pattern>) {
use ast::Pattern::*;
let mut destructs = Vec::with_capacity(patterns.len());
let mut opt_erroneous = None;
for loc_pattern in patterns.iter() {
match loc_pattern.value {
Identifier { ident: label } => {
match scope.introduce(label.into(), region) {
Ok(symbol) => {
output.references.insert_bound(symbol);
destructs.push(Loc {
region: loc_pattern.region,
value: RecordDestruct {
var: var_store.fresh(),
label: Lowercase::from(label),
symbol,
typ: DestructType::Required,
},
});
}
Err((shadowed_symbol, shadow, new_symbol)) => {
env.problem(Problem::RuntimeError(RuntimeError::Shadowing {
original_region: shadowed_symbol.region,
shadow: shadow.clone(),
kind: ShadowKind::Variable,
}));
// No matter what the other patterns
// are, we're definitely shadowed and will
// get a runtime exception as soon as we
// encounter the first bad pattern.
opt_erroneous = Some(Pattern::Shadowed(
shadowed_symbol.region,
shadow,
new_symbol,
));
}
};
}
RequiredField(label, loc_guard) => {
// a guard does not introduce the label into scope!
let symbol = scope.scopeless_symbol(&Ident::from(label), loc_pattern.region);
let can_guard = canonicalize_pattern(
env,
var_store,
scope,
output,
pattern_type,
&loc_guard.value,
loc_guard.region,
permit_shadows,
);
destructs.push(Loc {
region: loc_pattern.region,
value: RecordDestruct {
var: var_store.fresh(),
label: Lowercase::from(label),
symbol,
typ: DestructType::Guard(var_store.fresh(), can_guard),
},
});
}
OptionalField(label, loc_default) => {
// an optional DOES introduce the label into scope!
match scope.introduce(label.into(), region) {
Ok(symbol) => {
let (can_default, expr_output) = canonicalize_expr(
env,
var_store,
scope,
loc_default.region,
&loc_default.value,
);
// an optional field binds the symbol!
output.references.insert_bound(symbol);
output.union(expr_output);
destructs.push(Loc {
region: loc_pattern.region,
value: RecordDestruct {
var: var_store.fresh(),
label: Lowercase::from(label),
symbol,
typ: DestructType::Optional(var_store.fresh(), can_default),
},
});
}
Err((shadowed_symbol, shadow, new_symbol)) => {
env.problem(Problem::RuntimeError(RuntimeError::Shadowing {
original_region: shadowed_symbol.region,
shadow: shadow.clone(),
kind: ShadowKind::Variable,
}));
// No matter what the other patterns
// are, we're definitely shadowed and will
// get a runtime exception as soon as we
// encounter the first bad pattern.
opt_erroneous = Some(Pattern::Shadowed(
shadowed_symbol.region,
shadow,
new_symbol,
));
}
};
}
_ => unreachable!("Any other pattern should have given a parse error"),
}
}
(destructs, opt_erroneous)
}
/// When we detect an unsupported pattern type (e.g. 5 = 1 + 2 is unsupported because you can't
/// assign to Int patterns), report it to Env and return an UnsupportedPattern runtime error pattern.
fn unsupported_pattern(env: &mut Env, pattern_type: PatternType, region: Region) -> Pattern {

View file

@ -1,5 +1,5 @@
use crate::expr::Expr;
use crate::pattern::Pattern;
use crate::{expr::Expr, scope::SymbolLookup};
use roc_module::symbol::{ModuleId, Symbol};
use roc_region::all::{Loc, Region};
use roc_types::subs::Variable;
@ -125,8 +125,18 @@ impl References {
}
}
pub fn insert_value_lookup(&mut self, symbol: Symbol, qualified: QualifiedReference) {
self.insert(symbol, qualified.flags(ReferencesBitflags::VALUE_LOOKUP));
pub fn insert_value_lookup(&mut self, lookup: SymbolLookup, qualified: QualifiedReference) {
self.insert(
lookup.symbol,
qualified.flags(ReferencesBitflags::VALUE_LOOKUP),
);
if let Some((_, params_symbol)) = lookup.module_params {
self.insert(
params_symbol,
qualified.flags(ReferencesBitflags::VALUE_LOOKUP),
);
}
}
pub fn insert_type_lookup(&mut self, symbol: Symbol, qualified: QualifiedReference) {

View file

@ -1,8 +1,8 @@
use roc_collections::{VecMap, VecSet};
use roc_error_macros::internal_error;
use roc_module::ident::{Ident, ModuleName};
use roc_module::symbol::{IdentId, IdentIds, ModuleId, ScopeModules, Symbol};
use roc_problem::can::RuntimeError;
use roc_module::symbol::{IdentId, IdentIds, ModuleId, ModuleIds, Symbol};
use roc_problem::can::{RuntimeError, ScopeModuleSource};
use roc_region::all::{Loc, Region};
use roc_types::subs::Variable;
use roc_types::types::{Alias, AliasKind, AliasVar, Type};
@ -76,7 +76,7 @@ impl Scope {
}
}
pub fn lookup(&self, ident: &Ident, region: Region) -> Result<Symbol, RuntimeError> {
pub fn lookup(&self, ident: &Ident, region: Region) -> Result<SymbolLookup, RuntimeError> {
self.lookup_str(ident.as_str(), region)
}
@ -91,7 +91,7 @@ impl Scope {
.push(("Set".into(), Symbol::SET_SET, Region::zero()));
}
pub fn lookup_str(&self, ident: &str, region: Region) -> Result<Symbol, RuntimeError> {
pub fn lookup_str(&self, ident: &str, region: Region) -> Result<SymbolLookup, RuntimeError> {
use ContainsIdent::*;
match self.scope_contains_ident(ident) {
@ -205,14 +205,19 @@ impl Scope {
}
}
fn has_imported_symbol(&self, ident: &str) -> Option<(Symbol, Region)> {
for (import, shadow, original_region) in self.imported_symbols.iter() {
if ident == import.as_str() {
return Some((*shadow, *original_region));
}
}
None
fn has_imported_symbol(&self, ident: &str) -> Option<(SymbolLookup, Region)> {
self.imported_symbols
.iter()
.find_map(|(import, symbol, original_region)| {
if ident == import.as_str() {
match self.modules.lookup_by_id(&symbol.module_id()) {
Some(module) => Some((module.into_symbol(*symbol), *original_region)),
None => Some((SymbolLookup::no_params(*symbol), *original_region)),
}
} else {
None
}
})
}
/// Is an identifier in scope, either in the locals or imports
@ -229,7 +234,7 @@ impl Scope {
ContainsIdent::InScope(original_symbol, original_region) => {
// the ident is already in scope; up to the caller how to handle that
// (usually it's shadowing, but it is valid to shadow ability members)
Err((original_symbol, original_region))
Err((original_symbol.symbol, original_region))
}
ContainsIdent::NotPresent => {
// We know nothing about this ident yet; introduce it to the scope
@ -389,7 +394,13 @@ impl Scope {
region: Region,
) -> Result<(), (Symbol, Region)> {
match self.scope_contains_ident(ident.as_str()) {
ContainsIdent::InScope(symbol, region) => Err((symbol, region)),
ContainsIdent::InScope(
SymbolLookup {
symbol,
module_params: _,
},
region,
) => Err((symbol, region)),
ContainsIdent::NotPresent | ContainsIdent::NotInScope(_) => {
self.imported_symbols.push((ident, symbol, region));
Ok(())
@ -455,7 +466,7 @@ impl Scope {
self.home.register_debug_idents(&self.locals.ident_ids)
}
/// Generates a unique, new symbol like "$1" or "$5",
/// Generates a unique, new symbol like "1" or "5",
/// using the home module as the module_id.
///
/// This is used, for example, during canonicalization of an Expr::Closure
@ -464,6 +475,12 @@ impl Scope {
Symbol::new(self.home, self.locals.gen_unique())
}
/// Generates a unique new symbol and return the symbol's unqualified identifier name.
pub fn gen_unique_symbol_name(&mut self) -> &str {
let ident_id = self.locals.gen_unique();
self.locals.ident_ids.get_name(ident_id).unwrap()
}
/// Introduce a new ignored variable (variable starting with an underscore).
/// The underscore itself should not be included in `ident`.
pub fn introduce_ignored_local(&mut self, ident: &str, region: Region) {
@ -534,7 +551,7 @@ pub fn create_alias(
#[derive(Debug)]
enum ContainsIdent {
InScope(Symbol, Region),
InScope(SymbolLookup, Region),
NotInScope(IdentId),
NotPresent,
}
@ -561,7 +578,7 @@ impl ScopedIdentIds {
fn has_in_scope(&self, ident: &Ident) -> Option<(Symbol, Region)> {
match self.contains_ident(ident.as_str()) {
ContainsIdent::InScope(symbol, region) => Some((symbol, region)),
ContainsIdent::InScope(symbol, region) => Some((symbol.symbol, region)),
ContainsIdent::NotInScope(_) | ContainsIdent::NotPresent => None,
}
}
@ -574,7 +591,10 @@ impl ScopedIdentIds {
for ident_id in self.ident_ids.get_id_many(ident) {
let index = ident_id.index();
if self.in_scope[index] {
return InScope(Symbol::new(self.home, ident_id), self.regions[index]);
return InScope(
SymbolLookup::no_params(Symbol::new(self.home, ident_id)),
self.regions[index],
);
} else {
result = NotInScope(ident_id)
}
@ -646,6 +666,149 @@ impl ScopedIdentIds {
}
}
#[derive(Debug, Clone)]
pub struct ScopeModules {
/// The ids of all modules in scope
ids: Vec<ModuleId>,
/// The alias or original name of each module in scope
names: Vec<ModuleName>,
/// Why is this module in scope?
sources: Vec<ScopeModuleSource>,
/// The params of a module if any
params: Vec<Option<(Variable, Symbol)>>,
}
impl ScopeModules {
pub fn new(home_id: ModuleId, home_name: ModuleName) -> Self {
let builtins = ModuleIds::default();
let builtins_iter = builtins.iter();
let count = builtins_iter.len();
let mut ids = Vec::with_capacity(count + 1);
let mut names = Vec::with_capacity(count + 1);
let mut sources = vec![ScopeModuleSource::Builtin; count];
let mut params = vec![None; count];
for (module_id, module_name) in builtins_iter {
ids.push(module_id);
names.push(module_name.clone());
}
if !home_id.is_builtin() {
ids.push(home_id);
names.push(home_name);
sources.push(ScopeModuleSource::Current);
params.push(None);
}
Self {
ids,
names,
sources,
params,
}
}
pub fn lookup(&self, module_name: &ModuleName) -> Option<ModuleLookup> {
self.names
.iter()
.position(|name| name == module_name)
.map(|index| ModuleLookup {
id: self.ids[index],
params: self.params[index],
})
}
pub fn lookup_by_id(&self, module_id: &ModuleId) -> Option<ModuleLookup> {
self.ids
.iter()
.position(|id| id == module_id)
.map(|index| ModuleLookup {
id: self.ids[index],
params: self.params[index],
})
}
pub fn available_names(&self) -> impl Iterator<Item = &ModuleName> {
self.names.iter()
}
pub fn insert(
&mut self,
module_name: ModuleName,
module_id: ModuleId,
params: Option<(Variable, Symbol)>,
region: Region,
) -> Result<(), ScopeModuleSource> {
if let Some(index) = self.names.iter().position(|name| name == &module_name) {
if self.ids[index] == module_id {
return Ok(());
}
return Err(self.sources[index]);
}
self.ids.push(module_id);
self.names.push(module_name);
self.sources.push(ScopeModuleSource::Import(region));
self.params.push(params);
Ok(())
}
pub fn len(&self) -> usize {
debug_assert_eq!(self.ids.len(), self.names.len());
debug_assert_eq!(self.ids.len(), self.sources.len());
debug_assert_eq!(self.ids.len(), self.params.len());
self.ids.len()
}
pub fn is_empty(&self) -> bool {
self.ids.is_empty()
}
pub fn truncate(&mut self, len: usize) {
self.ids.truncate(len);
self.names.truncate(len);
self.sources.truncate(len);
self.params.truncate(len);
}
}
#[derive(Debug, Clone, Copy)]
pub struct SymbolLookup {
pub symbol: Symbol,
pub module_params: Option<(Variable, Symbol)>,
}
impl SymbolLookup {
pub fn new(symbol: Symbol, params: Option<(Variable, Symbol)>) -> Self {
Self {
symbol,
module_params: params,
}
}
pub fn no_params(symbol: Symbol) -> Self {
Self::new(symbol, None)
}
}
pub struct ModuleLookup {
pub id: ModuleId,
pub params: Option<(Variable, Symbol)>,
}
impl ModuleLookup {
pub fn into_symbol(&self, symbol: Symbol) -> SymbolLookup {
debug_assert_eq!(symbol.module_id(), self.id);
SymbolLookup {
symbol,
module_params: self.params,
}
}
}
#[cfg(test)]
mod test {
use super::*;
@ -701,7 +864,7 @@ mod test {
let lookup = scope.lookup(&ident, Region::zero()).unwrap();
assert_eq!(first, lookup);
assert_eq!(first, lookup.symbol);
}
#[test]
@ -857,6 +1020,6 @@ mod test {
let lookup = scope.lookup(&ident, Region::zero()).unwrap();
assert_eq!(symbol, lookup);
assert_eq!(symbol, lookup.symbol);
}
}

View file

@ -6,7 +6,7 @@ use roc_error_macros::internal_error;
use roc_module::called_via::CalledVia;
use roc_module::ident::ModuleName;
use roc_parse::ast::Expr::{self, *};
use roc_parse::ast::{is_expr_suffixed, Pattern, TypeAnnotation, ValueDef, WhenBranch};
use roc_parse::ast::{is_expr_suffixed, Pattern, TryTarget, TypeAnnotation, ValueDef, WhenBranch};
use roc_region::all::{Loc, Region};
use std::cell::Cell;
@ -34,14 +34,17 @@ pub enum EUnwrapped<'a> {
/// e.g. x = first! (second! 42)
/// The first unwrap will produce
/// `UnwrappedDefExpr<first (second! 42)>`
UnwrappedDefExpr(&'a Loc<Expr<'a>>),
UnwrappedDefExpr {
loc_expr: &'a Loc<Expr<'a>>,
target: TryTarget,
},
/// Suffixed sub expression
/// e.g. x = first! (second! 42)
/// In this example, the second unwrap (after unwrapping the top level `first!`) will produce
/// `UnwrappedSubExpr<{ sub_arg: second 42, sub_pat: #!0_arg, sub_new: #!0_arg }>`
UnwrappedSubExpr {
/// the unwrapped expression argument for Task.await
/// the unwrapped expression argument for `try` functions
sub_arg: &'a Loc<Expr<'a>>,
/// the pattern for the closure
@ -49,6 +52,9 @@ pub enum EUnwrapped<'a> {
/// the expression to replace the unwrapped
sub_new: &'a Loc<Expr<'a>>,
/// The type of the target for the suffix, e.g. a Task or Result
target: TryTarget,
},
/// Malformed use of the suffix
@ -59,12 +65,16 @@ fn init_unwrapped_err<'a>(
arena: &'a Bump,
unwrapped_expr: &'a Loc<Expr<'a>>,
maybe_def_pat: Option<&'a Loc<Pattern<'a>>>,
target: TryTarget,
) -> Result<&'a Loc<Expr<'a>>, EUnwrapped<'a>> {
match maybe_def_pat {
Some(..) => {
// we have a def pattern, so no need to generate a new pattern
// as this should only be created in the first call from a def
Err(EUnwrapped::UnwrappedDefExpr(unwrapped_expr))
Err(EUnwrapped::UnwrappedDefExpr {
loc_expr: unwrapped_expr,
target,
})
}
None => {
// Provide an intermediate answer expression and pattern when unwrapping a
@ -87,13 +97,14 @@ fn init_unwrapped_err<'a>(
sub_arg: unwrapped_expr,
sub_pat,
sub_new,
target,
})
}
}
}
/// Descend through the AST and unwrap each suffixed expression
/// when an expression is unwrapped, we apply a `Task.await` and
/// when an expression is unwrapped, we apply the appropriate try function and
/// then descend through the AST again until there are no more suffixed
/// expressions, or we hit an error
pub fn unwrap_suffixed_expression<'a>(
@ -103,10 +114,13 @@ pub fn unwrap_suffixed_expression<'a>(
) -> Result<&'a Loc<Expr<'a>>, EUnwrapped<'a>> {
let unwrapped_expression = {
match loc_expr.value {
Expr::TaskAwaitBang(sub_expr) => {
Expr::TrySuffix {
expr: sub_expr,
target,
} => {
let unwrapped_sub_expr = arena.alloc(Loc::at(loc_expr.region, *sub_expr));
init_unwrapped_err(arena, unwrapped_sub_expr, maybe_def_pat)
init_unwrapped_err(arena, unwrapped_sub_expr, maybe_def_pat, target)
}
Expr::Defs(..) => unwrap_suffixed_expression_defs_help(arena, loc_expr, maybe_def_pat),
@ -117,7 +131,7 @@ pub fn unwrap_suffixed_expression<'a>(
Expr::When(..) => unwrap_suffixed_expression_when_help(arena, loc_expr, maybe_def_pat),
Expr::If(..) => {
Expr::If { .. } => {
unwrap_suffixed_expression_if_then_else_help(arena, loc_expr, maybe_def_pat)
}
@ -154,15 +168,22 @@ pub fn unwrap_suffixed_expression<'a>(
.alloc(Loc::at(loc_expr.region, Expect(condition, unwrapped_expr)));
return Ok(new_expect);
}
Err(EUnwrapped::UnwrappedDefExpr(unwrapped_expr)) => {
Err(EUnwrapped::UnwrappedDefExpr {
loc_expr: unwrapped_expr,
target,
}) => {
let new_expect = arena
.alloc(Loc::at(loc_expr.region, Expect(condition, unwrapped_expr)));
Err(EUnwrapped::UnwrappedDefExpr(new_expect))
Err(EUnwrapped::UnwrappedDefExpr {
loc_expr: new_expect,
target,
})
}
Err(EUnwrapped::UnwrappedSubExpr {
sub_arg: unwrapped_expr,
sub_pat,
sub_new,
target,
}) => {
let new_expect = arena
.alloc(Loc::at(loc_expr.region, Expect(condition, unwrapped_expr)));
@ -170,6 +191,7 @@ pub fn unwrap_suffixed_expression<'a>(
sub_arg: new_expect,
sub_pat,
sub_new,
target,
})
}
Err(EUnwrapped::Malformed) => Err(EUnwrapped::Malformed),
@ -208,13 +230,14 @@ pub fn unwrap_suffixed_expression_parens_help<'a>(
));
Ok(new_parens)
}
Err(EUnwrapped::UnwrappedDefExpr(..)) => {
Err(EUnwrapped::UnwrappedDefExpr { .. }) => {
internal_error!("unreachable, child expressions from ParensAround should generate UnwrappedSubExpr instead");
}
Err(EUnwrapped::UnwrappedSubExpr {
sub_arg,
sub_pat,
sub_new,
target,
}) => {
let new_parens = arena.alloc(Loc::at(
loc_expr.region,
@ -224,6 +247,7 @@ pub fn unwrap_suffixed_expression_parens_help<'a>(
sub_arg,
sub_pat,
sub_new: new_parens,
target,
})
}
Err(err) => Err(err),
@ -247,13 +271,13 @@ pub fn unwrap_suffixed_expression_closure_help<'a>(
let new_closure = arena.alloc(Loc::at(loc_expr.region, Expr::Closure(closure_args, unwrapped_expr)));
Ok(new_closure)
}
Err(EUnwrapped::UnwrappedSubExpr { sub_arg, sub_pat, sub_new }) => {
let new_closure_loc_ret = apply_task_await(arena, loc_expr.region, sub_arg, sub_pat, sub_new, None);
Err(EUnwrapped::UnwrappedSubExpr { sub_arg, sub_pat, sub_new, target }) => {
let new_closure_loc_ret = apply_try_function(arena, loc_expr.region, sub_arg, sub_pat, sub_new, None, target);
let new_closure = arena.alloc(Loc::at(loc_expr.region, Expr::Closure(closure_args, new_closure_loc_ret)));
Ok(new_closure)
}
Err(err) => {
debug_assert!(false,"the closure Defs was malformd, got {:#?}", err);
debug_assert!(false,"the closure Defs was malformed, got {:#?}", err);
Err(EUnwrapped::Malformed)
}
}
@ -278,22 +302,22 @@ pub fn unwrap_suffixed_expression_apply_help<'a>(
Ok(new_arg) => {
*arg = new_arg;
}
Err(EUnwrapped::UnwrappedDefExpr(..)) => {
Err(EUnwrapped::UnwrappedDefExpr { .. }) => {
internal_error!("unreachable, unwrapped arg cannot be def expression as `None` was passed as pattern");
}
Err(EUnwrapped::UnwrappedSubExpr { sub_arg, sub_pat, sub_new: new_arg }) => {
Err(EUnwrapped::UnwrappedSubExpr { sub_arg, sub_pat, sub_new: new_arg, target }) => {
*arg = new_arg;
let new_apply = arena.alloc(Loc::at(loc_expr.region, Apply(function, local_args, called_via)));
return Err(EUnwrapped::UnwrappedSubExpr { sub_arg, sub_pat, sub_new: new_apply});
return Err(EUnwrapped::UnwrappedSubExpr { sub_arg, sub_pat, sub_new: new_apply, target });
}
Err(err) => return Err(err),
}
}
// special case for when our Apply function is a suffixed Var (but not multiple suffixed)
if let Expr::TaskAwaitBang(sub_expr) = function.value {
if let Expr::TrySuffix { expr: sub_expr, target } = function.value {
let unwrapped_function = arena.alloc(Loc::at(
loc_expr.region,
*sub_expr,
@ -301,7 +325,7 @@ pub fn unwrap_suffixed_expression_apply_help<'a>(
let new_apply = arena.alloc(Loc::at(loc_expr.region, Expr::Apply(unwrapped_function, local_args, called_via)));
return init_unwrapped_err(arena, new_apply, maybe_def_pat);
return init_unwrapped_err(arena, new_apply, maybe_def_pat, target);
}
// function is another expression
@ -310,15 +334,14 @@ pub fn unwrap_suffixed_expression_apply_help<'a>(
let new_apply = arena.alloc(Loc::at(loc_expr.region, Expr::Apply(new_function, local_args, called_via)));
Ok(new_apply)
}
Err(EUnwrapped::UnwrappedDefExpr(unwrapped_function)) => {
Err(EUnwrapped::UnwrappedDefExpr { loc_expr: unwrapped_function, target }) => {
let new_apply = arena.alloc(Loc::at(loc_expr.region, Expr::Apply(unwrapped_function, local_args, called_via)));
Err(EUnwrapped::UnwrappedDefExpr(new_apply))
Err(EUnwrapped::UnwrappedDefExpr { loc_expr: new_apply, target })
}
Err(EUnwrapped::UnwrappedSubExpr { sub_arg: unwrapped_function, sub_pat, sub_new }) => {
Err(EUnwrapped::UnwrappedSubExpr { sub_arg: unwrapped_function, sub_pat, sub_new, target }) => {
let new_apply = arena.alloc(Loc::at(loc_expr.region, Expr::Apply(sub_new, local_args, called_via)));
Err(EUnwrapped::UnwrappedSubExpr { sub_arg: unwrapped_function, sub_pat, sub_new:new_apply})
Err(EUnwrapped::UnwrappedSubExpr { sub_arg: unwrapped_function, sub_pat, sub_new:new_apply, target })
}
Err(err) => Err(err)
}
@ -334,7 +357,11 @@ pub fn unwrap_suffixed_expression_if_then_else_help<'a>(
maybe_def_pat: Option<&'a Loc<Pattern<'a>>>,
) -> Result<&'a Loc<Expr<'a>>, EUnwrapped<'a>> {
match loc_expr.value {
Expr::If(if_thens, final_else_branch) => {
Expr::If {
if_thens,
final_else: final_else_branch,
indented_else,
} => {
for (index, if_then) in if_thens.iter().enumerate() {
let (current_if_then_statement, current_if_then_expression) = if_then;
@ -353,29 +380,32 @@ pub fn unwrap_suffixed_expression_if_then_else_help<'a>(
let new_if = arena.alloc(Loc::at(
loc_expr.region,
Expr::If(
arena.alloc_slice_copy(new_if_thens.as_slice()),
final_else_branch,
),
Expr::If {
if_thens: arena.alloc_slice_copy(new_if_thens.as_slice()),
final_else: final_else_branch,
indented_else,
},
));
return unwrap_suffixed_expression(arena, new_if, maybe_def_pat);
}
Err(EUnwrapped::UnwrappedDefExpr(..)) => {
Err(EUnwrapped::UnwrappedDefExpr { .. }) => {
internal_error!("unexpected, unwrapped if-then-else Def expr should have intermediate answer as `None` was passed as pattern");
}
Err(EUnwrapped::UnwrappedSubExpr {
sub_arg,
sub_pat,
sub_new,
target,
}) => {
let unwrapped_expression = apply_task_await(
let unwrapped_expression = apply_try_function(
arena,
sub_arg.region,
sub_arg,
sub_pat,
sub_new,
None,
target,
);
let mut new_if_thens = Vec::new_in(arena);
@ -386,10 +416,11 @@ pub fn unwrap_suffixed_expression_if_then_else_help<'a>(
let new_if = arena.alloc(Loc::at(
loc_expr.region,
Expr::If(
arena.alloc_slice_copy(new_if_thens.as_slice()),
final_else_branch,
),
Expr::If {
if_thens: arena.alloc_slice_copy(new_if_thens.as_slice()),
final_else: final_else_branch,
indented_else,
},
));
return unwrap_suffixed_expression(arena, new_if, maybe_def_pat);
@ -414,21 +445,23 @@ pub fn unwrap_suffixed_expression_if_then_else_help<'a>(
let new_if = arena.alloc(Loc::at(
loc_expr.region,
Expr::If(
arena.alloc_slice_copy(new_if_thens.as_slice()),
final_else_branch,
),
Expr::If {
if_thens: arena.alloc_slice_copy(new_if_thens.as_slice()),
final_else: final_else_branch,
indented_else,
},
));
return unwrap_suffixed_expression(arena, new_if, maybe_def_pat);
}
Err(EUnwrapped::UnwrappedDefExpr(..)) => {
Err(EUnwrapped::UnwrappedDefExpr { .. }) => {
internal_error!("unexpected, unwrapped if-then-else Def expr should have intermediate answer as `None` was passed as pattern");
}
Err(EUnwrapped::UnwrappedSubExpr {
sub_arg,
sub_pat,
sub_new,
target,
}) => {
if before.is_empty() {
let mut new_if_thens = Vec::new_in(arena);
@ -439,19 +472,21 @@ pub fn unwrap_suffixed_expression_if_then_else_help<'a>(
let new_if = arena.alloc(Loc::at(
loc_expr.region,
Expr::If(
arena.alloc_slice_copy(new_if_thens.as_slice()),
final_else_branch,
),
Expr::If {
if_thens: arena.alloc_slice_copy(new_if_thens.as_slice()),
final_else: final_else_branch,
indented_else,
},
));
let unwrapped_if_then = apply_task_await(
let unwrapped_if_then = apply_try_function(
arena,
sub_arg.region,
sub_arg,
sub_pat,
new_if,
None,
target,
);
return unwrap_suffixed_expression(
@ -467,24 +502,30 @@ pub fn unwrap_suffixed_expression_if_then_else_help<'a>(
let after_if = arena.alloc(Loc::at(
loc_expr.region,
Expr::If(
arena.alloc_slice_copy(after_if_thens.as_slice()),
final_else_branch,
),
Expr::If {
if_thens: arena.alloc_slice_copy(after_if_thens.as_slice()),
final_else: final_else_branch,
indented_else,
},
));
let after_if_then = apply_task_await(
let after_if_then = apply_try_function(
arena,
sub_arg.region,
sub_arg,
sub_pat,
after_if,
None,
target,
);
let before_if_then = arena.alloc(Loc::at(
loc_expr.region,
Expr::If(before, after_if_then),
Expr::If {
if_thens: before,
final_else: after_if_then,
indented_else: false,
},
));
return unwrap_suffixed_expression(
@ -504,23 +545,39 @@ pub fn unwrap_suffixed_expression_if_then_else_help<'a>(
Ok(unwrapped_final_else) => {
return Ok(arena.alloc(Loc::at(
loc_expr.region,
Expr::If(if_thens, unwrapped_final_else),
Expr::If {
if_thens,
final_else: unwrapped_final_else,
indented_else,
},
)));
}
Err(EUnwrapped::UnwrappedDefExpr(..)) => {
Err(EUnwrapped::UnwrappedDefExpr { .. }) => {
internal_error!("unexpected, unwrapped if-then-else Def expr should have intermediate answer as `None` was passed as pattern");
}
Err(EUnwrapped::UnwrappedSubExpr {
sub_arg,
sub_pat,
sub_new,
target,
}) => {
let unwrapped_final_else =
apply_task_await(arena, sub_arg.region, sub_arg, sub_pat, sub_new, None);
let unwrapped_final_else = apply_try_function(
arena,
sub_arg.region,
sub_arg,
sub_pat,
sub_new,
None,
target,
);
let new_if = arena.alloc(Loc::at(
loc_expr.region,
Expr::If(if_thens, unwrapped_final_else),
Expr::If {
if_thens,
final_else: unwrapped_final_else,
indented_else,
},
));
return unwrap_suffixed_expression(arena, new_if, maybe_def_pat);
@ -551,7 +608,7 @@ pub fn unwrap_suffixed_expression_when_help<'a>(
if is_expr_suffixed(&branch_loc_expr.value) {
let unwrapped_branch_value = match unwrap_suffixed_expression(arena, branch_loc_expr, None) {
Ok(unwrapped_branch_value) => unwrapped_branch_value,
Err(EUnwrapped::UnwrappedSubExpr { sub_arg, sub_pat, sub_new }) => apply_task_await(arena, branch_loc_expr.region, sub_arg, sub_pat, sub_new, None),
Err(EUnwrapped::UnwrappedSubExpr { sub_arg, sub_pat, sub_new, target }) => apply_try_function(arena, branch_loc_expr.region, sub_arg, sub_pat, sub_new, None, target),
Err(..) => return Err(EUnwrapped::Malformed),
};
@ -578,12 +635,12 @@ pub fn unwrap_suffixed_expression_when_help<'a>(
let new_when = arena.alloc(Loc::at(loc_expr.region, Expr::When(unwrapped_condition, branches)));
Ok(new_when)
}
Err(EUnwrapped::UnwrappedSubExpr { sub_arg, sub_pat, sub_new }) => {
Err(EUnwrapped::UnwrappedSubExpr { sub_arg, sub_pat, sub_new, target }) => {
let new_when = arena.alloc(Loc::at(loc_expr.region, Expr::When(sub_new, branches)));
let applied_task_await = apply_task_await(arena,loc_expr.region,sub_arg,sub_pat,new_when, None);
let applied_task_await = apply_try_function(arena,loc_expr.region,sub_arg,sub_pat,new_when, None, target);
Ok(applied_task_await)
}
Err(EUnwrapped::UnwrappedDefExpr(..))
Err(EUnwrapped::UnwrappedDefExpr { .. })
| Err(EUnwrapped::Malformed) => Err(EUnwrapped::Malformed)
}
@ -631,7 +688,7 @@ pub fn unwrap_suffixed_expression_defs_help<'a>(
current_value_def.replace_expr(unwrapped_def);
local_defs.replace_with_value_def(tag_index, current_value_def, def_expr.region);
}
Err(EUnwrapped::UnwrappedDefExpr(unwrapped_expr)) => {
Err(EUnwrapped::UnwrappedDefExpr { loc_expr: unwrapped_expr, target }) => {
let split_defs = local_defs.split_defs_around(tag_index);
let before_empty = split_defs.before.is_empty();
let after_empty = split_defs.after.is_empty();
@ -640,48 +697,60 @@ pub fn unwrap_suffixed_expression_defs_help<'a>(
// We pass None as a def pattern here because it's desugaring of the ret expression
let next_expr = match unwrap_suffixed_expression(arena,loc_ret, None) {
Ok(next_expr) => next_expr,
Err(EUnwrapped::UnwrappedSubExpr { sub_arg, sub_pat, sub_new }) => {
Err(EUnwrapped::UnwrappedSubExpr { sub_arg, sub_pat, sub_new, target }) => {
// We need to apply Task.ok here as the defs final expression was unwrapped
apply_task_await(arena,def_expr.region,sub_arg,sub_pat,sub_new, None)
apply_try_function(arena,def_expr.region,sub_arg,sub_pat,sub_new, None, target)
}
Err(EUnwrapped::UnwrappedDefExpr(..)) | Err(EUnwrapped::Malformed) => {
Err(EUnwrapped::UnwrappedDefExpr { .. }) | Err(EUnwrapped::Malformed) => {
// TODO handle case when we have maybe_def_pat so can return an unwrapped up
return Err(EUnwrapped::Malformed);
},
};
return unwrap_suffixed_expression(arena, apply_task_await(arena,def_expr.region,unwrapped_expr,def_pattern,next_expr, ann_type), maybe_def_pat);
return unwrap_suffixed_expression(
arena,
apply_try_function(
arena,
def_expr.region,
unwrapped_expr,
def_pattern,
next_expr,
ann_type,
target,
),
maybe_def_pat
);
} else if before_empty {
// NIL before, SOME after -> FIRST DEF
let new_defs = arena.alloc(Loc::at(def_expr.region, Defs(arena.alloc(split_defs.after), loc_ret)));
let next_expr = match unwrap_suffixed_expression(arena,new_defs,maybe_def_pat){
Ok(next_expr) => next_expr,
Err(EUnwrapped::UnwrappedSubExpr { sub_arg, sub_pat, sub_new }) => {
apply_task_await(arena, def_expr.region, sub_arg, sub_pat, sub_new, None)
Err(EUnwrapped::UnwrappedSubExpr { sub_arg, sub_pat, sub_new, target }) => {
apply_try_function(arena, def_expr.region, sub_arg, sub_pat, sub_new, None, target)
}
Err(EUnwrapped::UnwrappedDefExpr(..)) | Err(EUnwrapped::Malformed) => {
Err(EUnwrapped::UnwrappedDefExpr { .. }) | Err(EUnwrapped::Malformed) => {
// TODO handle case when we have maybe_def_pat so can return an unwrapped up
return Err(EUnwrapped::Malformed);
},
};
return unwrap_suffixed_expression(arena, apply_task_await(arena,def_expr.region,unwrapped_expr,def_pattern,next_expr,ann_type), maybe_def_pat);
return unwrap_suffixed_expression(arena, apply_try_function(arena,def_expr.region,unwrapped_expr,def_pattern,next_expr, ann_type, target), maybe_def_pat);
} else if after_empty {
// SOME before, NIL after -> LAST DEF
// We pass None as a def pattern here because it's desugaring of the ret expression
match unwrap_suffixed_expression(arena,loc_ret,None){
Ok(new_loc_ret) => {
let applied_task_await = apply_task_await(arena, loc_expr.region, unwrapped_expr, def_pattern, new_loc_ret, ann_type);
let applied_task_await = apply_try_function(arena, loc_expr.region, unwrapped_expr, def_pattern, new_loc_ret, ann_type, target);
let new_defs = arena.alloc(Loc::at(loc_expr.region,Defs(arena.alloc(split_defs.before), applied_task_await)));
return unwrap_suffixed_expression(arena, new_defs, maybe_def_pat);
},
Err(EUnwrapped::UnwrappedSubExpr { sub_arg, sub_pat, sub_new }) => {
let new_loc_ret = apply_task_await(arena,def_expr.region,sub_arg,sub_pat,sub_new, None);
let applied_task_await = apply_task_await(arena, loc_expr.region, unwrapped_expr, def_pattern, new_loc_ret, ann_type);
Err(EUnwrapped::UnwrappedSubExpr { sub_arg, sub_pat, sub_new, target }) => {
let new_loc_ret = apply_try_function(arena,def_expr.region,sub_arg,sub_pat,sub_new, None, target);
let applied_task_await = apply_try_function(arena, loc_expr.region, unwrapped_expr, def_pattern, new_loc_ret, ann_type, target);
let new_defs = arena.alloc(Loc::at(loc_expr.region,Defs(arena.alloc(split_defs.before), applied_task_await)));
return unwrap_suffixed_expression(arena, new_defs, maybe_def_pat);
}
Err(EUnwrapped::UnwrappedDefExpr(..)) => {
Err(EUnwrapped::UnwrappedDefExpr { .. }) => {
// TODO confirm this is correct with test case
return Err(EUnwrapped::Malformed);
}
@ -695,28 +764,28 @@ pub fn unwrap_suffixed_expression_defs_help<'a>(
match unwrap_suffixed_expression(arena,after_defs,maybe_def_pat){
Ok(new_loc_ret) => {
let applied_await = apply_task_await(arena, loc_expr.region, unwrapped_expr, def_pattern, new_loc_ret, ann_type);
let applied_await = apply_try_function(arena, loc_expr.region, unwrapped_expr, def_pattern, new_loc_ret, ann_type, target);
let new_defs = arena.alloc(Loc::at(loc_expr.region,Defs(arena.alloc(split_defs.before), applied_await)));
return unwrap_suffixed_expression(arena, new_defs, maybe_def_pat);
},
Err(EUnwrapped::UnwrappedSubExpr { sub_arg, sub_pat, sub_new }) => {
let new_loc_ret = apply_task_await(arena, def_expr.region, sub_arg, sub_pat, sub_new, None);
let applied_await = apply_task_await(arena, loc_expr.region, unwrapped_expr, def_pattern, new_loc_ret, ann_type);
Err(EUnwrapped::UnwrappedSubExpr { sub_arg, sub_pat, sub_new, target }) => {
let new_loc_ret = apply_try_function(arena, def_expr.region, sub_arg, sub_pat, sub_new, None, target);
let applied_await = apply_try_function(arena, loc_expr.region, unwrapped_expr, def_pattern, new_loc_ret, ann_type, target);
let new_defs = arena.alloc(Loc::at(loc_expr.region,Defs(arena.alloc(split_defs.before), applied_await)));
return unwrap_suffixed_expression(arena, new_defs, maybe_def_pat);
}
Err(EUnwrapped::UnwrappedDefExpr(..)) | Err(EUnwrapped::Malformed) => {
Err(EUnwrapped::UnwrappedDefExpr { .. }) | Err(EUnwrapped::Malformed) => {
// TODO handle case when we have maybe_def_pat so can return an unwrapped up
return Err(EUnwrapped::Malformed);
},
};
}
}
Err(EUnwrapped::UnwrappedSubExpr { sub_arg, sub_pat, sub_new }) => {
Err(EUnwrapped::UnwrappedSubExpr { sub_arg, sub_pat, sub_new, target }) => {
let new_body_def = ValueDef::Body(def_pattern, sub_new);
local_defs.replace_with_value_def(tag_index,new_body_def, sub_new.region);
let new_defs_expr = arena.alloc(Loc::at(def_expr.region,Defs(arena.alloc(local_defs), loc_ret)));
let replaced_def = apply_task_await(arena,def_expr.region,sub_arg,sub_pat,new_defs_expr, ann_type);
let replaced_def = apply_try_function(arena,def_expr.region,sub_arg,sub_pat,new_defs_expr, ann_type, target);
return unwrap_suffixed_expression(arena,replaced_def,maybe_def_pat);
}
Err(err) => return Err(err)
@ -730,12 +799,12 @@ pub fn unwrap_suffixed_expression_defs_help<'a>(
Ok(new_loc_ret) => {
Ok(arena.alloc(Loc::at(loc_expr.region,Defs(arena.alloc(local_defs), new_loc_ret))))
},
Err(EUnwrapped::UnwrappedSubExpr { sub_arg, sub_pat, sub_new }) => {
let new_loc_ret = apply_task_await(arena, loc_expr.region,sub_arg,sub_pat,sub_new, None);
Err(EUnwrapped::UnwrappedSubExpr { sub_arg, sub_pat, sub_new, target }) => {
let new_loc_ret = apply_try_function(arena, loc_expr.region,sub_arg,sub_pat,sub_new, None, target);
let new_defs = arena.alloc(Loc::at(loc_expr.region,Defs(arena.alloc(local_defs), new_loc_ret)));
unwrap_suffixed_expression(arena, new_defs, None)
}
Err(EUnwrapped::UnwrappedDefExpr(..)) => {
Err(EUnwrapped::UnwrappedDefExpr { .. }) => {
// TODO confirm this is correct with test case
Err(EUnwrapped::Malformed)
}
@ -769,6 +838,7 @@ fn unwrap_low_level_dbg<'a>(
sub_arg,
sub_pat,
sub_new,
target,
}) => {
let new_dbg = arena.alloc(Loc::at(
loc_expr.region,
@ -777,18 +847,19 @@ fn unwrap_low_level_dbg<'a>(
unwrap_suffixed_expression(
arena,
apply_task_await(
apply_try_function(
arena,
new_dbg.region,
sub_arg,
sub_pat,
new_dbg,
None,
target,
),
maybe_def_pat,
)
}
Err(EUnwrapped::UnwrappedDefExpr(..)) => {
Err(EUnwrapped::UnwrappedDefExpr { .. }) => {
internal_error!(
"unreachable, arg of LowLevelDbg should generate UnwrappedSubExpr instead"
);
@ -805,17 +876,24 @@ fn unwrap_low_level_dbg<'a>(
));
Ok(&*new_dbg)
}
Err(EUnwrapped::UnwrappedDefExpr(unwrapped_expr)) => {
Err(EUnwrapped::UnwrappedDefExpr {
loc_expr: unwrapped_expr,
target,
}) => {
let new_dbg = arena.alloc(Loc::at(
loc_expr.region,
LowLevelDbg(dbg_src, arg, unwrapped_expr),
));
Err(EUnwrapped::UnwrappedDefExpr(new_dbg))
Err(EUnwrapped::UnwrappedDefExpr {
loc_expr: new_dbg,
target,
})
}
Err(EUnwrapped::UnwrappedSubExpr {
sub_arg: unwrapped_expr,
sub_pat,
sub_new,
target,
}) => {
let new_dbg = arena.alloc(Loc::at(
loc_expr.region,
@ -825,6 +903,7 @@ fn unwrap_low_level_dbg<'a>(
sub_arg: new_dbg,
sub_pat,
sub_new,
target,
})
}
Err(EUnwrapped::Malformed) => Err(EUnwrapped::Malformed),
@ -836,25 +915,26 @@ fn unwrap_low_level_dbg<'a>(
}
}
/// Helper for `Task.await loc_expr \loc_pat -> loc_cont`
pub fn apply_task_await<'a>(
/// Helper for try_function loc_expr \loc_pat -> loc_cont`
pub fn apply_try_function<'a>(
arena: &'a Bump,
region: Region,
loc_expr: &'a Loc<Expr<'a>>,
loc_pat: &'a Loc<Pattern<'a>>,
loc_cont: &'a Loc<Expr<'a>>,
maybe_loc_ann: Option<(&'a Loc<Pattern>, &'a Loc<TypeAnnotation<'a>>)>,
target: TryTarget,
) -> &'a Loc<Expr<'a>> {
let task_await_first_arg = match maybe_loc_ann {
let try_function_first_arg = match maybe_loc_ann {
Some((loc_ann_pat, loc_type)) => {
// loc_ann_pat : loc_type
// loc_pat = loc_expr!
// loc_cont
// desugar to
// Task.await
// try_function
// (
// #!0_expr : Task loc_type _
// #!0_expr : Target loc_type _
// #!0_expr = loc_expr
// #!0_expr
// )
@ -875,8 +955,12 @@ pub fn apply_task_await<'a>(
let new_ident = arena.alloc(new_ident);
// #!0_expr (pattern)
// #!0_expr : Task loc_type _
// #!0_expr : Target loc_type _
// #!0_expr = loc_expr
let target_type_name = match target {
TryTarget::Task => "Task",
TryTarget::Result => "Result",
};
let value_def = ValueDef::AnnotatedBody {
ann_pattern: arena.alloc(Loc::at(
loc_ann_pat.region,
@ -893,7 +977,7 @@ pub fn apply_task_await<'a>(
loc_type.region,
TypeAnnotation::Apply(
arena.alloc(""),
arena.alloc("Task"),
arena.alloc(target_type_name),
arena.alloc([
*loc_type,
Loc::at(loc_type.region, TypeAnnotation::Inferred),
@ -918,7 +1002,7 @@ pub fn apply_task_await<'a>(
));
// (
// #!0_expr : Task loc_type _
// #!0_expr : Target loc_type _
// #!0_expr = loc_expr
// #!0_expr
// )
@ -935,7 +1019,7 @@ pub fn apply_task_await<'a>(
// loc_cont
// desugar to
// Task.await loc_expr \loc_pat -> loc_cont
// try_function loc_expr \loc_pat -> loc_cont
loc_expr
}
};
@ -945,25 +1029,29 @@ pub fn apply_task_await<'a>(
// \x -> x!
// \x -> x
if is_matching_intermediate_answer(loc_pat, loc_cont) {
return task_await_first_arg;
return try_function_first_arg;
}
// \loc_pat -> loc_cont
let closure = arena.alloc(Loc::at(region, Closure(arena.alloc([*loc_pat]), loc_cont)));
// Task.await task_first_arg closure
// try_function first_arg closure
let (try_function_module, try_function_ident, called_via) = match target {
TryTarget::Task => (ModuleName::TASK, "await", CalledVia::BangSuffix),
TryTarget::Result => (ModuleName::RESULT, "try", CalledVia::QuestionSuffix),
};
arena.alloc(Loc::at(
region,
Apply(
arena.alloc(Loc {
region,
value: Var {
module_name: ModuleName::TASK,
ident: "await",
module_name: try_function_module,
ident: try_function_ident,
},
}),
arena.alloc([task_await_first_arg, closure]),
CalledVia::BangSuffix,
arena.alloc([try_function_first_arg, closure]),
called_via,
),
))
}

View file

@ -0,0 +1,207 @@
use crate::def::Def;
use crate::expr::{AnnotatedMark, ClosureData, Expr, Recursive};
use crate::pattern::Pattern;
use crate::scope::Scope;
use roc_collections::SendMap;
use roc_module::symbol::Symbol;
use roc_region::all::{Loc, Region};
use roc_types::subs::{VarStore, Variable};
use roc_types::types::{LambdaSet, OptAbleVar, Type};
pub fn build_host_exposed_def(
scope: &mut Scope,
symbol: Symbol,
ident: &str,
var_store: &mut VarStore,
annotation: crate::annotation::Annotation,
) -> Def {
let expr_var = var_store.fresh();
let pattern = Pattern::Identifier(symbol);
let mut pattern_vars = SendMap::default();
pattern_vars.insert(symbol, expr_var);
let mut arguments: Vec<(Variable, AnnotatedMark, Loc<Pattern>)> = Vec::new();
let mut linked_symbol_arguments: Vec<(Variable, Expr)> = Vec::new();
let mut captured_symbols: Vec<(Symbol, Variable)> = Vec::new();
let crate::annotation::Annotation {
introduced_variables,
typ,
aliases,
..
} = annotation;
let def_body = {
match typ.shallow_structural_dealias() {
Type::Function(args, _, _) => {
for i in 0..args.len() {
let name = format!("closure_arg_{ident}_{i}");
let arg_symbol = {
let ident = name.clone().into();
scope.introduce(ident, Region::zero()).unwrap()
};
let arg_var = var_store.fresh();
arguments.push((
arg_var,
AnnotatedMark::new(var_store),
Loc::at_zero(Pattern::Identifier(arg_symbol)),
));
captured_symbols.push((arg_symbol, arg_var));
linked_symbol_arguments.push((arg_var, Expr::Var(arg_symbol, arg_var)));
}
let foreign_symbol_name = format!("roc_fx_{ident}");
let low_level_call = Expr::ForeignCall {
foreign_symbol: foreign_symbol_name.into(),
args: linked_symbol_arguments,
ret_var: var_store.fresh(),
};
let task_closure_symbol = {
let name = format!("task_closure_{ident}");
let ident = name.into();
scope.introduce(ident, Region::zero()).unwrap()
};
let task_closure = Expr::Closure(ClosureData {
function_type: var_store.fresh(),
closure_type: var_store.fresh(),
return_type: var_store.fresh(),
name: task_closure_symbol,
captured_symbols,
recursive: Recursive::NotRecursive,
arguments: vec![(
var_store.fresh(),
AnnotatedMark::new(var_store),
Loc::at_zero(empty_record_pattern(var_store)),
)],
loc_body: Box::new(Loc::at_zero(low_level_call)),
});
let (specialized_def_type, type_arguments, lambda_set_variables) =
build_fresh_opaque_variables(var_store);
let body = Expr::OpaqueRef {
opaque_var: var_store.fresh(),
name: Symbol::TASK_TASK,
argument: Box::new((var_store.fresh(), Loc::at_zero(task_closure))),
specialized_def_type,
type_arguments,
lambda_set_variables,
};
Expr::Closure(ClosureData {
function_type: var_store.fresh(),
closure_type: var_store.fresh(),
return_type: var_store.fresh(),
name: symbol,
captured_symbols: std::vec::Vec::new(),
recursive: Recursive::NotRecursive,
arguments,
loc_body: Box::new(Loc::at_zero(body)),
})
}
_ => {
// not a function
let foreign_symbol_name = format!("roc_fx_{ident}");
let low_level_call = Expr::ForeignCall {
foreign_symbol: foreign_symbol_name.into(),
args: linked_symbol_arguments,
ret_var: var_store.fresh(),
};
let task_closure_symbol = {
let name = format!("task_closure_{ident}");
let ident = name.into();
scope.introduce(ident, Region::zero()).unwrap()
};
let task_closure = Expr::Closure(ClosureData {
function_type: var_store.fresh(),
closure_type: var_store.fresh(),
return_type: var_store.fresh(),
name: task_closure_symbol,
captured_symbols,
recursive: Recursive::NotRecursive,
arguments: vec![(
var_store.fresh(),
AnnotatedMark::new(var_store),
Loc::at_zero(empty_record_pattern(var_store)),
)],
loc_body: Box::new(Loc::at_zero(low_level_call)),
});
let (specialized_def_type, type_arguments, lambda_set_variables) =
build_fresh_opaque_variables(var_store);
Expr::OpaqueRef {
opaque_var: var_store.fresh(),
name: Symbol::TASK_TASK,
argument: Box::new((var_store.fresh(), Loc::at_zero(task_closure))),
specialized_def_type,
type_arguments,
lambda_set_variables,
}
}
}
};
let def_annotation = crate::def::Annotation {
signature: typ,
introduced_variables,
aliases,
region: Region::zero(),
};
Def {
loc_pattern: Loc::at_zero(pattern),
loc_expr: Loc::at_zero(def_body),
expr_var,
pattern_vars,
annotation: Some(def_annotation),
}
}
fn build_fresh_opaque_variables(
var_store: &mut VarStore,
) -> (Box<Type>, Vec<OptAbleVar>, Vec<LambdaSet>) {
let closure_var = var_store.fresh();
let ok_var = var_store.fresh();
let err_var = var_store.fresh();
let result_var = var_store.fresh();
let actual = Type::Function(
vec![Type::EmptyRec],
Box::new(Type::Variable(closure_var)),
Box::new(Type::Variable(result_var)),
);
let type_arguments = vec![
OptAbleVar {
var: ok_var,
opt_abilities: None,
},
OptAbleVar {
var: err_var,
opt_abilities: None,
},
];
let lambda_set_variables = vec![roc_types::types::LambdaSet(Type::Variable(closure_var))];
(Box::new(actual), type_arguments, lambda_set_variables)
}
#[inline(always)]
fn empty_record_pattern(var_store: &mut VarStore) -> Pattern {
Pattern::RecordDestructure {
whole_var: var_store.fresh(),
ext_var: var_store.fresh(),
destructs: vec![],
}
}

View file

@ -266,6 +266,7 @@ pub fn walk_expr<V: Visitor>(visitor: &mut V, expr: &Expr, var: Variable) {
walk_list(visitor, *elem_var, loc_elems);
}
Expr::Var(..) => { /* terminal */ }
Expr::ParamsVar { .. } => { /* terminal */ }
Expr::AbilityMember(..) => { /* terminal */ }
Expr::If {
cond_var,
@ -317,6 +318,8 @@ pub fn walk_expr<V: Visitor>(visitor: &mut V, expr: &Expr, var: Variable) {
.iter()
.for_each(|(var, elem)| visitor.visit_expr(&elem.value, elem.region, *var)),
Expr::EmptyRecord => { /* terminal */ }
Expr::ImportParams(_, region, Some((_, expr))) => visitor.visit_expr(expr, *region, var),
Expr::ImportParams(_, _, None) => { /* terminal */ }
Expr::RecordAccess {
field_var,
loc_expr,

View file

@ -46,6 +46,24 @@ pub fn can_expr_with(arena: &Bump, home: ModuleId, expr_str: &str) -> CanExprOut
let var = var_store.fresh();
let qualified_module_ids = PackageModuleIds::default();
let mut scope = Scope::new(
home,
"TestPath".into(),
IdentIds::default(),
Default::default(),
);
let dep_idents = IdentIds::exposed_builtins(0);
let mut env = Env::new(
arena,
expr_str,
home,
Path::new("Test.roc"),
&dep_idents,
&qualified_module_ids,
None,
);
// Desugar operators (convert them to Apply calls, taking into account
// operator precedence and associativity rules), before doing other canonicalization.
//
@ -53,20 +71,8 @@ pub fn can_expr_with(arena: &Bump, home: ModuleId, expr_str: &str) -> CanExprOut
// visited a BinOp node we'd recursively try to apply this to each of its nested
// operators, and then again on *their* nested operators, ultimately applying the
// rules multiple times unnecessarily.
let loc_expr = desugar::desugar_expr(
arena,
&loc_expr,
expr_str,
&mut None,
arena.alloc("TestPath"),
);
let loc_expr = desugar::desugar_expr(&mut env, &mut scope, &loc_expr);
let mut scope = Scope::new(
home,
"TestPath".into(),
IdentIds::default(),
Default::default(),
);
scope.add_alias(
Symbol::NUM_INT,
Region::zero(),
@ -79,15 +85,6 @@ pub fn can_expr_with(arena: &Bump, home: ModuleId, expr_str: &str) -> CanExprOut
roc_types::types::AliasKind::Structural,
);
let dep_idents = IdentIds::exposed_builtins(0);
let mut env = Env::new(
arena,
home,
Path::new("Test.roc"),
&dep_idents,
&qualified_module_ids,
None,
);
let (loc_expr, output) = canonicalize_expr(
&mut env,
&mut var_store,

View file

@ -0,0 +1,145 @@
---
source: crates/compiler/can/tests/test_suffixed.rs
assertion_line: 463
expression: snapshot
---
Defs {
tags: [
Index(2147483648),
],
regions: [
@0-26,
],
space_before: [
Slice(start = 0, length = 0),
],
space_after: [
Slice(start = 0, length = 1),
],
spaces: [
Newline,
],
type_defs: [],
value_defs: [
Body(
@0-4 Identifier {
ident: "main",
},
@11-26 Defs(
Defs {
tags: [
Index(2147483648),
],
regions: [
@15-26,
],
space_before: [
Slice(start = 0, length = 0),
],
space_after: [
Slice(start = 0, length = 0),
],
spaces: [],
type_defs: [],
value_defs: [
Body(
@15-26 Identifier {
ident: "1",
},
@15-26 ParensAround(
Defs(
Defs {
tags: [
Index(2147483648),
],
regions: [
@20-25,
],
space_before: [
Slice(start = 0, length = 0),
],
space_after: [
Slice(start = 0, length = 0),
],
spaces: [],
type_defs: [],
value_defs: [
Body(
@20-25 Identifier {
ident: "0",
},
@20-25 Apply(
@22-23 Var {
module_name: "Num",
ident: "add",
},
[
@20-21 Num(
"1",
),
@24-25 Num(
"1",
),
],
BinOp(
Plus,
),
),
),
],
},
@15-26 LowLevelDbg(
(
"test.roc:3",
" ",
),
@20-25 Apply(
@20-25 Var {
module_name: "Inspect",
ident: "toStr",
},
[
@20-25 Var {
module_name: "",
ident: "0",
},
],
Space,
),
@20-25 Var {
module_name: "",
ident: "0",
},
),
),
),
),
],
},
@11-26 LowLevelDbg(
(
"test.roc:2",
"in =\n ",
),
@15-26 Apply(
@15-26 Var {
module_name: "Inspect",
ident: "toStr",
},
[
@15-26 Var {
module_name: "",
ident: "1",
},
],
Space,
),
@15-26 Var {
module_name: "",
ident: "1",
},
),
),
),
],
}

View file

@ -63,8 +63,8 @@ Defs {
),
],
},
@66-130 If(
[
@66-130 If {
if_thens: [
(
@69-70 Var {
module_name: "",
@ -76,11 +76,12 @@ Defs {
},
),
],
@128-130 Var {
final_else: @128-130 Var {
module_name: "",
ident: "c",
},
),
indented_else: false,
},
),
guard: None,
},

View file

@ -136,8 +136,8 @@ Defs {
ident: "#!1_arg",
},
],
@109-298 If(
[
@109-298 If {
if_thens: [
(
@112-122 Apply(
@112-113 Var {
@ -244,7 +244,7 @@ Defs {
),
),
],
Apply(
final_else: Apply(
Var {
module_name: "Task",
ident: "await",
@ -269,8 +269,8 @@ Defs {
ident: "#!3_arg",
},
],
@109-298 If(
[
@109-298 If {
if_thens: [
(
@187-209 ParensAround(
Var {
@ -366,7 +366,7 @@ Defs {
),
),
],
@283-298 Apply(
final_else: @283-298 Apply(
@283-298 Var {
module_name: "",
ident: "line",
@ -380,12 +380,14 @@ Defs {
],
Space,
),
),
indented_else: false,
},
),
],
BangSuffix,
),
),
indented_else: false,
},
),
],
BangSuffix,

View file

@ -101,8 +101,8 @@ Defs {
ident: "#!0_arg",
},
],
@76-189 If(
[
@76-189 If {
if_thens: [
(
@79-87 Var {
module_name: "",
@ -124,7 +124,7 @@ Defs {
),
),
],
@125-132 Apply(
final_else: @125-132 Apply(
@125-132 Var {
module_name: "Task",
ident: "await",
@ -140,8 +140,8 @@ Defs {
ident: "#!1_arg",
},
],
@76-189 If(
[
@76-189 If {
if_thens: [
(
@125-132 Var {
module_name: "",
@ -163,7 +163,7 @@ Defs {
),
),
],
@178-189 Apply(
final_else: @178-189 Apply(
@178-182 Var {
module_name: "",
ident: "line",
@ -177,12 +177,14 @@ Defs {
],
Space,
),
),
indented_else: false,
},
),
],
BangSuffix,
),
),
indented_else: false,
},
),
],
BangSuffix,

View file

@ -0,0 +1,143 @@
---
source: crates/compiler/can/tests/test_suffixed.rs
assertion_line: 473
expression: snapshot
---
Defs {
tags: [
Index(2147483648),
],
regions: [
@0-51,
],
space_before: [
Slice(start = 0, length = 0),
],
space_after: [
Slice(start = 0, length = 1),
],
spaces: [
Newline,
],
type_defs: [],
value_defs: [
Body(
@0-4 Identifier {
ident: "main",
},
@11-51 Defs(
Defs {
tags: [
Index(2147483648),
],
regions: [
@11-40,
],
space_before: [
Slice(start = 0, length = 0),
],
space_after: [
Slice(start = 0, length = 0),
],
spaces: [],
type_defs: [],
value_defs: [
Body(
@11-40 Identifier {
ident: "1",
},
@11-40 Apply(
@31-38 Var {
module_name: "Num",
ident: "add",
},
[
@11-23 Defs(
Defs {
tags: [
Index(2147483648),
],
regions: [
@11-12,
],
space_before: [
Slice(start = 0, length = 0),
],
space_after: [
Slice(start = 0, length = 0),
],
spaces: [],
type_defs: [],
value_defs: [
Body(
@11-12 Identifier {
ident: "0",
},
@11-12 Num(
"1",
),
),
],
},
@11-23 LowLevelDbg(
(
"test.roc:2",
" ",
),
@11-12 Apply(
@11-12 Var {
module_name: "Inspect",
ident: "toStr",
},
[
@11-12 Var {
module_name: "",
ident: "0",
},
],
Space,
),
@11-12 Var {
module_name: "",
ident: "0",
},
),
),
@39-40 Num(
"2",
),
],
BinOp(
Pizza,
),
),
),
],
},
@11-51 LowLevelDbg(
(
"test.roc:2",
" main =\n 1\n ",
),
@11-40 Apply(
@11-40 Var {
module_name: "Inspect",
ident: "toStr",
},
[
@11-40 Var {
module_name: "",
ident: "1",
},
],
Space,
),
@11-40 Var {
module_name: "",
ident: "1",
},
),
),
),
],
}

View file

@ -6,13 +6,38 @@ mod suffixed_tests {
use bumpalo::Bump;
use insta::assert_snapshot;
use roc_can::desugar::desugar_defs_node_values;
use roc_can::env::Env;
use roc_can::scope::Scope;
use roc_module::symbol::{IdentIds, ModuleIds, PackageModuleIds};
use roc_parse::test_helpers::parse_defs_with;
use std::path::Path;
macro_rules! run_test {
($src:expr) => {{
let arena = &Bump::new();
let home = ModuleIds::default().get_or_insert(&"Test".into());
let mut scope = Scope::new(
home,
"TestPath".into(),
IdentIds::default(),
Default::default(),
);
let dep_idents = IdentIds::exposed_builtins(0);
let qualified_module_ids = PackageModuleIds::default();
let mut env = Env::new(
arena,
$src,
home,
Path::new("test.roc"),
&dep_idents,
&qualified_module_ids,
None,
);
let mut defs = parse_defs_with(arena, indoc!($src)).unwrap();
desugar_defs_node_values(arena, &mut defs, $src, &mut None, "test.roc", true);
desugar_defs_node_values(&mut env, &mut scope, &mut defs, true);
let snapshot = format!("{:#?}", &defs);
println!("{}", snapshot);
@ -433,6 +458,29 @@ mod suffixed_tests {
);
}
#[test]
fn dbg_expr() {
run_test!(
r#"
main =
dbg (dbg 1 + 1)
"#
);
}
#[test]
fn pizza_dbg() {
run_test!(
r#"
main =
1
|> dbg
|> Num.add 2
|> dbg
"#
)
}
#[test]
fn apply_argument_single() {
run_test!(

View file

@ -3424,21 +3424,15 @@
"dev": true
},
"node_modules/@jridgewell/trace-mapping": {
"version": "0.3.18",
"resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.18.tgz",
"integrity": "sha512-w+niJYzMHdd7USdiH2U6869nqhD2nbfZXND5Yp93qIbEmnDNk7PD48o+YchRVpzMU7M6jVCbenTR7PA1FLQ9pA==",
"version": "0.3.25",
"resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz",
"integrity": "sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==",
"dev": true,
"dependencies": {
"@jridgewell/resolve-uri": "3.1.0",
"@jridgewell/sourcemap-codec": "1.4.14"
"@jridgewell/resolve-uri": "^3.1.0",
"@jridgewell/sourcemap-codec": "^1.4.14"
}
},
"node_modules/@jridgewell/trace-mapping/node_modules/@jridgewell/sourcemap-codec": {
"version": "1.4.14",
"resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.14.tgz",
"integrity": "sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw==",
"dev": true
},
"node_modules/@jsdevtools/ono": {
"version": "7.1.3",
"resolved": "https://registry.npmjs.org/@jsdevtools/ono/-/ono-7.1.3.tgz",
@ -4351,20 +4345,10 @@
"@types/json-schema": "*"
}
},
"node_modules/@types/eslint-scope": {
"version": "3.7.4",
"resolved": "https://registry.npmjs.org/@types/eslint-scope/-/eslint-scope-3.7.4.tgz",
"integrity": "sha512-9K4zoImiZc3HlIp6AVUDE4CWYx22a+lhSZMYNpbjW04+YF0KWj4pJXnEMjdnFTiQibFFmElcsasJXDbdI/EPhA==",
"dev": true,
"dependencies": {
"@types/eslint": "*",
"@types/estree": "*"
}
},
"node_modules/@types/estree": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.1.tgz",
"integrity": "sha512-LG4opVs2ANWZ1TJoKc937iMmNstM/d0ae1vNbnBvBhqCSezgVUOzcLCqbI5elV8Vy6WKwKjaqR+zO9VKirBBCA==",
"version": "1.0.5",
"resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.5.tgz",
"integrity": "sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw==",
"dev": true
},
"node_modules/@types/express": {
@ -4932,9 +4916,9 @@
}
},
"node_modules/@webassemblyjs/ast": {
"version": "1.11.6",
"resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.11.6.tgz",
"integrity": "sha512-IN1xI7PwOvLPgjcf180gC1bqn3q/QaOCwYUahIOhbYUu8KA/3tw2RT/T0Gidi1l7Hhj5D/INhJxiICObqpMu4Q==",
"version": "1.12.1",
"resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.12.1.tgz",
"integrity": "sha512-EKfMUOPRRUTy5UII4qJDGPpqfwjOmZ5jeGFwid9mnoqIFK+e0vqoi1qH56JpmZSzEL53jKnNzScdmftJyG5xWg==",
"dev": true,
"dependencies": {
"@webassemblyjs/helper-numbers": "1.11.6",
@ -4954,9 +4938,9 @@
"dev": true
},
"node_modules/@webassemblyjs/helper-buffer": {
"version": "1.11.6",
"resolved": "https://registry.npmjs.org/@webassemblyjs/helper-buffer/-/helper-buffer-1.11.6.tgz",
"integrity": "sha512-z3nFzdcp1mb8nEOFFk8DrYLpHvhKC3grJD2ardfKOzmbmJvEf/tPIqCY+sNcwZIY8ZD7IkB2l7/pqhUhqm7hLA==",
"version": "1.12.1",
"resolved": "https://registry.npmjs.org/@webassemblyjs/helper-buffer/-/helper-buffer-1.12.1.tgz",
"integrity": "sha512-nzJwQw99DNDKr9BVCOZcLuJJUlqkJh+kVzVl6Fmq/tI5ZtEyWT1KZMyOXltXLZJmDtvLCDgwsyrkohEtopTXCw==",
"dev": true
},
"node_modules/@webassemblyjs/helper-numbers": {
@ -4977,15 +4961,15 @@
"dev": true
},
"node_modules/@webassemblyjs/helper-wasm-section": {
"version": "1.11.6",
"resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.11.6.tgz",
"integrity": "sha512-LPpZbSOwTpEC2cgn4hTydySy1Ke+XEu+ETXuoyvuyezHO3Kjdu90KK95Sh9xTbmjrCsUwvWwCOQQNta37VrS9g==",
"version": "1.12.1",
"resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.12.1.tgz",
"integrity": "sha512-Jif4vfB6FJlUlSbgEMHUyk1j234GTNG9dBJ4XJdOySoj518Xj0oGsNi59cUQF4RRMS9ouBUxDDdyBVfPTypa5g==",
"dev": true,
"dependencies": {
"@webassemblyjs/ast": "1.11.6",
"@webassemblyjs/helper-buffer": "1.11.6",
"@webassemblyjs/ast": "1.12.1",
"@webassemblyjs/helper-buffer": "1.12.1",
"@webassemblyjs/helper-wasm-bytecode": "1.11.6",
"@webassemblyjs/wasm-gen": "1.11.6"
"@webassemblyjs/wasm-gen": "1.12.1"
}
},
"node_modules/@webassemblyjs/ieee754": {
@ -5013,28 +4997,28 @@
"dev": true
},
"node_modules/@webassemblyjs/wasm-edit": {
"version": "1.11.6",
"resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-edit/-/wasm-edit-1.11.6.tgz",
"integrity": "sha512-Ybn2I6fnfIGuCR+Faaz7YcvtBKxvoLV3Lebn1tM4o/IAJzmi9AWYIPWpyBfU8cC+JxAO57bk4+zdsTjJR+VTOw==",
"version": "1.12.1",
"resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-edit/-/wasm-edit-1.12.1.tgz",
"integrity": "sha512-1DuwbVvADvS5mGnXbE+c9NfA8QRcZ6iKquqjjmR10k6o+zzsRVesil54DKexiowcFCPdr/Q0qaMgB01+SQ1u6g==",
"dev": true,
"dependencies": {
"@webassemblyjs/ast": "1.11.6",
"@webassemblyjs/helper-buffer": "1.11.6",
"@webassemblyjs/ast": "1.12.1",
"@webassemblyjs/helper-buffer": "1.12.1",
"@webassemblyjs/helper-wasm-bytecode": "1.11.6",
"@webassemblyjs/helper-wasm-section": "1.11.6",
"@webassemblyjs/wasm-gen": "1.11.6",
"@webassemblyjs/wasm-opt": "1.11.6",
"@webassemblyjs/wasm-parser": "1.11.6",
"@webassemblyjs/wast-printer": "1.11.6"
"@webassemblyjs/helper-wasm-section": "1.12.1",
"@webassemblyjs/wasm-gen": "1.12.1",
"@webassemblyjs/wasm-opt": "1.12.1",
"@webassemblyjs/wasm-parser": "1.12.1",
"@webassemblyjs/wast-printer": "1.12.1"
}
},
"node_modules/@webassemblyjs/wasm-gen": {
"version": "1.11.6",
"resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-gen/-/wasm-gen-1.11.6.tgz",
"integrity": "sha512-3XOqkZP/y6B4F0PBAXvI1/bky7GryoogUtfwExeP/v7Nzwo1QLcq5oQmpKlftZLbT+ERUOAZVQjuNVak6UXjPA==",
"version": "1.12.1",
"resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-gen/-/wasm-gen-1.12.1.tgz",
"integrity": "sha512-TDq4Ojh9fcohAw6OIMXqiIcTq5KUXTGRkVxbSo1hQnSy6lAM5GSdfwWeSxpAo0YzgsgF182E/U0mDNhuA0tW7w==",
"dev": true,
"dependencies": {
"@webassemblyjs/ast": "1.11.6",
"@webassemblyjs/ast": "1.12.1",
"@webassemblyjs/helper-wasm-bytecode": "1.11.6",
"@webassemblyjs/ieee754": "1.11.6",
"@webassemblyjs/leb128": "1.11.6",
@ -5042,24 +5026,24 @@
}
},
"node_modules/@webassemblyjs/wasm-opt": {
"version": "1.11.6",
"resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-opt/-/wasm-opt-1.11.6.tgz",
"integrity": "sha512-cOrKuLRE7PCe6AsOVl7WasYf3wbSo4CeOk6PkrjS7g57MFfVUF9u6ysQBBODX0LdgSvQqRiGz3CXvIDKcPNy4g==",
"version": "1.12.1",
"resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-opt/-/wasm-opt-1.12.1.tgz",
"integrity": "sha512-Jg99j/2gG2iaz3hijw857AVYekZe2SAskcqlWIZXjji5WStnOpVoat3gQfT/Q5tb2djnCjBtMocY/Su1GfxPBg==",
"dev": true,
"dependencies": {
"@webassemblyjs/ast": "1.11.6",
"@webassemblyjs/helper-buffer": "1.11.6",
"@webassemblyjs/wasm-gen": "1.11.6",
"@webassemblyjs/wasm-parser": "1.11.6"
"@webassemblyjs/ast": "1.12.1",
"@webassemblyjs/helper-buffer": "1.12.1",
"@webassemblyjs/wasm-gen": "1.12.1",
"@webassemblyjs/wasm-parser": "1.12.1"
}
},
"node_modules/@webassemblyjs/wasm-parser": {
"version": "1.11.6",
"resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-parser/-/wasm-parser-1.11.6.tgz",
"integrity": "sha512-6ZwPeGzMJM3Dqp3hCsLgESxBGtT/OeCvCZ4TA1JUPYgmhAx38tTPR9JaKy0S5H3evQpO/h2uWs2j6Yc/fjkpTQ==",
"version": "1.12.1",
"resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-parser/-/wasm-parser-1.12.1.tgz",
"integrity": "sha512-xikIi7c2FHXysxXe3COrVUPSheuBtpcfhbpFj4gmu7KRLYOzANztwUU0IbsqvMqzuNK2+glRGWCEqZo1WCLyAQ==",
"dev": true,
"dependencies": {
"@webassemblyjs/ast": "1.11.6",
"@webassemblyjs/ast": "1.12.1",
"@webassemblyjs/helper-api-error": "1.11.6",
"@webassemblyjs/helper-wasm-bytecode": "1.11.6",
"@webassemblyjs/ieee754": "1.11.6",
@ -5068,12 +5052,12 @@
}
},
"node_modules/@webassemblyjs/wast-printer": {
"version": "1.11.6",
"resolved": "https://registry.npmjs.org/@webassemblyjs/wast-printer/-/wast-printer-1.11.6.tgz",
"integrity": "sha512-JM7AhRcE+yW2GWYaKeHL5vt4xqee5N2WcezptmgyhNS+ScggqcT1OtXykhAb13Sn5Yas0j2uv9tHgrjwvzAP4A==",
"version": "1.12.1",
"resolved": "https://registry.npmjs.org/@webassemblyjs/wast-printer/-/wast-printer-1.12.1.tgz",
"integrity": "sha512-+X4WAlOisVWQMikjbcvY2e0rwPsKQ9F688lksZhBcPycBBuii3O7m8FACbDMWDojpAqvjIncrG8J0XHKyQfVeA==",
"dev": true,
"dependencies": {
"@webassemblyjs/ast": "1.11.6",
"@webassemblyjs/ast": "1.12.1",
"@xtuc/long": "4.2.2"
}
},
@ -5142,10 +5126,10 @@
"node": ">=0.4.0"
}
},
"node_modules/acorn-import-assertions": {
"version": "1.9.0",
"resolved": "https://registry.npmjs.org/acorn-import-assertions/-/acorn-import-assertions-1.9.0.tgz",
"integrity": "sha512-cmMwop9x+8KFhxvKrKfPYmN6/pKTYYHBqLa0DfvVZcKMJWNyWLnaqND7dx/qn66R7ewM1UX5XMaDVP5wlVTaVA==",
"node_modules/acorn-import-attributes": {
"version": "1.9.5",
"resolved": "https://registry.npmjs.org/acorn-import-attributes/-/acorn-import-attributes-1.9.5.tgz",
"integrity": "sha512-n02Vykv5uA3eHGM/Z2dQrcD56kL8TyDb2p1+0P83PClMnC/nc+anbQRhIOWnSq4Ke/KvDPrY3C9hDtC/A3eHnQ==",
"dev": true,
"peerDependencies": {
"acorn": "^8"
@ -6039,9 +6023,9 @@
"dev": true
},
"node_modules/browserslist": {
"version": "4.21.9",
"resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.21.9.tgz",
"integrity": "sha512-M0MFoZzbUrRU4KNfCrDLnvyE7gub+peetoTid3TBIqtunaDJyXlwhakT+/VkvSXcfIzFfK/nkCs4nmyTmxdNSg==",
"version": "4.23.3",
"resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.23.3.tgz",
"integrity": "sha512-btwCFJVjI4YWDNfau8RhZ+B1Q/VLoUITrm3RlP6y1tYGWIOa+InuYiRGXUBXo8nA1qKmHMyLB/iVQg5TT4eFoA==",
"dev": true,
"funding": [
{
@ -6058,10 +6042,10 @@
}
],
"dependencies": {
"caniuse-lite": "^1.0.30001503",
"electron-to-chromium": "^1.4.431",
"node-releases": "^2.0.12",
"update-browserslist-db": "^1.0.11"
"caniuse-lite": "^1.0.30001646",
"electron-to-chromium": "^1.5.4",
"node-releases": "^2.0.18",
"update-browserslist-db": "^1.1.0"
},
"bin": {
"browserslist": "cli.js"
@ -6178,9 +6162,9 @@
}
},
"node_modules/caniuse-lite": {
"version": "1.0.30001516",
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001516.tgz",
"integrity": "sha512-Wmec9pCBY8CWbmI4HsjBeQLqDTqV91nFVR83DnZpYyRnPI1wePDsTg0bGLPC5VU/3OIZV1fmxEea1b+tFKe86g==",
"version": "1.0.30001655",
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001655.tgz",
"integrity": "sha512-jRGVy3iSGO5Uutn2owlb5gR6qsGngTw9ZTb4ali9f3glshcNmJ2noam4Mo9zia5P9Dk3jNNydy7vQjuE5dQmfg==",
"dev": true,
"funding": [
{
@ -7575,9 +7559,9 @@
}
},
"node_modules/electron-to-chromium": {
"version": "1.4.461",
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.461.tgz",
"integrity": "sha512-1JkvV2sgEGTDXjdsaQCeSwYYuhLRphRpc+g6EHTFELJXEiznLt3/0pZ9JuAOQ5p2rI3YxKTbivtvajirIfhrEQ==",
"version": "1.5.13",
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.13.tgz",
"integrity": "sha512-lbBcvtIJ4J6sS4tb5TLp1b4LyfCdMkwStzXPyAgVgTRAsep4bvrAGaBOP7ZJtQMNJpSQ9SqG4brWOroNaQtm7Q==",
"dev": true
},
"node_modules/elkjs": {
@ -7622,9 +7606,9 @@
}
},
"node_modules/enhanced-resolve": {
"version": "5.15.0",
"resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.15.0.tgz",
"integrity": "sha512-LXYT42KJ7lpIKECr2mAXIaMldcNCh/7E0KBKOu4KSfkHmP+mZmSs+8V5gBAqisWBy0OO4W5Oyys0GO1Y8KtdKg==",
"version": "5.17.1",
"resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.17.1.tgz",
"integrity": "sha512-LMHl3dXhTcfv8gM4kEzIUeTQ+7fpdA0l2tUf34BddXPkz2A5xJ5L/Pchd5BL6rdccM9QGvu0sWZzK1Z1t4wwyg==",
"dev": true,
"dependencies": {
"graceful-fs": "^4.2.4",
@ -7815,9 +7799,9 @@
}
},
"node_modules/escalade": {
"version": "3.1.1",
"resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz",
"integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==",
"version": "3.2.0",
"resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz",
"integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==",
"dev": true,
"engines": {
"node": ">=6"
@ -13471,9 +13455,9 @@
"dev": true
},
"node_modules/node-releases": {
"version": "2.0.13",
"resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.13.tgz",
"integrity": "sha512-uYr7J37ae/ORWdZeQ1xxMJe3NtdmqMC/JZK+geofDrkLUApKRHPd18/TxtBOJ4A0/+uUIliorNrfYV6s1b02eQ==",
"version": "2.0.18",
"resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.18.tgz",
"integrity": "sha512-d9VeXT4SJ7ZeOqGX6R5EM022wpL+eWPooLI+5UpWn2jCT1aosUQEhQP214x33Wkwx3JQMvIm+tIoVOdodFS40g==",
"dev": true
},
"node_modules/normalize-path": {
@ -13926,9 +13910,9 @@
"dev": true
},
"node_modules/picocolors": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz",
"integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==",
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.1.tgz",
"integrity": "sha512-anP1Z8qwhkbmu7MFP5iTt+wQKXgwzf7zTyGlcdzabySa9vd0Xt392U0rVmz9poOaBj0uHJKyyo9/upk0HrEQew==",
"dev": true
},
"node_modules/picomatch": {
@ -17473,9 +17457,9 @@
}
},
"node_modules/terser": {
"version": "5.19.0",
"resolved": "https://registry.npmjs.org/terser/-/terser-5.19.0.tgz",
"integrity": "sha512-JpcpGOQLOXm2jsomozdMDpd5f8ZHh1rR48OFgWUH3QsyZcfPgv2qDCYbcDEAYNd4OZRj2bWYKpwdll/udZCk/Q==",
"version": "5.31.6",
"resolved": "https://registry.npmjs.org/terser/-/terser-5.31.6.tgz",
"integrity": "sha512-PQ4DAriWzKj+qgehQ7LK5bQqCFNMmlhjR2PFFLuqGCpuCAauxemVBWwWOxo3UIwWQx8+Pr61Df++r76wDmkQBg==",
"dev": true,
"dependencies": {
"@jridgewell/source-map": "^0.3.3",
@ -17491,16 +17475,16 @@
}
},
"node_modules/terser-webpack-plugin": {
"version": "5.3.9",
"resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-5.3.9.tgz",
"integrity": "sha512-ZuXsqE07EcggTWQjXUj+Aot/OMcD0bMKGgF63f7UxYcu5/AJF53aIpK1YoP5xR9l6s/Hy2b+t1AM0bLNPRuhwA==",
"version": "5.3.10",
"resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-5.3.10.tgz",
"integrity": "sha512-BKFPWlPDndPs+NGGCr1U59t0XScL5317Y0UReNrHaw9/FwhPENlq6bfgs+4yPfyP51vqC1bQ4rp1EfXW5ZSH9w==",
"dev": true,
"dependencies": {
"@jridgewell/trace-mapping": "^0.3.17",
"@jridgewell/trace-mapping": "^0.3.20",
"jest-worker": "^27.4.5",
"schema-utils": "^3.1.1",
"serialize-javascript": "^6.0.1",
"terser": "^5.16.8"
"terser": "^5.26.0"
},
"engines": {
"node": ">= 10.13.0"
@ -17984,9 +17968,9 @@
}
},
"node_modules/update-browserslist-db": {
"version": "1.0.11",
"resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.11.tgz",
"integrity": "sha512-dCwEFf0/oT85M1fHBg4F0jtLwJrutGoHSQXCh7u4o2t1drG+c0a9Flnqww6XUKSfQMPpJBRjU8d4RXB09qtvaA==",
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.0.tgz",
"integrity": "sha512-EdRAaAyk2cUE1wOf2DkEhzxqOQvFOoRJFNS6NeyJ01Gp2beMRpBAINjM2iDXE3KCuKhwnvHIQCJm6ThL2Z+HzQ==",
"dev": true,
"funding": [
{
@ -18003,8 +17987,8 @@
}
],
"dependencies": {
"escalade": "^3.1.1",
"picocolors": "^1.0.0"
"escalade": "^3.1.2",
"picocolors": "^1.0.1"
},
"bin": {
"update-browserslist-db": "cli.js"
@ -18140,9 +18124,9 @@
}
},
"node_modules/watchpack": {
"version": "2.4.0",
"resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.4.0.tgz",
"integrity": "sha512-Lcvm7MGST/4fup+ifyKi2hjyIAwcdI4HRgtvTpIUxBRhB+RFtUh8XtDOxUfctVCnhVi+QQj49i91OyvzkJl6cg==",
"version": "2.4.2",
"resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.4.2.tgz",
"integrity": "sha512-TnbFSbcOCcDgjZ4piURLCbJ3nJhznVh9kw6F6iokjiFPl8ONxe9A6nMDVXDiNbrSfLILs6vB07F7wLBrwPYzJw==",
"dev": true,
"dependencies": {
"glob-to-regexp": "^0.4.1",
@ -18171,34 +18155,33 @@
}
},
"node_modules/webpack": {
"version": "5.88.1",
"resolved": "https://registry.npmjs.org/webpack/-/webpack-5.88.1.tgz",
"integrity": "sha512-FROX3TxQnC/ox4N+3xQoWZzvGXSuscxR32rbzjpXgEzWudJFEJBpdlkkob2ylrv5yzzufD1zph1OoFsLtm6stQ==",
"version": "5.94.0",
"resolved": "https://registry.npmjs.org/webpack/-/webpack-5.94.0.tgz",
"integrity": "sha512-KcsGn50VT+06JH/iunZJedYGUJS5FGjow8wb9c0v5n1Om8O1g4L6LjtfxwlXIATopoQu+vOXXa7gYisWxCoPyg==",
"dev": true,
"dependencies": {
"@types/eslint-scope": "^3.7.3",
"@types/estree": "^1.0.0",
"@webassemblyjs/ast": "^1.11.5",
"@webassemblyjs/wasm-edit": "^1.11.5",
"@webassemblyjs/wasm-parser": "^1.11.5",
"@types/estree": "^1.0.5",
"@webassemblyjs/ast": "^1.12.1",
"@webassemblyjs/wasm-edit": "^1.12.1",
"@webassemblyjs/wasm-parser": "^1.12.1",
"acorn": "^8.7.1",
"acorn-import-assertions": "^1.9.0",
"browserslist": "^4.14.5",
"acorn-import-attributes": "^1.9.5",
"browserslist": "^4.21.10",
"chrome-trace-event": "^1.0.2",
"enhanced-resolve": "^5.15.0",
"enhanced-resolve": "^5.17.1",
"es-module-lexer": "^1.2.1",
"eslint-scope": "5.1.1",
"events": "^3.2.0",
"glob-to-regexp": "^0.4.1",
"graceful-fs": "^4.2.9",
"graceful-fs": "^4.2.11",
"json-parse-even-better-errors": "^2.3.1",
"loader-runner": "^4.2.0",
"mime-types": "^2.1.27",
"neo-async": "^2.6.2",
"schema-utils": "^3.2.0",
"tapable": "^2.1.1",
"terser-webpack-plugin": "^5.3.7",
"watchpack": "^2.4.0",
"terser-webpack-plugin": "^5.3.10",
"watchpack": "^2.4.1",
"webpack-sources": "^3.2.3"
},
"bin": {

View file

@ -17,6 +17,10 @@ impl<T> VecSet<T> {
pub fn into_vec(self) -> Vec<T> {
self.elements
}
pub fn reserve(&mut self, additional: usize) {
self.elements.reserve(additional)
}
}
impl<T: PartialEq> VecSet<T> {

View file

@ -566,7 +566,12 @@ pub fn constrain_expr(
constraints.exists([*ret_var], and)
}
Var(symbol, variable) => {
Var(symbol, variable)
| ParamsVar {
symbol,
var: variable,
..
} => {
// Save the expectation in the variable, then lookup the symbol's type in the environment
let expected_type = *constraints[expected].get_type_ref();
let store_expected = constraints.store(expected_type, *variable, file!(), line!());
@ -575,6 +580,22 @@ pub fn constrain_expr(
constraints.and_constraint([store_expected, lookup_constr])
}
ImportParams(module_id, region, Some((var, params))) => {
let index = constraints.push_variable(*var);
let expected_params = constraints.push_expected_type(Expected::ForReason(
Reason::ImportParams(*module_id),
index,
*region,
));
let expr_con =
constrain_expr(types, constraints, env, *region, params, expected_params);
let params_con = constraints.import_params(Some(index), *module_id, *region);
let expr_and_params = constraints.and_constraint([expr_con, params_con]);
constraints.exists([*var], expr_and_params)
}
ImportParams(module_id, region, None) => {
constraints.import_params(None, *module_id, *region)
}
&AbilityMember(symbol, specialization_id, specialization_var) => {
// Save the expectation in the `specialization_var` so we know what to specialize, then
// lookup the member in the environment.
@ -4089,6 +4110,8 @@ fn is_generalizable_expr(mut expr: &Expr) -> bool {
return true;
}
OpaqueRef { argument, .. } => expr = &argument.1.value,
ImportParams(_, _, Some((_, params))) => expr = params,
ImportParams(_, _, None) => return false,
Str(_)
| IngestedFile(..)
| List { .. }
@ -4115,7 +4138,8 @@ fn is_generalizable_expr(mut expr: &Expr) -> bool {
| ZeroArgumentTag { .. }
| Tag { .. }
| AbilityMember(..)
| Var(..) => return false,
| Var(..)
| ParamsVar { .. } => return false,
}
}
}

View file

@ -1,9 +1,12 @@
use crate::expr::{constrain_def_make_constraint, constrain_def_pattern, Env};
use crate::pattern::{constrain_pattern, PatternState};
use roc_can::abilities::{PendingAbilitiesStore, PendingMemberType};
use roc_can::constraint::{Constraint, Constraints, Generalizable};
use roc_can::expected::Expected;
use roc_can::expected::{Expected, PExpected};
use roc_can::expr::Declarations;
use roc_can::module::ModuleParams;
use roc_can::pattern::Pattern;
use roc_collections::MutMap;
use roc_module::symbol::{ModuleId, Symbol};
use roc_region::all::{Loc, Region};
use roc_types::types::{AnnotationSource, Category, Type, Types};
@ -14,9 +17,18 @@ pub fn constrain_module(
symbols_from_requires: Vec<(Loc<Symbol>, Loc<Type>)>,
abilities_store: &PendingAbilitiesStore,
declarations: &Declarations,
opt_module_params: &Option<ModuleParams>,
home: ModuleId,
) -> Constraint {
let constraint = crate::expr::constrain_decls(types, constraints, home, declarations);
let constraint = match opt_module_params {
Some(params_pattern) => {
constrain_params(types, constraints, home, constraint, params_pattern)
}
None => constraint,
};
let constraint = constrain_symbols_from_requires(
types,
constraints,
@ -33,6 +45,53 @@ pub fn constrain_module(
constraint
}
fn constrain_params(
types: &mut Types,
constraints: &mut Constraints,
home: ModuleId,
constraint: Constraint,
module_params: &ModuleParams,
) -> Constraint {
let mut env = Env {
home,
rigids: MutMap::default(),
resolutions_to_make: vec![],
};
let index = constraints.push_variable(module_params.whole_var);
let expected_params = constraints.push_pat_expected_type(PExpected::NoExpectation(index));
let mut state = PatternState::default();
let empty_rec = constraints.push_type(types, Types::EMPTY_RECORD);
let closed_con = constraints.store(empty_rec, module_params.record_ext_var, file!(), line!());
state.constraints.push(closed_con);
constrain_pattern(
types,
constraints,
&mut env,
&module_params.pattern().value,
module_params.region,
expected_params,
&mut state,
);
let pattern_constraints = constraints.and_constraint(state.constraints);
let cons = constraints.let_constraint(
[],
state.vars,
state.headers,
pattern_constraints,
constraint,
Generalizable(true),
);
constraints.exists([module_params.whole_var], cons)
}
fn constrain_symbols_from_requires(
types: &mut Types,
constraints: &mut Constraints,

View file

@ -38,6 +38,7 @@ pub enum Parens {
InFunctionType,
InApply,
InOperator,
InAsPattern,
}
/// In an AST node, do we show newlines around it
@ -428,9 +429,9 @@ fn is_multiline_assigned_field_help<T: Formattable>(afield: &AssignedField<'_, T
use self::AssignedField::*;
match afield {
RequiredValue(_, spaces, ann) | OptionalValue(_, spaces, ann) => {
!spaces.is_empty() || ann.value.is_multiline()
}
RequiredValue(_, spaces, ann)
| OptionalValue(_, spaces, ann)
| IgnoredValue(_, spaces, ann) => !spaces.is_empty() || ann.value.is_multiline(),
LabelOnly(_) => false,
AssignedField::SpaceBefore(_, _) | AssignedField::SpaceAfter(_, _) => true,
Malformed(text) => text.chars().any(|c| c == '\n'),
@ -483,6 +484,24 @@ fn format_assigned_field_help<T>(
buf.spaces(1);
ann.value.format(buf, indent);
}
IgnoredValue(name, spaces, ann) => {
if is_multiline {
buf.newline();
}
buf.indent(indent);
buf.push('_');
buf.push_str(name.value);
if !spaces.is_empty() {
fmt_spaces(buf, spaces.iter(), indent);
}
buf.spaces(separator_spaces);
buf.push(':');
buf.spaces(1);
ann.value.format(buf, indent);
}
LabelOnly(name) => {
if is_multiline {
buf.newline();

View file

@ -252,14 +252,14 @@ impl<'a> Formattable for ModuleImportParams<'a> {
fn is_multiline(&self) -> bool {
let ModuleImportParams { before, params } = self;
!before.is_empty() || is_collection_multiline(params)
!before.is_empty() || is_collection_multiline(&params.value)
}
fn format_with_options(&self, buf: &mut Buf, _parens: Parens, newlines: Newlines, indent: u16) {
let ModuleImportParams { before, params } = self;
fmt_default_spaces(buf, before, indent);
fmt_collection(buf, indent, Braces::Curly, *params, newlines);
fmt_collection(buf, indent, Braces::Curly, params.value, newlines);
}
}

View file

@ -10,7 +10,7 @@ use crate::Buf;
use roc_module::called_via::{self, BinOp};
use roc_parse::ast::{
is_expr_suffixed, AssignedField, Base, Collection, CommentOrNewline, Expr, ExtractSpaces,
OldRecordBuilderField, Pattern, WhenBranch,
OldRecordBuilderField, Pattern, TryTarget, WhenBranch,
};
use roc_parse::ast::{StrLiteral, StrSegment};
use roc_parse::ident::Accessor;
@ -39,15 +39,17 @@ impl<'a> Formattable for Expr<'a> {
| NonBase10Int { .. }
| SingleQuote(_)
| AccessorFunction(_)
| RecordUpdater(_)
| Var { .. }
| Underscore { .. }
| MalformedIdent(_, _)
| MalformedClosure
| Tag(_)
| OpaqueRef(_)
| Crash => false,
| Crash
| Dbg => false,
RecordAccess(inner, _) | TupleAccess(inner, _) | TaskAwaitBang(inner) => {
RecordAccess(inner, _) | TupleAccess(inner, _) | TrySuffix { expr: inner, .. } => {
inner.is_multiline()
}
@ -64,12 +66,16 @@ impl<'a> Formattable for Expr<'a> {
Expect(condition, continuation) => {
condition.is_multiline() || continuation.is_multiline()
}
Dbg(condition, _) => condition.is_multiline(),
DbgStmt(condition, _) => condition.is_multiline(),
LowLevelDbg(_, _, _) => unreachable!(
"LowLevelDbg should only exist after desugaring, not during formatting"
),
If(branches, final_else) => {
If {
if_thens: branches,
final_else,
..
} => {
final_else.is_multiline()
|| branches
.iter()
@ -452,14 +458,29 @@ impl<'a> Formattable for Expr<'a> {
Expect(condition, continuation) => {
fmt_expect(buf, condition, continuation, self.is_multiline(), indent);
}
Dbg(condition, continuation) => {
fmt_dbg(buf, condition, continuation, self.is_multiline(), indent);
Dbg => {
buf.indent(indent);
buf.push_str("dbg");
}
DbgStmt(condition, continuation) => {
fmt_dbg_stmt(buf, condition, continuation, self.is_multiline(), indent);
}
LowLevelDbg(_, _, _) => unreachable!(
"LowLevelDbg should only exist after desugaring, not during formatting"
),
If(branches, final_else) => {
fmt_if(buf, branches, final_else, self.is_multiline(), indent);
If {
if_thens: branches,
final_else,
indented_else,
} => {
fmt_if(
buf,
branches,
final_else,
self.is_multiline(),
*indented_else,
indent,
);
}
When(loc_condition, branches) => fmt_when(buf, loc_condition, branches, indent),
Tuple(items) => fmt_collection(buf, indent, Braces::Round, *items, Newlines::No),
@ -510,6 +531,11 @@ impl<'a> Formattable for Expr<'a> {
Accessor::TupleIndex(key) => buf.push_str(key),
}
}
RecordUpdater(key) => {
buf.indent(indent);
buf.push('&');
buf.push_str(key);
}
RecordAccess(expr, key) => {
expr.format_with_options(buf, Parens::InApply, Newlines::Yes, indent);
buf.push('.');
@ -520,9 +546,12 @@ impl<'a> Formattable for Expr<'a> {
buf.push('.');
buf.push_str(key);
}
TaskAwaitBang(expr) => {
TrySuffix { expr, target } => {
expr.format_with_options(buf, Parens::InApply, Newlines::Yes, indent);
buf.push('!');
match target {
TryTarget::Task => buf.push('!'),
TryTarget::Result => buf.push('?'),
}
}
MalformedIdent(str, _) => {
buf.indent(indent);
@ -615,6 +644,22 @@ fn starts_with_newline(expr: &Expr) -> bool {
}
}
fn fmt_str_body(body: &str, buf: &mut Buf) {
for c in body.chars() {
match c {
// Format blank characters as unicode escapes
'\u{200a}' => buf.push_str("\\u(200a)"),
'\u{200b}' => buf.push_str("\\u(200b)"),
'\u{200c}' => buf.push_str("\\u(200c)"),
'\u{feff}' => buf.push_str("\\u(feff)"),
// Don't change anything else in the string
' ' => buf.push_str_allow_spaces(" "),
'\n' => buf.push_str_allow_spaces("\n"),
_ => buf.push(c),
}
}
}
fn format_str_segment(seg: &StrSegment, buf: &mut Buf, indent: u16) {
use StrSegment::*;
@ -624,10 +669,10 @@ fn format_str_segment(seg: &StrSegment, buf: &mut Buf, indent: u16) {
// a line break in the input string
match string.strip_suffix('\n') {
Some(string_without_newline) => {
buf.push_str_allow_spaces(string_without_newline);
fmt_str_body(string_without_newline, buf);
buf.newline();
}
None => buf.push_str_allow_spaces(string),
None => fmt_str_body(string, buf),
}
}
Unicode(loc_str) => {
@ -687,7 +732,7 @@ pub fn fmt_str_literal(buf: &mut Buf, literal: StrLiteral, indent: u16) {
buf.push_newline_literal();
for line in string.split('\n') {
buf.indent(indent);
buf.push_str_allow_spaces(line);
fmt_str_body(line, buf);
buf.push_newline_literal();
}
buf.indent(indent);
@ -695,7 +740,7 @@ pub fn fmt_str_literal(buf: &mut Buf, literal: StrLiteral, indent: u16) {
} else {
buf.indent(indent);
buf.push('"');
buf.push_str_allow_spaces(string);
fmt_str_body(string, buf);
buf.push('"');
};
}
@ -993,7 +1038,7 @@ fn fmt_when<'a>(
}
}
fn fmt_dbg<'a>(
fn fmt_dbg_stmt<'a>(
buf: &mut Buf,
condition: &'a Loc<Expr<'a>>,
continuation: &'a Loc<Expr<'a>>,
@ -1046,6 +1091,7 @@ fn fmt_if<'a>(
branches: &'a [(Loc<Expr<'a>>, Loc<Expr<'a>>)],
final_else: &'a Loc<Expr<'a>>,
is_multiline: bool,
indented_else: bool,
indent: u16,
) {
// let is_multiline_then = loc_then.is_multiline();
@ -1178,16 +1224,22 @@ fn fmt_if<'a>(
}
}
buf.indent(indent);
if is_multiline {
if indented_else {
buf.indent(indent + INDENT);
buf.push_str("else");
buf.newline();
buf.newline();
} else if is_multiline {
buf.indent(indent);
buf.push_str("else");
buf.newline();
} else {
buf.indent(indent);
buf.push_str(" else");
buf.spaces(1);
}
final_else.format(buf, return_indent);
let indent = if indented_else { indent } else { return_indent };
final_else.format(buf, indent);
}
fn fmt_closure<'a>(
@ -1215,7 +1267,7 @@ fn fmt_closure<'a>(
let mut it = loc_patterns.iter().peekable();
while let Some(loc_pattern) = it.next() {
loc_pattern.format(buf, indent);
loc_pattern.format_with_options(buf, Parens::InAsPattern, Newlines::No, indent);
if it.peek().is_some() {
buf.indent(indent);
@ -1529,6 +1581,23 @@ fn format_assigned_field_multiline<T>(
ann.value.format(buf, indent);
buf.push(',');
}
IgnoredValue(name, spaces, ann) => {
buf.newline();
buf.indent(indent);
buf.push('_');
buf.push_str(name.value);
if !spaces.is_empty() {
fmt_spaces(buf, spaces.iter(), indent);
buf.indent(indent);
}
buf.push_str(separator_prefix);
buf.push_str(":");
buf.spaces(1);
ann.value.format(buf, indent);
buf.push(',');
}
LabelOnly(name) => {
buf.newline();
buf.indent(indent);
@ -1706,7 +1775,7 @@ fn sub_expr_requests_parens(expr: &Expr<'_>) -> bool {
| BinOp::Pizza => true,
})
}
Expr::If(_, _) => true,
Expr::If { .. } => true,
Expr::SpaceBefore(e, _) => sub_expr_requests_parens(e),
Expr::SpaceAfter(e, _) => sub_expr_requests_parens(e),
_ => false,

View file

@ -5,20 +5,19 @@ use crate::collection::{fmt_collection, Braces};
use crate::expr::fmt_str_literal;
use crate::spaces::{fmt_comments_only, fmt_default_spaces, fmt_spaces, NewlineAt, INDENT};
use crate::Buf;
use roc_parse::ast::{Collection, CommentOrNewline, Header, Module, Spaced, Spaces};
use roc_parse::ast::{Collection, CommentOrNewline, Header, Spaced, Spaces, SpacesBefore};
use roc_parse::header::{
AppHeader, ExposedName, ExposesKeyword, GeneratesKeyword, HostedHeader, ImportsEntry,
ImportsKeyword, Keyword, KeywordItem, ModuleHeader, ModuleName, PackageEntry, PackageHeader,
PackageKeyword, PackageName, PackagesKeyword, PlatformHeader, PlatformKeyword,
PlatformRequires, ProvidesKeyword, ProvidesTo, RequiresKeyword, To, ToKeyword, TypedIdent,
WithKeyword,
AppHeader, ExposedName, ExposesKeyword, HostedHeader, ImportsEntry, ImportsKeyword, Keyword,
KeywordItem, ModuleHeader, ModuleName, PackageEntry, PackageHeader, PackageKeyword,
PackageName, PackagesKeyword, PlatformHeader, PlatformKeyword, PlatformRequires,
ProvidesKeyword, ProvidesTo, RequiresKeyword, To, ToKeyword, TypedIdent,
};
use roc_parse::ident::UppercaseIdent;
use roc_region::all::Loc;
pub fn fmt_module<'a>(buf: &mut Buf<'_>, module: &'a Module<'a>) {
fmt_comments_only(buf, module.comments.iter(), NewlineAt::Bottom, 0);
match &module.header {
pub fn fmt_header<'a>(buf: &mut Buf<'_>, header: &'a SpacesBefore<'a, Header<'a>>) {
fmt_comments_only(buf, header.before.iter(), NewlineAt::Bottom, 0);
match &header.item {
Header::Module(header) => {
fmt_module_header(buf, header);
}
@ -63,8 +62,6 @@ macro_rules! keywords {
keywords! {
ExposesKeyword,
ImportsKeyword,
WithKeyword,
GeneratesKeyword,
PackageKeyword,
PackagesKeyword,
RequiresKeyword,
@ -174,11 +171,17 @@ pub fn fmt_module_header<'a>(buf: &mut Buf, header: &'a ModuleHeader<'a>) {
let mut indent = fmt_spaces_with_outdent(buf, header.after_keyword, 0);
if let Some(params) = &header.params {
if is_collection_multiline(&params.params) {
if is_collection_multiline(&params.pattern.value) {
indent = INDENT;
}
fmt_collection(buf, indent, Braces::Curly, params.params, Newlines::Yes);
fmt_collection(
buf,
indent,
Braces::Curly,
params.pattern.value,
Newlines::Yes,
);
indent = fmt_spaces_with_outdent(buf, params.before_arrow, indent);
buf.push_str("->");
@ -200,9 +203,6 @@ pub fn fmt_hosted_header<'a>(buf: &mut Buf, header: &'a HostedHeader<'a>) {
fmt_exposes(buf, header.exposes.item, indent);
header.imports.keyword.format(buf, indent);
fmt_imports(buf, header.imports.item, indent);
header.generates.format(buf, indent);
header.generates_with.keyword.format(buf, indent);
fmt_exposes(buf, header.generates_with.item, indent);
}
pub fn fmt_app_header<'a>(buf: &mut Buf, header: &'a AppHeader<'a>) {

View file

@ -6,18 +6,11 @@ pub mod annotation;
pub mod collection;
pub mod def;
pub mod expr;
pub mod module;
pub mod header;
pub mod pattern;
pub mod spaces;
use bumpalo::{collections::String, Bump};
use roc_parse::ast::Module;
#[derive(Debug)]
pub struct Ast<'a> {
pub module: Module<'a>,
pub defs: roc_parse::ast::Defs<'a>,
}
#[derive(Debug)]
pub struct Buf<'a> {

View file

@ -244,9 +244,19 @@ impl<'a> Formattable for Pattern<'a> {
}
As(pattern, pattern_as) => {
let needs_parens = parens == Parens::InAsPattern;
if needs_parens {
buf.push('(');
}
fmt_pattern(buf, &pattern.value, indent, parens);
pattern_as.format(buf, indent + INDENT);
if needs_parens {
buf.push(')');
}
}
// Space

View file

@ -1,7 +1,6 @@
use bumpalo::Bump;
use roc_parse::{ast::CommentOrNewline, remove_spaces::RemoveSpaces};
use roc_parse::ast::CommentOrNewline;
use crate::{Ast, Buf};
use crate::Buf;
/// The number of spaces to indent.
pub const INDENT: u16 = 4;
@ -192,12 +191,3 @@ fn fmt_docs(buf: &mut Buf, docs: &str) {
}
buf.push_str(docs.trim_end());
}
impl<'a> RemoveSpaces<'a> for Ast<'a> {
fn remove_spaces(&self, arena: &'a Bump) -> Self {
Ast {
module: self.module.remove_spaces(arena),
defs: self.defs.remove_spaces(arena),
}
}
}

View file

@ -263,7 +263,7 @@ fn alignment_type(context: &Context, alignment: u32) -> BasicTypeEnum {
2 => context.i16_type().into(),
4 => context.i32_type().into(),
8 => context.i64_type().into(),
16 => context.i128_type().into(),
16 => context.f128_type().into(),
_ => unimplemented!("weird alignment: {alignment}"),
}
}
@ -404,7 +404,8 @@ impl<'ctx> RocUnion<'ctx> {
let mut width = self.data_width;
// add padding between data and the tag id
width = round_up_to_alignment(width, tag_id_width);
let tag_id_alignment = tag_id_width.max(1);
width = round_up_to_alignment(width, tag_id_alignment);
// add tag id
width += tag_id_width;

View file

@ -25,6 +25,7 @@ const MODULES: &[(ModuleId, &str)] = &[
(ModuleId::DECODE, "Decode.roc"),
(ModuleId::HASH, "Hash.roc"),
(ModuleId::INSPECT, "Inspect.roc"),
(ModuleId::TASK, "Task.roc"),
];
fn main() {

View file

@ -256,6 +256,7 @@ fn read_cached_types() -> MutMap<ModuleId, TypeState> {
let mod_decode = include_bytes_align_as!(u128, concat!(env!("OUT_DIR"), "/Decode.dat"));
let mod_hash = include_bytes_align_as!(u128, concat!(env!("OUT_DIR"), "/Hash.dat"));
let mod_inspect = include_bytes_align_as!(u128, concat!(env!("OUT_DIR"), "/Inspect.dat"));
let mod_task = include_bytes_align_as!(u128, concat!(env!("OUT_DIR"), "/Task.dat"));
let mut output = MutMap::default();
@ -279,6 +280,8 @@ fn read_cached_types() -> MutMap<ModuleId, TypeState> {
output.insert(ModuleId::HASH, deserialize_help(mod_hash));
output.insert(ModuleId::INSPECT, deserialize_help(mod_inspect));
output.insert(ModuleId::TASK, deserialize_help(mod_task));
}
output

View file

@ -51,6 +51,8 @@ pub fn infer_expr(
exposed_by_module: &Default::default(),
derived_module,
function_kind: FunctionKind::LambdaSet,
module_params: None,
module_params_vars: Default::default(),
#[cfg(debug_assertions)]
checkmate: None,
};
@ -159,21 +161,6 @@ pub fn can_expr_with<'a>(
// ensure the Test module is accessible in our tests
module_ids.get_or_insert(&PQModuleName::Unqualified("Test".into()));
// Desugar operators (convert them to Apply calls, taking into account
// operator precedence and associativity rules), before doing other canonicalization.
//
// If we did this *during* canonicalization, then each time we
// visited a BinOp node we'd recursively try to apply this to each of its nested
// operators, and then again on *their* nested operators, ultimately applying the
// rules multiple times unnecessarily.
let loc_expr = desugar::desugar_expr(
arena,
&loc_expr,
expr_str,
&mut None,
arena.alloc("TestPath"),
);
let mut scope = Scope::new(
home,
"TestPath".into(),
@ -184,12 +171,23 @@ pub fn can_expr_with<'a>(
let dep_idents = IdentIds::exposed_builtins(0);
let mut env = Env::new(
arena,
expr_str,
home,
Path::new("Test.roc"),
&dep_idents,
&module_ids,
None,
);
// Desugar operators (convert them to Apply calls, taking into account
// operator precedence and associativity rules), before doing other canonicalization.
//
// If we did this *during* canonicalization, then each time we
// visited a BinOp node we'd recursively try to apply this to each of its nested
// operators, and then again on *their* nested operators, ultimately applying the
// rules multiple times unnecessarily.
let loc_expr = desugar::desugar_expr(&mut env, &mut scope, &loc_expr);
let (loc_expr, output) = canonicalize_expr(
&mut env,
&mut var_store,

View file

@ -16,7 +16,7 @@ mod test_reporting {
use roc_load::{self, ExecutionMode, LoadConfig, LoadedModule, LoadingProblem, Threading};
use roc_module::symbol::{Interns, ModuleId};
use roc_packaging::cache::RocCacheDir;
use roc_parse::module::parse_header;
use roc_parse::header::parse_header;
use roc_parse::state::State;
use roc_parse::test_helpers::parse_expr_with;
use roc_problem::Severity;
@ -359,7 +359,7 @@ mod test_reporting {
let src_lines: Vec<&str> = src.split('\n').collect();
let lines = LineInfo::new(src);
match roc_parse::module::parse_header(arena, state) {
match roc_parse::header::parse_header(arena, state) {
Err(fail) => {
let interns = Interns::default();
let home = crate::helpers::test_home();
@ -3793,9 +3793,9 @@ mod test_reporting {
of these?
Set
Task
List
Dict
Hash
SYNTAX PROBLEM in /code/proj/Main.roc
@ -3804,7 +3804,7 @@ mod test_reporting {
10 y = { Test.example & age: 3 }
^^^^^^^^^^^^
Only variables can be updated with record update syntax.
Only variables can be updated with record update syntax.
"
);
@ -4816,7 +4816,7 @@ mod test_reporting {
expression_indentation_end,
indoc!(
r"
f <- Foo.foo
f = Foo.foo
"
),
@r#"
@ -4827,8 +4827,8 @@ mod test_reporting {
1 app "test" provides [main] to "./platform"
2
3 main =
4 f <- Foo.foo
^
4 f = Foo.foo
^
Looks like the indentation ends prematurely here. Did you mean to have
another expression after this line?
@ -5760,28 +5760,6 @@ mod test_reporting {
"#
);
test_report!(
dbg_without_final_expression,
indoc!(
r"
dbg 42
"
),
@r#"
INDENT ENDS AFTER EXPRESSION in tmp/dbg_without_final_expression/Test.roc
I am partway through parsing a dbg statement, but I got stuck here:
4 dbg 42
^
I was expecting a final expression, like so
dbg 42
"done"
"#
);
test_report!(
expect_without_final_expression,
indoc!(
@ -6318,16 +6296,17 @@ All branches in an `if` must have the same type!
)
}
// TODO: this test seems out of date (what is the `effects` clause?) and as such should be removed
#[test]
fn platform_requires_rigids() {
report_header_problem_as(
indoc!(
r#"
platform "folkertdev/foo"
requires { main : Effect {} }
requires { main : Task {} [] }
exposes []
packages {}
imports [Task]
imports []
provides [mainForHost]
effects fx.Effect
{
@ -6344,13 +6323,13 @@ All branches in an `if` must have the same type!
I am partway through parsing a header, but I got stuck here:
1 platform "folkertdev/foo"
2 requires { main : Effect {} }
2 requires { main : Task {} [] }
^
I am expecting a list of type names like `{}` or `{ Model }` next. A full
`requires` definition looks like
requires { Model, Msg } {main : Effect {}}
requires { Model, Msg } {main : Task {} []}
"#
),
)
@ -6617,34 +6596,6 @@ All branches in an `if` must have the same type!
"
);
test_report!(
backpassing_type_error,
indoc!(
r#"
x <- List.map ["a", "b"]
x + 1
"#
),
@r#"
TYPE MISMATCH in /code/proj/Main.roc
This 2nd argument to `map` has an unexpected type:
4> x <- List.map ["a", "b"]
5>
6> x + 1
The argument is an anonymous function of type:
Num * -> Num *
But `map` needs its 2nd argument to be:
Str -> Num *
"#
);
test_report!(
expect_expr_type_error,
indoc!(
@ -8144,7 +8095,7 @@ All branches in an `if` must have the same type!
unimported_modules_reported,
indoc!(
r#"
alt : Task.Task {} []
alt : Unimported.CustomType
alt = "whatever man you don't even know my type"
alt
"#
@ -8152,18 +8103,18 @@ All branches in an `if` must have the same type!
@r"
MODULE NOT IMPORTED in /code/proj/Main.roc
The `Task` module is not imported:
The `Unimported` module is not imported:
4 alt : Task.Task {} []
^^^^^^^^^^^^^^^
4 alt : Unimported.CustomType
^^^^^^^^^^^^^^^^^^^^^
Is there an import missing? Perhaps there is a typo. Did you mean one
of these?
Hash
Encode
Inspect
Dict
List
Num
Box
"
);
@ -10198,9 +10149,9 @@ All branches in an `if` must have the same type!
withOpen : (Handle -> Result {} *) -> Result {} *
withOpen = \callback ->
handle <- await (open {})
{} <- await (callback handle)
close handle
await (open {}) \handle ->
await (callback handle) \_ ->
close handle
withOpen
"
@ -10212,9 +10163,9 @@ All branches in an `if` must have the same type!
10 withOpen : (Handle -> Result {} *) -> Result {} *
11 withOpen = \callback ->
12> handle <- await (open {})
13> {} <- await (callback handle)
14> close handle
12> await (open {}) \handle ->
13> await (callback handle) \_ ->
14> close handle
The type annotation on `withOpen` says this `await` call should have the
type:
@ -10227,6 +10178,7 @@ All branches in an `if` must have the same type!
Tip: Any connection between types must use a named type variable, not
a `*`! Maybe the annotation on `withOpen` should have a named type
variable in place of the `*`?
"
);
@ -10830,7 +10782,7 @@ All branches in an `if` must have the same type!
7 a: <- "a",
^^^
Tip: Remove `<-` to assign the field directly.
Tip: Remove <- to assign the field directly.
"#
);
@ -10875,12 +10827,12 @@ All branches in an `if` must have the same type!
),
@r#"
EMPTY RECORD BUILDER in /code/proj/Main.roc
This record builder has no fields:
4 { a <- }
^^^^^^^^
I need at least two fields to combine their values into a record.
"#
);
@ -10898,11 +10850,11 @@ All branches in an `if` must have the same type!
NOT ENOUGH FIELDS IN RECORD BUILDER in /code/proj/Main.roc
This record builder only has one field:
4> { a <-
5> b: 123
6> }
I need at least two fields to combine their values into a record.
"#
);
@ -10919,14 +10871,14 @@ All branches in an `if` must have the same type!
),
@r#"
OPTIONAL FIELD IN RECORD BUILDER in /code/proj/Main.roc
Optional fields are not allowed to be used in record builders.
4 { a <-
5 b: 123,
6> c? 456
7 }
Record builders can only have required values for their fields.
"#
);
@ -10962,7 +10914,7 @@ All branches in an `if` must have the same type!
6 { xyz <-
^^^
Note: Record builders need a mapper function before the `<-` to combine
Note: Record builders need a mapper function before the <- to combine
fields together with.
"#
);
@ -11844,6 +11796,32 @@ All branches in an `if` must have the same type!
@r"
"
);
test_report!(
deprecated_backpassing,
indoc!(
r#"
foo = \bar ->
baz <- Result.try bar
Ok (baz * 3)
foo (Ok 123)
"#
),
@r###"
BACKPASSING DEPRECATED in /code/proj/Main.roc
Backpassing (<-) like this will soon be deprecated:
5 baz <- Result.try bar
^^^^^^^^^^^^^^^^^^^^^
You should use a ! for awaiting tasks or a ? for trying results, and
functions everywhere else.
"###
);
test_report!(
unknown_shorthand_no_deps,
indoc!(
@ -13881,7 +13859,7 @@ All branches in an `if` must have the same type!
"#
),
@r#"
DEFINITIONs ONLY USED IN RECURSION in /code/proj/Main.roc
DEFINITIONS ONLY USED IN RECURSION in /code/proj/Main.roc
These 2 definitions are only used in mutual recursion with themselves:
@ -13890,6 +13868,7 @@ All branches in an `if` must have the same type!
If you don't intend to use or export any of them, they should all be
removed!
"#
);
@ -13953,7 +13932,7 @@ All branches in an `if` must have the same type!
"#
),
@r#"
DEFINITIONs ONLY USED IN RECURSION in /code/proj/Main.roc
DEFINITIONS ONLY USED IN RECURSION in /code/proj/Main.roc
These 2 definitions are only used in mutual recursion with themselves:
@ -13962,6 +13941,7 @@ All branches in an `if` must have the same type!
If you don't intend to use or export any of them, they should all be
removed!
"#
);
@ -14515,6 +14495,76 @@ All branches in an `if` must have the same type!
"
);
test_report!(
dbg_unapplied,
indoc!(
r"
1 + dbg + 2
"
),
@r"
UNAPPLIED DBG in /code/proj/Main.roc
This `dbg` doesn't have a value given to it:
4 1 + dbg + 2
^^^
`dbg` must be passed a value to print at the exact place it's used. `dbg`
can't be used as a value that's passed around, like functions can be -
it must be applied immediately!
TYPE MISMATCH in /code/proj/Main.roc
This 2nd argument to + has an unexpected type:
4 1 + dbg + 2
^^^
This value is a:
{}
But + needs its 2nd argument to be:
Num *
"
);
test_report!(
dbg_overapplied,
indoc!(
r#"
1 + dbg "" "" + 2
"#
),
@r#"
OVERAPPLIED DBG in /code/proj/Main.roc
This `dbg` has too many values given to it:
4 1 + dbg "" "" + 2
^^^^^
`dbg` must be given exactly one value to print.
TYPE MISMATCH in /code/proj/Main.roc
This 2nd argument to + has an unexpected type:
4 1 + dbg "" "" + 2
^^^^^^^^^
This value is a:
{}
But + needs its 2nd argument to be:
Num *
"#
);
// TODO: add the following tests after built-in Tasks are added
// https://github.com/roc-lang/roc/pull/6836

View file

@ -33,6 +33,7 @@ roc_tracing = { path = "../../tracing" }
roc_types = { path = "../types" }
roc_unify = { path = "../unify" }
roc_worker = { path = "../worker" }
roc_lower_params = { path = "../lower_params" }
ven_pretty = { path = "../../vendor/pretty" }

View file

@ -465,7 +465,8 @@ fn contains_unexposed_type(
while let Some(field) = fields_to_process.pop() {
match field {
AssignedField::RequiredValue(_field, _spaces, loc_val)
| AssignedField::OptionalValue(_field, _spaces, loc_val) => {
| AssignedField::OptionalValue(_field, _spaces, loc_val)
| AssignedField::IgnoredValue(_field, _spaces, loc_val) => {
if contains_unexposed_type(&loc_val.value, exposed_module_ids, module_ids) {
return true;
}
@ -721,7 +722,7 @@ fn record_field_to_doc(
AssignedField::LabelOnly(label) => Some(RecordField::LabelOnly {
name: label.value.to_string(),
}),
AssignedField::Malformed(_) => None,
AssignedField::Malformed(_) | AssignedField::IgnoredValue(_, _, _) => None,
}
}

View file

@ -18,7 +18,7 @@ use roc_can::constraint::{Constraint as ConstraintSoa, Constraints, TypeOrVar};
use roc_can::expr::{DbgLookup, Declarations, ExpectLookup, PendingDerives};
use roc_can::module::{
canonicalize_module_defs, ExposedByModule, ExposedForModule, ExposedModuleTypes, Module,
ResolvedImplementations, TypeState,
ModuleParams, ResolvedImplementations, TypeState,
};
use roc_collections::{default_hasher, BumpMap, MutMap, MutSet, VecMap, VecSet};
use roc_constrain::module::constrain_module;
@ -48,11 +48,11 @@ use roc_mono::reset_reuse;
use roc_mono::{drop_specialization, inc_dec};
use roc_packaging::cache::RocCacheDir;
use roc_parse::ast::{self, CommentOrNewline, ExtractSpaces, Spaced, ValueDef};
use roc_parse::header::parse_module_defs;
use roc_parse::header::{
self, AppHeader, ExposedName, HeaderType, ImportsKeywordItem, PackageEntry, PackageHeader,
PlatformHeader, To, TypedIdent,
};
use roc_parse::module::parse_module_defs;
use roc_parse::parser::{FileError, SourceError, SyntaxError};
use roc_problem::Severity;
use roc_region::all::{LineInfo, Loc, Region};
@ -251,6 +251,7 @@ fn start_phase<'a>(
let mut aliases = MutMap::default();
let mut abilities_store = PendingAbilitiesStore::default();
let mut imported_module_params = VecMap::default();
for imported in parsed.available_modules.keys() {
match state.module_cache.aliases.get(imported) {
@ -293,6 +294,10 @@ fn start_phase<'a>(
.union(import_store.closure_from_imported(exposed_symbols));
}
}
if let Some(params) = state.module_cache.module_params.get(imported) {
imported_module_params.insert(*imported, params.clone());
}
}
let skip_constraint_gen = {
@ -310,6 +315,8 @@ fn start_phase<'a>(
abilities_store,
skip_constraint_gen,
exposed_module_ids: state.exposed_modules,
exec_mode: state.exec_mode,
imported_module_params,
}
}
@ -356,6 +363,7 @@ fn start_phase<'a>(
declarations,
state.cached_types.clone(),
derived_module,
state.exec_mode,
//
#[cfg(debug_assertions)]
checkmate,
@ -889,6 +897,8 @@ enum BuildTask<'a> {
abilities_store: PendingAbilitiesStore,
exposed_module_ids: &'a [ModuleId],
skip_constraint_gen: bool,
exec_mode: ExecutionMode,
imported_module_params: VecMap<ModuleId, ModuleParams>,
},
Solve {
module: Module,
@ -905,6 +915,7 @@ enum BuildTask<'a> {
dep_idents: IdentIdsByModule,
cached_subs: CachedTypeState,
derived_module: SharedDerivedModule,
exec_mode: ExecutionMode,
#[cfg(debug_assertions)]
checkmate: Option<roc_checkmate::Collector>,
@ -1327,8 +1338,8 @@ fn load_packages_from_main<'a>(
let parse_state = roc_parse::state::State::new(arena.alloc(src_bytes));
let (parsed_module, _) =
roc_parse::module::parse_header(arena, parse_state.clone()).map_err(|fail| {
let (parsed_header, _) =
roc_parse::header::parse_header(arena, parse_state.clone()).map_err(|fail| {
LoadingProblem::ParsingFailed(
fail.map_problem(SyntaxError::Header)
.into_file_error(filename.clone()),
@ -1337,7 +1348,7 @@ fn load_packages_from_main<'a>(
use ast::Header::*;
let packages = match parsed_module.header {
let packages = match parsed_header.item {
App(AppHeader { packages, .. }) | Package(PackageHeader { packages, .. }) => {
unspace(arena, packages.value.items)
}
@ -1654,7 +1665,7 @@ fn state_thread_step<'a>(
Ok(ControlFlow::Break(LoadResult::Monomorphized(monomorphized)))
}
Msg::FailedToReadFile { filename, error } => {
let buf = to_file_problem_report_string(filename, error);
let buf = to_file_problem_report_string(filename, error, true);
Err(LoadingProblem::FormattedReport(buf))
}
@ -1828,7 +1839,7 @@ pub fn report_loading_problem(
}
LoadingProblem::FormattedReport(report) => report,
LoadingProblem::FileProblem { filename, error } => {
to_file_problem_report_string(filename, error)
to_file_problem_report_string(filename, error, true)
}
LoadingProblem::NoPlatformPackage {
filename,
@ -2318,6 +2329,7 @@ fn update<'a>(
extend_module_with_builtin_import(parsed, ModuleId::DECODE);
extend_module_with_builtin_import(parsed, ModuleId::HASH);
extend_module_with_builtin_import(parsed, ModuleId::INSPECT);
extend_module_with_builtin_import(parsed, ModuleId::TASK);
}
state
@ -2395,6 +2407,13 @@ fn update<'a>(
.pending_abilities
.insert(module_id, constrained_module.module.abilities_store.clone());
if let Some(module_params) = constrained_module.module.module_params.clone() {
state
.module_cache
.module_params
.insert(module_id, module_params);
}
state
.module_cache
.constrained
@ -3349,7 +3368,7 @@ fn load_package_from_disk<'a>(
let parse_start = Instant::now();
let bytes = arena.alloc(bytes_vec);
let parse_state = roc_parse::state::State::new(bytes);
let parsed = roc_parse::module::parse_header(arena, parse_state.clone());
let parsed = roc_parse::header::parse_header(arena, parse_state.clone());
let parse_header_duration = parse_start.elapsed();
// Insert the first entries for this module's timings
@ -3360,8 +3379,8 @@ fn load_package_from_disk<'a>(
match parsed {
Ok((
ast::Module {
header: ast::Header::Module(header),
ast::SpacesBefore {
item: ast::Header::Module(header),
..
},
_parse_state,
@ -3369,8 +3388,8 @@ fn load_package_from_disk<'a>(
"expected platform/package module, got Module with header\n{header:?}"
))),
Ok((
ast::Module {
header: ast::Header::Hosted(header),
ast::SpacesBefore {
item: ast::Header::Hosted(header),
..
},
_parse_state,
@ -3378,8 +3397,8 @@ fn load_package_from_disk<'a>(
"expected platform/package module, got Hosted module with header\n{header:?}"
))),
Ok((
ast::Module {
header: ast::Header::App(header),
ast::SpacesBefore {
item: ast::Header::App(header),
..
},
_parse_state,
@ -3387,9 +3406,9 @@ fn load_package_from_disk<'a>(
"expected platform/package module, got App with header\n{header:?}"
))),
Ok((
ast::Module {
header: ast::Header::Package(header),
comments,
ast::SpacesBefore {
item: ast::Header::Package(header),
before: comments,
},
parser_state,
)) => {
@ -3430,9 +3449,9 @@ fn load_package_from_disk<'a>(
Ok(Msg::Many(messages))
}
Ok((
ast::Module {
header: ast::Header::Platform(header),
comments,
ast::SpacesBefore {
item: ast::Header::Platform(header),
before: comments,
},
parser_state,
)) => {
@ -3530,13 +3549,13 @@ fn load_builtin_module_help<'a>(
let opt_shorthand = None;
let filename = PathBuf::from(filename);
let parse_state = roc_parse::state::State::new(src_bytes.as_bytes());
let parsed = roc_parse::module::parse_header(arena, parse_state.clone());
let parsed = roc_parse::header::parse_header(arena, parse_state.clone());
match parsed {
Ok((
ast::Module {
header: ast::Header::Module(header),
comments,
ast::SpacesBefore {
item: ast::Header::Module(header),
before: comments,
},
parse_state,
)) => {
@ -3550,7 +3569,7 @@ fn load_builtin_module_help<'a>(
header_type: HeaderType::Builtin {
name: header::ModuleName::new(name_stem),
exposes: unspace(arena, header.exposes.items),
generates_with: &[],
opt_params: header.params,
},
module_comments: comments,
header_imports: header.interface_imports,
@ -3632,6 +3651,7 @@ fn load_module<'a>(
"Decode", ModuleId::DECODE
"Hash", ModuleId::HASH
"Inspect", ModuleId::INSPECT
"Task", ModuleId::TASK
}
let (filename, opt_shorthand) = module_name_to_path(src_dir, &module_name, arc_shorthands);
@ -3786,7 +3806,7 @@ fn parse_header<'a>(
) -> Result<HeaderOutput<'a>, LoadingProblem<'a>> {
let parse_start = Instant::now();
let parse_state = roc_parse::state::State::new(src_bytes);
let parsed = roc_parse::module::parse_header(arena, parse_state.clone());
let parsed = roc_parse::header::parse_header(arena, parse_state.clone());
let parse_header_duration = parse_start.elapsed();
if let Err(problem) = ensure_roc_file(&filename, src_bytes) {
@ -3815,9 +3835,9 @@ fn parse_header<'a>(
match parsed {
Ok((
ast::Module {
header: ast::Header::Module(header),
comments,
ast::SpacesBefore {
item: ast::Header::Module(header),
before: comments,
},
parse_state,
)) => {
@ -3837,6 +3857,7 @@ fn parse_header<'a>(
header_type: HeaderType::Module {
name: roc_parse::header::ModuleName::new(module_name),
exposes: unspace(arena, header.exposes.items),
opt_params: header.params,
},
module_comments: comments,
header_imports: header.interface_imports,
@ -3852,9 +3873,9 @@ fn parse_header<'a>(
})
}
Ok((
ast::Module {
header: ast::Header::Hosted(header),
comments,
ast::SpacesBefore {
item: ast::Header::Hosted(header),
before: comments,
},
parse_state,
)) => {
@ -3866,8 +3887,6 @@ fn parse_header<'a>(
header_type: HeaderType::Hosted {
name: header.name.value,
exposes: unspace(arena, header.exposes.item.items),
generates: header.generates.item,
generates_with: unspace(arena, header.generates_with.item.items),
},
module_comments: comments,
header_imports: Some(header.imports),
@ -3883,9 +3902,9 @@ fn parse_header<'a>(
})
}
Ok((
ast::Module {
header: ast::Header::App(header),
comments,
ast::SpacesBefore {
item: ast::Header::App(header),
before: comments,
},
parse_state,
)) => {
@ -3988,9 +4007,9 @@ fn parse_header<'a>(
})
}
Ok((
ast::Module {
header: ast::Header::Package(header),
comments,
ast::SpacesBefore {
item: ast::Header::Package(header),
before: comments,
},
parse_state,
)) => {
@ -4015,9 +4034,9 @@ fn parse_header<'a>(
}
Ok((
ast::Module {
header: ast::Header::Platform(header),
comments,
ast::SpacesBefore {
item: ast::Header::Platform(header),
before: comments,
},
parse_state,
)) => {
@ -4302,6 +4321,7 @@ impl<'a> BuildTask<'a> {
declarations: Declarations,
cached_subs: CachedTypeState,
derived_module: SharedDerivedModule,
exec_mode: ExecutionMode,
#[cfg(debug_assertions)] checkmate: Option<roc_checkmate::Collector>,
) -> Self {
@ -4325,6 +4345,7 @@ impl<'a> BuildTask<'a> {
module_timing,
cached_subs,
derived_module,
exec_mode,
#[cfg(debug_assertions)]
checkmate,
@ -4380,7 +4401,7 @@ pub fn add_imports(
def_types: &mut Vec<(Symbol, Loc<TypeOrVar>)>,
imported_rigid_vars: &mut Vec<Variable>,
imported_flex_vars: &mut Vec<Variable>,
) -> (Vec<Variable>, AbilitiesStore) {
) -> (Vec<Variable>, VecMap<ModuleId, Variable>, AbilitiesStore) {
let mut import_variables = Vec::new();
let mut cached_symbol_vars = VecMap::default();
@ -4487,7 +4508,31 @@ pub fn add_imports(
},
);
(import_variables, abilities_store)
let mut imported_param_vars = VecMap::default();
for (module_id, _) in exposed_for_module.exposed_by_module.iter_all() {
let ExposedModuleTypes {
exposed_types_storage_subs: exposed_types,
..
} = exposed_for_module.exposed_by_module.get(module_id).unwrap();
if let Some(stored_aprams_var) = exposed_types.stored_params_var {
let copied_import = exposed_types
.storage_subs
.export_variable_to(subs, stored_aprams_var);
let copied_import_var = extend_imports_data_with_copied_import(
copied_import,
&mut import_variables,
imported_rigid_vars,
imported_flex_vars,
);
imported_param_vars.insert(*module_id, copied_import_var);
}
}
(import_variables, imported_param_vars, abilities_store)
}
enum OnSymbolNotFound {
@ -4585,6 +4630,7 @@ struct SolveResult {
exposed_vars_by_symbol: Vec<(Symbol, Variable)>,
problems: Vec<TypeError>,
abilities_store: AbilitiesStore,
imported_modules_with_params: Vec<ModuleId>,
#[cfg(debug_assertions)]
checkmate: Option<roc_checkmate::Collector>,
@ -4609,6 +4655,7 @@ fn run_solve_solve(
aliases,
rigid_variables,
abilities_store: pending_abilities,
module_params,
..
} = module;
@ -4618,7 +4665,7 @@ fn run_solve_solve(
let mut subs = Subs::new_from_varstore(var_store);
let (import_variables, abilities_store) = add_imports(
let (import_variables, imported_param_vars, abilities_store) = add_imports(
module.module_id,
&mut constraints,
&mut subs,
@ -4629,6 +4676,11 @@ fn run_solve_solve(
&mut imported_flex_vars,
);
let imported_modules_with_params = imported_param_vars
.keys()
.copied()
.collect::<Vec<ModuleId>>();
let actual_constraint = constraints.let_import_constraint(
imported_rigid_vars,
imported_flex_vars,
@ -4656,6 +4708,8 @@ fn run_solve_solve(
derived_module,
#[cfg(debug_assertions)]
checkmate,
module_params,
module_params_vars: imported_param_vars,
};
let solve_output = roc_solve::module::run_solve(
@ -4709,6 +4763,7 @@ fn run_solve_solve(
exposed_vars_by_symbol,
problems: errors,
abilities_store: resolved_abilities_store,
imported_modules_with_params,
#[cfg(debug_assertions)]
checkmate,
@ -4730,6 +4785,7 @@ fn run_solve<'a>(
dep_idents: IdentIdsByModule,
cached_types: CachedTypeState,
derived_module: SharedDerivedModule,
exec_mode: ExecutionMode,
#[cfg(debug_assertions)] checkmate: Option<roc_checkmate::Collector>,
) -> Msg<'a> {
@ -4740,6 +4796,9 @@ fn run_solve<'a>(
// TODO remove when we write builtins in roc
let aliases = module.aliases.clone();
let opt_params_var = module.module_params.as_ref().map(|params| params.whole_var);
let home_has_params = opt_params_var.is_some();
let mut module = module;
let loc_expects = std::mem::take(&mut module.loc_expects);
let loc_dbgs = std::mem::take(&mut module.loc_dbgs);
@ -4773,6 +4832,7 @@ fn run_solve<'a>(
exposed_vars_by_symbol,
problems: vec![],
abilities_store: abilities,
imported_modules_with_params: vec![],
#[cfg(debug_assertions)]
checkmate: None,
@ -4800,8 +4860,9 @@ fn run_solve<'a>(
solved: mut solved_subs,
solved_implementations,
exposed_vars_by_symbol,
problems,
mut problems,
abilities_store,
imported_modules_with_params,
#[cfg(debug_assertions)]
checkmate,
@ -4811,10 +4872,25 @@ fn run_solve<'a>(
module_id,
&mut solved_subs,
&exposed_vars_by_symbol,
opt_params_var,
&solved_implementations,
&abilities_store,
);
match exec_mode {
ExecutionMode::Check => {
// Params are not lowered in check mode
}
ExecutionMode::Executable | ExecutionMode::ExecutableIfCheck | ExecutionMode::Test => {
roc_lower_params::type_error::remove_module_param_arguments(
&mut problems,
home_has_params,
&decls.symbols,
imported_modules_with_params,
);
}
}
let solved_module = SolvedModule {
exposed_vars_by_symbol,
problems,
@ -4922,6 +4998,7 @@ fn build_platform_header<'a>(
.zip(requires.iter().copied()),
arena,
);
let packages = unspace(arena, header.packages.item.items);
let exposes = bumpalo::collections::Vec::from_iter_in(
unspace(arena, header.exposes.item.items).iter().copied(),
arena,
@ -4943,7 +5020,7 @@ fn build_platform_header<'a>(
filename,
is_root_module,
opt_shorthand,
packages: &[],
packages,
header_type,
module_comments: comments,
header_imports: Some(header.imports),
@ -4963,6 +5040,8 @@ fn canonicalize_and_constrain<'a>(
parsed: ParsedModule<'a>,
skip_constraint_gen: bool,
exposed_module_ids: &[ModuleId],
exec_mode: ExecutionMode,
imported_module_params: VecMap<ModuleId, ModuleParams>,
) -> CanAndCon {
let canonicalize_start = Instant::now();
@ -4989,7 +5068,7 @@ fn canonicalize_and_constrain<'a>(
let mut var_store = VarStore::default();
let module_output = canonicalize_module_defs(
let mut module_output = canonicalize_module_defs(
arena,
parsed_defs,
&header_type,
@ -5048,6 +5127,26 @@ fn canonicalize_and_constrain<'a>(
// _before has an underscore because it's unused in --release builds
let _before = roc_types::types::get_type_clone_count();
match exec_mode {
ExecutionMode::Check => {
// No need to lower params for `roc check` and lang server
// If we did, we'd have to update the language server to exclude the extra arguments
}
ExecutionMode::Executable | ExecutionMode::ExecutableIfCheck | ExecutionMode::Test => {
// We need to lower params only if the current module has any or imports at least one with params
if module_output.module_params.is_some() || !imported_module_params.is_empty() {
roc_lower_params::lower::lower(
module_id,
&module_output.module_params,
imported_module_params,
&mut module_output.declarations,
&mut module_output.scope.locals.ident_ids,
&mut var_store,
);
}
}
}
let mut constraints = Constraints::new();
let constraint = if skip_constraint_gen {
@ -5059,6 +5158,7 @@ fn canonicalize_and_constrain<'a>(
module_output.symbols_from_requires,
&module_output.scope.abilities_store,
&module_output.declarations,
&module_output.module_params,
module_id,
)
};
@ -5096,6 +5196,7 @@ fn canonicalize_and_constrain<'a>(
| ModuleId::SET
| ModuleId::HASH
| ModuleId::INSPECT
| ModuleId::TASK
);
if !name.is_builtin() || should_include_builtin {
@ -5115,6 +5216,7 @@ fn canonicalize_and_constrain<'a>(
abilities_store: module_output.scope.abilities_store,
loc_expects: module_output.loc_expects,
loc_dbgs: module_output.loc_dbgs,
module_params: module_output.module_params,
};
let constrained_module = ConstrainedModule {
@ -5152,7 +5254,7 @@ fn parse<'a>(
let parse_state = header.parse_state;
let header_import_defs =
roc_parse::ast::Module::header_imports_to_defs(arena, header.header_imports);
roc_parse::ast::Header::header_imports_to_defs(arena, header.header_imports);
let parsed_defs = match parse_module_defs(arena, parse_state.clone(), header_import_defs) {
Ok(success) => success,
@ -5458,6 +5560,7 @@ fn make_specializations<'a>(
) -> Msg<'a> {
let make_specializations_start = Instant::now();
let mut update_mode_ids = UpdateModeIds::new();
// do the thing
let mut mono_env = roc_mono::ir::Env {
arena,
@ -6163,6 +6266,8 @@ fn run_task<'a>(
abilities_store,
skip_constraint_gen,
exposed_module_ids,
exec_mode,
imported_module_params,
} => {
let can_and_con = canonicalize_and_constrain(
arena,
@ -6174,6 +6279,8 @@ fn run_task<'a>(
parsed,
skip_constraint_gen,
exposed_module_ids,
exec_mode,
imported_module_params,
);
Ok(Msg::CanonicalizedAndConstrained(can_and_con))
@ -6193,6 +6300,7 @@ fn run_task<'a>(
dep_idents,
cached_subs,
derived_module,
exec_mode,
#[cfg(debug_assertions)]
checkmate,
@ -6211,6 +6319,7 @@ fn run_task<'a>(
dep_idents,
cached_subs,
derived_module,
exec_mode,
//
#[cfg(debug_assertions)]
checkmate,

View file

@ -25,4 +25,5 @@ pub const BUILTIN_MODULES: &[(ModuleId, &str)] = &[
(ModuleId::DECODE, "Decode"),
(ModuleId::HASH, "Hash"),
(ModuleId::INSPECT, "Inspect"),
(ModuleId::TASK, "Task"),
];

View file

@ -4,6 +4,7 @@ use crate::module::{
ModuleHeader, ParsedModule, TypeCheckedModule,
};
use roc_can::abilities::PendingAbilitiesStore;
use roc_can::module::ModuleParams;
use roc_collections::{MutMap, MutSet, VecMap};
use roc_module::ident::ModuleName;
use roc_module::symbol::{ModuleId, PQModuleName, Symbol};
@ -26,6 +27,7 @@ pub(crate) struct ModuleCache<'a> {
pub(crate) aliases: MutMap<ModuleId, MutMap<Symbol, (bool, Alias)>>,
pub(crate) pending_abilities: MutMap<ModuleId, PendingAbilitiesStore>,
pub(crate) constrained: MutMap<ModuleId, ConstrainedModule>,
pub(crate) module_params: MutMap<ModuleId, ModuleParams>,
pub(crate) typechecked: MutMap<ModuleId, TypeCheckedModule<'a>>,
pub(crate) checked: MutMap<ModuleId, CheckedModule>,
pub(crate) found_specializations: MutMap<ModuleId, FoundSpecializationsModule<'a>>,
@ -91,6 +93,7 @@ impl Default for ModuleCache<'_> {
DECODE,
HASH,
INSPECT,
TASK,
}
Self {
@ -100,6 +103,7 @@ impl Default for ModuleCache<'_> {
aliases: Default::default(),
pending_abilities: Default::default(),
constrained: Default::default(),
module_params: Default::default(),
typechecked: Default::default(),
checked: Default::default(),
found_specializations: Default::default(),

View file

@ -27,10 +27,11 @@ use roc_module::symbol::{Interns, ModuleId};
use roc_packaging::cache::RocCacheDir;
use roc_problem::can::Problem;
use roc_region::all::LineInfo;
use roc_reporting::report::RocDocAllocator;
use roc_reporting::report::{can_problem, DEFAULT_PALETTE};
use roc_reporting::report::{strip_colors, RenderTarget};
use roc_reporting::report::{type_problem, RocDocAllocator};
use roc_solve::FunctionKind;
use roc_solve_problem::TypeError;
use roc_target::Target;
use roc_test_utils_dir::TmpDir;
use roc_types::pretty_print::name_and_print_var;
@ -107,6 +108,33 @@ fn format_can_problems(
buf
}
fn format_type_problems(
problems: Vec<TypeError>,
home: ModuleId,
interns: &Interns,
filename: PathBuf,
src: &str,
) -> String {
use ven_pretty::DocAllocator;
let src_lines: Vec<&str> = src.split('\n').collect();
let lines = LineInfo::new(src);
let alloc = RocDocAllocator::new(&src_lines, home, interns);
let reports = problems
.into_iter()
.flat_map(|problem| type_problem(&alloc, &lines, filename.clone(), problem))
.map(|report| report.pretty(&alloc));
let mut buf = String::new();
alloc
.stack(reports)
.append(alloc.line())
.1
.render_raw(70, &mut roc_reporting::report::CiWrite::new(&mut buf))
.unwrap();
buf
}
fn multiple_modules(subdir: &str, files: Vec<(&str, &str)>) -> Result<LoadedModule, String> {
let arena = Bump::new();
let arena = &arena;
@ -130,11 +158,19 @@ fn multiple_modules(subdir: &str, files: Vec<(&str, &str)>) -> Result<LoadedModu
));
}
assert!(loaded_module
let type_problems = loaded_module
.type_problems
.remove(&home)
.unwrap_or_default()
.is_empty(),);
.unwrap_or_default();
if !type_problems.is_empty() {
return Err(format_type_problems(
type_problems,
home,
&loaded_module.interns,
filepath.clone(),
src,
));
}
Ok(loaded_module)
}
@ -279,6 +315,13 @@ fn expect_types(mut loaded_module: LoadedModule, mut expected_types: HashMap<&st
match declarations.declarations[index] {
Value | Function(_) | Recursive(_) | TailRecursive(_) => {
let body = declarations.expressions[index].clone();
if let roc_can::expr::Expr::ImportParams(_, _, None) = body.value {
// Skip import defs without params
continue;
}
let symbol = declarations.symbols[index].value;
let expr_var = declarations.variables[index];
@ -407,7 +450,13 @@ fn module_with_deps() {
match declarations.declarations[index] {
Value | Function(_) | Recursive(_) | TailRecursive(_) => {
def_count += 1;
let body = declarations.expressions[index].clone();
if let roc_can::expr::Expr::ImportParams(_, _, None) = body.value {
// Skip import defs without params
} else {
def_count += 1;
}
}
Destructure(_) => {
def_count += 1;
@ -1563,6 +1612,325 @@ fn cannot_use_original_name_if_imported_with_alias() {
multiple_modules("cannot_use_original_name_if_imported_with_alias", modules).unwrap_err();
}
#[test]
fn module_params_checks() {
let modules = vec![
(
"Api.roc",
indoc!(
r#"
module { key } -> [url]
url = "example.com/$(key)"
"#
),
),
(
"Main.roc",
indoc!(
r#"
module [example]
import Api { key: "abcdef" }
example = Api.url
"#
),
),
];
let result = multiple_modules("module_params_checks", modules);
assert!(result.is_ok());
}
#[test]
fn module_params_optional() {
let modules = vec![
(
"Api.roc",
indoc!(
r#"
module { key, exp ? "default" } -> [url]
url = "example.com/$(key)?exp=$(exp)"
"#
),
),
(
"Main.roc",
indoc!(
r#"
module [example]
import Api { key: "abcdef" }
example = Api.url
"#
),
),
];
let result = multiple_modules("module_params_optional", modules);
assert!(result.is_ok())
}
#[test]
fn module_params_typecheck_fail() {
let modules = vec![
(
"Api.roc",
indoc!(
r#"
module { key } -> [url]
url = "example.com/$(key)"
"#
),
),
(
"Main.roc",
indoc!(
r#"
module [example]
import Api { key: 123 }
example = Api.url
"#
),
),
];
let result = multiple_modules("module_params_typecheck_fail", modules).unwrap_err();
assert_eq!(
result,
indoc!(
r#"
MODULE PARAMS MISMATCH in tmp/module_params_typecheck_fail/Main.roc
Something is off with the params provided by this import:
3 import Api { key: 123 }
^^^^^^^^^^^^
This is the type I inferred:
{ key : Num * }
However, Api expects:
{ key : Str }
"#
)
);
}
#[test]
fn module_params_missing_fields() {
let modules = vec![
(
"Api.roc",
indoc!(
r#"
module { key } -> [url]
url = "example.com/$(key)"
"#
),
),
(
"Main.roc",
indoc!(
r#"
module [example]
import Api {}
example = Api.url
"#
),
),
];
let result = multiple_modules("module_params_missing_fields", modules).unwrap_err();
assert_eq!(
result,
indoc!(
r#"
MODULE PARAMS MISMATCH in tmp/module_params_missing_fields/Main.roc
Something is off with the params provided by this import:
3 import Api {}
^^
This is the type I inferred:
{}
However, Api expects:
{ key : Str }
Tip: Looks like the key field is missing.
"#
)
);
}
#[test]
fn module_params_extra_fields() {
let modules = vec![
(
"Api.roc",
indoc!(
r#"
module { key } -> [url]
url = "example.com/$(key)"
"#
),
),
(
"Main.roc",
indoc!(
r#"
module [example]
import Api { key: "123", doesNotExist: Bool.true }
example = Api.url
"#
),
),
];
let result = multiple_modules("module_params_extra_fields", modules).unwrap_err();
assert_eq!(
result,
indoc!(
r#"
MODULE PARAMS MISMATCH in tmp/module_params_extra_fields/Main.roc
Something is off with the params provided by this import:
3 import Api { key: "123", doesNotExist: Bool.true }
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
This is the type I inferred:
{ doesNotExist : Bool, }
However, Api expects:
{ }
"#
)
);
}
#[test]
fn module_params_unexpected() {
let modules = vec![
(
"Api.roc",
indoc!(
r#"
module [url]
url = "example.com"
"#
),
),
(
"Main.roc",
indoc!(
r#"
module [example]
import Api { key: 123 }
example = Api.url
"#
),
),
];
let err = multiple_modules("module_params_unexpected", modules).unwrap_err();
assert_eq!(
err,
indoc!(
r#"
UNEXPECTED MODULE PARAMS in tmp/module_params_unexpected/Main.roc
This import specifies module params:
3 import Api { key: 123 }
^^^^^^^^^^^^
However, Api does not expect any. Did you intend to import a different
module?
"#
)
)
}
#[test]
fn module_params_missing() {
let modules = vec![
(
"Api.roc",
indoc!(
r#"
module { key, exp } -> [url]
url = "example.com/$(key)?exp=$(Num.toStr exp)"
"#
),
),
(
"Main.roc",
indoc!(
r#"
module [example]
import Api
example = Api.url
"#
),
),
];
let err = multiple_modules("module_params_missing", modules).unwrap_err();
assert_eq!(
err,
indoc!(
r#"
MISSING MODULE PARAMS in tmp/module_params_missing/Main.roc
This import specifies no module params:
3 import Api
^^^^^^^^^^
However, Api expects the following to be provided:
{
exp : Num *,
key : Str,
}
You can provide params after the module name, like:
import Menu { echo, read }
"#
)
)
}
#[test]
fn issue_2863_module_type_does_not_exist() {
let modules = vec![
@ -1783,7 +2151,7 @@ fn roc_file_no_extension() {
indoc!(
r#"
app "helloWorld"
packages { pf: "https://github.com/roc-lang/basic-cli/releases/download/0.12.0/Lb8EgiejTUzbggO2HVVuPJFkwvvsfW6LojkLR20kTVE.tar.br" }
packages { pf: "https://github.com/roc-lang/basic-cli/releases/download/0.15.0/SlwdbJ-3GR7uBWQo6zlmYWNYOxnvo8r6YABXD-45UOw.tar.br" }
imports [pf.Stdout]
provides [main] to pf

View file

@ -0,0 +1,18 @@
[package]
name = "roc_lower_params"
description = "Lowers module params by extending functions and their calls"
authors.workspace = true
edition.workspace = true
license.workspace = true
version.workspace = true
[dependencies]
roc_can = { path = "../can" }
roc_module = { path = "../module" }
roc_region = { path = "../region" }
roc_types = { path = "../types" }
roc_collections = { path = "../collections" }
roc_solve_problem = { path = "../solve_problem" }
bumpalo.workspace = true

View file

@ -0,0 +1,2 @@
pub mod lower;
pub mod type_error;

View file

@ -0,0 +1,566 @@
use roc_can::{
expr::{
AnnotatedMark, ClosureData,
DeclarationTag::*,
Declarations,
Expr::{self, *},
},
module::ModuleParams,
pattern::{Pattern, RecordDestruct},
};
use roc_collections::VecMap;
use roc_module::symbol::{IdentId, IdentIds, ModuleId, Symbol};
use roc_region::all::Loc;
use roc_types::{
subs::{VarStore, Variable},
types::Type,
};
use std::iter::once;
struct LowerParams<'a> {
home_id: ModuleId,
home_params: &'a Option<ModuleParams>,
imported_params: VecMap<ModuleId, ModuleParams>,
var_store: &'a mut VarStore,
ident_ids: &'a mut IdentIds,
top_level_idents: Vec<IdentId>,
}
pub fn lower(
home_id: ModuleId,
home_params: &Option<ModuleParams>,
imported_params: VecMap<ModuleId, ModuleParams>,
decls: &mut Declarations,
ident_ids: &mut IdentIds,
var_store: &mut VarStore,
) {
let top_level_idents = decls
.symbols
.iter()
.map(|symbol| symbol.value.ident_id())
.collect();
let mut env = LowerParams {
home_id,
home_params,
imported_params,
ident_ids,
var_store,
top_level_idents,
};
env.lower_decls(decls);
}
impl<'a> LowerParams<'a> {
fn lower_decls(&mut self, decls: &mut Declarations) {
let home_param_symbols = match self.home_params {
Some(params) => params
.destructs
.iter()
.map(|destruct| destruct.value.symbol)
.chain(once(params.whole_symbol))
.collect::<Vec<Symbol>>(),
None => vec![],
};
for index in 0..decls.len() {
let tag = decls.declarations[index];
match tag {
Value => {
self.lower_expr(&mut decls.expressions[index].value);
if let Some(new_arg) = self.home_params_argument() {
// This module has params, and this is a top-level value,
// so we need to convert it into a function that takes them.
decls.convert_value_to_function(index, vec![new_arg], self.var_store);
}
}
Function(fn_def_index) | Recursive(fn_def_index) | TailRecursive(fn_def_index) => {
if let Some((var, mark, pattern)) = self.home_params_argument() {
// This module has params, and this is a top-level function,
// so we need to extend its definition to take them.
let function_body = &mut decls.function_bodies[fn_def_index.index()].value;
function_body.arguments.push((var, mark, pattern));
// Remove home params from the captured symbols, only nested lambdas need them.
function_body
.captured_symbols
.retain(|(sym, _)| !home_param_symbols.contains(sym));
if let Some(ann) = &mut decls.annotations[index] {
if let Type::Function(args, _, _) = &mut ann.signature {
args.push(Type::Variable(var));
}
}
}
self.lower_expr(&mut decls.expressions[index].value);
}
Destructure(_) | Expectation | ExpectationFx => {
self.lower_expr(&mut decls.expressions[index].value);
}
MutualRecursion { .. } => {}
}
}
}
fn lower_expr(&mut self, expr: &mut Expr) {
let mut expr_stack = vec![expr];
while let Some(expr) = expr_stack.pop() {
match expr {
// Nodes to lower
ParamsVar {
symbol,
var,
params_symbol,
params_var,
} => {
// The module was imported with params, but it might not actually expect them.
// We should only lower if it does to prevent confusing type errors.
if let Some(arity) = self.get_imported_def_arity(symbol) {
*expr = self.lower_naked_params_var(
arity,
*symbol,
*var,
*params_symbol,
*params_var,
);
}
}
Var(symbol, var) => {
if let Some((params, arity)) = self.params_extended_home_symbol(symbol) {
*expr = self.lower_naked_params_var(
arity,
*symbol,
*var,
params.whole_symbol,
params.whole_var,
);
}
}
Call(fun, args, _called_via) => {
expr_stack.reserve(args.len() + 1);
match fun.1.value {
ParamsVar {
symbol,
var,
params_var,
params_symbol,
} => {
// Calling an imported function with params
match self.get_imported_def_arity(&symbol) {
Some(0) => {
// We are calling a function but the top-level declaration has no arguments.
// This can either be a function alias or a top-level def that returns functions
// under multiple branches.
// We call the value def with params, and apply the returned function to the original arguments.
fun.1.value = self.call_value_def_with_params(
symbol,
var,
params_symbol,
params_var,
);
}
Some(_) => {
// The module expects params and they were provided, we need to extend the call.
fun.1.value = Var(symbol, var);
args.push((
params_var,
Loc::at_zero(Var(params_symbol, params_var)),
));
}
None => {
// The module expects no params, do not extend to prevent confusing type errors.
fun.1.value = Var(symbol, var);
}
}
}
Var(symbol, var) => {
if let Some((params, arity)) = self.params_extended_home_symbol(&symbol)
{
if arity == 0 {
// Calling the result of a top-level value def in the current module
fun.1.value = self.call_value_def_with_params(
symbol,
var,
params.whole_symbol,
params.whole_var,
);
} else {
// Calling a top-level function in the current module with params
args.push((
params.whole_var,
Loc::at_zero(Var(params.whole_symbol, params.whole_var)),
));
}
}
}
_ => expr_stack.push(&mut fun.1.value),
}
for (_, arg) in args.iter_mut() {
expr_stack.push(&mut arg.value);
}
}
// Nodes to walk
Closure(ClosureData {
loc_body,
captured_symbols: _,
name: _,
function_type: _,
closure_type: _,
return_type: _,
recursive: _,
arguments: _,
}) => {
expr_stack.push(&mut loc_body.value);
}
LetNonRec(def, cont) => {
expr_stack.reserve(2);
expr_stack.push(&mut def.loc_expr.value);
expr_stack.push(&mut cont.value);
}
LetRec(defs, cont, _cycle_mark) => {
expr_stack.reserve(defs.len() + 1);
for def in defs {
expr_stack.push(&mut def.loc_expr.value);
}
expr_stack.push(&mut cont.value);
}
When {
loc_cond,
branches,
cond_var: _,
expr_var: _,
region: _,
branches_cond_var: _,
exhaustive: _,
} => {
expr_stack.reserve(branches.len() + 1);
expr_stack.push(&mut loc_cond.value);
for branch in branches.iter_mut() {
expr_stack.push(&mut branch.value.value);
}
}
If {
branches,
final_else,
cond_var: _,
branch_var: _,
} => {
expr_stack.reserve(branches.len() * 2 + 1);
for (cond, ret) in branches.iter_mut() {
expr_stack.push(&mut cond.value);
expr_stack.push(&mut ret.value);
}
expr_stack.push(&mut final_else.value);
}
RunLowLevel {
args,
op: _,
ret_var: _,
}
| ForeignCall {
foreign_symbol: _,
args,
ret_var: _,
} => {
expr_stack.extend(args.iter_mut().map(|(_, arg)| arg));
}
List {
elem_var: _,
loc_elems,
} => {
expr_stack.extend(loc_elems.iter_mut().map(|loc_elem| &mut loc_elem.value));
}
Record {
record_var: _,
fields,
} => {
expr_stack.extend(
fields
.iter_mut()
.map(|(_, field)| &mut field.loc_expr.value),
);
}
Tuple {
tuple_var: _,
elems,
} => {
expr_stack.extend(elems.iter_mut().map(|(_, elem)| &mut elem.value));
}
ImportParams(_, _, Some((_, params_expr))) => {
expr_stack.push(params_expr);
}
Crash { msg, ret_var: _ } => {
expr_stack.push(&mut msg.value);
}
RecordAccess {
loc_expr,
record_var: _,
ext_var: _,
field_var: _,
field: _,
} => expr_stack.push(&mut loc_expr.value),
TupleAccess {
loc_expr,
tuple_var: _,
ext_var: _,
elem_var: _,
index: _,
} => expr_stack.push(&mut loc_expr.value),
RecordUpdate {
updates,
record_var: _,
ext_var: _,
symbol: _,
} => expr_stack.extend(
updates
.iter_mut()
.map(|(_, field)| &mut field.loc_expr.value),
),
Tag {
arguments,
tag_union_var: _,
ext_var: _,
name: _,
} => expr_stack.extend(arguments.iter_mut().map(|(_, arg)| &mut arg.value)),
OpaqueRef {
argument,
opaque_var: _,
name: _,
specialized_def_type: _,
type_arguments: _,
lambda_set_variables: _,
} => expr_stack.push(&mut argument.1.value),
Expect {
loc_condition,
loc_continuation,
lookups_in_cond: _,
} => {
expr_stack.reserve(2);
expr_stack.push(&mut loc_condition.value);
expr_stack.push(&mut loc_continuation.value);
}
ExpectFx {
loc_condition,
loc_continuation,
lookups_in_cond: _,
} => {
expr_stack.reserve(2);
expr_stack.push(&mut loc_condition.value);
expr_stack.push(&mut loc_continuation.value);
}
Dbg {
loc_message,
loc_continuation,
source_location: _,
source: _,
variable: _,
symbol: _,
} => {
expr_stack.reserve(2);
expr_stack.push(&mut loc_message.value);
expr_stack.push(&mut loc_continuation.value);
}
RecordAccessor(_)
| ImportParams(_, _, None)
| ZeroArgumentTag {
closure_name: _,
variant_var: _,
ext_var: _,
name: _,
}
| OpaqueWrapFunction(_)
| EmptyRecord
| TypedHole(_)
| RuntimeError(_)
| Num(_, _, _, _)
| Int(_, _, _, _, _)
| Float(_, _, _, _, _)
| Str(_)
| SingleQuote(_, _, _, _)
| IngestedFile(_, _, _)
| AbilityMember(_, _, _) => { /* terminal */ }
}
}
}
fn unique_symbol(&mut self) -> Symbol {
Symbol::new(self.home_id, self.ident_ids.gen_unique())
}
fn home_params_argument(&mut self) -> Option<(Variable, AnnotatedMark, Loc<Pattern>)> {
match &self.home_params {
Some(module_params) => {
let destructs: Vec<Loc<RecordDestruct>> = module_params
.destructs
.iter()
.map(|destructure| {
destructure.map(|d| RecordDestruct {
symbol: d.symbol,
var: self.var_store.fresh(),
label: d.label.clone(),
typ: d.typ.clone(),
})
})
.collect();
let record_pattern = Pattern::RecordDestructure {
whole_var: module_params.record_var,
ext_var: module_params.record_ext_var,
destructs,
};
let loc_record_pattern = Loc::at(module_params.region, record_pattern);
let as_pattern =
Pattern::As(Box::new(loc_record_pattern), module_params.whole_symbol);
let loc_pattern = Loc::at(module_params.region, as_pattern);
Some((
self.var_store.fresh(),
AnnotatedMark::new(self.var_store),
loc_pattern,
))
}
None => None,
}
}
fn params_extended_home_symbol(&self, symbol: &Symbol) -> Option<(&ModuleParams, usize)> {
if symbol.module_id() == self.home_id {
self.home_params.as_ref().and_then(|params| {
params
.arity_by_name
.get(&symbol.ident_id())
.map(|arity| (params, *arity))
})
} else {
None
}
}
fn get_imported_def_arity(&self, symbol: &Symbol) -> Option<usize> {
self.imported_params
.get(&symbol.module_id())
.and_then(|params| params.arity_by_name.get(&symbol.ident_id()).copied())
}
fn lower_naked_params_var(
&mut self,
arity: usize,
symbol: Symbol,
var: Variable,
params_symbol: Symbol,
params_var: Variable,
) -> Expr {
if arity == 0 {
// We are passing a top-level value that takes params, so we need to replace the Var
// with a call that passes the params to get the final result.
//
// value = \#params -> #params.x * 2
// record = \... #params -> { doubled: value }
// ↓
// value #params
self.call_value_def_with_params(symbol, var, params_symbol, params_var)
} else {
// We are passing a top-level function that takes params, so we need to replace
// the Var with a closure that captures the params and passes them to the function.
//
// fn1 = \arg #params -> #params.x * arg
// fn2 = \... #params -> List.map [1, 2] fn1
// ↓
// (\#1 -> fn1 #1 #params)
//
let mut arguments = Vec::with_capacity(arity);
let mut call_arguments = Vec::with_capacity(arity + 1);
for _ in 0..arity {
let sym = self.unique_symbol();
let var = self.var_store.fresh();
arguments.push((
var,
AnnotatedMark::new(self.var_store),
Loc::at_zero(Pattern::Identifier(sym)),
));
call_arguments.push((var, Loc::at_zero(Var(sym, var))));
}
let params_arg = (params_var, Loc::at_zero(Var(params_symbol, params_var)));
call_arguments.push(params_arg);
let call_fn = Box::new((
self.var_store.fresh(),
Loc::at_zero(Var(symbol, var)),
self.var_store.fresh(),
self.var_store.fresh(),
));
let body = Call(
call_fn,
call_arguments,
roc_module::called_via::CalledVia::NakedParamsVar,
);
let captured_symbols = if symbol.module_id() == self.home_id
|| !self.top_level_idents.contains(&params_symbol.ident_id())
{
vec![(params_symbol, params_var)]
} else {
vec![]
};
Closure(ClosureData {
function_type: self.var_store.fresh(),
closure_type: self.var_store.fresh(),
return_type: self.var_store.fresh(),
name: self.unique_symbol(),
captured_symbols,
recursive: roc_can::expr::Recursive::NotRecursive,
arguments,
loc_body: Box::new(Loc::at_zero(body)),
})
}
}
fn call_value_def_with_params(
&mut self,
symbol: Symbol,
var: Variable,
params_symbol: Symbol,
params_var: Variable,
) -> Expr {
let params_arg = (params_var, Loc::at_zero(Var(params_symbol, params_var)));
let call_fn = Box::new((
self.var_store.fresh(),
Loc::at_zero(Var(symbol, var)),
self.var_store.fresh(),
self.var_store.fresh(),
));
Call(
call_fn,
vec![params_arg],
roc_module::called_via::CalledVia::NakedParamsVar,
)
}
}

View file

@ -0,0 +1,208 @@
use roc_can::{expected::Expected, pattern::Pattern};
use roc_module::symbol::{ModuleId, Symbol};
use roc_region::all::Loc;
use roc_solve_problem::TypeError;
use roc_types::types::{ErrorType, Reason};
/// Removes the arguments added as a result of lowering params
pub fn remove_module_param_arguments(
errors: &mut Vec<TypeError>,
home_has_params: bool,
home_top_level_symbols: &[Loc<Symbol>],
imported_modules_with_params: Vec<ModuleId>,
) {
if errors.is_empty() || (!home_has_params && imported_modules_with_params.is_empty()) {
return;
}
let home_top_level_symbols = if home_has_params {
home_top_level_symbols
.iter()
.map(|loc| loc.value)
.collect::<Vec<_>>()
} else {
vec![]
};
let env = Env {
home_top_level_symbols,
imported_modules_with_params,
};
for error in errors {
match error {
TypeError::BadExpr(_, _, found, Expected::ForReason(reason, expected, _)) => {
remove_for_reason(&env, found, reason, expected);
}
TypeError::BadExpr(
_,
_,
found,
Expected::FromAnnotation(
Loc {
value: Pattern::Identifier(name),
region: _,
},
arity,
_,
expected,
),
) if env.is_extended(name) => {
if *arity > 1 {
*arity -= 1;
}
drop_last_argument(found);
drop_last_argument(expected);
if let (
ErrorType::Function(found_args, _, _),
ErrorType::Function(expected_args, _, _),
) = (found, expected)
{
if found_args.len() > expected_args.len() {
// If the found arity doesn't match the annotation's, the params
// record would appear in the error, so we replace it with `?`.
if let Some(last) = found_args.last_mut() {
*last = ErrorType::Error;
}
}
}
}
TypeError::BadExpr(_, _, _, Expected::FromAnnotation(_, _, _, _))
| TypeError::BadExpr(_, _, _, Expected::NoExpectation(_)) => {}
// Irrelevant
TypeError::BadPattern(_, _, _, _)
| TypeError::CircularType(_, _, _)
| TypeError::CircularDef(_)
| TypeError::UnexposedLookup(_, _)
| TypeError::UnfulfilledAbility(_)
| TypeError::BadExprMissingAbility(_, _, _, _)
| TypeError::BadPatternMissingAbility(_, _, _, _)
| TypeError::Exhaustive(_)
| TypeError::StructuralSpecialization {
region: _,
typ: _,
ability: _,
member: _,
}
| TypeError::WrongSpecialization {
region: _,
ability_member: _,
expected_opaque: _,
found_opaque: _,
}
| TypeError::IngestedFileBadUtf8(_, _)
| TypeError::IngestedFileUnsupportedType(_, _)
| TypeError::UnexpectedModuleParams(_, _)
| TypeError::MissingModuleParams(_, _, _)
| TypeError::ModuleParamsMismatch(_, _, _, _) => {}
}
}
}
struct Env {
home_top_level_symbols: Vec<Symbol>,
imported_modules_with_params: Vec<ModuleId>,
}
impl Env {
fn is_extended(&self, symbol: &Symbol) -> bool {
self.home_top_level_symbols.contains(symbol)
|| self
.imported_modules_with_params
.contains(&symbol.module_id())
}
}
fn remove_for_reason(
env: &Env,
found_type: &mut ErrorType,
reason: &mut Reason,
expected_type: &mut ErrorType,
) {
match reason {
Reason::FnCall {
name: Some(name),
arity,
called_via: _,
} if env.is_extended(name) => {
*arity -= 1;
debug_assert_ne!(0, *arity);
drop_last_argument(found_type);
drop_last_argument(expected_type);
}
Reason::FnCall { .. } => {}
Reason::FnArg { .. } | Reason::TypedArg { .. } => {
// I believe these don't need to be touched because reporting only
// shows the type of the arguments, not the whole function.
}
// Irrelevant
Reason::LowLevelOpArg {
op: _,
arg_index: _,
}
| Reason::ForeignCallArg {
foreign_symbol: _,
arg_index: _,
}
| Reason::FloatLiteral
| Reason::IntLiteral
| Reason::NumLiteral
| Reason::StrInterpolation
| Reason::WhenBranches
| Reason::WhenBranch { index: _ }
| Reason::WhenGuard
| Reason::ExpectCondition
| Reason::IfCondition
| Reason::IfBranch {
index: _,
total_branches: _,
}
| Reason::ElemInList { index: _ }
| Reason::RecordUpdateValue(_)
| Reason::RecordUpdateKeys(_, _)
| Reason::RecordDefaultField(_)
| Reason::NumericLiteralSuffix
| Reason::InvalidAbilityMemberSpecialization {
member_name: _,
def_region: _,
unimplemented_abilities: _,
}
| Reason::GeneralizedAbilityMemberSpecialization {
member_name: _,
def_region: _,
}
| Reason::CrashArg
| Reason::ImportParams(_) => {}
}
}
fn drop_last_argument(err_type: &mut ErrorType) {
match err_type {
ErrorType::Function(arguments, _, _) => {
arguments.pop();
}
// Irrelevant
ErrorType::Infinite
| ErrorType::Type(_, _)
| ErrorType::FlexVar(_)
| ErrorType::RigidVar(_)
| ErrorType::FlexAbleVar(_, _)
| ErrorType::RigidAbleVar(_, _)
| ErrorType::Record(_, _)
| ErrorType::Tuple(_, _)
| ErrorType::TagUnion(_, _, _)
| ErrorType::RecursiveTagUnion(_, _, _, _)
| ErrorType::Alias(_, _, _, _)
| ErrorType::Range(_)
| ErrorType::Error => {}
}
}

View file

@ -14,7 +14,6 @@ roc_ident = { path = "../ident" }
roc_region = { path = "../region" }
bumpalo.workspace = true
snafu.workspace = true
static_assertions.workspace = true
[features]

View file

@ -95,6 +95,13 @@ pub enum CalledVia {
/// This call is the result of desugaring a Task.await from `!` syntax
/// e.g. Stdout.line! "Hello" becomes Task.await (Stdout.line "Hello") \{} -> ...
BangSuffix,
/// This call is the result of desugaring a Result.try from `?` syntax
/// e.g. Dict.get? items "key" becomes Result.try (Dict.get items "key") \item -> ...
QuestionSuffix,
/// This call is a result of lowering a reference to a module-params-extended def
NakedParamsVar,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
@ -114,6 +121,23 @@ impl std::fmt::Display for UnaryOp {
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum Suffix {
/// (!), e.g. (Stdin.line!)
Bang,
/// (?), e.g. (parseData? data)
Question,
}
impl std::fmt::Display for Suffix {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Suffix::Bang => write!(f, "!"),
Suffix::Question => write!(f, "?"),
}
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum BinOp {
// highest precedence

View file

@ -1,30 +1,45 @@
use snafu::{Backtrace, Snafu};
use crate::symbol::IdentId;
#[derive(Debug, Snafu)]
#[snafu(visibility(pub))]
#[derive(Debug)]
pub enum ModuleError {
#[snafu(display(
"ModuleIdNotFound: I could not find the ModuleId {} in Interns.all_ident_ids: {}.",
module_id,
all_ident_ids
))]
ModuleIdNotFound {
module_id: String,
all_ident_ids: String,
backtrace: Backtrace,
},
#[snafu(display(
"IdentIdNotFound: I could not find IdentId {:?} in ident_ids {:?}.",
ident_id,
ident_ids_str
))]
IdentIdNotFound {
ident_id: IdentId,
ident_ids_str: String,
backtrace: Backtrace,
},
}
impl std::fmt::Display for ModuleError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::ModuleIdNotFound {
module_id,
all_ident_ids,
} => {
write!(
f,
"ModuleIdNotFound: I could not find the ModuleId {} in Interns.all_ident_ids: {}.",
module_id,
all_ident_ids
)
}
Self::IdentIdNotFound {
ident_id,
ident_ids_str,
} => {
write!(
f,
"IdentIdNotFound: I could not find IdentId {:?} in ident_ids {:?}.",
ident_id, ident_ids_str
)
}
}
}
}
impl std::error::Error for ModuleError {}
pub type ModuleResult<T, E = ModuleError> = std::result::Result<T, E>;

View file

@ -1,10 +1,9 @@
use crate::ident::{Ident, Lowercase, ModuleName};
use crate::module_err::{IdentIdNotFoundSnafu, ModuleIdNotFoundSnafu, ModuleResult};
use crate::module_err::{ModuleError, ModuleResult};
use roc_collections::{SmallStringInterner, VecMap};
use roc_error_macros::internal_error;
use roc_ident::IdentStr;
use roc_region::all::Region;
use snafu::OptionExt;
use std::num::NonZeroU32;
use std::{fmt, u32};
@ -124,6 +123,10 @@ impl Symbol {
.any(|(_, (s, _))| *s == self)
}
pub fn is_generated(self, interns: &Interns) -> bool {
self.ident_ids(interns).is_generated_id(self.ident_id())
}
pub fn module_string<'a>(&self, interns: &'a Interns) -> &'a ModuleName {
interns
.module_ids
@ -138,24 +141,15 @@ impl Symbol {
}
pub fn as_str(self, interns: &Interns) -> &str {
let ident_ids = interns
.all_ident_ids
.get(&self.module_id())
self.ident_ids(interns)
.get_name(self.ident_id())
.unwrap_or_else(|| {
internal_error!(
"ident_string could not find IdentIds for module {:?} in {:?}",
self.module_id(),
interns
"ident_string's IdentIds did not contain an entry for {} in module {:?}",
self.ident_id().0,
self.module_id()
)
});
ident_ids.get_name(self.ident_id()).unwrap_or_else(|| {
internal_error!(
"ident_string's IdentIds did not contain an entry for {} in module {:?}",
self.ident_id().0,
self.module_id()
)
})
})
}
pub const fn as_u64(self) -> u64 {
@ -187,6 +181,19 @@ impl Symbol {
pub fn contains(self, needle: &str) -> bool {
format!("{self:?}").contains(needle)
}
fn ident_ids(self, interns: &Interns) -> &IdentIds {
interns
.all_ident_ids
.get(&self.module_id())
.unwrap_or_else(|| {
internal_error!(
"ident_string could not find IdentIds for module {:?} in {:?}",
self.module_id(),
interns
)
})
}
}
/// Rather than displaying as this:
@ -324,7 +331,7 @@ pub fn get_module_ident_ids<'a>(
) -> ModuleResult<&'a IdentIds> {
all_ident_ids
.get(module_id)
.with_context(|| ModuleIdNotFoundSnafu {
.ok_or_else(|| ModuleError::ModuleIdNotFound {
module_id: format!("{module_id:?}"),
all_ident_ids: format!("{all_ident_ids:?}"),
})
@ -336,9 +343,10 @@ pub fn get_module_ident_ids_mut<'a>(
) -> ModuleResult<&'a mut IdentIds> {
all_ident_ids
.get_mut(module_id)
.with_context(|| ModuleIdNotFoundSnafu {
.ok_or_else(|| ModuleError::ModuleIdNotFound {
module_id: format!("{module_id:?}"),
all_ident_ids: "I could not return all_ident_ids here because of borrowing issues.",
all_ident_ids: "I could not return all_ident_ids here because of borrowing issues."
.into(),
})
}
@ -438,13 +446,6 @@ impl fmt::Debug for ModuleId {
}
}
/// pf.Task
/// 1. build mapping from short name to package
/// 2. when adding new modules from package we need to register them in some other map (this module id goes with short name) (shortname, module-name) -> moduleId
/// 3. pass this around to other modules getting headers parsed. when parsing interfaces we need to use this map to reference shortnames
/// 4. throw away short names. stash the module id in the can env under the resolved module name
/// 5. test:
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum PackageQualified<'a, T> {
Unqualified(T),
@ -627,67 +628,12 @@ impl ModuleIds {
pub fn available_modules(&self) -> impl Iterator<Item = &ModuleName> {
self.by_id.iter()
}
}
#[derive(Debug, Clone)]
pub struct ScopeModules {
modules: VecMap<ModuleName, ModuleId>,
sources: VecMap<ModuleId, ScopeModuleSource>,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum ScopeModuleSource {
Builtin,
Current,
Import(Region),
}
impl ScopeModules {
pub fn get_id(&self, module_name: &ModuleName) -> Option<ModuleId> {
self.modules.get(module_name).copied()
}
pub fn has_id(&self, module_id: ModuleId) -> bool {
self.sources.contains_key(&module_id)
}
pub fn available_names(&self) -> impl Iterator<Item = &ModuleName> {
self.modules.keys()
}
pub fn insert(
&mut self,
module_name: ModuleName,
module_id: ModuleId,
region: Region,
) -> Result<(), ScopeModuleSource> {
if let Some(existing_module_id) = self.modules.get(&module_name) {
if *existing_module_id == module_id {
return Ok(());
}
return Err(*self.sources.get(existing_module_id).unwrap());
}
self.modules.insert(module_name, module_id);
self.sources
.insert(module_id, ScopeModuleSource::Import(region));
Ok(())
}
pub fn len(&self) -> usize {
debug_assert_eq!(self.modules.len(), self.sources.len());
self.modules.len()
}
pub fn is_empty(&self) -> bool {
debug_assert_eq!(self.modules.is_empty(), self.sources.is_empty());
self.modules.is_empty()
}
pub fn truncate(&mut self, len: usize) {
self.modules.truncate(len);
self.sources.truncate(len);
pub fn iter(&self) -> impl ExactSizeIterator<Item = (ModuleId, &ModuleName)> {
self.by_id
.iter()
.enumerate()
.map(|(index, name)| (ModuleId::from_zero_indexed(index), name))
}
}
@ -762,6 +708,12 @@ impl IdentIds {
IdentId(self.interner.insert_index_str() as u32)
}
pub fn is_generated_id(&self, id: IdentId) -> bool {
self.interner
.try_get(id.0 as usize)
.map_or(false, |str| str.starts_with(|c: char| c.is_ascii_digit()))
}
#[inline(always)]
pub fn get_id(&self, ident_name: &str) -> Option<IdentId> {
self.interner
@ -782,7 +734,7 @@ impl IdentIds {
pub fn get_name_str_res(&self, ident_id: IdentId) -> ModuleResult<&str> {
self.get_name(ident_id)
.with_context(|| IdentIdNotFoundSnafu {
.ok_or_else(|| ModuleError::IdentIdNotFound {
ident_id,
ident_ids_str: format!("{self:?}"),
})
@ -1031,32 +983,6 @@ macro_rules! define_builtins {
}
}
impl ScopeModules {
pub fn new(home_id: ModuleId, home_name: ModuleName) -> Self {
// +1 because the user will be compiling at least 1 non-builtin module!
let capacity = $total + 1;
let mut modules = VecMap::with_capacity(capacity);
let mut sources = VecMap::with_capacity(capacity);
modules.insert(home_name, home_id);
sources.insert(home_id, ScopeModuleSource::Current);
let mut insert_both = |id: ModuleId, name_str: &'static str| {
let name: ModuleName = name_str.into();
modules.insert(name, id);
sources.insert(id, ScopeModuleSource::Builtin);
};
$(
insert_both(ModuleId::$module_const, $module_name);
)+
ScopeModules { modules, sources }
}
}
impl<'a> Default for PackageModuleIds<'a> {
fn default() -> Self {
// +1 because the user will be compiling at least 1 non-builtin module!
@ -1556,6 +1482,8 @@ define_builtins! {
5 RESULT_WITH_DEFAULT: "withDefault"
6 RESULT_TRY: "try"
7 RESULT_IS_OK: "isOk"
8 RESULT_MAP_BOTH: "mapBoth"
9 RESULT_MAP_TWO: "map2"
}
8 DICT: "Dict" => {
0 DICT_DICT: "Dict" exposed_type=true // the Dict.Dict type alias
@ -1745,6 +1673,23 @@ define_builtins! {
32 INSPECT_TO_INSPECTOR: "toInspector"
33 INSPECT_TO_STR: "toStr"
}
15 TASK: "Task" => {
0 TASK_TASK: "Task" exposed_type=true // the Task.Task opaque type
1 TASK_FOREVER: "forever"
2 TASK_LOOP: "loop"
3 TASK_OK: "ok"
4 TASK_ERR: "err"
5 TASK_ATTEMPT: "attempt"
6 TASK_AWAIT: "await"
7 TASK_ON_ERR: "onErr"
8 TASK_MAP: "map"
9 TASK_MAP_ERR: "mapErr"
10 TASK_FROM_RESULT: "fromResult"
11 TASK_BATCH: "batch"
12 TASK_SEQUENCE: "sequence"
13 TASK_FOR_EACH: "forEach"
14 TASK_RESULT: "result"
}
num_modules: 15 // Keep this count up to date by hand! (TODO: see the mut_map! macro for how we could determine this count correctly in the macro)
num_modules: 16 // Keep this count up to date by hand! (TODO: see the mut_map! macro for how we could determine this count correctly in the macro)
}

View file

@ -395,8 +395,10 @@ impl<'state, 'a> State<'state, 'a> {
Some(s) => s,
None => unreachable!(
"\n\tNo borrow signature for {name:?} layout.\n\n\t\
Tip: This can happen when you call a function with less arguments than it expects.\n\t\
Like `Arg.list!` instead of `Arg.list! {{}}`"
Tip 1: This can happen when you call a function with less arguments than it expects.\n\t\
Like `Arg.list!` instead of `Arg.list! {{}}`.\n\t
Tip 2: `roc check yourfile.roc` can sometimes give you a helpful error.
"
)
};

View file

@ -2870,7 +2870,6 @@ fn pattern_to_when(
body: Loc<roc_can::expr::Expr>,
) -> (Symbol, Loc<roc_can::expr::Expr>) {
use roc_can::expr::Expr::*;
use roc_can::expr::{WhenBranch, WhenBranchPattern};
use roc_can::pattern::Pattern::{self, *};
match &pattern.value {
@ -2888,7 +2887,7 @@ fn pattern_to_when(
(*new_symbol, Loc::at_zero(RuntimeError(error)))
}
As(_, _) => todo!("as bindings are not supported yet"),
As(pattern, symbol) => pattern_to_when_help(pattern_var, pattern, body_var, body, *symbol),
UnsupportedPattern(region) => {
// create the runtime error here, instead of delegating to When.
@ -2915,28 +2914,7 @@ fn pattern_to_when(
| TupleDestructure { .. }
| UnwrappedOpaque { .. } => {
let symbol = env.unique_symbol();
let wrapped_body = When {
cond_var: pattern_var,
expr_var: body_var,
region: Region::zero(),
loc_cond: Box::new(Loc::at_zero(Var(symbol, pattern_var))),
branches: vec![WhenBranch {
patterns: vec![WhenBranchPattern {
pattern,
degenerate: false,
}],
value: body,
guard: None,
// If this type-checked, it's non-redundant
redundant: RedundantMark::known_non_redundant(),
}],
branches_cond_var: pattern_var,
// If this type-checked, it's exhaustive
exhaustive: ExhaustiveMark::known_exhaustive(),
};
(symbol, Loc::at_zero(wrapped_body))
pattern_to_when_help(pattern_var, &pattern, body_var, body, symbol)
}
Pattern::List { .. } => todo!(),
@ -2960,6 +2938,38 @@ fn pattern_to_when(
}
}
fn pattern_to_when_help(
pattern_var: Variable,
pattern: &Loc<roc_can::pattern::Pattern>,
body_var: Variable,
body: Loc<roc_can::expr::Expr>,
symbol: Symbol,
) -> (Symbol, Loc<roc_can::expr::Expr>) {
use roc_can::expr::{Expr, WhenBranch, WhenBranchPattern};
let wrapped_body = Expr::When {
cond_var: pattern_var,
expr_var: body_var,
region: Region::zero(),
loc_cond: Box::new(Loc::at_zero(Expr::Var(symbol, pattern_var))),
branches: vec![WhenBranch {
patterns: vec![WhenBranchPattern {
pattern: pattern.to_owned(),
degenerate: false,
}],
value: body,
guard: None,
// If this type-checked, it's non-redundant
redundant: RedundantMark::known_non_redundant(),
}],
branches_cond_var: pattern_var,
// If this type-checked, it's exhaustive
exhaustive: ExhaustiveMark::known_exhaustive(),
};
(symbol, Loc::at_zero(wrapped_body))
}
fn specialize_suspended<'a>(
env: &mut Env<'a, '_>,
procs: &mut Procs<'a>,
@ -4443,6 +4453,15 @@ pub fn with_hole<'a>(
specialize_naked_symbol(env, variable, procs, layout_cache, assigned, hole, symbol)
}
ParamsVar { .. } => {
internal_error!("ParamsVar should've been lowered to Var")
}
ImportParams(_, _, Some((_, value))) => {
with_hole(env, *value, variable, procs, layout_cache, assigned, hole)
}
ImportParams(_, _, None) => {
internal_error!("Missing module params should've been dropped by now");
}
AbilityMember(member, specialization_id, specialization_var) => {
let specialization_symbol = late_resolve_ability_specialization(
env,
@ -6140,7 +6159,7 @@ fn late_resolve_ability_specialization(
member,
specialization_var,
)
.expect("Ability specialization is unknown - code generation cannot proceed!");
.expect("Ability specialization is unknown. Tip: check out <https://roc.zulipchat.com/#narrow/stream/231634-beginners/topic/Non-Functions.20in.20Abilities/near/456068617>");
match specialization {
Resolved::Specialization(symbol) => symbol,

View file

@ -1027,7 +1027,7 @@ impl<'a> UnionLayout<'a> {
tags.iter()
.map(|field_layouts| LayoutRepr::struct_(field_layouts).alignment_bytes(interner))
.max()
.unwrap_or(0)
.unwrap_or(1)
}
pub fn allocation_alignment_bytes<I>(&self, interner: &I) -> u32
@ -1196,8 +1196,8 @@ impl Discriminant {
}
}
pub const fn alignment_bytes(&self) -> u32 {
self.stack_size()
pub fn alignment_bytes(&self) -> u32 {
self.stack_size().max(1)
}
pub const fn layout(&self) -> InLayout<'static> {
@ -2381,9 +2381,9 @@ impl<'a, 'b> Env<'a, 'b> {
}
}
pub const fn round_up_to_alignment(width: u32, alignment: u32) -> u32 {
pub fn round_up_to_alignment(width: u32, alignment: u32) -> u32 {
match alignment {
0 => width,
0 => panic!("Alignment invalid: Got 0, but alignment must be at least 1"),
1 => width,
_ => {
if width % alignment > 0 {
@ -2770,7 +2770,7 @@ impl<'a> LayoutRepr<'a> {
.iter()
.map(|x| interner.get_repr(*x).alignment_bytes(interner))
.max()
.unwrap_or(0),
.unwrap_or(1),
Union(variant) => {
use UnionLayout::*;

View file

@ -2,7 +2,7 @@ use bumpalo::Bump;
use criterion::{black_box, criterion_group, criterion_main, Criterion};
use roc_parse::{
ast::Defs,
module::{self, parse_module_defs},
header::{self, parse_module_defs},
state::State,
};
use std::path::PathBuf;
@ -20,7 +20,7 @@ pub fn parse_benchmark(c: &mut Criterion) {
let arena = Bump::new();
let (_actual, state) =
module::parse_header(&arena, State::new(src.as_bytes())).unwrap();
header::parse_header(&arena, State::new(src.as_bytes())).unwrap();
let res = parse_module_defs(&arena, state, Defs::default()).unwrap();
@ -41,7 +41,7 @@ pub fn parse_benchmark(c: &mut Criterion) {
let arena = Bump::new();
let (_actual, state) =
module::parse_header(&arena, State::new(src.as_bytes())).unwrap();
header::parse_header(&arena, State::new(src.as_bytes())).unwrap();
let res = parse_module_defs(&arena, state, Defs::default()).unwrap();

View file

@ -14,6 +14,12 @@ use roc_module::called_via::{BinOp, CalledVia, UnaryOp};
use roc_module::ident::QualifiedModuleName;
use roc_region::all::{Loc, Position, Region};
#[derive(Debug, Clone)]
pub struct FullAst<'a> {
pub header: SpacesBefore<'a, Header<'a>>,
pub defs: Defs<'a>,
}
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub struct Spaces<'a, T> {
pub before: &'a [CommentOrNewline<'a>],
@ -111,15 +117,9 @@ impl<'a, T: ExtractSpaces<'a>> ExtractSpaces<'a> for Loc<T> {
}
}
#[derive(Clone, Debug, PartialEq)]
pub struct Module<'a> {
pub comments: &'a [CommentOrNewline<'a>],
pub header: Header<'a>,
}
impl<'a> Module<'a> {
impl<'a> Header<'a> {
pub fn upgrade_header_imports(self, arena: &'a Bump) -> (Self, Defs<'a>) {
let (header, defs) = match self.header {
let (header, defs) = match self {
Header::Module(header) => (
Header::Module(ModuleHeader {
interface_imports: None,
@ -134,12 +134,10 @@ impl<'a> Module<'a> {
}),
Self::header_imports_to_defs(arena, header.old_imports),
),
Header::Package(_) | Header::Platform(_) | Header::Hosted(_) => {
(self.header, Defs::default())
}
Header::Package(_) | Header::Platform(_) | Header::Hosted(_) => (self, Defs::default()),
};
(Module { header, ..self }, defs)
(header, defs)
}
pub fn header_imports_to_defs(
@ -394,6 +392,15 @@ pub enum StrLiteral<'a> {
Block(&'a [&'a [StrSegment<'a>]]),
}
/// Values that can be tried, extracting success values or "returning early" on failure
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum TryTarget {
/// Tasks suffixed with ! are `Task.await`ed
Task,
/// Results suffixed with ? are `Result.try`ed
Result,
}
/// A parsed expression. This uses lifetimes extensively for two reasons:
///
/// 1. It uses Bump::alloc for all allocations, which returns a reference.
@ -425,11 +432,17 @@ pub enum Expr<'a> {
/// e.g. `.foo` or `.0`
AccessorFunction(Accessor<'a>),
/// Update the value of a field in a record, e.g. `&foo`
RecordUpdater(&'a str),
/// Look up exactly one field on a tuple, e.g. `(x, y).1`.
TupleAccess(&'a Expr<'a>, &'a str),
/// Task await bang - i.e. the ! in `File.readUtf8! path`
TaskAwaitBang(&'a Expr<'a>),
/// Early return on failures - e.g. the ! in `File.readUtf8! path`
TrySuffix {
target: TryTarget,
expr: &'a Expr<'a>,
},
// Collection Literals
List(Collection<'a, &'a Loc<Expr<'a>>>),
@ -484,7 +497,10 @@ pub enum Expr<'a> {
Backpassing(&'a [Loc<Pattern<'a>>], &'a Loc<Expr<'a>>, &'a Loc<Expr<'a>>),
Expect(&'a Loc<Expr<'a>>, &'a Loc<Expr<'a>>),
Dbg(&'a Loc<Expr<'a>>, &'a Loc<Expr<'a>>),
Dbg,
DbgStmt(&'a Loc<Expr<'a>>, &'a Loc<Expr<'a>>),
// This form of debug is a desugared call to roc_dbg
LowLevelDbg(&'a (&'a str, &'a str), &'a Loc<Expr<'a>>, &'a Loc<Expr<'a>>),
@ -496,7 +512,11 @@ pub enum Expr<'a> {
UnaryOp(&'a Loc<Expr<'a>>, Loc<UnaryOp>),
// Conditionals
If(&'a [(Loc<Expr<'a>>, Loc<Expr<'a>>)], &'a Loc<Expr<'a>>),
If {
if_thens: &'a [(Loc<Expr<'a>>, Loc<Expr<'a>>)],
final_else: &'a Loc<Expr<'a>>,
indented_else: bool,
},
When(
/// The condition
&'a Loc<Expr<'a>>,
@ -557,9 +577,9 @@ pub fn split_loc_exprs_around<'a>(
/// Checks if the bang suffix is applied only at the top level of expression
pub fn is_top_level_suffixed(expr: &Expr) -> bool {
// TODO: should we check BinOps with pizza where the last expression is TaskAwaitBang?
// TODO: should we check BinOps with pizza where the last expression is TrySuffix?
match expr {
Expr::TaskAwaitBang(..) => true,
Expr::TrySuffix { .. } => true,
Expr::Apply(a, _, _) => is_top_level_suffixed(&a.value),
Expr::SpaceBefore(a, _) => is_top_level_suffixed(a),
Expr::SpaceAfter(a, _) => is_top_level_suffixed(a),
@ -573,7 +593,7 @@ pub fn is_expr_suffixed(expr: &Expr) -> bool {
// expression without arguments, `read!`
Expr::Var { .. } => false,
Expr::TaskAwaitBang(..) => true,
Expr::TrySuffix { .. } => true,
// expression with arguments, `line! "Foo"`
Expr::Apply(sub_loc_expr, apply_args, _) => {
@ -592,7 +612,11 @@ pub fn is_expr_suffixed(expr: &Expr) -> bool {
}
// expression in a if-then-else, `if isOk! then "ok" else doSomething!`
Expr::If(if_thens, final_else) => {
Expr::If {
if_thens,
final_else,
..
} => {
let any_if_thens_suffixed = if_thens.iter().any(|(if_then, else_expr)| {
is_expr_suffixed(&if_then.value) || is_expr_suffixed(&else_expr.value)
});
@ -626,6 +650,7 @@ pub fn is_expr_suffixed(expr: &Expr) -> bool {
Expr::SingleQuote(_) => false,
Expr::RecordAccess(a, _) => is_expr_suffixed(a),
Expr::AccessorFunction(_) => false,
Expr::RecordUpdater(_) => false,
Expr::TupleAccess(a, _) => is_expr_suffixed(a),
Expr::List(items) => items.iter().any(|x| is_expr_suffixed(&x.value)),
Expr::RecordUpdate { update, fields } => {
@ -649,9 +674,9 @@ pub fn is_expr_suffixed(expr: &Expr) -> bool {
Expr::Tag(_) => false,
Expr::OpaqueRef(_) => false,
Expr::Backpassing(_, _, _) => false, // TODO: we might want to check this?
Expr::Expect(a, b) | Expr::Dbg(a, b) => {
is_expr_suffixed(&a.value) || is_expr_suffixed(&b.value)
}
Expr::Expect(a, b) => is_expr_suffixed(&a.value) || is_expr_suffixed(&b.value),
Expr::Dbg => false,
Expr::DbgStmt(a, b) => is_expr_suffixed(&a.value) || is_expr_suffixed(&b.value),
Expr::LowLevelDbg(_, a, b) => is_expr_suffixed(&a.value) || is_expr_suffixed(&b.value),
Expr::UnaryOp(a, _) => is_expr_suffixed(&a.value),
Expr::When(cond, branches) => {
@ -681,9 +706,9 @@ fn is_when_branch_suffixed(branch: &WhenBranch<'_>) -> bool {
fn is_assigned_value_suffixed<'a>(value: &AssignedField<'a, Expr<'a>>) -> bool {
match value {
AssignedField::RequiredValue(_, _, a) | AssignedField::OptionalValue(_, _, a) => {
is_expr_suffixed(&a.value)
}
AssignedField::RequiredValue(_, _, a)
| AssignedField::OptionalValue(_, _, a)
| AssignedField::IgnoredValue(_, _, a) => is_expr_suffixed(&a.value),
AssignedField::LabelOnly(_) => false,
AssignedField::SpaceBefore(a, _) | AssignedField::SpaceAfter(a, _) => {
is_assigned_value_suffixed(a)
@ -869,9 +894,9 @@ impl<'a, 'b> RecursiveValueDefIter<'a, 'b> {
use AssignedField::*;
match current {
RequiredValue(_, _, loc_val) | OptionalValue(_, _, loc_val) => {
break expr_stack.push(&loc_val.value)
}
RequiredValue(_, _, loc_val)
| OptionalValue(_, _, loc_val)
| IgnoredValue(_, _, loc_val) => break expr_stack.push(&loc_val.value),
SpaceBefore(next, _) | SpaceAfter(next, _) => current = *next,
LabelOnly(_) | Malformed(_) => break,
}
@ -944,9 +969,17 @@ impl<'a, 'b> RecursiveValueDefIter<'a, 'b> {
expr_stack.push(&a.value);
expr_stack.push(&b.value);
}
Expect(condition, cont)
| Dbg(condition, cont)
| LowLevelDbg(_, condition, cont) => {
Expect(condition, cont) => {
expr_stack.reserve(2);
expr_stack.push(&condition.value);
expr_stack.push(&cont.value);
}
DbgStmt(condition, cont) => {
expr_stack.reserve(2);
expr_stack.push(&condition.value);
expr_stack.push(&cont.value);
}
LowLevelDbg(_, condition, cont) => {
expr_stack.reserve(2);
expr_stack.push(&condition.value);
expr_stack.push(&cont.value);
@ -968,14 +1001,18 @@ impl<'a, 'b> RecursiveValueDefIter<'a, 'b> {
expr_stack.push(&expr.value);
}
UnaryOp(expr, _) => expr_stack.push(&expr.value),
If(ifs, alternate) => {
expr_stack.reserve(ifs.len() * 2 + 1);
If {
if_thens,
final_else,
..
} => {
expr_stack.reserve(if_thens.len() * 2 + 1);
for (condition, consequent) in ifs.iter() {
for (condition, consequent) in if_thens.iter() {
expr_stack.push(&condition.value);
expr_stack.push(&consequent.value);
}
expr_stack.push(&alternate.value);
expr_stack.push(&final_else.value);
}
When(condition, branches) => {
expr_stack.reserve(branches.len() + 1);
@ -997,7 +1034,7 @@ impl<'a, 'b> RecursiveValueDefIter<'a, 'b> {
}
RecordAccess(expr, _)
| TupleAccess(expr, _)
| TaskAwaitBang(expr)
| TrySuffix { expr, .. }
| SpaceBefore(expr, _)
| SpaceAfter(expr, _)
| ParensAround(expr) => expr_stack.push(expr),
@ -1014,9 +1051,11 @@ impl<'a, 'b> RecursiveValueDefIter<'a, 'b> {
| Str(_)
| SingleQuote(_)
| AccessorFunction(_)
| RecordUpdater(_)
| Var { .. }
| Underscore(_)
| Crash
| Dbg
| Tag(_)
| OpaqueRef(_)
| MalformedIdent(_, _)
@ -1070,7 +1109,7 @@ impl<'a, 'b> Iterator for RecursiveValueDefIter<'a, 'b> {
params,
}) => {
if let Some(ModuleImportParams { before: _, params }) = params {
for loc_assigned_field in params.items {
for loc_assigned_field in params.value.items {
if let Some(expr) = loc_assigned_field.value.value() {
self.push_pending_from_expr(&expr.value);
}
@ -1118,7 +1157,7 @@ pub struct ModuleImport<'a> {
#[derive(Debug, Clone, Copy, PartialEq)]
pub struct ModuleImportParams<'a> {
pub before: &'a [CommentOrNewline<'a>],
pub params: Collection<'a, Loc<AssignedField<'a, Expr<'a>>>>,
pub params: Loc<Collection<'a, Loc<AssignedField<'a, Expr<'a>>>>>,
}
#[derive(Debug, Clone, Copy, PartialEq)]
@ -1598,6 +1637,9 @@ pub enum AssignedField<'a, Val> {
// and in destructuring patterns (e.g. `{ name ? "blah" }`)
OptionalValue(Loc<&'a str>, &'a [CommentOrNewline<'a>], &'a Loc<Val>),
// An ignored field, e.g. `{ _name: "blah" }` or `{ _ : Str }`
IgnoredValue(Loc<&'a str>, &'a [CommentOrNewline<'a>], &'a Loc<Val>),
// A label with no value, e.g. `{ name }` (this is sugar for { name: name })
LabelOnly(Loc<&'a str>),
@ -1615,7 +1657,9 @@ impl<'a, Val> AssignedField<'a, Val> {
loop {
match current {
Self::RequiredValue(_, _, val) | Self::OptionalValue(_, _, val) => break Some(val),
Self::RequiredValue(_, _, val)
| Self::OptionalValue(_, _, val)
| Self::IgnoredValue(_, _, val) => break Some(val),
Self::LabelOnly(_) | Self::Malformed(_) => break None,
Self::SpaceBefore(next, _) | Self::SpaceAfter(next, _) => current = *next,
}
@ -2433,9 +2477,9 @@ pub trait Malformed {
fn is_malformed(&self) -> bool;
}
impl<'a> Malformed for Module<'a> {
impl<'a> Malformed for FullAst<'a> {
fn is_malformed(&self) -> bool {
self.header.is_malformed()
self.header.item.is_malformed() || self.defs.is_malformed()
}
}
@ -2457,6 +2501,12 @@ impl<'a, T: Malformed> Malformed for Spaces<'a, T> {
}
}
impl<'a, T: Malformed> Malformed for SpacesBefore<'a, T> {
fn is_malformed(&self) -> bool {
self.item.is_malformed()
}
}
impl<'a> Malformed for Expr<'a> {
fn is_malformed(&self) -> bool {
use Expr::*;
@ -2466,6 +2516,7 @@ impl<'a> Malformed for Expr<'a> {
Num(_) |
NonBase10Int { .. } |
AccessorFunction(_) |
RecordUpdater(_) |
Var { .. } |
Underscore(_) |
Tag(_) |
@ -2477,7 +2528,7 @@ impl<'a> Malformed for Expr<'a> {
RecordAccess(inner, _) |
TupleAccess(inner, _) |
TaskAwaitBang(inner) => inner.is_malformed(),
TrySuffix { expr: inner, .. } => inner.is_malformed(),
List(items) => items.is_malformed(),
@ -2491,13 +2542,14 @@ impl<'a> Malformed for Expr<'a> {
Closure(args, body) => args.iter().any(|arg| arg.is_malformed()) || body.is_malformed(),
Defs(defs, body) => defs.is_malformed() || body.is_malformed(),
Backpassing(args, call, body) => args.iter().any(|arg| arg.is_malformed()) || call.is_malformed() || body.is_malformed(),
Expect(condition, continuation) |
Dbg(condition, continuation) => condition.is_malformed() || continuation.is_malformed(),
Expect(condition, continuation) => condition.is_malformed() || continuation.is_malformed(),
Dbg => false,
DbgStmt(condition, continuation) => condition.is_malformed() || continuation.is_malformed(),
LowLevelDbg(_, condition, continuation) => condition.is_malformed() || continuation.is_malformed(),
Apply(func, args, _) => func.is_malformed() || args.iter().any(|arg| arg.is_malformed()),
BinOps(firsts, last) => firsts.iter().any(|(expr, _)| expr.is_malformed()) || last.is_malformed(),
UnaryOp(expr, _) => expr.is_malformed(),
If(chain, els) => chain.iter().any(|(cond, body)| cond.is_malformed() || body.is_malformed()) || els.is_malformed(),
If { if_thens, final_else, ..} => if_thens.iter().any(|(cond, body)| cond.is_malformed() || body.is_malformed()) || final_else.is_malformed(),
When(cond, branches) => cond.is_malformed() || branches.iter().any(|branch| branch.is_malformed()),
SpaceBefore(expr, _) |
@ -2577,9 +2629,9 @@ impl<T: Malformed> Malformed for Option<T> {
impl<'a, T: Malformed> Malformed for AssignedField<'a, T> {
fn is_malformed(&self) -> bool {
match self {
AssignedField::RequiredValue(_, _, val) | AssignedField::OptionalValue(_, _, val) => {
val.is_malformed()
}
AssignedField::RequiredValue(_, _, val)
| AssignedField::OptionalValue(_, _, val)
| AssignedField::IgnoredValue(_, _, val) => val.is_malformed(),
AssignedField::LabelOnly(_) => false,
AssignedField::SpaceBefore(field, _) | AssignedField::SpaceAfter(field, _) => {
field.is_malformed()

View file

@ -3,16 +3,16 @@ use crate::ast::{
Implements, ImplementsAbilities, ImportAlias, ImportAsKeyword, ImportExposingKeyword,
ImportedModuleName, IngestedFileAnnotation, IngestedFileImport, ModuleImport,
ModuleImportParams, OldRecordBuilderField, Pattern, Spaceable, Spaced, Spaces, SpacesBefore,
TypeAnnotation, TypeDef, TypeHeader, ValueDef,
TryTarget, TypeAnnotation, TypeDef, TypeHeader, ValueDef,
};
use crate::blankspace::{
loc_space0_e, require_newline_or_eof, space0_after_e, space0_around_ee, space0_before_e,
space0_before_optional_after, space0_e, spaces, spaces_around, spaces_before,
};
use crate::header::module_name_help;
use crate::ident::{
integer_ident, lowercase_ident, parse_ident, unqualified_ident, Accessor, Ident, Suffix,
};
use crate::module::module_name_help;
use crate::parser::{
self, and, backtrackable, between, byte, byte_indent, collection_inner,
collection_trailing_sep_e, either, increment_min_indent, indented_seq_skip_first, loc, map,
@ -24,8 +24,8 @@ use crate::parser::{
use crate::pattern::closure_param;
use crate::state::State;
use crate::string_literal::{self, StrLikeLiteral};
use crate::type_annotation;
use crate::{header, keyword};
use crate::{module, type_annotation};
use bumpalo::collections::Vec;
use bumpalo::Bump;
use roc_collections::soa::Slice;
@ -169,7 +169,12 @@ fn record_field_access_chain<'a>() -> impl Parser<'a, Vec<'a, Suffix<'a>>, EExpr
)
)
),
map(byte(b'!', EExpr::Access), |_| Suffix::TaskAwaitBang),
map(byte(b'!', EExpr::Access), |_| Suffix::TrySuffix(
TryTarget::Task
)),
map(byte(b'?', EExpr::Access), |_| Suffix::TrySuffix(
TryTarget::Result
)),
))
}
@ -189,6 +194,7 @@ fn loc_term_or_underscore_or_conditional<'a>(
)),
loc(specialize_err(EExpr::Closure, closure_help(options))),
loc(crash_kw()),
loc(specialize_err(EExpr::Dbg, dbg_kw())),
loc(underscore_expression()),
loc(record_literal_help()),
loc(specialize_err(EExpr::List, list_literal_help())),
@ -210,6 +216,7 @@ fn loc_term_or_underscore<'a>(
positive_number_literal_help()
)),
loc(specialize_err(EExpr::Closure, closure_help(options))),
loc(specialize_err(EExpr::Dbg, dbg_kw())),
loc(underscore_expression()),
loc(record_literal_help()),
loc(specialize_err(EExpr::List, list_literal_help())),
@ -227,6 +234,7 @@ fn loc_term<'a>(options: ExprParseOptions) -> impl Parser<'a, Loc<Expr<'a>>, EEx
positive_number_literal_help()
)),
loc(specialize_err(EExpr::Closure, closure_help(options))),
loc(specialize_err(EExpr::Dbg, dbg_kw())),
loc(record_literal_help()),
loc(specialize_err(EExpr::List, list_literal_help())),
ident_seq(),
@ -536,7 +544,7 @@ fn stmt_start<'a>(
)),
loc(specialize_err(
EExpr::Dbg,
dbg_help(options, preceding_comment)
dbg_stmt_help(options, preceding_comment)
)),
loc(specialize_err(EExpr::Import, map(import(), Stmt::ValueDef))),
map(
@ -892,10 +900,10 @@ fn import_params<'a>() -> impl Parser<'a, ModuleImportParams<'a>, EImportParams<
then(
and(
backtrackable(space0_e(EImportParams::Indent)),
specialize_err(EImportParams::Record, record_help()),
specialize_err(EImportParams::Record, loc(record_help())),
),
|arena, state, _, (before, record): (_, RecordHelp<'a>)| {
if let Some(prefix) = record.prefix {
|arena, state, _, (before, loc_record): (_, Loc<RecordHelp<'a>>)| {
if let Some(prefix) = loc_record.value.prefix {
match prefix {
(update, RecordHelpPrefix::Update) => {
return Err((
@ -912,17 +920,27 @@ fn import_params<'a>() -> impl Parser<'a, ModuleImportParams<'a>, EImportParams<
}
}
let params = record.fields.map_items_result(arena, |loc_field| {
match loc_field.value.to_assigned_field(arena) {
Ok(field) => Ok(Loc::at(loc_field.region, field)),
Err(FoundApplyValue) => Err((
MadeProgress,
EImportParams::RecordApplyFound(loc_field.region),
)),
}
})?;
let params = loc_record
.value
.fields
.map_items_result(arena, |loc_field| {
match loc_field.value.to_assigned_field(arena) {
Ok(AssignedField::IgnoredValue(_, _, _)) => Err((
MadeProgress,
EImportParams::RecordIgnoredFieldFound(loc_field.region),
)),
Ok(field) => Ok(Loc::at(loc_field.region, field)),
Err(FoundApplyValue) => Err((
MadeProgress,
EImportParams::RecordApplyFound(loc_field.region),
)),
}
})?;
let import_params = ModuleImportParams { before, params };
let import_params = ModuleImportParams {
before,
params: Loc::at(loc_record.region, params),
};
Ok((MadeProgress, import_params, state))
},
@ -944,7 +962,7 @@ fn imported_module_name<'a>() -> impl Parser<'a, ImportedModuleName<'a>, EImport
fn import_as<'a>(
) -> impl Parser<'a, header::KeywordItem<'a, ImportAsKeyword, Loc<ImportAlias<'a>>>, EImport<'a>> {
record!(header::KeywordItem {
keyword: module::spaces_around_keyword(
keyword: header::spaces_around_keyword(
ImportAsKeyword,
EImport::As,
EImport::IndentAs,
@ -978,7 +996,7 @@ fn import_exposing<'a>() -> impl Parser<
EImport<'a>,
> {
record!(header::KeywordItem {
keyword: module::spaces_around_keyword(
keyword: header::spaces_around_keyword(
ImportExposingKeyword,
EImport::Exposing,
EImport::IndentExposing,
@ -1023,7 +1041,7 @@ fn import_ingested_file_body<'a>() -> impl Parser<'a, ValueDef<'a>, EImport<'a>>
fn import_ingested_file_as<'a>(
) -> impl Parser<'a, header::KeywordItem<'a, ImportAsKeyword, Loc<&'a str>>, EImport<'a>> {
record!(header::KeywordItem {
keyword: module::spaces_around_keyword(
keyword: header::spaces_around_keyword(
ImportAsKeyword,
EImport::As,
EImport::IndentAs,
@ -2152,10 +2170,11 @@ fn expr_to_pattern_help<'a>(arena: &'a Bump, expr: &Expr<'a>) -> Result<Pattern<
| Expr::Backpassing(_, _, _)
| Expr::BinOps { .. }
| Expr::Defs(_, _)
| Expr::If(_, _)
| Expr::If { .. }
| Expr::When(_, _)
| Expr::Expect(_, _)
| Expr::Dbg(_, _)
| Expr::Dbg
| Expr::DbgStmt(_, _)
| Expr::LowLevelDbg(_, _, _)
| Expr::MalformedClosure
| Expr::MalformedSuffixed(..)
@ -2166,8 +2185,9 @@ fn expr_to_pattern_help<'a>(arena: &'a Bump, expr: &Expr<'a>) -> Result<Pattern<
| Expr::SingleFieldRecordBuilder(_)
| Expr::OptionalFieldInRecordBuilder(_, _)
| Expr::RecordUpdate { .. }
| Expr::RecordUpdater(_)
| Expr::UnaryOp(_, _)
| Expr::TaskAwaitBang(..)
| Expr::TrySuffix { .. }
| Expr::Crash
| Expr::OldRecordBuilder(..)
| Expr::RecordBuilder { .. } => return Err(()),
@ -2234,6 +2254,7 @@ fn assigned_expr_field_to_pattern_help<'a>(
spaces,
),
AssignedField::Malformed(string) => Pattern::Malformed(string),
AssignedField::IgnoredValue(_, _, _) => return Err(()),
})
}
@ -2630,7 +2651,7 @@ fn expect_help<'a>(
}
}
fn dbg_help<'a>(
fn dbg_stmt_help<'a>(
options: ExprParseOptions,
preceding_comment: Region,
) -> impl Parser<'a, Stmt<'a>, EExpect<'a>> {
@ -2655,7 +2676,17 @@ fn dbg_help<'a>(
Ok((MadeProgress, stmt, state))
})
.trace("dbg_help")
.trace("dbg_stmt_help")
}
fn dbg_kw<'a>() -> impl Parser<'a, Expr<'a>, EExpect<'a>> {
(move |arena: &'a Bump, state: State<'a>, min_indent: u32| {
let (_, _, next_state) =
parser::keyword(keyword::DBG, EExpect::Dbg).parse(arena, state, min_indent)?;
Ok((MadeProgress, Expr::Dbg, next_state))
})
.trace("dbg_kw")
}
fn import<'a>() -> impl Parser<'a, ValueDef<'a>, EImport<'a>> {
@ -2673,6 +2704,8 @@ fn if_expr_help<'a>(options: ExprParseOptions) -> impl Parser<'a, Expr<'a>, EIf<
let (_, _, state) =
parser::keyword(keyword::IF, EIf::If).parse(arena, state, min_indent)?;
let if_indent = state.line_indent();
let mut branches = Vec::with_capacity_in(1, arena);
let mut loop_state = state;
@ -2699,16 +2732,37 @@ fn if_expr_help<'a>(options: ExprParseOptions) -> impl Parser<'a, Expr<'a>, EIf<
}
};
let (_, else_branch, state) = parse_block(
let else_indent = state_final_else.line_indent();
let indented_else = else_indent > if_indent;
let min_indent = if !indented_else {
else_indent + 1
} else {
if_indent
};
let (_, loc_first_space, state_final_else) =
loc_space0_e(EIf::IndentElseBranch).parse(arena, state_final_else, min_indent)?;
let allow_defs = !loc_first_space.value.is_empty();
// use parse_block_inner so we can set min_indent
let (_, else_branch, state) = parse_block_inner(
options,
arena,
state_final_else,
true,
min_indent,
EIf::IndentElseBranch,
EIf::ElseBranch,
loc_first_space,
allow_defs,
)?;
let expr = Expr::If(branches.into_bump_slice(), arena.alloc(else_branch));
let expr = Expr::If {
if_thens: branches.into_bump_slice(),
final_else: arena.alloc(else_branch),
indented_else,
};
Ok((MadeProgress, expr, state))
}
@ -2969,14 +3023,17 @@ fn stmts_to_expr<'a>(
arena.alloc(e).before(space)
}
}
Stmt::ValueDef(ValueDef::Dbg { .. }) => {
return Err(EExpr::Dbg(
EExpect::Continuation(
arena.alloc(EExpr::IndentEnd(loc_stmt.region.end())),
loc_stmt.region.end(),
),
loc_stmt.region.start(),
));
Stmt::ValueDef(ValueDef::Dbg { condition, .. }) => {
// If we parse a `dbg` as the last thing in a series of statements then it's
// actually an expression.
Expr::Apply(
arena.alloc(Loc {
value: Expr::Dbg,
region: loc_stmt.region,
}),
arena.alloc([condition]),
CalledVia::Space,
)
}
Stmt::ValueDef(ValueDef::Expect { .. }) => {
return Err(EExpr::Expect(
@ -3128,13 +3185,19 @@ fn stmts_to_defs<'a>(
} = vd
{
if exprify_dbg {
if i + 1 >= stmts.len() {
return Err(EExpr::DbgContinue(sp_stmt.item.region.end()));
}
let rest = stmts_to_expr(&stmts[i + 1..], arena)?;
let e = Expr::Dbg(arena.alloc(condition), arena.alloc(rest));
let e = if i + 1 < stmts.len() {
let rest = stmts_to_expr(&stmts[i + 1..], arena)?;
Expr::DbgStmt(arena.alloc(condition), arena.alloc(rest))
} else {
Expr::Apply(
arena.alloc(Loc {
value: Expr::Dbg,
region: sp_stmt.item.region,
}),
arena.alloc([condition]),
CalledVia::Space,
)
};
let e = if sp_stmt.before.is_empty() {
e
@ -3235,7 +3298,7 @@ pub fn join_alias_to_body<'a>(
/// 2. The beginning of a function call (e.g. `foo bar baz`)
/// 3. The beginning of a definition (e.g. `foo =`)
/// 4. The beginning of a type annotation (e.g. `foo :`)
/// 5. A reserved keyword (e.g. `if ` or `case `), meaning we should do something else.
/// 5. A reserved keyword (e.g. `if ` or `when `), meaning we should do something else.
fn assign_or_destructure_identifier<'a>() -> impl Parser<'a, Ident<'a>, EExpr<'a>> {
parse_ident
@ -3297,6 +3360,7 @@ fn ident_to_expr<'a>(arena: &'a Bump, src: Ident<'a>) -> Expr<'a> {
answer
}
Ident::AccessorFunction(string) => Expr::AccessorFunction(string),
Ident::RecordUpdaterFunction(string) => Expr::RecordUpdater(string),
Ident::Malformed(string, problem) => Expr::MalformedIdent(string, problem),
}
}
@ -3322,6 +3386,7 @@ fn list_literal_help<'a>() -> impl Parser<'a, Expr<'a>, EList<'a>> {
pub enum RecordField<'a> {
RequiredValue(Loc<&'a str>, &'a [CommentOrNewline<'a>], &'a Loc<Expr<'a>>),
OptionalValue(Loc<&'a str>, &'a [CommentOrNewline<'a>], &'a Loc<Expr<'a>>),
IgnoredValue(Loc<&'a str>, &'a [CommentOrNewline<'a>], &'a Loc<Expr<'a>>),
LabelOnly(Loc<&'a str>),
SpaceBefore(&'a RecordField<'a>, &'a [CommentOrNewline<'a>]),
SpaceAfter(&'a RecordField<'a>, &'a [CommentOrNewline<'a>]),
@ -3337,7 +3402,10 @@ pub enum RecordField<'a> {
pub struct FoundApplyValue;
#[derive(Debug)]
struct FoundOptionalValue;
pub enum NotOldBuilderFieldValue {
FoundOptionalValue,
FoundIgnoredValue,
}
impl<'a> RecordField<'a> {
fn is_apply_value(&self) -> bool {
@ -3354,6 +3422,20 @@ impl<'a> RecordField<'a> {
}
}
fn is_ignored_value(&self) -> bool {
let mut current = self;
loop {
match current {
RecordField::IgnoredValue(_, _, _) => break true,
RecordField::SpaceBefore(field, _) | RecordField::SpaceAfter(field, _) => {
current = *field;
}
_ => break false,
}
}
}
pub fn to_assigned_field(
self,
arena: &'a Bump,
@ -3369,6 +3451,10 @@ impl<'a> RecordField<'a> {
Ok(OptionalValue(loc_label, spaces, loc_expr))
}
RecordField::IgnoredValue(loc_label, spaces, loc_expr) => {
Ok(IgnoredValue(loc_label, spaces, loc_expr))
}
RecordField::LabelOnly(loc_label) => Ok(LabelOnly(loc_label)),
RecordField::ApplyValue(_, _, _, _) => Err(FoundApplyValue),
@ -3390,7 +3476,7 @@ impl<'a> RecordField<'a> {
fn to_builder_field(
self,
arena: &'a Bump,
) -> Result<OldRecordBuilderField<'a>, FoundOptionalValue> {
) -> Result<OldRecordBuilderField<'a>, NotOldBuilderFieldValue> {
use OldRecordBuilderField::*;
match self {
@ -3398,7 +3484,9 @@ impl<'a> RecordField<'a> {
Ok(Value(loc_label, spaces, loc_expr))
}
RecordField::OptionalValue(_, _, _) => Err(FoundOptionalValue),
RecordField::OptionalValue(_, _, _) => Err(NotOldBuilderFieldValue::FoundOptionalValue),
RecordField::IgnoredValue(_, _, _) => Err(NotOldBuilderFieldValue::FoundIgnoredValue),
RecordField::LabelOnly(loc_label) => Ok(LabelOnly(loc_label)),
@ -3434,42 +3522,70 @@ pub fn record_field<'a>() -> impl Parser<'a, RecordField<'a>, ERecord<'a>> {
use RecordField::*;
map_with_arena(
and(
specialize_err(|_, pos| ERecord::Field(pos), loc(lowercase_ident())),
either(
and(
spaces(),
optional(either(
and(byte(b':', ERecord::Colon), record_field_expr()),
and(
byte(b'?', ERecord::QuestionMark),
specialize_err(|_, pos| ERecord::Field(pos), loc(lowercase_ident())),
and(
spaces(),
optional(either(
and(byte(b':', ERecord::Colon), record_field_expr()),
and(
byte(b'?', ERecord::QuestionMark),
spaces_before(specialize_err_ref(ERecord::Expr, loc_expr(false))),
),
)),
),
),
and(
loc(skip_first(
byte(b'_', ERecord::UnderscoreField),
optional(specialize_err(
|_, pos| ERecord::Field(pos),
lowercase_ident(),
)),
)),
and(
spaces(),
skip_first(
byte(b':', ERecord::Colon),
spaces_before(specialize_err_ref(ERecord::Expr, loc_expr(false))),
),
)),
),
),
),
|arena: &'a bumpalo::Bump, (loc_label, (spaces, opt_loc_val))| {
match opt_loc_val {
Some(Either::First((_, RecordFieldExpr::Value(loc_val)))) => {
RequiredValue(loc_label, spaces, arena.alloc(loc_val))
}
|arena: &'a bumpalo::Bump, field_data| {
match field_data {
Either::First((loc_label, (spaces, opt_loc_val))) => {
match opt_loc_val {
Some(Either::First((_, RecordFieldExpr::Value(loc_val)))) => {
RequiredValue(loc_label, spaces, arena.alloc(loc_val))
}
Some(Either::First((_, RecordFieldExpr::Apply(arrow_spaces, loc_val)))) => {
ApplyValue(loc_label, spaces, arrow_spaces, arena.alloc(loc_val))
}
Some(Either::First((_, RecordFieldExpr::Apply(arrow_spaces, loc_val)))) => {
ApplyValue(loc_label, spaces, arrow_spaces, arena.alloc(loc_val))
}
Some(Either::Second((_, loc_val))) => {
OptionalValue(loc_label, spaces, arena.alloc(loc_val))
}
Some(Either::Second((_, loc_val))) => {
OptionalValue(loc_label, spaces, arena.alloc(loc_val))
}
// If no value was provided, record it as a Var.
// Canonicalize will know what to do with a Var later.
None => {
if !spaces.is_empty() {
SpaceAfter(arena.alloc(LabelOnly(loc_label)), spaces)
} else {
LabelOnly(loc_label)
// If no value was provided, record it as a Var.
// Canonicalize will know what to do with a Var later.
None => {
if !spaces.is_empty() {
SpaceAfter(arena.alloc(LabelOnly(loc_label)), spaces)
} else {
LabelOnly(loc_label)
}
}
}
}
Either::Second((loc_opt_label, (spaces, loc_val))) => {
let loc_label = loc_opt_label
.map(|opt_label| opt_label.unwrap_or_else(|| arena.alloc_str("")));
IgnoredValue(loc_label, spaces, arena.alloc(loc_val))
}
}
},
)
@ -3573,20 +3689,23 @@ fn record_literal_help<'a>() -> impl Parser<'a, Expr<'a>, EExpr<'a>> {
new_record_builder_help(arena, mapper, record.fields)
}
None => {
let is_old_record_builder = record
.fields
.iter()
.any(|field| field.value.is_apply_value());
let special_field_found = record.fields.iter().find_map(|field| {
if field.value.is_apply_value() {
Some(old_record_builder_help(arena, record.fields))
} else if field.value.is_ignored_value() {
Some(Err(EExpr::RecordUpdateIgnoredField(field.region)))
} else {
None
}
});
if is_old_record_builder {
old_record_builder_help(arena, record.fields)
} else {
special_field_found.unwrap_or_else(|| {
let fields = record.fields.map_items(arena, |loc_field| {
loc_field.map(|field| field.to_assigned_field(arena).unwrap())
});
Ok(Expr::Record(fields))
}
})
}
};
@ -3609,11 +3728,14 @@ fn record_update_help<'a>(
) -> Result<Expr<'a>, EExpr<'a>> {
let result = fields.map_items_result(arena, |loc_field| {
match loc_field.value.to_assigned_field(arena) {
Ok(AssignedField::IgnoredValue(_, _, _)) => {
Err(EExpr::RecordUpdateIgnoredField(loc_field.region))
}
Ok(builder_field) => Ok(Loc {
region: loc_field.region,
value: builder_field,
}),
Err(FoundApplyValue) => Err(EExpr::RecordUpdateAccumulator(loc_field.region)),
Err(FoundApplyValue) => Err(EExpr::RecordUpdateOldBuilderField(loc_field.region)),
}
});
@ -3634,7 +3756,7 @@ fn new_record_builder_help<'a>(
region: loc_field.region,
value: builder_field,
}),
Err(FoundApplyValue) => Err(EExpr::RecordBuilderAccumulator(loc_field.region)),
Err(FoundApplyValue) => Err(EExpr::RecordBuilderOldBuilderField(loc_field.region)),
}
});
@ -3654,7 +3776,12 @@ fn old_record_builder_help<'a>(
region: loc_field.region,
value: builder_field,
}),
Err(FoundOptionalValue) => Err(EExpr::OptionalValueInRecordBuilder(loc_field.region)),
Err(NotOldBuilderFieldValue::FoundOptionalValue) => {
Err(EExpr::OptionalValueInOldRecordBuilder(loc_field.region))
}
Err(NotOldBuilderFieldValue::FoundIgnoredValue) => {
Err(EExpr::IgnoredValueInOldRecordBuilder(loc_field.region))
}
}
});
@ -3675,7 +3802,10 @@ fn apply_expr_access_chain<'a>(
Suffix::Accessor(Accessor::TupleIndex(field)) => {
Expr::TupleAccess(arena.alloc(value), field)
}
Suffix::TaskAwaitBang => Expr::TaskAwaitBang(arena.alloc(value)),
Suffix::TrySuffix(target) => Expr::TrySuffix {
target,
expr: arena.alloc(value),
},
})
}

File diff suppressed because it is too large Load diff

View file

@ -74,7 +74,7 @@ pub fn highlight(text: &str) -> Vec<Loc<Token>> {
let header_keywords = HEADER_KEYWORDS.iter().copied().collect::<HashSet<_>>();
let body_keywords = KEYWORDS.iter().copied().collect::<HashSet<_>>();
if let Ok((_prog, _, new_state)) = crate::module::header().parse(&arena, state.clone(), 0) {
if let Ok((_prog, _, new_state)) = crate::header::header().parse(&arena, state.clone(), 0) {
let inner_state =
State::new(text[..state.bytes().len() - new_state.bytes().len()].as_bytes());
highlight_inner(&arena, inner_state, &mut tokens, &header_keywords);
@ -365,7 +365,7 @@ fn fast_forward_to(
tokens.push(Loc::at(Region::between(start, state.pos()), Token::Error));
}
pub const HEADER_KEYWORDS: [&str; 14] = [
pub const HEADER_KEYWORDS: [&str; 12] = [
"interface",
"app",
"package",
@ -373,8 +373,6 @@ pub const HEADER_KEYWORDS: [&str; 14] = [
"hosted",
"exposes",
"imports",
"with",
"generates",
"package",
"packages",
"requires",

View file

@ -1,3 +1,4 @@
use crate::ast::TryTarget;
use crate::parser::Progress::{self, *};
use crate::parser::{BadInputError, EExpr, ParseResult, Parser};
use crate::state::State;
@ -45,42 +46,12 @@ pub enum Ident<'a> {
},
/// `.foo { foo: 42 }` or `.1 (1, 2, 3)`
AccessorFunction(Accessor<'a>),
/// `&foo { foo: 42 } 3`
RecordUpdaterFunction(&'a str),
/// .Foo or foo. or something like foo.Bar
Malformed(&'a str, BadIdent),
}
impl<'a> Ident<'a> {
pub fn len(&self) -> usize {
use self::Ident::*;
match self {
Tag(string) | OpaqueRef(string) => string.len(),
Access {
module_name, parts, ..
} => {
let mut len = if module_name.is_empty() {
0
} else {
module_name.len() + 1
// +1 for the dot
};
for part in parts.iter() {
len += part.len() + 1 // +1 for the dot
}
len - 1
}
AccessorFunction(string) => string.len(),
Malformed(string, _) => string.len(),
}
}
pub fn is_empty(&self) -> bool {
self.len() == 0
}
}
/// This could be:
///
/// * A record field, e.g. "email" in `.email` or in `email:`
@ -271,6 +242,7 @@ pub enum BadIdent {
WeirdDotAccess(Position),
WeirdDotQualified(Position),
StrayDot(Position),
StrayAmpersand(Position),
BadOpaqueRef(Position),
QualifiedTupleAccessor(Position),
}
@ -377,7 +349,7 @@ impl<'a> Accessor<'a> {
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub enum Suffix<'a> {
Accessor(Accessor<'a>),
TaskAwaitBang,
TrySuffix(TryTarget),
}
/// a `.foo` or `.1` accessor function
@ -415,6 +387,18 @@ fn chomp_accessor(buffer: &[u8], pos: Position) -> Result<Accessor, BadIdent> {
}
}
/// a `&foo` record updater function
fn chomp_record_updater(buffer: &[u8], pos: Position) -> Result<&str, BadIdent> {
// assumes the leading `&` has been chomped already
match chomp_lowercase_part(buffer) {
Ok(name) => Ok(name),
Err(_) => {
// we've already made progress with the initial `&`
Err(BadIdent::StrayAmpersand(pos.bump_column(1)))
}
}
}
/// a `@Token` opaque
fn chomp_opaque_ref(buffer: &[u8], pos: Position) -> Result<&str, BadIdent> {
// assumes the leading `@` has NOT been chomped already
@ -457,6 +441,14 @@ fn chomp_identifier_chain<'a>(
}
Err(fail) => return Err((1, fail)),
},
'&' => match chomp_record_updater(&buffer[1..], pos) {
Ok(updater) => {
let bytes_parsed = 1 + updater.len();
return Ok((bytes_parsed as u32, Ident::RecordUpdaterFunction(updater)));
}
// return 0 bytes consumed on failure to allow parsing &&
Err(fail) => return Err((0, fail)),
},
'@' => match chomp_opaque_ref(buffer, pos) {
Ok(tagname) => {
let bytes_parsed = tagname.len();

View file

@ -13,11 +13,10 @@ pub mod header;
pub mod highlight;
pub mod ident;
pub mod keyword;
pub mod module;
pub mod normalize;
pub mod number_literal;
pub mod pattern;
pub mod problems;
pub mod remove_spaces;
pub mod src64;
pub mod state;
pub mod string_literal;

View file

@ -2,19 +2,19 @@ use crate::ast::{Collection, CommentOrNewline, Defs, Header, Module, Spaced, Spa
use crate::blankspace::{space0_around_ee, space0_before_e, space0_e};
use crate::expr::merge_spaces;
use crate::header::{
package_entry, package_name, AppHeader, ExposedName, ExposesKeyword, GeneratesKeyword,
HostedHeader, ImportsCollection, ImportsEntry, ImportsKeyword, ImportsKeywordItem, Keyword,
KeywordItem, ModuleHeader, ModuleName, ModuleParams, PackageEntry, PackageHeader,
PackagesKeyword, PlatformHeader, PlatformRequires, ProvidesKeyword, ProvidesTo,
RequiresKeyword, To, ToKeyword, TypedIdent, WithKeyword,
package_entry, package_name, AppHeader, ExposedName, ExposesKeyword, HostedHeader,
ImportsCollection, ImportsEntry, ImportsKeyword, ImportsKeywordItem, Keyword, KeywordItem,
ModuleHeader, ModuleName, ModuleParams, PackageEntry, PackageHeader, PackagesKeyword,
PlatformHeader, PlatformRequires, ProvidesKeyword, ProvidesTo, RequiresKeyword, To, ToKeyword,
TypedIdent,
};
use crate::ident::{self, lowercase_ident, unqualified_ident, uppercase, UppercaseIdent};
use crate::ident::{self, lowercase_ident, unqualified_ident, UppercaseIdent};
use crate::parser::Progress::{self, *};
use crate::parser::{
and, backtrackable, byte, collection_trailing_sep_e, increment_min_indent, loc, map,
map_with_arena, optional, reset_min_indent, skip_first, skip_second, specialize_err, succeed,
two_bytes, zero_or_more, EExposes, EGenerates, EGeneratesWith, EHeader, EImports, EPackages,
EParams, EProvides, ERequires, ETypedIdent, Parser, SourceError, SpaceProblem, SyntaxError,
two_bytes, zero_or_more, EExposes, EHeader, EImports, EPackages, EParams, EProvides, ERequires,
ETypedIdent, Parser, SourceError, SpaceProblem, SyntaxError,
};
use crate::pattern::record_pattern_fields;
use crate::state::State;
@ -188,8 +188,6 @@ fn hosted_header<'a>() -> impl Parser<'a, HostedHeader<'a>, EHeader<'a>> {
name: loc(module_name_help(EHeader::ModuleName)),
exposes: specialize_err(EHeader::Exposes, exposes_values_kw()),
imports: specialize_err(EHeader::Imports, imports()),
generates: specialize_err(EHeader::Generates, generates()),
generates_with: specialize_err(EHeader::GeneratesWith, generates_with()),
})
.trace("hosted_header")
}
@ -761,44 +759,6 @@ fn packages_collection<'a>(
)
}
#[inline(always)]
fn generates<'a>(
) -> impl Parser<'a, KeywordItem<'a, GeneratesKeyword, UppercaseIdent<'a>>, EGenerates> {
record!(KeywordItem {
keyword: spaces_around_keyword(
GeneratesKeyword,
EGenerates::Generates,
EGenerates::IndentGenerates,
EGenerates::IndentTypeStart
),
item: specialize_err(|(), pos| EGenerates::Identifier(pos), uppercase())
})
}
#[inline(always)]
fn generates_with<'a>() -> impl Parser<
'a,
KeywordItem<'a, WithKeyword, Collection<'a, Loc<Spaced<'a, ExposedName<'a>>>>>,
EGeneratesWith,
> {
record!(KeywordItem {
keyword: spaces_around_keyword(
WithKeyword,
EGeneratesWith::With,
EGeneratesWith::IndentWith,
EGeneratesWith::IndentListStart
),
item: collection_trailing_sep_e(
byte(b'[', EGeneratesWith::ListStart),
exposes_entry(EGeneratesWith::Identifier),
byte(b',', EGeneratesWith::ListEnd),
byte(b']', EGeneratesWith::ListEnd),
Spaced::SpaceBefore
)
})
}
#[inline(always)]
fn imports<'a>() -> impl Parser<
'a,
KeywordItem<'a, ImportsKeyword, Collection<'a, Loc<Spaced<'a, ImportsEntry<'a>>>>>,
@ -826,7 +786,7 @@ fn imports<'a>() -> impl Parser<
pub fn typed_ident<'a>() -> impl Parser<'a, Spaced<'a, TypedIdent<'a>>, ETypedIdent<'a>> {
// e.g.
//
// printLine : Str -> Effect {}
// printLine : Str -> Task {} *
map(
and(
and(

View file

@ -83,8 +83,6 @@ impl_space_problem! {
EExpect<'a>,
EExposes,
EExpr<'a>,
EGenerates,
EGeneratesWith,
EHeader<'a>,
EIf<'a>,
EImport<'a>,
@ -122,8 +120,6 @@ pub enum EHeader<'a> {
Imports(EImports, Position),
Requires(ERequires<'a>, Position),
Packages(EPackages<'a>, Position),
Generates(EGenerates, Position),
GeneratesWith(EGeneratesWith, Position),
Space(BadInputError, Position),
Start(Position),
@ -251,30 +247,6 @@ pub enum EImports {
StrLiteral(Position),
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum EGenerates {
Open(Position),
Generates(Position),
IndentGenerates(Position),
Identifier(Position),
Space(BadInputError, Position),
IndentTypeStart(Position),
IndentTypeEnd(Position),
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum EGeneratesWith {
Open(Position),
With(Position),
IndentWith(Position),
IndentListStart(Position),
IndentListEnd(Position),
ListStart(Position),
ListEnd(Position),
Identifier(Position),
Space(BadInputError, Position),
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum BadInputError {
HasTab,
@ -372,9 +344,11 @@ pub enum EExpr<'a> {
InParens(EInParens<'a>, Position),
Record(ERecord<'a>, Position),
OptionalValueInRecordBuilder(Region),
RecordUpdateAccumulator(Region),
RecordBuilderAccumulator(Region),
OptionalValueInOldRecordBuilder(Region),
IgnoredValueInOldRecordBuilder(Region),
RecordUpdateOldBuilderField(Region),
RecordUpdateIgnoredField(Region),
RecordBuilderOldBuilderField(Region),
// SingleQuote errors are folded into the EString
Str(EString<'a>, Position),
@ -428,6 +402,7 @@ pub enum ERecord<'a> {
Prefix(Position),
Field(Position),
UnderscoreField(Position),
Colon(Position),
QuestionMark(Position),
Arrow(Position),
@ -577,6 +552,7 @@ pub enum EImportParams<'a> {
RecordUpdateFound(Region),
RecordBuilderFound(Region),
RecordApplyFound(Region),
RecordIgnoredFieldFound(Region),
Space(BadInputError, Position),
}
@ -601,6 +577,7 @@ pub enum EPattern<'a> {
AsIndentStart(Position),
AccessorFunction(Position),
RecordUpdaterFunction(Position),
}
#[derive(Debug, Clone, PartialEq, Eq)]
@ -735,6 +712,7 @@ pub enum ETypeAbilityImpl<'a> {
Open(Position),
Field(Position),
UnderscoreField(Position),
Colon(Position),
Arrow(Position),
Optional(Position),
@ -756,6 +734,7 @@ impl<'a> From<ERecord<'a>> for ETypeAbilityImpl<'a> {
ERecord::End(p) => ETypeAbilityImpl::End(p),
ERecord::Open(p) => ETypeAbilityImpl::Open(p),
ERecord::Field(p) => ETypeAbilityImpl::Field(p),
ERecord::UnderscoreField(p) => ETypeAbilityImpl::UnderscoreField(p),
ERecord::Colon(p) => ETypeAbilityImpl::Colon(p),
ERecord::Arrow(p) => ETypeAbilityImpl::Arrow(p),
ERecord::Space(s, p) => ETypeAbilityImpl::Space(s, p),
@ -849,8 +828,7 @@ where
}
// This should be enough for anyone. Right? RIGHT?
let indent_text =
"| ; : ! | ; : ! | ; : ! | ; : ! | ; : ! | ; : ! | ; : ! | ; : ! | ; : ! ";
let indent_text = "| ; : ! ".repeat(20);
let cur_indent = INDENT.with(|i| *i.borrow());

View file

@ -25,6 +25,7 @@ pub enum PatternType {
DefExpr,
FunctionArg,
WhenBranch,
ModuleParams,
}
pub fn closure_param<'a>() -> impl Parser<'a, Loc<Pattern<'a>>, EPattern<'a>> {
@ -435,6 +436,10 @@ fn loc_ident_pattern_help<'a>(
MadeProgress,
EPattern::AccessorFunction(loc_ident.region.start()),
)),
Ident::RecordUpdaterFunction(_string) => Err((
MadeProgress,
EPattern::RecordUpdaterFunction(loc_ident.region.start()),
)),
Ident::Malformed(malformed, problem) => {
debug_assert!(!malformed.is_empty());

View file

@ -1,6 +1,8 @@
use crate::ast;
use crate::ast::Defs;
use crate::module::parse_module_defs;
use crate::ast::Header;
use crate::ast::SpacesBefore;
use crate::header::parse_module_defs;
use crate::parser::SourceError;
use crate::parser::SyntaxError;
use crate::state::State;
@ -39,10 +41,10 @@ pub fn parse_defs_with<'a>(arena: &'a Bump, input: &'a str) -> Result<Defs<'a>,
pub fn parse_header_with<'a>(
arena: &'a Bump,
input: &'a str,
) -> Result<ast::Module<'a>, SyntaxError<'a>> {
) -> Result<SpacesBefore<'a, Header<'a>>, SyntaxError<'a>> {
let state = State::new(input.as_bytes());
match crate::module::parse_header(arena, state.clone()) {
match crate::header::parse_header(arena, state.clone()) {
Ok((header, _)) => Ok(header),
Err(fail) => Err(SyntaxError::Header(fail.problem)),
}

View file

@ -565,6 +565,9 @@ fn parse_implements_ability<'a>() -> impl Parser<'a, ImplementsAbility<'a>, ETyp
fn ability_impl_field<'a>() -> impl Parser<'a, AssignedField<'a, Expr<'a>>, ERecord<'a>> {
then(record_field(), move |arena, state, _, field| {
match field.to_assigned_field(arena) {
Ok(AssignedField::IgnoredValue(_, _, _)) => {
Err((MadeProgress, ERecord::Field(state.pos())))
}
Ok(assigned_field) => Ok((MadeProgress, assigned_field, state)),
Err(FoundApplyValue) => Err((MadeProgress, ERecord::Field(state.pos()))),
}

View file

@ -22,7 +22,7 @@ mod test_parse {
use roc_parse::ast::StrSegment::*;
use roc_parse::ast::{self, EscapedChar};
use roc_parse::ast::{CommentOrNewline, StrLiteral::*};
use roc_parse::module::parse_module_defs;
use roc_parse::header::parse_module_defs;
use roc_parse::parser::SyntaxError;
use roc_parse::state::State;
use roc_parse::test_helpers::parse_expr_with;

View file

@ -4,7 +4,7 @@ use std::path::PathBuf;
use roc_collections::all::MutSet;
use roc_module::called_via::BinOp;
use roc_module::ident::{Ident, Lowercase, ModuleName, TagName};
use roc_module::symbol::{ModuleId, ScopeModuleSource, Symbol};
use roc_module::symbol::{ModuleId, Symbol};
use roc_parse::ast::Base;
use roc_parse::pattern::PatternType;
use roc_region::all::{Loc, Region};
@ -39,7 +39,6 @@ pub enum Problem {
UnusedImport(Symbol, Region),
UnusedModuleImport(ModuleId, Region),
ExposedButNotDefined(Symbol),
UnknownGeneratesWith(Loc<Ident>),
ImportNameConflict {
name: ModuleName,
is_alias: bool,
@ -54,6 +53,7 @@ pub enum Problem {
new_symbol: Symbol,
existing_symbol_region: Region,
},
DeprecatedBackpassing(Region),
/// First symbol is the name of the closure with that argument
/// Bool is whether the closure is anonymous
/// Second symbol is the name of the argument that is unused
@ -220,6 +220,12 @@ pub enum Problem {
OverAppliedCrash {
region: Region,
},
UnappliedDbg {
region: Region,
},
OverAppliedDbg {
region: Region,
},
FileProblem {
filename: PathBuf,
error: io::ErrorKind,
@ -238,6 +244,13 @@ pub enum Problem {
},
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum ScopeModuleSource {
Builtin,
Current,
Import(Region),
}
impl Problem {
pub fn severity(&self) -> Severity {
use Severity::{Fatal, RuntimeError, Warning};
@ -250,8 +263,8 @@ impl Problem {
Problem::ExplicitBuiltinImport(_, _) => Warning,
Problem::ExplicitBuiltinTypeImport(_, _) => Warning,
Problem::ImportShadowsSymbol { .. } => RuntimeError,
Problem::DeprecatedBackpassing(_) => Warning,
Problem::ExposedButNotDefined(_) => RuntimeError,
Problem::UnknownGeneratesWith(_) => RuntimeError,
Problem::UnusedArgument(_, _, _, _) => Warning,
Problem::UnusedBranchDef(_, _) => Warning,
Problem::PrecedenceProblem(_) => RuntimeError,
@ -306,6 +319,8 @@ impl Problem {
// injecting a crash message
Problem::UnappliedCrash { .. } => RuntimeError,
Problem::OverAppliedCrash { .. } => RuntimeError,
Problem::UnappliedDbg { .. } => RuntimeError,
Problem::OverAppliedDbg { .. } => RuntimeError,
Problem::DefsOnlyUsedInRecursion(_, _) => Warning,
Problem::FileProblem { .. } => Fatal,
}
@ -333,7 +348,7 @@ impl Problem {
| Problem::ExplicitBuiltinImport(_, region)
| Problem::ExplicitBuiltinTypeImport(_, region)
| Problem::ImportShadowsSymbol { region, .. }
| Problem::UnknownGeneratesWith(Loc { region, .. })
| Problem::DeprecatedBackpassing(region)
| Problem::UnusedArgument(_, _, _, region)
| Problem::UnusedBranchDef(_, region)
| Problem::PrecedenceProblem(PrecedenceProblem::BothNonAssociative(region, _, _))
@ -470,6 +485,8 @@ impl Problem {
| Problem::UnnecessaryOutputWildcard { region }
| Problem::OverAppliedCrash { region }
| Problem::UnappliedCrash { region }
| Problem::OverAppliedDbg { region }
| Problem::UnappliedDbg { region }
| Problem::DefsOnlyUsedInRecursion(_, region) => Some(*region),
Problem::RuntimeError(RuntimeError::CircularDef(cycle_entries))
| Problem::BadRecursion(cycle_entries) => {

View file

@ -4,7 +4,7 @@ use crate::{aliases::Aliases, solve};
use roc_can::abilities::{AbilitiesStore, ResolvedImpl};
use roc_can::constraint::{Constraint, Constraints};
use roc_can::expr::PendingDerives;
use roc_can::module::{ExposedByModule, ResolvedImplementations, RigidVariables};
use roc_can::module::{ExposedByModule, ModuleParams, ResolvedImplementations, RigidVariables};
use roc_collections::all::MutMap;
use roc_collections::VecMap;
use roc_derive::SharedDerivedModule;
@ -80,6 +80,10 @@ pub struct SolveConfig<'a> {
#[cfg(debug_assertions)]
/// The checkmate collector for this module.
pub checkmate: Option<roc_checkmate::Collector>,
/// Module params
pub module_params: Option<ModuleParams>,
pub module_params_vars: VecMap<ModuleId, Variable>,
}
pub struct SolveOutput {
@ -144,6 +148,7 @@ pub fn exposed_types_storage_subs(
home: ModuleId,
solved_subs: &mut Solved<Subs>,
exposed_vars_by_symbol: &[(Symbol, Variable)],
params_var: Option<Variable>,
solved_implementations: &ResolvedImplementations,
abilities_store: &AbilitiesStore,
) -> ExposedTypesStorageSubs {
@ -156,6 +161,9 @@ pub fn exposed_types_storage_subs(
stored_vars_by_symbol.insert(*symbol, new_var);
}
let stored_params_var =
params_var.map(|params_var| storage_subs.import_variable_from(subs, params_var).variable);
let mut stored_specialization_lambda_set_vars =
VecMap::with_capacity(solved_implementations.len());
@ -213,6 +221,7 @@ pub fn exposed_types_storage_subs(
stored_vars_by_symbol,
stored_specialization_lambda_set_vars,
stored_ability_member_vars,
stored_params_var,
}
}

View file

@ -1,3 +1,4 @@
use roc_error_macros::internal_error;
use roc_types::subs::{Rank, Variable};
const DEFAULT_POOLS: usize = 8;
@ -27,14 +28,14 @@ impl Pools {
pub fn get_mut(&mut self, rank: Rank) -> &mut Vec<Variable> {
match self.0.get_mut(rank.into_usize()) {
Some(reference) => reference,
None => panic!("Compiler bug: could not find pool at rank {rank}"),
None => internal_error!("Compiler bug: could not find pool at rank {rank}"),
}
}
pub fn get(&self, rank: Rank) -> &Vec<Variable> {
match self.0.get(rank.into_usize()) {
Some(reference) => reference,
None => panic!("Compiler bug: could not find pool at rank {rank}"),
None => internal_error!("Compiler bug: could not find pool at rank {rank}"),
}
}
@ -46,7 +47,7 @@ impl Pools {
let last = self
.0
.pop()
.unwrap_or_else(|| panic!("Attempted to split_last() on non-empty Pools"));
.unwrap_or_else(|| internal_error!("Attempted to split_last() on non-empty Pools"));
(last, self.0)
}

View file

@ -16,11 +16,13 @@ use roc_can::abilities::{AbilitiesStore, MemberSpecializationInfo};
use roc_can::constraint::Constraint::{self, *};
use roc_can::constraint::{Cycle, LetConstraint, OpportunisticResolve};
use roc_can::expected::{Expected, PExpected};
use roc_can::module::ModuleParams;
use roc_collections::VecMap;
use roc_debug_flags::dbg_do;
#[cfg(debug_assertions)]
use roc_debug_flags::ROC_VERIFY_RIGID_LET_GENERALIZED;
use roc_error_macros::internal_error;
use roc_module::symbol::Symbol;
use roc_module::symbol::{ModuleId, Symbol};
use roc_problem::can::CycleEntry;
use roc_region::all::Loc;
use roc_solve_problem::TypeError;
@ -129,15 +131,13 @@ fn run_help(
exposed_by_module,
derived_module,
function_kind,
module_params,
module_params_vars,
..
} = config;
let mut pools = Pools::default();
let state = State {
scope: Scope::default(),
mark: Mark::NONE.next(),
};
let rank = Rank::toplevel();
let arena = Bump::new();
@ -178,7 +178,6 @@ fn run_help(
let state = solve(
&mut env,
types,
state,
rank,
problems,
aliases,
@ -186,6 +185,8 @@ fn run_help(
abilities_store,
&mut obligation_cache,
&mut awaiting_specializations,
module_params,
module_params_vars,
);
RunSolveOutput {
@ -236,7 +237,6 @@ enum Work<'a> {
fn solve(
env: &mut InferenceEnv,
mut can_types: Types,
mut state: State,
rank: Rank,
problems: &mut Vec<TypeError>,
aliases: &mut Aliases,
@ -244,15 +244,24 @@ fn solve(
abilities_store: &mut AbilitiesStore,
obligation_cache: &mut ObligationCache,
awaiting_specializations: &mut AwaitingSpecializations,
module_params: Option<ModuleParams>,
module_params_vars: VecMap<ModuleId, Variable>,
) -> State {
let scope = Scope::new(module_params);
let initial = Work::Constraint {
scope: &Scope::default(),
scope: &scope.clone(),
rank,
constraint,
};
let mut stack = vec![initial];
let mut state = State {
scope,
mark: Mark::NONE.next(),
};
while let Some(work_item) = stack.pop() {
let (scope, rank, constraint) = match work_item {
Work::Constraint {
@ -1392,6 +1401,86 @@ fn solve(
}
}
}
ImportParams(opt_provided, module_id, region) => {
match (module_params_vars.get(module_id), opt_provided) {
(Some(expected), Some(provided)) => {
let actual = either_type_index_to_var(
env,
rank,
problems,
abilities_store,
obligation_cache,
&mut can_types,
aliases,
*provided,
);
match unify(
&mut env.uenv(),
actual,
*expected,
UnificationMode::EQ,
Polarity::OF_VALUE,
) {
Success {
vars,
must_implement_ability,
lambda_sets_to_specialize,
extra_metadata: _,
} => {
env.introduce(rank, &vars);
problems.extend(obligation_cache.check_obligations(
env.subs,
abilities_store,
must_implement_ability,
AbilityImplError::DoesNotImplement,
));
compact_lambdas_and_check_obligations(
env,
problems,
abilities_store,
obligation_cache,
awaiting_specializations,
lambda_sets_to_specialize,
);
state
}
Failure(vars, actual_type, expected_type, _) => {
env.introduce(rank, &vars);
problems.push(TypeError::ModuleParamsMismatch(
*region,
*module_id,
actual_type,
expected_type,
));
state
}
}
}
(Some(expected), None) => {
let expected_type = env.uenv().var_to_error_type(*expected, Polarity::Neg);
problems.push(TypeError::MissingModuleParams(
*region,
*module_id,
expected_type,
));
state
}
(None, Some(_)) => {
problems.push(TypeError::UnexpectedModuleParams(*region, *module_id));
state
}
(None, None) => state,
}
}
};
}

View file

@ -1,14 +1,35 @@
use roc_can::module::ModuleParams;
use roc_module::symbol::Symbol;
use roc_types::subs::Variable;
/// The scope of the solver, as symbols are introduced.
#[derive(Clone, Debug, Default)]
#[derive(Clone, Debug)]
pub struct Scope {
symbols: Vec<Symbol>,
variables: Vec<Variable>,
}
impl Scope {
pub fn new(opt_module_params: Option<ModuleParams>) -> Self {
match opt_module_params {
Some(module_params) => {
let mut symbols = Vec::with_capacity(module_params.destructs.len());
let mut variables = Vec::with_capacity(module_params.destructs.len());
for destruct in module_params.destructs {
symbols.push(destruct.value.symbol);
variables.push(destruct.value.var);
}
Self { symbols, variables }
}
None => Self {
symbols: Vec::default(),
variables: Vec::default(),
},
}
}
pub fn vars_by_symbol(&self) -> impl Iterator<Item = (Symbol, Variable)> + '_ {
let it1 = self.symbols.iter().copied();
let it2 = self.variables.iter().copied();

View file

@ -2,7 +2,10 @@
use std::{path::PathBuf, str::Utf8Error};
use roc_can::expected::{Expected, PExpected};
use roc_module::{ident::Lowercase, symbol::Symbol};
use roc_module::{
ident::Lowercase,
symbol::{ModuleId, Symbol},
};
use roc_problem::{can::CycleEntry, Severity};
use roc_region::all::Region;
@ -33,6 +36,9 @@ pub enum TypeError {
},
IngestedFileBadUtf8(Box<PathBuf>, Utf8Error),
IngestedFileUnsupportedType(Box<PathBuf>, ErrorType),
UnexpectedModuleParams(Region, ModuleId),
MissingModuleParams(Region, ModuleId, ErrorType),
ModuleParamsMismatch(Region, ModuleId, ErrorType, ErrorType),
}
impl TypeError {
@ -52,6 +58,9 @@ impl TypeError {
TypeError::Exhaustive(exhtv) => exhtv.severity(),
TypeError::StructuralSpecialization { .. } => RuntimeError,
TypeError::WrongSpecialization { .. } => RuntimeError,
TypeError::UnexpectedModuleParams(..) => Warning,
TypeError::MissingModuleParams(..) => RuntimeError,
TypeError::ModuleParamsMismatch(..) => RuntimeError,
TypeError::IngestedFileBadUtf8(..) => Fatal,
TypeError::IngestedFileUnsupportedType(..) => Fatal,
}
@ -66,7 +75,10 @@ impl TypeError {
| TypeError::BadExprMissingAbility(region, ..)
| TypeError::StructuralSpecialization { region, .. }
| TypeError::WrongSpecialization { region, .. }
| TypeError::BadPatternMissingAbility(region, ..) => Some(*region),
| TypeError::BadPatternMissingAbility(region, ..)
| TypeError::UnexpectedModuleParams(region, ..)
| TypeError::MissingModuleParams(region, ..)
| TypeError::ModuleParamsMismatch(region, ..) => Some(*region),
TypeError::UnfulfilledAbility(ab, ..) => ab.region(),
TypeError::Exhaustive(e) => Some(e.region()),
TypeError::CircularDef(c) => c.first().map(|ce| ce.symbol_region),

View file

@ -405,7 +405,7 @@ fn check_derived_typechecks_and_golden(
let mut def_types = Default::default();
let mut rigid_vars = Default::default();
let mut flex_vars = Default::default();
let (import_variables, abilities_store) = add_imports(
let (import_variables, imported_param_vars, abilities_store) = add_imports(
test_module,
&mut constraints,
&mut test_subs,
@ -438,6 +438,8 @@ fn check_derived_typechecks_and_golden(
pending_derives: Default::default(),
exposed_by_module: &exposed_for_module.exposed_by_module,
derived_module: Default::default(),
module_params: None,
module_params_vars: imported_param_vars,
#[cfg(debug_assertions)]
checkmate: None,

View file

@ -1885,22 +1885,22 @@ fn task_always_twice() {
effectAfter : Effect a, (a -> Effect b) -> Effect b
effectAfter = \(@Effect thunk), transform -> transform (thunk {})
Task a err : Effect (Result a err)
MyTask a err : Effect (Result a err)
always : a -> Task a *
always : a -> MyTask a *
always = \x -> effectAlways (Ok x)
fail : err -> Task * err
fail : err -> MyTask * err
fail = \x -> effectAlways (Err x)
after : Task a err, (a -> Task b err) -> Task b err
after : MyTask a err, (a -> MyTask b err) -> MyTask b err
after = \task, transform ->
effectAfter task \res ->
when res is
Ok x -> transform x
Err e -> fail e
main : Task {} F64
main : MyTask {} F64
main = after (always "foo") (\_ -> always {})
"#
@ -1921,17 +1921,17 @@ fn wildcard_rigid() {
Effect a := {} -> a
Task a err : Effect (Result a err)
MyTask a err : Effect (Result a err)
# this failed because of the `*`, but worked with `err`
always : a -> Task a *
always : a -> MyTask a *
always = \x ->
inner = \{} -> (Ok x)
@Effect inner
main : Task {} (Frac *)
main : MyTask {} (Frac *)
main = always {}
"#
),
@ -1950,16 +1950,16 @@ fn alias_of_alias_with_type_arguments() {
Effect a := a
Task a err : Effect (Result a err)
MyTask a err : Effect (Result a err)
always : a -> Task a *
always : a -> MyTask a *
always = \x ->
inner = (Ok x)
@Effect inner
main : Task {} F64
main : MyTask {} F64
main = always {}
"#
),
@ -1989,24 +1989,24 @@ fn todo_bad_error_message() {
effectAfter : Effect a, (a -> Effect b) -> Effect b
effectAfter = \(@Effect thunk), transform -> transform (thunk {})
Task a err : Effect (Result a err)
MyTask a err : Effect (Result a err)
always : a -> Task a (Frac *)
always : a -> MyTask a (Frac *)
always = \x -> effectAlways (Ok x)
# the problem is that this restricts to `Task {} *`
fail : err -> Task {} err
# the problem is that this restricts to `MyTask {} *`
fail : err -> MyTask {} err
fail = \x -> effectAlways (Err x)
after : Task a err, (a -> Task b err) -> Task b err
after : MyTask a err, (a -> MyTask b err) -> MyTask b err
after = \task, transform ->
effectAfter task \res ->
when res is
Ok x -> transform x
# but here it must be `forall b. Task b {}`
# but here it must be `forall b. MyTask b {}`
Err e -> fail e
main : Task {} (Frac *)
main : MyTask {} (Frac *)
main =
after (always "foo") (\_ -> always {})
"#
@ -2479,39 +2479,6 @@ fn expanded_result() {
);
}
#[test]
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm", feature = "gen-dev"))]
fn backpassing_result() {
assert_evals_to!(
indoc!(
r#"
app "test" provides [main] to "./platform"
a : Result I64 Str
a = Ok 1
f = \x -> Ok (x + 1)
g = \y -> Ok (y * 2)
main : I64
main =
helper =
x <- Result.try a
y <- Result.try (f x)
z <- Result.try (g y)
Ok z
helper
|> Result.withDefault 0
"#
),
4,
i64
);
}
#[test]
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
#[should_panic(expected = "Shadowing { original_region: @55-56, shadow: @72-73 Ident")]

View file

@ -885,6 +885,38 @@ fn update_single_element_record() {
);
}
#[test]
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
fn update_record_shorthand() {
assert_evals_to!(
indoc!(
r"
rec = { foo: 42, bar: 2.46f64 }
rec |> &foo (rec.foo + 1)
"
),
(2.46, 43),
(f64, i64)
);
}
#[test]
#[cfg(any(feature = "gen-llvm", feature = "gen-dev", feature = "gen-wasm"))]
fn update_single_element_record_shorthand() {
assert_evals_to!(
indoc!(
r"
rec = { foo: 42}
&foo rec (rec.foo + 1)
"
),
43,
i64
);
}
#[test]
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
fn booleans_in_record() {

View file

@ -354,3 +354,85 @@ fn roc_result_after_err() {
RocResult<RocStr, i64>
);
}
#[test]
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm", feature = "gen-dev"))]
fn roc_result_map_both() {
assert_evals_to!(
indoc!(
r#"
result : Result I64 I64
result = Ok 42
result |> Result.mapBoth Num.toStr Num.toStr
"#
),
RocResult::ok(RocStr::from("42")),
RocResult<RocStr, RocStr>
);
assert_evals_to!(
indoc!(
r#"
result : Result I64 I64
result = Err 24
result |> Result.mapBoth Num.toStr Num.toStr
"#
),
RocResult::err(RocStr::from("24")),
RocResult<RocStr, RocStr>
);
}
#[test]
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm", feature = "gen-dev"))]
fn roc_result_map_two() {
assert_evals_to!(
indoc!(
r#"
first : Result I64 Str
first = Ok 24
second : Result I64 Str
second = Ok -10
Result.map2 first second \a, b -> a + b
"#
),
RocResult::ok(14i64),
RocResult<i64, RocStr>
);
assert_evals_to!(
indoc!(
r#"
first : Result I64 Str
first = Err "foo"
second : Result I64 Str
second = Err "bar"
Result.map2 first second \a, b -> a + b
"#
),
RocResult::err(RocStr::from("foo")),
RocResult<i64, RocStr>
);
assert_evals_to!(
indoc!(
r#"
first : Result I64 Str
first = Ok 42
second : Result I64 Str
second = Err "bar"
Result.map2 first second \a, b -> a + b
"#
),
RocResult::err(RocStr::from("bar")),
RocResult<i64, RocStr>
);
}

View file

@ -2,93 +2,93 @@ procedure Bool.11 (#Attr.2, #Attr.3):
let Bool.23 : Int1 = lowlevel Eq #Attr.2 #Attr.3;
ret Bool.23;
procedure List.107 (List.536, List.537, List.538):
let List.640 : U64 = 0i64;
let List.641 : U64 = CallByName List.6 List.536;
let List.639 : [C U64, C U64] = CallByName List.80 List.536 List.537 List.538 List.640 List.641;
ret List.639;
procedure List.110 (List.539, List.540, List.541):
let List.643 : U64 = 0i64;
let List.644 : U64 = CallByName List.6 List.539;
let List.642 : [C U64, C U64] = CallByName List.80 List.539 List.540 List.541 List.643 List.644;
ret List.642;
procedure List.26 (List.204, List.205, List.206):
let List.633 : [C U64, C U64] = CallByName List.107 List.204 List.205 List.206;
let List.636 : U8 = 1i64;
let List.637 : U8 = GetTagId List.633;
let List.638 : Int1 = lowlevel Eq List.636 List.637;
if List.638 then
let List.207 : U64 = UnionAtIndex (Id 1) (Index 0) List.633;
ret List.207;
procedure List.26 (List.207, List.208, List.209):
let List.636 : [C U64, C U64] = CallByName List.110 List.207 List.208 List.209;
let List.639 : U8 = 1i64;
let List.640 : U8 = GetTagId List.636;
let List.641 : Int1 = lowlevel Eq List.639 List.640;
if List.641 then
let List.210 : U64 = UnionAtIndex (Id 1) (Index 0) List.636;
ret List.210;
else
let List.208 : U64 = UnionAtIndex (Id 0) (Index 0) List.633;
ret List.208;
let List.211 : U64 = UnionAtIndex (Id 0) (Index 0) List.636;
ret List.211;
procedure List.38 (List.392, List.393):
let List.632 : U64 = CallByName List.6 List.392;
let List.394 : U64 = CallByName Num.77 List.632 List.393;
let List.622 : List U8 = CallByName List.43 List.392 List.394;
ret List.622;
procedure List.43 (List.390, List.391):
let List.630 : U64 = CallByName List.6 List.390;
let List.629 : U64 = CallByName Num.77 List.630 List.391;
let List.624 : {U64, U64} = Struct {List.391, List.629};
let List.623 : List U8 = CallByName List.49 List.390 List.624;
ret List.623;
procedure List.49 (List.468, List.469):
let List.626 : U64 = StructAtIndex 1 List.469;
let List.627 : U64 = StructAtIndex 0 List.469;
let List.625 : List U8 = CallByName List.72 List.468 List.626 List.627;
procedure List.38 (List.395, List.396):
let List.635 : U64 = CallByName List.6 List.395;
let List.397 : U64 = CallByName Num.77 List.635 List.396;
let List.625 : List U8 = CallByName List.43 List.395 List.397;
ret List.625;
procedure List.6 (#Attr.2):
let List.631 : U64 = lowlevel ListLenU64 #Attr.2;
ret List.631;
procedure List.43 (List.393, List.394):
let List.633 : U64 = CallByName List.6 List.393;
let List.632 : U64 = CallByName Num.77 List.633 List.394;
let List.627 : {U64, U64} = Struct {List.394, List.632};
let List.626 : List U8 = CallByName List.49 List.393 List.627;
ret List.626;
procedure List.66 (#Attr.2, #Attr.3):
let List.654 : U8 = lowlevel ListGetUnsafe #Attr.2 #Attr.3;
ret List.654;
procedure List.72 (#Attr.2, #Attr.3, #Attr.4):
let List.628 : List U8 = lowlevel ListSublist #Attr.2 #Attr.3 #Attr.4;
procedure List.49 (List.471, List.472):
let List.629 : U64 = StructAtIndex 1 List.472;
let List.630 : U64 = StructAtIndex 0 List.472;
let List.628 : List U8 = CallByName List.72 List.471 List.629 List.630;
ret List.628;
procedure List.6 (#Attr.2):
let List.634 : U64 = lowlevel ListLenU64 #Attr.2;
ret List.634;
procedure List.66 (#Attr.2, #Attr.3):
let List.657 : U8 = lowlevel ListGetUnsafe #Attr.2 #Attr.3;
ret List.657;
procedure List.72 (#Attr.2, #Attr.3, #Attr.4):
let List.631 : List U8 = lowlevel ListSublist #Attr.2 #Attr.3 #Attr.4;
ret List.631;
procedure List.80 (#Derived_gen.0, #Derived_gen.1, #Derived_gen.2, #Derived_gen.3, #Derived_gen.4):
joinpoint List.642 List.539 List.540 List.541 List.542 List.543:
let List.644 : Int1 = CallByName Num.22 List.542 List.543;
if List.644 then
let List.653 : U8 = CallByName List.66 List.539 List.542;
let List.645 : [C U64, C U64] = CallByName Test.4 List.540 List.653;
let List.650 : U8 = 1i64;
let List.651 : U8 = GetTagId List.645;
let List.652 : Int1 = lowlevel Eq List.650 List.651;
if List.652 then
let List.544 : U64 = UnionAtIndex (Id 1) (Index 0) List.645;
let List.648 : U64 = 1i64;
let List.647 : U64 = CallByName Num.51 List.542 List.648;
jump List.642 List.539 List.544 List.541 List.647 List.543;
joinpoint List.645 List.542 List.543 List.544 List.545 List.546:
let List.647 : Int1 = CallByName Num.22 List.545 List.546;
if List.647 then
let List.656 : U8 = CallByName List.66 List.542 List.545;
let List.648 : [C U64, C U64] = CallByName Test.4 List.543 List.656;
let List.653 : U8 = 1i64;
let List.654 : U8 = GetTagId List.648;
let List.655 : Int1 = lowlevel Eq List.653 List.654;
if List.655 then
let List.547 : U64 = UnionAtIndex (Id 1) (Index 0) List.648;
let List.651 : U64 = 1i64;
let List.650 : U64 = CallByName Num.51 List.545 List.651;
jump List.645 List.542 List.547 List.544 List.650 List.546;
else
dec List.539;
let List.545 : U64 = UnionAtIndex (Id 0) (Index 0) List.645;
let List.649 : [C U64, C U64] = TagId(0) List.545;
ret List.649;
dec List.542;
let List.548 : U64 = UnionAtIndex (Id 0) (Index 0) List.648;
let List.652 : [C U64, C U64] = TagId(0) List.548;
ret List.652;
else
dec List.539;
let List.643 : [C U64, C U64] = TagId(1) List.540;
ret List.643;
dec List.542;
let List.646 : [C U64, C U64] = TagId(1) List.543;
ret List.646;
in
inc #Derived_gen.0;
jump List.642 #Derived_gen.0 #Derived_gen.1 #Derived_gen.2 #Derived_gen.3 #Derived_gen.4;
jump List.645 #Derived_gen.0 #Derived_gen.1 #Derived_gen.2 #Derived_gen.3 #Derived_gen.4;
procedure Num.22 (#Attr.2, #Attr.3):
let Num.282 : Int1 = lowlevel NumLt #Attr.2 #Attr.3;
ret Num.282;
let Num.284 : Int1 = lowlevel NumLt #Attr.2 #Attr.3;
ret Num.284;
procedure Num.51 (#Attr.2, #Attr.3):
let Num.281 : U64 = lowlevel NumAddWrap #Attr.2 #Attr.3;
ret Num.281;
let Num.283 : U64 = lowlevel NumAddWrap #Attr.2 #Attr.3;
ret Num.283;
procedure Num.77 (#Attr.2, #Attr.3):
let Num.280 : U64 = lowlevel NumSubSaturated #Attr.2 #Attr.3;
ret Num.280;
let Num.282 : U64 = lowlevel NumSubSaturated #Attr.2 #Attr.3;
ret Num.282;
procedure Test.1 (Test.2):
let Test.13 : U64 = 0i64;

View file

@ -0,0 +1,31 @@
procedure Num.19 (#Attr.2, #Attr.3):
let Num.282 : I64 = lowlevel NumAdd #Attr.2 #Attr.3;
ret Num.282;
procedure Test.1 (Test.12):
let Test.6 : I64 = StructAtIndex 0 Test.12;
let Test.5 : I64 = StructAtIndex 1 Test.12;
let Test.3 : I64 = StructAtIndex 2 Test.12;
let Test.4 : I64 = StructAtIndex 3 Test.12;
let Test.18 : I64 = CallByName Num.19 Test.3 Test.5;
let Test.19 : I64 = CallByName Num.19 Test.4 Test.6;
let Test.17 : {I64, I64} = Struct {Test.18, Test.19};
ret Test.17;
procedure Test.2 (Test.9):
let Test.7 : I64 = StructAtIndex 2 Test.9;
let Test.8 : I64 = StructAtIndex 3 Test.9;
let Test.16 : {I64, I64} = CallByName Test.1 Test.9;
let Test.10 : I64 = StructAtIndex 0 Test.16;
let Test.11 : I64 = StructAtIndex 1 Test.16;
let Test.15 : {I64, I64, I64, I64} = Struct {Test.7, Test.8, Test.10, Test.11};
ret Test.15;
procedure Test.0 ():
let Test.20 : I64 = 4i64;
let Test.21 : I64 = 3i64;
let Test.22 : I64 = 1i64;
let Test.23 : I64 = 2i64;
let Test.14 : {I64, I64, I64, I64} = Struct {Test.20, Test.21, Test.22, Test.23};
let Test.13 : {I64, I64, I64, I64} = CallByName Test.2 Test.14;
ret Test.13;

View file

@ -1,6 +1,6 @@
procedure Num.19 (#Attr.2, #Attr.3):
let Num.279 : I64 = lowlevel NumAdd #Attr.2 #Attr.3;
ret Num.279;
let Num.281 : I64 = lowlevel NumAdd #Attr.2 #Attr.3;
ret Num.281;
procedure Test.4 (Test.27):
let Test.39 : [<rnu>C [<rnu><null>, C *self *self] *self, <null>] = TagId(0) ;

View file

@ -1,59 +1,59 @@
procedure List.18 (List.163, List.164, List.165):
let List.626 : U64 = 0i64;
let List.627 : U64 = CallByName List.6 List.163;
let List.625 : List {} = CallByName List.92 List.163 List.164 List.165 List.626 List.627;
ret List.625;
procedure List.18 (List.166, List.167, List.168):
let List.629 : U64 = 0i64;
let List.630 : U64 = CallByName List.6 List.166;
let List.628 : List {} = CallByName List.95 List.166 List.167 List.168 List.629 List.630;
ret List.628;
procedure List.275 (List.276, List.277, List.273):
let List.639 : {} = CallByName Test.2 List.277;
let List.638 : List {} = CallByName List.71 List.276 List.639;
ret List.638;
procedure List.5 (List.272, List.273):
let List.274 : U64 = CallByName List.6 List.272;
let List.623 : List {} = CallByName List.68 List.274;
let List.622 : List {} = CallByName List.18 List.272 List.623 List.273;
ret List.622;
procedure List.6 (#Attr.2):
let List.636 : U64 = lowlevel ListLenU64 #Attr.2;
ret List.636;
procedure List.66 (#Attr.2, #Attr.3):
let List.635 : [] = lowlevel ListGetUnsafe #Attr.2 #Attr.3;
ret List.635;
procedure List.68 (#Attr.2):
let List.641 : List {} = lowlevel ListWithCapacity #Attr.2;
procedure List.278 (List.279, List.280, List.276):
let List.642 : {} = CallByName Test.2 List.280;
let List.641 : List {} = CallByName List.71 List.279 List.642;
ret List.641;
procedure List.71 (#Attr.2, #Attr.3):
let List.640 : List {} = lowlevel ListAppendUnsafe #Attr.2 #Attr.3;
ret List.640;
procedure List.5 (List.275, List.276):
let List.277 : U64 = CallByName List.6 List.275;
let List.626 : List {} = CallByName List.68 List.277;
let List.625 : List {} = CallByName List.18 List.275 List.626 List.276;
ret List.625;
procedure List.92 (#Derived_gen.3, #Derived_gen.4, #Derived_gen.5, #Derived_gen.6, #Derived_gen.7):
joinpoint List.628 List.166 List.167 List.168 List.169 List.170:
let List.630 : Int1 = CallByName Num.22 List.169 List.170;
if List.630 then
let List.634 : [] = CallByName List.66 List.166 List.169;
let List.171 : List {} = CallByName List.275 List.167 List.634 List.168;
let List.633 : U64 = 1i64;
let List.632 : U64 = CallByName Num.51 List.169 List.633;
jump List.628 List.166 List.171 List.168 List.632 List.170;
procedure List.6 (#Attr.2):
let List.639 : U64 = lowlevel ListLenU64 #Attr.2;
ret List.639;
procedure List.66 (#Attr.2, #Attr.3):
let List.638 : [] = lowlevel ListGetUnsafe #Attr.2 #Attr.3;
ret List.638;
procedure List.68 (#Attr.2):
let List.644 : List {} = lowlevel ListWithCapacity #Attr.2;
ret List.644;
procedure List.71 (#Attr.2, #Attr.3):
let List.643 : List {} = lowlevel ListAppendUnsafe #Attr.2 #Attr.3;
ret List.643;
procedure List.95 (#Derived_gen.0, #Derived_gen.1, #Derived_gen.2, #Derived_gen.3, #Derived_gen.4):
joinpoint List.631 List.169 List.170 List.171 List.172 List.173:
let List.633 : Int1 = CallByName Num.22 List.172 List.173;
if List.633 then
let List.637 : [] = CallByName List.66 List.169 List.172;
let List.174 : List {} = CallByName List.278 List.170 List.637 List.171;
let List.636 : U64 = 1i64;
let List.635 : U64 = CallByName Num.51 List.172 List.636;
jump List.631 List.169 List.174 List.171 List.635 List.173;
else
dec List.166;
ret List.167;
dec List.169;
ret List.170;
in
inc #Derived_gen.3;
jump List.628 #Derived_gen.3 #Derived_gen.4 #Derived_gen.5 #Derived_gen.6 #Derived_gen.7;
inc #Derived_gen.0;
jump List.631 #Derived_gen.0 #Derived_gen.1 #Derived_gen.2 #Derived_gen.3 #Derived_gen.4;
procedure Num.22 (#Attr.2, #Attr.3):
let Num.280 : Int1 = lowlevel NumLt #Attr.2 #Attr.3;
ret Num.280;
let Num.282 : Int1 = lowlevel NumLt #Attr.2 #Attr.3;
ret Num.282;
procedure Num.51 (#Attr.2, #Attr.3):
let Num.279 : U64 = lowlevel NumAddWrap #Attr.2 #Attr.3;
ret Num.279;
let Num.281 : U64 = lowlevel NumAddWrap #Attr.2 #Attr.3;
ret Num.281;
procedure Test.2 (Test.3):
let Test.7 : {} = Struct {};

View file

@ -1,59 +1,59 @@
procedure List.18 (List.163, List.164, List.165):
let List.626 : U64 = 0i64;
let List.627 : U64 = CallByName List.6 List.163;
let List.625 : List [] = CallByName List.92 List.163 List.164 List.165 List.626 List.627;
ret List.625;
procedure List.18 (List.166, List.167, List.168):
let List.629 : U64 = 0i64;
let List.630 : U64 = CallByName List.6 List.166;
let List.628 : List [] = CallByName List.95 List.166 List.167 List.168 List.629 List.630;
ret List.628;
procedure List.275 (List.276, List.277, List.273):
let List.639 : [] = CallByName Test.2 List.277;
let List.638 : List [] = CallByName List.71 List.276 List.639;
ret List.638;
procedure List.5 (List.272, List.273):
let List.274 : U64 = CallByName List.6 List.272;
let List.623 : List [] = CallByName List.68 List.274;
let List.622 : List [] = CallByName List.18 List.272 List.623 List.273;
ret List.622;
procedure List.6 (#Attr.2):
let List.636 : U64 = lowlevel ListLenU64 #Attr.2;
ret List.636;
procedure List.66 (#Attr.2, #Attr.3):
let List.635 : [] = lowlevel ListGetUnsafe #Attr.2 #Attr.3;
ret List.635;
procedure List.68 (#Attr.2):
let List.641 : List [] = lowlevel ListWithCapacity #Attr.2;
procedure List.278 (List.279, List.280, List.276):
let List.642 : [] = CallByName Test.2 List.280;
let List.641 : List [] = CallByName List.71 List.279 List.642;
ret List.641;
procedure List.71 (#Attr.2, #Attr.3):
let List.640 : List [] = lowlevel ListAppendUnsafe #Attr.2 #Attr.3;
ret List.640;
procedure List.5 (List.275, List.276):
let List.277 : U64 = CallByName List.6 List.275;
let List.626 : List [] = CallByName List.68 List.277;
let List.625 : List [] = CallByName List.18 List.275 List.626 List.276;
ret List.625;
procedure List.92 (#Derived_gen.3, #Derived_gen.4, #Derived_gen.5, #Derived_gen.6, #Derived_gen.7):
joinpoint List.628 List.166 List.167 List.168 List.169 List.170:
let List.630 : Int1 = CallByName Num.22 List.169 List.170;
if List.630 then
let List.634 : [] = CallByName List.66 List.166 List.169;
let List.171 : List [] = CallByName List.275 List.167 List.634 List.168;
let List.633 : U64 = 1i64;
let List.632 : U64 = CallByName Num.51 List.169 List.633;
jump List.628 List.166 List.171 List.168 List.632 List.170;
procedure List.6 (#Attr.2):
let List.639 : U64 = lowlevel ListLenU64 #Attr.2;
ret List.639;
procedure List.66 (#Attr.2, #Attr.3):
let List.638 : [] = lowlevel ListGetUnsafe #Attr.2 #Attr.3;
ret List.638;
procedure List.68 (#Attr.2):
let List.644 : List [] = lowlevel ListWithCapacity #Attr.2;
ret List.644;
procedure List.71 (#Attr.2, #Attr.3):
let List.643 : List [] = lowlevel ListAppendUnsafe #Attr.2 #Attr.3;
ret List.643;
procedure List.95 (#Derived_gen.0, #Derived_gen.1, #Derived_gen.2, #Derived_gen.3, #Derived_gen.4):
joinpoint List.631 List.169 List.170 List.171 List.172 List.173:
let List.633 : Int1 = CallByName Num.22 List.172 List.173;
if List.633 then
let List.637 : [] = CallByName List.66 List.169 List.172;
let List.174 : List [] = CallByName List.278 List.170 List.637 List.171;
let List.636 : U64 = 1i64;
let List.635 : U64 = CallByName Num.51 List.172 List.636;
jump List.631 List.169 List.174 List.171 List.635 List.173;
else
dec List.166;
ret List.167;
dec List.169;
ret List.170;
in
inc #Derived_gen.3;
jump List.628 #Derived_gen.3 #Derived_gen.4 #Derived_gen.5 #Derived_gen.6 #Derived_gen.7;
inc #Derived_gen.0;
jump List.631 #Derived_gen.0 #Derived_gen.1 #Derived_gen.2 #Derived_gen.3 #Derived_gen.4;
procedure Num.22 (#Attr.2, #Attr.3):
let Num.280 : Int1 = lowlevel NumLt #Attr.2 #Attr.3;
ret Num.280;
let Num.282 : Int1 = lowlevel NumLt #Attr.2 #Attr.3;
ret Num.282;
procedure Num.51 (#Attr.2, #Attr.3):
let Num.279 : U64 = lowlevel NumAddWrap #Attr.2 #Attr.3;
ret Num.279;
let Num.281 : U64 = lowlevel NumAddWrap #Attr.2 #Attr.3;
ret Num.281;
procedure Test.2 (Test.3):
let Test.7 : {} = Struct {};

View file

@ -1,45 +1,45 @@
procedure List.18 (List.163, List.164, List.165):
let List.623 : U64 = 0i64;
let List.624 : U64 = CallByName List.6 List.163;
let List.622 : [<r>C {}, C *self {{}, []}] = CallByName List.92 List.163 List.164 List.165 List.623 List.624;
ret List.622;
procedure List.18 (List.166, List.167, List.168):
let List.626 : U64 = 0i64;
let List.627 : U64 = CallByName List.6 List.166;
let List.625 : [<r>C {}, C *self {{}, []}] = CallByName List.95 List.166 List.167 List.168 List.626 List.627;
ret List.625;
procedure List.6 (#Attr.2):
let List.633 : U64 = lowlevel ListLenU64 #Attr.2;
ret List.633;
let List.636 : U64 = lowlevel ListLenU64 #Attr.2;
ret List.636;
procedure List.66 (#Attr.2, #Attr.3):
let List.632 : [] = lowlevel ListGetUnsafe #Attr.2 #Attr.3;
ret List.632;
let List.635 : [] = lowlevel ListGetUnsafe #Attr.2 #Attr.3;
ret List.635;
procedure List.92 (#Derived_gen.11, #Derived_gen.12, #Derived_gen.13, #Derived_gen.14, #Derived_gen.15):
joinpoint List.625 List.166 List.167 List.168 List.169 List.170:
let List.627 : Int1 = CallByName Num.22 List.169 List.170;
if List.627 then
let List.631 : [] = CallByName List.66 List.166 List.169;
let List.171 : [<r>C {}, C *self {{}, []}] = CallByName Test.29 List.167 List.631 List.168;
let List.630 : U64 = 1i64;
let List.629 : U64 = CallByName Num.51 List.169 List.630;
jump List.625 List.166 List.171 List.168 List.629 List.170;
procedure List.95 (#Derived_gen.13, #Derived_gen.14, #Derived_gen.15, #Derived_gen.16, #Derived_gen.17):
joinpoint List.628 List.169 List.170 List.171 List.172 List.173:
let List.630 : Int1 = CallByName Num.22 List.172 List.173;
if List.630 then
let List.634 : [] = CallByName List.66 List.169 List.172;
let List.174 : [<r>C {}, C *self {{}, []}] = CallByName Test.29 List.170 List.634 List.171;
let List.633 : U64 = 1i64;
let List.632 : U64 = CallByName Num.51 List.172 List.633;
jump List.628 List.169 List.174 List.171 List.632 List.173;
else
dec List.166;
ret List.167;
dec List.169;
ret List.170;
in
inc #Derived_gen.11;
jump List.625 #Derived_gen.11 #Derived_gen.12 #Derived_gen.13 #Derived_gen.14 #Derived_gen.15;
inc #Derived_gen.13;
jump List.628 #Derived_gen.13 #Derived_gen.14 #Derived_gen.15 #Derived_gen.16 #Derived_gen.17;
procedure Num.22 (#Attr.2, #Attr.3):
let Num.280 : Int1 = lowlevel NumLt #Attr.2 #Attr.3;
ret Num.280;
let Num.282 : Int1 = lowlevel NumLt #Attr.2 #Attr.3;
ret Num.282;
procedure Num.51 (#Attr.2, #Attr.3):
let Num.279 : U64 = lowlevel NumAddWrap #Attr.2 #Attr.3;
ret Num.279;
let Num.281 : U64 = lowlevel NumAddWrap #Attr.2 #Attr.3;
ret Num.281;
procedure Test.10 (Test.69, #Attr.12):
let Test.72 : {} = UnionAtIndex (Id 0) (Index 0) #Attr.12;
let #Derived_gen.18 : Int1 = lowlevel RefCountIsUnique #Attr.12;
if #Derived_gen.18 then
let #Derived_gen.20 : Int1 = lowlevel RefCountIsUnique #Attr.12;
if #Derived_gen.20 then
free #Attr.12;
ret Test.72;
else
@ -53,7 +53,7 @@ procedure Test.10 (Test.69, #Attr.12):
procedure Test.14 (Test.45, #Attr.12):
let Test.55 : {{}, []} = UnionAtIndex (Id 1) (Index 1) #Attr.12;
let Test.54 : [<r>C {}, C *self {{}, []}] = UnionAtIndex (Id 1) (Index 0) #Attr.12;
joinpoint #Derived_gen.19:
joinpoint #Derived_gen.18:
let Test.50 : {} = Struct {};
let Test.51 : U8 = GetTagId Test.54;
joinpoint Test.52 Test.15:
@ -80,14 +80,14 @@ procedure Test.14 (Test.45, #Attr.12):
jump Test.52 Test.53;
in
let #Derived_gen.20 : Int1 = lowlevel RefCountIsUnique #Attr.12;
if #Derived_gen.20 then
let #Derived_gen.19 : Int1 = lowlevel RefCountIsUnique #Attr.12;
if #Derived_gen.19 then
free #Attr.12;
jump #Derived_gen.19;
jump #Derived_gen.18;
else
inc Test.54;
decref #Attr.12;
jump #Derived_gen.19;
jump #Derived_gen.18;
procedure Test.20 (Test.21, Test.18):
let Test.23 : [C {}, C []] = CallByName Test.32 Test.21 Test.18;

View file

@ -2,112 +2,112 @@ procedure Bool.1 ():
let Bool.24 : Int1 = false;
ret Bool.24;
procedure List.18 (List.163, List.164, List.165):
let List.642 : U64 = 0i64;
let List.643 : U64 = CallByName List.6 List.163;
let List.641 : List Str = CallByName List.92 List.163 List.164 List.165 List.642 List.643;
ret List.641;
procedure List.18 (List.166, List.167, List.168):
let List.645 : U64 = 0i64;
let List.646 : U64 = CallByName List.6 List.166;
let List.644 : List Str = CallByName List.95 List.166 List.167 List.168 List.645 List.646;
ret List.644;
procedure List.2 (List.111, List.112):
let List.636 : U64 = CallByName List.6 List.111;
let List.632 : Int1 = CallByName Num.22 List.112 List.636;
if List.632 then
let List.634 : Str = CallByName List.66 List.111 List.112;
inc List.634;
let List.633 : [C {}, C Str] = TagId(1) List.634;
ret List.633;
procedure List.2 (List.114, List.115):
let List.639 : U64 = CallByName List.6 List.114;
let List.635 : Int1 = CallByName Num.22 List.115 List.639;
if List.635 then
let List.637 : Str = CallByName List.66 List.114 List.115;
inc List.637;
let List.636 : [C {}, C Str] = TagId(1) List.637;
ret List.636;
else
let List.631 : {} = Struct {};
let List.630 : [C {}, C Str] = TagId(0) List.631;
ret List.630;
let List.634 : {} = Struct {};
let List.633 : [C {}, C Str] = TagId(0) List.634;
ret List.633;
procedure List.275 (List.276, List.277, List.273):
let List.655 : Str = CallByName Test.10 List.277;
let List.654 : List Str = CallByName List.71 List.276 List.655;
ret List.654;
procedure List.5 (List.272, List.273):
let List.274 : U64 = CallByName List.6 List.272;
let List.639 : List Str = CallByName List.68 List.274;
let List.638 : List Str = CallByName List.18 List.272 List.639 List.273;
ret List.638;
procedure List.6 (#Attr.2):
let List.637 : U64 = lowlevel ListLenU64 #Attr.2;
ret List.637;
procedure List.6 (#Attr.2):
let List.652 : U64 = lowlevel ListLenU64 #Attr.2;
ret List.652;
procedure List.66 (#Attr.2, #Attr.3):
let List.635 : Str = lowlevel ListGetUnsafe #Attr.2 #Attr.3;
ret List.635;
procedure List.66 (#Attr.2, #Attr.3):
let List.651 : [<r>C List [<r>C List *self, C *self], C [<r>C List *self, C *self]] = lowlevel ListGetUnsafe #Attr.2 #Attr.3;
ret List.651;
procedure List.68 (#Attr.2):
let List.657 : List Str = lowlevel ListWithCapacity #Attr.2;
procedure List.278 (List.279, List.280, List.276):
let List.658 : Str = CallByName Test.10 List.280;
let List.657 : List Str = CallByName List.71 List.279 List.658;
ret List.657;
procedure List.5 (List.275, List.276):
let List.277 : U64 = CallByName List.6 List.275;
let List.642 : List Str = CallByName List.68 List.277;
let List.641 : List Str = CallByName List.18 List.275 List.642 List.276;
ret List.641;
procedure List.6 (#Attr.2):
let List.640 : U64 = lowlevel ListLenU64 #Attr.2;
ret List.640;
procedure List.6 (#Attr.2):
let List.655 : U64 = lowlevel ListLenU64 #Attr.2;
ret List.655;
procedure List.66 (#Attr.2, #Attr.3):
let List.638 : Str = lowlevel ListGetUnsafe #Attr.2 #Attr.3;
ret List.638;
procedure List.66 (#Attr.2, #Attr.3):
let List.654 : [<r>C List [<r>C List *self, C *self], C [<r>C List *self, C *self]] = lowlevel ListGetUnsafe #Attr.2 #Attr.3;
ret List.654;
procedure List.68 (#Attr.2):
let List.660 : List Str = lowlevel ListWithCapacity #Attr.2;
ret List.660;
procedure List.71 (#Attr.2, #Attr.3):
let List.656 : List Str = lowlevel ListAppendUnsafe #Attr.2 #Attr.3;
ret List.656;
let List.659 : List Str = lowlevel ListAppendUnsafe #Attr.2 #Attr.3;
ret List.659;
procedure List.9 (List.383):
let List.629 : U64 = 0i64;
let List.622 : [C {}, C Str] = CallByName List.2 List.383 List.629;
let List.626 : U8 = 1i64;
let List.627 : U8 = GetTagId List.622;
let List.628 : Int1 = lowlevel Eq List.626 List.627;
if List.628 then
let List.384 : Str = UnionAtIndex (Id 1) (Index 0) List.622;
let List.623 : [C {}, C Str] = TagId(1) List.384;
ret List.623;
procedure List.9 (List.386):
let List.632 : U64 = 0i64;
let List.625 : [C {}, C Str] = CallByName List.2 List.386 List.632;
let List.629 : U8 = 1i64;
let List.630 : U8 = GetTagId List.625;
let List.631 : Int1 = lowlevel Eq List.629 List.630;
if List.631 then
let List.387 : Str = UnionAtIndex (Id 1) (Index 0) List.625;
let List.626 : [C {}, C Str] = TagId(1) List.387;
ret List.626;
else
dec List.622;
let List.625 : {} = Struct {};
let List.624 : [C {}, C Str] = TagId(0) List.625;
ret List.624;
dec List.625;
let List.628 : {} = Struct {};
let List.627 : [C {}, C Str] = TagId(0) List.628;
ret List.627;
procedure List.92 (#Derived_gen.4, #Derived_gen.5, #Derived_gen.6, #Derived_gen.7, #Derived_gen.8):
joinpoint List.644 List.166 List.167 List.168 List.169 List.170:
let List.646 : Int1 = CallByName Num.22 List.169 List.170;
if List.646 then
let List.650 : [<r>C List [<r>C List *self, C *self], C [<r>C List *self, C *self]] = CallByName List.66 List.166 List.169;
inc List.650;
let List.171 : List Str = CallByName List.275 List.167 List.650 List.168;
let List.649 : U64 = 1i64;
let List.648 : U64 = CallByName Num.51 List.169 List.649;
jump List.644 List.166 List.171 List.168 List.648 List.170;
procedure List.95 (#Derived_gen.4, #Derived_gen.5, #Derived_gen.6, #Derived_gen.7, #Derived_gen.8):
joinpoint List.647 List.169 List.170 List.171 List.172 List.173:
let List.649 : Int1 = CallByName Num.22 List.172 List.173;
if List.649 then
let List.653 : [<r>C List [<r>C List *self, C *self], C [<r>C List *self, C *self]] = CallByName List.66 List.169 List.172;
inc List.653;
let List.174 : List Str = CallByName List.278 List.170 List.653 List.171;
let List.652 : U64 = 1i64;
let List.651 : U64 = CallByName Num.51 List.172 List.652;
jump List.647 List.169 List.174 List.171 List.651 List.173;
else
dec List.166;
ret List.167;
dec List.169;
ret List.170;
in
inc #Derived_gen.4;
jump List.644 #Derived_gen.4 #Derived_gen.5 #Derived_gen.6 #Derived_gen.7 #Derived_gen.8;
jump List.647 #Derived_gen.4 #Derived_gen.5 #Derived_gen.6 #Derived_gen.7 #Derived_gen.8;
procedure Num.22 (#Attr.2, #Attr.3):
let Num.280 : Int1 = lowlevel NumLt #Attr.2 #Attr.3;
ret Num.280;
let Num.282 : Int1 = lowlevel NumLt #Attr.2 #Attr.3;
ret Num.282;
procedure Num.51 (#Attr.2, #Attr.3):
let Num.281 : U64 = lowlevel NumAddWrap #Attr.2 #Attr.3;
ret Num.281;
let Num.283 : U64 = lowlevel NumAddWrap #Attr.2 #Attr.3;
ret Num.283;
procedure Result.5 (Result.10, Result.11):
let Result.37 : U8 = 1i64;
let Result.38 : U8 = GetTagId Result.10;
let Result.39 : Int1 = lowlevel Eq Result.37 Result.38;
if Result.39 then
dec Result.11;
let Result.12 : Str = UnionAtIndex (Id 1) (Index 0) Result.10;
ret Result.12;
procedure Result.5 (Result.13, Result.14):
let Result.57 : U8 = 1i64;
let Result.58 : U8 = GetTagId Result.13;
let Result.59 : Int1 = lowlevel Eq Result.57 Result.58;
if Result.59 then
dec Result.14;
let Result.15 : Str = UnionAtIndex (Id 1) (Index 0) Result.13;
ret Result.15;
else
dec Result.10;
ret Result.11;
dec Result.13;
ret Result.14;
procedure Test.10 (Test.11):
let Test.12 : Str = CallByName Test.2 Test.11;

View file

@ -1,6 +1,6 @@
procedure Num.19 (#Attr.2, #Attr.3):
let Num.280 : I128 = lowlevel NumAdd #Attr.2 #Attr.3;
ret Num.280;
let Num.282 : I128 = lowlevel NumAdd #Attr.2 #Attr.3;
ret Num.282;
procedure Test.0 ():
let Test.6 : I128 = 18446744073709551616i64;

View file

@ -1,6 +1,6 @@
procedure Num.19 (#Attr.2, #Attr.3):
let Num.279 : U128 = lowlevel NumAdd #Attr.2 #Attr.3;
ret Num.279;
let Num.281 : U128 = lowlevel NumAdd #Attr.2 #Attr.3;
ret Num.281;
procedure Test.0 ():
let Test.2 : U128 = 170141183460469231731687303715884105728u128;

View file

@ -1,6 +1,6 @@
procedure Num.19 (#Attr.2, #Attr.3):
let Num.279 : U64 = lowlevel NumAdd #Attr.2 #Attr.3;
ret Num.279;
let Num.281 : U64 = lowlevel NumAdd #Attr.2 #Attr.3;
ret Num.281;
procedure Test.0 ():
let Test.2 : U64 = 9999999999999999999i64;

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