mirror of
https://github.com/roc-lang/roc.git
synced 2025-09-29 06:44:46 +00:00
Merge branch 'trunk' of github.com:rtfeldman/roc into wasm-link-host-to-app-calls
This commit is contained in:
commit
d045f3be7b
24 changed files with 575 additions and 2027 deletions
5
.github/workflows/nix.yml
vendored
5
.github/workflows/nix.yml
vendored
|
@ -21,5 +21,8 @@ jobs:
|
|||
with:
|
||||
clean: "true"
|
||||
|
||||
- name: setup dependencies with nix, build and test
|
||||
- name: setup dependencies with nix and build the tests
|
||||
run: nix develop -c cargo test --locked --release --no-run
|
||||
|
||||
- name: execute tests with guaranteed success
|
||||
run: nix develop -c cargo test --locked --release --no-fail-fast || true # || true to return a successful exit code so that test failures can be observed
|
||||
|
|
8
Cargo.lock
generated
8
Cargo.lock
generated
|
@ -4141,7 +4141,6 @@ dependencies = [
|
|||
"roc_module",
|
||||
"roc_region",
|
||||
"static_assertions 1.1.0",
|
||||
"ven_ena",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -5077,13 +5076,6 @@ dependencies = [
|
|||
"getrandom",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ven_ena"
|
||||
version = "0.13.1"
|
||||
dependencies = [
|
||||
"log",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ven_graph"
|
||||
version = "2.0.5-pre"
|
||||
|
|
|
@ -27,7 +27,6 @@ members = [
|
|||
"compiler/test_gen",
|
||||
"compiler/roc_target",
|
||||
"compiler/debug_flags",
|
||||
"vendor/ena",
|
||||
"vendor/inkwell",
|
||||
"vendor/pathfinding",
|
||||
"vendor/pretty",
|
||||
|
|
|
@ -361,11 +361,12 @@ pub fn build_swift_host_native(
|
|||
unimplemented!("Linking a shared library to Swift not yet implemented");
|
||||
}
|
||||
|
||||
let mut command = Command::new("swiftc");
|
||||
let mut command = Command::new("xcrun"); // xcrun helps swiftc to find the right header files
|
||||
command
|
||||
.env_clear()
|
||||
.env("PATH", &env_path)
|
||||
.env("HOME", &env_home)
|
||||
.arg("swiftc")
|
||||
.args(sources)
|
||||
.arg("-emit-object")
|
||||
.arg("-parse-as-library")
|
||||
|
|
|
@ -867,9 +867,9 @@ fn resolve_abilities<'a>(
|
|||
.iter()
|
||||
.partition(|av| av.ability == loc_ability_name.value);
|
||||
|
||||
let mut bad_has_clauses = false;
|
||||
|
||||
if variables_bound_to_ability.is_empty() {
|
||||
let var_bound_to_ability = match variables_bound_to_ability.as_slice() {
|
||||
[one] => one.variable,
|
||||
[] => {
|
||||
// There are no variables bound to the parent ability - then this member doesn't
|
||||
// need to be a part of the ability.
|
||||
env.problem(Problem::AbilityMemberMissingHasClause {
|
||||
|
@ -877,16 +877,17 @@ fn resolve_abilities<'a>(
|
|||
ability: loc_ability_name.value,
|
||||
region: name_region,
|
||||
});
|
||||
bad_has_clauses = true;
|
||||
// Pretend the member isn't a part of the ability
|
||||
continue;
|
||||
}
|
||||
|
||||
if variables_bound_to_ability.len() > 1 {
|
||||
[..] => {
|
||||
// There is more than one variable bound to the member signature, so something like
|
||||
// Eq has eq : a, b -> Bool | a has Eq, b has Eq
|
||||
// We have no way of telling what type implements a particular instance of Eq in
|
||||
// this case (a or b?), so disallow it.
|
||||
let span_has_clauses =
|
||||
Region::across_all(variables_bound_to_ability.iter().map(|v| &v.first_seen));
|
||||
let span_has_clauses = Region::across_all(
|
||||
variables_bound_to_ability.iter().map(|v| &v.first_seen),
|
||||
);
|
||||
let bound_var_names = variables_bound_to_ability
|
||||
.iter()
|
||||
.map(|v| v.name.clone())
|
||||
|
@ -897,13 +898,10 @@ fn resolve_abilities<'a>(
|
|||
span_has_clauses,
|
||||
bound_var_names,
|
||||
});
|
||||
bad_has_clauses = true;
|
||||
}
|
||||
|
||||
if bad_has_clauses {
|
||||
// Pretend the member isn't a part of the ability
|
||||
continue;
|
||||
}
|
||||
};
|
||||
|
||||
// The introduced variables are good; add them to the output.
|
||||
output
|
||||
|
@ -918,6 +916,13 @@ fn resolve_abilities<'a>(
|
|||
flex_vars: iv.collect_flex(),
|
||||
};
|
||||
|
||||
let signature = {
|
||||
let mut signature = member_annot.typ;
|
||||
signature
|
||||
.instantiate_lambda_sets_as_unspecialized(var_bound_to_ability, member_sym);
|
||||
signature
|
||||
};
|
||||
|
||||
can_members.push((
|
||||
member_sym,
|
||||
AbilityMemberData {
|
||||
|
@ -925,7 +930,7 @@ fn resolve_abilities<'a>(
|
|||
region: name_region,
|
||||
typ: MemberTypeInfo::Local {
|
||||
variables,
|
||||
signature: member_annot.typ,
|
||||
signature,
|
||||
signature_var: var_store.fresh(),
|
||||
},
|
||||
},
|
||||
|
|
|
@ -1685,7 +1685,6 @@ impl LocalDefVarsVec<(Symbol, Loc<Variable>)> {
|
|||
}
|
||||
}
|
||||
|
||||
use std::borrow::Borrow;
|
||||
use std::cell::RefCell;
|
||||
use std::ops::ControlFlow;
|
||||
std::thread_local! {
|
||||
|
@ -1888,6 +1887,18 @@ fn type_to_variable<'a>(
|
|||
|
||||
register_with_known_var(subs, destination, rank, pools, content)
|
||||
}
|
||||
UnspecializedLambdaSet(..) => {
|
||||
// TODO: instantiate properly!
|
||||
let union_lambdas =
|
||||
UnionLambdas::from_slices(SubsSlice::new(0, 0), SubsSlice::new(0, 0));
|
||||
|
||||
let content = Content::LambdaSet(subs::LambdaSet {
|
||||
solved: union_lambdas,
|
||||
recursion_var: OptVariable::NONE,
|
||||
});
|
||||
|
||||
register_with_known_var(subs, destination, rank, pools, content)
|
||||
}
|
||||
// This case is important for the rank of boolean variables
|
||||
Function(arguments, closure_type, ret_type) => {
|
||||
let new_arguments = VariableSubsSlice::reserve_into_subs(subs, arguments.len());
|
||||
|
@ -2379,12 +2390,12 @@ fn insert_tags_fast_path<'a>(
|
|||
rank: Rank,
|
||||
pools: &mut Pools,
|
||||
arena: &'_ bumpalo::Bump,
|
||||
tags: &'a [(TagName, impl Borrow<[Type]>)],
|
||||
tags: &'a [(TagName, Vec<Type>)],
|
||||
stack: &mut bumpalo::collections::Vec<'_, TypeToVar<'a>>,
|
||||
) -> UnionTags {
|
||||
if let [(TagName(tag_name), arguments)] = tags {
|
||||
let variable_slice =
|
||||
register_tag_arguments(subs, rank, pools, arena, stack, arguments.borrow());
|
||||
register_tag_arguments(subs, rank, pools, arena, stack, arguments.as_slice());
|
||||
let new_variable_slices =
|
||||
SubsSlice::extend_new(&mut subs.variable_slices, [variable_slice]);
|
||||
|
||||
|
@ -2411,7 +2422,7 @@ fn insert_tags_fast_path<'a>(
|
|||
|
||||
for (variable_slice_index, (_, arguments)) in it {
|
||||
subs.variable_slices[variable_slice_index] =
|
||||
register_tag_arguments(subs, rank, pools, arena, stack, arguments.borrow());
|
||||
register_tag_arguments(subs, rank, pools, arena, stack, arguments.as_slice());
|
||||
}
|
||||
|
||||
UnionTags::from_slices(new_tag_names, new_variable_slices)
|
||||
|
@ -2425,7 +2436,7 @@ fn insert_tags_fast_path<'a>(
|
|||
|
||||
for ((variable_slice_index, tag_name_index), (tag_name, arguments)) in it {
|
||||
subs.variable_slices[variable_slice_index] =
|
||||
register_tag_arguments(subs, rank, pools, arena, stack, arguments.borrow());
|
||||
register_tag_arguments(subs, rank, pools, arena, stack, arguments.as_slice());
|
||||
|
||||
subs.tag_names[tag_name_index] = tag_name.clone();
|
||||
}
|
||||
|
@ -2440,12 +2451,12 @@ fn insert_tags_slow_path<'a>(
|
|||
rank: Rank,
|
||||
pools: &mut Pools,
|
||||
arena: &'_ bumpalo::Bump,
|
||||
tags: &'a [(TagName, impl Borrow<[Type]>)],
|
||||
tags: &'a [(TagName, Vec<Type>)],
|
||||
mut tag_vars: bumpalo::collections::Vec<(TagName, VariableSubsSlice)>,
|
||||
stack: &mut bumpalo::collections::Vec<'_, TypeToVar<'a>>,
|
||||
) -> UnionTags {
|
||||
for (tag, tag_argument_types) in tags {
|
||||
let tag_argument_types: &[Type] = tag_argument_types.borrow();
|
||||
let tag_argument_types: &[Type] = tag_argument_types.as_slice();
|
||||
let new_slice = VariableSubsSlice::reserve_into_subs(subs, tag_argument_types.len());
|
||||
|
||||
for (i, arg) in (new_slice.indices()).zip(tag_argument_types) {
|
||||
|
@ -2466,7 +2477,7 @@ fn type_to_union_tags<'a>(
|
|||
rank: Rank,
|
||||
pools: &mut Pools,
|
||||
arena: &'_ bumpalo::Bump,
|
||||
tags: &'a [(TagName, impl Borrow<[Type]>)],
|
||||
tags: &'a [(TagName, Vec<Type>)],
|
||||
ext: &'a TypeExtension,
|
||||
stack: &mut bumpalo::collections::Vec<'_, TypeToVar<'a>>,
|
||||
) -> (UnionTags, Variable) {
|
||||
|
|
|
@ -2504,7 +2504,7 @@ mod solve_expr {
|
|||
{ numIdentity, x : numIdentity 42, y }
|
||||
"#
|
||||
),
|
||||
"{ numIdentity : Num a -> Num a, x : Num a, y : Float * }",
|
||||
"{ numIdentity : Num a -> Num a, x : Num b, y : Float * }",
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -3395,7 +3395,7 @@ mod solve_expr {
|
|||
{ id1, id2 }
|
||||
"#
|
||||
),
|
||||
"{ id1 : q -> q, id2 : q -> q }",
|
||||
"{ id1 : q -> q, id2 : a -> a }",
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -3910,7 +3910,7 @@ mod solve_expr {
|
|||
{ a, b }
|
||||
"#
|
||||
),
|
||||
"{ a : { x : I64, y : I64, z : Num c }, b : { blah : Str, x : I64, y : I64, z : Num c } }",
|
||||
"{ a : { x : I64, y : I64, z : Num c }, b : { blah : Str, x : I64, y : I64, z : Num a } }",
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -3941,7 +3941,7 @@ mod solve_expr {
|
|||
{ a, b }
|
||||
"#
|
||||
),
|
||||
"{ a : { x : Num a, y : Float *, z : c }, b : { blah : Str, x : Num a, y : Float *, z : c } }",
|
||||
"{ a : { x : Num a, y : Float *, z : c }, b : { blah : Str, x : Num b, y : Float *, z : d } }",
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -6100,7 +6100,7 @@ mod solve_expr {
|
|||
hashEq = \x, y -> hash x == hash y
|
||||
"#
|
||||
),
|
||||
"a, a -> Bool | a has Hash",
|
||||
"a, b -> Bool | a has Hash, b has Hash",
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
@ -11,6 +11,5 @@ roc_region = { path = "../region" }
|
|||
roc_module = { path = "../module" }
|
||||
roc_error_macros = {path="../../error_macros"}
|
||||
roc_debug_flags = {path="../debug_flags"}
|
||||
ven_ena = { path = "../../vendor/ena" }
|
||||
bumpalo = { version = "3.8.0", features = ["collections"] }
|
||||
static_assertions = "1.1.0"
|
||||
|
|
|
@ -3,7 +3,7 @@ use crate::subs::{
|
|||
UnionTags, Variable,
|
||||
};
|
||||
use crate::types::{name_type_var, RecordField};
|
||||
use roc_collections::all::{MutMap, MutSet};
|
||||
use roc_collections::all::MutMap;
|
||||
use roc_error_macros::internal_error;
|
||||
use roc_module::ident::{Lowercase, TagName};
|
||||
use roc_module::symbol::{Interns, ModuleId, Symbol};
|
||||
|
@ -58,6 +58,7 @@ struct Env<'a> {
|
|||
/// We only care about whether it was a single time or multiple times,
|
||||
/// because single appearances get a wildcard (*) and multiple times
|
||||
/// get a generated letter ("a" etc).
|
||||
#[derive(Debug)]
|
||||
enum Appearances {
|
||||
Single,
|
||||
Multiple,
|
||||
|
@ -75,7 +76,7 @@ fn find_names_needed(
|
|||
subs: &mut Subs,
|
||||
roots: &mut Vec<Variable>,
|
||||
root_appearances: &mut MutMap<Variable, Appearances>,
|
||||
names_taken: &mut MutSet<Lowercase>,
|
||||
names_taken: &mut MutMap<Lowercase, Variable>,
|
||||
) {
|
||||
use crate::subs::Content::*;
|
||||
use crate::subs::FlatType::*;
|
||||
|
@ -151,19 +152,27 @@ fn find_names_needed(
|
|||
..
|
||||
}
|
||||
| FlexVar(Some(name_index))
|
||||
| FlexAbleVar(Some(name_index), _) => {
|
||||
// This root already has a name. Nothing more to do here!
|
||||
| FlexAbleVar(Some(name_index), _)
|
||||
| RigidVar(name_index)
|
||||
| RigidAbleVar(name_index, _) => {
|
||||
let root = subs.get_root_key_without_compacting(variable);
|
||||
|
||||
// User-defined names are already taken.
|
||||
// We must not accidentally generate names that collide with them!
|
||||
let name = subs.field_names[name_index.index as usize].clone();
|
||||
names_taken.insert(name);
|
||||
match names_taken.get(&name) {
|
||||
Some(var) if *var == root => {}
|
||||
Some(_) => {
|
||||
if !root_appearances.contains_key(&root) {
|
||||
roots.push(root);
|
||||
}
|
||||
// We want a name, but the default name is already taken by another root.
|
||||
root_appearances.insert(root, Appearances::Multiple);
|
||||
}
|
||||
None => {
|
||||
names_taken.insert(name, root);
|
||||
}
|
||||
}
|
||||
RigidVar(name_index) | RigidAbleVar(name_index, _) => {
|
||||
// User-defined names are already taken.
|
||||
// We must not accidentally generate names that collide with them!
|
||||
let name = subs.field_names[name_index.index as usize].clone();
|
||||
names_taken.insert(name);
|
||||
}
|
||||
Structure(Apply(_, args)) => {
|
||||
for index in args.into_iter() {
|
||||
|
@ -255,7 +264,7 @@ fn name_all_type_vars(variable: Variable, subs: &mut Subs) -> NamedResult {
|
|||
let mut roots = Vec::new();
|
||||
let mut letters_used = 0;
|
||||
let mut appearances = MutMap::default();
|
||||
let mut taken = MutSet::default();
|
||||
let mut taken = MutMap::default();
|
||||
|
||||
// Populate names_needed
|
||||
find_names_needed(variable, subs, &mut roots, &mut appearances, &mut taken);
|
||||
|
@ -290,14 +299,14 @@ fn name_root(
|
|||
letters_used: u32,
|
||||
root: Variable,
|
||||
subs: &mut Subs,
|
||||
taken: &mut MutSet<Lowercase>,
|
||||
taken: &mut MutMap<Lowercase, Variable>,
|
||||
) -> u32 {
|
||||
let (generated_name, new_letters_used) =
|
||||
name_type_var(letters_used, &mut taken.iter(), |var, str| {
|
||||
name_type_var(letters_used, &mut taken.keys(), |var, str| {
|
||||
var.as_str() == str
|
||||
});
|
||||
|
||||
taken.insert(generated_name.clone());
|
||||
taken.insert(generated_name.clone(), root);
|
||||
|
||||
set_root_name(root, generated_name, subs);
|
||||
|
||||
|
@ -310,12 +319,12 @@ fn set_root_name(root: Variable, name: Lowercase, subs: &mut Subs) {
|
|||
let old_content = subs.get_content_without_compacting(root);
|
||||
|
||||
match old_content {
|
||||
FlexVar(None) => {
|
||||
FlexVar(_) => {
|
||||
let name_index = SubsIndex::push_new(&mut subs.field_names, name);
|
||||
let content = FlexVar(Some(name_index));
|
||||
subs.set_content(root, content);
|
||||
}
|
||||
&FlexAbleVar(None, ability) => {
|
||||
&FlexAbleVar(_, ability) => {
|
||||
let name_index = SubsIndex::push_new(&mut subs.field_names, name);
|
||||
let content = FlexAbleVar(Some(name_index), ability);
|
||||
subs.set_content(root, content);
|
||||
|
@ -335,8 +344,7 @@ fn set_root_name(root: Variable, name: Lowercase, subs: &mut Subs) {
|
|||
RecursionVar {
|
||||
opt_name: Some(_existing),
|
||||
..
|
||||
}
|
||||
| FlexVar(Some(_existing)) => {
|
||||
} => {
|
||||
panic!("TODO FIXME - make sure the generated name does not clash with any bound vars! In other words, if the user decided to name a type variable 'a', make sure we don't generate 'a' to name a different one!");
|
||||
}
|
||||
|
||||
|
|
|
@ -8,7 +8,6 @@ use roc_module::ident::{Lowercase, TagName, Uppercase};
|
|||
use roc_module::symbol::Symbol;
|
||||
use std::fmt;
|
||||
use std::iter::{once, Iterator, Map};
|
||||
use ven_ena::unify::UnifyKey;
|
||||
|
||||
use crate::unification_table::{Snapshot, UnificationTable};
|
||||
|
||||
|
@ -1206,22 +1205,6 @@ impl fmt::Debug for Variable {
|
|||
}
|
||||
}
|
||||
|
||||
impl UnifyKey for Variable {
|
||||
type Value = Descriptor;
|
||||
|
||||
fn index(&self) -> u32 {
|
||||
self.0
|
||||
}
|
||||
|
||||
fn from_index(index: u32) -> Self {
|
||||
Variable(index)
|
||||
}
|
||||
|
||||
fn tag() -> &'static str {
|
||||
"Variable"
|
||||
}
|
||||
}
|
||||
|
||||
/// Used in SolvedType
|
||||
#[derive(Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
|
||||
pub struct VarId(u32);
|
||||
|
|
|
@ -240,6 +240,7 @@ pub enum Type {
|
|||
name: Symbol,
|
||||
captures: Vec<Type>,
|
||||
},
|
||||
UnspecializedLambdaSet(Uls),
|
||||
DelayedAlias(AliasCommon),
|
||||
Alias {
|
||||
symbol: Symbol,
|
||||
|
@ -264,6 +265,17 @@ pub enum Type {
|
|||
Erroneous(Problem),
|
||||
}
|
||||
|
||||
/// A lambda set under an arrow in a ability member signature. For example, in
|
||||
/// Default has default : {} -> a | a has Default
|
||||
/// the unspecialized lambda set for the arrow "{} -> a" would be `a:default:1`.
|
||||
///
|
||||
/// Lambda sets in member signatures are never known until those members are specialized at a
|
||||
/// usage site. Unspecialized lambda sets aid us in recovering those lambda sets; when we
|
||||
/// instantiate `a` with a proper type `T`, we'll know to resolve the lambda set by extracting
|
||||
/// it at region "1" from the specialization of "default" for `T`.
|
||||
#[derive(PartialEq, Eq, Clone, Copy)]
|
||||
pub struct Uls(Variable, Symbol, u8);
|
||||
|
||||
static mut TYPE_CLONE_COUNT: std::sync::atomic::AtomicUsize =
|
||||
std::sync::atomic::AtomicUsize::new(0);
|
||||
|
||||
|
@ -297,6 +309,7 @@ impl Clone for Type {
|
|||
name: *name,
|
||||
captures: captures.clone(),
|
||||
},
|
||||
Self::UnspecializedLambdaSet(uls) => Self::UnspecializedLambdaSet(*uls),
|
||||
Self::DelayedAlias(arg0) => Self::DelayedAlias(arg0.clone()),
|
||||
Self::Alias {
|
||||
symbol,
|
||||
|
@ -367,6 +380,14 @@ impl TypeExtension {
|
|||
TypeExtension::Closed => true,
|
||||
}
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn iter_mut(&mut self) -> impl Iterator<Item = &mut Type> {
|
||||
match self {
|
||||
TypeExtension::Open(ext) => Some(ext.as_mut()).into_iter(),
|
||||
TypeExtension::Closed => None.into_iter(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> IntoIterator for &'a TypeExtension {
|
||||
|
@ -627,6 +648,9 @@ impl fmt::Debug for Type {
|
|||
Type::RangedNumber(typ, range_vars) => {
|
||||
write!(f, "Ranged({:?}, {:?})", typ, range_vars)
|
||||
}
|
||||
Type::UnspecializedLambdaSet(Uls(a, mem, region)) => {
|
||||
write!(f, "ULS({:?}:{:?}:{:?})", a, mem, region)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -766,6 +790,12 @@ impl Type {
|
|||
RangedNumber(typ, _) => {
|
||||
stack.push(typ);
|
||||
}
|
||||
UnspecializedLambdaSet(Uls(v, _, _)) => {
|
||||
debug_assert!(
|
||||
substitutions.get(v).is_none(),
|
||||
"unspecialized lambda sets should never be substituted before solving"
|
||||
);
|
||||
}
|
||||
|
||||
EmptyRec | EmptyTagUnion | Erroneous(_) => {}
|
||||
}
|
||||
|
@ -877,6 +907,12 @@ impl Type {
|
|||
RangedNumber(typ, _) => {
|
||||
stack.push(typ);
|
||||
}
|
||||
UnspecializedLambdaSet(Uls(v, _, _)) => {
|
||||
debug_assert!(
|
||||
substitutions.get(v).is_none(),
|
||||
"unspecialized lambda sets should never be substituted before solving"
|
||||
);
|
||||
}
|
||||
|
||||
EmptyRec | EmptyTagUnion | Erroneous(_) => {}
|
||||
}
|
||||
|
@ -974,6 +1010,7 @@ impl Type {
|
|||
Ok(())
|
||||
}
|
||||
RangedNumber(typ, _) => typ.substitute_alias(rep_symbol, rep_args, actual),
|
||||
UnspecializedLambdaSet(..) => Ok(()),
|
||||
EmptyRec | EmptyTagUnion | ClosureTag { .. } | Erroneous(_) | Variable(_) => Ok(()),
|
||||
}
|
||||
}
|
||||
|
@ -1030,6 +1067,7 @@ impl Type {
|
|||
Apply(symbol, _, _) if *symbol == rep_symbol => true,
|
||||
Apply(_, args, _) => args.iter().any(|arg| arg.contains_symbol(rep_symbol)),
|
||||
RangedNumber(typ, _) => typ.contains_symbol(rep_symbol),
|
||||
UnspecializedLambdaSet(Uls(_, sym, _)) => *sym == rep_symbol,
|
||||
EmptyRec | EmptyTagUnion | ClosureTag { .. } | Erroneous(_) | Variable(_) => false,
|
||||
}
|
||||
}
|
||||
|
@ -1055,6 +1093,7 @@ impl Type {
|
|||
ClosureTag { name: _, captures } => {
|
||||
captures.iter().any(|t| t.contains_variable(rep_variable))
|
||||
}
|
||||
UnspecializedLambdaSet(Uls(v, _, _)) => *v == rep_variable,
|
||||
RecursiveTagUnion(_, tags, ext) | TagUnion(tags, ext) => {
|
||||
Self::contains_variable_ext(ext, rep_variable)
|
||||
|| tags
|
||||
|
@ -1344,10 +1383,19 @@ impl Type {
|
|||
RangedNumber(typ, _) => {
|
||||
typ.instantiate_aliases(region, aliases, var_store, new_lambda_set_variables);
|
||||
}
|
||||
UnspecializedLambdaSet(..) => {}
|
||||
EmptyRec | EmptyTagUnion | ClosureTag { .. } | Erroneous(_) | Variable(_) => {}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn instantiate_lambda_sets_as_unspecialized(
|
||||
&mut self,
|
||||
able_var: Variable,
|
||||
ability_member: Symbol,
|
||||
) {
|
||||
instantiate_lambda_sets_as_unspecialized(self, able_var, ability_member)
|
||||
}
|
||||
|
||||
pub fn is_tag_union_like(&self) -> bool {
|
||||
matches!(
|
||||
self,
|
||||
|
@ -1473,6 +1521,9 @@ fn symbols_help(initial: &Type) -> Vec<Symbol> {
|
|||
RangedNumber(typ, _) => {
|
||||
stack.push(typ);
|
||||
}
|
||||
UnspecializedLambdaSet(Uls(_, _sym, _)) => {
|
||||
// ignore the member symbol because unspecialized lambda sets are internal-only
|
||||
}
|
||||
EmptyRec | EmptyTagUnion | ClosureTag { .. } | Erroneous(_) | Variable(_) => {}
|
||||
}
|
||||
}
|
||||
|
@ -1520,6 +1571,9 @@ fn variables_help(tipe: &Type, accum: &mut ImSet<Variable>) {
|
|||
variables_help(t, accum);
|
||||
}
|
||||
}
|
||||
UnspecializedLambdaSet(Uls(v, _, _)) => {
|
||||
accum.insert(*v);
|
||||
}
|
||||
TagUnion(tags, ext) => {
|
||||
for (_, args) in tags {
|
||||
for x in args {
|
||||
|
@ -1670,6 +1724,9 @@ fn variables_help_detailed(tipe: &Type, accum: &mut VariableDetail) {
|
|||
variables_help_detailed(ext, accum);
|
||||
}
|
||||
}
|
||||
UnspecializedLambdaSet(Uls(var, _, _)) => {
|
||||
accum.type_variables.insert(*var);
|
||||
}
|
||||
RecursiveTagUnion(rec, tags, ext) => {
|
||||
for (_, args) in tags {
|
||||
for x in args {
|
||||
|
@ -2665,3 +2722,170 @@ pub fn gather_tags(subs: &Subs, other_fields: UnionTags, var: Variable) -> TagUn
|
|||
ext,
|
||||
}
|
||||
}
|
||||
|
||||
fn instantiate_lambda_sets_as_unspecialized(
|
||||
typ: &mut Type,
|
||||
able_var: Variable,
|
||||
ability_member: Symbol,
|
||||
) {
|
||||
// We want to pop and assign lambda sets pre-order for readability, so types
|
||||
// should be pushed onto the stack in post-order
|
||||
let mut stack = vec![typ];
|
||||
let mut region = 0;
|
||||
|
||||
let mut new_uls = || {
|
||||
region += 1;
|
||||
Type::UnspecializedLambdaSet(Uls(able_var, ability_member, region))
|
||||
};
|
||||
|
||||
while let Some(typ) = stack.pop() {
|
||||
match typ {
|
||||
Type::EmptyRec => {}
|
||||
Type::EmptyTagUnion => {}
|
||||
Type::Function(args, lambda_set, ret) => {
|
||||
debug_assert!(
|
||||
matches!(**lambda_set, Type::Variable(..)),
|
||||
"lambda set already bound"
|
||||
);
|
||||
|
||||
**lambda_set = new_uls();
|
||||
stack.push(ret);
|
||||
stack.extend(args.iter_mut().rev());
|
||||
}
|
||||
Type::Record(fields, ext) => {
|
||||
stack.extend(ext.iter_mut());
|
||||
for (_, x) in fields.iter_mut() {
|
||||
stack.push(x.as_inner_mut());
|
||||
}
|
||||
}
|
||||
Type::TagUnion(tags, ext) | Type::RecursiveTagUnion(_, tags, ext) => {
|
||||
stack.extend(ext.iter_mut());
|
||||
for (_, ts) in tags {
|
||||
for t in ts.iter_mut().rev() {
|
||||
stack.push(t);
|
||||
}
|
||||
}
|
||||
}
|
||||
Type::FunctionOrTagUnion(_, _, ext) => {
|
||||
stack.extend(ext.iter_mut());
|
||||
}
|
||||
Type::ClosureTag { name: _, captures } => {
|
||||
stack.extend(captures.iter_mut().rev());
|
||||
}
|
||||
Type::UnspecializedLambdaSet(..) => {
|
||||
internal_error!("attempting to re-instantiate ULS")
|
||||
}
|
||||
Type::DelayedAlias(AliasCommon {
|
||||
symbol: _,
|
||||
type_arguments,
|
||||
lambda_set_variables,
|
||||
}) => {
|
||||
stack.extend(lambda_set_variables.iter_mut().rev().map(|ls| &mut ls.0));
|
||||
stack.extend(type_arguments.iter_mut().rev());
|
||||
}
|
||||
Type::Alias {
|
||||
symbol: _,
|
||||
type_arguments,
|
||||
lambda_set_variables,
|
||||
actual,
|
||||
kind: _,
|
||||
} => {
|
||||
stack.push(actual);
|
||||
stack.extend(lambda_set_variables.iter_mut().rev().map(|ls| &mut ls.0));
|
||||
stack.extend(type_arguments.iter_mut().rev().map(|t| &mut t.typ));
|
||||
}
|
||||
Type::HostExposedAlias {
|
||||
name: _,
|
||||
type_arguments,
|
||||
lambda_set_variables,
|
||||
actual_var: _,
|
||||
actual,
|
||||
} => {
|
||||
stack.push(actual);
|
||||
stack.extend(lambda_set_variables.iter_mut().rev().map(|ls| &mut ls.0));
|
||||
stack.extend(type_arguments.iter_mut().rev());
|
||||
}
|
||||
Type::Apply(_sym, args, _region) => {
|
||||
stack.extend(args.iter_mut().rev());
|
||||
}
|
||||
Type::Variable(_) => {}
|
||||
Type::RangedNumber(t, _) => {
|
||||
stack.push(t);
|
||||
}
|
||||
Type::Erroneous(_) => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn instantiate_lambda_sets_as_unspecialized() {
|
||||
let mut var_store = VarStore::default();
|
||||
let l1 = Box::new(Type::Variable(var_store.fresh()));
|
||||
let l2 = Box::new(Type::Variable(var_store.fresh()));
|
||||
let l3 = Box::new(Type::Variable(var_store.fresh()));
|
||||
let mut typ = Type::Function(
|
||||
vec![Type::Function(vec![], l2, Box::new(Type::EmptyRec))],
|
||||
l1,
|
||||
Box::new(Type::TagUnion(
|
||||
vec![(
|
||||
TagName("A".into()),
|
||||
vec![Type::Function(vec![], l3, Box::new(Type::EmptyRec))],
|
||||
)],
|
||||
TypeExtension::Closed,
|
||||
)),
|
||||
);
|
||||
|
||||
let able_var = var_store.fresh();
|
||||
let member = Symbol::UNDERSCORE;
|
||||
typ.instantiate_lambda_sets_as_unspecialized(able_var, member);
|
||||
|
||||
macro_rules! check_uls {
|
||||
($typ:expr, $region:literal) => {{
|
||||
match $typ {
|
||||
Type::UnspecializedLambdaSet(Uls(var1, member1, $region)) => {
|
||||
assert!(var1 == able_var && member1 == member)
|
||||
}
|
||||
_ => panic!(),
|
||||
}
|
||||
}};
|
||||
}
|
||||
|
||||
match typ {
|
||||
Type::Function(args, l1, ret) => {
|
||||
check_uls!(*l1, 1);
|
||||
|
||||
match args.as_slice() {
|
||||
[Type::Function(args, l2, ret)] => {
|
||||
check_uls!(**l2, 2);
|
||||
assert!(args.is_empty());
|
||||
assert!(matches!(**ret, Type::EmptyRec));
|
||||
}
|
||||
_ => panic!(),
|
||||
}
|
||||
|
||||
match *ret {
|
||||
Type::TagUnion(tags, TypeExtension::Closed) => match tags.as_slice() {
|
||||
[(name, args)] => {
|
||||
assert_eq!(name.0.as_str(), "A");
|
||||
match args.as_slice() {
|
||||
[Type::Function(args, l3, ret)] => {
|
||||
check_uls!(**l3, 3);
|
||||
assert!(args.is_empty());
|
||||
assert!(matches!(**ret, Type::EmptyRec));
|
||||
}
|
||||
_ => panic!(),
|
||||
}
|
||||
}
|
||||
_ => panic!(),
|
||||
},
|
||||
_ => panic!(),
|
||||
}
|
||||
}
|
||||
_ => panic!(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -99,13 +99,6 @@ bitflags! {
|
|||
///
|
||||
/// For example, t1 += [A Str] says we should "add" the tag "A Str" to the type of "t1".
|
||||
const PRESENT = 1 << 1;
|
||||
/// Instructs the unifier to treat rigids exactly like flex vars.
|
||||
/// Usually rigids can only unify with flex vars, because rigids are named and bound
|
||||
/// explicitly.
|
||||
/// However, when checking type ranges, as we do for `RangedNumber` types, we must loosen
|
||||
/// this restriction because otherwise an admissible range will appear inadmissible.
|
||||
/// For example, Int * is in the range <I8, U8, ...>.
|
||||
const RIGID_AS_FLEX = 1 << 2;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -126,11 +119,7 @@ impl Mode {
|
|||
|
||||
#[cfg(debug_assertions)]
|
||||
fn pretty_print(&self) -> &str {
|
||||
if self.contains(Mode::EQ | Mode::RIGID_AS_FLEX) {
|
||||
"~*"
|
||||
} else if self.contains(Mode::PRESENT | Mode::RIGID_AS_FLEX) {
|
||||
"+=*"
|
||||
} else if self.contains(Mode::EQ) {
|
||||
if self.contains(Mode::EQ) {
|
||||
"~"
|
||||
} else if self.contains(Mode::PRESENT) {
|
||||
"+="
|
||||
|
@ -364,14 +353,10 @@ fn unify_context(subs: &mut Subs, pool: &mut Pool, ctx: Context) -> Outcome {
|
|||
// This #[allow] is needed in release builds, where `result` is no longer used.
|
||||
#[allow(clippy::let_and_return)]
|
||||
let result = match &ctx.first_desc.content {
|
||||
FlexVar(opt_name) => unify_flex(subs, &ctx, opt_name, None, &ctx.second_desc.content),
|
||||
FlexAbleVar(opt_name, ability) => unify_flex(
|
||||
subs,
|
||||
&ctx,
|
||||
opt_name,
|
||||
Some(*ability),
|
||||
&ctx.second_desc.content,
|
||||
),
|
||||
FlexVar(opt_name) => unify_flex(subs, &ctx, opt_name, &ctx.second_desc.content),
|
||||
FlexAbleVar(opt_name, ability) => {
|
||||
unify_flex_able(subs, &ctx, opt_name, *ability, &ctx.second_desc.content)
|
||||
}
|
||||
RecursionVar {
|
||||
opt_name,
|
||||
structure,
|
||||
|
@ -383,9 +368,9 @@ fn unify_context(subs: &mut Subs, pool: &mut Pool, ctx: Context) -> Outcome {
|
|||
*structure,
|
||||
&ctx.second_desc.content,
|
||||
),
|
||||
RigidVar(name) => unify_rigid(subs, &ctx, name, None, &ctx.second_desc.content),
|
||||
RigidVar(name) => unify_rigid(subs, &ctx, name, &ctx.second_desc.content),
|
||||
RigidAbleVar(name, ability) => {
|
||||
unify_rigid(subs, &ctx, name, Some(*ability), &ctx.second_desc.content)
|
||||
unify_rigid_able(subs, &ctx, name, *ability, &ctx.second_desc.content)
|
||||
}
|
||||
Structure(flat_type) => {
|
||||
unify_structure(subs, pool, &ctx, flat_type, &ctx.second_desc.content)
|
||||
|
@ -1916,7 +1901,6 @@ fn unify_rigid(
|
|||
subs: &mut Subs,
|
||||
ctx: &Context,
|
||||
name: &SubsIndex<Lowercase>,
|
||||
opt_able_bound: Option<Symbol>,
|
||||
other: &Content,
|
||||
) -> Outcome {
|
||||
match other {
|
||||
|
@ -1925,8 +1909,47 @@ fn unify_rigid(
|
|||
merge(subs, ctx, RigidVar(*name))
|
||||
}
|
||||
FlexAbleVar(_, other_ability) => {
|
||||
match opt_able_bound {
|
||||
Some(ability) => {
|
||||
// Mismatch - Rigid can unify with FlexAble only when the Rigid has an ability
|
||||
// bound as well, otherwise the user failed to correctly annotate the bound.
|
||||
mismatch!(
|
||||
%not_able, ctx.first, *other_ability,
|
||||
"Rigid {:?} with FlexAble {:?}", ctx.first, other
|
||||
)
|
||||
}
|
||||
|
||||
RigidVar(_)
|
||||
| RigidAbleVar(..)
|
||||
| RecursionVar { .. }
|
||||
| Structure(_)
|
||||
| Alias(..)
|
||||
| RangedNumber(..)
|
||||
| LambdaSet(..) => {
|
||||
// Type mismatch! Rigid can only unify with flex, even if the
|
||||
// rigid names are the same.
|
||||
mismatch!("Rigid {:?} with {:?}", ctx.first, &other)
|
||||
}
|
||||
|
||||
Error => {
|
||||
// Error propagates.
|
||||
merge(subs, ctx, Error)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn unify_rigid_able(
|
||||
subs: &mut Subs,
|
||||
ctx: &Context,
|
||||
name: &SubsIndex<Lowercase>,
|
||||
ability: Symbol,
|
||||
other: &Content,
|
||||
) -> Outcome {
|
||||
match other {
|
||||
FlexVar(_) => {
|
||||
// If the other is flex, rigid wins!
|
||||
merge(subs, ctx, RigidVar(*name))
|
||||
}
|
||||
FlexAbleVar(_, other_ability) => {
|
||||
if ability == *other_ability {
|
||||
// The ability bounds are the same, so rigid wins!
|
||||
merge(subs, ctx, RigidAbleVar(*name, ability))
|
||||
|
@ -1942,53 +1965,6 @@ fn unify_rigid(
|
|||
)
|
||||
}
|
||||
}
|
||||
None => {
|
||||
// Mismatch - Rigid can unify with FlexAble only when the Rigid has an ability
|
||||
// bound as well, otherwise the user failed to correctly annotate the bound.
|
||||
mismatch!(
|
||||
%not_able, ctx.first, *other_ability,
|
||||
"Rigid {:?} with FlexAble {:?}", ctx.first, other
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
RigidVar(_)
|
||||
| RecursionVar { .. }
|
||||
| Structure(_)
|
||||
| Alias(_, _, _, _)
|
||||
| RangedNumber(..)
|
||||
| LambdaSet(..)
|
||||
if ctx.mode.contains(Mode::RIGID_AS_FLEX) =>
|
||||
{
|
||||
// Usually rigids can only unify with flex, but the mode indicates we are treating
|
||||
// rigid vars as flex, so admit this.
|
||||
match (opt_able_bound, other) {
|
||||
(None, other) => merge(subs, ctx, *other),
|
||||
(Some(ability), Alias(opaque_name, vars, _real_var, AliasKind::Opaque))
|
||||
if vars.is_empty() =>
|
||||
{
|
||||
let mut output = merge(subs, ctx, *other);
|
||||
let must_implement_ability = MustImplementAbility {
|
||||
typ: Obligated::Opaque(*opaque_name),
|
||||
ability,
|
||||
};
|
||||
output.must_implement_ability.push(must_implement_ability);
|
||||
output
|
||||
}
|
||||
|
||||
// these have underscores because they're unused in --release builds
|
||||
(Some(_ability), _other) => {
|
||||
// For now, only allow opaque types with no type variables to implement abilities.
|
||||
mismatch!(
|
||||
%not_able, ctx.second, _ability,
|
||||
"RigidAble {:?} with non-opaque or opaque with type variables {:?}",
|
||||
ctx.first,
|
||||
&_other
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
RigidVar(_)
|
||||
| RigidAbleVar(..)
|
||||
|
@ -2014,27 +1990,57 @@ fn unify_flex(
|
|||
subs: &mut Subs,
|
||||
ctx: &Context,
|
||||
opt_name: &Option<SubsIndex<Lowercase>>,
|
||||
opt_able_bound: Option<Symbol>,
|
||||
other: &Content,
|
||||
) -> Outcome {
|
||||
match other {
|
||||
FlexVar(other_opt_name) => {
|
||||
// Prefer using right's name.
|
||||
let opt_name = opt_name.or(*other_opt_name);
|
||||
match opt_able_bound {
|
||||
Some(ability) => merge(subs, ctx, FlexAbleVar(opt_name, ability)),
|
||||
None => merge(subs, ctx, FlexVar(opt_name)),
|
||||
merge(subs, ctx, FlexVar(opt_name))
|
||||
}
|
||||
|
||||
FlexAbleVar(opt_other_name, ability) => {
|
||||
// Prefer using right's name.
|
||||
let opt_name = (opt_other_name).or(*opt_name);
|
||||
merge(subs, ctx, FlexAbleVar(opt_name, *ability))
|
||||
}
|
||||
|
||||
RigidVar(_)
|
||||
| RigidAbleVar(_, _)
|
||||
| RecursionVar { .. }
|
||||
| Structure(_)
|
||||
| Alias(_, _, _, _)
|
||||
| RangedNumber(..)
|
||||
| LambdaSet(..) => {
|
||||
// TODO special-case boolean here
|
||||
// In all other cases, if left is flex, defer to right.
|
||||
merge(subs, ctx, *other)
|
||||
}
|
||||
|
||||
Error => merge(subs, ctx, Error),
|
||||
}
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn unify_flex_able(
|
||||
subs: &mut Subs,
|
||||
ctx: &Context,
|
||||
opt_name: &Option<SubsIndex<Lowercase>>,
|
||||
ability: Symbol,
|
||||
other: &Content,
|
||||
) -> Outcome {
|
||||
match other {
|
||||
FlexVar(opt_other_name) => {
|
||||
// Prefer using right's name.
|
||||
let opt_name = (opt_other_name).or(*opt_name);
|
||||
merge(subs, ctx, FlexAbleVar(opt_name, ability))
|
||||
}
|
||||
|
||||
FlexAbleVar(opt_other_name, other_ability) => {
|
||||
// Prefer the right's name when possible.
|
||||
let opt_name = (opt_other_name).or(*opt_name);
|
||||
|
||||
match opt_able_bound {
|
||||
Some(ability) => {
|
||||
if ability == *other_ability {
|
||||
// The ability bounds are the same! Keep the name around if it exists.
|
||||
merge(subs, ctx, FlexAbleVar(opt_name, ability))
|
||||
} else {
|
||||
// Ability names differ; mismatch for now.
|
||||
|
@ -2048,23 +2054,41 @@ fn unify_flex(
|
|||
)
|
||||
}
|
||||
}
|
||||
None => {
|
||||
// Right has an ability bound, but left might have the name. Combine them.
|
||||
merge(subs, ctx, FlexAbleVar(opt_name, *other_ability))
|
||||
}
|
||||
|
||||
RigidAbleVar(_, other_ability) => {
|
||||
if ability == *other_ability {
|
||||
merge(subs, ctx, *other)
|
||||
} else {
|
||||
mismatch!(%not_able, ctx.second, ability, "RigidAble {:?} vs {:?}", ability, other_ability)
|
||||
}
|
||||
}
|
||||
|
||||
RigidVar(_)
|
||||
| RigidAbleVar(_, _)
|
||||
| RecursionVar { .. }
|
||||
| Structure(_)
|
||||
| Alias(_, _, _, _)
|
||||
| RangedNumber(..)
|
||||
| LambdaSet(..) => {
|
||||
// TODO special-case boolean here
|
||||
// In all other cases, if left is flex, defer to right.
|
||||
merge(subs, ctx, *other)
|
||||
RigidVar(_) => mismatch!("FlexAble can never unify with non-able Rigid"),
|
||||
RecursionVar { .. } => mismatch!("FlexAble with RecursionVar"),
|
||||
LambdaSet(..) => mismatch!("FlexAble with LambdaSet"),
|
||||
|
||||
Alias(name, args, _real_var, AliasKind::Opaque) => {
|
||||
if args.is_empty() {
|
||||
// Opaque type wins
|
||||
let mut outcome = merge(subs, ctx, *other);
|
||||
outcome.must_implement_ability.push(MustImplementAbility {
|
||||
typ: Obligated::Opaque(*name),
|
||||
ability,
|
||||
});
|
||||
outcome
|
||||
} else {
|
||||
mismatch!("FlexAble vs Opaque with type vars")
|
||||
}
|
||||
}
|
||||
|
||||
Structure(_) | Alias(_, _, _, AliasKind::Structural) | RangedNumber(..) => {
|
||||
// Structural type wins.
|
||||
let mut outcome = merge(subs, ctx, *other);
|
||||
outcome.must_implement_ability.push(MustImplementAbility {
|
||||
typ: Obligated::Adhoc(ctx.second),
|
||||
ability,
|
||||
});
|
||||
outcome
|
||||
}
|
||||
|
||||
Error => merge(subs, ctx, Error),
|
||||
|
|
|
@ -6,4 +6,4 @@ struct RocStr {
|
|||
size_t capacity;
|
||||
};
|
||||
|
||||
extern struct RocStr roc__mainForHost_1_exposed();
|
||||
extern void roc__mainForHost_1_exposed_generic(const struct RocStr *data);
|
||||
|
|
|
@ -21,39 +21,42 @@ func rocRealloc(ptr: UInt, _oldSize: Int, newSize: Int, _alignment: UInt) -> UIn
|
|||
return UInt(bitPattern: ptr)
|
||||
}
|
||||
|
||||
extension RocStr {
|
||||
var isSmallString: Bool {
|
||||
capacity < 0
|
||||
func isSmallString(rocStr: RocStr) -> Bool {
|
||||
return rocStr.capacity < 0
|
||||
}
|
||||
|
||||
var length: Int {
|
||||
if isSmallString {
|
||||
func getStrLen(rocStr: RocStr) -> Int {
|
||||
if isSmallString(rocStr: rocStr) {
|
||||
// Small String length is last in the byte of capacity.
|
||||
var cap = capacity
|
||||
var cap = rocStr.capacity
|
||||
let count = MemoryLayout.size(ofValue: cap)
|
||||
let bytes = Data(bytes: &cap, count: count)
|
||||
let lastByte = bytes[count - 1]
|
||||
return Int(lastByte ^ 0b1000_0000)
|
||||
} else {
|
||||
return len
|
||||
return rocStr.len
|
||||
}
|
||||
}
|
||||
|
||||
var string: String {
|
||||
if isSmallString {
|
||||
let data: Data = withUnsafePointer(to: self) { ptr in
|
||||
func getSwiftString(rocStr: RocStr) -> String {
|
||||
let length = getStrLen(rocStr: rocStr)
|
||||
|
||||
if isSmallString(rocStr: rocStr) {
|
||||
let data: Data = withUnsafePointer(to: rocStr) { ptr in
|
||||
Data(bytes: ptr, count: length)
|
||||
}
|
||||
return String(data: data, encoding: .utf8)!
|
||||
} else {
|
||||
let data = Data(bytes: bytes, count: len)
|
||||
let data = Data(bytes: rocStr.bytes, count: length)
|
||||
return String(data: data, encoding: .utf8)!
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@_cdecl("main")
|
||||
func main() -> UInt8 {
|
||||
print(roc__mainForHost_1_exposed().string, terminator: "")
|
||||
var rocStr = RocStr()
|
||||
roc__mainForHost_1_exposed_generic(&rocStr)
|
||||
|
||||
print(getSwiftString(rocStr: rocStr), terminator: "")
|
||||
return 0
|
||||
}
|
||||
|
|
|
@ -98,7 +98,8 @@
|
|||
debugir
|
||||
rust
|
||||
]);
|
||||
in {
|
||||
in
|
||||
{
|
||||
|
||||
devShell = pkgs.mkShell {
|
||||
buildInputs = sharedInputs ++ darwinInputs ++ linuxInputs;
|
||||
|
@ -111,6 +112,7 @@
|
|||
NIXPKGS_ALLOW_UNFREE = 1; # to run the editor with NVIDIA's closed source drivers
|
||||
};
|
||||
|
||||
formatter = pkgs.nixpkgs-fmt;
|
||||
}
|
||||
);
|
||||
}
|
||||
|
|
13
vendor/ena/Cargo.toml
vendored
13
vendor/ena/Cargo.toml
vendored
|
@ -1,13 +0,0 @@
|
|||
[package]
|
||||
name = "ven_ena"
|
||||
description = "Union-find, congruence closure, and other unification code. Based on code from rustc."
|
||||
license = "MIT/Apache-2.0"
|
||||
homepage = "https://github.com/rust-lang-nursery/ena"
|
||||
repository = "https://github.com/rust-lang-nursery/ena"
|
||||
version = "0.13.1"
|
||||
authors = ["Niko Matsakis <niko@alum.mit.edu>"]
|
||||
readme = "README.md"
|
||||
keywords = ["unification", "union-find"]
|
||||
|
||||
[dependencies]
|
||||
log = "0.4.14"
|
201
vendor/ena/LICENSE-APACHE
vendored
201
vendor/ena/LICENSE-APACHE
vendored
|
@ -1,201 +0,0 @@
|
|||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. We also recommend that a
|
||||
file or class name and description of purpose be included on the
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright [yyyy] [name of copyright owner]
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
25
vendor/ena/LICENSE-MIT
vendored
25
vendor/ena/LICENSE-MIT
vendored
|
@ -1,25 +0,0 @@
|
|||
Copyright (c) 2010 The Rust Project Developers
|
||||
|
||||
Permission is hereby granted, free of charge, to any
|
||||
person obtaining a copy of this software and associated
|
||||
documentation files (the "Software"), to deal in the
|
||||
Software without restriction, including without
|
||||
limitation the rights to use, copy, modify, merge,
|
||||
publish, distribute, sublicense, and/or sell copies of
|
||||
the Software, and to permit persons to whom the Software
|
||||
is furnished to do so, subject to the following
|
||||
conditions:
|
||||
|
||||
The above copyright notice and this permission notice
|
||||
shall be included in all copies or substantial portions
|
||||
of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
|
||||
ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
|
||||
TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
|
||||
PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT
|
||||
SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
||||
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR
|
||||
IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
DEALINGS IN THE SOFTWARE.
|
301
vendor/ena/src/bitvec.rs
vendored
301
vendor/ena/src/bitvec.rs
vendored
|
@ -1,301 +0,0 @@
|
|||
// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
|
||||
// file at the top-level directory of this distribution and at
|
||||
// http://rust-lang.org/COPYRIGHT.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
/// A very simple BitVector type.
|
||||
pub struct BitVector {
|
||||
data: Vec<u64>,
|
||||
}
|
||||
|
||||
impl BitVector {
|
||||
pub fn new(num_bits: usize) -> BitVector {
|
||||
let num_words = u64s(num_bits);
|
||||
BitVector { data: vec![0; num_words] }
|
||||
}
|
||||
|
||||
pub fn contains(&self, bit: usize) -> bool {
|
||||
let (word, mask) = word_mask(bit);
|
||||
(self.data[word] & mask) != 0
|
||||
}
|
||||
|
||||
/// Returns true if the bit has changed.
|
||||
pub fn insert(&mut self, bit: usize) -> bool {
|
||||
let (word, mask) = word_mask(bit);
|
||||
let data = &mut self.data[word];
|
||||
let value = *data;
|
||||
let new_value = value | mask;
|
||||
*data = new_value;
|
||||
new_value != value
|
||||
}
|
||||
|
||||
pub fn insert_all(&mut self, all: &BitVector) -> bool {
|
||||
assert!(self.data.len() == all.data.len());
|
||||
let mut changed = false;
|
||||
for (i, j) in self.data.iter_mut().zip(&all.data) {
|
||||
let value = *i;
|
||||
*i = value | *j;
|
||||
if value != *i {
|
||||
changed = true;
|
||||
}
|
||||
}
|
||||
changed
|
||||
}
|
||||
|
||||
pub fn grow(&mut self, num_bits: usize) {
|
||||
let num_words = u64s(num_bits);
|
||||
let extra_words = self.data.len() - num_words;
|
||||
self.data.extend((0..extra_words).map(|_| 0));
|
||||
}
|
||||
|
||||
/// Iterates over indexes of set bits in a sorted order
|
||||
pub fn iter<'a>(&'a self) -> BitVectorIter<'a> {
|
||||
BitVectorIter {
|
||||
iter: self.data.iter(),
|
||||
current: 0,
|
||||
idx: 0,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct BitVectorIter<'a> {
|
||||
iter: ::std::slice::Iter<'a, u64>,
|
||||
current: u64,
|
||||
idx: usize,
|
||||
}
|
||||
|
||||
impl<'a> Iterator for BitVectorIter<'a> {
|
||||
type Item = usize;
|
||||
fn next(&mut self) -> Option<usize> {
|
||||
while self.current == 0 {
|
||||
self.current = if let Some(&i) = self.iter.next() {
|
||||
if i == 0 {
|
||||
self.idx += 64;
|
||||
continue;
|
||||
} else {
|
||||
self.idx = u64s(self.idx) * 64;
|
||||
i
|
||||
}
|
||||
} else {
|
||||
return None;
|
||||
}
|
||||
}
|
||||
let offset = self.current.trailing_zeros() as usize;
|
||||
self.current >>= offset;
|
||||
self.current >>= 1; // shift otherwise overflows for 0b1000_0000_…_0000
|
||||
self.idx += offset + 1;
|
||||
return Some(self.idx - 1);
|
||||
}
|
||||
}
|
||||
|
||||
/// A "bit matrix" is basically a square matrix of booleans
|
||||
/// represented as one gigantic bitvector. In other words, it is as if
|
||||
/// you have N bitvectors, each of length N. Note that `elements` here is `N`/
|
||||
#[derive(Clone)]
|
||||
pub struct BitMatrix {
|
||||
elements: usize,
|
||||
vector: Vec<u64>,
|
||||
}
|
||||
|
||||
impl BitMatrix {
|
||||
// Create a new `elements x elements` matrix, initially empty.
|
||||
pub fn new(elements: usize) -> BitMatrix {
|
||||
// For every element, we need one bit for every other
|
||||
// element. Round up to an even number of u64s.
|
||||
let u64s_per_elem = u64s(elements);
|
||||
BitMatrix {
|
||||
elements: elements,
|
||||
vector: vec![0; elements * u64s_per_elem],
|
||||
}
|
||||
}
|
||||
|
||||
/// The range of bits for a given element.
|
||||
fn range(&self, element: usize) -> (usize, usize) {
|
||||
let u64s_per_elem = u64s(self.elements);
|
||||
let start = element * u64s_per_elem;
|
||||
(start, start + u64s_per_elem)
|
||||
}
|
||||
|
||||
pub fn add(&mut self, source: usize, target: usize) -> bool {
|
||||
let (start, _) = self.range(source);
|
||||
let (word, mask) = word_mask(target);
|
||||
let mut vector = &mut self.vector[..];
|
||||
let v1 = vector[start + word];
|
||||
let v2 = v1 | mask;
|
||||
vector[start + word] = v2;
|
||||
v1 != v2
|
||||
}
|
||||
|
||||
/// Do the bits from `source` contain `target`?
|
||||
///
|
||||
/// Put another way, if the matrix represents (transitive)
|
||||
/// reachability, can `source` reach `target`?
|
||||
pub fn contains(&self, source: usize, target: usize) -> bool {
|
||||
let (start, _) = self.range(source);
|
||||
let (word, mask) = word_mask(target);
|
||||
(self.vector[start + word] & mask) != 0
|
||||
}
|
||||
|
||||
/// Returns those indices that are reachable from both `a` and
|
||||
/// `b`. This is an O(n) operation where `n` is the number of
|
||||
/// elements (somewhat independent from the actual size of the
|
||||
/// intersection, in particular).
|
||||
pub fn intersection(&self, a: usize, b: usize) -> Vec<usize> {
|
||||
let (a_start, a_end) = self.range(a);
|
||||
let (b_start, b_end) = self.range(b);
|
||||
let mut result = Vec::with_capacity(self.elements);
|
||||
for (base, (i, j)) in (a_start..a_end).zip(b_start..b_end).enumerate() {
|
||||
let mut v = self.vector[i] & self.vector[j];
|
||||
for bit in 0..64 {
|
||||
if v == 0 {
|
||||
break;
|
||||
}
|
||||
if v & 0x1 != 0 {
|
||||
result.push(base * 64 + bit);
|
||||
}
|
||||
v >>= 1;
|
||||
}
|
||||
}
|
||||
result
|
||||
}
|
||||
|
||||
/// Add the bits from `read` to the bits from `write`,
|
||||
/// return true if anything changed.
|
||||
///
|
||||
/// This is used when computing transitive reachability because if
|
||||
/// you have an edge `write -> read`, because in that case
|
||||
/// `write` can reach everything that `read` can (and
|
||||
/// potentially more).
|
||||
pub fn merge(&mut self, read: usize, write: usize) -> bool {
|
||||
let (read_start, read_end) = self.range(read);
|
||||
let (write_start, write_end) = self.range(write);
|
||||
let vector = &mut self.vector[..];
|
||||
let mut changed = false;
|
||||
for (read_index, write_index) in (read_start..read_end).zip(write_start..write_end) {
|
||||
let v1 = vector[write_index];
|
||||
let v2 = v1 | vector[read_index];
|
||||
vector[write_index] = v2;
|
||||
changed = changed | (v1 != v2);
|
||||
}
|
||||
changed
|
||||
}
|
||||
}
|
||||
|
||||
fn u64s(elements: usize) -> usize {
|
||||
(elements + 63) / 64
|
||||
}
|
||||
|
||||
fn word_mask(index: usize) -> (usize, u64) {
|
||||
let word = index / 64;
|
||||
let mask = 1 << (index % 64);
|
||||
(word, mask)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn bitvec_iter_works() {
|
||||
let mut bitvec = BitVector::new(100);
|
||||
bitvec.insert(1);
|
||||
bitvec.insert(10);
|
||||
bitvec.insert(19);
|
||||
bitvec.insert(62);
|
||||
bitvec.insert(63);
|
||||
bitvec.insert(64);
|
||||
bitvec.insert(65);
|
||||
bitvec.insert(66);
|
||||
bitvec.insert(99);
|
||||
assert_eq!(bitvec.iter().collect::<Vec<_>>(),
|
||||
[1, 10, 19, 62, 63, 64, 65, 66, 99]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn bitvec_iter_works_2() {
|
||||
let mut bitvec = BitVector::new(300);
|
||||
bitvec.insert(1);
|
||||
bitvec.insert(10);
|
||||
bitvec.insert(19);
|
||||
bitvec.insert(62);
|
||||
bitvec.insert(66);
|
||||
bitvec.insert(99);
|
||||
bitvec.insert(299);
|
||||
assert_eq!(bitvec.iter().collect::<Vec<_>>(),
|
||||
[1, 10, 19, 62, 66, 99, 299]);
|
||||
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn bitvec_iter_works_3() {
|
||||
let mut bitvec = BitVector::new(319);
|
||||
bitvec.insert(0);
|
||||
bitvec.insert(127);
|
||||
bitvec.insert(191);
|
||||
bitvec.insert(255);
|
||||
bitvec.insert(319);
|
||||
assert_eq!(bitvec.iter().collect::<Vec<_>>(), [0, 127, 191, 255, 319]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn union_two_vecs() {
|
||||
let mut vec1 = BitVector::new(65);
|
||||
let mut vec2 = BitVector::new(65);
|
||||
assert!(vec1.insert(3));
|
||||
assert!(!vec1.insert(3));
|
||||
assert!(vec2.insert(5));
|
||||
assert!(vec2.insert(64));
|
||||
assert!(vec1.insert_all(&vec2));
|
||||
assert!(!vec1.insert_all(&vec2));
|
||||
assert!(vec1.contains(3));
|
||||
assert!(!vec1.contains(4));
|
||||
assert!(vec1.contains(5));
|
||||
assert!(!vec1.contains(63));
|
||||
assert!(vec1.contains(64));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn grow() {
|
||||
let mut vec1 = BitVector::new(65);
|
||||
assert!(vec1.insert(3));
|
||||
assert!(!vec1.insert(3));
|
||||
assert!(vec1.insert(5));
|
||||
assert!(vec1.insert(64));
|
||||
vec1.grow(128);
|
||||
assert!(vec1.contains(3));
|
||||
assert!(vec1.contains(5));
|
||||
assert!(vec1.contains(64));
|
||||
assert!(!vec1.contains(126));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn matrix_intersection() {
|
||||
let mut vec1 = BitMatrix::new(200);
|
||||
|
||||
// (*) Elements reachable from both 2 and 65.
|
||||
|
||||
vec1.add(2, 3);
|
||||
vec1.add(2, 6);
|
||||
vec1.add(2, 10); // (*)
|
||||
vec1.add(2, 64); // (*)
|
||||
vec1.add(2, 65);
|
||||
vec1.add(2, 130);
|
||||
vec1.add(2, 160); // (*)
|
||||
|
||||
vec1.add(64, 133);
|
||||
|
||||
vec1.add(65, 2);
|
||||
vec1.add(65, 8);
|
||||
vec1.add(65, 10); // (*)
|
||||
vec1.add(65, 64); // (*)
|
||||
vec1.add(65, 68);
|
||||
vec1.add(65, 133);
|
||||
vec1.add(65, 160); // (*)
|
||||
|
||||
let intersection = vec1.intersection(2, 64);
|
||||
assert!(intersection.is_empty());
|
||||
|
||||
let intersection = vec1.intersection(2, 65);
|
||||
assert_eq!(intersection, &[10, 64, 160]);
|
||||
}
|
18
vendor/ena/src/lib.rs
vendored
18
vendor/ena/src/lib.rs
vendored
|
@ -1,18 +0,0 @@
|
|||
// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
|
||||
// file at the top-level directory of this distribution and at
|
||||
// http://rust-lang.org/COPYRIGHT.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
//! An implementation of union-find. See the `unify` module for more
|
||||
//! details.
|
||||
|
||||
pub mod snapshot_vec;
|
||||
pub mod unify;
|
||||
|
||||
#[macro_use]
|
||||
extern crate log;
|
399
vendor/ena/src/snapshot_vec.rs
vendored
399
vendor/ena/src/snapshot_vec.rs
vendored
|
@ -1,399 +0,0 @@
|
|||
// Copyright 2014 The Rust Project Developers. See the COPYRIGHT
|
||||
// file at the top-level directory of this distribution and at
|
||||
// http://rust-lang.org/COPYRIGHT.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
//! A utility class for implementing "snapshottable" things; a snapshottable data structure permits
|
||||
//! you to take a snapshot (via `start_snapshot`) and then, after making some changes, elect either
|
||||
//! to rollback to the start of the snapshot or commit those changes.
|
||||
//!
|
||||
//! This vector is intended to be used as part of an abstraction, not serve as a complete
|
||||
//! abstraction on its own. As such, while it will roll back most changes on its own, it also
|
||||
//! supports a `get_mut` operation that gives you an arbitrary mutable pointer into the vector. To
|
||||
//! ensure that any changes you make this with this pointer are rolled back, you must invoke
|
||||
//! `record` to record any changes you make and also supplying a delegate capable of reversing
|
||||
//! those changes.
|
||||
|
||||
use self::UndoLog::*;
|
||||
|
||||
use std::fmt;
|
||||
use std::mem;
|
||||
use std::ops;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum UndoLog<D: SnapshotVecDelegate> {
|
||||
/// New variable with given index was created.
|
||||
NewElem(usize),
|
||||
|
||||
/// Variable with given index was changed *from* the given value.
|
||||
SetElem(usize, D::Value),
|
||||
|
||||
/// Extensible set of actions
|
||||
Other(D::Undo),
|
||||
}
|
||||
|
||||
/// A Vec where we have Debug overridden to render the indices like
|
||||
/// a hashmap, since we really care about those when debugging one of these.
|
||||
#[derive(Clone)]
|
||||
struct BackingVec<T>(Vec<T>);
|
||||
|
||||
pub struct SnapshotVec<D: SnapshotVecDelegate> {
|
||||
values: BackingVec<D::Value>,
|
||||
undo_log: Vec<UndoLog<D>>,
|
||||
num_open_snapshots: usize,
|
||||
}
|
||||
|
||||
impl<D> fmt::Debug for SnapshotVec<D>
|
||||
where
|
||||
D: SnapshotVecDelegate,
|
||||
D: fmt::Debug,
|
||||
D::Undo: fmt::Debug,
|
||||
D::Value: fmt::Debug,
|
||||
{
|
||||
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
|
||||
self.values.fmt(fmt)
|
||||
}
|
||||
}
|
||||
|
||||
// Snapshots are tokens that should be created/consumed linearly.
|
||||
pub struct Snapshot {
|
||||
// Number of values at the time the snapshot was taken.
|
||||
pub(crate) value_count: usize,
|
||||
// Length of the undo log at the time the snapshot was taken.
|
||||
undo_len: usize,
|
||||
}
|
||||
|
||||
pub trait SnapshotVecDelegate {
|
||||
type Value;
|
||||
type Undo;
|
||||
|
||||
fn reverse(values: &mut Vec<Self::Value>, action: Self::Undo);
|
||||
}
|
||||
|
||||
// HACK(eddyb) manual impl avoids `Default` bound on `D`.
|
||||
impl<D: SnapshotVecDelegate> Default for SnapshotVec<D> {
|
||||
fn default() -> Self {
|
||||
SnapshotVec {
|
||||
values: BackingVec(Vec::new()),
|
||||
undo_log: Vec::new(),
|
||||
num_open_snapshots: 0,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> fmt::Debug for BackingVec<T>
|
||||
where
|
||||
T: fmt::Debug,
|
||||
{
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(f, "{{{{")?;
|
||||
|
||||
for (index, elem) in self.0.iter().enumerate() {
|
||||
write!(f, "\n {} => {:?},", index, elem)?;
|
||||
}
|
||||
|
||||
if !self.0.is_empty() {
|
||||
writeln!(f)?;
|
||||
}
|
||||
|
||||
write!(f, "}}}}")
|
||||
}
|
||||
}
|
||||
|
||||
impl<D: SnapshotVecDelegate> SnapshotVec<D> {
|
||||
pub fn new() -> Self {
|
||||
Self::default()
|
||||
}
|
||||
|
||||
pub fn with_capacity(c: usize) -> SnapshotVec<D> {
|
||||
SnapshotVec {
|
||||
values: BackingVec(Vec::with_capacity(c)),
|
||||
undo_log: Vec::new(),
|
||||
num_open_snapshots: 0,
|
||||
}
|
||||
}
|
||||
|
||||
fn in_snapshot(&self) -> bool {
|
||||
self.num_open_snapshots > 0
|
||||
}
|
||||
|
||||
pub fn record(&mut self, action: D::Undo) {
|
||||
if self.in_snapshot() {
|
||||
self.undo_log.push(Other(action));
|
||||
}
|
||||
}
|
||||
|
||||
pub fn len(&self) -> usize {
|
||||
self.values.0.len()
|
||||
}
|
||||
|
||||
pub fn is_empty(&self) -> bool {
|
||||
self.values.0.len() == 0
|
||||
}
|
||||
|
||||
pub fn push(&mut self, elem: D::Value) -> usize {
|
||||
let len = self.values.0.len();
|
||||
self.values.0.push(elem);
|
||||
|
||||
if self.in_snapshot() {
|
||||
self.undo_log.push(NewElem(len));
|
||||
}
|
||||
|
||||
len
|
||||
}
|
||||
|
||||
pub fn get(&self, index: usize) -> &D::Value {
|
||||
&self.values.0[index]
|
||||
}
|
||||
|
||||
/// Reserve space for new values, just like an ordinary vec.
|
||||
pub fn reserve(&mut self, additional: usize) {
|
||||
// This is not affected by snapshots or anything.
|
||||
self.values.0.reserve(additional);
|
||||
}
|
||||
|
||||
/// Returns a mutable pointer into the vec; whatever changes you make here cannot be undone
|
||||
/// automatically, so you should be sure call `record()` with some sort of suitable undo
|
||||
/// action.
|
||||
pub fn get_mut(&mut self, index: usize) -> &mut D::Value {
|
||||
&mut self.values.0[index]
|
||||
}
|
||||
|
||||
/// Updates the element at the given index. The old value will saved (and perhaps restored) if
|
||||
/// a snapshot is active.
|
||||
pub fn set(&mut self, index: usize, new_elem: D::Value) {
|
||||
let old_elem = mem::replace(&mut self.values.0[index], new_elem);
|
||||
if self.in_snapshot() {
|
||||
self.undo_log.push(SetElem(index, old_elem));
|
||||
}
|
||||
}
|
||||
|
||||
/// Updates all elements. Potentially more efficient -- but
|
||||
/// otherwise equivalent to -- invoking `set` for each element.
|
||||
pub fn set_all(&mut self, mut new_elems: impl FnMut(usize) -> D::Value) {
|
||||
if !self.in_snapshot() {
|
||||
for (index, slot) in self.values.0.iter_mut().enumerate() {
|
||||
*slot = new_elems(index);
|
||||
}
|
||||
} else {
|
||||
for i in 0..self.values.0.len() {
|
||||
self.set(i, new_elems(i));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn update<OP>(&mut self, index: usize, op: OP)
|
||||
where
|
||||
OP: FnOnce(&mut D::Value),
|
||||
D::Value: Clone,
|
||||
{
|
||||
if self.in_snapshot() {
|
||||
let old_elem = self.values.0[index].clone();
|
||||
self.undo_log.push(SetElem(index, old_elem));
|
||||
}
|
||||
op(&mut self.values.0[index]);
|
||||
}
|
||||
|
||||
pub fn start_snapshot(&mut self) -> Snapshot {
|
||||
self.num_open_snapshots += 1;
|
||||
Snapshot {
|
||||
value_count: self.values.0.len(),
|
||||
undo_len: self.undo_log.len(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn actions_since_snapshot(&self, snapshot: &Snapshot) -> &[UndoLog<D>] {
|
||||
&self.undo_log[snapshot.undo_len..]
|
||||
}
|
||||
|
||||
fn assert_open_snapshot(&self, snapshot: &Snapshot) {
|
||||
// Failures here may indicate a failure to follow a stack discipline.
|
||||
assert!(self.undo_log.len() >= snapshot.undo_len);
|
||||
assert!(self.num_open_snapshots > 0);
|
||||
}
|
||||
|
||||
pub fn rollback_to(&mut self, snapshot: Snapshot) {
|
||||
debug!("rollback_to({})", snapshot.undo_len);
|
||||
|
||||
self.assert_open_snapshot(&snapshot);
|
||||
|
||||
while self.undo_log.len() > snapshot.undo_len {
|
||||
match self.undo_log.pop().unwrap() {
|
||||
NewElem(i) => {
|
||||
self.values.0.pop();
|
||||
assert!(self.values.0.len() == i);
|
||||
}
|
||||
|
||||
SetElem(i, v) => {
|
||||
self.values.0[i] = v;
|
||||
}
|
||||
|
||||
Other(u) => {
|
||||
D::reverse(&mut self.values.0, u);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
self.num_open_snapshots -= 1;
|
||||
}
|
||||
|
||||
/// Commits all changes since the last snapshot. Of course, they
|
||||
/// can still be undone if there is a snapshot further out.
|
||||
pub fn commit(&mut self, snapshot: Snapshot) {
|
||||
debug!("commit({})", snapshot.undo_len);
|
||||
|
||||
self.assert_open_snapshot(&snapshot);
|
||||
|
||||
if self.num_open_snapshots == 1 {
|
||||
// The root snapshot. It's safe to clear the undo log because
|
||||
// there's no snapshot further out that we might need to roll back
|
||||
// to.
|
||||
assert!(snapshot.undo_len == 0);
|
||||
self.undo_log.clear();
|
||||
}
|
||||
|
||||
self.num_open_snapshots -= 1;
|
||||
}
|
||||
}
|
||||
|
||||
impl<D: SnapshotVecDelegate> ops::Deref for SnapshotVec<D> {
|
||||
type Target = [D::Value];
|
||||
fn deref(&self) -> &[D::Value] {
|
||||
&*self.values.0
|
||||
}
|
||||
}
|
||||
|
||||
impl<D: SnapshotVecDelegate> ops::DerefMut for SnapshotVec<D> {
|
||||
fn deref_mut(&mut self) -> &mut [D::Value] {
|
||||
&mut *self.values.0
|
||||
}
|
||||
}
|
||||
|
||||
impl<D: SnapshotVecDelegate> ops::Index<usize> for SnapshotVec<D> {
|
||||
type Output = D::Value;
|
||||
fn index(&self, index: usize) -> &D::Value {
|
||||
self.get(index)
|
||||
}
|
||||
}
|
||||
|
||||
impl<D: SnapshotVecDelegate> ops::IndexMut<usize> for SnapshotVec<D> {
|
||||
fn index_mut(&mut self, index: usize) -> &mut D::Value {
|
||||
self.get_mut(index)
|
||||
}
|
||||
}
|
||||
|
||||
impl<D: SnapshotVecDelegate> Extend<D::Value> for SnapshotVec<D> {
|
||||
fn extend<T>(&mut self, iterable: T)
|
||||
where
|
||||
T: IntoIterator<Item = D::Value>,
|
||||
{
|
||||
let initial_len = self.values.0.len();
|
||||
self.values.0.extend(iterable);
|
||||
let final_len = self.values.0.len();
|
||||
|
||||
if self.in_snapshot() {
|
||||
self.undo_log.extend((initial_len..final_len).map(NewElem));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<D: SnapshotVecDelegate> Clone for SnapshotVec<D>
|
||||
where
|
||||
D::Value: Clone,
|
||||
D::Undo: Clone,
|
||||
{
|
||||
fn clone(&self) -> Self {
|
||||
SnapshotVec {
|
||||
values: self.values.clone(),
|
||||
undo_log: self.undo_log.clone(),
|
||||
num_open_snapshots: self.num_open_snapshots,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<D: SnapshotVecDelegate> Clone for UndoLog<D>
|
||||
where
|
||||
D::Value: Clone,
|
||||
D::Undo: Clone,
|
||||
{
|
||||
fn clone(&self) -> Self {
|
||||
match *self {
|
||||
NewElem(i) => NewElem(i),
|
||||
SetElem(i, ref v) => SetElem(i, v.clone()),
|
||||
Other(ref u) => Other(u.clone()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl SnapshotVecDelegate for i32 {
|
||||
type Value = i32;
|
||||
type Undo = ();
|
||||
|
||||
fn reverse(_: &mut Vec<i32>, _: ()) {}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn basic() {
|
||||
let mut vec: SnapshotVec<i32> = SnapshotVec::default();
|
||||
assert!(!vec.in_snapshot());
|
||||
assert_eq!(vec.len(), 0);
|
||||
vec.push(22);
|
||||
vec.push(33);
|
||||
assert_eq!(vec.len(), 2);
|
||||
assert_eq!(*vec.get(0), 22);
|
||||
assert_eq!(*vec.get(1), 33);
|
||||
vec.set(1, 34);
|
||||
assert_eq!(vec.len(), 2);
|
||||
assert_eq!(*vec.get(0), 22);
|
||||
assert_eq!(*vec.get(1), 34);
|
||||
|
||||
let snapshot = vec.start_snapshot();
|
||||
assert!(vec.in_snapshot());
|
||||
|
||||
vec.push(44);
|
||||
vec.push(55);
|
||||
vec.set(1, 35);
|
||||
assert_eq!(vec.len(), 4);
|
||||
assert_eq!(*vec.get(0), 22);
|
||||
assert_eq!(*vec.get(1), 35);
|
||||
assert_eq!(*vec.get(2), 44);
|
||||
assert_eq!(*vec.get(3), 55);
|
||||
|
||||
vec.rollback_to(snapshot);
|
||||
assert!(!vec.in_snapshot());
|
||||
|
||||
assert_eq!(vec.len(), 2);
|
||||
assert_eq!(*vec.get(0), 22);
|
||||
assert_eq!(*vec.get(1), 34);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[should_panic]
|
||||
fn out_of_order() {
|
||||
let mut vec: SnapshotVec<i32> = SnapshotVec::default();
|
||||
vec.push(22);
|
||||
let snapshot1 = vec.start_snapshot();
|
||||
vec.push(33);
|
||||
let snapshot2 = vec.start_snapshot();
|
||||
vec.push(44);
|
||||
vec.rollback_to(snapshot1); // bogus, but accepted
|
||||
vec.rollback_to(snapshot2); // asserts
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn nested_commit_then_rollback() {
|
||||
let mut vec: SnapshotVec<i32> = SnapshotVec::default();
|
||||
vec.push(22);
|
||||
let snapshot1 = vec.start_snapshot();
|
||||
let snapshot2 = vec.start_snapshot();
|
||||
vec.set(0, 23);
|
||||
vec.commit(snapshot2);
|
||||
assert_eq!(*vec.get(0), 23);
|
||||
vec.rollback_to(snapshot1);
|
||||
assert_eq!(*vec.get(0), 22);
|
||||
}
|
249
vendor/ena/src/unify/backing_vec.rs
vendored
249
vendor/ena/src/unify/backing_vec.rs
vendored
|
@ -1,249 +0,0 @@
|
|||
use crate::snapshot_vec as sv;
|
||||
#[cfg(feature = "persistent")]
|
||||
use im_rc::Vector;
|
||||
use std::fmt::{self, Debug};
|
||||
use std::marker::PhantomData;
|
||||
use std::ops::{self, Range};
|
||||
|
||||
use super::{UnifyKey, VarValue};
|
||||
|
||||
#[allow(dead_code)] // rustc BUG
|
||||
#[allow(type_alias_bounds)]
|
||||
type Key<S: UnificationStore> = <S as UnificationStore>::Key;
|
||||
|
||||
/// Largely internal trait implemented by the unification table
|
||||
/// 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>>>
|
||||
+ ops::IndexMut<usize, Output = VarValue<Key<Self>>>
|
||||
+ Clone
|
||||
+ Default
|
||||
{
|
||||
type Key: UnifyKey<Value = Self::Value>;
|
||||
type Value: Clone + Debug;
|
||||
type Snapshot;
|
||||
|
||||
fn start_snapshot(&mut self) -> Self::Snapshot;
|
||||
|
||||
fn rollback_to(&mut self, snapshot: Self::Snapshot);
|
||||
|
||||
fn commit(&mut self, snapshot: Self::Snapshot);
|
||||
|
||||
fn values_since_snapshot(&self, snapshot: &Self::Snapshot) -> Range<usize>;
|
||||
|
||||
fn reset_unifications(&mut self, value: impl FnMut(usize) -> VarValue<Self::Key>);
|
||||
|
||||
fn len(&self) -> usize;
|
||||
|
||||
fn is_empty(&self) -> bool;
|
||||
|
||||
fn push(&mut self, value: VarValue<Self::Key>);
|
||||
|
||||
fn reserve(&mut self, num_new_values: usize);
|
||||
|
||||
fn update<F>(&mut self, index: usize, op: F)
|
||||
where
|
||||
F: FnOnce(&mut VarValue<Self::Key>);
|
||||
|
||||
fn tag() -> &'static str {
|
||||
Self::Key::tag()
|
||||
}
|
||||
}
|
||||
|
||||
/// Backing store for an in-place unification table.
|
||||
/// Not typically used directly.
|
||||
#[derive(Clone)]
|
||||
pub struct InPlace<K: UnifyKey> {
|
||||
values: sv::SnapshotVec<Delegate<K>>,
|
||||
}
|
||||
|
||||
impl<K> fmt::Debug for InPlace<K>
|
||||
where
|
||||
K: UnifyKey,
|
||||
K: Debug,
|
||||
{
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
self.values.fmt(f)
|
||||
}
|
||||
}
|
||||
|
||||
// HACK(eddyb) manual impl avoids `Default` bound on `K`.
|
||||
impl<K: UnifyKey> Default for InPlace<K> {
|
||||
fn default() -> Self {
|
||||
InPlace {
|
||||
values: sv::SnapshotVec::new(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<K: UnifyKey> UnificationStore for InPlace<K> {
|
||||
type Key = K;
|
||||
type Value = K::Value;
|
||||
type Snapshot = sv::Snapshot;
|
||||
|
||||
#[inline]
|
||||
fn start_snapshot(&mut self) -> Self::Snapshot {
|
||||
self.values.start_snapshot()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn rollback_to(&mut self, snapshot: Self::Snapshot) {
|
||||
self.values.rollback_to(snapshot);
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn commit(&mut self, snapshot: Self::Snapshot) {
|
||||
self.values.commit(snapshot);
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn values_since_snapshot(&self, snapshot: &Self::Snapshot) -> Range<usize> {
|
||||
snapshot.value_count..self.len()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn reset_unifications(&mut self, value: impl FnMut(usize) -> VarValue<Self::Key>) {
|
||||
self.values.set_all(value);
|
||||
}
|
||||
|
||||
fn len(&self) -> usize {
|
||||
self.values.len()
|
||||
}
|
||||
|
||||
fn is_empty(&self) -> bool {
|
||||
self.values.len() == 0
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn push(&mut self, value: VarValue<Self::Key>) {
|
||||
self.values.push(value);
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn reserve(&mut self, num_new_values: usize) {
|
||||
self.values.reserve(num_new_values);
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn update<F>(&mut self, index: usize, op: F)
|
||||
where
|
||||
F: FnOnce(&mut VarValue<Self::Key>),
|
||||
{
|
||||
self.values.update(index, op)
|
||||
}
|
||||
}
|
||||
|
||||
impl<K> ops::Index<usize> for InPlace<K>
|
||||
where
|
||||
K: UnifyKey,
|
||||
{
|
||||
type Output = VarValue<K>;
|
||||
fn index(&self, index: usize) -> &VarValue<K> {
|
||||
&self.values[index]
|
||||
}
|
||||
}
|
||||
|
||||
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>);
|
||||
|
||||
impl<K: UnifyKey> sv::SnapshotVecDelegate for Delegate<K> {
|
||||
type Value = VarValue<K>;
|
||||
type Undo = ();
|
||||
|
||||
fn reverse(_: &mut Vec<VarValue<K>>, _: ()) {}
|
||||
}
|
||||
|
||||
#[cfg(feature = "persistent")]
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct Persistent<K: UnifyKey> {
|
||||
values: Vector<VarValue<K>>,
|
||||
}
|
||||
|
||||
// HACK(eddyb) manual impl avoids `Default` bound on `K`.
|
||||
#[cfg(feature = "persistent")]
|
||||
impl<K: UnifyKey> Default for Persistent<K> {
|
||||
fn default() -> Self {
|
||||
Persistent {
|
||||
values: Vector::new(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "persistent")]
|
||||
impl<K: UnifyKey> UnificationStore for Persistent<K> {
|
||||
type Key = K;
|
||||
type Value = K::Value;
|
||||
type Snapshot = Self;
|
||||
|
||||
#[inline]
|
||||
fn start_snapshot(&mut self) -> Self::Snapshot {
|
||||
self.clone()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn rollback_to(&mut self, snapshot: Self::Snapshot) {
|
||||
*self = snapshot;
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn commit(&mut self, _snapshot: Self::Snapshot) {}
|
||||
|
||||
#[inline]
|
||||
fn values_since_snapshot(&self, snapshot: &Self::Snapshot) -> Range<usize> {
|
||||
snapshot.len()..self.len()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn reset_unifications(&mut self, mut value: impl FnMut(usize) -> VarValue<Self::Key>) {
|
||||
// Without extending dogged, there isn't obviously a more
|
||||
// efficient way to do this. But it's pretty dumb. Maybe
|
||||
// dogged needs a `map`. [NOTE: revisit in light of replacing dogged with im_rc!]
|
||||
for i in 0..self.values.len() {
|
||||
self.values[i] = value(i);
|
||||
}
|
||||
}
|
||||
|
||||
fn len(&self) -> usize {
|
||||
self.values.len()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn push(&mut self, value: VarValue<Self::Key>) {
|
||||
self.values.push(value);
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn reserve(&mut self, _num_new_values: usize) {
|
||||
// not obviously relevant to Vector.
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn update<F>(&mut self, index: usize, op: F)
|
||||
where
|
||||
F: FnOnce(&mut VarValue<Self::Key>),
|
||||
{
|
||||
let p = &mut self.values[index];
|
||||
op(p);
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "persistent")]
|
||||
impl<K> ops::Index<usize> for Persistent<K>
|
||||
where
|
||||
K: UnifyKey,
|
||||
{
|
||||
type Output = VarValue<K>;
|
||||
fn index(&self, index: usize) -> &VarValue<K> {
|
||||
&self.values[index]
|
||||
}
|
||||
}
|
500
vendor/ena/src/unify/mod.rs
vendored
500
vendor/ena/src/unify/mod.rs
vendored
|
@ -1,500 +0,0 @@
|
|||
// Copyright 2012-2014 The Rust Project Developers. See the COPYRIGHT
|
||||
// file at the top-level directory of this distribution and at
|
||||
// http://rust-lang.org/COPYRIGHT.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
//! Union-find implementation. The main type is `UnificationTable`.
|
||||
//!
|
||||
//! You can define your own type for the *keys* in the table, but you
|
||||
//! must implement `UnifyKey` for that type. The assumption is that
|
||||
//! keys will be newtyped integers, hence we require that they
|
||||
//! implement `Copy`.
|
||||
//!
|
||||
//! Keys can have values associated with them. The assumption is that
|
||||
//! these values are cheaply cloneable (ideally, `Copy`), and some of
|
||||
//! the interfaces are oriented around that assumption. If you just
|
||||
//! want the classical "union-find" algorithm where you group things
|
||||
//! into sets, use the `Value` type of `()`.
|
||||
//!
|
||||
//! When you have keys with non-trivial values, you must also define
|
||||
//! how those values can be merged. As part of doing this, you can
|
||||
//! define the "error" type to return on error; if errors are not
|
||||
//! possible, use `NoError` (an uninstantiable struct). Using this
|
||||
//! type also unlocks various more ergonomic methods (e.g., `union()`
|
||||
//! in place of `unify_var_var()`).
|
||||
//!
|
||||
//! The best way to see how it is used is to read the `tests.rs` file;
|
||||
//! search for e.g. `UnitKey`.
|
||||
|
||||
use std::cmp::Ordering;
|
||||
use std::fmt::{self, Debug};
|
||||
use std::marker;
|
||||
use std::ops::Range;
|
||||
|
||||
mod backing_vec;
|
||||
pub use self::backing_vec::{InPlace, UnificationStore};
|
||||
|
||||
#[cfg(feature = "persistent")]
|
||||
pub use self::backing_vec::Persistent;
|
||||
|
||||
/// This trait is implemented by any type that can serve as a type
|
||||
/// variable. We call such variables *unification keys*. For example,
|
||||
/// this trait is implemented by `IntVid`, which represents integral
|
||||
/// variables.
|
||||
///
|
||||
/// Each key type has an associated value type `V`. For example, for
|
||||
/// `IntVid`, this is `Option<IntVarValue>`, representing some
|
||||
/// (possibly not yet known) sort of integer.
|
||||
///
|
||||
/// Clients are expected to provide implementations of this trait; you
|
||||
/// can see some examples in the `test` module.
|
||||
pub trait UnifyKey: Copy + Clone + Debug + PartialEq {
|
||||
type Value: Clone + Debug;
|
||||
|
||||
fn index(&self) -> u32;
|
||||
|
||||
fn from_index(u: u32) -> Self;
|
||||
|
||||
fn tag() -> &'static str;
|
||||
|
||||
/// If true, then `self` should be preferred as root to `other`.
|
||||
/// Note that we assume a consistent partial ordering, so
|
||||
/// returning true implies that `other.prefer_as_root_to(self)`
|
||||
/// would return false. If there is no ordering between two keys
|
||||
/// (i.e., `a.prefer_as_root_to(b)` and `b.prefer_as_root_to(a)`
|
||||
/// both return false) then the rank will be used to determine the
|
||||
/// root in an optimal way.
|
||||
///
|
||||
/// NB. The only reason to implement this method is if you want to
|
||||
/// control what value is returned from `find()`. In general, it
|
||||
/// is better to let the unification table determine the root,
|
||||
/// since overriding the rank can cause execution time to increase
|
||||
/// dramatically.
|
||||
#[allow(unused_variables)]
|
||||
fn order_roots(
|
||||
a: Self,
|
||||
a_value: &Self::Value,
|
||||
b: Self,
|
||||
b_value: &Self::Value,
|
||||
) -> Option<(Self, Self)> {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
/// A struct which can never be instantiated. Used
|
||||
/// for the error type for infallible cases.
|
||||
#[derive(Debug)]
|
||||
pub struct NoError {
|
||||
_dummy: (),
|
||||
}
|
||||
|
||||
/// Value of a unification key. We implement Tarjan's union-find
|
||||
/// algorithm: when two keys are unified, one of them is converted
|
||||
/// into a "redirect" pointing at the other. These redirects form a
|
||||
/// DAG: the roots of the DAG (nodes that are not redirected) are each
|
||||
/// associated with a value of type `V` and a rank. The rank is used
|
||||
/// to keep the DAG relatively balanced, which helps keep the running
|
||||
/// time of the algorithm under control. For more information, see
|
||||
/// <http://en.wikipedia.org/wiki/Disjoint-set_data_structure>.
|
||||
#[derive(PartialEq, Clone)]
|
||||
pub struct VarValue<K: UnifyKey> {
|
||||
// FIXME pub
|
||||
parent: K, // if equal to self, this is a root
|
||||
pub value: K::Value, // value assigned (only relevant to root)
|
||||
rank: u32, // max depth (only relevant to root)
|
||||
}
|
||||
|
||||
impl<K> fmt::Debug for VarValue<K>
|
||||
where
|
||||
K: UnifyKey,
|
||||
{
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(f, "p: {:?}, c: {:?}", self.parent, self.value)
|
||||
}
|
||||
}
|
||||
|
||||
/// Table of unification keys and their values. You must define a key type K
|
||||
/// that implements the `UnifyKey` trait. Unification tables can be used in two-modes:
|
||||
///
|
||||
/// - in-place (`UnificationTable<InPlace<K>>` or `InPlaceUnificationTable<K>`):
|
||||
/// - This is the standard mutable mode, where the array is modified
|
||||
/// in place.
|
||||
/// - To do backtracking, you can employ the `snapshot` and `rollback_to`
|
||||
/// methods.
|
||||
/// - persistent (`UnificationTable<Persistent<K>>` or `PersistentUnificationTable<K>`):
|
||||
/// - In this mode, we use a persistent vector to store the data, so that
|
||||
/// cloning the table is an O(1) operation.
|
||||
/// - This implies that ordinary operations are quite a bit slower though.
|
||||
/// - Requires the `persistent` feature be selected in your Cargo.toml file.
|
||||
#[derive(Clone, Default)]
|
||||
pub struct UnificationTable<S: UnificationStore> {
|
||||
/// Indicates the current value of each key.
|
||||
values: S,
|
||||
}
|
||||
|
||||
impl<S> fmt::Debug for UnificationTable<S>
|
||||
where
|
||||
S: UnificationStore,
|
||||
S: Debug,
|
||||
{
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
self.values.fmt(f)
|
||||
}
|
||||
}
|
||||
|
||||
/// A unification table that uses an "in-place" vector.
|
||||
#[allow(type_alias_bounds)]
|
||||
pub type InPlaceUnificationTable<K: UnifyKey> = UnificationTable<InPlace<K>>;
|
||||
|
||||
/// A unification table that uses a "persistent" vector.
|
||||
#[cfg(feature = "persistent")]
|
||||
#[allow(type_alias_bounds)]
|
||||
pub type PersistentUnificationTable<K: UnifyKey> = UnificationTable<Persistent<K>>;
|
||||
|
||||
/// At any time, users may snapshot a unification table. The changes
|
||||
/// made during the snapshot may either be *committed* or *rolled back*.
|
||||
pub struct Snapshot<S: UnificationStore> {
|
||||
// Link snapshot to the unification store `S` of the table.
|
||||
marker: marker::PhantomData<S>,
|
||||
snapshot: S::Snapshot,
|
||||
}
|
||||
|
||||
impl<K: UnifyKey> VarValue<K> {
|
||||
fn new_var(key: K, value: K::Value) -> VarValue<K> {
|
||||
VarValue::new(key, value, 0)
|
||||
}
|
||||
|
||||
fn new(parent: K, value: K::Value, rank: u32) -> VarValue<K> {
|
||||
VarValue {
|
||||
parent, // this is a root
|
||||
value,
|
||||
rank,
|
||||
}
|
||||
}
|
||||
|
||||
fn redirect(&mut self, to: K) {
|
||||
self.parent = to;
|
||||
}
|
||||
|
||||
fn root(&mut self, rank: u32, value: K::Value) {
|
||||
self.rank = rank;
|
||||
self.value = value;
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn parent(&self, self_key: K) -> Option<K> {
|
||||
self.if_not_self(self.parent, self_key)
|
||||
}
|
||||
|
||||
fn raw_parent(&self) -> K {
|
||||
self.parent
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn if_not_self(&self, key: K, self_key: K) -> Option<K> {
|
||||
if key == self_key {
|
||||
None
|
||||
} else {
|
||||
Some(key)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// We can't use V:LatticeValue, much as I would like to,
|
||||
// because frequently the pattern is that V=Option<U> for some
|
||||
// other type parameter U, and we have no way to say
|
||||
// Option<U>:LatticeValue.
|
||||
|
||||
impl<S: UnificationStore> UnificationTable<S> {
|
||||
pub fn new() -> Self {
|
||||
Self::default()
|
||||
}
|
||||
|
||||
/// Starts a new snapshot. Each snapshot must be either
|
||||
/// rolled back or committed in a "LIFO" (stack) order.
|
||||
pub fn snapshot(&mut self) -> Snapshot<S> {
|
||||
Snapshot {
|
||||
marker: marker::PhantomData::<S>,
|
||||
snapshot: self.values.start_snapshot(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Reverses all changes since the last snapshot. Also
|
||||
/// removes any keys that have been created since then.
|
||||
pub fn rollback_to(&mut self, snapshot: Snapshot<S>) {
|
||||
debug!("{}: rollback_to()", S::tag());
|
||||
self.values.rollback_to(snapshot.snapshot);
|
||||
}
|
||||
|
||||
/// Commits all changes since the last snapshot. Of course, they
|
||||
/// can still be undone if there is a snapshot further out.
|
||||
pub fn commit(&mut self, snapshot: Snapshot<S>) {
|
||||
debug!("{}: commit()", S::tag());
|
||||
self.values.commit(snapshot.snapshot);
|
||||
}
|
||||
|
||||
/// Creates a fresh key with the given value.
|
||||
pub fn new_key(&mut self, value: S::Value) -> S::Key {
|
||||
let len = self.values.len() as u32;
|
||||
let key: S::Key = UnifyKey::from_index(len);
|
||||
self.values.push(VarValue::new_var(key, value));
|
||||
debug!("{}: created new key: {:?}", S::tag(), key);
|
||||
key
|
||||
}
|
||||
|
||||
/// Reserve memory for `num_new_keys` to be created. Does not
|
||||
/// actually create the new keys; you must then invoke `new_key`.
|
||||
pub fn reserve(&mut self, num_new_keys: usize) {
|
||||
self.values.reserve(num_new_keys);
|
||||
}
|
||||
|
||||
/// Clears all unifications that have been performed, resetting to
|
||||
/// the initial state. The values of each variable are given by
|
||||
/// the closure.
|
||||
pub fn reset_unifications(&mut self, mut value: impl FnMut(S::Key) -> S::Value) {
|
||||
self.values.reset_unifications(|i| {
|
||||
let key = UnifyKey::from_index(i as u32);
|
||||
let value = value(key);
|
||||
VarValue::new_var(key, value)
|
||||
});
|
||||
}
|
||||
|
||||
/// Returns the number of keys created so far.
|
||||
pub fn len(&self) -> usize {
|
||||
self.values.len()
|
||||
}
|
||||
|
||||
/// Returns true iff there have been no keys created yet.
|
||||
pub fn is_empty(&self) -> bool {
|
||||
self.values.len() == 0
|
||||
}
|
||||
|
||||
/// Returns the keys of all variables created since the `snapshot`.
|
||||
pub fn vars_since_snapshot(&self, snapshot: &Snapshot<S>) -> Range<S::Key> {
|
||||
let range = self.values.values_since_snapshot(&snapshot.snapshot);
|
||||
S::Key::from_index(range.start as u32)..S::Key::from_index(range.end as u32)
|
||||
}
|
||||
|
||||
/// Obtains the current value for a particular key.
|
||||
/// Not for end-users; they can use `probe_value`.
|
||||
pub fn value(&self, key: S::Key) -> &VarValue<S::Key> {
|
||||
&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>.
|
||||
///
|
||||
/// NB. This is a building-block operation and you would probably
|
||||
/// prefer to call `probe` below.
|
||||
///
|
||||
/// This is an always-inlined version of this function for the hot
|
||||
/// callsites. `uninlined_get_root_key` is the never-inlined version.
|
||||
#[inline(always)]
|
||||
pub fn inlined_get_root_key(&mut self, vid: S::Key) -> S::Key {
|
||||
match self.value(vid).parent(vid) {
|
||||
None => vid,
|
||||
Some(redirect) => {
|
||||
let root_key: S::Key = self.uninlined_get_root_key(redirect);
|
||||
if root_key != redirect {
|
||||
// Path compression
|
||||
self.update_value(vid, |value| value.parent = root_key);
|
||||
}
|
||||
|
||||
root_key
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// This is a never-inlined version of this function for cold callsites.
|
||||
// 'inlined_get_root_key` is the always-inlined version.
|
||||
#[inline(never)]
|
||||
fn uninlined_get_root_key(&mut self, vid: S::Key) -> S::Key {
|
||||
self.inlined_get_root_key(vid)
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub fn get_root_key_without_compacting(&self, mut vid: S::Key) -> S::Key {
|
||||
while let Some(redirect) = self.value(vid).parent(vid) {
|
||||
vid = redirect;
|
||||
}
|
||||
|
||||
vid
|
||||
}
|
||||
|
||||
pub fn is_redirect(&self, vid: S::Key) -> bool {
|
||||
self.value(vid).raw_parent() != vid
|
||||
}
|
||||
|
||||
pub fn update_value<OP>(&mut self, key: S::Key, op: OP)
|
||||
where
|
||||
OP: FnOnce(&mut VarValue<S::Key>),
|
||||
{
|
||||
self.values.update(key.index() as usize, op);
|
||||
debug!("Updated variable {:?} to {:?}", key, self.value(key));
|
||||
}
|
||||
|
||||
/// Either redirects `node_a` to `node_b` or vice versa, depending
|
||||
/// on the relative rank. The value associated with the new root
|
||||
/// will be `new_value`.
|
||||
///
|
||||
/// NB: This is the "union" operation of "union-find". It is
|
||||
/// really more of a building block. If the values associated with
|
||||
/// your key are non-trivial, you would probably prefer to call
|
||||
/// `unify_var_var` below.
|
||||
pub fn unify_roots(&mut self, key_a: S::Key, key_b: S::Key, new_value: S::Value) {
|
||||
debug!("unify(key_a={:?}, key_b={:?})", key_a, key_b);
|
||||
|
||||
let rank_a = self.value(key_a).rank;
|
||||
let rank_b = self.value(key_b).rank;
|
||||
if let Some((new_root, redirected)) = S::Key::order_roots(
|
||||
key_a,
|
||||
&self.value(key_a).value,
|
||||
key_b,
|
||||
&self.value(key_b).value,
|
||||
) {
|
||||
// compute the new rank for the new root that they chose;
|
||||
// this may not be the optimal choice.
|
||||
let new_rank = if new_root == key_a {
|
||||
debug_assert!(redirected == key_b);
|
||||
if rank_a > rank_b {
|
||||
rank_a
|
||||
} else {
|
||||
rank_b + 1
|
||||
}
|
||||
} else {
|
||||
debug_assert!(new_root == key_b);
|
||||
debug_assert!(redirected == key_a);
|
||||
if rank_b > rank_a {
|
||||
rank_b
|
||||
} else {
|
||||
rank_a + 1
|
||||
}
|
||||
};
|
||||
self.redirect_root(new_rank, redirected, new_root, new_value);
|
||||
} else {
|
||||
match rank_a.cmp(&rank_b) {
|
||||
Ordering::Greater => {
|
||||
// a has greater rank, so a should become b's parent,
|
||||
// i.e., b should redirect to a.
|
||||
self.redirect_root(rank_a, key_b, key_a, new_value);
|
||||
}
|
||||
Ordering::Less => {
|
||||
// b has greater rank, so a should redirect to b.
|
||||
self.redirect_root(rank_b, key_a, key_b, new_value);
|
||||
}
|
||||
Ordering::Equal => {
|
||||
// If equal, redirect one to the other and increment the
|
||||
// other's rank.
|
||||
self.redirect_root(rank_a + 1, key_a, key_b, new_value);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Internal method to redirect `old_root_key` (which is currently
|
||||
/// a root) to a child of `new_root_key` (which will remain a
|
||||
/// root). The rank and value of `new_root_key` will be updated to
|
||||
/// `new_rank` and `new_value` respectively.
|
||||
fn redirect_root(
|
||||
&mut self,
|
||||
new_rank: u32,
|
||||
old_root_key: S::Key,
|
||||
new_root_key: S::Key,
|
||||
new_value: S::Value,
|
||||
) {
|
||||
self.update_value(old_root_key, |old_root_value| {
|
||||
old_root_value.redirect(new_root_key);
|
||||
});
|
||||
self.update_value(new_root_key, |new_root_value| {
|
||||
new_root_value.root(new_rank, new_value);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/// ////////////////////////////////////////////////////////////////////////
|
||||
/// Public API
|
||||
|
||||
impl<'tcx, S, K, V> UnificationTable<S>
|
||||
where
|
||||
S: UnificationStore<Key = K, Value = V>,
|
||||
K: UnifyKey<Value = V>,
|
||||
V: Clone + Debug,
|
||||
{
|
||||
/// Given two keys, indicates whether they have been unioned together.
|
||||
pub fn unioned<K1, K2>(&mut self, a_id: K1, b_id: K2) -> bool
|
||||
where
|
||||
K1: Into<K>,
|
||||
K2: Into<K>,
|
||||
{
|
||||
self.find(a_id) == self.find(b_id)
|
||||
}
|
||||
|
||||
/// Given a key, returns the (current) root key.
|
||||
pub fn find<K1>(&mut self, id: K1) -> K
|
||||
where
|
||||
K1: Into<K>,
|
||||
{
|
||||
let id = id.into();
|
||||
self.inlined_get_root_key(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.
|
||||
#[inline(always)]
|
||||
pub fn probe_value<K1>(&mut self, id: K1) -> V
|
||||
where
|
||||
K1: Into<K>,
|
||||
{
|
||||
let id = id.into();
|
||||
let id = self.inlined_get_root_key(id);
|
||||
self.value(id).value.clone()
|
||||
}
|
||||
|
||||
/// Returns the current value for the given key. If the key has
|
||||
/// been union'd, this will give the value from the current root.
|
||||
#[inline(always)]
|
||||
pub fn probe_value_ref<K1>(&self, id: K1) -> &VarValue<K>
|
||||
where
|
||||
K1: Into<K>,
|
||||
{
|
||||
let id = id.into();
|
||||
let id = self.get_root_key_without_compacting(id);
|
||||
self.value(id)
|
||||
}
|
||||
|
||||
/// Returns the current value for the given key. If the key has
|
||||
/// been union'd, this will give the value from the current root.
|
||||
#[inline(always)]
|
||||
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.inlined_get_root_key(id);
|
||||
self.value_mut(id)
|
||||
}
|
||||
|
||||
/// This is for a debug_assert! in solve() only. Do not use it elsewhere!
|
||||
#[inline(always)]
|
||||
pub fn probe_value_without_compacting<K1>(&self, id: K1) -> V
|
||||
where
|
||||
K1: Into<K>,
|
||||
{
|
||||
let id = id.into();
|
||||
let id = self.get_root_key_without_compacting(id);
|
||||
|
||||
self.value(id).value.clone()
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue