mirror of
https://github.com/roc-lang/roc.git
synced 2025-09-30 15:21:12 +00:00
Merge branch 'dev-backend-num-is-zero' of https://github.com/rtfeldman/roc into dev-backend-num-is-zero
This commit is contained in:
commit
d59a5b2f73
67 changed files with 2502 additions and 1105 deletions
2
.github/workflows/spellcheck.yml
vendored
2
.github/workflows/spellcheck.yml
vendored
|
@ -8,7 +8,7 @@ env:
|
||||||
jobs:
|
jobs:
|
||||||
spell-check:
|
spell-check:
|
||||||
name: spell check
|
name: spell check
|
||||||
runs-on: [self-hosted]
|
runs-on: [self-hosted, linux]
|
||||||
timeout-minutes: 10
|
timeout-minutes: 10
|
||||||
env:
|
env:
|
||||||
FORCE_COLOR: 1
|
FORCE_COLOR: 1
|
||||||
|
|
2
.github/workflows/www.yml
vendored
2
.github/workflows/www.yml
vendored
|
@ -9,7 +9,7 @@ on:
|
||||||
jobs:
|
jobs:
|
||||||
deploy:
|
deploy:
|
||||||
name: 'Deploy to Netlify'
|
name: 'Deploy to Netlify'
|
||||||
runs-on: [self-hosted]
|
runs-on: [self-hosted, linux]
|
||||||
steps:
|
steps:
|
||||||
- uses: jsmrcaga/action-netlify-deploy@v1.6.0
|
- uses: jsmrcaga/action-netlify-deploy@v1.6.0
|
||||||
with:
|
with:
|
||||||
|
|
2
AUTHORS
2
AUTHORS
|
@ -54,3 +54,5 @@ Takeshi Sato <doublequotation@gmail.com>
|
||||||
Joost Baas <joost@joostbaas.eu>
|
Joost Baas <joost@joostbaas.eu>
|
||||||
Callum Dunster <cdunster@users.noreply.github.com>
|
Callum Dunster <cdunster@users.noreply.github.com>
|
||||||
Martin Stewart <MartinSStewart@gmail.com>
|
Martin Stewart <MartinSStewart@gmail.com>
|
||||||
|
James Hegedus <jthegedus@hey.com>
|
||||||
|
Cristiano Piemontese <cristiano.piemontese@vidiemme.it>
|
||||||
|
|
|
@ -94,13 +94,7 @@ Install nix:
|
||||||
|
|
||||||
`curl -L https://nixos.org/nix/install | sh`
|
`curl -L https://nixos.org/nix/install | sh`
|
||||||
|
|
||||||
If you're on MacOS and using a OS version >= 10.15:
|
You will need to start a fresh terminal session to use nix.
|
||||||
|
|
||||||
`sh <(curl -L https://nixos.org/nix/install) --darwin-use-unencrypted-nix-store-volume`
|
|
||||||
|
|
||||||
You may prefer to setup up the volume manually by following nix documentation.
|
|
||||||
|
|
||||||
> You may need to restart your terminal
|
|
||||||
|
|
||||||
### Usage
|
### Usage
|
||||||
|
|
||||||
|
|
1
Cargo.lock
generated
1
Cargo.lock
generated
|
@ -3554,6 +3554,7 @@ version = "0.1.0"
|
||||||
name = "roc_types"
|
name = "roc_types"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
|
"bumpalo",
|
||||||
"roc_collections",
|
"roc_collections",
|
||||||
"roc_module",
|
"roc_module",
|
||||||
"roc_region",
|
"roc_region",
|
||||||
|
|
10
Earthfile
10
Earthfile
|
@ -79,17 +79,17 @@ test-rust:
|
||||||
# not pre-compiling the host can cause race conditions
|
# not pre-compiling the host can cause race conditions
|
||||||
RUN echo "4" | cargo run --release examples/benchmarks/NQueens.roc
|
RUN echo "4" | cargo run --release examples/benchmarks/NQueens.roc
|
||||||
RUN --mount=type=cache,target=$SCCACHE_DIR \
|
RUN --mount=type=cache,target=$SCCACHE_DIR \
|
||||||
cargo test --release --features with_sound --workspace && sccache --show-stats
|
cargo test --locked --release --features with_sound --workspace && sccache --show-stats
|
||||||
# test the dev and wasm backend: they require an explicit feature flag.
|
# test the dev and wasm backend: they require an explicit feature flag.
|
||||||
RUN --mount=type=cache,target=$SCCACHE_DIR \
|
RUN --mount=type=cache,target=$SCCACHE_DIR \
|
||||||
cargo test --release --package test_gen --no-default-features --features gen-dev && sccache --show-stats
|
cargo test --locked --release --package test_gen --no-default-features --features gen-dev && sccache --show-stats
|
||||||
# gen-wasm has some multithreading problems to do with the wasmer runtime. Run it single-threaded as a separate job
|
# gen-wasm has some multithreading problems to do with the wasmer runtime. Run it single-threaded as a separate job
|
||||||
RUN --mount=type=cache,target=$SCCACHE_DIR \
|
RUN --mount=type=cache,target=$SCCACHE_DIR \
|
||||||
cargo test --release --package test_gen --no-default-features --features gen-wasm -- --test-threads=1 && sccache --show-stats
|
cargo test --locked --release --package test_gen --no-default-features --features gen-wasm -- --test-threads=1 && sccache --show-stats
|
||||||
# run i386 (32-bit linux) cli tests
|
# run i386 (32-bit linux) cli tests
|
||||||
RUN echo "4" | cargo run --release --features="target-x86" -- --backend=x86_32 examples/benchmarks/NQueens.roc
|
RUN echo "4" | cargo run --locked --release --features="target-x86" -- --backend=x86_32 examples/benchmarks/NQueens.roc
|
||||||
RUN --mount=type=cache,target=$SCCACHE_DIR \
|
RUN --mount=type=cache,target=$SCCACHE_DIR \
|
||||||
cargo test --release --features with_sound --test cli_run i386 --features="i386-cli-run" && sccache --show-stats
|
cargo test --locked --release --features with_sound --test cli_run i386 --features="i386-cli-run" && sccache --show-stats
|
||||||
|
|
||||||
verify-no-git-changes:
|
verify-no-git-changes:
|
||||||
FROM +test-rust
|
FROM +test-rust
|
||||||
|
|
|
@ -277,7 +277,7 @@ pub fn constrain_expr<'a>(
|
||||||
expr_id: expr_node_id,
|
expr_id: expr_node_id,
|
||||||
closure_var,
|
closure_var,
|
||||||
fn_var,
|
fn_var,
|
||||||
..
|
called_via,
|
||||||
} => {
|
} => {
|
||||||
// The expression that evaluates to the function being called, e.g. `foo` in
|
// The expression that evaluates to the function being called, e.g. `foo` in
|
||||||
// (foo) bar baz
|
// (foo) bar baz
|
||||||
|
@ -349,7 +349,7 @@ pub fn constrain_expr<'a>(
|
||||||
region,
|
region,
|
||||||
);
|
);
|
||||||
|
|
||||||
let category = Category::CallResult(opt_symbol);
|
let category = Category::CallResult(opt_symbol, *called_via);
|
||||||
|
|
||||||
let mut and_constraints = BumpVec::with_capacity_in(4, arena);
|
let mut and_constraints = BumpVec::with_capacity_in(4, arena);
|
||||||
|
|
||||||
|
@ -1919,7 +1919,7 @@ pub mod test_constrain {
|
||||||
aliases,
|
aliases,
|
||||||
};
|
};
|
||||||
|
|
||||||
let mut subs = Subs::new(var_store);
|
let mut subs = Subs::new_from_varstore(var_store);
|
||||||
|
|
||||||
for (var, name) in rigid_variables {
|
for (var, name) in rigid_variables {
|
||||||
subs.rigid_var(var, name);
|
subs.rigid_var(var, name);
|
||||||
|
|
|
@ -6,8 +6,8 @@ use crate::{
|
||||||
mem_pool::{pool::NodeId, pool_str::PoolStr, pool_vec::PoolVec},
|
mem_pool::{pool::NodeId, pool_str::PoolStr, pool_vec::PoolVec},
|
||||||
};
|
};
|
||||||
use roc_can::expr::Recursive;
|
use roc_can::expr::Recursive;
|
||||||
|
use roc_module::called_via::CalledVia;
|
||||||
use roc_module::low_level::LowLevel;
|
use roc_module::low_level::LowLevel;
|
||||||
use roc_module::operator::CalledVia;
|
|
||||||
use roc_module::symbol::Symbol;
|
use roc_module::symbol::Symbol;
|
||||||
|
|
||||||
use super::record_field::RecordField;
|
use super::record_field::RecordField;
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
use roc_module::{operator::CalledVia, symbol::Symbol};
|
use roc_module::{called_via::CalledVia, symbol::Symbol};
|
||||||
use roc_parse::ast::StrLiteral;
|
use roc_parse::ast::StrLiteral;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
use crate::mem_pool::pool::{NodeId, Pool};
|
use crate::mem_pool::pool::{NodeId, Pool};
|
||||||
use bumpalo::{collections::Vec as BumpVec, Bump};
|
use bumpalo::{collections::Vec as BumpVec, Bump};
|
||||||
use roc_collections::all::{MutMap, MutSet};
|
use roc_collections::all::{MutMap, MutSet};
|
||||||
use roc_module::ident::{Ident, ModuleName};
|
use roc_module::ident::{Ident, Lowercase, ModuleName};
|
||||||
use roc_module::symbol::{IdentIds, ModuleId, ModuleIds, Symbol};
|
use roc_module::symbol::{IdentIds, ModuleId, ModuleIds, Symbol};
|
||||||
use roc_problem::can::{Problem, RuntimeError};
|
use roc_problem::can::{Problem, RuntimeError};
|
||||||
use roc_region::all::{Located, Region};
|
use roc_region::all::{Located, Region};
|
||||||
|
@ -134,23 +134,37 @@ impl<'a> Env<'a> {
|
||||||
)),
|
)),
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
match self
|
match self.dep_idents.get(&module_id) {
|
||||||
.dep_idents
|
Some(exposed_ids) => match exposed_ids.get_id(&ident) {
|
||||||
.get(&module_id)
|
Some(ident_id) => {
|
||||||
.and_then(|exposed_ids| exposed_ids.get_id(&ident))
|
let symbol = Symbol::new(module_id, *ident_id);
|
||||||
{
|
|
||||||
Some(ident_id) => {
|
|
||||||
let symbol = Symbol::new(module_id, *ident_id);
|
|
||||||
|
|
||||||
self.qualified_lookups.insert(symbol);
|
self.qualified_lookups.insert(symbol);
|
||||||
|
|
||||||
Ok(symbol)
|
Ok(symbol)
|
||||||
|
}
|
||||||
|
None => {
|
||||||
|
let exposed_values = exposed_ids
|
||||||
|
.idents()
|
||||||
|
.filter(|(_, ident)| {
|
||||||
|
ident.as_ref().starts_with(|c: char| c.is_lowercase())
|
||||||
|
})
|
||||||
|
.map(|(_, ident)| Lowercase::from(ident.as_ref()))
|
||||||
|
.collect();
|
||||||
|
Err(RuntimeError::ValueNotExposed {
|
||||||
|
module_name,
|
||||||
|
ident,
|
||||||
|
region,
|
||||||
|
exposed_values,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
},
|
||||||
|
None => {
|
||||||
|
panic!(
|
||||||
|
"Module {} exists, but is not recorded in dep_idents",
|
||||||
|
module_name
|
||||||
|
)
|
||||||
}
|
}
|
||||||
None => Err(RuntimeError::ValueNotExposed {
|
|
||||||
module_name,
|
|
||||||
ident,
|
|
||||||
region,
|
|
||||||
}),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -295,6 +295,8 @@ fn spawn_rebuild_thread(
|
||||||
) -> std::thread::JoinHandle<u128> {
|
) -> std::thread::JoinHandle<u128> {
|
||||||
let thread_local_target = target.clone();
|
let thread_local_target = target.clone();
|
||||||
std::thread::spawn(move || {
|
std::thread::spawn(move || {
|
||||||
|
print!("🔨 Rebuilding host... ");
|
||||||
|
|
||||||
let rebuild_host_start = SystemTime::now();
|
let rebuild_host_start = SystemTime::now();
|
||||||
if !precompiled {
|
if !precompiled {
|
||||||
if surgically_link {
|
if surgically_link {
|
||||||
|
@ -322,6 +324,9 @@ fn spawn_rebuild_thread(
|
||||||
std::fs::copy(prehost, binary_path.as_path()).unwrap();
|
std::fs::copy(prehost, binary_path.as_path()).unwrap();
|
||||||
}
|
}
|
||||||
let rebuild_host_end = rebuild_host_start.elapsed().unwrap();
|
let rebuild_host_end = rebuild_host_start.elapsed().unwrap();
|
||||||
|
|
||||||
|
println!("Done!");
|
||||||
|
|
||||||
rebuild_host_end.as_millis()
|
rebuild_host_end.as_millis()
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,8 +2,8 @@ use bumpalo::collections::Vec;
|
||||||
use bumpalo::Bump;
|
use bumpalo::Bump;
|
||||||
use libloading::Library;
|
use libloading::Library;
|
||||||
use roc_gen_llvm::{run_jit_function, run_jit_function_dynamic_type};
|
use roc_gen_llvm::{run_jit_function, run_jit_function_dynamic_type};
|
||||||
|
use roc_module::called_via::CalledVia;
|
||||||
use roc_module::ident::TagName;
|
use roc_module::ident::TagName;
|
||||||
use roc_module::operator::CalledVia;
|
|
||||||
use roc_module::symbol::{Interns, ModuleId, Symbol};
|
use roc_module::symbol::{Interns, ModuleId, Symbol};
|
||||||
use roc_mono::ir::ProcLayout;
|
use roc_mono::ir::ProcLayout;
|
||||||
use roc_mono::layout::{union_sorted_tags_help, Builtin, Layout, UnionLayout, UnionVariant};
|
use roc_mono::layout::{union_sorted_tags_help, Builtin, Layout, UnionLayout, UnionVariant};
|
||||||
|
|
|
@ -1112,6 +1112,36 @@ pub fn listAny(
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn listAll(
|
||||||
|
list: RocList,
|
||||||
|
caller: Caller1,
|
||||||
|
data: Opaque,
|
||||||
|
inc_n_data: IncN,
|
||||||
|
data_is_owned: bool,
|
||||||
|
element_width: usize,
|
||||||
|
) callconv(.C) bool {
|
||||||
|
if (list.bytes) |source_ptr| {
|
||||||
|
const size = list.len();
|
||||||
|
|
||||||
|
if (data_is_owned) {
|
||||||
|
inc_n_data(data, size);
|
||||||
|
}
|
||||||
|
|
||||||
|
var i: usize = 0;
|
||||||
|
while (i < size) : (i += 1) {
|
||||||
|
var satisfied = false;
|
||||||
|
const element = source_ptr + i * element_width;
|
||||||
|
caller(data, element, @ptrCast(?[*]u8, &satisfied));
|
||||||
|
|
||||||
|
if (!satisfied) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
// SWAP ELEMENTS
|
// SWAP ELEMENTS
|
||||||
|
|
||||||
inline fn swapHelp(width: usize, temporary: [*]u8, ptr1: [*]u8, ptr2: [*]u8) void {
|
inline fn swapHelp(width: usize, temporary: [*]u8, ptr1: [*]u8, ptr2: [*]u8) void {
|
||||||
|
|
|
@ -51,6 +51,7 @@ comptime {
|
||||||
exportListFn(list.listSetInPlace, "set_in_place");
|
exportListFn(list.listSetInPlace, "set_in_place");
|
||||||
exportListFn(list.listSwap, "swap");
|
exportListFn(list.listSwap, "swap");
|
||||||
exportListFn(list.listAny, "any");
|
exportListFn(list.listAny, "any");
|
||||||
|
exportListFn(list.listAll, "all");
|
||||||
exportListFn(list.listFindUnsafe, "find_unsafe");
|
exportListFn(list.listFindUnsafe, "find_unsafe");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -691,6 +691,10 @@ all : List elem, (elem -> Bool) -> Bool
|
||||||
## any of the elements satisfy it.
|
## any of the elements satisfy it.
|
||||||
any : List elem, (elem -> Bool) -> Bool
|
any : List elem, (elem -> Bool) -> Bool
|
||||||
|
|
||||||
|
## Run the given predicate on each element of the list, returning `True` if
|
||||||
|
## all of the elements satisfy it.
|
||||||
|
all : List elem, (elem -> Bool) -> Bool
|
||||||
|
|
||||||
## Returns the first element of the list satisfying a predicate function.
|
## Returns the first element of the list satisfying a predicate function.
|
||||||
## If no satisfying element is found, an `Err NotFound` is returned.
|
## If no satisfying element is found, an `Err NotFound` is returned.
|
||||||
find : List elem, (elem -> Bool) -> Result elem [ NotFound ]*
|
find : List elem, (elem -> Bool) -> Result elem [ NotFound ]*
|
||||||
|
|
|
@ -196,6 +196,7 @@ pub const LIST_CONCAT: &str = "roc_builtins.list.concat";
|
||||||
pub const LIST_SET: &str = "roc_builtins.list.set";
|
pub const LIST_SET: &str = "roc_builtins.list.set";
|
||||||
pub const LIST_SET_IN_PLACE: &str = "roc_builtins.list.set_in_place";
|
pub const LIST_SET_IN_PLACE: &str = "roc_builtins.list.set_in_place";
|
||||||
pub const LIST_ANY: &str = "roc_builtins.list.any";
|
pub const LIST_ANY: &str = "roc_builtins.list.any";
|
||||||
|
pub const LIST_ALL: &str = "roc_builtins.list.all";
|
||||||
pub const LIST_FIND_UNSAFE: &str = "roc_builtins.list.find_unsafe";
|
pub const LIST_FIND_UNSAFE: &str = "roc_builtins.list.find_unsafe";
|
||||||
|
|
||||||
pub const DEC_FROM_F64: &str = "roc_builtins.dec.from_f64";
|
pub const DEC_FROM_F64: &str = "roc_builtins.dec.from_f64";
|
||||||
|
|
|
@ -1128,6 +1128,16 @@ pub fn types() -> MutMap<Symbol, (SolvedType, Region)> {
|
||||||
Box::new(bool_type()),
|
Box::new(bool_type()),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// all: List elem, (elem -> Bool) -> Bool
|
||||||
|
add_top_level_function_type!(
|
||||||
|
Symbol::LIST_ALL,
|
||||||
|
vec![
|
||||||
|
list_type(flex(TVAR1)),
|
||||||
|
closure(vec![flex(TVAR1)], TVAR2, Box::new(bool_type())),
|
||||||
|
],
|
||||||
|
Box::new(bool_type()),
|
||||||
|
);
|
||||||
|
|
||||||
// sortWith : List a, (a, a -> Ordering) -> List a
|
// sortWith : List a, (a, a -> Ordering) -> List a
|
||||||
add_top_level_function_type!(
|
add_top_level_function_type!(
|
||||||
Symbol::LIST_SORT_WITH,
|
Symbol::LIST_SORT_WITH,
|
||||||
|
@ -1159,6 +1169,13 @@ pub fn types() -> MutMap<Symbol, (SolvedType, Region)> {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// intersperse : List elem, elem -> List elem
|
||||||
|
add_top_level_function_type!(
|
||||||
|
Symbol::LIST_INTERSPERSE,
|
||||||
|
vec![list_type(flex(TVAR1)), flex(TVAR1)],
|
||||||
|
Box::new(list_type(flex(TVAR1))),
|
||||||
|
);
|
||||||
|
|
||||||
// Dict module
|
// Dict module
|
||||||
|
|
||||||
// len : Dict * * -> Nat
|
// len : Dict * * -> Nat
|
||||||
|
|
|
@ -3,9 +3,9 @@ use crate::expr::{ClosureData, Expr::*};
|
||||||
use crate::expr::{Expr, Field, Recursive, WhenBranch};
|
use crate::expr::{Expr, Field, Recursive, WhenBranch};
|
||||||
use crate::pattern::Pattern;
|
use crate::pattern::Pattern;
|
||||||
use roc_collections::all::SendMap;
|
use roc_collections::all::SendMap;
|
||||||
|
use roc_module::called_via::CalledVia;
|
||||||
use roc_module::ident::{Lowercase, TagName};
|
use roc_module::ident::{Lowercase, TagName};
|
||||||
use roc_module::low_level::LowLevel;
|
use roc_module::low_level::LowLevel;
|
||||||
use roc_module::operator::CalledVia;
|
|
||||||
use roc_module::symbol::Symbol;
|
use roc_module::symbol::Symbol;
|
||||||
use roc_region::all::{Located, Region};
|
use roc_region::all::{Located, Region};
|
||||||
use roc_types::subs::{VarStore, Variable};
|
use roc_types::subs::{VarStore, Variable};
|
||||||
|
@ -97,6 +97,7 @@ pub fn builtin_defs_map(symbol: Symbol, var_store: &mut VarStore) -> Option<Def>
|
||||||
LIST_TAKE_LAST => list_take_last,
|
LIST_TAKE_LAST => list_take_last,
|
||||||
LIST_SUBLIST => list_sublist,
|
LIST_SUBLIST => list_sublist,
|
||||||
LIST_SPLIT => list_split,
|
LIST_SPLIT => list_split,
|
||||||
|
LIST_INTERSPERSE => list_intersperse,
|
||||||
LIST_DROP => list_drop,
|
LIST_DROP => list_drop,
|
||||||
LIST_DROP_AT => list_drop_at,
|
LIST_DROP_AT => list_drop_at,
|
||||||
LIST_DROP_FIRST => list_drop_first,
|
LIST_DROP_FIRST => list_drop_first,
|
||||||
|
@ -112,6 +113,7 @@ pub fn builtin_defs_map(symbol: Symbol, var_store: &mut VarStore) -> Option<Def>
|
||||||
LIST_WALK_UNTIL => list_walk_until,
|
LIST_WALK_UNTIL => list_walk_until,
|
||||||
LIST_SORT_WITH => list_sort_with,
|
LIST_SORT_WITH => list_sort_with,
|
||||||
LIST_ANY => list_any,
|
LIST_ANY => list_any,
|
||||||
|
LIST_ALL => list_all,
|
||||||
LIST_FIND => list_find,
|
LIST_FIND => list_find,
|
||||||
DICT_LEN => dict_len,
|
DICT_LEN => dict_len,
|
||||||
DICT_EMPTY => dict_empty,
|
DICT_EMPTY => dict_empty,
|
||||||
|
@ -2148,6 +2150,84 @@ fn list_sublist(symbol: Symbol, var_store: &mut VarStore) -> Def {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// List.intersperse : List elem, elem -> List elem
|
||||||
|
fn list_intersperse(symbol: Symbol, var_store: &mut VarStore) -> Def {
|
||||||
|
let list_var = var_store.fresh();
|
||||||
|
let sep_var = var_store.fresh();
|
||||||
|
|
||||||
|
let list_sym = Symbol::ARG_1;
|
||||||
|
let sep_sym = Symbol::ARG_2;
|
||||||
|
|
||||||
|
let clos_var = var_store.fresh();
|
||||||
|
let clos_acc_var = var_store.fresh();
|
||||||
|
|
||||||
|
let clos_sym = Symbol::LIST_INTERSPERSE_CLOS;
|
||||||
|
let clos_acc_sym = Symbol::ARG_3;
|
||||||
|
let clos_elem_sym = Symbol::ARG_4;
|
||||||
|
|
||||||
|
let int_var = var_store.fresh();
|
||||||
|
let zero = int(int_var, Variable::NATURAL, 0);
|
||||||
|
|
||||||
|
// \acc, elem -> acc |> List.append sep |> List.append elem
|
||||||
|
let clos = Closure(ClosureData {
|
||||||
|
function_type: clos_var,
|
||||||
|
closure_type: var_store.fresh(),
|
||||||
|
closure_ext_var: var_store.fresh(),
|
||||||
|
return_type: clos_acc_var,
|
||||||
|
name: clos_sym,
|
||||||
|
recursive: Recursive::NotRecursive,
|
||||||
|
captured_symbols: vec![(sep_sym, sep_var)],
|
||||||
|
arguments: vec![
|
||||||
|
(clos_acc_var, no_region(Pattern::Identifier(clos_acc_sym))),
|
||||||
|
(sep_var, no_region(Pattern::Identifier(clos_elem_sym))),
|
||||||
|
],
|
||||||
|
loc_body: {
|
||||||
|
let append_sep = RunLowLevel {
|
||||||
|
op: LowLevel::ListAppend,
|
||||||
|
args: vec![(clos_acc_var, Var(clos_acc_sym)), (sep_var, Var(sep_sym))],
|
||||||
|
ret_var: clos_acc_var,
|
||||||
|
};
|
||||||
|
|
||||||
|
Box::new(no_region(RunLowLevel {
|
||||||
|
op: LowLevel::ListAppend,
|
||||||
|
args: vec![(clos_acc_var, append_sep), (sep_var, Var(clos_elem_sym))],
|
||||||
|
ret_var: clos_acc_var,
|
||||||
|
}))
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
// List.walk [] l (\acc, elem -> acc |> List.append sep |> List.append elem)
|
||||||
|
let acc = RunLowLevel {
|
||||||
|
op: LowLevel::ListWalk,
|
||||||
|
args: vec![
|
||||||
|
(list_var, Var(list_sym)),
|
||||||
|
(
|
||||||
|
clos_acc_var,
|
||||||
|
List {
|
||||||
|
elem_var: sep_var,
|
||||||
|
loc_elems: vec![],
|
||||||
|
},
|
||||||
|
),
|
||||||
|
(clos_var, clos),
|
||||||
|
],
|
||||||
|
ret_var: clos_acc_var,
|
||||||
|
};
|
||||||
|
|
||||||
|
let body = RunLowLevel {
|
||||||
|
op: LowLevel::ListDropAt,
|
||||||
|
args: vec![(clos_acc_var, acc), (int_var, zero)],
|
||||||
|
ret_var: clos_acc_var,
|
||||||
|
};
|
||||||
|
|
||||||
|
defn(
|
||||||
|
symbol,
|
||||||
|
vec![(list_var, list_sym), (sep_var, sep_sym)],
|
||||||
|
var_store,
|
||||||
|
body,
|
||||||
|
clos_acc_var,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
/// List.split : List elem, Nat -> { before: List elem, others: List elem }
|
/// List.split : List elem, Nat -> { before: List elem, others: List elem }
|
||||||
fn list_split(symbol: Symbol, var_store: &mut VarStore) -> Def {
|
fn list_split(symbol: Symbol, var_store: &mut VarStore) -> Def {
|
||||||
let list_var = var_store.fresh();
|
let list_var = var_store.fresh();
|
||||||
|
@ -2966,6 +3046,11 @@ fn list_any(symbol: Symbol, var_store: &mut VarStore) -> Def {
|
||||||
lowlevel_2(symbol, LowLevel::ListAny, var_store)
|
lowlevel_2(symbol, LowLevel::ListAny, var_store)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// List.all: List elem, (elem -> Bool) -> Bool
|
||||||
|
fn list_all(symbol: Symbol, var_store: &mut VarStore) -> Def {
|
||||||
|
lowlevel_2(symbol, LowLevel::ListAll, var_store)
|
||||||
|
}
|
||||||
|
|
||||||
/// List.find : List elem, (elem -> Bool) -> Result elem [ NotFound ]*
|
/// List.find : List elem, (elem -> Bool) -> Result elem [ NotFound ]*
|
||||||
fn list_find(symbol: Symbol, var_store: &mut VarStore) -> Def {
|
fn list_find(symbol: Symbol, var_store: &mut VarStore) -> Def {
|
||||||
let list = Symbol::ARG_1;
|
let list = Symbol::ARG_1;
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
use crate::procedure::References;
|
use crate::procedure::References;
|
||||||
use roc_collections::all::{MutMap, MutSet};
|
use roc_collections::all::{MutMap, MutSet};
|
||||||
use roc_module::ident::{Ident, ModuleName};
|
use roc_module::ident::{Ident, Lowercase, ModuleName};
|
||||||
use roc_module::symbol::{IdentIds, ModuleId, ModuleIds, Symbol};
|
use roc_module::symbol::{IdentIds, ModuleId, ModuleIds, Symbol};
|
||||||
use roc_problem::can::{Problem, RuntimeError};
|
use roc_problem::can::{Problem, RuntimeError};
|
||||||
use roc_region::all::{Located, Region};
|
use roc_region::all::{Located, Region};
|
||||||
|
@ -99,23 +99,37 @@ impl<'a> Env<'a> {
|
||||||
)),
|
)),
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
match self
|
match self.dep_idents.get(&module_id) {
|
||||||
.dep_idents
|
Some(exposed_ids) => match exposed_ids.get_id(&ident) {
|
||||||
.get(&module_id)
|
Some(ident_id) => {
|
||||||
.and_then(|exposed_ids| exposed_ids.get_id(&ident))
|
let symbol = Symbol::new(module_id, *ident_id);
|
||||||
{
|
|
||||||
Some(ident_id) => {
|
|
||||||
let symbol = Symbol::new(module_id, *ident_id);
|
|
||||||
|
|
||||||
self.qualified_lookups.insert(symbol);
|
self.qualified_lookups.insert(symbol);
|
||||||
|
|
||||||
Ok(symbol)
|
Ok(symbol)
|
||||||
|
}
|
||||||
|
None => {
|
||||||
|
let exposed_values = exposed_ids
|
||||||
|
.idents()
|
||||||
|
.filter(|(_, ident)| {
|
||||||
|
ident.as_ref().starts_with(|c: char| c.is_lowercase())
|
||||||
|
})
|
||||||
|
.map(|(_, ident)| Lowercase::from(ident.as_ref()))
|
||||||
|
.collect();
|
||||||
|
Err(RuntimeError::ValueNotExposed {
|
||||||
|
module_name,
|
||||||
|
ident,
|
||||||
|
region,
|
||||||
|
exposed_values,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
},
|
||||||
|
None => {
|
||||||
|
panic!(
|
||||||
|
"Module {} exists, but is not recorded in dep_idents",
|
||||||
|
module_name
|
||||||
|
)
|
||||||
}
|
}
|
||||||
None => Err(RuntimeError::ValueNotExposed {
|
|
||||||
module_name,
|
|
||||||
ident,
|
|
||||||
region,
|
|
||||||
}),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,9 +10,9 @@ use crate::pattern::{canonicalize_pattern, Pattern};
|
||||||
use crate::procedure::References;
|
use crate::procedure::References;
|
||||||
use crate::scope::Scope;
|
use crate::scope::Scope;
|
||||||
use roc_collections::all::{ImSet, MutMap, MutSet, SendMap};
|
use roc_collections::all::{ImSet, MutMap, MutSet, SendMap};
|
||||||
|
use roc_module::called_via::CalledVia;
|
||||||
use roc_module::ident::{ForeignSymbol, Lowercase, TagName};
|
use roc_module::ident::{ForeignSymbol, Lowercase, TagName};
|
||||||
use roc_module::low_level::LowLevel;
|
use roc_module::low_level::LowLevel;
|
||||||
use roc_module::operator::CalledVia;
|
|
||||||
use roc_module::symbol::Symbol;
|
use roc_module::symbol::Symbol;
|
||||||
use roc_parse::ast::{self, EscapedChar, StrLiteral};
|
use roc_parse::ast::{self, EscapedChar, StrLiteral};
|
||||||
use roc_parse::pattern::PatternType::*;
|
use roc_parse::pattern::PatternType::*;
|
||||||
|
@ -1711,7 +1711,7 @@ fn desugar_str_segments(var_store: &mut VarStore, segments: Vec<StrSegment>) ->
|
||||||
(var_store.fresh(), loc_new_expr),
|
(var_store.fresh(), loc_new_expr),
|
||||||
(var_store.fresh(), loc_expr),
|
(var_store.fresh(), loc_expr),
|
||||||
],
|
],
|
||||||
CalledVia::Space,
|
CalledVia::StringInterpolation,
|
||||||
);
|
);
|
||||||
|
|
||||||
loc_expr = Located::new(0, 0, 0, 0, expr);
|
loc_expr = Located::new(0, 0, 0, 0, expr);
|
||||||
|
|
|
@ -2,9 +2,9 @@
|
||||||
|
|
||||||
use bumpalo::collections::Vec;
|
use bumpalo::collections::Vec;
|
||||||
use bumpalo::Bump;
|
use bumpalo::Bump;
|
||||||
|
use roc_module::called_via::BinOp::Pizza;
|
||||||
|
use roc_module::called_via::{BinOp, CalledVia};
|
||||||
use roc_module::ident::ModuleName;
|
use roc_module::ident::ModuleName;
|
||||||
use roc_module::operator::BinOp::Pizza;
|
|
||||||
use roc_module::operator::{BinOp, CalledVia};
|
|
||||||
use roc_parse::ast::Expr::{self, *};
|
use roc_parse::ast::Expr::{self, *};
|
||||||
use roc_parse::ast::{AssignedField, Def, WhenBranch};
|
use roc_parse::ast::{AssignedField, Def, WhenBranch};
|
||||||
use roc_region::all::{Located, Region};
|
use roc_region::all::{Located, Region};
|
||||||
|
@ -277,7 +277,7 @@ pub fn desugar_expr<'a>(arena: &'a Bump, loc_expr: &'a Located<Expr<'a>>) -> &'a
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
UnaryOp(loc_arg, loc_op) => {
|
UnaryOp(loc_arg, loc_op) => {
|
||||||
use roc_module::operator::UnaryOp::*;
|
use roc_module::called_via::UnaryOp::*;
|
||||||
|
|
||||||
let region = loc_op.region;
|
let region = loc_op.region;
|
||||||
let op = loc_op.value;
|
let op = loc_op.value;
|
||||||
|
@ -475,7 +475,7 @@ fn binop_step<'a>(
|
||||||
op_stack: &mut Vec<Located<BinOp>>,
|
op_stack: &mut Vec<Located<BinOp>>,
|
||||||
next_op: Located<BinOp>,
|
next_op: Located<BinOp>,
|
||||||
) -> Step<'a> {
|
) -> Step<'a> {
|
||||||
use roc_module::operator::Associativity::*;
|
use roc_module::called_via::Associativity::*;
|
||||||
use std::cmp::Ordering;
|
use std::cmp::Ordering;
|
||||||
|
|
||||||
match op_stack.pop() {
|
match op_stack.pop() {
|
||||||
|
|
|
@ -254,7 +254,7 @@ pub fn constrain_expr(
|
||||||
exists(vec![*elem_var], And(constraints))
|
exists(vec![*elem_var], And(constraints))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Call(boxed, loc_args, _application_style) => {
|
Call(boxed, loc_args, called_via) => {
|
||||||
let (fn_var, loc_fn, closure_var, ret_var) = &**boxed;
|
let (fn_var, loc_fn, closure_var, ret_var) = &**boxed;
|
||||||
// The expression that evaluates to the function being called, e.g. `foo` in
|
// The expression that evaluates to the function being called, e.g. `foo` in
|
||||||
// (foo) bar baz
|
// (foo) bar baz
|
||||||
|
@ -317,7 +317,7 @@ pub fn constrain_expr(
|
||||||
region,
|
region,
|
||||||
);
|
);
|
||||||
|
|
||||||
let category = Category::CallResult(opt_symbol);
|
let category = Category::CallResult(opt_symbol, *called_via);
|
||||||
|
|
||||||
exists(
|
exists(
|
||||||
vars,
|
vars,
|
||||||
|
|
|
@ -3,7 +3,7 @@ use crate::def::fmt_def;
|
||||||
use crate::pattern::fmt_pattern;
|
use crate::pattern::fmt_pattern;
|
||||||
use crate::spaces::{add_spaces, fmt_comments_only, fmt_spaces, newline, NewlineAt, INDENT};
|
use crate::spaces::{add_spaces, fmt_comments_only, fmt_spaces, newline, NewlineAt, INDENT};
|
||||||
use bumpalo::collections::String;
|
use bumpalo::collections::String;
|
||||||
use roc_module::operator::{self, BinOp};
|
use roc_module::called_via::{self, BinOp};
|
||||||
use roc_parse::ast::StrSegment;
|
use roc_parse::ast::StrSegment;
|
||||||
use roc_parse::ast::{
|
use roc_parse::ast::{
|
||||||
AssignedField, Base, Collection, CommentOrNewline, Expr, Pattern, WhenBranch,
|
AssignedField, Base, Collection, CommentOrNewline, Expr, Pattern, WhenBranch,
|
||||||
|
@ -296,10 +296,10 @@ impl<'a> Formattable<'a> for Expr<'a> {
|
||||||
BinOps(lefts, right) => fmt_bin_ops(buf, lefts, right, false, parens, indent),
|
BinOps(lefts, right) => fmt_bin_ops(buf, lefts, right, false, parens, indent),
|
||||||
UnaryOp(sub_expr, unary_op) => {
|
UnaryOp(sub_expr, unary_op) => {
|
||||||
match &unary_op.value {
|
match &unary_op.value {
|
||||||
operator::UnaryOp::Negate => {
|
called_via::UnaryOp::Negate => {
|
||||||
buf.push('-');
|
buf.push('-');
|
||||||
}
|
}
|
||||||
operator::UnaryOp::Not => {
|
called_via::UnaryOp::Not => {
|
||||||
buf.push('!');
|
buf.push('!');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -354,26 +354,26 @@ fn format_str_segment<'a>(seg: &StrSegment<'a>, buf: &mut String<'a>, indent: u1
|
||||||
|
|
||||||
fn push_op(buf: &mut String, op: BinOp) {
|
fn push_op(buf: &mut String, op: BinOp) {
|
||||||
match op {
|
match op {
|
||||||
operator::BinOp::Caret => buf.push('^'),
|
called_via::BinOp::Caret => buf.push('^'),
|
||||||
operator::BinOp::Star => buf.push('*'),
|
called_via::BinOp::Star => buf.push('*'),
|
||||||
operator::BinOp::Slash => buf.push('/'),
|
called_via::BinOp::Slash => buf.push('/'),
|
||||||
operator::BinOp::DoubleSlash => buf.push_str("//"),
|
called_via::BinOp::DoubleSlash => buf.push_str("//"),
|
||||||
operator::BinOp::Percent => buf.push('%'),
|
called_via::BinOp::Percent => buf.push('%'),
|
||||||
operator::BinOp::DoublePercent => buf.push_str("%%"),
|
called_via::BinOp::DoublePercent => buf.push_str("%%"),
|
||||||
operator::BinOp::Plus => buf.push('+'),
|
called_via::BinOp::Plus => buf.push('+'),
|
||||||
operator::BinOp::Minus => buf.push('-'),
|
called_via::BinOp::Minus => buf.push('-'),
|
||||||
operator::BinOp::Equals => buf.push_str("=="),
|
called_via::BinOp::Equals => buf.push_str("=="),
|
||||||
operator::BinOp::NotEquals => buf.push_str("!="),
|
called_via::BinOp::NotEquals => buf.push_str("!="),
|
||||||
operator::BinOp::LessThan => buf.push('<'),
|
called_via::BinOp::LessThan => buf.push('<'),
|
||||||
operator::BinOp::GreaterThan => buf.push('>'),
|
called_via::BinOp::GreaterThan => buf.push('>'),
|
||||||
operator::BinOp::LessThanOrEq => buf.push_str("<="),
|
called_via::BinOp::LessThanOrEq => buf.push_str("<="),
|
||||||
operator::BinOp::GreaterThanOrEq => buf.push_str(">="),
|
called_via::BinOp::GreaterThanOrEq => buf.push_str(">="),
|
||||||
operator::BinOp::And => buf.push_str("&&"),
|
called_via::BinOp::And => buf.push_str("&&"),
|
||||||
operator::BinOp::Or => buf.push_str("||"),
|
called_via::BinOp::Or => buf.push_str("||"),
|
||||||
operator::BinOp::Pizza => buf.push_str("|>"),
|
called_via::BinOp::Pizza => buf.push_str("|>"),
|
||||||
operator::BinOp::Assignment => unreachable!(),
|
called_via::BinOp::Assignment => unreachable!(),
|
||||||
operator::BinOp::HasType => unreachable!(),
|
called_via::BinOp::HasType => unreachable!(),
|
||||||
operator::BinOp::Backpassing => unreachable!(),
|
called_via::BinOp::Backpassing => unreachable!(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1634,6 +1634,34 @@ mod tests {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_sub_reg64_reg64() {
|
||||||
|
let arena = bumpalo::Bump::new();
|
||||||
|
let mut buf = bumpalo::vec![in &arena];
|
||||||
|
for ((dst, src), expected) in &[
|
||||||
|
(
|
||||||
|
(X86_64GeneralReg::RAX, X86_64GeneralReg::RAX),
|
||||||
|
[0x48, 0x29, 0xC0],
|
||||||
|
),
|
||||||
|
(
|
||||||
|
(X86_64GeneralReg::RAX, X86_64GeneralReg::R15),
|
||||||
|
[0x4C, 0x29, 0xF8],
|
||||||
|
),
|
||||||
|
(
|
||||||
|
(X86_64GeneralReg::R15, X86_64GeneralReg::RAX),
|
||||||
|
[0x49, 0x29, 0xC7],
|
||||||
|
),
|
||||||
|
(
|
||||||
|
(X86_64GeneralReg::R15, X86_64GeneralReg::R15),
|
||||||
|
[0x4D, 0x29, 0xFF],
|
||||||
|
),
|
||||||
|
] {
|
||||||
|
buf.clear();
|
||||||
|
sub_reg64_reg64(&mut buf, *dst, *src);
|
||||||
|
assert_eq!(expected, &buf[..]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_addsd_freg64_freg64() {
|
fn test_addsd_freg64_freg64() {
|
||||||
let arena = bumpalo::Bump::new();
|
let arena = bumpalo::Bump::new();
|
||||||
|
|
|
@ -8,11 +8,11 @@ use crate::llvm::build_dict::{
|
||||||
};
|
};
|
||||||
use crate::llvm::build_hash::generic_hash;
|
use crate::llvm::build_hash::generic_hash;
|
||||||
use crate::llvm::build_list::{
|
use crate::llvm::build_list::{
|
||||||
self, allocate_list, empty_list, empty_polymorphic_list, list_any, list_append, list_concat,
|
self, allocate_list, empty_list, empty_polymorphic_list, list_all, list_any, list_append,
|
||||||
list_contains, list_drop_at, list_find_trivial_not_found, list_find_unsafe, list_get_unsafe,
|
list_concat, list_contains, list_drop_at, list_find_trivial_not_found, list_find_unsafe,
|
||||||
list_join, list_keep_errs, list_keep_if, list_keep_oks, list_len, list_map, list_map2,
|
list_get_unsafe, list_join, list_keep_errs, list_keep_if, list_keep_oks, list_len, list_map,
|
||||||
list_map3, list_map4, list_map_with_index, list_prepend, list_range, list_repeat, list_reverse,
|
list_map2, list_map3, list_map4, list_map_with_index, list_prepend, list_range, list_repeat,
|
||||||
list_set, list_single, list_sort_with, list_sublist, list_swap,
|
list_reverse, list_set, list_single, list_sort_with, list_sublist, list_swap,
|
||||||
};
|
};
|
||||||
use crate::llvm::build_str::{
|
use crate::llvm::build_str::{
|
||||||
empty_str, str_concat, str_count_graphemes, str_ends_with, str_from_float, str_from_int,
|
empty_str, str_concat, str_count_graphemes, str_ends_with, str_from_float, str_from_int,
|
||||||
|
@ -4136,7 +4136,7 @@ fn build_proc_header<'a, 'ctx, 'env>(
|
||||||
env.module,
|
env.module,
|
||||||
fn_name.as_str(),
|
fn_name.as_str(),
|
||||||
fn_type,
|
fn_type,
|
||||||
Linkage::Private,
|
Linkage::Internal,
|
||||||
FAST_CALL_CONV,
|
FAST_CALL_CONV,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -5141,6 +5141,33 @@ fn run_higher_order_low_level<'a, 'ctx, 'env>(
|
||||||
_ => unreachable!("invalid list layout"),
|
_ => unreachable!("invalid list layout"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
ListAll { xs } => {
|
||||||
|
let (list, list_layout) = load_symbol_and_layout(scope, xs);
|
||||||
|
let (function, closure, closure_layout) = function_details!();
|
||||||
|
|
||||||
|
match list_layout {
|
||||||
|
Layout::Builtin(Builtin::EmptyList) => {
|
||||||
|
env.context.bool_type().const_int(1, false).into()
|
||||||
|
}
|
||||||
|
Layout::Builtin(Builtin::List(element_layout)) => {
|
||||||
|
let argument_layouts = &[**element_layout];
|
||||||
|
|
||||||
|
let roc_function_call = roc_function_call(
|
||||||
|
env,
|
||||||
|
layout_ids,
|
||||||
|
function,
|
||||||
|
closure,
|
||||||
|
closure_layout,
|
||||||
|
function_owns_closure_data,
|
||||||
|
argument_layouts,
|
||||||
|
Layout::Builtin(Builtin::Int1),
|
||||||
|
);
|
||||||
|
|
||||||
|
list_all(env, roc_function_call, list, element_layout)
|
||||||
|
}
|
||||||
|
_ => unreachable!("invalid list layout"),
|
||||||
|
}
|
||||||
|
}
|
||||||
ListFindUnsafe { xs } => {
|
ListFindUnsafe { xs } => {
|
||||||
let (list, list_layout) = load_symbol_and_layout(scope, xs);
|
let (list, list_layout) = load_symbol_and_layout(scope, xs);
|
||||||
|
|
||||||
|
@ -6041,7 +6068,7 @@ fn run_low_level<'a, 'ctx, 'env>(
|
||||||
|
|
||||||
ListMap | ListMap2 | ListMap3 | ListMap4 | ListMapWithIndex | ListKeepIf | ListWalk
|
ListMap | ListMap2 | ListMap3 | ListMap4 | ListMapWithIndex | ListKeepIf | ListWalk
|
||||||
| ListWalkUntil | ListWalkBackwards | ListKeepOks | ListKeepErrs | ListSortWith
|
| ListWalkUntil | ListWalkBackwards | ListKeepOks | ListKeepErrs | ListSortWith
|
||||||
| ListAny | ListFindUnsafe | DictWalk => {
|
| ListAny | ListAll | ListFindUnsafe | DictWalk => {
|
||||||
unreachable!("these are higher order, and are handled elsewhere")
|
unreachable!("these are higher order, and are handled elsewhere")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -6213,7 +6240,7 @@ fn build_foreign_symbol<'a, 'ctx, 'env>(
|
||||||
env.module,
|
env.module,
|
||||||
&fastcc_function_name,
|
&fastcc_function_name,
|
||||||
fastcc_type,
|
fastcc_type,
|
||||||
Linkage::Private,
|
Linkage::Internal,
|
||||||
FAST_CALL_CONV,
|
FAST_CALL_CONV,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
|
@ -926,6 +926,27 @@ pub fn list_any<'a, 'ctx, 'env>(
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// List.all : List elem, \(elem -> Bool) -> Bool
|
||||||
|
pub fn list_all<'a, 'ctx, 'env>(
|
||||||
|
env: &Env<'a, 'ctx, 'env>,
|
||||||
|
roc_function_call: RocFunctionCall<'ctx>,
|
||||||
|
list: BasicValueEnum<'ctx>,
|
||||||
|
element_layout: &Layout<'a>,
|
||||||
|
) -> BasicValueEnum<'ctx> {
|
||||||
|
call_bitcode_fn(
|
||||||
|
env,
|
||||||
|
&[
|
||||||
|
pass_list_cc(env, list),
|
||||||
|
roc_function_call.caller.into(),
|
||||||
|
pass_as_opaque(env, roc_function_call.data),
|
||||||
|
roc_function_call.inc_n_data.into(),
|
||||||
|
roc_function_call.data_is_owned.into(),
|
||||||
|
layout_width(env, element_layout),
|
||||||
|
],
|
||||||
|
bitcode::LIST_ALL,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
/// List.findUnsafe : List elem, (elem -> Bool) -> { value: elem, found: bool }
|
/// List.findUnsafe : List elem, (elem -> Bool) -> { value: elem, found: bool }
|
||||||
pub fn list_find_unsafe<'a, 'ctx, 'env>(
|
pub fn list_find_unsafe<'a, 'ctx, 'env>(
|
||||||
env: &Env<'a, 'ctx, 'env>,
|
env: &Env<'a, 'ctx, 'env>,
|
||||||
|
|
|
@ -183,7 +183,7 @@ impl<'ctx> PointerToRefcount<'ctx> {
|
||||||
env.module,
|
env.module,
|
||||||
fn_name,
|
fn_name,
|
||||||
fn_type,
|
fn_type,
|
||||||
Linkage::Private,
|
Linkage::Internal,
|
||||||
FAST_CALL_CONV, // Because it's an internal-only function, it should use the fast calling convention.
|
FAST_CALL_CONV, // Because it's an internal-only function, it should use the fast calling convention.
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
|
@ -28,17 +28,38 @@ pub fn decode_low_level<'a>(
|
||||||
|
|
||||||
match lowlevel {
|
match lowlevel {
|
||||||
StrConcat => return BuiltinCall(bitcode::STR_CONCAT),
|
StrConcat => return BuiltinCall(bitcode::STR_CONCAT),
|
||||||
|
StrJoinWith => return NotImplemented, // needs Array
|
||||||
|
StrIsEmpty => {
|
||||||
|
code_builder.i64_const(i64::MIN);
|
||||||
|
code_builder.i64_eq();
|
||||||
|
}
|
||||||
|
StrStartsWith => return BuiltinCall(bitcode::STR_STARTS_WITH),
|
||||||
|
StrStartsWithCodePt => return BuiltinCall(bitcode::STR_STARTS_WITH_CODE_PT),
|
||||||
|
StrEndsWith => return BuiltinCall(bitcode::STR_ENDS_WITH),
|
||||||
|
StrSplit => return NotImplemented, // needs Array
|
||||||
|
StrCountGraphemes => return NotImplemented, // test needs Array
|
||||||
|
StrFromInt => return NotImplemented, // choose builtin based on storage size
|
||||||
|
StrFromUtf8 => return NotImplemented, // needs Array
|
||||||
|
StrTrimLeft => return BuiltinCall(bitcode::STR_TRIM_LEFT),
|
||||||
|
StrTrimRight => return BuiltinCall(bitcode::STR_TRIM_RIGHT),
|
||||||
|
StrFromUtf8Range => return NotImplemented, // needs Array
|
||||||
|
StrToUtf8 => return NotImplemented, // needs Array
|
||||||
|
StrRepeat => return BuiltinCall(bitcode::STR_REPEAT),
|
||||||
|
StrFromFloat => {
|
||||||
|
// linker errors for __ashlti3, __fixunsdfti, __multi3, __udivti3, __umodti3
|
||||||
|
// https://gcc.gnu.org/onlinedocs/gccint/Integer-library-routines.html
|
||||||
|
// https://gcc.gnu.org/onlinedocs/gccint/Soft-float-library-routines.html
|
||||||
|
return NotImplemented;
|
||||||
|
}
|
||||||
|
StrTrim => return BuiltinCall(bitcode::STR_TRIM),
|
||||||
|
|
||||||
StrJoinWith | StrIsEmpty | StrStartsWith | StrStartsWithCodePt | StrEndsWith | StrSplit
|
ListLen | ListGetUnsafe | ListSet | ListSingle | ListRepeat | ListReverse | ListConcat
|
||||||
| StrCountGraphemes | StrFromInt | StrFromUtf8 | StrTrimLeft | StrTrimRight
|
|
||||||
| StrFromUtf8Range | StrToUtf8 | StrRepeat | StrFromFloat | StrTrim | ListLen
|
|
||||||
| ListGetUnsafe | ListSet | ListSingle | ListRepeat | ListReverse | ListConcat
|
|
||||||
| ListContains | ListAppend | ListPrepend | ListJoin | ListRange | ListMap | ListMap2
|
| ListContains | ListAppend | ListPrepend | ListJoin | ListRange | ListMap | ListMap2
|
||||||
| ListMap3 | ListMap4 | ListMapWithIndex | ListKeepIf | ListWalk | ListWalkUntil
|
| ListMap3 | ListMap4 | ListMapWithIndex | ListKeepIf | ListWalk | ListWalkUntil
|
||||||
| ListWalkBackwards | ListKeepOks | ListKeepErrs | ListSortWith | ListSublist
|
| ListWalkBackwards | ListKeepOks | ListKeepErrs | ListSortWith | ListSublist
|
||||||
| ListDropAt | ListSwap | ListAny | ListFindUnsafe | DictSize | DictEmpty | DictInsert
|
| ListDropAt | ListSwap | ListAny | ListAll | ListFindUnsafe | DictSize | DictEmpty
|
||||||
| DictRemove | DictContains | DictGetUnsafe | DictKeys | DictValues | DictUnion
|
| DictInsert | DictRemove | DictContains | DictGetUnsafe | DictKeys | DictValues
|
||||||
| DictIntersection | DictDifference | DictWalk | SetFromList => {
|
| DictUnion | DictIntersection | DictDifference | DictWalk | SetFromList => {
|
||||||
return NotImplemented;
|
return NotImplemented;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -5,8 +5,8 @@ use roc_can::expr::{ClosureData, Expr, Recursive};
|
||||||
use roc_can::pattern::Pattern;
|
use roc_can::pattern::Pattern;
|
||||||
use roc_can::scope::Scope;
|
use roc_can::scope::Scope;
|
||||||
use roc_collections::all::{MutSet, SendMap};
|
use roc_collections::all::{MutSet, SendMap};
|
||||||
|
use roc_module::called_via::CalledVia;
|
||||||
use roc_module::ident::TagName;
|
use roc_module::ident::TagName;
|
||||||
use roc_module::operator::CalledVia;
|
|
||||||
use roc_module::symbol::Symbol;
|
use roc_module::symbol::Symbol;
|
||||||
use roc_region::all::{Located, Region};
|
use roc_region::all::{Located, Region};
|
||||||
use roc_types::subs::{VarStore, Variable};
|
use roc_types::subs::{VarStore, Variable};
|
||||||
|
|
|
@ -19,8 +19,7 @@ use roc_module::symbol::{
|
||||||
Symbol,
|
Symbol,
|
||||||
};
|
};
|
||||||
use roc_mono::ir::{
|
use roc_mono::ir::{
|
||||||
CapturedSymbols, EntryPoint, ExternalSpecializations, PartialProc, PendingSpecialization, Proc,
|
CapturedSymbols, EntryPoint, ExternalSpecializations, PartialProc, Proc, ProcLayout, Procs,
|
||||||
ProcLayout, Procs,
|
|
||||||
};
|
};
|
||||||
use roc_mono::layout::{Layout, LayoutCache, LayoutProblem};
|
use roc_mono::layout::{Layout, LayoutCache, LayoutProblem};
|
||||||
use roc_parse::ast::{self, StrLiteral, TypeAnnotation};
|
use roc_parse::ast::{self, StrLiteral, TypeAnnotation};
|
||||||
|
@ -356,7 +355,7 @@ struct ModuleCache<'a> {
|
||||||
constrained: MutMap<ModuleId, ConstrainedModule>,
|
constrained: MutMap<ModuleId, ConstrainedModule>,
|
||||||
typechecked: MutMap<ModuleId, TypeCheckedModule<'a>>,
|
typechecked: MutMap<ModuleId, TypeCheckedModule<'a>>,
|
||||||
found_specializations: MutMap<ModuleId, FoundSpecializationsModule<'a>>,
|
found_specializations: MutMap<ModuleId, FoundSpecializationsModule<'a>>,
|
||||||
external_specializations_requested: MutMap<ModuleId, ExternalSpecializations<'a>>,
|
external_specializations_requested: MutMap<ModuleId, Vec<ExternalSpecializations>>,
|
||||||
|
|
||||||
/// Various information
|
/// Various information
|
||||||
imports: MutMap<ModuleId, MutSet<ModuleId>>,
|
imports: MutMap<ModuleId, MutSet<ModuleId>>,
|
||||||
|
@ -587,7 +586,7 @@ fn start_phase<'a>(
|
||||||
.module_cache
|
.module_cache
|
||||||
.external_specializations_requested
|
.external_specializations_requested
|
||||||
.remove(&module_id)
|
.remove(&module_id)
|
||||||
.unwrap_or_else(|| ExternalSpecializations::new_in(arena));
|
.unwrap_or_default();
|
||||||
|
|
||||||
let FoundSpecializationsModule {
|
let FoundSpecializationsModule {
|
||||||
module_id,
|
module_id,
|
||||||
|
@ -831,7 +830,7 @@ enum Msg<'a> {
|
||||||
module_id: ModuleId,
|
module_id: ModuleId,
|
||||||
ident_ids: IdentIds,
|
ident_ids: IdentIds,
|
||||||
layout_cache: LayoutCache<'a>,
|
layout_cache: LayoutCache<'a>,
|
||||||
external_specializations_requested: BumpMap<ModuleId, ExternalSpecializations<'a>>,
|
external_specializations_requested: BumpMap<ModuleId, ExternalSpecializations>,
|
||||||
procedures: MutMap<(Symbol, ProcLayout<'a>), Proc<'a>>,
|
procedures: MutMap<(Symbol, ProcLayout<'a>), Proc<'a>>,
|
||||||
problems: Vec<roc_mono::ir::MonoProblem>,
|
problems: Vec<roc_mono::ir::MonoProblem>,
|
||||||
module_timing: ModuleTiming,
|
module_timing: ModuleTiming,
|
||||||
|
@ -911,9 +910,6 @@ struct State<'a> {
|
||||||
/// pending specializations in the same thread.
|
/// pending specializations in the same thread.
|
||||||
pub needs_specialization: MutSet<ModuleId>,
|
pub needs_specialization: MutSet<ModuleId>,
|
||||||
|
|
||||||
pub all_pending_specializations:
|
|
||||||
MutMap<Symbol, MutMap<ProcLayout<'a>, PendingSpecialization<'a>>>,
|
|
||||||
|
|
||||||
pub specializations_in_flight: u32,
|
pub specializations_in_flight: u32,
|
||||||
|
|
||||||
pub timings: MutMap<ModuleId, ModuleTiming>,
|
pub timings: MutMap<ModuleId, ModuleTiming>,
|
||||||
|
@ -1054,7 +1050,7 @@ enum BuildTask<'a> {
|
||||||
subs: Subs,
|
subs: Subs,
|
||||||
procs_base: ProcsBase<'a>,
|
procs_base: ProcsBase<'a>,
|
||||||
layout_cache: LayoutCache<'a>,
|
layout_cache: LayoutCache<'a>,
|
||||||
specializations_we_must_make: ExternalSpecializations<'a>,
|
specializations_we_must_make: Vec<ExternalSpecializations>,
|
||||||
module_timing: ModuleTiming,
|
module_timing: ModuleTiming,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
@ -1538,7 +1534,6 @@ where
|
||||||
unsolved_modules: MutMap::default(),
|
unsolved_modules: MutMap::default(),
|
||||||
timings: MutMap::default(),
|
timings: MutMap::default(),
|
||||||
needs_specialization: MutSet::default(),
|
needs_specialization: MutSet::default(),
|
||||||
all_pending_specializations: MutMap::default(),
|
|
||||||
specializations_in_flight: 0,
|
specializations_in_flight: 0,
|
||||||
layout_caches: std::vec::Vec::with_capacity(num_cpus::get()),
|
layout_caches: std::vec::Vec::with_capacity(num_cpus::get()),
|
||||||
procs: Procs::new_in(arena),
|
procs: Procs::new_in(arena),
|
||||||
|
@ -2067,17 +2062,6 @@ fn update<'a>(
|
||||||
log!("found specializations for {:?}", module_id);
|
log!("found specializations for {:?}", module_id);
|
||||||
let subs = solved_subs.into_inner();
|
let subs = solved_subs.into_inner();
|
||||||
|
|
||||||
for (symbol, specs) in &procs_base.specializations_for_host {
|
|
||||||
let existing = match state.all_pending_specializations.entry(*symbol) {
|
|
||||||
Vacant(entry) => entry.insert(MutMap::default()),
|
|
||||||
Occupied(entry) => entry.into_mut(),
|
|
||||||
};
|
|
||||||
|
|
||||||
for (layout, pend) in specs {
|
|
||||||
existing.insert(*layout, pend.clone());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
state
|
state
|
||||||
.module_cache
|
.module_cache
|
||||||
.top_level_thunks
|
.top_level_thunks
|
||||||
|
@ -2171,11 +2155,11 @@ fn update<'a>(
|
||||||
.external_specializations_requested
|
.external_specializations_requested
|
||||||
.entry(module_id)
|
.entry(module_id)
|
||||||
{
|
{
|
||||||
Vacant(entry) => entry.insert(ExternalSpecializations::new_in(arena)),
|
Vacant(entry) => entry.insert(vec![]),
|
||||||
Occupied(entry) => entry.into_mut(),
|
Occupied(entry) => entry.into_mut(),
|
||||||
};
|
};
|
||||||
|
|
||||||
existing.extend(requested);
|
existing.push(requested);
|
||||||
}
|
}
|
||||||
|
|
||||||
msg_tx
|
msg_tx
|
||||||
|
@ -2198,11 +2182,11 @@ fn update<'a>(
|
||||||
.external_specializations_requested
|
.external_specializations_requested
|
||||||
.entry(module_id)
|
.entry(module_id)
|
||||||
{
|
{
|
||||||
Vacant(entry) => entry.insert(ExternalSpecializations::new_in(arena)),
|
Vacant(entry) => entry.insert(vec![]),
|
||||||
Occupied(entry) => entry.into_mut(),
|
Occupied(entry) => entry.into_mut(),
|
||||||
};
|
};
|
||||||
|
|
||||||
existing.extend(requested);
|
existing.push(requested);
|
||||||
}
|
}
|
||||||
|
|
||||||
start_tasks(arena, &mut state, work, injector, worker_listeners)?;
|
start_tasks(arena, &mut state, work, injector, worker_listeners)?;
|
||||||
|
@ -3936,7 +3920,7 @@ fn make_specializations<'a>(
|
||||||
mut subs: Subs,
|
mut subs: Subs,
|
||||||
procs_base: ProcsBase<'a>,
|
procs_base: ProcsBase<'a>,
|
||||||
mut layout_cache: LayoutCache<'a>,
|
mut layout_cache: LayoutCache<'a>,
|
||||||
specializations_we_must_make: ExternalSpecializations<'a>,
|
specializations_we_must_make: Vec<ExternalSpecializations>,
|
||||||
mut module_timing: ModuleTiming,
|
mut module_timing: ModuleTiming,
|
||||||
ptr_bytes: u32,
|
ptr_bytes: u32,
|
||||||
) -> Msg<'a> {
|
) -> Msg<'a> {
|
||||||
|
@ -3972,7 +3956,7 @@ fn make_specializations<'a>(
|
||||||
&mut mono_env,
|
&mut mono_env,
|
||||||
procs,
|
procs,
|
||||||
specializations_we_must_make,
|
specializations_we_must_make,
|
||||||
procs_base.specializations_for_host,
|
procs_base.host_specializations,
|
||||||
&mut layout_cache,
|
&mut layout_cache,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -4004,27 +3988,11 @@ struct ProcsBase<'a> {
|
||||||
partial_procs: BumpMap<Symbol, PartialProc<'a>>,
|
partial_procs: BumpMap<Symbol, PartialProc<'a>>,
|
||||||
module_thunks: &'a [Symbol],
|
module_thunks: &'a [Symbol],
|
||||||
/// A host-exposed function must be specialized; it's a seed for subsequent specializations
|
/// A host-exposed function must be specialized; it's a seed for subsequent specializations
|
||||||
specializations_for_host: BumpMap<Symbol, MutMap<ProcLayout<'a>, PendingSpecialization<'a>>>,
|
host_specializations: roc_mono::ir::HostSpecializations,
|
||||||
runtime_errors: BumpMap<Symbol, &'a str>,
|
runtime_errors: BumpMap<Symbol, &'a str>,
|
||||||
imported_module_thunks: &'a [Symbol],
|
imported_module_thunks: &'a [Symbol],
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> ProcsBase<'a> {
|
|
||||||
fn add_specialization_for_host(
|
|
||||||
&mut self,
|
|
||||||
symbol: Symbol,
|
|
||||||
layout: ProcLayout<'a>,
|
|
||||||
pending: PendingSpecialization<'a>,
|
|
||||||
) {
|
|
||||||
let all_pending = self
|
|
||||||
.specializations_for_host
|
|
||||||
.entry(symbol)
|
|
||||||
.or_insert_with(|| HashMap::with_capacity_and_hasher(1, default_hasher()));
|
|
||||||
|
|
||||||
all_pending.insert(layout, pending);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[allow(clippy::too_many_arguments)]
|
#[allow(clippy::too_many_arguments)]
|
||||||
fn build_pending_specializations<'a>(
|
fn build_pending_specializations<'a>(
|
||||||
arena: &'a Bump,
|
arena: &'a Bump,
|
||||||
|
@ -4046,7 +4014,7 @@ fn build_pending_specializations<'a>(
|
||||||
let mut procs_base = ProcsBase {
|
let mut procs_base = ProcsBase {
|
||||||
partial_procs: BumpMap::default(),
|
partial_procs: BumpMap::default(),
|
||||||
module_thunks: &[],
|
module_thunks: &[],
|
||||||
specializations_for_host: BumpMap::default(),
|
host_specializations: roc_mono::ir::HostSpecializations::new(),
|
||||||
runtime_errors: BumpMap::default(),
|
runtime_errors: BumpMap::default(),
|
||||||
imported_module_thunks,
|
imported_module_thunks,
|
||||||
};
|
};
|
||||||
|
@ -4134,7 +4102,7 @@ fn add_def_to_module<'a>(
|
||||||
|
|
||||||
match def.loc_pattern.value {
|
match def.loc_pattern.value {
|
||||||
Identifier(symbol) => {
|
Identifier(symbol) => {
|
||||||
let is_exposed = exposed_to_host.contains_key(&symbol);
|
let is_host_exposed = exposed_to_host.contains_key(&symbol);
|
||||||
|
|
||||||
match def.loc_expr.value {
|
match def.loc_expr.value {
|
||||||
Closure(ClosureData {
|
Closure(ClosureData {
|
||||||
|
@ -4152,42 +4120,37 @@ fn add_def_to_module<'a>(
|
||||||
// register it as such. Otherwise, since it
|
// register it as such. Otherwise, since it
|
||||||
// never gets called by Roc code, it will never
|
// never gets called by Roc code, it will never
|
||||||
// get specialized!
|
// get specialized!
|
||||||
if is_exposed {
|
if is_host_exposed {
|
||||||
let layout = match layout_cache.raw_from_var(
|
let layout_result =
|
||||||
mono_env.arena,
|
layout_cache.raw_from_var(mono_env.arena, annotation, mono_env.subs);
|
||||||
annotation,
|
|
||||||
mono_env.subs,
|
|
||||||
) {
|
|
||||||
Ok(l) => l,
|
|
||||||
Err(LayoutProblem::Erroneous) => {
|
|
||||||
let message = "top level function has erroneous type";
|
|
||||||
procs.runtime_errors.insert(symbol, message);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
Err(LayoutProblem::UnresolvedTypeVar(v)) => {
|
|
||||||
let message = format!(
|
|
||||||
"top level function has unresolved type variable {:?}",
|
|
||||||
v
|
|
||||||
);
|
|
||||||
procs
|
|
||||||
.runtime_errors
|
|
||||||
.insert(symbol, mono_env.arena.alloc(message));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
let pending = PendingSpecialization::from_exposed_function(
|
// cannot specialize when e.g. main's type contains type variables
|
||||||
mono_env.arena,
|
if let Err(e) = layout_result {
|
||||||
|
match e {
|
||||||
|
LayoutProblem::Erroneous => {
|
||||||
|
let message = "top level function has erroneous type";
|
||||||
|
procs.runtime_errors.insert(symbol, message);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
LayoutProblem::UnresolvedTypeVar(v) => {
|
||||||
|
let message = format!(
|
||||||
|
"top level function has unresolved type variable {:?}",
|
||||||
|
v
|
||||||
|
);
|
||||||
|
procs
|
||||||
|
.runtime_errors
|
||||||
|
.insert(symbol, mono_env.arena.alloc(message));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
procs.host_specializations.insert_host_exposed(
|
||||||
mono_env.subs,
|
mono_env.subs,
|
||||||
|
symbol,
|
||||||
def.annotation,
|
def.annotation,
|
||||||
annotation,
|
annotation,
|
||||||
);
|
);
|
||||||
|
|
||||||
procs.add_specialization_for_host(
|
|
||||||
symbol,
|
|
||||||
ProcLayout::from_raw(mono_env.arena, layout),
|
|
||||||
pending,
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let partial_proc = PartialProc::from_named_function(
|
let partial_proc = PartialProc::from_named_function(
|
||||||
|
@ -4207,51 +4170,47 @@ fn add_def_to_module<'a>(
|
||||||
// mark this symbols as a top-level thunk before any other work on the procs
|
// mark this symbols as a top-level thunk before any other work on the procs
|
||||||
module_thunks.push(symbol);
|
module_thunks.push(symbol);
|
||||||
|
|
||||||
|
let annotation = def.expr_var;
|
||||||
|
|
||||||
// If this is an exposed symbol, we need to
|
// If this is an exposed symbol, we need to
|
||||||
// register it as such. Otherwise, since it
|
// register it as such. Otherwise, since it
|
||||||
// never gets called by Roc code, it will never
|
// never gets called by Roc code, it will never
|
||||||
// get specialized!
|
// get specialized!
|
||||||
if is_exposed {
|
if is_host_exposed {
|
||||||
let annotation = def.expr_var;
|
let layout_result =
|
||||||
|
layout_cache.raw_from_var(mono_env.arena, annotation, mono_env.subs);
|
||||||
|
|
||||||
let top_level = match layout_cache.from_var(
|
// cannot specialize when e.g. main's type contains type variables
|
||||||
mono_env.arena,
|
if let Err(e) = layout_result {
|
||||||
annotation,
|
match e {
|
||||||
mono_env.subs,
|
LayoutProblem::Erroneous => {
|
||||||
) {
|
let message = "top level function has erroneous type";
|
||||||
Ok(l) => {
|
procs.runtime_errors.insert(symbol, message);
|
||||||
// remember, this is a 0-argument thunk
|
return;
|
||||||
ProcLayout::new(mono_env.arena, &[], l)
|
}
|
||||||
|
LayoutProblem::UnresolvedTypeVar(v) => {
|
||||||
|
let message = format!(
|
||||||
|
"top level function has unresolved type variable {:?}",
|
||||||
|
v
|
||||||
|
);
|
||||||
|
procs
|
||||||
|
.runtime_errors
|
||||||
|
.insert(symbol, mono_env.arena.alloc(message));
|
||||||
|
return;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Err(LayoutProblem::Erroneous) => {
|
}
|
||||||
let message = "top level function has erroneous type";
|
|
||||||
procs.runtime_errors.insert(symbol, message);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
Err(LayoutProblem::UnresolvedTypeVar(v)) => {
|
|
||||||
let message = format!(
|
|
||||||
"top level function has unresolved type variable {:?}",
|
|
||||||
v
|
|
||||||
);
|
|
||||||
procs
|
|
||||||
.runtime_errors
|
|
||||||
.insert(symbol, mono_env.arena.alloc(message));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
let pending = PendingSpecialization::from_exposed_function(
|
procs.host_specializations.insert_host_exposed(
|
||||||
mono_env.arena,
|
|
||||||
mono_env.subs,
|
mono_env.subs,
|
||||||
|
symbol,
|
||||||
def.annotation,
|
def.annotation,
|
||||||
annotation,
|
annotation,
|
||||||
);
|
);
|
||||||
|
|
||||||
procs.add_specialization_for_host(symbol, top_level, pending);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let proc = PartialProc {
|
let proc = PartialProc {
|
||||||
annotation: def.expr_var,
|
annotation,
|
||||||
// This is a 0-arity thunk, so it has no arguments.
|
// This is a 0-arity thunk, so it has no arguments.
|
||||||
pattern_symbols: &[],
|
pattern_symbols: &[],
|
||||||
// This is a top-level definition, so it cannot capture anything
|
// This is a top-level definition, so it cannot capture anything
|
||||||
|
|
|
@ -12,6 +12,10 @@ pub enum CalledVia {
|
||||||
|
|
||||||
/// Calling with a unary operator, e.g. (!foo bar baz) or (-foo bar baz)
|
/// Calling with a unary operator, e.g. (!foo bar baz) or (-foo bar baz)
|
||||||
UnaryOp(UnaryOp),
|
UnaryOp(UnaryOp),
|
||||||
|
|
||||||
|
/// This call is the result of desugaring string interpolation,
|
||||||
|
/// e.g. "\(first) \(last)" is transformed into Str.concat (Str.concat first " ") last.
|
||||||
|
StringInterpolation,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
|
@ -2,10 +2,10 @@
|
||||||
// See github.com/rtfeldman/roc/issues/800 for discussion of the large_enum_variant check.
|
// See github.com/rtfeldman/roc/issues/800 for discussion of the large_enum_variant check.
|
||||||
#![allow(clippy::large_enum_variant, clippy::upper_case_acronyms)]
|
#![allow(clippy::large_enum_variant, clippy::upper_case_acronyms)]
|
||||||
|
|
||||||
|
pub mod called_via;
|
||||||
pub mod ident;
|
pub mod ident;
|
||||||
pub mod low_level;
|
pub mod low_level;
|
||||||
pub mod module_err;
|
pub mod module_err;
|
||||||
pub mod operator;
|
|
||||||
pub mod symbol;
|
pub mod symbol;
|
||||||
|
|
||||||
#[macro_use]
|
#[macro_use]
|
||||||
|
|
|
@ -50,6 +50,7 @@ pub enum LowLevel {
|
||||||
ListDropAt,
|
ListDropAt,
|
||||||
ListSwap,
|
ListSwap,
|
||||||
ListAny,
|
ListAny,
|
||||||
|
ListAll,
|
||||||
ListFindUnsafe,
|
ListFindUnsafe,
|
||||||
DictSize,
|
DictSize,
|
||||||
DictEmpty,
|
DictEmpty,
|
||||||
|
@ -131,6 +132,7 @@ macro_rules! higher_order {
|
||||||
| ListKeepErrs
|
| ListKeepErrs
|
||||||
| ListSortWith
|
| ListSortWith
|
||||||
| ListAny
|
| ListAny
|
||||||
|
| ListAll
|
||||||
| ListFindUnsafe
|
| ListFindUnsafe
|
||||||
| DictWalk
|
| DictWalk
|
||||||
};
|
};
|
||||||
|
@ -162,6 +164,7 @@ impl LowLevel {
|
||||||
ListKeepErrs => 1,
|
ListKeepErrs => 1,
|
||||||
ListSortWith => 1,
|
ListSortWith => 1,
|
||||||
ListAny => 1,
|
ListAny => 1,
|
||||||
|
ListAll => 1,
|
||||||
ListFindUnsafe => 1,
|
ListFindUnsafe => 1,
|
||||||
DictWalk => 2,
|
DictWalk => 2,
|
||||||
_ => unreachable!(),
|
_ => unreachable!(),
|
||||||
|
@ -220,6 +223,7 @@ impl LowLevel {
|
||||||
Symbol::LIST_DROP_AT => Some(ListDropAt),
|
Symbol::LIST_DROP_AT => Some(ListDropAt),
|
||||||
Symbol::LIST_SWAP => Some(ListSwap),
|
Symbol::LIST_SWAP => Some(ListSwap),
|
||||||
Symbol::LIST_ANY => Some(ListAny),
|
Symbol::LIST_ANY => Some(ListAny),
|
||||||
|
Symbol::LIST_ALL => Some(ListAll),
|
||||||
Symbol::LIST_FIND => None,
|
Symbol::LIST_FIND => None,
|
||||||
Symbol::DICT_LEN => Some(DictSize),
|
Symbol::DICT_LEN => Some(DictSize),
|
||||||
Symbol::DICT_EMPTY => Some(DictEmpty),
|
Symbol::DICT_EMPTY => Some(DictEmpty),
|
||||||
|
|
|
@ -877,6 +877,9 @@ define_builtins! {
|
||||||
|
|
||||||
// used in wasm dev backend to mark temporary values in the VM stack
|
// used in wasm dev backend to mark temporary values in the VM stack
|
||||||
24 WASM_TMP: "#wasm_tmp"
|
24 WASM_TMP: "#wasm_tmp"
|
||||||
|
|
||||||
|
// the _ used in mono when a specialized symbol is deleted
|
||||||
|
25 REMOVED_SPECIALIZATION: "#removed_specialization"
|
||||||
}
|
}
|
||||||
1 NUM: "Num" => {
|
1 NUM: "Num" => {
|
||||||
0 NUM_NUM: "Num" imported // the Num.Num type alias
|
0 NUM_NUM: "Num" imported // the Num.Num type alias
|
||||||
|
@ -989,12 +992,16 @@ define_builtins! {
|
||||||
}
|
}
|
||||||
2 BOOL: "Bool" => {
|
2 BOOL: "Bool" => {
|
||||||
0 BOOL_BOOL: "Bool" imported // the Bool.Bool type alias
|
0 BOOL_BOOL: "Bool" imported // the Bool.Bool type alias
|
||||||
1 BOOL_AND: "and"
|
1 BOOL_FALSE: "False" imported // Bool.Bool = [ False, True ]
|
||||||
2 BOOL_OR: "or"
|
// NB: not strictly needed; used for finding global tag names in error suggestions
|
||||||
3 BOOL_NOT: "not"
|
2 BOOL_TRUE: "True" imported // Bool.Bool = [ False, True ]
|
||||||
4 BOOL_XOR: "xor"
|
// NB: not strictly needed; used for finding global tag names in error suggestions
|
||||||
5 BOOL_EQ: "isEq"
|
3 BOOL_AND: "and"
|
||||||
6 BOOL_NEQ: "isNotEq"
|
4 BOOL_OR: "or"
|
||||||
|
5 BOOL_NOT: "not"
|
||||||
|
6 BOOL_XOR: "xor"
|
||||||
|
7 BOOL_EQ: "isEq"
|
||||||
|
8 BOOL_NEQ: "isNotEq"
|
||||||
}
|
}
|
||||||
3 STR: "Str" => {
|
3 STR: "Str" => {
|
||||||
0 STR_STR: "Str" imported // the Str.Str type alias
|
0 STR_STR: "Str" imported // the Str.Str type alias
|
||||||
|
@ -1072,17 +1079,24 @@ define_builtins! {
|
||||||
47 LIST_FIND: "find"
|
47 LIST_FIND: "find"
|
||||||
48 LIST_FIND_RESULT: "#find_result" // symbol used in the definition of List.find
|
48 LIST_FIND_RESULT: "#find_result" // symbol used in the definition of List.find
|
||||||
49 LIST_SUBLIST: "sublist"
|
49 LIST_SUBLIST: "sublist"
|
||||||
50 LIST_SPLIT: "split"
|
50 LIST_INTERSPERSE: "intersperse"
|
||||||
51 LIST_SPLIT_CLOS: "#splitClos"
|
51 LIST_INTERSPERSE_CLOS: "#intersperseClos"
|
||||||
|
52 LIST_SPLIT: "split"
|
||||||
|
53 LIST_SPLIT_CLOS: "#splitClos"
|
||||||
|
54 LIST_ALL: "all"
|
||||||
}
|
}
|
||||||
5 RESULT: "Result" => {
|
5 RESULT: "Result" => {
|
||||||
0 RESULT_RESULT: "Result" imported // the Result.Result type alias
|
0 RESULT_RESULT: "Result" imported // the Result.Result type alias
|
||||||
1 RESULT_MAP: "map"
|
1 RESULT_OK: "Ok" imported // Result.Result a e = [ Ok a, Err e ]
|
||||||
2 RESULT_MAP_ERR: "mapErr"
|
// NB: not strictly needed; used for finding global tag names in error suggestions
|
||||||
3 RESULT_WITH_DEFAULT: "withDefault"
|
2 RESULT_ERR: "Err" imported // Result.Result a e = [ Ok a, Err e ]
|
||||||
4 RESULT_AFTER: "after"
|
// NB: not strictly needed; used for finding global tag names in error suggestions
|
||||||
5 RESULT_IS_OK: "isOk"
|
3 RESULT_MAP: "map"
|
||||||
6 RESULT_IS_ERR: "isErr"
|
4 RESULT_MAP_ERR: "mapErr"
|
||||||
|
5 RESULT_WITH_DEFAULT: "withDefault"
|
||||||
|
6 RESULT_AFTER: "after"
|
||||||
|
7 RESULT_IS_OK: "isOk"
|
||||||
|
8 RESULT_IS_ERR: "isErr"
|
||||||
}
|
}
|
||||||
6 DICT: "Dict" => {
|
6 DICT: "Dict" => {
|
||||||
0 DICT_DICT: "Dict" imported // the Dict.Dict type alias
|
0 DICT_DICT: "Dict" imported // the Dict.Dict type alias
|
||||||
|
|
|
@ -1093,6 +1093,25 @@ fn call_spec(
|
||||||
|
|
||||||
add_loop(builder, block, state_type, init_state, loop_body)
|
add_loop(builder, block, state_type, init_state, loop_body)
|
||||||
}
|
}
|
||||||
|
ListAll { xs } => {
|
||||||
|
let list = env.symbols[xs];
|
||||||
|
|
||||||
|
let loop_body = |builder: &mut FuncDefBuilder, block, _state| {
|
||||||
|
let bag = builder.add_get_tuple_field(block, list, LIST_BAG_INDEX)?;
|
||||||
|
let element = builder.add_bag_get(block, bag)?;
|
||||||
|
|
||||||
|
let new_state = call_function!(builder, block, [element]);
|
||||||
|
|
||||||
|
Ok(new_state)
|
||||||
|
};
|
||||||
|
|
||||||
|
let state_layout = Layout::Builtin(Builtin::Int1);
|
||||||
|
let state_type = layout_spec(builder, &state_layout)?;
|
||||||
|
|
||||||
|
let init_state = new_num(builder, block)?;
|
||||||
|
|
||||||
|
add_loop(builder, block, state_type, init_state, loop_body)
|
||||||
|
}
|
||||||
ListFindUnsafe { xs } => {
|
ListFindUnsafe { xs } => {
|
||||||
let list = env.symbols[xs];
|
let list = env.symbols[xs];
|
||||||
|
|
||||||
|
|
|
@ -619,6 +619,7 @@ impl<'a> BorrowInfState<'a> {
|
||||||
| ListKeepOks { xs }
|
| ListKeepOks { xs }
|
||||||
| ListKeepErrs { xs }
|
| ListKeepErrs { xs }
|
||||||
| ListAny { xs }
|
| ListAny { xs }
|
||||||
|
| ListAll { xs }
|
||||||
| ListFindUnsafe { xs } => {
|
| ListFindUnsafe { xs } => {
|
||||||
// own the list if the function wants to own the element
|
// own the list if the function wants to own the element
|
||||||
if !function_ps[0].borrow {
|
if !function_ps[0].borrow {
|
||||||
|
@ -953,7 +954,7 @@ pub fn lowlevel_borrow_signature(arena: &Bump, op: LowLevel) -> &[bool] {
|
||||||
ListMap2 => arena.alloc_slice_copy(&[owned, owned, function, closure_data]),
|
ListMap2 => arena.alloc_slice_copy(&[owned, owned, function, closure_data]),
|
||||||
ListMap3 => arena.alloc_slice_copy(&[owned, owned, owned, function, closure_data]),
|
ListMap3 => arena.alloc_slice_copy(&[owned, owned, owned, function, closure_data]),
|
||||||
ListMap4 => arena.alloc_slice_copy(&[owned, owned, owned, owned, function, closure_data]),
|
ListMap4 => arena.alloc_slice_copy(&[owned, owned, owned, owned, function, closure_data]),
|
||||||
ListKeepIf | ListKeepOks | ListKeepErrs | ListAny => {
|
ListKeepIf | ListKeepOks | ListKeepErrs | ListAny | ListAll => {
|
||||||
arena.alloc_slice_copy(&[owned, function, closure_data])
|
arena.alloc_slice_copy(&[owned, function, closure_data])
|
||||||
}
|
}
|
||||||
ListContains => arena.alloc_slice_copy(&[borrowed, irrelevant]),
|
ListContains => arena.alloc_slice_copy(&[borrowed, irrelevant]),
|
||||||
|
|
|
@ -536,6 +536,7 @@ impl<'a> Context<'a> {
|
||||||
| ListKeepOks { xs }
|
| ListKeepOks { xs }
|
||||||
| ListKeepErrs { xs }
|
| ListKeepErrs { xs }
|
||||||
| ListAny { xs }
|
| ListAny { xs }
|
||||||
|
| ListAll { xs }
|
||||||
| ListFindUnsafe { xs } => {
|
| ListFindUnsafe { xs } => {
|
||||||
let borrows = [function_ps[0].borrow, FUNCTION, CLOSURE_DATA];
|
let borrows = [function_ps[0].borrow, FUNCTION, CLOSURE_DATA];
|
||||||
|
|
||||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -50,6 +50,9 @@ pub enum HigherOrder {
|
||||||
ListAny {
|
ListAny {
|
||||||
xs: Symbol,
|
xs: Symbol,
|
||||||
},
|
},
|
||||||
|
ListAll {
|
||||||
|
xs: Symbol,
|
||||||
|
},
|
||||||
ListFindUnsafe {
|
ListFindUnsafe {
|
||||||
xs: Symbol,
|
xs: Symbol,
|
||||||
},
|
},
|
||||||
|
@ -77,6 +80,7 @@ impl HigherOrder {
|
||||||
HigherOrder::ListFindUnsafe { .. } => 1,
|
HigherOrder::ListFindUnsafe { .. } => 1,
|
||||||
HigherOrder::DictWalk { .. } => 2,
|
HigherOrder::DictWalk { .. } => 2,
|
||||||
HigherOrder::ListAny { .. } => 1,
|
HigherOrder::ListAny { .. } => 1,
|
||||||
|
HigherOrder::ListAll { .. } => 1,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,7 +4,7 @@ use crate::header::{AppHeader, ImportsEntry, InterfaceHeader, PlatformHeader, Ty
|
||||||
use crate::ident::Ident;
|
use crate::ident::Ident;
|
||||||
use bumpalo::collections::{String, Vec};
|
use bumpalo::collections::{String, Vec};
|
||||||
use bumpalo::Bump;
|
use bumpalo::Bump;
|
||||||
use roc_module::operator::{BinOp, CalledVia, UnaryOp};
|
use roc_module::called_via::{BinOp, CalledVia, UnaryOp};
|
||||||
use roc_region::all::{Loc, Position, Region};
|
use roc_region::all::{Loc, Position, Region};
|
||||||
|
|
||||||
#[derive(Clone, Debug, PartialEq)]
|
#[derive(Clone, Debug, PartialEq)]
|
||||||
|
|
|
@ -13,7 +13,7 @@ use crate::pattern::loc_closure_param;
|
||||||
use crate::type_annotation;
|
use crate::type_annotation;
|
||||||
use bumpalo::collections::Vec;
|
use bumpalo::collections::Vec;
|
||||||
use bumpalo::Bump;
|
use bumpalo::Bump;
|
||||||
use roc_module::operator::{BinOp, CalledVia, UnaryOp};
|
use roc_module::called_via::{BinOp, CalledVia, UnaryOp};
|
||||||
use roc_region::all::{Located, Position, Region};
|
use roc_region::all::{Located, Position, Region};
|
||||||
|
|
||||||
use crate::parser::Progress::{self, *};
|
use crate::parser::Progress::{self, *};
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
use roc_collections::all::MutSet;
|
use roc_collections::all::MutSet;
|
||||||
|
use roc_module::called_via::BinOp;
|
||||||
use roc_module::ident::{Ident, Lowercase, ModuleName, TagName};
|
use roc_module::ident::{Ident, Lowercase, ModuleName, TagName};
|
||||||
use roc_module::operator::BinOp;
|
|
||||||
use roc_module::symbol::{ModuleId, Symbol};
|
use roc_module::symbol::{ModuleId, Symbol};
|
||||||
use roc_parse::ast::Base;
|
use roc_parse::ast::Base;
|
||||||
use roc_parse::pattern::PatternType;
|
use roc_parse::pattern::PatternType;
|
||||||
|
@ -139,6 +139,7 @@ pub enum RuntimeError {
|
||||||
module_name: ModuleName,
|
module_name: ModuleName,
|
||||||
ident: Ident,
|
ident: Ident,
|
||||||
region: Region,
|
region: Region,
|
||||||
|
exposed_values: Vec<Lowercase>,
|
||||||
},
|
},
|
||||||
ModuleNotImported {
|
ModuleNotImported {
|
||||||
module_name: ModuleName,
|
module_name: ModuleName,
|
||||||
|
|
|
@ -12,6 +12,7 @@ roc_module = { path = "../module" }
|
||||||
roc_types = { path = "../types" }
|
roc_types = { path = "../types" }
|
||||||
roc_can = { path = "../can" }
|
roc_can = { path = "../can" }
|
||||||
roc_unify = { path = "../unify" }
|
roc_unify = { path = "../unify" }
|
||||||
|
bumpalo = { version = "3.8.0", features = ["collections"] }
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
roc_load = { path = "../load" }
|
roc_load = { path = "../load" }
|
||||||
|
|
|
@ -27,7 +27,7 @@ pub fn run_solve(
|
||||||
aliases,
|
aliases,
|
||||||
};
|
};
|
||||||
|
|
||||||
let mut subs = Subs::new(var_store);
|
let mut subs = Subs::new_from_varstore(var_store);
|
||||||
|
|
||||||
for (var, name) in rigid_variables {
|
for (var, name) in rigid_variables {
|
||||||
subs.rigid_var(var, name);
|
subs.rigid_var(var, name);
|
||||||
|
|
|
@ -619,10 +619,13 @@ fn type_to_var(
|
||||||
subs: &mut Subs,
|
subs: &mut Subs,
|
||||||
rank: Rank,
|
rank: Rank,
|
||||||
pools: &mut Pools,
|
pools: &mut Pools,
|
||||||
cached: &mut MutMap<Symbol, Variable>,
|
_: &mut MutMap<Symbol, Variable>,
|
||||||
typ: &Type,
|
typ: &Type,
|
||||||
) -> Variable {
|
) -> Variable {
|
||||||
type_to_variable(subs, rank, pools, cached, typ)
|
// capacity based on the false hello world program
|
||||||
|
let arena = bumpalo::Bump::with_capacity(4 * 1024);
|
||||||
|
|
||||||
|
type_to_variable(subs, rank, pools, &arena, typ)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Abusing existing functions for our purposes
|
/// Abusing existing functions for our purposes
|
||||||
|
@ -630,25 +633,29 @@ fn type_to_var(
|
||||||
pub fn insert_type_into_subs(subs: &mut Subs, typ: &Type) -> Variable {
|
pub fn insert_type_into_subs(subs: &mut Subs, typ: &Type) -> Variable {
|
||||||
let rank = Rank::NONE;
|
let rank = Rank::NONE;
|
||||||
let mut pools = Pools::default();
|
let mut pools = Pools::default();
|
||||||
let mut cached = MutMap::default();
|
|
||||||
|
|
||||||
type_to_variable(subs, rank, &mut pools, &mut cached, typ)
|
// capacity based on the false hello world program
|
||||||
|
let arena = bumpalo::Bump::with_capacity(4 * 1024);
|
||||||
|
|
||||||
|
type_to_variable(subs, rank, &mut pools, &arena, typ)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn type_to_variable(
|
fn type_to_variable<'a>(
|
||||||
subs: &mut Subs,
|
subs: &mut Subs,
|
||||||
rank: Rank,
|
rank: Rank,
|
||||||
pools: &mut Pools,
|
pools: &mut Pools,
|
||||||
cached: &mut MutMap<Symbol, Variable>,
|
arena: &'a bumpalo::Bump,
|
||||||
typ: &Type,
|
typ: &Type,
|
||||||
) -> Variable {
|
) -> Variable {
|
||||||
|
use bumpalo::collections::Vec;
|
||||||
|
|
||||||
match typ {
|
match typ {
|
||||||
Variable(var) => *var,
|
Variable(var) => *var,
|
||||||
Apply(symbol, args) => {
|
Apply(symbol, args) => {
|
||||||
let mut new_arg_vars = Vec::with_capacity(args.len());
|
let mut new_arg_vars = Vec::with_capacity_in(args.len(), arena);
|
||||||
|
|
||||||
for arg in args {
|
for arg in args {
|
||||||
let var = type_to_variable(subs, rank, pools, cached, arg);
|
let var = type_to_variable(subs, rank, pools, arena, arg);
|
||||||
new_arg_vars.push(var);
|
new_arg_vars.push(var);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -663,32 +670,32 @@ fn type_to_variable(
|
||||||
|
|
||||||
// This case is important for the rank of boolean variables
|
// This case is important for the rank of boolean variables
|
||||||
Function(arg_vars, closure_type, ret_type) => {
|
Function(arg_vars, closure_type, ret_type) => {
|
||||||
let mut new_arg_vars = Vec::with_capacity(arg_vars.len());
|
let mut new_arg_vars = Vec::with_capacity_in(arg_vars.len(), arena);
|
||||||
|
|
||||||
for arg in arg_vars {
|
for arg in arg_vars {
|
||||||
let var = type_to_variable(subs, rank, pools, cached, arg);
|
let var = type_to_variable(subs, rank, pools, arena, arg);
|
||||||
new_arg_vars.push(var);
|
new_arg_vars.push(var);
|
||||||
}
|
}
|
||||||
|
|
||||||
let arg_vars = VariableSubsSlice::insert_into_subs(subs, new_arg_vars);
|
let arg_vars = VariableSubsSlice::insert_into_subs(subs, new_arg_vars);
|
||||||
|
|
||||||
let ret_var = type_to_variable(subs, rank, pools, cached, ret_type);
|
let ret_var = type_to_variable(subs, rank, pools, arena, ret_type);
|
||||||
let closure_var = type_to_variable(subs, rank, pools, cached, closure_type);
|
let closure_var = type_to_variable(subs, rank, pools, arena, closure_type);
|
||||||
let content = Content::Structure(FlatType::Func(arg_vars, closure_var, ret_var));
|
let content = Content::Structure(FlatType::Func(arg_vars, closure_var, ret_var));
|
||||||
|
|
||||||
register(subs, rank, pools, content)
|
register(subs, rank, pools, content)
|
||||||
}
|
}
|
||||||
Record(fields, ext) => {
|
Record(fields, ext) => {
|
||||||
let mut field_vars = Vec::with_capacity(fields.len());
|
let mut field_vars = Vec::with_capacity_in(fields.len(), arena);
|
||||||
|
|
||||||
for (field, field_type) in fields {
|
for (field, field_type) in fields {
|
||||||
let field_var =
|
let field_var =
|
||||||
field_type.map(|typ| type_to_variable(subs, rank, pools, cached, typ));
|
field_type.map(|typ| type_to_variable(subs, rank, pools, arena, typ));
|
||||||
|
|
||||||
field_vars.push((field.clone(), field_var));
|
field_vars.push((field.clone(), field_var));
|
||||||
}
|
}
|
||||||
|
|
||||||
let temp_ext_var = type_to_variable(subs, rank, pools, cached, ext);
|
let temp_ext_var = type_to_variable(subs, rank, pools, arena, ext);
|
||||||
|
|
||||||
let (it, new_ext_var) =
|
let (it, new_ext_var) =
|
||||||
gather_fields_unsorted_iter(subs, RecordFields::empty(), temp_ext_var);
|
gather_fields_unsorted_iter(subs, RecordFields::empty(), temp_ext_var);
|
||||||
|
@ -707,14 +714,14 @@ fn type_to_variable(
|
||||||
register(subs, rank, pools, content)
|
register(subs, rank, pools, content)
|
||||||
}
|
}
|
||||||
TagUnion(tags, ext) => {
|
TagUnion(tags, ext) => {
|
||||||
let (union_tags, ext) = type_to_union_tags(subs, rank, pools, cached, tags, ext);
|
let (union_tags, ext) = type_to_union_tags(subs, rank, pools, arena, tags, ext);
|
||||||
let content = Content::Structure(FlatType::TagUnion(union_tags, ext));
|
let content = Content::Structure(FlatType::TagUnion(union_tags, ext));
|
||||||
|
|
||||||
register(subs, rank, pools, content)
|
register(subs, rank, pools, content)
|
||||||
}
|
}
|
||||||
FunctionOrTagUnion(tag_name, symbol, ext) => {
|
FunctionOrTagUnion(tag_name, symbol, ext) => {
|
||||||
let temp_ext_var = type_to_variable(subs, rank, pools, cached, ext);
|
let temp_ext_var = type_to_variable(subs, rank, pools, arena, ext);
|
||||||
let mut ext_tag_vec = Vec::new();
|
let mut ext_tag_vec = std::vec::Vec::new();
|
||||||
let new_ext_var = match roc_types::pretty_print::chase_ext_tag_union(
|
let new_ext_var = match roc_types::pretty_print::chase_ext_tag_union(
|
||||||
subs,
|
subs,
|
||||||
temp_ext_var,
|
temp_ext_var,
|
||||||
|
@ -735,7 +742,7 @@ fn type_to_variable(
|
||||||
register(subs, rank, pools, content)
|
register(subs, rank, pools, content)
|
||||||
}
|
}
|
||||||
RecursiveTagUnion(rec_var, tags, ext) => {
|
RecursiveTagUnion(rec_var, tags, ext) => {
|
||||||
let (union_tags, ext) = type_to_union_tags(subs, rank, pools, cached, tags, ext);
|
let (union_tags, ext) = type_to_union_tags(subs, rank, pools, arena, tags, ext);
|
||||||
let content =
|
let content =
|
||||||
Content::Structure(FlatType::RecursiveTagUnion(*rec_var, union_tags, ext));
|
Content::Structure(FlatType::RecursiveTagUnion(*rec_var, union_tags, ext));
|
||||||
|
|
||||||
|
@ -780,22 +787,22 @@ fn type_to_variable(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut arg_vars = Vec::with_capacity(args.len());
|
let mut arg_vars = Vec::with_capacity_in(args.len(), arena);
|
||||||
|
|
||||||
for (arg, arg_type) in args {
|
for (arg, arg_type) in args {
|
||||||
let arg_var = type_to_variable(subs, rank, pools, cached, arg_type);
|
let arg_var = type_to_variable(subs, rank, pools, arena, arg_type);
|
||||||
|
|
||||||
arg_vars.push((arg.clone(), arg_var));
|
arg_vars.push((arg.clone(), arg_var));
|
||||||
}
|
}
|
||||||
|
|
||||||
let lambda_set_variables: Vec<_> = lambda_set_variables
|
let lambda_set_variables_it = lambda_set_variables
|
||||||
.iter()
|
.iter()
|
||||||
.map(|ls| type_to_variable(subs, rank, pools, cached, &ls.0))
|
.map(|ls| type_to_variable(subs, rank, pools, arena, &ls.0));
|
||||||
.collect();
|
let lambda_set_variables = Vec::from_iter_in(lambda_set_variables_it, arena);
|
||||||
|
|
||||||
let arg_vars = AliasVariables::insert_into_subs(subs, arg_vars, lambda_set_variables);
|
let arg_vars = AliasVariables::insert_into_subs(subs, arg_vars, lambda_set_variables);
|
||||||
|
|
||||||
let alias_var = type_to_variable(subs, rank, pools, cached, alias_type);
|
let alias_var = type_to_variable(subs, rank, pools, arena, alias_type);
|
||||||
let content = Content::Alias(*symbol, arg_vars, alias_var);
|
let content = Content::Alias(*symbol, arg_vars, alias_var);
|
||||||
|
|
||||||
register(subs, rank, pools, content)
|
register(subs, rank, pools, content)
|
||||||
|
@ -808,22 +815,22 @@ fn type_to_variable(
|
||||||
lambda_set_variables,
|
lambda_set_variables,
|
||||||
..
|
..
|
||||||
} => {
|
} => {
|
||||||
let mut arg_vars = Vec::with_capacity(args.len());
|
let mut arg_vars = Vec::with_capacity_in(args.len(), arena);
|
||||||
|
|
||||||
for (arg, arg_type) in args {
|
for (arg, arg_type) in args {
|
||||||
let arg_var = type_to_variable(subs, rank, pools, cached, arg_type);
|
let arg_var = type_to_variable(subs, rank, pools, arena, arg_type);
|
||||||
|
|
||||||
arg_vars.push((arg.clone(), arg_var));
|
arg_vars.push((arg.clone(), arg_var));
|
||||||
}
|
}
|
||||||
|
|
||||||
let lambda_set_variables: Vec<_> = lambda_set_variables
|
let lambda_set_variables_it = lambda_set_variables
|
||||||
.iter()
|
.iter()
|
||||||
.map(|ls| type_to_variable(subs, rank, pools, cached, &ls.0))
|
.map(|ls| type_to_variable(subs, rank, pools, arena, &ls.0));
|
||||||
.collect();
|
let lambda_set_variables = Vec::from_iter_in(lambda_set_variables_it, arena);
|
||||||
|
|
||||||
let arg_vars = AliasVariables::insert_into_subs(subs, arg_vars, lambda_set_variables);
|
let arg_vars = AliasVariables::insert_into_subs(subs, arg_vars, lambda_set_variables);
|
||||||
|
|
||||||
let alias_var = type_to_variable(subs, rank, pools, cached, alias_type);
|
let alias_var = type_to_variable(subs, rank, pools, arena, alias_type);
|
||||||
|
|
||||||
// unify the actual_var with the result var
|
// unify the actual_var with the result var
|
||||||
// this can be used to access the type of the actual_var
|
// this can be used to access the type of the actual_var
|
||||||
|
@ -853,20 +860,22 @@ fn type_to_variable(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn type_to_union_tags(
|
fn type_to_union_tags<'a>(
|
||||||
subs: &mut Subs,
|
subs: &mut Subs,
|
||||||
rank: Rank,
|
rank: Rank,
|
||||||
pools: &mut Pools,
|
pools: &mut Pools,
|
||||||
cached: &mut MutMap<Symbol, Variable>,
|
arena: &'a bumpalo::Bump,
|
||||||
tags: &[(TagName, Vec<Type>)],
|
tags: &[(TagName, Vec<Type>)],
|
||||||
ext: &Type,
|
ext: &Type,
|
||||||
) -> (UnionTags, Variable) {
|
) -> (UnionTags, Variable) {
|
||||||
let mut tag_vars = Vec::with_capacity(tags.len());
|
use bumpalo::collections::Vec;
|
||||||
|
|
||||||
let mut tag_argument_vars = Vec::new();
|
let mut tag_vars = Vec::with_capacity_in(tags.len(), arena);
|
||||||
|
|
||||||
|
let mut tag_argument_vars = Vec::with_capacity_in(tags.len(), arena);
|
||||||
for (tag, tag_argument_types) in tags {
|
for (tag, tag_argument_types) in tags {
|
||||||
for arg_type in tag_argument_types {
|
for arg_type in tag_argument_types {
|
||||||
let new_var = type_to_variable(subs, rank, pools, cached, arg_type);
|
let new_var = type_to_variable(subs, rank, pools, arena, arg_type);
|
||||||
tag_argument_vars.push(new_var);
|
tag_argument_vars.push(new_var);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -875,7 +884,7 @@ fn type_to_union_tags(
|
||||||
tag_vars.push((tag.clone(), new_slice));
|
tag_vars.push((tag.clone(), new_slice));
|
||||||
}
|
}
|
||||||
|
|
||||||
let temp_ext_var = type_to_variable(subs, rank, pools, cached, ext);
|
let temp_ext_var = type_to_variable(subs, rank, pools, arena, ext);
|
||||||
|
|
||||||
let ext = {
|
let ext = {
|
||||||
let (it, ext) =
|
let (it, ext) =
|
||||||
|
@ -1031,8 +1040,7 @@ fn pool_to_rank_table(
|
||||||
|
|
||||||
// Sort the variables into buckets by rank.
|
// Sort the variables into buckets by rank.
|
||||||
for &var in young_vars.iter() {
|
for &var in young_vars.iter() {
|
||||||
let rank = subs.get_rank(var);
|
let rank = subs.get_rank_set_mark(var, young_mark);
|
||||||
subs.set_mark(var, young_mark);
|
|
||||||
|
|
||||||
debug_assert!(rank.into_usize() < young_rank.into_usize() + 1);
|
debug_assert!(rank.into_usize() < young_rank.into_usize() + 1);
|
||||||
pools.get_mut(rank).push(var);
|
pools.get_mut(rank).push(var);
|
||||||
|
@ -1245,140 +1253,136 @@ fn introduce(subs: &mut Subs, rank: Rank, pools: &mut Pools, vars: &[Variable])
|
||||||
/// this is used during the monomorphization process
|
/// this is used during the monomorphization process
|
||||||
pub fn instantiate_rigids(subs: &mut Subs, var: Variable) {
|
pub fn instantiate_rigids(subs: &mut Subs, var: Variable) {
|
||||||
let rank = Rank::NONE;
|
let rank = Rank::NONE;
|
||||||
let mut pools = Pools::default();
|
|
||||||
|
|
||||||
instantiate_rigids_help(subs, rank, &mut pools, var);
|
instantiate_rigids_help(subs, rank, var);
|
||||||
|
|
||||||
|
// NOTE subs.restore(var) is done at the end of instantiate_rigids_help
|
||||||
}
|
}
|
||||||
|
|
||||||
fn instantiate_rigids_help(
|
fn instantiate_rigids_help(subs: &mut Subs, max_rank: Rank, initial: Variable) {
|
||||||
subs: &mut Subs,
|
let mut visited = vec![];
|
||||||
max_rank: Rank,
|
let mut stack = vec![initial];
|
||||||
pools: &mut Pools,
|
|
||||||
var: Variable,
|
|
||||||
) -> Variable {
|
|
||||||
use roc_types::subs::Content::*;
|
|
||||||
use roc_types::subs::FlatType::*;
|
|
||||||
|
|
||||||
let desc = subs.get_without_compacting(var);
|
macro_rules! var_slice {
|
||||||
|
($variable_subs_slice:expr) => {{
|
||||||
if let Some(copy) = desc.copy.into_variable() {
|
let slice = $variable_subs_slice;
|
||||||
return copy;
|
&subs.variables[slice.slice.start as usize..][..slice.slice.length as usize]
|
||||||
|
}};
|
||||||
}
|
}
|
||||||
|
|
||||||
let make_descriptor = |content| Descriptor {
|
while let Some(var) = stack.pop() {
|
||||||
content,
|
visited.push(var);
|
||||||
rank: max_rank,
|
|
||||||
mark: Mark::NONE,
|
|
||||||
copy: OptVariable::NONE,
|
|
||||||
};
|
|
||||||
|
|
||||||
let content = desc.content;
|
let desc = subs.get_ref_mut(var);
|
||||||
let copy = var;
|
if desc.copy.is_some() {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
pools.get_mut(max_rank).push(copy);
|
desc.rank = Rank::NONE;
|
||||||
|
desc.mark = Mark::NONE;
|
||||||
|
desc.copy = OptVariable::from(var);
|
||||||
|
|
||||||
// Link the original variable to the new variable. This lets us
|
use Content::*;
|
||||||
// avoid making multiple copies of the variable we are instantiating.
|
use FlatType::*;
|
||||||
//
|
|
||||||
// Need to do this before recursively copying to avoid looping.
|
|
||||||
subs.set(
|
|
||||||
var,
|
|
||||||
Descriptor {
|
|
||||||
content: content.clone(),
|
|
||||||
rank: desc.rank,
|
|
||||||
mark: Mark::NONE,
|
|
||||||
copy: copy.into(),
|
|
||||||
},
|
|
||||||
);
|
|
||||||
|
|
||||||
// Now we recursively copy the content of the variable.
|
match &desc.content {
|
||||||
// We have already marked the variable as copied, so we
|
RigidVar(name) => {
|
||||||
// will not repeat this work or crawl this variable again.
|
// what it's all about: convert the rigid var into a flex var
|
||||||
match content {
|
let name = name.clone();
|
||||||
Structure(flat_type) => {
|
|
||||||
match flat_type {
|
// NOTE: we must write to the mutually borrowed `desc` value here
|
||||||
|
// using `subs.set` does not work (unclear why, really)
|
||||||
|
// but get_ref_mut approach saves a lookup, so the weirdness is worth it
|
||||||
|
desc.content = FlexVar(Some(name));
|
||||||
|
desc.rank = max_rank;
|
||||||
|
desc.mark = Mark::NONE;
|
||||||
|
desc.copy = OptVariable::NONE;
|
||||||
|
}
|
||||||
|
FlexVar(_) | Error => (),
|
||||||
|
|
||||||
|
RecursionVar { structure, .. } => {
|
||||||
|
stack.push(*structure);
|
||||||
|
}
|
||||||
|
|
||||||
|
Structure(flat_type) => match flat_type {
|
||||||
Apply(_, args) => {
|
Apply(_, args) => {
|
||||||
for var_index in args.into_iter() {
|
stack.extend(var_slice!(*args));
|
||||||
let var = subs[var_index];
|
|
||||||
instantiate_rigids_help(subs, max_rank, pools, var);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Func(arg_vars, closure_var, ret_var) => {
|
Func(arg_vars, closure_var, ret_var) => {
|
||||||
instantiate_rigids_help(subs, max_rank, pools, ret_var);
|
let arg_vars = *arg_vars;
|
||||||
instantiate_rigids_help(subs, max_rank, pools, closure_var);
|
let ret_var = *ret_var;
|
||||||
|
let closure_var = *closure_var;
|
||||||
|
|
||||||
for index in arg_vars.into_iter() {
|
stack.extend(var_slice!(arg_vars));
|
||||||
let var = subs[index];
|
|
||||||
instantiate_rigids_help(subs, max_rank, pools, var);
|
stack.push(ret_var);
|
||||||
}
|
stack.push(closure_var);
|
||||||
}
|
}
|
||||||
|
|
||||||
EmptyRecord | EmptyTagUnion | Erroneous(_) => {}
|
EmptyRecord => (),
|
||||||
|
EmptyTagUnion => (),
|
||||||
|
|
||||||
Record(fields, ext_var) => {
|
Record(fields, ext_var) => {
|
||||||
for index in fields.iter_variables() {
|
let fields = *fields;
|
||||||
let var = subs[index];
|
let ext_var = *ext_var;
|
||||||
instantiate_rigids_help(subs, max_rank, pools, var);
|
stack.extend(var_slice!(fields.variables()));
|
||||||
}
|
|
||||||
|
|
||||||
instantiate_rigids_help(subs, max_rank, pools, ext_var);
|
stack.push(ext_var);
|
||||||
}
|
}
|
||||||
|
|
||||||
TagUnion(tags, ext_var) => {
|
TagUnion(tags, ext_var) => {
|
||||||
for (_, index) in tags.iter_all() {
|
let tags = *tags;
|
||||||
let slice = subs[index];
|
let ext_var = *ext_var;
|
||||||
for var_index in slice {
|
|
||||||
let var = subs[var_index];
|
for slice_index in tags.variables() {
|
||||||
instantiate_rigids_help(subs, max_rank, pools, var);
|
let slice = subs.variable_slices[slice_index.start as usize];
|
||||||
}
|
stack.extend(var_slice!(slice));
|
||||||
}
|
}
|
||||||
|
|
||||||
instantiate_rigids_help(subs, max_rank, pools, ext_var);
|
stack.push(ext_var);
|
||||||
}
|
}
|
||||||
|
FunctionOrTagUnion(_, _, ext_var) => {
|
||||||
FunctionOrTagUnion(_tag_name, _symbol, ext_var) => {
|
stack.push(*ext_var);
|
||||||
instantiate_rigids_help(subs, max_rank, pools, ext_var);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
RecursiveTagUnion(rec_var, tags, ext_var) => {
|
RecursiveTagUnion(rec_var, tags, ext_var) => {
|
||||||
instantiate_rigids_help(subs, max_rank, pools, rec_var);
|
let tags = *tags;
|
||||||
|
let ext_var = *ext_var;
|
||||||
|
let rec_var = *rec_var;
|
||||||
|
|
||||||
for (_, index) in tags.iter_all() {
|
for slice_index in tags.variables() {
|
||||||
let slice = subs[index];
|
let slice = subs.variable_slices[slice_index.start as usize];
|
||||||
for var_index in slice {
|
stack.extend(var_slice!(slice));
|
||||||
let var = subs[var_index];
|
|
||||||
instantiate_rigids_help(subs, max_rank, pools, var);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
instantiate_rigids_help(subs, max_rank, pools, ext_var);
|
stack.push(ext_var);
|
||||||
|
stack.push(rec_var);
|
||||||
}
|
}
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
FlexVar(_) | Error => {}
|
Erroneous(_) => (),
|
||||||
|
},
|
||||||
|
Alias(_, args, var) => {
|
||||||
|
let var = *var;
|
||||||
|
let args = *args;
|
||||||
|
|
||||||
RecursionVar { structure, .. } => {
|
stack.extend(var_slice!(args.variables()));
|
||||||
instantiate_rigids_help(subs, max_rank, pools, structure);
|
|
||||||
}
|
|
||||||
|
|
||||||
RigidVar(name) => {
|
stack.push(var);
|
||||||
// what it's all about: convert the rigid var into a flex var
|
|
||||||
subs.set(copy, make_descriptor(FlexVar(Some(name))));
|
|
||||||
}
|
|
||||||
|
|
||||||
Alias(_symbol, args, real_type_var) => {
|
|
||||||
for var_index in args.variables().into_iter() {
|
|
||||||
let var = subs[var_index];
|
|
||||||
instantiate_rigids_help(subs, max_rank, pools, var);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
instantiate_rigids_help(subs, max_rank, pools, real_type_var);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var
|
// we have tracked all visited variables, and can now traverse them
|
||||||
|
// in one go (without looking at the UnificationTable) and clear the copy field
|
||||||
|
for var in visited {
|
||||||
|
let descriptor = subs.get_ref_mut(var);
|
||||||
|
|
||||||
|
if descriptor.copy.is_some() {
|
||||||
|
descriptor.rank = Rank::NONE;
|
||||||
|
descriptor.mark = Mark::NONE;
|
||||||
|
descriptor.copy = OptVariable::NONE;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn deep_copy_var(subs: &mut Subs, rank: Rank, pools: &mut Pools, var: Variable) -> Variable {
|
fn deep_copy_var(subs: &mut Subs, rank: Rank, pools: &mut Pools, var: Variable) -> Variable {
|
||||||
|
|
|
@ -3813,6 +3813,17 @@ mod solve_expr {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn list_intersperse() {
|
||||||
|
infer_eq_without_problem(
|
||||||
|
indoc!(
|
||||||
|
r#"
|
||||||
|
List.intersperse
|
||||||
|
"#
|
||||||
|
),
|
||||||
|
"List a, a -> List a",
|
||||||
|
);
|
||||||
|
}
|
||||||
#[test]
|
#[test]
|
||||||
fn function_that_captures_nothing_is_not_captured() {
|
fn function_that_captures_nothing_is_not_captured() {
|
||||||
// we should make sure that a function that doesn't capture anything it not itself captured
|
// we should make sure that a function that doesn't capture anything it not itself captured
|
||||||
|
|
|
@ -318,6 +318,29 @@ fn list_drop_at() {
|
||||||
assert_evals_to!("List.dropAt [0] 0", RocList::from_slice(&[]), RocList<i64>);
|
assert_evals_to!("List.dropAt [0] 0", RocList::from_slice(&[]), RocList<i64>);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
#[cfg(any(feature = "gen-llvm"))]
|
||||||
|
fn list_intersperse() {
|
||||||
|
assert_evals_to!(
|
||||||
|
indoc!(
|
||||||
|
r#"
|
||||||
|
List.intersperse [0, 0, 0] 1
|
||||||
|
"#
|
||||||
|
),
|
||||||
|
RocList::from_slice(&[0, 1, 0, 1, 0]),
|
||||||
|
RocList<i64>
|
||||||
|
);
|
||||||
|
assert_evals_to!(
|
||||||
|
indoc!(
|
||||||
|
r#"
|
||||||
|
List.intersperse [] 1
|
||||||
|
"#
|
||||||
|
),
|
||||||
|
RocList::from_slice(&[]),
|
||||||
|
RocList<i64>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
#[cfg(any(feature = "gen-llvm"))]
|
#[cfg(any(feature = "gen-llvm"))]
|
||||||
fn list_drop_at_shared() {
|
fn list_drop_at_shared() {
|
||||||
|
@ -2393,6 +2416,28 @@ fn list_any_empty_with_unknown_element_type() {
|
||||||
assert_evals_to!("List.any [] (\\_ -> True)", false, bool);
|
assert_evals_to!("List.any [] (\\_ -> True)", false, bool);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
#[cfg(any(feature = "gen-llvm"))]
|
||||||
|
fn list_all() {
|
||||||
|
assert_evals_to!("List.all [] (\\e -> e > 3)", true, bool);
|
||||||
|
assert_evals_to!("List.all [ 1, 2, 3 ] (\\e -> e > 3)", false, bool);
|
||||||
|
assert_evals_to!("List.all [ 1, 2, 4 ] (\\e -> e > 3)", false, bool);
|
||||||
|
assert_evals_to!("List.all [ 1, 2, 3 ] (\\e -> e >= 1)", true, bool);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
#[cfg(any(feature = "gen-llvm"))]
|
||||||
|
#[should_panic(expected = r#"Roc failed with message: "UnresolvedTypeVar"#)]
|
||||||
|
fn list_all_empty_with_unknown_element_type() {
|
||||||
|
// Segfaults with invalid memory reference. Running this as a stand-alone
|
||||||
|
// Roc program, generates the following error message:
|
||||||
|
//
|
||||||
|
// Application crashed with message
|
||||||
|
// UnresolvedTypeVar compiler/mono/src/ir.rs line 3775
|
||||||
|
// Shutting down
|
||||||
|
assert_evals_to!("List.all [] (\\_ -> True)", false, bool);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
#[cfg(any(feature = "gen-llvm"))]
|
#[cfg(any(feature = "gen-llvm"))]
|
||||||
#[should_panic(expected = r#"Roc failed with message: "invalid ret_layout""#)]
|
#[should_panic(expected = r#"Roc failed with message: "invalid ret_layout""#)]
|
||||||
|
|
|
@ -968,3 +968,24 @@ fn update_the_only_field() {
|
||||||
i64
|
i64
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
|
||||||
|
// https://github.com/rtfeldman/roc/issues/1513
|
||||||
|
fn both_have_unique_fields() {
|
||||||
|
assert_evals_to!(
|
||||||
|
indoc!(
|
||||||
|
r#"
|
||||||
|
a = { x: 42, y: 43 }
|
||||||
|
b = { x: 42, z: 44 }
|
||||||
|
|
||||||
|
f : { x : I64 }a, { x : I64 }b -> I64
|
||||||
|
f = \{ x: x1}, { x: x2 } -> x1 + x2
|
||||||
|
|
||||||
|
f a b
|
||||||
|
"#
|
||||||
|
),
|
||||||
|
84,
|
||||||
|
i64
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
|
@ -305,129 +305,95 @@ fn small_str_concat_empty_first_arg() {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// #[test]
|
#[test]
|
||||||
// fn small_str_concat_empty_second_arg() {
|
fn small_str_concat_empty_second_arg() {
|
||||||
// assert_llvm_evals_to!(
|
assert_evals_to!(
|
||||||
// r#"Str.concat "JJJJJJJJJJJJJJJ" """#,
|
r#"Str.concat "JJJJJJJ" """#,
|
||||||
// [
|
[0x4a, 0x4a, 0x4a, 0x4a, 0x4a, 0x4a, 0x4a, 0b1000_0111],
|
||||||
// 0x4a,
|
[u8; 8]
|
||||||
// 0x4a,
|
);
|
||||||
// 0x4a,
|
}
|
||||||
// 0x4a,
|
|
||||||
// 0x4a,
|
|
||||||
// 0x4a,
|
|
||||||
// 0x4a,
|
|
||||||
// 0x4a,
|
|
||||||
// 0x4a,
|
|
||||||
// 0x4a,
|
|
||||||
// 0x4a,
|
|
||||||
// 0x4a,
|
|
||||||
// 0x4a,
|
|
||||||
// 0x4a,
|
|
||||||
// 0x4a,
|
|
||||||
// 0b1000_1111
|
|
||||||
// ],
|
|
||||||
// [u8; 16]
|
|
||||||
// );
|
|
||||||
// }
|
|
||||||
|
|
||||||
// #[test]
|
#[test]
|
||||||
// fn small_str_concat_small_to_big() {
|
fn small_str_concat_small_to_big() {
|
||||||
// assert_evals_to!(
|
assert_evals_to!(
|
||||||
// r#"Str.concat "abc" " this is longer than 15 chars""#,
|
r#"Str.concat "abc" " this is longer than 7 chars""#,
|
||||||
// RocStr::from_slice(b"abc this is longer than 15 chars"),
|
RocStr::from_slice(b"abc this is longer than 7 chars"),
|
||||||
// RocStr
|
RocStr
|
||||||
// );
|
);
|
||||||
// }
|
}
|
||||||
|
|
||||||
// #[test]
|
#[test]
|
||||||
// fn small_str_concat_small_to_small_staying_small() {
|
fn small_str_concat_small_to_small_staying_small() {
|
||||||
// assert_llvm_evals_to!(
|
assert_evals_to!(
|
||||||
// r#"Str.concat "J" "JJJJJJJJJJJJJJ""#,
|
r#"Str.concat "J" "JJJJJJ""#,
|
||||||
// [
|
[0x4a, 0x4a, 0x4a, 0x4a, 0x4a, 0x4a, 0x4a, 0b1000_0111],
|
||||||
// 0x4a,
|
[u8; 8]
|
||||||
// 0x4a,
|
);
|
||||||
// 0x4a,
|
}
|
||||||
// 0x4a,
|
|
||||||
// 0x4a,
|
|
||||||
// 0x4a,
|
|
||||||
// 0x4a,
|
|
||||||
// 0x4a,
|
|
||||||
// 0x4a,
|
|
||||||
// 0x4a,
|
|
||||||
// 0x4a,
|
|
||||||
// 0x4a,
|
|
||||||
// 0x4a,
|
|
||||||
// 0x4a,
|
|
||||||
// 0x4a,
|
|
||||||
// 0b1000_1111
|
|
||||||
// ],
|
|
||||||
// [u8; 16]
|
|
||||||
// );
|
|
||||||
// }
|
|
||||||
|
|
||||||
// #[test]
|
#[test]
|
||||||
// fn small_str_concat_small_to_small_overflow_to_big() {
|
fn small_str_concat_small_to_small_overflow_to_big() {
|
||||||
// assert_evals_to!(
|
assert_evals_to!(
|
||||||
// r#"Str.concat "abcdefghijklm" "nopqrstuvwxyz""#,
|
r#"Str.concat "abcdefg" "hijklmn""#,
|
||||||
// RocStr::from_slice(b"abcdefghijklmnopqrstuvwxyz"),
|
RocStr::from_slice(b"abcdefghijklmn"),
|
||||||
// RocStr
|
RocStr
|
||||||
// );
|
);
|
||||||
// }
|
}
|
||||||
|
|
||||||
// #[test]
|
#[test]
|
||||||
// fn str_concat_empty() {
|
fn str_concat_empty() {
|
||||||
// assert_evals_to!(r#"Str.concat "" """#, RocStr::default(), RocStr);
|
assert_evals_to!(r#"Str.concat "" """#, RocStr::default(), RocStr);
|
||||||
// }
|
}
|
||||||
|
|
||||||
// #[test]
|
#[test]
|
||||||
// fn small_str_is_empty() {
|
fn small_str_is_empty() {
|
||||||
// assert_evals_to!(r#"Str.isEmpty "abc""#, false, bool);
|
assert_evals_to!(r#"Str.isEmpty "abc""#, false, bool);
|
||||||
// }
|
}
|
||||||
|
|
||||||
// #[test]
|
#[test]
|
||||||
// fn big_str_is_empty() {
|
fn big_str_is_empty() {
|
||||||
// assert_evals_to!(
|
assert_evals_to!(
|
||||||
// r#"Str.isEmpty "this is more than 15 chars long""#,
|
r#"Str.isEmpty "this is more than 15 chars long""#,
|
||||||
// false,
|
false,
|
||||||
// bool
|
bool
|
||||||
// );
|
);
|
||||||
// }
|
}
|
||||||
|
|
||||||
// #[test]
|
#[test]
|
||||||
// fn empty_str_is_empty() {
|
fn empty_str_is_empty() {
|
||||||
// assert_evals_to!(r#"Str.isEmpty """#, true, bool);
|
assert_evals_to!(r#"Str.isEmpty """#, true, bool);
|
||||||
// }
|
}
|
||||||
|
|
||||||
// #[test]
|
#[test]
|
||||||
// fn str_starts_with() {
|
fn str_starts_with() {
|
||||||
// assert_evals_to!(r#"Str.startsWith "hello world" "hell""#, true, bool);
|
assert_evals_to!(r#"Str.startsWith "hello world" "hell""#, true, bool);
|
||||||
// assert_evals_to!(r#"Str.startsWith "hello world" """#, true, bool);
|
assert_evals_to!(r#"Str.startsWith "hello world" """#, true, bool);
|
||||||
// assert_evals_to!(r#"Str.startsWith "nope" "hello world""#, false, bool);
|
assert_evals_to!(r#"Str.startsWith "nope" "hello world""#, false, bool);
|
||||||
// assert_evals_to!(r#"Str.startsWith "hell" "hello world""#, false, bool);
|
assert_evals_to!(r#"Str.startsWith "hell" "hello world""#, false, bool);
|
||||||
// assert_evals_to!(r#"Str.startsWith "" "hello world""#, false, bool);
|
assert_evals_to!(r#"Str.startsWith "" "hello world""#, false, bool);
|
||||||
// }
|
}
|
||||||
|
|
||||||
// #[test]
|
#[test]
|
||||||
// fn str_starts_with_code_point() {
|
fn str_starts_with_code_point() {
|
||||||
// assert_evals_to!(
|
assert_evals_to!(
|
||||||
// &format!(r#"Str.startsWithCodePt "foobar" {}"#, 'f' as u32),
|
&format!(r#"Str.startsWithCodePt "foobar" {}"#, 'f' as u32),
|
||||||
// true,
|
true,
|
||||||
// bool
|
bool
|
||||||
// );
|
);
|
||||||
// assert_evals_to!(
|
assert_evals_to!(
|
||||||
// &format!(r#"Str.startsWithCodePt "zoobar" {}"#, 'f' as u32),
|
&format!(r#"Str.startsWithCodePt "zoobar" {}"#, 'f' as u32),
|
||||||
// false,
|
false,
|
||||||
// bool
|
bool
|
||||||
// );
|
);
|
||||||
// }
|
}
|
||||||
|
|
||||||
// #[test]
|
#[test]
|
||||||
// fn str_ends_with() {
|
fn str_ends_with() {
|
||||||
// assert_evals_to!(r#"Str.endsWith "hello world" "world""#, true, bool);
|
assert_evals_to!(r#"Str.endsWith "hello world" "world""#, true, bool);
|
||||||
// assert_evals_to!(r#"Str.endsWith "nope" "hello world""#, false, bool);
|
assert_evals_to!(r#"Str.endsWith "nope" "hello world""#, false, bool);
|
||||||
// assert_evals_to!(r#"Str.endsWith "" "hello world""#, false, bool);
|
assert_evals_to!(r#"Str.endsWith "" "hello world""#, false, bool);
|
||||||
// }
|
}
|
||||||
|
|
||||||
// #[test]
|
// #[test]
|
||||||
// fn str_count_graphemes_small_str() {
|
// fn str_count_graphemes_small_str() {
|
||||||
|
@ -448,37 +414,37 @@ fn small_str_concat_empty_first_arg() {
|
||||||
// );
|
// );
|
||||||
// }
|
// }
|
||||||
|
|
||||||
// #[test]
|
#[test]
|
||||||
// fn str_starts_with_same_big_str() {
|
fn str_starts_with_same_big_str() {
|
||||||
// assert_evals_to!(
|
assert_evals_to!(
|
||||||
// r#"Str.startsWith "123456789123456789" "123456789123456789""#,
|
r#"Str.startsWith "123456789123456789" "123456789123456789""#,
|
||||||
// true,
|
true,
|
||||||
// bool
|
bool
|
||||||
// );
|
);
|
||||||
// }
|
}
|
||||||
|
|
||||||
// #[test]
|
#[test]
|
||||||
// fn str_starts_with_different_big_str() {
|
fn str_starts_with_different_big_str() {
|
||||||
// assert_evals_to!(
|
assert_evals_to!(
|
||||||
// r#"Str.startsWith "12345678912345678910" "123456789123456789""#,
|
r#"Str.startsWith "12345678912345678910" "123456789123456789""#,
|
||||||
// true,
|
true,
|
||||||
// bool
|
bool
|
||||||
// );
|
);
|
||||||
// }
|
}
|
||||||
|
|
||||||
// #[test]
|
#[test]
|
||||||
// fn str_starts_with_same_small_str() {
|
fn str_starts_with_same_small_str() {
|
||||||
// assert_evals_to!(r#"Str.startsWith "1234" "1234""#, true, bool);
|
assert_evals_to!(r#"Str.startsWith "1234" "1234""#, true, bool);
|
||||||
// }
|
}
|
||||||
|
|
||||||
// #[test]
|
#[test]
|
||||||
// fn str_starts_with_different_small_str() {
|
fn str_starts_with_different_small_str() {
|
||||||
// assert_evals_to!(r#"Str.startsWith "1234" "12""#, true, bool);
|
assert_evals_to!(r#"Str.startsWith "1234" "12""#, true, bool);
|
||||||
// }
|
}
|
||||||
// #[test]
|
#[test]
|
||||||
// fn str_starts_with_false_small_str() {
|
fn str_starts_with_false_small_str() {
|
||||||
// assert_evals_to!(r#"Str.startsWith "1234" "23""#, false, bool);
|
assert_evals_to!(r#"Str.startsWith "1234" "23""#, false, bool);
|
||||||
// }
|
}
|
||||||
|
|
||||||
// #[test]
|
// #[test]
|
||||||
// fn str_from_int() {
|
// fn str_from_int() {
|
||||||
|
@ -934,121 +900,239 @@ fn small_str_concat_empty_first_arg() {
|
||||||
// );
|
// );
|
||||||
// }
|
// }
|
||||||
|
|
||||||
// #[test]
|
#[test]
|
||||||
// fn str_repeat_small() {
|
fn str_repeat_small() {
|
||||||
// assert_evals_to!(
|
assert_evals_to!(
|
||||||
// indoc!(r#"Str.repeat "Roc" 3"#),
|
indoc!(r#"Str.repeat "Roc" 3"#),
|
||||||
// RocStr::from("RocRocRoc"),
|
RocStr::from("RocRocRoc"),
|
||||||
// RocStr
|
RocStr
|
||||||
// );
|
);
|
||||||
// }
|
}
|
||||||
|
|
||||||
// #[test]
|
#[test]
|
||||||
// fn str_repeat_big() {
|
fn str_repeat_big() {
|
||||||
// assert_evals_to!(
|
assert_evals_to!(
|
||||||
// indoc!(r#"Str.repeat "more than 16 characters" 2"#),
|
indoc!(r#"Str.repeat "more than 16 characters" 2"#),
|
||||||
// RocStr::from("more than 16 charactersmore than 16 characters"),
|
RocStr::from("more than 16 charactersmore than 16 characters"),
|
||||||
// RocStr
|
RocStr
|
||||||
// );
|
);
|
||||||
// }
|
}
|
||||||
|
|
||||||
// #[test]
|
#[test]
|
||||||
// fn str_repeat_empty_string() {
|
fn str_repeat_empty_string() {
|
||||||
// assert_evals_to!(indoc!(r#"Str.repeat "" 3"#), RocStr::from(""), RocStr);
|
assert_evals_to!(indoc!(r#"Str.repeat "" 3"#), RocStr::from(""), RocStr);
|
||||||
// }
|
}
|
||||||
|
|
||||||
// #[test]
|
#[test]
|
||||||
// fn str_repeat_zero_times() {
|
fn str_repeat_zero_times() {
|
||||||
// assert_evals_to!(indoc!(r#"Str.repeat "Roc" 0"#), RocStr::from(""), RocStr);
|
assert_evals_to!(indoc!(r#"Str.repeat "Roc" 0"#), RocStr::from(""), RocStr);
|
||||||
// }
|
}
|
||||||
|
|
||||||
// #[test]
|
#[test]
|
||||||
// fn str_trim_empty_string() {
|
fn str_trim_empty_string() {
|
||||||
// assert_evals_to!(indoc!(r#"Str.trim """#), RocStr::from(""), RocStr);
|
assert_evals_to!(indoc!(r#"Str.trim """#), RocStr::from(""), RocStr);
|
||||||
// }
|
}
|
||||||
|
|
||||||
// #[test]
|
#[test]
|
||||||
// fn str_trim_small_blank_string() {
|
fn str_trim_small_blank_string() {
|
||||||
// assert_evals_to!(indoc!(r#"Str.trim " ""#), RocStr::from(""), RocStr);
|
assert_evals_to!(indoc!(r#"Str.trim " ""#), RocStr::from(""), RocStr);
|
||||||
// }
|
}
|
||||||
|
|
||||||
// #[test]
|
#[test]
|
||||||
// fn str_trim_small_to_small() {
|
fn str_trim_small_to_small() {
|
||||||
// assert_evals_to!(
|
assert_evals_to!(
|
||||||
// indoc!(r#"Str.trim " hello world ""#),
|
indoc!(r#"Str.trim " hello world ""#),
|
||||||
// RocStr::from("hello world"),
|
RocStr::from("hello world"),
|
||||||
// RocStr
|
RocStr
|
||||||
// );
|
);
|
||||||
// }
|
}
|
||||||
|
|
||||||
// #[test]
|
#[test]
|
||||||
// fn str_trim_large_to_large_unique() {
|
fn str_trim_large_to_large_unique() {
|
||||||
// assert_evals_to!(
|
assert_evals_to!(
|
||||||
// indoc!(r#"Str.trim (Str.concat " " "hello world from a large string ")"#),
|
indoc!(r#"Str.trim (Str.concat " " "hello world from a large string ")"#),
|
||||||
// RocStr::from("hello world from a large string"),
|
RocStr::from("hello world from a large string"),
|
||||||
// RocStr
|
RocStr
|
||||||
// );
|
);
|
||||||
// }
|
}
|
||||||
|
|
||||||
// #[test]
|
#[test]
|
||||||
// fn str_trim_large_to_small_unique() {
|
fn str_trim_large_to_small_unique() {
|
||||||
// assert_evals_to!(
|
assert_evals_to!(
|
||||||
// indoc!(r#"Str.trim (Str.concat " " "hello world ")"#),
|
indoc!(r#"Str.trim (Str.concat " " "hello world ")"#),
|
||||||
// RocStr::from("hello world"),
|
RocStr::from("hello world"),
|
||||||
// RocStr
|
RocStr
|
||||||
// );
|
);
|
||||||
// }
|
}
|
||||||
|
|
||||||
// #[test]
|
#[test]
|
||||||
// fn str_trim_large_to_large_shared() {
|
fn str_trim_large_to_large_shared() {
|
||||||
// assert_evals_to!(
|
assert_evals_to!(
|
||||||
// indoc!(
|
indoc!(
|
||||||
// r#"
|
r#"
|
||||||
// original : Str
|
original : Str
|
||||||
// original = " hello world world "
|
original = " hello world world "
|
||||||
|
|
||||||
// { trimmed: Str.trim original, original: original }
|
{ trimmed: Str.trim original, original: original }
|
||||||
// "#
|
"#
|
||||||
// ),
|
),
|
||||||
// (
|
(
|
||||||
// RocStr::from(" hello world world "),
|
RocStr::from(" hello world world "),
|
||||||
// RocStr::from("hello world world"),
|
RocStr::from("hello world world"),
|
||||||
// ),
|
),
|
||||||
// (RocStr, RocStr)
|
(RocStr, RocStr)
|
||||||
// );
|
);
|
||||||
// }
|
}
|
||||||
|
|
||||||
// #[test]
|
#[test]
|
||||||
// fn str_trim_large_to_small_shared() {
|
fn str_trim_large_to_small_shared() {
|
||||||
// assert_evals_to!(
|
assert_evals_to!(
|
||||||
// indoc!(
|
indoc!(
|
||||||
// r#"
|
r#"
|
||||||
// original : Str
|
original : Str
|
||||||
// original = " hello world "
|
original = " hello world "
|
||||||
|
|
||||||
// { trimmed: Str.trim original, original: original }
|
{ trimmed: Str.trim original, original: original }
|
||||||
// "#
|
"#
|
||||||
// ),
|
),
|
||||||
// (
|
(
|
||||||
// RocStr::from(" hello world "),
|
RocStr::from(" hello world "),
|
||||||
// RocStr::from("hello world"),
|
RocStr::from("hello world"),
|
||||||
// ),
|
),
|
||||||
// (RocStr, RocStr)
|
(RocStr, RocStr)
|
||||||
// );
|
);
|
||||||
// }
|
}
|
||||||
|
|
||||||
// #[test]
|
#[test]
|
||||||
// fn str_trim_small_to_small_shared() {
|
fn str_trim_small_to_small_shared() {
|
||||||
// assert_evals_to!(
|
assert_evals_to!(
|
||||||
// indoc!(
|
indoc!(
|
||||||
// r#"
|
r#"
|
||||||
// original : Str
|
original : Str
|
||||||
// original = " hello world "
|
original = " hello world "
|
||||||
|
|
||||||
// { trimmed: Str.trim original, original: original }
|
{ trimmed: Str.trim original, original: original }
|
||||||
// "#
|
"#
|
||||||
// ),
|
),
|
||||||
// (RocStr::from(" hello world "), RocStr::from("hello world"),),
|
(RocStr::from(" hello world "), RocStr::from("hello world"),),
|
||||||
// (RocStr, RocStr)
|
(RocStr, RocStr)
|
||||||
// );
|
);
|
||||||
// }
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn str_trim_left_small_blank_string() {
|
||||||
|
assert_evals_to!(indoc!(r#"Str.trimLeft " ""#), RocStr::from(""), RocStr);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn str_trim_left_small_to_small() {
|
||||||
|
assert_evals_to!(
|
||||||
|
indoc!(r#"Str.trimLeft " hello world ""#),
|
||||||
|
RocStr::from("hello world "),
|
||||||
|
RocStr
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn str_trim_left_large_to_large_unique() {
|
||||||
|
assert_evals_to!(
|
||||||
|
indoc!(r#"Str.trimLeft (Str.concat " " "hello world from a large string ")"#),
|
||||||
|
RocStr::from("hello world from a large string "),
|
||||||
|
RocStr
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn str_trim_left_large_to_small_unique() {
|
||||||
|
assert_evals_to!(
|
||||||
|
indoc!(r#"Str.trimLeft (Str.concat " " "hello world ")"#),
|
||||||
|
RocStr::from("hello world "),
|
||||||
|
RocStr
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn str_trim_right_small_blank_string() {
|
||||||
|
assert_evals_to!(indoc!(r#"Str.trimRight " ""#), RocStr::from(""), RocStr);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn str_trim_right_small_to_small() {
|
||||||
|
assert_evals_to!(
|
||||||
|
indoc!(r#"Str.trimRight " hello world ""#),
|
||||||
|
RocStr::from(" hello world"),
|
||||||
|
RocStr
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn str_trim_right_large_to_large_unique() {
|
||||||
|
assert_evals_to!(
|
||||||
|
indoc!(r#"Str.trimRight (Str.concat " hello world from a large string" " ")"#),
|
||||||
|
RocStr::from(" hello world from a large string"),
|
||||||
|
RocStr
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn str_trim_right_large_to_small_unique() {
|
||||||
|
assert_evals_to!(
|
||||||
|
indoc!(r#"Str.trimRight (Str.concat " hello world" " ")"#),
|
||||||
|
RocStr::from(" hello world"),
|
||||||
|
RocStr
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn str_trim_right_large_to_large_shared() {
|
||||||
|
assert_evals_to!(
|
||||||
|
indoc!(
|
||||||
|
r#"
|
||||||
|
original : Str
|
||||||
|
original = " hello world world "
|
||||||
|
|
||||||
|
{ trimmed: Str.trimRight original, original: original }
|
||||||
|
"#
|
||||||
|
),
|
||||||
|
(
|
||||||
|
RocStr::from(" hello world world "),
|
||||||
|
RocStr::from(" hello world world"),
|
||||||
|
),
|
||||||
|
(RocStr, RocStr)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn str_trim_right_large_to_small_shared() {
|
||||||
|
assert_evals_to!(
|
||||||
|
indoc!(
|
||||||
|
r#"
|
||||||
|
original : Str
|
||||||
|
original = " hello world "
|
||||||
|
|
||||||
|
{ trimmed: Str.trimRight original, original: original }
|
||||||
|
"#
|
||||||
|
),
|
||||||
|
(
|
||||||
|
RocStr::from(" hello world "),
|
||||||
|
RocStr::from(" hello world"),
|
||||||
|
),
|
||||||
|
(RocStr, RocStr)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn str_trim_right_small_to_small_shared() {
|
||||||
|
assert_evals_to!(
|
||||||
|
indoc!(
|
||||||
|
r#"
|
||||||
|
original : Str
|
||||||
|
original = " hello world "
|
||||||
|
|
||||||
|
{ trimmed: Str.trimRight original, original: original }
|
||||||
|
"#
|
||||||
|
),
|
||||||
|
(RocStr::from(" hello world "), RocStr::from(" hello world"),),
|
||||||
|
(RocStr, RocStr)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
procedure Bool.5 (#Attr.2, #Attr.3):
|
procedure Bool.7 (#Attr.2, #Attr.3):
|
||||||
let Test.11 = lowlevel Eq #Attr.2 #Attr.3;
|
let Test.11 = lowlevel Eq #Attr.2 #Attr.3;
|
||||||
ret Test.11;
|
ret Test.11;
|
||||||
|
|
||||||
|
@ -13,7 +13,7 @@ procedure Test.1 (Test.3):
|
||||||
ret Test.12;
|
ret Test.12;
|
||||||
in
|
in
|
||||||
let Test.10 = 5i64;
|
let Test.10 = 5i64;
|
||||||
let Test.9 = CallByName Bool.5 Test.6 Test.10;
|
let Test.9 = CallByName Bool.7 Test.6 Test.10;
|
||||||
jump Test.8 Test.9;
|
jump Test.8 Test.9;
|
||||||
|
|
||||||
procedure Test.0 ():
|
procedure Test.0 ():
|
||||||
|
|
|
@ -10,4 +10,5 @@ roc_collections = { path = "../collections" }
|
||||||
roc_region = { path = "../region" }
|
roc_region = { path = "../region" }
|
||||||
roc_module = { path = "../module" }
|
roc_module = { path = "../module" }
|
||||||
ven_ena = { path = "../../vendor/ena" }
|
ven_ena = { path = "../../vendor/ena" }
|
||||||
|
bumpalo = { version = "3.8.0", features = ["collections"] }
|
||||||
static_assertions = "1.1.0"
|
static_assertions = "1.1.0"
|
||||||
|
|
|
@ -9,10 +9,12 @@ use ven_ena::unify::{InPlace, Snapshot, UnificationTable, UnifyKey};
|
||||||
// if your changes cause this number to go down, great!
|
// if your changes cause this number to go down, great!
|
||||||
// please change it to the lower number.
|
// please change it to the lower number.
|
||||||
// if it went up, maybe check that the change is really required
|
// if it went up, maybe check that the change is really required
|
||||||
static_assertions::assert_eq_size!([u8; 48], Descriptor);
|
static_assertions::assert_eq_size!([u8; 6 * 8], Descriptor);
|
||||||
static_assertions::assert_eq_size!([u8; 32], Content);
|
static_assertions::assert_eq_size!([u8; 4 * 8], Content);
|
||||||
static_assertions::assert_eq_size!([u8; 24], FlatType);
|
static_assertions::assert_eq_size!([u8; 3 * 8], FlatType);
|
||||||
static_assertions::assert_eq_size!([u8; 48], Problem);
|
static_assertions::assert_eq_size!([u8; 6 * 8], Problem);
|
||||||
|
static_assertions::assert_eq_size!([u8; 12], UnionTags);
|
||||||
|
static_assertions::assert_eq_size!([u8; 2 * 8], RecordFields);
|
||||||
|
|
||||||
#[derive(Clone, Copy, Hash, PartialEq, Eq)]
|
#[derive(Clone, Copy, Hash, PartialEq, Eq)]
|
||||||
pub struct Mark(i32);
|
pub struct Mark(i32);
|
||||||
|
@ -61,16 +63,7 @@ pub struct Subs {
|
||||||
|
|
||||||
impl Default for Subs {
|
impl Default for Subs {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
Subs {
|
Subs::new()
|
||||||
utable: Default::default(),
|
|
||||||
variables: Default::default(),
|
|
||||||
tag_names: Default::default(),
|
|
||||||
field_names: Default::default(),
|
|
||||||
record_fields: Default::default(),
|
|
||||||
// store an empty slice at the first position
|
|
||||||
// used for "TagOrFunction"
|
|
||||||
variable_slices: vec![VariableSubsSlice::default()],
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -79,14 +72,14 @@ impl Default for Subs {
|
||||||
/// The starting position is a u32 which should be plenty
|
/// The starting position is a u32 which should be plenty
|
||||||
/// We limit slices to u16::MAX = 65535 elements
|
/// We limit slices to u16::MAX = 65535 elements
|
||||||
pub struct SubsSlice<T> {
|
pub struct SubsSlice<T> {
|
||||||
start: u32,
|
pub start: u32,
|
||||||
length: u16,
|
pub length: u16,
|
||||||
_marker: std::marker::PhantomData<T>,
|
_marker: std::marker::PhantomData<T>,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// An index into the Vec<T> of subs
|
/// An index into the Vec<T> of subs
|
||||||
pub struct SubsIndex<T> {
|
pub struct SubsIndex<T> {
|
||||||
start: u32,
|
pub start: u32,
|
||||||
_marker: std::marker::PhantomData<T>,
|
_marker: std::marker::PhantomData<T>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -324,7 +317,8 @@ fn subs_fmt_desc(this: &Descriptor, subs: &Subs, f: &mut fmt::Formatter) -> fmt:
|
||||||
subs_fmt_content(&this.content, subs, f)?;
|
subs_fmt_content(&this.content, subs, f)?;
|
||||||
|
|
||||||
write!(f, " r: {:?}", &this.rank)?;
|
write!(f, " r: {:?}", &this.rank)?;
|
||||||
write!(f, " m: {:?}", &this.mark)
|
write!(f, " m: {:?}", &this.mark)?;
|
||||||
|
write!(f, " c: {:?}", &this.copy)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct SubsFmtContent<'a>(pub &'a Content, pub &'a Subs);
|
pub struct SubsFmtContent<'a>(pub &'a Content, pub &'a Subs);
|
||||||
|
@ -679,11 +673,11 @@ impl Variable {
|
||||||
///
|
///
|
||||||
/// This should only ever be called from tests!
|
/// This should only ever be called from tests!
|
||||||
pub unsafe fn unsafe_test_debug_variable(v: u32) -> Self {
|
pub unsafe fn unsafe_test_debug_variable(v: u32) -> Self {
|
||||||
debug_assert!(v <= Self::NUM_RESERVED_VARS as u32);
|
debug_assert!(v >= Self::NUM_RESERVED_VARS as u32);
|
||||||
Variable(v)
|
Variable(v)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn index(&self) -> u32 {
|
pub const fn index(&self) -> u32 {
|
||||||
self.0
|
self.0
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -985,21 +979,31 @@ fn define_integer_types(subs: &mut Subs) {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Subs {
|
impl Subs {
|
||||||
pub fn new(var_store: VarStore) -> Self {
|
pub fn new() -> Self {
|
||||||
let entries = var_store.next;
|
Self::with_capacity(0)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn with_capacity(capacity: usize) -> Self {
|
||||||
|
let capacity = capacity.max(Variable::NUM_RESERVED_VARS);
|
||||||
|
|
||||||
let mut subs = Subs {
|
let mut subs = Subs {
|
||||||
utable: UnificationTable::default(),
|
utable: UnificationTable::default(),
|
||||||
..Default::default()
|
variables: Default::default(),
|
||||||
|
tag_names: Default::default(),
|
||||||
|
field_names: Default::default(),
|
||||||
|
record_fields: Default::default(),
|
||||||
|
// store an empty slice at the first position
|
||||||
|
// used for "TagOrFunction"
|
||||||
|
variable_slices: vec![VariableSubsSlice::default()],
|
||||||
};
|
};
|
||||||
|
|
||||||
// NOTE the utable does not (currently) have a with_capacity; using this as the next-best thing
|
// NOTE the utable does not (currently) have a with_capacity; using this as the next-best thing
|
||||||
subs.utable.reserve(entries as usize);
|
subs.utable.reserve(capacity);
|
||||||
|
|
||||||
// TODO There are at least these opportunities for performance optimization here:
|
// TODO There are at least these opportunities for performance optimization here:
|
||||||
// * Making the default flex_var_descriptor be all 0s, so no init step is needed.
|
// * Making the default flex_var_descriptor be all 0s, so no init step is needed.
|
||||||
|
|
||||||
for _ in 0..entries {
|
for _ in 0..capacity {
|
||||||
subs.utable.new_key(flex_var_descriptor());
|
subs.utable.new_key(flex_var_descriptor());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1040,7 +1044,14 @@ impl Subs {
|
||||||
subs
|
subs
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn new_from_varstore(var_store: VarStore) -> Self {
|
||||||
|
let entries = var_store.next;
|
||||||
|
|
||||||
|
Self::with_capacity(entries as usize)
|
||||||
|
}
|
||||||
|
|
||||||
pub fn extend_by(&mut self, entries: usize) {
|
pub fn extend_by(&mut self, entries: usize) {
|
||||||
|
self.utable.reserve(entries);
|
||||||
for _ in 0..entries {
|
for _ in 0..entries {
|
||||||
self.utable.new_key(flex_var_descriptor());
|
self.utable.new_key(flex_var_descriptor());
|
||||||
}
|
}
|
||||||
|
@ -1080,6 +1091,10 @@ impl Subs {
|
||||||
&self.utable.probe_value_ref(key).value
|
&self.utable.probe_value_ref(key).value
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn get_ref_mut(&mut self, key: Variable) -> &mut Descriptor {
|
||||||
|
&mut self.utable.probe_value_ref_mut(key).value
|
||||||
|
}
|
||||||
|
|
||||||
pub fn get_rank(&self, key: Variable) -> Rank {
|
pub fn get_rank(&self, key: Variable) -> Rank {
|
||||||
self.utable.probe_value_ref(key).value.rank
|
self.utable.probe_value_ref(key).value.rank
|
||||||
}
|
}
|
||||||
|
@ -1149,6 +1164,26 @@ impl Subs {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn modify<F>(&mut self, key: Variable, mapper: F)
|
||||||
|
where
|
||||||
|
F: Fn(&mut Descriptor),
|
||||||
|
{
|
||||||
|
mapper(self.get_ref_mut(key));
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_rank_set_mark(&mut self, key: Variable, mark: Mark) -> Rank {
|
||||||
|
let l_key = self.utable.get_root_key(key);
|
||||||
|
|
||||||
|
let mut rank = Rank::NONE;
|
||||||
|
|
||||||
|
self.utable.update_value(l_key, |node| {
|
||||||
|
node.value.mark = mark;
|
||||||
|
rank = node.value.rank;
|
||||||
|
});
|
||||||
|
|
||||||
|
rank
|
||||||
|
}
|
||||||
|
|
||||||
pub fn equivalent(&mut self, left: Variable, right: Variable) -> bool {
|
pub fn equivalent(&mut self, left: Variable, right: Variable) -> bool {
|
||||||
self.utable.unioned(left, right)
|
self.utable.unioned(left, right)
|
||||||
}
|
}
|
||||||
|
@ -1192,22 +1227,7 @@ impl Subs {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn restore(&mut self, var: Variable) {
|
pub fn restore(&mut self, var: Variable) {
|
||||||
let desc = self.get(var);
|
restore_help(self, var)
|
||||||
|
|
||||||
if desc.copy.is_some() {
|
|
||||||
let content = desc.content;
|
|
||||||
|
|
||||||
let desc = Descriptor {
|
|
||||||
content: content.clone(),
|
|
||||||
rank: Rank::NONE,
|
|
||||||
mark: Mark::NONE,
|
|
||||||
copy: OptVariable::NONE,
|
|
||||||
};
|
|
||||||
|
|
||||||
self.set(var, desc);
|
|
||||||
|
|
||||||
restore_content(self, &content);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn len(&self) -> usize {
|
pub fn len(&self) -> usize {
|
||||||
|
@ -1218,6 +1238,10 @@ impl Subs {
|
||||||
self.utable.is_empty()
|
self.utable.is_empty()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn contains(&self, var: Variable) -> bool {
|
||||||
|
(var.index() as usize) < self.len()
|
||||||
|
}
|
||||||
|
|
||||||
pub fn snapshot(&mut self) -> Snapshot<InPlace<Variable>> {
|
pub fn snapshot(&mut self) -> Snapshot<InPlace<Variable>> {
|
||||||
self.utable.snapshot()
|
self.utable.snapshot()
|
||||||
}
|
}
|
||||||
|
@ -1326,6 +1350,12 @@ impl From<Content> for Descriptor {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static_assertions::assert_eq_size!([u8; 4 * 8], Content);
|
||||||
|
static_assertions::assert_eq_size!([u8; 4 * 8], (Variable, Option<Lowercase>));
|
||||||
|
static_assertions::assert_eq_size!([u8; 3 * 8], (Symbol, AliasVariables, Variable));
|
||||||
|
static_assertions::assert_eq_size!([u8; 12], AliasVariables);
|
||||||
|
static_assertions::assert_eq_size!([u8; 3 * 8], FlatType);
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
pub enum Content {
|
pub enum Content {
|
||||||
/// A type variable which the user did not name in an annotation,
|
/// A type variable which the user did not name in an annotation,
|
||||||
|
@ -1347,10 +1377,10 @@ pub enum Content {
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug, Default)]
|
#[derive(Clone, Copy, Debug, Default)]
|
||||||
pub struct AliasVariables {
|
pub struct AliasVariables {
|
||||||
lowercases_start: u32,
|
pub lowercases_start: u32,
|
||||||
variables_start: u32,
|
pub variables_start: u32,
|
||||||
lowercases_len: u16,
|
pub lowercases_len: u16,
|
||||||
variables_len: u16,
|
pub variables_len: u16,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl AliasVariables {
|
impl AliasVariables {
|
||||||
|
@ -1466,6 +1496,8 @@ impl Content {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static_assertions::assert_eq_size!([u8; 3 * 8], FlatType);
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
pub enum FlatType {
|
pub enum FlatType {
|
||||||
Apply(Symbol, VariableSubsSlice),
|
Apply(Symbol, VariableSubsSlice),
|
||||||
|
@ -1489,7 +1521,7 @@ pub enum Builtin {
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug, Default)]
|
#[derive(Clone, Copy, Debug, Default)]
|
||||||
pub struct VariableSubsSlice {
|
pub struct VariableSubsSlice {
|
||||||
slice: SubsSlice<Variable>,
|
pub slice: SubsSlice<Variable>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl VariableSubsSlice {
|
impl VariableSubsSlice {
|
||||||
|
@ -1813,6 +1845,12 @@ impl RecordFields {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub const fn variables(&self) -> VariableSubsSlice {
|
||||||
|
let slice = SubsSlice::new(self.variables_start, self.length);
|
||||||
|
|
||||||
|
VariableSubsSlice { slice }
|
||||||
|
}
|
||||||
|
|
||||||
pub fn iter_variables(&self) -> impl Iterator<Item = SubsIndex<Variable>> {
|
pub fn iter_variables(&self) -> impl Iterator<Item = SubsIndex<Variable>> {
|
||||||
let slice = SubsSlice::new(self.variables_start, self.length);
|
let slice = SubsSlice::new(self.variables_start, self.length);
|
||||||
slice.into_iter()
|
slice.into_iter()
|
||||||
|
@ -2701,79 +2739,643 @@ fn get_fresh_var_name(state: &mut ErrorTypeState) -> Lowercase {
|
||||||
name
|
name
|
||||||
}
|
}
|
||||||
|
|
||||||
fn restore_content(subs: &mut Subs, content: &Content) {
|
fn restore_help(subs: &mut Subs, initial: Variable) {
|
||||||
use Content::*;
|
let mut stack = vec![initial];
|
||||||
use FlatType::*;
|
|
||||||
|
|
||||||
match content {
|
let variable_slices = &subs.variable_slices;
|
||||||
FlexVar(_) | RigidVar(_) | RecursionVar { .. } | Error => (),
|
|
||||||
|
|
||||||
Structure(flat_type) => match flat_type {
|
let variables = &subs.variables;
|
||||||
Apply(_, args) => {
|
let var_slice = |variable_subs_slice: VariableSubsSlice| {
|
||||||
for index in args.into_iter() {
|
&variables[variable_subs_slice.slice.start as usize..]
|
||||||
let var = subs[index];
|
[..variable_subs_slice.slice.length as usize]
|
||||||
subs.restore(var);
|
};
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Func(arg_vars, closure_var, ret_var) => {
|
while let Some(var) = stack.pop() {
|
||||||
for index in arg_vars.into_iter() {
|
let desc = &mut subs.utable.probe_value_ref_mut(var).value;
|
||||||
let var = subs[index];
|
|
||||||
subs.restore(var);
|
if desc.copy.is_some() {
|
||||||
|
desc.rank = Rank::NONE;
|
||||||
|
desc.mark = Mark::NONE;
|
||||||
|
desc.copy = OptVariable::NONE;
|
||||||
|
|
||||||
|
use Content::*;
|
||||||
|
use FlatType::*;
|
||||||
|
|
||||||
|
match &desc.content {
|
||||||
|
FlexVar(_) | RigidVar(_) | Error => (),
|
||||||
|
|
||||||
|
RecursionVar { structure, .. } => {
|
||||||
|
stack.push(*structure);
|
||||||
}
|
}
|
||||||
|
|
||||||
subs.restore(*ret_var);
|
Structure(flat_type) => match flat_type {
|
||||||
subs.restore(*closure_var);
|
Apply(_, args) => {
|
||||||
}
|
stack.extend(var_slice(*args));
|
||||||
|
|
||||||
EmptyRecord => (),
|
|
||||||
EmptyTagUnion => (),
|
|
||||||
|
|
||||||
Record(fields, ext_var) => {
|
|
||||||
for index in fields.iter_variables() {
|
|
||||||
let var = subs[index];
|
|
||||||
subs.restore(var);
|
|
||||||
}
|
|
||||||
|
|
||||||
subs.restore(*ext_var);
|
|
||||||
}
|
|
||||||
TagUnion(tags, ext_var) => {
|
|
||||||
for slice_index in tags.variables() {
|
|
||||||
let slice = subs[slice_index];
|
|
||||||
for var_index in slice {
|
|
||||||
let var = subs[var_index];
|
|
||||||
subs.restore(var);
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
subs.restore(*ext_var);
|
Func(arg_vars, closure_var, ret_var) => {
|
||||||
}
|
stack.extend(var_slice(*arg_vars));
|
||||||
FunctionOrTagUnion(_, _, ext_var) => {
|
|
||||||
subs.restore(*ext_var);
|
|
||||||
}
|
|
||||||
|
|
||||||
RecursiveTagUnion(rec_var, tags, ext_var) => {
|
stack.push(*ret_var);
|
||||||
for slice_index in tags.variables() {
|
stack.push(*closure_var);
|
||||||
let slice = subs[slice_index];
|
|
||||||
for var_index in slice {
|
|
||||||
let var = subs[var_index];
|
|
||||||
subs.restore(var);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
EmptyRecord => (),
|
||||||
|
EmptyTagUnion => (),
|
||||||
|
|
||||||
|
Record(fields, ext_var) => {
|
||||||
|
stack.extend(var_slice(fields.variables()));
|
||||||
|
|
||||||
|
stack.push(*ext_var);
|
||||||
|
}
|
||||||
|
TagUnion(tags, ext_var) => {
|
||||||
|
for slice_index in tags.variables() {
|
||||||
|
let slice = variable_slices[slice_index.start as usize];
|
||||||
|
stack.extend(var_slice(slice));
|
||||||
|
}
|
||||||
|
|
||||||
|
stack.push(*ext_var);
|
||||||
|
}
|
||||||
|
FunctionOrTagUnion(_, _, ext_var) => {
|
||||||
|
stack.push(*ext_var);
|
||||||
|
}
|
||||||
|
|
||||||
|
RecursiveTagUnion(rec_var, tags, ext_var) => {
|
||||||
|
for slice_index in tags.variables() {
|
||||||
|
let slice = variable_slices[slice_index.start as usize];
|
||||||
|
stack.extend(var_slice(slice));
|
||||||
|
}
|
||||||
|
|
||||||
|
stack.push(*ext_var);
|
||||||
|
stack.push(*rec_var);
|
||||||
|
}
|
||||||
|
|
||||||
|
Erroneous(_) => (),
|
||||||
|
},
|
||||||
|
Alias(_, args, var) => {
|
||||||
|
stack.extend(var_slice(args.variables()));
|
||||||
|
|
||||||
|
stack.push(*var);
|
||||||
}
|
}
|
||||||
|
|
||||||
subs.restore(*ext_var);
|
|
||||||
subs.restore(*rec_var);
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
Erroneous(_) => (),
|
}
|
||||||
},
|
}
|
||||||
Alias(_, args, var) => {
|
|
||||||
for var_index in args.variables().into_iter() {
|
#[derive(Clone, Debug)]
|
||||||
let var = subs[var_index];
|
pub struct StorageSubs {
|
||||||
subs.restore(var);
|
subs: Subs,
|
||||||
}
|
}
|
||||||
|
|
||||||
subs.restore(*var);
|
#[derive(Copy, Clone, Debug)]
|
||||||
|
struct StorageSubsOffsets {
|
||||||
|
utable: u32,
|
||||||
|
variables: u32,
|
||||||
|
tag_names: u32,
|
||||||
|
field_names: u32,
|
||||||
|
record_fields: u32,
|
||||||
|
variable_slices: u32,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl StorageSubs {
|
||||||
|
pub fn new(subs: Subs) -> Self {
|
||||||
|
Self { subs }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn extend_with_variable(&mut self, source: &mut Subs, variable: Variable) -> Variable {
|
||||||
|
deep_copy_var_to(source, &mut self.subs, variable)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn merge_into(self, target: &mut Subs) -> impl Fn(Variable) -> Variable {
|
||||||
|
let self_offsets = StorageSubsOffsets {
|
||||||
|
utable: self.subs.utable.len() as u32,
|
||||||
|
variables: self.subs.variables.len() as u32,
|
||||||
|
tag_names: self.subs.tag_names.len() as u32,
|
||||||
|
field_names: self.subs.field_names.len() as u32,
|
||||||
|
record_fields: self.subs.record_fields.len() as u32,
|
||||||
|
variable_slices: self.subs.variable_slices.len() as u32,
|
||||||
|
};
|
||||||
|
|
||||||
|
let offsets = StorageSubsOffsets {
|
||||||
|
utable: (target.utable.len() - Variable::NUM_RESERVED_VARS) as u32,
|
||||||
|
variables: target.variables.len() as u32,
|
||||||
|
tag_names: target.tag_names.len() as u32,
|
||||||
|
field_names: target.field_names.len() as u32,
|
||||||
|
record_fields: target.record_fields.len() as u32,
|
||||||
|
variable_slices: target.variable_slices.len() as u32,
|
||||||
|
};
|
||||||
|
|
||||||
|
// The first Variable::NUM_RESERVED_VARS are the same in every subs,
|
||||||
|
// so we can skip copying them!
|
||||||
|
let range = Variable::NUM_RESERVED_VARS..self.subs.utable.len();
|
||||||
|
|
||||||
|
// fill new slots with empty values
|
||||||
|
target.extend_by(range.len());
|
||||||
|
|
||||||
|
for i in range {
|
||||||
|
let variable = Variable(i as u32);
|
||||||
|
let descriptor = self.subs.get_ref(variable);
|
||||||
|
debug_assert!(descriptor.copy.is_none());
|
||||||
|
|
||||||
|
let new_content = Self::offset_content(&offsets, &descriptor.content);
|
||||||
|
|
||||||
|
let new_descriptor = Descriptor {
|
||||||
|
rank: descriptor.rank,
|
||||||
|
mark: descriptor.mark,
|
||||||
|
copy: OptVariable::NONE,
|
||||||
|
content: new_content,
|
||||||
|
};
|
||||||
|
|
||||||
|
let new_variable = Self::offset_variable(&offsets, variable);
|
||||||
|
target.set(new_variable, new_descriptor);
|
||||||
|
}
|
||||||
|
|
||||||
|
target.variables.extend(
|
||||||
|
self.subs
|
||||||
|
.variables
|
||||||
|
.iter()
|
||||||
|
.map(|v| Self::offset_variable(&offsets, *v)),
|
||||||
|
);
|
||||||
|
|
||||||
|
target.variable_slices.extend(
|
||||||
|
self.subs
|
||||||
|
.variable_slices
|
||||||
|
.into_iter()
|
||||||
|
.map(|v| Self::offset_variable_slice(&offsets, v)),
|
||||||
|
);
|
||||||
|
|
||||||
|
target.tag_names.extend(self.subs.tag_names);
|
||||||
|
target.field_names.extend(self.subs.field_names);
|
||||||
|
target.record_fields.extend(self.subs.record_fields);
|
||||||
|
|
||||||
|
debug_assert_eq!(
|
||||||
|
target.utable.len(),
|
||||||
|
(self_offsets.utable + offsets.utable) as usize
|
||||||
|
);
|
||||||
|
|
||||||
|
debug_assert_eq!(
|
||||||
|
target.tag_names.len(),
|
||||||
|
(self_offsets.tag_names + offsets.tag_names) as usize
|
||||||
|
);
|
||||||
|
|
||||||
|
move |v| {
|
||||||
|
let offsets = offsets;
|
||||||
|
Self::offset_variable(&offsets, v)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fn offset_flat_type(offsets: &StorageSubsOffsets, flat_type: &FlatType) -> FlatType {
|
||||||
|
match flat_type {
|
||||||
|
FlatType::Apply(symbol, arguments) => {
|
||||||
|
FlatType::Apply(*symbol, Self::offset_variable_slice(offsets, *arguments))
|
||||||
|
}
|
||||||
|
FlatType::Func(arguments, lambda_set, result) => FlatType::Func(
|
||||||
|
Self::offset_variable_slice(offsets, *arguments),
|
||||||
|
Self::offset_variable(offsets, *lambda_set),
|
||||||
|
Self::offset_variable(offsets, *result),
|
||||||
|
),
|
||||||
|
FlatType::Record(record_fields, ext) => FlatType::Record(
|
||||||
|
Self::offset_record_fields(offsets, *record_fields),
|
||||||
|
Self::offset_variable(offsets, *ext),
|
||||||
|
),
|
||||||
|
FlatType::TagUnion(union_tags, ext) => FlatType::TagUnion(
|
||||||
|
Self::offset_union_tags(offsets, *union_tags),
|
||||||
|
Self::offset_variable(offsets, *ext),
|
||||||
|
),
|
||||||
|
FlatType::FunctionOrTagUnion(tag_name, symbol, ext) => FlatType::FunctionOrTagUnion(
|
||||||
|
Self::offset_tag_name_index(offsets, *tag_name),
|
||||||
|
*symbol,
|
||||||
|
Self::offset_variable(offsets, *ext),
|
||||||
|
),
|
||||||
|
FlatType::RecursiveTagUnion(rec, union_tags, ext) => FlatType::RecursiveTagUnion(
|
||||||
|
Self::offset_variable(offsets, *rec),
|
||||||
|
Self::offset_union_tags(offsets, *union_tags),
|
||||||
|
Self::offset_variable(offsets, *ext),
|
||||||
|
),
|
||||||
|
FlatType::Erroneous(problem) => FlatType::Erroneous(problem.clone()),
|
||||||
|
FlatType::EmptyRecord => FlatType::EmptyRecord,
|
||||||
|
FlatType::EmptyTagUnion => FlatType::EmptyTagUnion,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn offset_content(offsets: &StorageSubsOffsets, content: &Content) -> Content {
|
||||||
|
use Content::*;
|
||||||
|
|
||||||
|
match content {
|
||||||
|
FlexVar(opt_name) => FlexVar(opt_name.clone()),
|
||||||
|
RigidVar(name) => RigidVar(name.clone()),
|
||||||
|
RecursionVar {
|
||||||
|
structure,
|
||||||
|
opt_name,
|
||||||
|
} => RecursionVar {
|
||||||
|
structure: Self::offset_variable(offsets, *structure),
|
||||||
|
opt_name: opt_name.clone(),
|
||||||
|
},
|
||||||
|
Structure(flat_type) => Structure(Self::offset_flat_type(offsets, flat_type)),
|
||||||
|
Alias(symbol, alias_variables, actual) => Alias(
|
||||||
|
*symbol,
|
||||||
|
Self::offset_alias_variables(offsets, *alias_variables),
|
||||||
|
Self::offset_variable(offsets, *actual),
|
||||||
|
),
|
||||||
|
Error => Content::Error,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn offset_alias_variables(
|
||||||
|
offsets: &StorageSubsOffsets,
|
||||||
|
mut alias_variables: AliasVariables,
|
||||||
|
) -> AliasVariables {
|
||||||
|
alias_variables.lowercases_start += offsets.field_names;
|
||||||
|
alias_variables.variables_start += offsets.variables;
|
||||||
|
|
||||||
|
alias_variables
|
||||||
|
}
|
||||||
|
|
||||||
|
fn offset_union_tags(offsets: &StorageSubsOffsets, mut union_tags: UnionTags) -> UnionTags {
|
||||||
|
union_tags.tag_names_start += offsets.tag_names;
|
||||||
|
union_tags.variables_start += offsets.variable_slices;
|
||||||
|
|
||||||
|
union_tags
|
||||||
|
}
|
||||||
|
|
||||||
|
fn offset_record_fields(
|
||||||
|
offsets: &StorageSubsOffsets,
|
||||||
|
mut record_fields: RecordFields,
|
||||||
|
) -> RecordFields {
|
||||||
|
record_fields.field_names_start += offsets.field_names;
|
||||||
|
record_fields.variables_start += offsets.variables;
|
||||||
|
record_fields.field_types_start += offsets.record_fields;
|
||||||
|
|
||||||
|
record_fields
|
||||||
|
}
|
||||||
|
|
||||||
|
fn offset_tag_name_index(
|
||||||
|
offsets: &StorageSubsOffsets,
|
||||||
|
mut tag_name: SubsIndex<TagName>,
|
||||||
|
) -> SubsIndex<TagName> {
|
||||||
|
tag_name.start += offsets.tag_names;
|
||||||
|
|
||||||
|
tag_name
|
||||||
|
}
|
||||||
|
|
||||||
|
fn offset_variable(offsets: &StorageSubsOffsets, variable: Variable) -> Variable {
|
||||||
|
if variable.index() < Variable::FIRST_USER_SPACE_VAR.index() {
|
||||||
|
variable
|
||||||
|
} else {
|
||||||
|
let new_index = variable.0 + offsets.utable;
|
||||||
|
Variable(new_index)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn offset_variable_slice(
|
||||||
|
offsets: &StorageSubsOffsets,
|
||||||
|
mut slice: VariableSubsSlice,
|
||||||
|
) -> VariableSubsSlice {
|
||||||
|
slice.slice.start += offsets.variables;
|
||||||
|
|
||||||
|
slice
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn deep_copy_var_to(
|
||||||
|
source: &mut Subs, // mut to set the copy
|
||||||
|
target: &mut Subs,
|
||||||
|
var: Variable,
|
||||||
|
) -> Variable {
|
||||||
|
let rank = Rank::toplevel();
|
||||||
|
|
||||||
|
// capacity based on the false hello world program
|
||||||
|
let arena = bumpalo::Bump::with_capacity(4 * 1024);
|
||||||
|
|
||||||
|
let mut visited = bumpalo::collections::Vec::with_capacity_in(256, &arena);
|
||||||
|
|
||||||
|
let copy = deep_copy_var_to_help(&arena, &mut visited, source, target, rank, var);
|
||||||
|
|
||||||
|
// we have tracked all visited variables, and can now traverse them
|
||||||
|
// in one go (without looking at the UnificationTable) and clear the copy field
|
||||||
|
for var in visited {
|
||||||
|
let descriptor = source.get_ref_mut(var);
|
||||||
|
|
||||||
|
if descriptor.copy.is_some() {
|
||||||
|
descriptor.rank = Rank::NONE;
|
||||||
|
descriptor.mark = Mark::NONE;
|
||||||
|
descriptor.copy = OptVariable::NONE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
copy
|
||||||
|
}
|
||||||
|
|
||||||
|
fn deep_copy_var_to_help<'a>(
|
||||||
|
arena: &'a bumpalo::Bump,
|
||||||
|
visited: &mut bumpalo::collections::Vec<'a, Variable>,
|
||||||
|
source: &mut Subs,
|
||||||
|
target: &mut Subs,
|
||||||
|
max_rank: Rank,
|
||||||
|
var: Variable,
|
||||||
|
) -> Variable {
|
||||||
|
use bumpalo::collections::Vec;
|
||||||
|
use Content::*;
|
||||||
|
use FlatType::*;
|
||||||
|
|
||||||
|
let desc = source.get_without_compacting(var);
|
||||||
|
|
||||||
|
if let Some(copy) = desc.copy.into_variable() {
|
||||||
|
debug_assert!(target.contains(copy));
|
||||||
|
return copy;
|
||||||
|
} else if desc.rank != Rank::NONE {
|
||||||
|
// DO NOTHING, Fall through
|
||||||
|
//
|
||||||
|
// The original deep_copy_var can do
|
||||||
|
// return var;
|
||||||
|
//
|
||||||
|
// but we cannot, because this `var` is in the source, not the target, and we
|
||||||
|
// should only return variables in the target
|
||||||
|
}
|
||||||
|
|
||||||
|
visited.push(var);
|
||||||
|
|
||||||
|
let make_descriptor = |content| Descriptor {
|
||||||
|
content,
|
||||||
|
rank: max_rank,
|
||||||
|
mark: Mark::NONE,
|
||||||
|
copy: OptVariable::NONE,
|
||||||
|
};
|
||||||
|
|
||||||
|
let copy = target.fresh_unnamed_flex_var();
|
||||||
|
|
||||||
|
// Link the original variable to the new variable. This lets us
|
||||||
|
// avoid making multiple copies of the variable we are instantiating.
|
||||||
|
//
|
||||||
|
// Need to do this before recursively copying to avoid looping.
|
||||||
|
source.modify(var, |descriptor| {
|
||||||
|
descriptor.mark = Mark::NONE;
|
||||||
|
descriptor.copy = copy.into();
|
||||||
|
});
|
||||||
|
|
||||||
|
// Now we recursively copy the content of the variable.
|
||||||
|
// We have already marked the variable as copied, so we
|
||||||
|
// will not repeat this work or crawl this variable again.
|
||||||
|
match desc.content {
|
||||||
|
Structure(flat_type) => {
|
||||||
|
let new_flat_type = match flat_type {
|
||||||
|
Apply(symbol, args) => {
|
||||||
|
let mut new_args = Vec::with_capacity_in(args.len(), arena);
|
||||||
|
|
||||||
|
for index in args.into_iter() {
|
||||||
|
let var = source[index];
|
||||||
|
new_args.push(deep_copy_var_to_help(
|
||||||
|
arena, visited, source, target, max_rank, var,
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
let arg_vars = VariableSubsSlice::insert_into_subs(target, new_args);
|
||||||
|
|
||||||
|
Apply(symbol, arg_vars)
|
||||||
|
}
|
||||||
|
|
||||||
|
Func(arg_vars, closure_var, ret_var) => {
|
||||||
|
let new_ret_var =
|
||||||
|
deep_copy_var_to_help(arena, visited, source, target, max_rank, ret_var);
|
||||||
|
|
||||||
|
let new_closure_var = deep_copy_var_to_help(
|
||||||
|
arena,
|
||||||
|
visited,
|
||||||
|
source,
|
||||||
|
target,
|
||||||
|
max_rank,
|
||||||
|
closure_var,
|
||||||
|
);
|
||||||
|
|
||||||
|
let mut new_arg_vars = Vec::with_capacity_in(arg_vars.len(), arena);
|
||||||
|
|
||||||
|
for index in arg_vars.into_iter() {
|
||||||
|
let var = source[index];
|
||||||
|
let copy_var =
|
||||||
|
deep_copy_var_to_help(arena, visited, source, target, max_rank, var);
|
||||||
|
new_arg_vars.push(copy_var);
|
||||||
|
}
|
||||||
|
|
||||||
|
let arg_vars = VariableSubsSlice::insert_into_subs(target, new_arg_vars);
|
||||||
|
|
||||||
|
Func(arg_vars, new_closure_var, new_ret_var)
|
||||||
|
}
|
||||||
|
|
||||||
|
same @ EmptyRecord | same @ EmptyTagUnion | same @ Erroneous(_) => same,
|
||||||
|
|
||||||
|
Record(fields, ext_var) => {
|
||||||
|
let record_fields = {
|
||||||
|
let mut new_vars = Vec::with_capacity_in(fields.len(), arena);
|
||||||
|
|
||||||
|
for index in fields.iter_variables() {
|
||||||
|
let var = source[index];
|
||||||
|
let copy_var = deep_copy_var_to_help(
|
||||||
|
arena, visited, source, target, max_rank, var,
|
||||||
|
);
|
||||||
|
|
||||||
|
new_vars.push(copy_var);
|
||||||
|
}
|
||||||
|
|
||||||
|
let field_names_start = target.field_names.len() as u32;
|
||||||
|
let variables_start = target.variables.len() as u32;
|
||||||
|
let field_types_start = target.record_fields.len() as u32;
|
||||||
|
|
||||||
|
let mut length = 0;
|
||||||
|
|
||||||
|
for ((i1, _, i3), var) in fields.iter_all().zip(new_vars) {
|
||||||
|
let record_field = source[i3].map(|_| var);
|
||||||
|
|
||||||
|
target.field_names.push(source[i1].clone());
|
||||||
|
target.record_fields.push(record_field.map(|_| ()));
|
||||||
|
target.variables.push(*record_field.as_inner());
|
||||||
|
|
||||||
|
length += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
RecordFields {
|
||||||
|
length,
|
||||||
|
field_names_start,
|
||||||
|
variables_start,
|
||||||
|
field_types_start,
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
Record(
|
||||||
|
record_fields,
|
||||||
|
deep_copy_var_to_help(arena, visited, source, target, max_rank, ext_var),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
TagUnion(tags, ext_var) => {
|
||||||
|
let new_ext =
|
||||||
|
deep_copy_var_to_help(arena, visited, source, target, max_rank, ext_var);
|
||||||
|
|
||||||
|
let mut new_variable_slices = Vec::with_capacity_in(tags.len(), arena);
|
||||||
|
|
||||||
|
let mut new_variables = Vec::with_capacity_in(tags.len(), arena);
|
||||||
|
for index in tags.variables() {
|
||||||
|
let slice = source[index];
|
||||||
|
for var_index in slice {
|
||||||
|
let var = source[var_index];
|
||||||
|
let new_var = deep_copy_var_to_help(
|
||||||
|
arena, visited, source, target, max_rank, var,
|
||||||
|
);
|
||||||
|
new_variables.push(new_var);
|
||||||
|
}
|
||||||
|
|
||||||
|
let new_slice =
|
||||||
|
VariableSubsSlice::insert_into_subs(target, new_variables.drain(..));
|
||||||
|
|
||||||
|
new_variable_slices.push(new_slice);
|
||||||
|
}
|
||||||
|
|
||||||
|
let new_variables = {
|
||||||
|
let start = target.variable_slices.len() as u32;
|
||||||
|
let length = new_variable_slices.len() as u16;
|
||||||
|
target.variable_slices.extend(new_variable_slices);
|
||||||
|
|
||||||
|
SubsSlice::new(start, length)
|
||||||
|
};
|
||||||
|
|
||||||
|
let new_tag_names = {
|
||||||
|
let tag_names = tags.tag_names();
|
||||||
|
let slice = &source.tag_names[tag_names.start as usize..]
|
||||||
|
[..tag_names.length as usize];
|
||||||
|
|
||||||
|
let start = target.tag_names.len() as u32;
|
||||||
|
let length = tag_names.len() as u16;
|
||||||
|
|
||||||
|
target.tag_names.extend(slice.iter().cloned());
|
||||||
|
|
||||||
|
SubsSlice::new(start, length)
|
||||||
|
};
|
||||||
|
|
||||||
|
let union_tags = UnionTags::from_slices(new_tag_names, new_variables);
|
||||||
|
|
||||||
|
TagUnion(union_tags, new_ext)
|
||||||
|
}
|
||||||
|
|
||||||
|
FunctionOrTagUnion(tag_name, symbol, ext_var) => {
|
||||||
|
let new_tag_name = SubsIndex::new(target.tag_names.len() as u32);
|
||||||
|
|
||||||
|
target.tag_names.push(source[tag_name].clone());
|
||||||
|
|
||||||
|
FunctionOrTagUnion(
|
||||||
|
new_tag_name,
|
||||||
|
symbol,
|
||||||
|
deep_copy_var_to_help(arena, visited, source, target, max_rank, ext_var),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
RecursiveTagUnion(rec_var, tags, ext_var) => {
|
||||||
|
let mut new_variable_slices = Vec::with_capacity_in(tags.len(), arena);
|
||||||
|
|
||||||
|
let mut new_variables = Vec::with_capacity_in(tags.len(), arena);
|
||||||
|
for index in tags.variables() {
|
||||||
|
let slice = source[index];
|
||||||
|
for var_index in slice {
|
||||||
|
let var = source[var_index];
|
||||||
|
let new_var = deep_copy_var_to_help(
|
||||||
|
arena, visited, source, target, max_rank, var,
|
||||||
|
);
|
||||||
|
new_variables.push(new_var);
|
||||||
|
}
|
||||||
|
|
||||||
|
let new_slice =
|
||||||
|
VariableSubsSlice::insert_into_subs(target, new_variables.drain(..));
|
||||||
|
|
||||||
|
new_variable_slices.push(new_slice);
|
||||||
|
}
|
||||||
|
|
||||||
|
let new_variables = {
|
||||||
|
let start = target.variable_slices.len() as u32;
|
||||||
|
let length = new_variable_slices.len() as u16;
|
||||||
|
target.variable_slices.extend(new_variable_slices);
|
||||||
|
|
||||||
|
SubsSlice::new(start, length)
|
||||||
|
};
|
||||||
|
|
||||||
|
let new_tag_names = {
|
||||||
|
let tag_names = tags.tag_names();
|
||||||
|
let slice = &source.tag_names[tag_names.start as usize..]
|
||||||
|
[..tag_names.length as usize];
|
||||||
|
|
||||||
|
let start = target.tag_names.len() as u32;
|
||||||
|
let length = tag_names.len() as u16;
|
||||||
|
|
||||||
|
target.tag_names.extend(slice.iter().cloned());
|
||||||
|
|
||||||
|
SubsSlice::new(start, length)
|
||||||
|
};
|
||||||
|
|
||||||
|
let union_tags = UnionTags::from_slices(new_tag_names, new_variables);
|
||||||
|
|
||||||
|
let new_ext =
|
||||||
|
deep_copy_var_to_help(arena, visited, source, target, max_rank, ext_var);
|
||||||
|
let new_rec_var =
|
||||||
|
deep_copy_var_to_help(arena, visited, source, target, max_rank, rec_var);
|
||||||
|
|
||||||
|
RecursiveTagUnion(new_rec_var, union_tags, new_ext)
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
target.set(copy, make_descriptor(Structure(new_flat_type)));
|
||||||
|
|
||||||
|
copy
|
||||||
|
}
|
||||||
|
|
||||||
|
FlexVar(_) | Error => copy,
|
||||||
|
|
||||||
|
RecursionVar {
|
||||||
|
opt_name,
|
||||||
|
structure,
|
||||||
|
} => {
|
||||||
|
let new_structure =
|
||||||
|
deep_copy_var_to_help(arena, visited, source, target, max_rank, structure);
|
||||||
|
|
||||||
|
debug_assert!((new_structure.index() as usize) < target.len());
|
||||||
|
|
||||||
|
target.set(
|
||||||
|
copy,
|
||||||
|
make_descriptor(RecursionVar {
|
||||||
|
opt_name,
|
||||||
|
structure: new_structure,
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
|
||||||
|
copy
|
||||||
|
}
|
||||||
|
|
||||||
|
RigidVar(name) => {
|
||||||
|
target.set(copy, make_descriptor(FlexVar(Some(name))));
|
||||||
|
|
||||||
|
copy
|
||||||
|
}
|
||||||
|
|
||||||
|
Alias(symbol, mut args, real_type_var) => {
|
||||||
|
let mut new_vars = Vec::with_capacity_in(args.variables().len(), arena);
|
||||||
|
|
||||||
|
for var_index in args.variables() {
|
||||||
|
let var = source[var_index];
|
||||||
|
let new_var = deep_copy_var_to_help(arena, visited, source, target, max_rank, var);
|
||||||
|
|
||||||
|
new_vars.push(new_var);
|
||||||
|
}
|
||||||
|
|
||||||
|
args.replace_variables(target, new_vars);
|
||||||
|
|
||||||
|
let lowercases = &source.field_names[args.lowercases_start as usize..]
|
||||||
|
[..args.lowercases_len as usize];
|
||||||
|
|
||||||
|
args.lowercases_start = target.field_names.len() as u32;
|
||||||
|
target.field_names.extend(lowercases.iter().cloned());
|
||||||
|
|
||||||
|
let new_real_type_var =
|
||||||
|
deep_copy_var_to_help(arena, visited, source, target, max_rank, real_type_var);
|
||||||
|
let new_content = Alias(symbol, args, new_real_type_var);
|
||||||
|
|
||||||
|
target.set(copy, make_descriptor(new_content));
|
||||||
|
|
||||||
|
copy
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,6 +3,7 @@ use crate::subs::{
|
||||||
GetSubsSlice, RecordFields, Subs, UnionTags, VarStore, Variable, VariableSubsSlice,
|
GetSubsSlice, RecordFields, Subs, UnionTags, VarStore, Variable, VariableSubsSlice,
|
||||||
};
|
};
|
||||||
use roc_collections::all::{ImMap, ImSet, Index, MutSet, SendMap};
|
use roc_collections::all::{ImMap, ImSet, Index, MutSet, SendMap};
|
||||||
|
use roc_module::called_via::CalledVia;
|
||||||
use roc_module::ident::{ForeignSymbol, Ident, Lowercase, TagName};
|
use roc_module::ident::{ForeignSymbol, Ident, Lowercase, TagName};
|
||||||
use roc_module::low_level::LowLevel;
|
use roc_module::low_level::LowLevel;
|
||||||
use roc_module::symbol::{Interns, ModuleId, Symbol};
|
use roc_module::symbol::{Interns, ModuleId, Symbol};
|
||||||
|
@ -1134,7 +1135,7 @@ pub enum Reason {
|
||||||
#[derive(PartialEq, Debug, Clone)]
|
#[derive(PartialEq, Debug, Clone)]
|
||||||
pub enum Category {
|
pub enum Category {
|
||||||
Lookup(Symbol),
|
Lookup(Symbol),
|
||||||
CallResult(Option<Symbol>),
|
CallResult(Option<Symbol>, CalledVia),
|
||||||
LowLevelOpResult(LowLevel),
|
LowLevelOpResult(LowLevel),
|
||||||
ForeignCall,
|
ForeignCall,
|
||||||
TagApply {
|
TagApply {
|
||||||
|
|
|
@ -133,12 +133,14 @@ fn unify_context(subs: &mut Subs, pool: &mut Pool, ctx: Context) -> Outcome {
|
||||||
// println!("\n --- \n");
|
// println!("\n --- \n");
|
||||||
// dbg!(ctx.second, type2);
|
// dbg!(ctx.second, type2);
|
||||||
// println!("\n --------------- \n");
|
// println!("\n --------------- \n");
|
||||||
|
let content_1 = subs.get(ctx.first).content;
|
||||||
|
let content_2 = subs.get(ctx.second).content;
|
||||||
println!(
|
println!(
|
||||||
"{:?} {:?} ~ {:?} {:?}",
|
"{:?} {:?} ~ {:?} {:?}",
|
||||||
ctx.first,
|
ctx.first,
|
||||||
subs.get(ctx.first).content,
|
roc_types::subs::SubsFmtContent(&content_1, subs),
|
||||||
ctx.second,
|
ctx.second,
|
||||||
subs.get(ctx.second).content
|
roc_types::subs::SubsFmtContent(&content_2, subs),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
match &ctx.first_desc.content {
|
match &ctx.first_desc.content {
|
||||||
|
|
|
@ -5,7 +5,7 @@ Unlike most editors, we use projectional or structural editing to edit the [Abst
|
||||||
|
|
||||||
## Getting started
|
## Getting started
|
||||||
|
|
||||||
- Install the compiler, see [here](../BUILDING_FROM_SOURCE).
|
- Install the compiler, see [here](../BUILDING_FROM_SOURCE.md).
|
||||||
- Run the following from the roc folder:
|
- Run the following from the roc folder:
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|
|
@ -501,7 +501,7 @@ impl<'a> EdModel<'a> {
|
||||||
};
|
};
|
||||||
let arena = Bump::new();
|
let arena = Bump::new();
|
||||||
|
|
||||||
let mut subs = Subs::new(var_store);
|
let mut subs = Subs::new_from_varstore(var_store);
|
||||||
|
|
||||||
for (var, name) in rigid_variables {
|
for (var, name) in rigid_variables {
|
||||||
subs.rigid_var(var, name);
|
subs.rigid_var(var, name);
|
||||||
|
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
@ -6,6 +6,7 @@ use roc_problem::can::{BadPattern, FloatErrorKind, IntErrorKind, Problem, Runtim
|
||||||
use roc_region::all::{Located, Region};
|
use roc_region::all::{Located, Region};
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
|
|
||||||
|
use crate::error::r#type::suggest;
|
||||||
use crate::report::{Annotation, Report, RocDocAllocator, RocDocBuilder, Severity};
|
use crate::report::{Annotation, Report, RocDocAllocator, RocDocBuilder, Severity};
|
||||||
use ven_pretty::DocAllocator;
|
use ven_pretty::DocAllocator;
|
||||||
|
|
||||||
|
@ -874,16 +875,36 @@ fn pretty_runtime_error<'b>(
|
||||||
module_name,
|
module_name,
|
||||||
ident,
|
ident,
|
||||||
region,
|
region,
|
||||||
|
exposed_values,
|
||||||
} => {
|
} => {
|
||||||
|
let mut suggestions = suggest::sort(ident.as_ref(), exposed_values);
|
||||||
|
suggestions.truncate(4);
|
||||||
|
|
||||||
|
let did_you_mean = if suggestions.is_empty() {
|
||||||
|
alloc.concat(vec![
|
||||||
|
alloc.reflow("In fact, it looks like "),
|
||||||
|
alloc.module_name(module_name.clone()),
|
||||||
|
alloc.reflow(" doesn't expose any values!"),
|
||||||
|
])
|
||||||
|
} else {
|
||||||
|
let qualified_suggestions = suggestions
|
||||||
|
.into_iter()
|
||||||
|
.map(|v| alloc.string(module_name.to_string() + "." + v.as_str()));
|
||||||
|
alloc.stack(vec![
|
||||||
|
alloc.reflow("Did you mean one of these?"),
|
||||||
|
alloc.vcat(qualified_suggestions).indent(4),
|
||||||
|
])
|
||||||
|
};
|
||||||
doc = alloc.stack(vec![
|
doc = alloc.stack(vec![
|
||||||
alloc.concat(vec![
|
alloc.concat(vec![
|
||||||
alloc.reflow("The "),
|
alloc.reflow("The "),
|
||||||
alloc.module_name(module_name),
|
alloc.module_name(module_name),
|
||||||
alloc.reflow(" module does not expose a "),
|
alloc.reflow(" module does not expose `"),
|
||||||
alloc.string(ident.to_string()),
|
alloc.string(ident.to_string()),
|
||||||
alloc.reflow(" value:"),
|
alloc.reflow("`:"),
|
||||||
]),
|
]),
|
||||||
alloc.region(region),
|
alloc.region(region),
|
||||||
|
did_you_mean,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
title = VALUE_NOT_EXPOSED;
|
title = VALUE_NOT_EXPOSED;
|
||||||
|
@ -1176,8 +1197,6 @@ fn not_found<'b>(
|
||||||
thing: &'b str,
|
thing: &'b str,
|
||||||
options: MutSet<Box<str>>,
|
options: MutSet<Box<str>>,
|
||||||
) -> RocDocBuilder<'b> {
|
) -> RocDocBuilder<'b> {
|
||||||
use crate::error::r#type::suggest;
|
|
||||||
|
|
||||||
let mut suggestions = suggest::sort(
|
let mut suggestions = suggest::sort(
|
||||||
name.as_inline_str().as_str(),
|
name.as_inline_str().as_str(),
|
||||||
options.iter().map(|v| v.as_ref()).collect(),
|
options.iter().map(|v| v.as_ref()).collect(),
|
||||||
|
@ -1225,8 +1244,6 @@ fn module_not_found<'b>(
|
||||||
name: &ModuleName,
|
name: &ModuleName,
|
||||||
options: MutSet<Box<str>>,
|
options: MutSet<Box<str>>,
|
||||||
) -> RocDocBuilder<'b> {
|
) -> RocDocBuilder<'b> {
|
||||||
use crate::error::r#type::suggest;
|
|
||||||
|
|
||||||
let mut suggestions =
|
let mut suggestions =
|
||||||
suggest::sort(name.as_str(), options.iter().map(|v| v.as_ref()).collect());
|
suggest::sort(name.as_str(), options.iter().map(|v| v.as_ref()).collect());
|
||||||
suggestions.truncate(4);
|
suggestions.truncate(4);
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
use roc_can::expected::{Expected, PExpected};
|
use roc_can::expected::{Expected, PExpected};
|
||||||
use roc_collections::all::{Index, MutSet, SendMap};
|
use roc_collections::all::{Index, MutSet, SendMap};
|
||||||
|
use roc_module::called_via::{BinOp, CalledVia};
|
||||||
use roc_module::ident::{Ident, IdentStr, Lowercase, TagName};
|
use roc_module::ident::{Ident, IdentStr, Lowercase, TagName};
|
||||||
use roc_module::symbol::Symbol;
|
use roc_module::symbol::Symbol;
|
||||||
use roc_region::all::{Located, Region};
|
use roc_region::all::{Located, Region};
|
||||||
|
@ -1043,13 +1044,26 @@ fn add_category<'b>(
|
||||||
alloc.record_field(field.to_owned()),
|
alloc.record_field(field.to_owned()),
|
||||||
alloc.text(" is a:"),
|
alloc.text(" is a:"),
|
||||||
]),
|
]),
|
||||||
|
CallResult(
|
||||||
CallResult(Some(symbol)) => alloc.concat(vec![
|
Some(_),
|
||||||
|
CalledVia::BinOp(
|
||||||
|
BinOp::Equals
|
||||||
|
| BinOp::NotEquals
|
||||||
|
| BinOp::LessThan
|
||||||
|
| BinOp::GreaterThan
|
||||||
|
| BinOp::LessThanOrEq
|
||||||
|
| BinOp::GreaterThanOrEq,
|
||||||
|
),
|
||||||
|
) => alloc.concat(vec![alloc.text("This comparison produces:")]),
|
||||||
|
CallResult(Some(_), CalledVia::StringInterpolation) => {
|
||||||
|
alloc.concat(vec![this_is, alloc.text(" a string of type:")])
|
||||||
|
}
|
||||||
|
CallResult(Some(symbol), _) => alloc.concat(vec![
|
||||||
alloc.text("This "),
|
alloc.text("This "),
|
||||||
alloc.symbol_foreign_qualified(*symbol),
|
alloc.symbol_foreign_qualified(*symbol),
|
||||||
alloc.text(" call produces:"),
|
alloc.text(" call produces:"),
|
||||||
]),
|
]),
|
||||||
CallResult(None) => alloc.concat(vec![this_is, alloc.text(":")]),
|
CallResult(None, _) => alloc.concat(vec![this_is, alloc.text(":")]),
|
||||||
LowLevelOpResult(op) => {
|
LowLevelOpResult(op) => {
|
||||||
panic!(
|
panic!(
|
||||||
"Compiler bug: invalid return type from low-level op {:?}",
|
"Compiler bug: invalid return type from low-level op {:?}",
|
||||||
|
|
|
@ -4,3 +4,35 @@
|
||||||
|
|
||||||
pub mod error;
|
pub mod error;
|
||||||
pub mod report;
|
pub mod report;
|
||||||
|
|
||||||
|
/// `internal_error!` should be used whenever a compiler invariant is broken.
|
||||||
|
/// It is a wrapper around panic that tells the user to file a bug.
|
||||||
|
/// This should only be used in cases where there would be a compiler bug and the user can't fix it.
|
||||||
|
/// If there is simply an unimplemented feature, please use `unimplemented!`
|
||||||
|
/// If there is a user error, please use roc_reporting to print a nice error message.
|
||||||
|
#[macro_export]
|
||||||
|
macro_rules! internal_error {
|
||||||
|
($($arg:tt)*) => ({
|
||||||
|
eprintln!("An internal compiler expectation was broken.");
|
||||||
|
eprintln!("This is definitely a compiler bug.");
|
||||||
|
// TODO: update this to the new bug template.
|
||||||
|
eprintln!("Please file an issue here: https://github.com/rtfeldman/roc/issues/new/choose");
|
||||||
|
#[allow(clippy::panic)] {
|
||||||
|
panic!($($arg)*);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/// `user_error!` should only ever be used temporarily.
|
||||||
|
/// It is a way to document locations where we do not yet have nice error reporting.
|
||||||
|
/// All cases of `user_error!` should eventually be replaced with pretty error printing using roc_reporting.
|
||||||
|
#[macro_export]
|
||||||
|
macro_rules! user_error {
|
||||||
|
($($arg:tt)*) => ({
|
||||||
|
eprintln!("We ran into an issue while compiling your code.");
|
||||||
|
eprintln!("Sadly, we don't havs a pretty error message for this case yet.");
|
||||||
|
eprintln!("If you can't figure out the problem from the context below, please reach out at: https://roc.zulipchat.com/");
|
||||||
|
eprintln!($($arg)*);
|
||||||
|
std::process::exit(1);
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
|
@ -353,7 +353,7 @@ impl<'a> RocDocAllocator<'a> {
|
||||||
|
|
||||||
pub fn binop(
|
pub fn binop(
|
||||||
&'a self,
|
&'a self,
|
||||||
content: roc_module::operator::BinOp,
|
content: roc_module::called_via::BinOp,
|
||||||
) -> DocBuilder<'a, Self, Annotation> {
|
) -> DocBuilder<'a, Self, Annotation> {
|
||||||
self.text(content.to_string()).annotate(Annotation::BinOp)
|
self.text(content.to_string()).annotate(Annotation::BinOp)
|
||||||
}
|
}
|
||||||
|
|
|
@ -66,7 +66,7 @@ mod test_reporting {
|
||||||
problems: can_problems,
|
problems: can_problems,
|
||||||
..
|
..
|
||||||
} = can_expr(arena, expr_src)?;
|
} = can_expr(arena, expr_src)?;
|
||||||
let mut subs = Subs::new(var_store);
|
let mut subs = Subs::new_from_varstore(var_store);
|
||||||
|
|
||||||
for (var, name) in output.introduced_variables.name_by_var {
|
for (var, name) in output.introduced_variables.name_by_var {
|
||||||
subs.rigid_var(var, name);
|
subs.rigid_var(var, name);
|
||||||
|
@ -298,17 +298,24 @@ mod test_reporting {
|
||||||
report_problem_as(
|
report_problem_as(
|
||||||
indoc!(
|
indoc!(
|
||||||
r#"
|
r#"
|
||||||
List.foobar 1 2
|
List.isempty 1 2
|
||||||
"#
|
"#
|
||||||
),
|
),
|
||||||
indoc!(
|
indoc!(
|
||||||
r#"
|
r#"
|
||||||
── NOT EXPOSED ─────────────────────────────────────────────────────────────────
|
── NOT EXPOSED ─────────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
The List module does not expose a foobar value:
|
The List module does not expose `isempty`:
|
||||||
|
|
||||||
1│ List.foobar 1 2
|
1│ List.isempty 1 2
|
||||||
^^^^^^^^^^^
|
^^^^^^^^^^^^
|
||||||
|
|
||||||
|
Did you mean one of these?
|
||||||
|
|
||||||
|
List.isEmpty
|
||||||
|
List.set
|
||||||
|
List.get
|
||||||
|
List.keepIf
|
||||||
"#
|
"#
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
@ -547,7 +554,35 @@ mod test_reporting {
|
||||||
baz
|
baz
|
||||||
Nat
|
Nat
|
||||||
Str
|
Str
|
||||||
U8
|
Err
|
||||||
|
"#
|
||||||
|
),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn lowercase_primitive_tag_bool() {
|
||||||
|
report_problem_as(
|
||||||
|
indoc!(
|
||||||
|
r#"
|
||||||
|
if true then 1 else 2
|
||||||
|
"#
|
||||||
|
),
|
||||||
|
indoc!(
|
||||||
|
r#"
|
||||||
|
── UNRECOGNIZED NAME ───────────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
I cannot find a `true` value
|
||||||
|
|
||||||
|
1│ if true then 1 else 2
|
||||||
|
^^^^
|
||||||
|
|
||||||
|
Did you mean one of these?
|
||||||
|
|
||||||
|
True
|
||||||
|
Str
|
||||||
|
Num
|
||||||
|
Err
|
||||||
"#
|
"#
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
@ -1950,10 +1985,10 @@ mod test_reporting {
|
||||||
|
|
||||||
Did you mean one of these?
|
Did you mean one of these?
|
||||||
|
|
||||||
|
Ok
|
||||||
U8
|
U8
|
||||||
f
|
f
|
||||||
I8
|
I8
|
||||||
F64
|
|
||||||
"#
|
"#
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
@ -5551,6 +5586,82 @@ mod test_reporting {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
// https://github.com/rtfeldman/roc/issues/1714
|
||||||
|
fn interpolate_concat_is_transparent_1714() {
|
||||||
|
report_problem_as(
|
||||||
|
indoc!(
|
||||||
|
r#"
|
||||||
|
greeting = "Privet"
|
||||||
|
|
||||||
|
if True then 1 else "\(greeting), World!"
|
||||||
|
"#,
|
||||||
|
),
|
||||||
|
indoc!(
|
||||||
|
r#"
|
||||||
|
── TYPE MISMATCH ───────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
This `if` has an `else` branch with a different type from its `then` branch:
|
||||||
|
|
||||||
|
3│ if True then 1 else "\(greeting), World!"
|
||||||
|
^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
|
The `else` branch is a string of type:
|
||||||
|
|
||||||
|
Str
|
||||||
|
|
||||||
|
but the `then` branch has the type:
|
||||||
|
|
||||||
|
Num a
|
||||||
|
|
||||||
|
I need all branches in an `if` to have the same type!
|
||||||
|
"#
|
||||||
|
),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
macro_rules! comparison_binop_transparency_tests {
|
||||||
|
($($op:expr, $name:ident),* $(,)?) => {
|
||||||
|
$(
|
||||||
|
#[test]
|
||||||
|
fn $name() {
|
||||||
|
report_problem_as(
|
||||||
|
&format!(r#"if True then "abc" else 1 {} 2"#, $op),
|
||||||
|
&format!(
|
||||||
|
r#"── TYPE MISMATCH ───────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
This `if` has an `else` branch with a different type from its `then` branch:
|
||||||
|
|
||||||
|
1│ if True then "abc" else 1 {} 2
|
||||||
|
^^{}^^
|
||||||
|
|
||||||
|
This comparison produces:
|
||||||
|
|
||||||
|
Bool
|
||||||
|
|
||||||
|
but the `then` branch has the type:
|
||||||
|
|
||||||
|
Str
|
||||||
|
|
||||||
|
I need all branches in an `if` to have the same type!
|
||||||
|
"#,
|
||||||
|
$op, "^".repeat($op.len())
|
||||||
|
),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
)*
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
comparison_binop_transparency_tests! {
|
||||||
|
"<", lt_binop_is_transparent,
|
||||||
|
">", gt_binop_is_transparent,
|
||||||
|
"==", eq_binop_is_transparent,
|
||||||
|
"!=", neq_binop_is_transparent,
|
||||||
|
"<=", leq_binop_is_transparent,
|
||||||
|
">=", geq_binop_is_transparent,
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn keyword_record_field_access() {
|
fn keyword_record_field_access() {
|
||||||
report_problem_as(
|
report_problem_as(
|
||||||
|
@ -5596,10 +5707,17 @@ mod test_reporting {
|
||||||
r#"
|
r#"
|
||||||
── NOT EXPOSED ─────────────────────────────────────────────────────────────────
|
── NOT EXPOSED ─────────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
The Num module does not expose a if value:
|
The Num module does not expose `if`:
|
||||||
|
|
||||||
1│ Num.if
|
1│ Num.if
|
||||||
^^^^^^
|
^^^^^^
|
||||||
|
|
||||||
|
Did you mean one of these?
|
||||||
|
|
||||||
|
Num.sin
|
||||||
|
Num.div
|
||||||
|
Num.abs
|
||||||
|
Num.neg
|
||||||
"#
|
"#
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
@ -5802,8 +5920,8 @@ mod test_reporting {
|
||||||
|
|
||||||
Nat
|
Nat
|
||||||
Str
|
Str
|
||||||
|
Err
|
||||||
U8
|
U8
|
||||||
F64
|
|
||||||
"#
|
"#
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|
|
@ -6,8 +6,6 @@ use core::{fmt, mem, ptr, slice};
|
||||||
|
|
||||||
// A list of C functions that are being imported
|
// A list of C functions that are being imported
|
||||||
extern "C" {
|
extern "C" {
|
||||||
pub fn printf(format: *const u8, ...) -> i32;
|
|
||||||
|
|
||||||
pub fn roc_alloc(size: usize, alignment: u32) -> *mut c_void;
|
pub fn roc_alloc(size: usize, alignment: u32) -> *mut c_void;
|
||||||
pub fn roc_realloc(
|
pub fn roc_realloc(
|
||||||
ptr: *mut c_void,
|
ptr: *mut c_void,
|
||||||
|
|
14
vendor/ena/src/unify/backing_vec.rs
vendored
14
vendor/ena/src/unify/backing_vec.rs
vendored
|
@ -15,7 +15,10 @@ type Key<S: UnificationStore> = <S as UnificationStore>::Key;
|
||||||
/// backing store types. The most common such type is `InPlace`,
|
/// backing store types. The most common such type is `InPlace`,
|
||||||
/// which indicates a standard, mutable unification table.
|
/// which indicates a standard, mutable unification table.
|
||||||
pub trait UnificationStore:
|
pub trait UnificationStore:
|
||||||
ops::Index<usize, Output = VarValue<Key<Self>>> + Clone + Default
|
ops::Index<usize, Output = VarValue<Key<Self>>>
|
||||||
|
+ ops::IndexMut<usize, Output = VarValue<Key<Self>>>
|
||||||
|
+ Clone
|
||||||
|
+ Default
|
||||||
{
|
{
|
||||||
type Key: UnifyKey<Value = Self::Value>;
|
type Key: UnifyKey<Value = Self::Value>;
|
||||||
type Value: Clone + Debug;
|
type Value: Clone + Debug;
|
||||||
|
@ -141,6 +144,15 @@ where
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl<K> ops::IndexMut<usize> for InPlace<K>
|
||||||
|
where
|
||||||
|
K: UnifyKey,
|
||||||
|
{
|
||||||
|
fn index_mut(&mut self, index: usize) -> &mut VarValue<K> {
|
||||||
|
&mut self.values[index]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Copy, Clone, Debug)]
|
#[derive(Copy, Clone, Debug)]
|
||||||
struct Delegate<K>(PhantomData<K>);
|
struct Delegate<K>(PhantomData<K>);
|
||||||
|
|
||||||
|
|
17
vendor/ena/src/unify/mod.rs
vendored
17
vendor/ena/src/unify/mod.rs
vendored
|
@ -284,6 +284,12 @@ impl<S: UnificationStore> UnificationTable<S> {
|
||||||
&self.values[key.index() as usize]
|
&self.values[key.index() as usize]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Obtains the current value for a particular key.
|
||||||
|
/// Not for end-users; they can use `probe_value`.
|
||||||
|
pub fn value_mut(&mut self, key: S::Key) -> &mut VarValue<S::Key> {
|
||||||
|
&mut self.values[key.index() as usize]
|
||||||
|
}
|
||||||
|
|
||||||
/// Find the root node for `vid`. This uses the standard
|
/// Find the root node for `vid`. This uses the standard
|
||||||
/// union-find algorithm with path compression:
|
/// union-find algorithm with path compression:
|
||||||
/// <http://en.wikipedia.org/wiki/Disjoint-set_data_structure>.
|
/// <http://en.wikipedia.org/wiki/Disjoint-set_data_structure>.
|
||||||
|
@ -451,6 +457,17 @@ where
|
||||||
self.value(id)
|
self.value(id)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns the current value for the given key. If the key has
|
||||||
|
/// been union'd, this will give the value from the current root.
|
||||||
|
pub fn probe_value_ref_mut<K1>(&mut self, id: K1) -> &mut VarValue<K>
|
||||||
|
where
|
||||||
|
K1: Into<K>,
|
||||||
|
{
|
||||||
|
let id = id.into();
|
||||||
|
let id = self.get_root_key_without_compacting(id);
|
||||||
|
self.value_mut(id)
|
||||||
|
}
|
||||||
|
|
||||||
/// This is for a debug_assert! in solve() only. Do not use it elsewhere!
|
/// This is for a debug_assert! in solve() only. Do not use it elsewhere!
|
||||||
pub fn probe_value_without_compacting<K1>(&self, id: K1) -> V
|
pub fn probe_value_without_compacting<K1>(&self, id: K1) -> V
|
||||||
where
|
where
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue