mirror of
https://github.com/roc-lang/roc.git
synced 2025-09-29 23:04:49 +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:
|
||||
spell-check:
|
||||
name: spell check
|
||||
runs-on: [self-hosted]
|
||||
runs-on: [self-hosted, linux]
|
||||
timeout-minutes: 10
|
||||
env:
|
||||
FORCE_COLOR: 1
|
||||
|
|
2
.github/workflows/www.yml
vendored
2
.github/workflows/www.yml
vendored
|
@ -9,7 +9,7 @@ on:
|
|||
jobs:
|
||||
deploy:
|
||||
name: 'Deploy to Netlify'
|
||||
runs-on: [self-hosted]
|
||||
runs-on: [self-hosted, linux]
|
||||
steps:
|
||||
- uses: jsmrcaga/action-netlify-deploy@v1.6.0
|
||||
with:
|
||||
|
|
2
AUTHORS
2
AUTHORS
|
@ -54,3 +54,5 @@ Takeshi Sato <doublequotation@gmail.com>
|
|||
Joost Baas <joost@joostbaas.eu>
|
||||
Callum Dunster <cdunster@users.noreply.github.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`
|
||||
|
||||
If you're on MacOS and using a OS version >= 10.15:
|
||||
|
||||
`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
|
||||
You will need to start a fresh terminal session to use nix.
|
||||
|
||||
### Usage
|
||||
|
||||
|
|
1
Cargo.lock
generated
1
Cargo.lock
generated
|
@ -3554,6 +3554,7 @@ version = "0.1.0"
|
|||
name = "roc_types"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"bumpalo",
|
||||
"roc_collections",
|
||||
"roc_module",
|
||||
"roc_region",
|
||||
|
|
10
Earthfile
10
Earthfile
|
@ -79,17 +79,17 @@ test-rust:
|
|||
# not pre-compiling the host can cause race conditions
|
||||
RUN echo "4" | cargo run --release examples/benchmarks/NQueens.roc
|
||||
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.
|
||||
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
|
||||
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 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 \
|
||||
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:
|
||||
FROM +test-rust
|
||||
|
|
|
@ -277,7 +277,7 @@ pub fn constrain_expr<'a>(
|
|||
expr_id: expr_node_id,
|
||||
closure_var,
|
||||
fn_var,
|
||||
..
|
||||
called_via,
|
||||
} => {
|
||||
// The expression that evaluates to the function being called, e.g. `foo` in
|
||||
// (foo) bar baz
|
||||
|
@ -349,7 +349,7 @@ pub fn constrain_expr<'a>(
|
|||
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);
|
||||
|
||||
|
@ -1919,7 +1919,7 @@ pub mod test_constrain {
|
|||
aliases,
|
||||
};
|
||||
|
||||
let mut subs = Subs::new(var_store);
|
||||
let mut subs = Subs::new_from_varstore(var_store);
|
||||
|
||||
for (var, name) in rigid_variables {
|
||||
subs.rigid_var(var, name);
|
||||
|
|
|
@ -6,8 +6,8 @@ use crate::{
|
|||
mem_pool::{pool::NodeId, pool_str::PoolStr, pool_vec::PoolVec},
|
||||
};
|
||||
use roc_can::expr::Recursive;
|
||||
use roc_module::called_via::CalledVia;
|
||||
use roc_module::low_level::LowLevel;
|
||||
use roc_module::operator::CalledVia;
|
||||
use roc_module::symbol::Symbol;
|
||||
|
||||
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 crate::{
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
use crate::mem_pool::pool::{NodeId, Pool};
|
||||
use bumpalo::{collections::Vec as BumpVec, Bump};
|
||||
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_problem::can::{Problem, RuntimeError};
|
||||
use roc_region::all::{Located, Region};
|
||||
|
@ -134,11 +134,8 @@ impl<'a> Env<'a> {
|
|||
)),
|
||||
}
|
||||
} else {
|
||||
match self
|
||||
.dep_idents
|
||||
.get(&module_id)
|
||||
.and_then(|exposed_ids| exposed_ids.get_id(&ident))
|
||||
{
|
||||
match self.dep_idents.get(&module_id) {
|
||||
Some(exposed_ids) => match exposed_ids.get_id(&ident) {
|
||||
Some(ident_id) => {
|
||||
let symbol = Symbol::new(module_id, *ident_id);
|
||||
|
||||
|
@ -146,11 +143,28 @@ impl<'a> Env<'a> {
|
|||
|
||||
Ok(symbol)
|
||||
}
|
||||
None => Err(RuntimeError::ValueNotExposed {
|
||||
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
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -295,6 +295,8 @@ fn spawn_rebuild_thread(
|
|||
) -> std::thread::JoinHandle<u128> {
|
||||
let thread_local_target = target.clone();
|
||||
std::thread::spawn(move || {
|
||||
print!("🔨 Rebuilding host... ");
|
||||
|
||||
let rebuild_host_start = SystemTime::now();
|
||||
if !precompiled {
|
||||
if surgically_link {
|
||||
|
@ -322,6 +324,9 @@ fn spawn_rebuild_thread(
|
|||
std::fs::copy(prehost, binary_path.as_path()).unwrap();
|
||||
}
|
||||
let rebuild_host_end = rebuild_host_start.elapsed().unwrap();
|
||||
|
||||
println!("Done!");
|
||||
|
||||
rebuild_host_end.as_millis()
|
||||
})
|
||||
}
|
||||
|
|
|
@ -2,8 +2,8 @@ use bumpalo::collections::Vec;
|
|||
use bumpalo::Bump;
|
||||
use libloading::Library;
|
||||
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::operator::CalledVia;
|
||||
use roc_module::symbol::{Interns, ModuleId, Symbol};
|
||||
use roc_mono::ir::ProcLayout;
|
||||
use roc_mono::layout::{union_sorted_tags_help, Builtin, Layout, UnionLayout, UnionVariant};
|
||||
|
|
|
@ -1112,6 +1112,36 @@ pub fn listAny(
|
|||
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
|
||||
|
||||
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.listSwap, "swap");
|
||||
exportListFn(list.listAny, "any");
|
||||
exportListFn(list.listAll, "all");
|
||||
exportListFn(list.listFindUnsafe, "find_unsafe");
|
||||
}
|
||||
|
||||
|
|
|
@ -691,6 +691,10 @@ all : List elem, (elem -> Bool) -> Bool
|
|||
## any of the elements satisfy it.
|
||||
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.
|
||||
## If no satisfying element is found, an `Err NotFound` is returned.
|
||||
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_IN_PLACE: &str = "roc_builtins.list.set_in_place";
|
||||
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 DEC_FROM_F64: &str = "roc_builtins.dec.from_f64";
|
||||
|
|
|
@ -1128,6 +1128,16 @@ pub fn types() -> MutMap<Symbol, (SolvedType, Region)> {
|
|||
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
|
||||
add_top_level_function_type!(
|
||||
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
|
||||
|
||||
// len : Dict * * -> Nat
|
||||
|
|
|
@ -3,9 +3,9 @@ use crate::expr::{ClosureData, Expr::*};
|
|||
use crate::expr::{Expr, Field, Recursive, WhenBranch};
|
||||
use crate::pattern::Pattern;
|
||||
use roc_collections::all::SendMap;
|
||||
use roc_module::called_via::CalledVia;
|
||||
use roc_module::ident::{Lowercase, TagName};
|
||||
use roc_module::low_level::LowLevel;
|
||||
use roc_module::operator::CalledVia;
|
||||
use roc_module::symbol::Symbol;
|
||||
use roc_region::all::{Located, Region};
|
||||
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_SUBLIST => list_sublist,
|
||||
LIST_SPLIT => list_split,
|
||||
LIST_INTERSPERSE => list_intersperse,
|
||||
LIST_DROP => list_drop,
|
||||
LIST_DROP_AT => list_drop_at,
|
||||
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_SORT_WITH => list_sort_with,
|
||||
LIST_ANY => list_any,
|
||||
LIST_ALL => list_all,
|
||||
LIST_FIND => list_find,
|
||||
DICT_LEN => dict_len,
|
||||
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 }
|
||||
fn list_split(symbol: Symbol, var_store: &mut VarStore) -> Def {
|
||||
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)
|
||||
}
|
||||
|
||||
/// 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 ]*
|
||||
fn list_find(symbol: Symbol, var_store: &mut VarStore) -> Def {
|
||||
let list = Symbol::ARG_1;
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
use crate::procedure::References;
|
||||
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_problem::can::{Problem, RuntimeError};
|
||||
use roc_region::all::{Located, Region};
|
||||
|
@ -99,11 +99,8 @@ impl<'a> Env<'a> {
|
|||
)),
|
||||
}
|
||||
} else {
|
||||
match self
|
||||
.dep_idents
|
||||
.get(&module_id)
|
||||
.and_then(|exposed_ids| exposed_ids.get_id(&ident))
|
||||
{
|
||||
match self.dep_idents.get(&module_id) {
|
||||
Some(exposed_ids) => match exposed_ids.get_id(&ident) {
|
||||
Some(ident_id) => {
|
||||
let symbol = Symbol::new(module_id, *ident_id);
|
||||
|
||||
|
@ -111,11 +108,28 @@ impl<'a> Env<'a> {
|
|||
|
||||
Ok(symbol)
|
||||
}
|
||||
None => Err(RuntimeError::ValueNotExposed {
|
||||
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
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -10,9 +10,9 @@ use crate::pattern::{canonicalize_pattern, Pattern};
|
|||
use crate::procedure::References;
|
||||
use crate::scope::Scope;
|
||||
use roc_collections::all::{ImSet, MutMap, MutSet, SendMap};
|
||||
use roc_module::called_via::CalledVia;
|
||||
use roc_module::ident::{ForeignSymbol, Lowercase, TagName};
|
||||
use roc_module::low_level::LowLevel;
|
||||
use roc_module::operator::CalledVia;
|
||||
use roc_module::symbol::Symbol;
|
||||
use roc_parse::ast::{self, EscapedChar, StrLiteral};
|
||||
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_expr),
|
||||
],
|
||||
CalledVia::Space,
|
||||
CalledVia::StringInterpolation,
|
||||
);
|
||||
|
||||
loc_expr = Located::new(0, 0, 0, 0, expr);
|
||||
|
|
|
@ -2,9 +2,9 @@
|
|||
|
||||
use bumpalo::collections::Vec;
|
||||
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::operator::BinOp::Pizza;
|
||||
use roc_module::operator::{BinOp, CalledVia};
|
||||
use roc_parse::ast::Expr::{self, *};
|
||||
use roc_parse::ast::{AssignedField, Def, WhenBranch};
|
||||
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) => {
|
||||
use roc_module::operator::UnaryOp::*;
|
||||
use roc_module::called_via::UnaryOp::*;
|
||||
|
||||
let region = loc_op.region;
|
||||
let op = loc_op.value;
|
||||
|
@ -475,7 +475,7 @@ fn binop_step<'a>(
|
|||
op_stack: &mut Vec<Located<BinOp>>,
|
||||
next_op: Located<BinOp>,
|
||||
) -> Step<'a> {
|
||||
use roc_module::operator::Associativity::*;
|
||||
use roc_module::called_via::Associativity::*;
|
||||
use std::cmp::Ordering;
|
||||
|
||||
match op_stack.pop() {
|
||||
|
|
|
@ -254,7 +254,7 @@ pub fn constrain_expr(
|
|||
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;
|
||||
// The expression that evaluates to the function being called, e.g. `foo` in
|
||||
// (foo) bar baz
|
||||
|
@ -317,7 +317,7 @@ pub fn constrain_expr(
|
|||
region,
|
||||
);
|
||||
|
||||
let category = Category::CallResult(opt_symbol);
|
||||
let category = Category::CallResult(opt_symbol, *called_via);
|
||||
|
||||
exists(
|
||||
vars,
|
||||
|
|
|
@ -3,7 +3,7 @@ use crate::def::fmt_def;
|
|||
use crate::pattern::fmt_pattern;
|
||||
use crate::spaces::{add_spaces, fmt_comments_only, fmt_spaces, newline, NewlineAt, INDENT};
|
||||
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::{
|
||||
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),
|
||||
UnaryOp(sub_expr, unary_op) => {
|
||||
match &unary_op.value {
|
||||
operator::UnaryOp::Negate => {
|
||||
called_via::UnaryOp::Negate => {
|
||||
buf.push('-');
|
||||
}
|
||||
operator::UnaryOp::Not => {
|
||||
called_via::UnaryOp::Not => {
|
||||
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) {
|
||||
match op {
|
||||
operator::BinOp::Caret => buf.push('^'),
|
||||
operator::BinOp::Star => buf.push('*'),
|
||||
operator::BinOp::Slash => buf.push('/'),
|
||||
operator::BinOp::DoubleSlash => buf.push_str("//"),
|
||||
operator::BinOp::Percent => buf.push('%'),
|
||||
operator::BinOp::DoublePercent => buf.push_str("%%"),
|
||||
operator::BinOp::Plus => buf.push('+'),
|
||||
operator::BinOp::Minus => buf.push('-'),
|
||||
operator::BinOp::Equals => buf.push_str("=="),
|
||||
operator::BinOp::NotEquals => buf.push_str("!="),
|
||||
operator::BinOp::LessThan => buf.push('<'),
|
||||
operator::BinOp::GreaterThan => buf.push('>'),
|
||||
operator::BinOp::LessThanOrEq => buf.push_str("<="),
|
||||
operator::BinOp::GreaterThanOrEq => buf.push_str(">="),
|
||||
operator::BinOp::And => buf.push_str("&&"),
|
||||
operator::BinOp::Or => buf.push_str("||"),
|
||||
operator::BinOp::Pizza => buf.push_str("|>"),
|
||||
operator::BinOp::Assignment => unreachable!(),
|
||||
operator::BinOp::HasType => unreachable!(),
|
||||
operator::BinOp::Backpassing => unreachable!(),
|
||||
called_via::BinOp::Caret => buf.push('^'),
|
||||
called_via::BinOp::Star => buf.push('*'),
|
||||
called_via::BinOp::Slash => buf.push('/'),
|
||||
called_via::BinOp::DoubleSlash => buf.push_str("//"),
|
||||
called_via::BinOp::Percent => buf.push('%'),
|
||||
called_via::BinOp::DoublePercent => buf.push_str("%%"),
|
||||
called_via::BinOp::Plus => buf.push('+'),
|
||||
called_via::BinOp::Minus => buf.push('-'),
|
||||
called_via::BinOp::Equals => buf.push_str("=="),
|
||||
called_via::BinOp::NotEquals => buf.push_str("!="),
|
||||
called_via::BinOp::LessThan => buf.push('<'),
|
||||
called_via::BinOp::GreaterThan => buf.push('>'),
|
||||
called_via::BinOp::LessThanOrEq => buf.push_str("<="),
|
||||
called_via::BinOp::GreaterThanOrEq => buf.push_str(">="),
|
||||
called_via::BinOp::And => buf.push_str("&&"),
|
||||
called_via::BinOp::Or => buf.push_str("||"),
|
||||
called_via::BinOp::Pizza => buf.push_str("|>"),
|
||||
called_via::BinOp::Assignment => unreachable!(),
|
||||
called_via::BinOp::HasType => 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]
|
||||
fn test_addsd_freg64_freg64() {
|
||||
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_list::{
|
||||
self, allocate_list, empty_list, empty_polymorphic_list, list_any, list_append, list_concat,
|
||||
list_contains, list_drop_at, list_find_trivial_not_found, list_find_unsafe, list_get_unsafe,
|
||||
list_join, list_keep_errs, list_keep_if, list_keep_oks, list_len, list_map, list_map2,
|
||||
list_map3, list_map4, list_map_with_index, list_prepend, list_range, list_repeat, list_reverse,
|
||||
list_set, list_single, list_sort_with, list_sublist, list_swap,
|
||||
self, allocate_list, empty_list, empty_polymorphic_list, list_all, list_any, list_append,
|
||||
list_concat, list_contains, list_drop_at, list_find_trivial_not_found, list_find_unsafe,
|
||||
list_get_unsafe, list_join, list_keep_errs, list_keep_if, list_keep_oks, list_len, list_map,
|
||||
list_map2, list_map3, list_map4, list_map_with_index, list_prepend, list_range, list_repeat,
|
||||
list_reverse, list_set, list_single, list_sort_with, list_sublist, list_swap,
|
||||
};
|
||||
use crate::llvm::build_str::{
|
||||
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,
|
||||
fn_name.as_str(),
|
||||
fn_type,
|
||||
Linkage::Private,
|
||||
Linkage::Internal,
|
||||
FAST_CALL_CONV,
|
||||
);
|
||||
|
||||
|
@ -5141,6 +5141,33 @@ fn run_higher_order_low_level<'a, 'ctx, 'env>(
|
|||
_ => 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 } => {
|
||||
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
|
||||
| ListWalkUntil | ListWalkBackwards | ListKeepOks | ListKeepErrs | ListSortWith
|
||||
| ListAny | ListFindUnsafe | DictWalk => {
|
||||
| ListAny | ListAll | ListFindUnsafe | DictWalk => {
|
||||
unreachable!("these are higher order, and are handled elsewhere")
|
||||
}
|
||||
}
|
||||
|
@ -6213,7 +6240,7 @@ fn build_foreign_symbol<'a, 'ctx, 'env>(
|
|||
env.module,
|
||||
&fastcc_function_name,
|
||||
fastcc_type,
|
||||
Linkage::Private,
|
||||
Linkage::Internal,
|
||||
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 }
|
||||
pub fn list_find_unsafe<'a, 'ctx, 'env>(
|
||||
env: &Env<'a, 'ctx, 'env>,
|
||||
|
|
|
@ -183,7 +183,7 @@ impl<'ctx> PointerToRefcount<'ctx> {
|
|||
env.module,
|
||||
fn_name,
|
||||
fn_type,
|
||||
Linkage::Private,
|
||||
Linkage::Internal,
|
||||
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 {
|
||||
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
|
||||
| StrCountGraphemes | StrFromInt | StrFromUtf8 | StrTrimLeft | StrTrimRight
|
||||
| StrFromUtf8Range | StrToUtf8 | StrRepeat | StrFromFloat | StrTrim | ListLen
|
||||
| ListGetUnsafe | ListSet | ListSingle | ListRepeat | ListReverse | ListConcat
|
||||
ListLen | ListGetUnsafe | ListSet | ListSingle | ListRepeat | ListReverse | ListConcat
|
||||
| ListContains | ListAppend | ListPrepend | ListJoin | ListRange | ListMap | ListMap2
|
||||
| ListMap3 | ListMap4 | ListMapWithIndex | ListKeepIf | ListWalk | ListWalkUntil
|
||||
| ListWalkBackwards | ListKeepOks | ListKeepErrs | ListSortWith | ListSublist
|
||||
| ListDropAt | ListSwap | ListAny | ListFindUnsafe | DictSize | DictEmpty | DictInsert
|
||||
| DictRemove | DictContains | DictGetUnsafe | DictKeys | DictValues | DictUnion
|
||||
| DictIntersection | DictDifference | DictWalk | SetFromList => {
|
||||
| ListDropAt | ListSwap | ListAny | ListAll | ListFindUnsafe | DictSize | DictEmpty
|
||||
| DictInsert | DictRemove | DictContains | DictGetUnsafe | DictKeys | DictValues
|
||||
| DictUnion | DictIntersection | DictDifference | DictWalk | SetFromList => {
|
||||
return NotImplemented;
|
||||
}
|
||||
|
||||
|
|
|
@ -5,8 +5,8 @@ use roc_can::expr::{ClosureData, Expr, Recursive};
|
|||
use roc_can::pattern::Pattern;
|
||||
use roc_can::scope::Scope;
|
||||
use roc_collections::all::{MutSet, SendMap};
|
||||
use roc_module::called_via::CalledVia;
|
||||
use roc_module::ident::TagName;
|
||||
use roc_module::operator::CalledVia;
|
||||
use roc_module::symbol::Symbol;
|
||||
use roc_region::all::{Located, Region};
|
||||
use roc_types::subs::{VarStore, Variable};
|
||||
|
|
|
@ -19,8 +19,7 @@ use roc_module::symbol::{
|
|||
Symbol,
|
||||
};
|
||||
use roc_mono::ir::{
|
||||
CapturedSymbols, EntryPoint, ExternalSpecializations, PartialProc, PendingSpecialization, Proc,
|
||||
ProcLayout, Procs,
|
||||
CapturedSymbols, EntryPoint, ExternalSpecializations, PartialProc, Proc, ProcLayout, Procs,
|
||||
};
|
||||
use roc_mono::layout::{Layout, LayoutCache, LayoutProblem};
|
||||
use roc_parse::ast::{self, StrLiteral, TypeAnnotation};
|
||||
|
@ -356,7 +355,7 @@ struct ModuleCache<'a> {
|
|||
constrained: MutMap<ModuleId, ConstrainedModule>,
|
||||
typechecked: MutMap<ModuleId, TypeCheckedModule<'a>>,
|
||||
found_specializations: MutMap<ModuleId, FoundSpecializationsModule<'a>>,
|
||||
external_specializations_requested: MutMap<ModuleId, ExternalSpecializations<'a>>,
|
||||
external_specializations_requested: MutMap<ModuleId, Vec<ExternalSpecializations>>,
|
||||
|
||||
/// Various information
|
||||
imports: MutMap<ModuleId, MutSet<ModuleId>>,
|
||||
|
@ -587,7 +586,7 @@ fn start_phase<'a>(
|
|||
.module_cache
|
||||
.external_specializations_requested
|
||||
.remove(&module_id)
|
||||
.unwrap_or_else(|| ExternalSpecializations::new_in(arena));
|
||||
.unwrap_or_default();
|
||||
|
||||
let FoundSpecializationsModule {
|
||||
module_id,
|
||||
|
@ -831,7 +830,7 @@ enum Msg<'a> {
|
|||
module_id: ModuleId,
|
||||
ident_ids: IdentIds,
|
||||
layout_cache: LayoutCache<'a>,
|
||||
external_specializations_requested: BumpMap<ModuleId, ExternalSpecializations<'a>>,
|
||||
external_specializations_requested: BumpMap<ModuleId, ExternalSpecializations>,
|
||||
procedures: MutMap<(Symbol, ProcLayout<'a>), Proc<'a>>,
|
||||
problems: Vec<roc_mono::ir::MonoProblem>,
|
||||
module_timing: ModuleTiming,
|
||||
|
@ -911,9 +910,6 @@ struct State<'a> {
|
|||
/// pending specializations in the same thread.
|
||||
pub needs_specialization: MutSet<ModuleId>,
|
||||
|
||||
pub all_pending_specializations:
|
||||
MutMap<Symbol, MutMap<ProcLayout<'a>, PendingSpecialization<'a>>>,
|
||||
|
||||
pub specializations_in_flight: u32,
|
||||
|
||||
pub timings: MutMap<ModuleId, ModuleTiming>,
|
||||
|
@ -1054,7 +1050,7 @@ enum BuildTask<'a> {
|
|||
subs: Subs,
|
||||
procs_base: ProcsBase<'a>,
|
||||
layout_cache: LayoutCache<'a>,
|
||||
specializations_we_must_make: ExternalSpecializations<'a>,
|
||||
specializations_we_must_make: Vec<ExternalSpecializations>,
|
||||
module_timing: ModuleTiming,
|
||||
},
|
||||
}
|
||||
|
@ -1538,7 +1534,6 @@ where
|
|||
unsolved_modules: MutMap::default(),
|
||||
timings: MutMap::default(),
|
||||
needs_specialization: MutSet::default(),
|
||||
all_pending_specializations: MutMap::default(),
|
||||
specializations_in_flight: 0,
|
||||
layout_caches: std::vec::Vec::with_capacity(num_cpus::get()),
|
||||
procs: Procs::new_in(arena),
|
||||
|
@ -2067,17 +2062,6 @@ fn update<'a>(
|
|||
log!("found specializations for {:?}", module_id);
|
||||
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
|
||||
.module_cache
|
||||
.top_level_thunks
|
||||
|
@ -2171,11 +2155,11 @@ fn update<'a>(
|
|||
.external_specializations_requested
|
||||
.entry(module_id)
|
||||
{
|
||||
Vacant(entry) => entry.insert(ExternalSpecializations::new_in(arena)),
|
||||
Vacant(entry) => entry.insert(vec![]),
|
||||
Occupied(entry) => entry.into_mut(),
|
||||
};
|
||||
|
||||
existing.extend(requested);
|
||||
existing.push(requested);
|
||||
}
|
||||
|
||||
msg_tx
|
||||
|
@ -2198,11 +2182,11 @@ fn update<'a>(
|
|||
.external_specializations_requested
|
||||
.entry(module_id)
|
||||
{
|
||||
Vacant(entry) => entry.insert(ExternalSpecializations::new_in(arena)),
|
||||
Vacant(entry) => entry.insert(vec![]),
|
||||
Occupied(entry) => entry.into_mut(),
|
||||
};
|
||||
|
||||
existing.extend(requested);
|
||||
existing.push(requested);
|
||||
}
|
||||
|
||||
start_tasks(arena, &mut state, work, injector, worker_listeners)?;
|
||||
|
@ -3936,7 +3920,7 @@ fn make_specializations<'a>(
|
|||
mut subs: Subs,
|
||||
procs_base: ProcsBase<'a>,
|
||||
mut layout_cache: LayoutCache<'a>,
|
||||
specializations_we_must_make: ExternalSpecializations<'a>,
|
||||
specializations_we_must_make: Vec<ExternalSpecializations>,
|
||||
mut module_timing: ModuleTiming,
|
||||
ptr_bytes: u32,
|
||||
) -> Msg<'a> {
|
||||
|
@ -3972,7 +3956,7 @@ fn make_specializations<'a>(
|
|||
&mut mono_env,
|
||||
procs,
|
||||
specializations_we_must_make,
|
||||
procs_base.specializations_for_host,
|
||||
procs_base.host_specializations,
|
||||
&mut layout_cache,
|
||||
);
|
||||
|
||||
|
@ -4004,27 +3988,11 @@ struct ProcsBase<'a> {
|
|||
partial_procs: BumpMap<Symbol, PartialProc<'a>>,
|
||||
module_thunks: &'a [Symbol],
|
||||
/// 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>,
|
||||
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)]
|
||||
fn build_pending_specializations<'a>(
|
||||
arena: &'a Bump,
|
||||
|
@ -4046,7 +4014,7 @@ fn build_pending_specializations<'a>(
|
|||
let mut procs_base = ProcsBase {
|
||||
partial_procs: BumpMap::default(),
|
||||
module_thunks: &[],
|
||||
specializations_for_host: BumpMap::default(),
|
||||
host_specializations: roc_mono::ir::HostSpecializations::new(),
|
||||
runtime_errors: BumpMap::default(),
|
||||
imported_module_thunks,
|
||||
};
|
||||
|
@ -4134,7 +4102,7 @@ fn add_def_to_module<'a>(
|
|||
|
||||
match def.loc_pattern.value {
|
||||
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 {
|
||||
Closure(ClosureData {
|
||||
|
@ -4152,19 +4120,19 @@ fn add_def_to_module<'a>(
|
|||
// register it as such. Otherwise, since it
|
||||
// never gets called by Roc code, it will never
|
||||
// get specialized!
|
||||
if is_exposed {
|
||||
let layout = match layout_cache.raw_from_var(
|
||||
mono_env.arena,
|
||||
annotation,
|
||||
mono_env.subs,
|
||||
) {
|
||||
Ok(l) => l,
|
||||
Err(LayoutProblem::Erroneous) => {
|
||||
if is_host_exposed {
|
||||
let layout_result =
|
||||
layout_cache.raw_from_var(mono_env.arena, annotation, mono_env.subs);
|
||||
|
||||
// cannot specialize when e.g. main's type contains type variables
|
||||
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;
|
||||
}
|
||||
Err(LayoutProblem::UnresolvedTypeVar(v)) => {
|
||||
LayoutProblem::UnresolvedTypeVar(v) => {
|
||||
let message = format!(
|
||||
"top level function has unresolved type variable {:?}",
|
||||
v
|
||||
|
@ -4174,20 +4142,15 @@ fn add_def_to_module<'a>(
|
|||
.insert(symbol, mono_env.arena.alloc(message));
|
||||
return;
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
let pending = PendingSpecialization::from_exposed_function(
|
||||
mono_env.arena,
|
||||
procs.host_specializations.insert_host_exposed(
|
||||
mono_env.subs,
|
||||
symbol,
|
||||
def.annotation,
|
||||
annotation,
|
||||
);
|
||||
|
||||
procs.add_specialization_for_host(
|
||||
symbol,
|
||||
ProcLayout::from_raw(mono_env.arena, layout),
|
||||
pending,
|
||||
);
|
||||
}
|
||||
|
||||
let partial_proc = PartialProc::from_named_function(
|
||||
|
@ -4207,28 +4170,25 @@ fn add_def_to_module<'a>(
|
|||
// mark this symbols as a top-level thunk before any other work on the procs
|
||||
module_thunks.push(symbol);
|
||||
|
||||
let annotation = def.expr_var;
|
||||
|
||||
// If this is an exposed symbol, we need to
|
||||
// register it as such. Otherwise, since it
|
||||
// never gets called by Roc code, it will never
|
||||
// get specialized!
|
||||
if is_exposed {
|
||||
let annotation = def.expr_var;
|
||||
if is_host_exposed {
|
||||
let layout_result =
|
||||
layout_cache.raw_from_var(mono_env.arena, annotation, mono_env.subs);
|
||||
|
||||
let top_level = match layout_cache.from_var(
|
||||
mono_env.arena,
|
||||
annotation,
|
||||
mono_env.subs,
|
||||
) {
|
||||
Ok(l) => {
|
||||
// remember, this is a 0-argument thunk
|
||||
ProcLayout::new(mono_env.arena, &[], l)
|
||||
}
|
||||
Err(LayoutProblem::Erroneous) => {
|
||||
// cannot specialize when e.g. main's type contains type variables
|
||||
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;
|
||||
}
|
||||
Err(LayoutProblem::UnresolvedTypeVar(v)) => {
|
||||
LayoutProblem::UnresolvedTypeVar(v) => {
|
||||
let message = format!(
|
||||
"top level function has unresolved type variable {:?}",
|
||||
v
|
||||
|
@ -4238,20 +4198,19 @@ fn add_def_to_module<'a>(
|
|||
.insert(symbol, mono_env.arena.alloc(message));
|
||||
return;
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
let pending = PendingSpecialization::from_exposed_function(
|
||||
mono_env.arena,
|
||||
procs.host_specializations.insert_host_exposed(
|
||||
mono_env.subs,
|
||||
symbol,
|
||||
def.annotation,
|
||||
annotation,
|
||||
);
|
||||
|
||||
procs.add_specialization_for_host(symbol, top_level, pending);
|
||||
}
|
||||
|
||||
let proc = PartialProc {
|
||||
annotation: def.expr_var,
|
||||
annotation,
|
||||
// This is a 0-arity thunk, so it has no arguments.
|
||||
pattern_symbols: &[],
|
||||
// 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)
|
||||
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)]
|
|
@ -2,10 +2,10 @@
|
|||
// See github.com/rtfeldman/roc/issues/800 for discussion of the large_enum_variant check.
|
||||
#![allow(clippy::large_enum_variant, clippy::upper_case_acronyms)]
|
||||
|
||||
pub mod called_via;
|
||||
pub mod ident;
|
||||
pub mod low_level;
|
||||
pub mod module_err;
|
||||
pub mod operator;
|
||||
pub mod symbol;
|
||||
|
||||
#[macro_use]
|
||||
|
|
|
@ -50,6 +50,7 @@ pub enum LowLevel {
|
|||
ListDropAt,
|
||||
ListSwap,
|
||||
ListAny,
|
||||
ListAll,
|
||||
ListFindUnsafe,
|
||||
DictSize,
|
||||
DictEmpty,
|
||||
|
@ -131,6 +132,7 @@ macro_rules! higher_order {
|
|||
| ListKeepErrs
|
||||
| ListSortWith
|
||||
| ListAny
|
||||
| ListAll
|
||||
| ListFindUnsafe
|
||||
| DictWalk
|
||||
};
|
||||
|
@ -162,6 +164,7 @@ impl LowLevel {
|
|||
ListKeepErrs => 1,
|
||||
ListSortWith => 1,
|
||||
ListAny => 1,
|
||||
ListAll => 1,
|
||||
ListFindUnsafe => 1,
|
||||
DictWalk => 2,
|
||||
_ => unreachable!(),
|
||||
|
@ -220,6 +223,7 @@ impl LowLevel {
|
|||
Symbol::LIST_DROP_AT => Some(ListDropAt),
|
||||
Symbol::LIST_SWAP => Some(ListSwap),
|
||||
Symbol::LIST_ANY => Some(ListAny),
|
||||
Symbol::LIST_ALL => Some(ListAll),
|
||||
Symbol::LIST_FIND => None,
|
||||
Symbol::DICT_LEN => Some(DictSize),
|
||||
Symbol::DICT_EMPTY => Some(DictEmpty),
|
||||
|
|
|
@ -877,6 +877,9 @@ define_builtins! {
|
|||
|
||||
// used in wasm dev backend to mark temporary values in the VM stack
|
||||
24 WASM_TMP: "#wasm_tmp"
|
||||
|
||||
// the _ used in mono when a specialized symbol is deleted
|
||||
25 REMOVED_SPECIALIZATION: "#removed_specialization"
|
||||
}
|
||||
1 NUM: "Num" => {
|
||||
0 NUM_NUM: "Num" imported // the Num.Num type alias
|
||||
|
@ -989,12 +992,16 @@ define_builtins! {
|
|||
}
|
||||
2 BOOL: "Bool" => {
|
||||
0 BOOL_BOOL: "Bool" imported // the Bool.Bool type alias
|
||||
1 BOOL_AND: "and"
|
||||
2 BOOL_OR: "or"
|
||||
3 BOOL_NOT: "not"
|
||||
4 BOOL_XOR: "xor"
|
||||
5 BOOL_EQ: "isEq"
|
||||
6 BOOL_NEQ: "isNotEq"
|
||||
1 BOOL_FALSE: "False" imported // Bool.Bool = [ False, True ]
|
||||
// NB: not strictly needed; used for finding global tag names in error suggestions
|
||||
2 BOOL_TRUE: "True" imported // Bool.Bool = [ False, True ]
|
||||
// NB: not strictly needed; used for finding global tag names in error suggestions
|
||||
3 BOOL_AND: "and"
|
||||
4 BOOL_OR: "or"
|
||||
5 BOOL_NOT: "not"
|
||||
6 BOOL_XOR: "xor"
|
||||
7 BOOL_EQ: "isEq"
|
||||
8 BOOL_NEQ: "isNotEq"
|
||||
}
|
||||
3 STR: "Str" => {
|
||||
0 STR_STR: "Str" imported // the Str.Str type alias
|
||||
|
@ -1072,17 +1079,24 @@ define_builtins! {
|
|||
47 LIST_FIND: "find"
|
||||
48 LIST_FIND_RESULT: "#find_result" // symbol used in the definition of List.find
|
||||
49 LIST_SUBLIST: "sublist"
|
||||
50 LIST_SPLIT: "split"
|
||||
51 LIST_SPLIT_CLOS: "#splitClos"
|
||||
50 LIST_INTERSPERSE: "intersperse"
|
||||
51 LIST_INTERSPERSE_CLOS: "#intersperseClos"
|
||||
52 LIST_SPLIT: "split"
|
||||
53 LIST_SPLIT_CLOS: "#splitClos"
|
||||
54 LIST_ALL: "all"
|
||||
}
|
||||
5 RESULT: "Result" => {
|
||||
0 RESULT_RESULT: "Result" imported // the Result.Result type alias
|
||||
1 RESULT_MAP: "map"
|
||||
2 RESULT_MAP_ERR: "mapErr"
|
||||
3 RESULT_WITH_DEFAULT: "withDefault"
|
||||
4 RESULT_AFTER: "after"
|
||||
5 RESULT_IS_OK: "isOk"
|
||||
6 RESULT_IS_ERR: "isErr"
|
||||
1 RESULT_OK: "Ok" imported // Result.Result a e = [ Ok a, Err e ]
|
||||
// NB: not strictly needed; used for finding global tag names in error suggestions
|
||||
2 RESULT_ERR: "Err" imported // Result.Result a e = [ Ok a, Err e ]
|
||||
// NB: not strictly needed; used for finding global tag names in error suggestions
|
||||
3 RESULT_MAP: "map"
|
||||
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" => {
|
||||
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)
|
||||
}
|
||||
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 } => {
|
||||
let list = env.symbols[xs];
|
||||
|
||||
|
|
|
@ -619,6 +619,7 @@ impl<'a> BorrowInfState<'a> {
|
|||
| ListKeepOks { xs }
|
||||
| ListKeepErrs { xs }
|
||||
| ListAny { xs }
|
||||
| ListAll { xs }
|
||||
| ListFindUnsafe { xs } => {
|
||||
// own the list if the function wants to own the element
|
||||
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]),
|
||||
ListMap3 => arena.alloc_slice_copy(&[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])
|
||||
}
|
||||
ListContains => arena.alloc_slice_copy(&[borrowed, irrelevant]),
|
||||
|
|
|
@ -536,6 +536,7 @@ impl<'a> Context<'a> {
|
|||
| ListKeepOks { xs }
|
||||
| ListKeepErrs { xs }
|
||||
| ListAny { xs }
|
||||
| ListAll { xs }
|
||||
| ListFindUnsafe { xs } => {
|
||||
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 {
|
||||
xs: Symbol,
|
||||
},
|
||||
ListAll {
|
||||
xs: Symbol,
|
||||
},
|
||||
ListFindUnsafe {
|
||||
xs: Symbol,
|
||||
},
|
||||
|
@ -77,6 +80,7 @@ impl HigherOrder {
|
|||
HigherOrder::ListFindUnsafe { .. } => 1,
|
||||
HigherOrder::DictWalk { .. } => 2,
|
||||
HigherOrder::ListAny { .. } => 1,
|
||||
HigherOrder::ListAll { .. } => 1,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,7 +4,7 @@ use crate::header::{AppHeader, ImportsEntry, InterfaceHeader, PlatformHeader, Ty
|
|||
use crate::ident::Ident;
|
||||
use bumpalo::collections::{String, Vec};
|
||||
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};
|
||||
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
|
|
|
@ -13,7 +13,7 @@ use crate::pattern::loc_closure_param;
|
|||
use crate::type_annotation;
|
||||
use bumpalo::collections::Vec;
|
||||
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 crate::parser::Progress::{self, *};
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
use roc_collections::all::MutSet;
|
||||
use roc_module::called_via::BinOp;
|
||||
use roc_module::ident::{Ident, Lowercase, ModuleName, TagName};
|
||||
use roc_module::operator::BinOp;
|
||||
use roc_module::symbol::{ModuleId, Symbol};
|
||||
use roc_parse::ast::Base;
|
||||
use roc_parse::pattern::PatternType;
|
||||
|
@ -139,6 +139,7 @@ pub enum RuntimeError {
|
|||
module_name: ModuleName,
|
||||
ident: Ident,
|
||||
region: Region,
|
||||
exposed_values: Vec<Lowercase>,
|
||||
},
|
||||
ModuleNotImported {
|
||||
module_name: ModuleName,
|
||||
|
|
|
@ -12,6 +12,7 @@ roc_module = { path = "../module" }
|
|||
roc_types = { path = "../types" }
|
||||
roc_can = { path = "../can" }
|
||||
roc_unify = { path = "../unify" }
|
||||
bumpalo = { version = "3.8.0", features = ["collections"] }
|
||||
|
||||
[dev-dependencies]
|
||||
roc_load = { path = "../load" }
|
||||
|
|
|
@ -27,7 +27,7 @@ pub fn run_solve(
|
|||
aliases,
|
||||
};
|
||||
|
||||
let mut subs = Subs::new(var_store);
|
||||
let mut subs = Subs::new_from_varstore(var_store);
|
||||
|
||||
for (var, name) in rigid_variables {
|
||||
subs.rigid_var(var, name);
|
||||
|
|
|
@ -619,10 +619,13 @@ fn type_to_var(
|
|||
subs: &mut Subs,
|
||||
rank: Rank,
|
||||
pools: &mut Pools,
|
||||
cached: &mut MutMap<Symbol, Variable>,
|
||||
_: &mut MutMap<Symbol, Variable>,
|
||||
typ: &Type,
|
||||
) -> 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
|
||||
|
@ -630,25 +633,29 @@ fn type_to_var(
|
|||
pub fn insert_type_into_subs(subs: &mut Subs, typ: &Type) -> Variable {
|
||||
let rank = Rank::NONE;
|
||||
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,
|
||||
rank: Rank,
|
||||
pools: &mut Pools,
|
||||
cached: &mut MutMap<Symbol, Variable>,
|
||||
arena: &'a bumpalo::Bump,
|
||||
typ: &Type,
|
||||
) -> Variable {
|
||||
use bumpalo::collections::Vec;
|
||||
|
||||
match typ {
|
||||
Variable(var) => *var,
|
||||
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 {
|
||||
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);
|
||||
}
|
||||
|
||||
|
@ -663,32 +670,32 @@ fn type_to_variable(
|
|||
|
||||
// This case is important for the rank of boolean variables
|
||||
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 {
|
||||
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);
|
||||
}
|
||||
|
||||
let arg_vars = VariableSubsSlice::insert_into_subs(subs, new_arg_vars);
|
||||
|
||||
let ret_var = type_to_variable(subs, rank, pools, cached, ret_type);
|
||||
let closure_var = type_to_variable(subs, rank, pools, cached, closure_type);
|
||||
let ret_var = type_to_variable(subs, rank, pools, arena, ret_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));
|
||||
|
||||
register(subs, rank, pools, content)
|
||||
}
|
||||
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 {
|
||||
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));
|
||||
}
|
||||
|
||||
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) =
|
||||
gather_fields_unsorted_iter(subs, RecordFields::empty(), temp_ext_var);
|
||||
|
@ -707,14 +714,14 @@ fn type_to_variable(
|
|||
register(subs, rank, pools, content)
|
||||
}
|
||||
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));
|
||||
|
||||
register(subs, rank, pools, content)
|
||||
}
|
||||
FunctionOrTagUnion(tag_name, symbol, ext) => {
|
||||
let temp_ext_var = type_to_variable(subs, rank, pools, cached, ext);
|
||||
let mut ext_tag_vec = Vec::new();
|
||||
let temp_ext_var = type_to_variable(subs, rank, pools, arena, ext);
|
||||
let mut ext_tag_vec = std::vec::Vec::new();
|
||||
let new_ext_var = match roc_types::pretty_print::chase_ext_tag_union(
|
||||
subs,
|
||||
temp_ext_var,
|
||||
|
@ -735,7 +742,7 @@ fn type_to_variable(
|
|||
register(subs, rank, pools, content)
|
||||
}
|
||||
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 =
|
||||
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 {
|
||||
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));
|
||||
}
|
||||
|
||||
let lambda_set_variables: Vec<_> = lambda_set_variables
|
||||
let lambda_set_variables_it = lambda_set_variables
|
||||
.iter()
|
||||
.map(|ls| type_to_variable(subs, rank, pools, cached, &ls.0))
|
||||
.collect();
|
||||
.map(|ls| type_to_variable(subs, rank, pools, arena, &ls.0));
|
||||
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 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);
|
||||
|
||||
register(subs, rank, pools, content)
|
||||
|
@ -808,22 +815,22 @@ fn type_to_variable(
|
|||
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 {
|
||||
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));
|
||||
}
|
||||
|
||||
let lambda_set_variables: Vec<_> = lambda_set_variables
|
||||
let lambda_set_variables_it = lambda_set_variables
|
||||
.iter()
|
||||
.map(|ls| type_to_variable(subs, rank, pools, cached, &ls.0))
|
||||
.collect();
|
||||
.map(|ls| type_to_variable(subs, rank, pools, arena, &ls.0));
|
||||
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 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
|
||||
// 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,
|
||||
rank: Rank,
|
||||
pools: &mut Pools,
|
||||
cached: &mut MutMap<Symbol, Variable>,
|
||||
arena: &'a bumpalo::Bump,
|
||||
tags: &[(TagName, Vec<Type>)],
|
||||
ext: &Type,
|
||||
) -> (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 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);
|
||||
}
|
||||
|
||||
|
@ -875,7 +884,7 @@ fn type_to_union_tags(
|
|||
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 (it, ext) =
|
||||
|
@ -1031,8 +1040,7 @@ fn pool_to_rank_table(
|
|||
|
||||
// Sort the variables into buckets by rank.
|
||||
for &var in young_vars.iter() {
|
||||
let rank = subs.get_rank(var);
|
||||
subs.set_mark(var, young_mark);
|
||||
let rank = subs.get_rank_set_mark(var, young_mark);
|
||||
|
||||
debug_assert!(rank.into_usize() < young_rank.into_usize() + 1);
|
||||
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
|
||||
pub fn instantiate_rigids(subs: &mut Subs, var: Variable) {
|
||||
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(
|
||||
subs: &mut Subs,
|
||||
max_rank: Rank,
|
||||
pools: &mut Pools,
|
||||
var: Variable,
|
||||
) -> Variable {
|
||||
use roc_types::subs::Content::*;
|
||||
use roc_types::subs::FlatType::*;
|
||||
fn instantiate_rigids_help(subs: &mut Subs, max_rank: Rank, initial: Variable) {
|
||||
let mut visited = vec![];
|
||||
let mut stack = vec![initial];
|
||||
|
||||
let desc = subs.get_without_compacting(var);
|
||||
|
||||
if let Some(copy) = desc.copy.into_variable() {
|
||||
return copy;
|
||||
macro_rules! var_slice {
|
||||
($variable_subs_slice:expr) => {{
|
||||
let slice = $variable_subs_slice;
|
||||
&subs.variables[slice.slice.start as usize..][..slice.slice.length as usize]
|
||||
}};
|
||||
}
|
||||
|
||||
let make_descriptor = |content| Descriptor {
|
||||
content,
|
||||
rank: max_rank,
|
||||
mark: Mark::NONE,
|
||||
copy: OptVariable::NONE,
|
||||
};
|
||||
while let Some(var) = stack.pop() {
|
||||
visited.push(var);
|
||||
|
||||
let content = desc.content;
|
||||
let copy = var;
|
||||
let desc = subs.get_ref_mut(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
|
||||
// avoid making multiple copies of the variable we are instantiating.
|
||||
//
|
||||
// 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(),
|
||||
},
|
||||
);
|
||||
use Content::*;
|
||||
use FlatType::*;
|
||||
|
||||
// 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 content {
|
||||
Structure(flat_type) => {
|
||||
match flat_type {
|
||||
match &desc.content {
|
||||
RigidVar(name) => {
|
||||
// what it's all about: convert the rigid var into a flex var
|
||||
let name = name.clone();
|
||||
|
||||
// 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) => {
|
||||
for var_index in args.into_iter() {
|
||||
let var = subs[var_index];
|
||||
instantiate_rigids_help(subs, max_rank, pools, var);
|
||||
}
|
||||
stack.extend(var_slice!(*args));
|
||||
}
|
||||
|
||||
Func(arg_vars, closure_var, ret_var) => {
|
||||
instantiate_rigids_help(subs, max_rank, pools, ret_var);
|
||||
instantiate_rigids_help(subs, max_rank, pools, closure_var);
|
||||
let arg_vars = *arg_vars;
|
||||
let ret_var = *ret_var;
|
||||
let closure_var = *closure_var;
|
||||
|
||||
for index in arg_vars.into_iter() {
|
||||
let var = subs[index];
|
||||
instantiate_rigids_help(subs, max_rank, pools, var);
|
||||
}
|
||||
stack.extend(var_slice!(arg_vars));
|
||||
|
||||
stack.push(ret_var);
|
||||
stack.push(closure_var);
|
||||
}
|
||||
|
||||
EmptyRecord | EmptyTagUnion | Erroneous(_) => {}
|
||||
EmptyRecord => (),
|
||||
EmptyTagUnion => (),
|
||||
|
||||
Record(fields, ext_var) => {
|
||||
for index in fields.iter_variables() {
|
||||
let var = subs[index];
|
||||
instantiate_rigids_help(subs, max_rank, pools, var);
|
||||
}
|
||||
let fields = *fields;
|
||||
let ext_var = *ext_var;
|
||||
stack.extend(var_slice!(fields.variables()));
|
||||
|
||||
instantiate_rigids_help(subs, max_rank, pools, ext_var);
|
||||
stack.push(ext_var);
|
||||
}
|
||||
|
||||
TagUnion(tags, ext_var) => {
|
||||
for (_, index) in tags.iter_all() {
|
||||
let slice = subs[index];
|
||||
for var_index in slice {
|
||||
let var = subs[var_index];
|
||||
instantiate_rigids_help(subs, max_rank, pools, var);
|
||||
}
|
||||
let tags = *tags;
|
||||
let ext_var = *ext_var;
|
||||
|
||||
for slice_index in tags.variables() {
|
||||
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(_tag_name, _symbol, ext_var) => {
|
||||
instantiate_rigids_help(subs, max_rank, pools, ext_var);
|
||||
FunctionOrTagUnion(_, _, ext_var) => {
|
||||
stack.push(*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() {
|
||||
let slice = subs[index];
|
||||
for var_index in slice {
|
||||
let var = subs[var_index];
|
||||
instantiate_rigids_help(subs, max_rank, pools, var);
|
||||
for slice_index in tags.variables() {
|
||||
let slice = subs.variable_slices[slice_index.start as usize];
|
||||
stack.extend(var_slice!(slice));
|
||||
}
|
||||
|
||||
stack.push(ext_var);
|
||||
stack.push(rec_var);
|
||||
}
|
||||
|
||||
Erroneous(_) => (),
|
||||
},
|
||||
Alias(_, args, var) => {
|
||||
let var = *var;
|
||||
let args = *args;
|
||||
|
||||
stack.extend(var_slice!(args.variables()));
|
||||
|
||||
stack.push(var);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
instantiate_rigids_help(subs, max_rank, pools, ext_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);
|
||||
|
||||
FlexVar(_) | Error => {}
|
||||
|
||||
RecursionVar { structure, .. } => {
|
||||
instantiate_rigids_help(subs, max_rank, pools, structure);
|
||||
}
|
||||
|
||||
RigidVar(name) => {
|
||||
// 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);
|
||||
if descriptor.copy.is_some() {
|
||||
descriptor.rank = Rank::NONE;
|
||||
descriptor.mark = Mark::NONE;
|
||||
descriptor.copy = OptVariable::NONE;
|
||||
}
|
||||
}
|
||||
|
||||
var
|
||||
}
|
||||
|
||||
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]
|
||||
fn function_that_captures_nothing_is_not_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>);
|
||||
}
|
||||
|
||||
#[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]
|
||||
#[cfg(any(feature = "gen-llvm"))]
|
||||
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);
|
||||
}
|
||||
|
||||
#[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]
|
||||
#[cfg(any(feature = "gen-llvm"))]
|
||||
#[should_panic(expected = r#"Roc failed with message: "invalid ret_layout""#)]
|
||||
|
|
|
@ -968,3 +968,24 @@ fn update_the_only_field() {
|
|||
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]
|
||||
// fn small_str_concat_empty_second_arg() {
|
||||
// assert_llvm_evals_to!(
|
||||
// r#"Str.concat "JJJJJJJJJJJJJJJ" """#,
|
||||
// [
|
||||
// 0x4a,
|
||||
// 0x4a,
|
||||
// 0x4a,
|
||||
// 0x4a,
|
||||
// 0x4a,
|
||||
// 0x4a,
|
||||
// 0x4a,
|
||||
// 0x4a,
|
||||
// 0x4a,
|
||||
// 0x4a,
|
||||
// 0x4a,
|
||||
// 0x4a,
|
||||
// 0x4a,
|
||||
// 0x4a,
|
||||
// 0x4a,
|
||||
// 0b1000_1111
|
||||
// ],
|
||||
// [u8; 16]
|
||||
// );
|
||||
// }
|
||||
#[test]
|
||||
fn small_str_concat_empty_second_arg() {
|
||||
assert_evals_to!(
|
||||
r#"Str.concat "JJJJJJJ" """#,
|
||||
[0x4a, 0x4a, 0x4a, 0x4a, 0x4a, 0x4a, 0x4a, 0b1000_0111],
|
||||
[u8; 8]
|
||||
);
|
||||
}
|
||||
|
||||
// #[test]
|
||||
// fn small_str_concat_small_to_big() {
|
||||
// assert_evals_to!(
|
||||
// r#"Str.concat "abc" " this is longer than 15 chars""#,
|
||||
// RocStr::from_slice(b"abc this is longer than 15 chars"),
|
||||
// RocStr
|
||||
// );
|
||||
// }
|
||||
#[test]
|
||||
fn small_str_concat_small_to_big() {
|
||||
assert_evals_to!(
|
||||
r#"Str.concat "abc" " this is longer than 7 chars""#,
|
||||
RocStr::from_slice(b"abc this is longer than 7 chars"),
|
||||
RocStr
|
||||
);
|
||||
}
|
||||
|
||||
// #[test]
|
||||
// fn small_str_concat_small_to_small_staying_small() {
|
||||
// assert_llvm_evals_to!(
|
||||
// r#"Str.concat "J" "JJJJJJJJJJJJJJ""#,
|
||||
// [
|
||||
// 0x4a,
|
||||
// 0x4a,
|
||||
// 0x4a,
|
||||
// 0x4a,
|
||||
// 0x4a,
|
||||
// 0x4a,
|
||||
// 0x4a,
|
||||
// 0x4a,
|
||||
// 0x4a,
|
||||
// 0x4a,
|
||||
// 0x4a,
|
||||
// 0x4a,
|
||||
// 0x4a,
|
||||
// 0x4a,
|
||||
// 0x4a,
|
||||
// 0b1000_1111
|
||||
// ],
|
||||
// [u8; 16]
|
||||
// );
|
||||
// }
|
||||
#[test]
|
||||
fn small_str_concat_small_to_small_staying_small() {
|
||||
assert_evals_to!(
|
||||
r#"Str.concat "J" "JJJJJJ""#,
|
||||
[0x4a, 0x4a, 0x4a, 0x4a, 0x4a, 0x4a, 0x4a, 0b1000_0111],
|
||||
[u8; 8]
|
||||
);
|
||||
}
|
||||
|
||||
// #[test]
|
||||
// fn small_str_concat_small_to_small_overflow_to_big() {
|
||||
// assert_evals_to!(
|
||||
// r#"Str.concat "abcdefghijklm" "nopqrstuvwxyz""#,
|
||||
// RocStr::from_slice(b"abcdefghijklmnopqrstuvwxyz"),
|
||||
// RocStr
|
||||
// );
|
||||
// }
|
||||
#[test]
|
||||
fn small_str_concat_small_to_small_overflow_to_big() {
|
||||
assert_evals_to!(
|
||||
r#"Str.concat "abcdefg" "hijklmn""#,
|
||||
RocStr::from_slice(b"abcdefghijklmn"),
|
||||
RocStr
|
||||
);
|
||||
}
|
||||
|
||||
// #[test]
|
||||
// fn str_concat_empty() {
|
||||
// assert_evals_to!(r#"Str.concat "" """#, RocStr::default(), RocStr);
|
||||
// }
|
||||
#[test]
|
||||
fn str_concat_empty() {
|
||||
assert_evals_to!(r#"Str.concat "" """#, RocStr::default(), RocStr);
|
||||
}
|
||||
|
||||
// #[test]
|
||||
// fn small_str_is_empty() {
|
||||
// assert_evals_to!(r#"Str.isEmpty "abc""#, false, bool);
|
||||
// }
|
||||
#[test]
|
||||
fn small_str_is_empty() {
|
||||
assert_evals_to!(r#"Str.isEmpty "abc""#, false, bool);
|
||||
}
|
||||
|
||||
// #[test]
|
||||
// fn big_str_is_empty() {
|
||||
// assert_evals_to!(
|
||||
// r#"Str.isEmpty "this is more than 15 chars long""#,
|
||||
// false,
|
||||
// bool
|
||||
// );
|
||||
// }
|
||||
#[test]
|
||||
fn big_str_is_empty() {
|
||||
assert_evals_to!(
|
||||
r#"Str.isEmpty "this is more than 15 chars long""#,
|
||||
false,
|
||||
bool
|
||||
);
|
||||
}
|
||||
|
||||
// #[test]
|
||||
// fn empty_str_is_empty() {
|
||||
// assert_evals_to!(r#"Str.isEmpty """#, true, bool);
|
||||
// }
|
||||
#[test]
|
||||
fn empty_str_is_empty() {
|
||||
assert_evals_to!(r#"Str.isEmpty """#, true, bool);
|
||||
}
|
||||
|
||||
// #[test]
|
||||
// fn str_starts_with() {
|
||||
// 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 "nope" "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);
|
||||
// }
|
||||
#[test]
|
||||
fn str_starts_with() {
|
||||
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 "nope" "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);
|
||||
}
|
||||
|
||||
// #[test]
|
||||
// fn str_starts_with_code_point() {
|
||||
// assert_evals_to!(
|
||||
// &format!(r#"Str.startsWithCodePt "foobar" {}"#, 'f' as u32),
|
||||
// true,
|
||||
// bool
|
||||
// );
|
||||
// assert_evals_to!(
|
||||
// &format!(r#"Str.startsWithCodePt "zoobar" {}"#, 'f' as u32),
|
||||
// false,
|
||||
// bool
|
||||
// );
|
||||
// }
|
||||
#[test]
|
||||
fn str_starts_with_code_point() {
|
||||
assert_evals_to!(
|
||||
&format!(r#"Str.startsWithCodePt "foobar" {}"#, 'f' as u32),
|
||||
true,
|
||||
bool
|
||||
);
|
||||
assert_evals_to!(
|
||||
&format!(r#"Str.startsWithCodePt "zoobar" {}"#, 'f' as u32),
|
||||
false,
|
||||
bool
|
||||
);
|
||||
}
|
||||
|
||||
// #[test]
|
||||
// fn str_ends_with() {
|
||||
// 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 "" "hello world""#, false, bool);
|
||||
// }
|
||||
#[test]
|
||||
fn str_ends_with() {
|
||||
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 "" "hello world""#, false, bool);
|
||||
}
|
||||
|
||||
// #[test]
|
||||
// fn str_count_graphemes_small_str() {
|
||||
|
@ -448,37 +414,37 @@ fn small_str_concat_empty_first_arg() {
|
|||
// );
|
||||
// }
|
||||
|
||||
// #[test]
|
||||
// fn str_starts_with_same_big_str() {
|
||||
// assert_evals_to!(
|
||||
// r#"Str.startsWith "123456789123456789" "123456789123456789""#,
|
||||
// true,
|
||||
// bool
|
||||
// );
|
||||
// }
|
||||
#[test]
|
||||
fn str_starts_with_same_big_str() {
|
||||
assert_evals_to!(
|
||||
r#"Str.startsWith "123456789123456789" "123456789123456789""#,
|
||||
true,
|
||||
bool
|
||||
);
|
||||
}
|
||||
|
||||
// #[test]
|
||||
// fn str_starts_with_different_big_str() {
|
||||
// assert_evals_to!(
|
||||
// r#"Str.startsWith "12345678912345678910" "123456789123456789""#,
|
||||
// true,
|
||||
// bool
|
||||
// );
|
||||
// }
|
||||
#[test]
|
||||
fn str_starts_with_different_big_str() {
|
||||
assert_evals_to!(
|
||||
r#"Str.startsWith "12345678912345678910" "123456789123456789""#,
|
||||
true,
|
||||
bool
|
||||
);
|
||||
}
|
||||
|
||||
// #[test]
|
||||
// fn str_starts_with_same_small_str() {
|
||||
// assert_evals_to!(r#"Str.startsWith "1234" "1234""#, true, bool);
|
||||
// }
|
||||
#[test]
|
||||
fn str_starts_with_same_small_str() {
|
||||
assert_evals_to!(r#"Str.startsWith "1234" "1234""#, true, bool);
|
||||
}
|
||||
|
||||
// #[test]
|
||||
// fn str_starts_with_different_small_str() {
|
||||
// assert_evals_to!(r#"Str.startsWith "1234" "12""#, true, bool);
|
||||
// }
|
||||
// #[test]
|
||||
// fn str_starts_with_false_small_str() {
|
||||
// assert_evals_to!(r#"Str.startsWith "1234" "23""#, false, bool);
|
||||
// }
|
||||
#[test]
|
||||
fn str_starts_with_different_small_str() {
|
||||
assert_evals_to!(r#"Str.startsWith "1234" "12""#, true, bool);
|
||||
}
|
||||
#[test]
|
||||
fn str_starts_with_false_small_str() {
|
||||
assert_evals_to!(r#"Str.startsWith "1234" "23""#, false, bool);
|
||||
}
|
||||
|
||||
// #[test]
|
||||
// fn str_from_int() {
|
||||
|
@ -934,121 +900,239 @@ fn small_str_concat_empty_first_arg() {
|
|||
// );
|
||||
// }
|
||||
|
||||
// #[test]
|
||||
// fn str_repeat_small() {
|
||||
// assert_evals_to!(
|
||||
// indoc!(r#"Str.repeat "Roc" 3"#),
|
||||
// RocStr::from("RocRocRoc"),
|
||||
// RocStr
|
||||
// );
|
||||
// }
|
||||
#[test]
|
||||
fn str_repeat_small() {
|
||||
assert_evals_to!(
|
||||
indoc!(r#"Str.repeat "Roc" 3"#),
|
||||
RocStr::from("RocRocRoc"),
|
||||
RocStr
|
||||
);
|
||||
}
|
||||
|
||||
// #[test]
|
||||
// fn str_repeat_big() {
|
||||
// assert_evals_to!(
|
||||
// indoc!(r#"Str.repeat "more than 16 characters" 2"#),
|
||||
// RocStr::from("more than 16 charactersmore than 16 characters"),
|
||||
// RocStr
|
||||
// );
|
||||
// }
|
||||
#[test]
|
||||
fn str_repeat_big() {
|
||||
assert_evals_to!(
|
||||
indoc!(r#"Str.repeat "more than 16 characters" 2"#),
|
||||
RocStr::from("more than 16 charactersmore than 16 characters"),
|
||||
RocStr
|
||||
);
|
||||
}
|
||||
|
||||
// #[test]
|
||||
// fn str_repeat_empty_string() {
|
||||
// assert_evals_to!(indoc!(r#"Str.repeat "" 3"#), RocStr::from(""), RocStr);
|
||||
// }
|
||||
#[test]
|
||||
fn str_repeat_empty_string() {
|
||||
assert_evals_to!(indoc!(r#"Str.repeat "" 3"#), RocStr::from(""), RocStr);
|
||||
}
|
||||
|
||||
// #[test]
|
||||
// fn str_repeat_zero_times() {
|
||||
// assert_evals_to!(indoc!(r#"Str.repeat "Roc" 0"#), RocStr::from(""), RocStr);
|
||||
// }
|
||||
#[test]
|
||||
fn str_repeat_zero_times() {
|
||||
assert_evals_to!(indoc!(r#"Str.repeat "Roc" 0"#), RocStr::from(""), RocStr);
|
||||
}
|
||||
|
||||
// #[test]
|
||||
// fn str_trim_empty_string() {
|
||||
// assert_evals_to!(indoc!(r#"Str.trim """#), RocStr::from(""), RocStr);
|
||||
// }
|
||||
#[test]
|
||||
fn str_trim_empty_string() {
|
||||
assert_evals_to!(indoc!(r#"Str.trim """#), RocStr::from(""), RocStr);
|
||||
}
|
||||
|
||||
// #[test]
|
||||
// fn str_trim_small_blank_string() {
|
||||
// assert_evals_to!(indoc!(r#"Str.trim " ""#), RocStr::from(""), RocStr);
|
||||
// }
|
||||
#[test]
|
||||
fn str_trim_small_blank_string() {
|
||||
assert_evals_to!(indoc!(r#"Str.trim " ""#), RocStr::from(""), RocStr);
|
||||
}
|
||||
|
||||
// #[test]
|
||||
// fn str_trim_small_to_small() {
|
||||
// assert_evals_to!(
|
||||
// indoc!(r#"Str.trim " hello world ""#),
|
||||
// RocStr::from("hello world"),
|
||||
// RocStr
|
||||
// );
|
||||
// }
|
||||
#[test]
|
||||
fn str_trim_small_to_small() {
|
||||
assert_evals_to!(
|
||||
indoc!(r#"Str.trim " hello world ""#),
|
||||
RocStr::from("hello world"),
|
||||
RocStr
|
||||
);
|
||||
}
|
||||
|
||||
// #[test]
|
||||
// fn str_trim_large_to_large_unique() {
|
||||
// assert_evals_to!(
|
||||
// indoc!(r#"Str.trim (Str.concat " " "hello world from a large string ")"#),
|
||||
// RocStr::from("hello world from a large string"),
|
||||
// RocStr
|
||||
// );
|
||||
// }
|
||||
#[test]
|
||||
fn str_trim_large_to_large_unique() {
|
||||
assert_evals_to!(
|
||||
indoc!(r#"Str.trim (Str.concat " " "hello world from a large string ")"#),
|
||||
RocStr::from("hello world from a large string"),
|
||||
RocStr
|
||||
);
|
||||
}
|
||||
|
||||
// #[test]
|
||||
// fn str_trim_large_to_small_unique() {
|
||||
// assert_evals_to!(
|
||||
// indoc!(r#"Str.trim (Str.concat " " "hello world ")"#),
|
||||
// RocStr::from("hello world"),
|
||||
// RocStr
|
||||
// );
|
||||
// }
|
||||
#[test]
|
||||
fn str_trim_large_to_small_unique() {
|
||||
assert_evals_to!(
|
||||
indoc!(r#"Str.trim (Str.concat " " "hello world ")"#),
|
||||
RocStr::from("hello world"),
|
||||
RocStr
|
||||
);
|
||||
}
|
||||
|
||||
// #[test]
|
||||
// fn str_trim_large_to_large_shared() {
|
||||
// assert_evals_to!(
|
||||
// indoc!(
|
||||
// r#"
|
||||
// original : Str
|
||||
// original = " hello world world "
|
||||
#[test]
|
||||
fn str_trim_large_to_large_shared() {
|
||||
assert_evals_to!(
|
||||
indoc!(
|
||||
r#"
|
||||
original : Str
|
||||
original = " hello world world "
|
||||
|
||||
// { trimmed: Str.trim original, original: original }
|
||||
// "#
|
||||
// ),
|
||||
// (
|
||||
// RocStr::from(" hello world world "),
|
||||
// RocStr::from("hello world world"),
|
||||
// ),
|
||||
// (RocStr, RocStr)
|
||||
// );
|
||||
// }
|
||||
{ trimmed: Str.trim original, original: original }
|
||||
"#
|
||||
),
|
||||
(
|
||||
RocStr::from(" hello world world "),
|
||||
RocStr::from("hello world world"),
|
||||
),
|
||||
(RocStr, RocStr)
|
||||
);
|
||||
}
|
||||
|
||||
// #[test]
|
||||
// fn str_trim_large_to_small_shared() {
|
||||
// assert_evals_to!(
|
||||
// indoc!(
|
||||
// r#"
|
||||
// original : Str
|
||||
// original = " hello world "
|
||||
#[test]
|
||||
fn str_trim_large_to_small_shared() {
|
||||
assert_evals_to!(
|
||||
indoc!(
|
||||
r#"
|
||||
original : Str
|
||||
original = " hello world "
|
||||
|
||||
// { trimmed: Str.trim original, original: original }
|
||||
// "#
|
||||
// ),
|
||||
// (
|
||||
// RocStr::from(" hello world "),
|
||||
// RocStr::from("hello world"),
|
||||
// ),
|
||||
// (RocStr, RocStr)
|
||||
// );
|
||||
// }
|
||||
{ trimmed: Str.trim original, original: original }
|
||||
"#
|
||||
),
|
||||
(
|
||||
RocStr::from(" hello world "),
|
||||
RocStr::from("hello world"),
|
||||
),
|
||||
(RocStr, RocStr)
|
||||
);
|
||||
}
|
||||
|
||||
// #[test]
|
||||
// fn str_trim_small_to_small_shared() {
|
||||
// assert_evals_to!(
|
||||
// indoc!(
|
||||
// r#"
|
||||
// original : Str
|
||||
// original = " hello world "
|
||||
#[test]
|
||||
fn str_trim_small_to_small_shared() {
|
||||
assert_evals_to!(
|
||||
indoc!(
|
||||
r#"
|
||||
original : Str
|
||||
original = " hello world "
|
||||
|
||||
// { trimmed: Str.trim original, original: original }
|
||||
// "#
|
||||
// ),
|
||||
// (RocStr::from(" hello world "), RocStr::from("hello world"),),
|
||||
// (RocStr, RocStr)
|
||||
// );
|
||||
// }
|
||||
{ trimmed: Str.trim original, original: original }
|
||||
"#
|
||||
),
|
||||
(RocStr::from(" hello world "), RocStr::from("hello world"),),
|
||||
(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;
|
||||
ret Test.11;
|
||||
|
||||
|
@ -13,7 +13,7 @@ procedure Test.1 (Test.3):
|
|||
ret Test.12;
|
||||
in
|
||||
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;
|
||||
|
||||
procedure Test.0 ():
|
||||
|
|
|
@ -10,4 +10,5 @@ roc_collections = { path = "../collections" }
|
|||
roc_region = { path = "../region" }
|
||||
roc_module = { path = "../module" }
|
||||
ven_ena = { path = "../../vendor/ena" }
|
||||
bumpalo = { version = "3.8.0", features = ["collections"] }
|
||||
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!
|
||||
// please change it to the lower number.
|
||||
// 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; 32], Content);
|
||||
static_assertions::assert_eq_size!([u8; 24], FlatType);
|
||||
static_assertions::assert_eq_size!([u8; 48], Problem);
|
||||
static_assertions::assert_eq_size!([u8; 6 * 8], Descriptor);
|
||||
static_assertions::assert_eq_size!([u8; 4 * 8], Content);
|
||||
static_assertions::assert_eq_size!([u8; 3 * 8], FlatType);
|
||||
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)]
|
||||
pub struct Mark(i32);
|
||||
|
@ -61,16 +63,7 @@ pub struct Subs {
|
|||
|
||||
impl Default for Subs {
|
||||
fn default() -> Self {
|
||||
Subs {
|
||||
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()],
|
||||
}
|
||||
Subs::new()
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -79,14 +72,14 @@ impl Default for Subs {
|
|||
/// The starting position is a u32 which should be plenty
|
||||
/// We limit slices to u16::MAX = 65535 elements
|
||||
pub struct SubsSlice<T> {
|
||||
start: u32,
|
||||
length: u16,
|
||||
pub start: u32,
|
||||
pub length: u16,
|
||||
_marker: std::marker::PhantomData<T>,
|
||||
}
|
||||
|
||||
/// An index into the Vec<T> of subs
|
||||
pub struct SubsIndex<T> {
|
||||
start: u32,
|
||||
pub start: u32,
|
||||
_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)?;
|
||||
|
||||
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);
|
||||
|
@ -679,11 +673,11 @@ impl Variable {
|
|||
///
|
||||
/// This should only ever be called from tests!
|
||||
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)
|
||||
}
|
||||
|
||||
pub fn index(&self) -> u32 {
|
||||
pub const fn index(&self) -> u32 {
|
||||
self.0
|
||||
}
|
||||
}
|
||||
|
@ -985,21 +979,31 @@ fn define_integer_types(subs: &mut Subs) {
|
|||
}
|
||||
|
||||
impl Subs {
|
||||
pub fn new(var_store: VarStore) -> Self {
|
||||
let entries = var_store.next;
|
||||
pub fn new() -> Self {
|
||||
Self::with_capacity(0)
|
||||
}
|
||||
|
||||
pub fn with_capacity(capacity: usize) -> Self {
|
||||
let capacity = capacity.max(Variable::NUM_RESERVED_VARS);
|
||||
|
||||
let mut subs = Subs {
|
||||
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
|
||||
subs.utable.reserve(entries as usize);
|
||||
subs.utable.reserve(capacity);
|
||||
|
||||
// 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.
|
||||
|
||||
for _ in 0..entries {
|
||||
for _ in 0..capacity {
|
||||
subs.utable.new_key(flex_var_descriptor());
|
||||
}
|
||||
|
||||
|
@ -1040,7 +1044,14 @@ impl 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) {
|
||||
self.utable.reserve(entries);
|
||||
for _ in 0..entries {
|
||||
self.utable.new_key(flex_var_descriptor());
|
||||
}
|
||||
|
@ -1080,6 +1091,10 @@ impl Subs {
|
|||
&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 {
|
||||
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 {
|
||||
self.utable.unioned(left, right)
|
||||
}
|
||||
|
@ -1192,22 +1227,7 @@ impl Subs {
|
|||
}
|
||||
|
||||
pub fn restore(&mut self, var: Variable) {
|
||||
let desc = self.get(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);
|
||||
}
|
||||
restore_help(self, var)
|
||||
}
|
||||
|
||||
pub fn len(&self) -> usize {
|
||||
|
@ -1218,6 +1238,10 @@ impl Subs {
|
|||
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>> {
|
||||
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)]
|
||||
pub enum Content {
|
||||
/// A type variable which the user did not name in an annotation,
|
||||
|
@ -1347,10 +1377,10 @@ pub enum Content {
|
|||
|
||||
#[derive(Clone, Copy, Debug, Default)]
|
||||
pub struct AliasVariables {
|
||||
lowercases_start: u32,
|
||||
variables_start: u32,
|
||||
lowercases_len: u16,
|
||||
variables_len: u16,
|
||||
pub lowercases_start: u32,
|
||||
pub variables_start: u32,
|
||||
pub lowercases_len: u16,
|
||||
pub variables_len: u16,
|
||||
}
|
||||
|
||||
impl AliasVariables {
|
||||
|
@ -1466,6 +1496,8 @@ impl Content {
|
|||
}
|
||||
}
|
||||
|
||||
static_assertions::assert_eq_size!([u8; 3 * 8], FlatType);
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub enum FlatType {
|
||||
Apply(Symbol, VariableSubsSlice),
|
||||
|
@ -1489,7 +1521,7 @@ pub enum Builtin {
|
|||
|
||||
#[derive(Clone, Copy, Debug, Default)]
|
||||
pub struct VariableSubsSlice {
|
||||
slice: SubsSlice<Variable>,
|
||||
pub slice: SubsSlice<Variable>,
|
||||
}
|
||||
|
||||
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>> {
|
||||
let slice = SubsSlice::new(self.variables_start, self.length);
|
||||
slice.into_iter()
|
||||
|
@ -2701,79 +2739,643 @@ fn get_fresh_var_name(state: &mut ErrorTypeState) -> Lowercase {
|
|||
name
|
||||
}
|
||||
|
||||
fn restore_content(subs: &mut Subs, content: &Content) {
|
||||
fn restore_help(subs: &mut Subs, initial: Variable) {
|
||||
let mut stack = vec![initial];
|
||||
|
||||
let variable_slices = &subs.variable_slices;
|
||||
|
||||
let variables = &subs.variables;
|
||||
let var_slice = |variable_subs_slice: VariableSubsSlice| {
|
||||
&variables[variable_subs_slice.slice.start as usize..]
|
||||
[..variable_subs_slice.slice.length as usize]
|
||||
};
|
||||
|
||||
while let Some(var) = stack.pop() {
|
||||
let desc = &mut subs.utable.probe_value_ref_mut(var).value;
|
||||
|
||||
if desc.copy.is_some() {
|
||||
desc.rank = Rank::NONE;
|
||||
desc.mark = Mark::NONE;
|
||||
desc.copy = OptVariable::NONE;
|
||||
|
||||
use Content::*;
|
||||
use FlatType::*;
|
||||
|
||||
match content {
|
||||
FlexVar(_) | RigidVar(_) | RecursionVar { .. } | Error => (),
|
||||
match &desc.content {
|
||||
FlexVar(_) | RigidVar(_) | Error => (),
|
||||
|
||||
RecursionVar { structure, .. } => {
|
||||
stack.push(*structure);
|
||||
}
|
||||
|
||||
Structure(flat_type) => match flat_type {
|
||||
Apply(_, args) => {
|
||||
for index in args.into_iter() {
|
||||
let var = subs[index];
|
||||
subs.restore(var);
|
||||
}
|
||||
stack.extend(var_slice(*args));
|
||||
}
|
||||
|
||||
Func(arg_vars, closure_var, ret_var) => {
|
||||
for index in arg_vars.into_iter() {
|
||||
let var = subs[index];
|
||||
subs.restore(var);
|
||||
}
|
||||
stack.extend(var_slice(*arg_vars));
|
||||
|
||||
subs.restore(*ret_var);
|
||||
subs.restore(*closure_var);
|
||||
stack.push(*ret_var);
|
||||
stack.push(*closure_var);
|
||||
}
|
||||
|
||||
EmptyRecord => (),
|
||||
EmptyTagUnion => (),
|
||||
|
||||
Record(fields, ext_var) => {
|
||||
for index in fields.iter_variables() {
|
||||
let var = subs[index];
|
||||
subs.restore(var);
|
||||
}
|
||||
stack.extend(var_slice(fields.variables()));
|
||||
|
||||
subs.restore(*ext_var);
|
||||
stack.push(*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);
|
||||
}
|
||||
let slice = variable_slices[slice_index.start as usize];
|
||||
stack.extend(var_slice(slice));
|
||||
}
|
||||
|
||||
subs.restore(*ext_var);
|
||||
stack.push(*ext_var);
|
||||
}
|
||||
FunctionOrTagUnion(_, _, ext_var) => {
|
||||
subs.restore(*ext_var);
|
||||
stack.push(*ext_var);
|
||||
}
|
||||
|
||||
RecursiveTagUnion(rec_var, 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);
|
||||
}
|
||||
let slice = variable_slices[slice_index.start as usize];
|
||||
stack.extend(var_slice(slice));
|
||||
}
|
||||
|
||||
subs.restore(*ext_var);
|
||||
subs.restore(*rec_var);
|
||||
stack.push(*ext_var);
|
||||
stack.push(*rec_var);
|
||||
}
|
||||
|
||||
Erroneous(_) => (),
|
||||
},
|
||||
Alias(_, args, var) => {
|
||||
for var_index in args.variables().into_iter() {
|
||||
let var = subs[var_index];
|
||||
subs.restore(var);
|
||||
}
|
||||
stack.extend(var_slice(args.variables()));
|
||||
|
||||
subs.restore(*var);
|
||||
stack.push(*var);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct StorageSubs {
|
||||
subs: Subs,
|
||||
}
|
||||
|
||||
#[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,
|
||||
};
|
||||
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::low_level::LowLevel;
|
||||
use roc_module::symbol::{Interns, ModuleId, Symbol};
|
||||
|
@ -1134,7 +1135,7 @@ pub enum Reason {
|
|||
#[derive(PartialEq, Debug, Clone)]
|
||||
pub enum Category {
|
||||
Lookup(Symbol),
|
||||
CallResult(Option<Symbol>),
|
||||
CallResult(Option<Symbol>, CalledVia),
|
||||
LowLevelOpResult(LowLevel),
|
||||
ForeignCall,
|
||||
TagApply {
|
||||
|
|
|
@ -133,12 +133,14 @@ fn unify_context(subs: &mut Subs, pool: &mut Pool, ctx: Context) -> Outcome {
|
|||
// println!("\n --- \n");
|
||||
// dbg!(ctx.second, type2);
|
||||
// println!("\n --------------- \n");
|
||||
let content_1 = subs.get(ctx.first).content;
|
||||
let content_2 = subs.get(ctx.second).content;
|
||||
println!(
|
||||
"{:?} {:?} ~ {:?} {:?}",
|
||||
ctx.first,
|
||||
subs.get(ctx.first).content,
|
||||
roc_types::subs::SubsFmtContent(&content_1, subs),
|
||||
ctx.second,
|
||||
subs.get(ctx.second).content
|
||||
roc_types::subs::SubsFmtContent(&content_2, subs),
|
||||
);
|
||||
}
|
||||
match &ctx.first_desc.content {
|
||||
|
|
|
@ -5,7 +5,7 @@ Unlike most editors, we use projectional or structural editing to edit the [Abst
|
|||
|
||||
## 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:
|
||||
|
||||
```
|
||||
|
|
|
@ -501,7 +501,7 @@ impl<'a> EdModel<'a> {
|
|||
};
|
||||
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 {
|
||||
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 std::path::PathBuf;
|
||||
|
||||
use crate::error::r#type::suggest;
|
||||
use crate::report::{Annotation, Report, RocDocAllocator, RocDocBuilder, Severity};
|
||||
use ven_pretty::DocAllocator;
|
||||
|
||||
|
@ -874,16 +875,36 @@ fn pretty_runtime_error<'b>(
|
|||
module_name,
|
||||
ident,
|
||||
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![
|
||||
alloc.concat(vec![
|
||||
alloc.reflow("The "),
|
||||
alloc.module_name(module_name),
|
||||
alloc.reflow(" module does not expose a "),
|
||||
alloc.reflow(" module does not expose `"),
|
||||
alloc.string(ident.to_string()),
|
||||
alloc.reflow(" value:"),
|
||||
alloc.reflow("`:"),
|
||||
]),
|
||||
alloc.region(region),
|
||||
did_you_mean,
|
||||
]);
|
||||
|
||||
title = VALUE_NOT_EXPOSED;
|
||||
|
@ -1176,8 +1197,6 @@ fn not_found<'b>(
|
|||
thing: &'b str,
|
||||
options: MutSet<Box<str>>,
|
||||
) -> RocDocBuilder<'b> {
|
||||
use crate::error::r#type::suggest;
|
||||
|
||||
let mut suggestions = suggest::sort(
|
||||
name.as_inline_str().as_str(),
|
||||
options.iter().map(|v| v.as_ref()).collect(),
|
||||
|
@ -1225,8 +1244,6 @@ fn module_not_found<'b>(
|
|||
name: &ModuleName,
|
||||
options: MutSet<Box<str>>,
|
||||
) -> RocDocBuilder<'b> {
|
||||
use crate::error::r#type::suggest;
|
||||
|
||||
let mut suggestions =
|
||||
suggest::sort(name.as_str(), options.iter().map(|v| v.as_ref()).collect());
|
||||
suggestions.truncate(4);
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
use roc_can::expected::{Expected, PExpected};
|
||||
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::symbol::Symbol;
|
||||
use roc_region::all::{Located, Region};
|
||||
|
@ -1043,13 +1044,26 @@ fn add_category<'b>(
|
|||
alloc.record_field(field.to_owned()),
|
||||
alloc.text(" is a:"),
|
||||
]),
|
||||
|
||||
CallResult(Some(symbol)) => alloc.concat(vec![
|
||||
CallResult(
|
||||
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.symbol_foreign_qualified(*symbol),
|
||||
alloc.text(" call produces:"),
|
||||
]),
|
||||
CallResult(None) => alloc.concat(vec![this_is, alloc.text(":")]),
|
||||
CallResult(None, _) => alloc.concat(vec![this_is, alloc.text(":")]),
|
||||
LowLevelOpResult(op) => {
|
||||
panic!(
|
||||
"Compiler bug: invalid return type from low-level op {:?}",
|
||||
|
|
|
@ -4,3 +4,35 @@
|
|||
|
||||
pub mod error;
|
||||
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(
|
||||
&'a self,
|
||||
content: roc_module::operator::BinOp,
|
||||
content: roc_module::called_via::BinOp,
|
||||
) -> DocBuilder<'a, Self, Annotation> {
|
||||
self.text(content.to_string()).annotate(Annotation::BinOp)
|
||||
}
|
||||
|
|
|
@ -66,7 +66,7 @@ mod test_reporting {
|
|||
problems: can_problems,
|
||||
..
|
||||
} = 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 {
|
||||
subs.rigid_var(var, name);
|
||||
|
@ -298,17 +298,24 @@ mod test_reporting {
|
|||
report_problem_as(
|
||||
indoc!(
|
||||
r#"
|
||||
List.foobar 1 2
|
||||
List.isempty 1 2
|
||||
"#
|
||||
),
|
||||
indoc!(
|
||||
r#"
|
||||
── 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
|
||||
Nat
|
||||
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?
|
||||
|
||||
Ok
|
||||
U8
|
||||
f
|
||||
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]
|
||||
fn keyword_record_field_access() {
|
||||
report_problem_as(
|
||||
|
@ -5596,10 +5707,17 @@ mod test_reporting {
|
|||
r#"
|
||||
── NOT EXPOSED ─────────────────────────────────────────────────────────────────
|
||||
|
||||
The Num module does not expose a if value:
|
||||
The Num module does not expose `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
|
||||
Str
|
||||
Err
|
||||
U8
|
||||
F64
|
||||
"#
|
||||
),
|
||||
)
|
||||
|
|
|
@ -6,8 +6,6 @@ use core::{fmt, mem, ptr, slice};
|
|||
|
||||
// A list of C functions that are being imported
|
||||
extern "C" {
|
||||
pub fn printf(format: *const u8, ...) -> i32;
|
||||
|
||||
pub fn roc_alloc(size: usize, alignment: u32) -> *mut c_void;
|
||||
pub fn roc_realloc(
|
||||
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`,
|
||||
/// which indicates a standard, mutable unification table.
|
||||
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 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)]
|
||||
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]
|
||||
}
|
||||
|
||||
/// 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
|
||||
/// union-find algorithm with path compression:
|
||||
/// <http://en.wikipedia.org/wiki/Disjoint-set_data_structure>.
|
||||
|
@ -451,6 +457,17 @@ where
|
|||
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!
|
||||
pub fn probe_value_without_compacting<K1>(&self, id: K1) -> V
|
||||
where
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue