Merge branch 'trunk' of github.com:rtfeldman/roc into wasm-link-host-to-app-calls

This commit is contained in:
Brian Carroll 2022-06-06 12:00:16 +01:00
commit d045f3be7b
No known key found for this signature in database
GPG key ID: 5C7B2EC4101703C0
24 changed files with 575 additions and 2027 deletions

View file

@ -21,5 +21,8 @@ jobs:
with: with:
clean: "true" 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 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
View file

@ -4141,7 +4141,6 @@ dependencies = [
"roc_module", "roc_module",
"roc_region", "roc_region",
"static_assertions 1.1.0", "static_assertions 1.1.0",
"ven_ena",
] ]
[[package]] [[package]]
@ -5077,13 +5076,6 @@ dependencies = [
"getrandom", "getrandom",
] ]
[[package]]
name = "ven_ena"
version = "0.13.1"
dependencies = [
"log",
]
[[package]] [[package]]
name = "ven_graph" name = "ven_graph"
version = "2.0.5-pre" version = "2.0.5-pre"

View file

@ -27,7 +27,6 @@ members = [
"compiler/test_gen", "compiler/test_gen",
"compiler/roc_target", "compiler/roc_target",
"compiler/debug_flags", "compiler/debug_flags",
"vendor/ena",
"vendor/inkwell", "vendor/inkwell",
"vendor/pathfinding", "vendor/pathfinding",
"vendor/pretty", "vendor/pretty",

View file

@ -361,11 +361,12 @@ pub fn build_swift_host_native(
unimplemented!("Linking a shared library to Swift not yet implemented"); 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 command
.env_clear() .env_clear()
.env("PATH", &env_path) .env("PATH", &env_path)
.env("HOME", &env_home) .env("HOME", &env_home)
.arg("swiftc")
.args(sources) .args(sources)
.arg("-emit-object") .arg("-emit-object")
.arg("-parse-as-library") .arg("-parse-as-library")

View file

@ -867,9 +867,9 @@ fn resolve_abilities<'a>(
.iter() .iter()
.partition(|av| av.ability == loc_ability_name.value); .partition(|av| av.ability == loc_ability_name.value);
let mut bad_has_clauses = false; let var_bound_to_ability = match variables_bound_to_ability.as_slice() {
[one] => one.variable,
if variables_bound_to_ability.is_empty() { [] => {
// There are no variables bound to the parent ability - then this member doesn't // There are no variables bound to the parent ability - then this member doesn't
// need to be a part of the ability. // need to be a part of the ability.
env.problem(Problem::AbilityMemberMissingHasClause { env.problem(Problem::AbilityMemberMissingHasClause {
@ -877,16 +877,17 @@ fn resolve_abilities<'a>(
ability: loc_ability_name.value, ability: loc_ability_name.value,
region: name_region, 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 // 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 // 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 // We have no way of telling what type implements a particular instance of Eq in
// this case (a or b?), so disallow it. // this case (a or b?), so disallow it.
let span_has_clauses = let span_has_clauses = Region::across_all(
Region::across_all(variables_bound_to_ability.iter().map(|v| &v.first_seen)); variables_bound_to_ability.iter().map(|v| &v.first_seen),
);
let bound_var_names = variables_bound_to_ability let bound_var_names = variables_bound_to_ability
.iter() .iter()
.map(|v| v.name.clone()) .map(|v| v.name.clone())
@ -897,13 +898,10 @@ fn resolve_abilities<'a>(
span_has_clauses, span_has_clauses,
bound_var_names, bound_var_names,
}); });
bad_has_clauses = true;
}
if bad_has_clauses {
// Pretend the member isn't a part of the ability // Pretend the member isn't a part of the ability
continue; continue;
} }
};
// The introduced variables are good; add them to the output. // The introduced variables are good; add them to the output.
output output
@ -918,6 +916,13 @@ fn resolve_abilities<'a>(
flex_vars: iv.collect_flex(), 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(( can_members.push((
member_sym, member_sym,
AbilityMemberData { AbilityMemberData {
@ -925,7 +930,7 @@ fn resolve_abilities<'a>(
region: name_region, region: name_region,
typ: MemberTypeInfo::Local { typ: MemberTypeInfo::Local {
variables, variables,
signature: member_annot.typ, signature,
signature_var: var_store.fresh(), signature_var: var_store.fresh(),
}, },
}, },

View file

@ -1685,7 +1685,6 @@ impl LocalDefVarsVec<(Symbol, Loc<Variable>)> {
} }
} }
use std::borrow::Borrow;
use std::cell::RefCell; use std::cell::RefCell;
use std::ops::ControlFlow; use std::ops::ControlFlow;
std::thread_local! { std::thread_local! {
@ -1888,6 +1887,18 @@ fn type_to_variable<'a>(
register_with_known_var(subs, destination, rank, pools, content) 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 // This case is important for the rank of boolean variables
Function(arguments, closure_type, ret_type) => { Function(arguments, closure_type, ret_type) => {
let new_arguments = VariableSubsSlice::reserve_into_subs(subs, arguments.len()); let new_arguments = VariableSubsSlice::reserve_into_subs(subs, arguments.len());
@ -2379,12 +2390,12 @@ fn insert_tags_fast_path<'a>(
rank: Rank, rank: Rank,
pools: &mut Pools, pools: &mut Pools,
arena: &'_ bumpalo::Bump, arena: &'_ bumpalo::Bump,
tags: &'a [(TagName, impl Borrow<[Type]>)], tags: &'a [(TagName, Vec<Type>)],
stack: &mut bumpalo::collections::Vec<'_, TypeToVar<'a>>, stack: &mut bumpalo::collections::Vec<'_, TypeToVar<'a>>,
) -> UnionTags { ) -> UnionTags {
if let [(TagName(tag_name), arguments)] = tags { if let [(TagName(tag_name), arguments)] = tags {
let variable_slice = 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 = let new_variable_slices =
SubsSlice::extend_new(&mut subs.variable_slices, [variable_slice]); 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 { for (variable_slice_index, (_, arguments)) in it {
subs.variable_slices[variable_slice_index] = 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) 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 { for ((variable_slice_index, tag_name_index), (tag_name, arguments)) in it {
subs.variable_slices[variable_slice_index] = 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(); subs.tag_names[tag_name_index] = tag_name.clone();
} }
@ -2440,12 +2451,12 @@ fn insert_tags_slow_path<'a>(
rank: Rank, rank: Rank,
pools: &mut Pools, pools: &mut Pools,
arena: &'_ bumpalo::Bump, arena: &'_ bumpalo::Bump,
tags: &'a [(TagName, impl Borrow<[Type]>)], tags: &'a [(TagName, Vec<Type>)],
mut tag_vars: bumpalo::collections::Vec<(TagName, VariableSubsSlice)>, mut tag_vars: bumpalo::collections::Vec<(TagName, VariableSubsSlice)>,
stack: &mut bumpalo::collections::Vec<'_, TypeToVar<'a>>, stack: &mut bumpalo::collections::Vec<'_, TypeToVar<'a>>,
) -> UnionTags { ) -> UnionTags {
for (tag, tag_argument_types) in tags { 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()); let new_slice = VariableSubsSlice::reserve_into_subs(subs, tag_argument_types.len());
for (i, arg) in (new_slice.indices()).zip(tag_argument_types) { for (i, arg) in (new_slice.indices()).zip(tag_argument_types) {
@ -2466,7 +2477,7 @@ fn type_to_union_tags<'a>(
rank: Rank, rank: Rank,
pools: &mut Pools, pools: &mut Pools,
arena: &'_ bumpalo::Bump, arena: &'_ bumpalo::Bump,
tags: &'a [(TagName, impl Borrow<[Type]>)], tags: &'a [(TagName, Vec<Type>)],
ext: &'a TypeExtension, ext: &'a TypeExtension,
stack: &mut bumpalo::collections::Vec<'_, TypeToVar<'a>>, stack: &mut bumpalo::collections::Vec<'_, TypeToVar<'a>>,
) -> (UnionTags, Variable) { ) -> (UnionTags, Variable) {

View file

@ -2504,7 +2504,7 @@ mod solve_expr {
{ numIdentity, x : numIdentity 42, y } { 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, id2 }
"# "#
), ),
"{ id1 : q -> q, id2 : q -> q }", "{ id1 : q -> q, id2 : a -> a }",
); );
} }
@ -3910,7 +3910,7 @@ mod solve_expr {
{ a, b } { 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, 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 hashEq = \x, y -> hash x == hash y
"# "#
), ),
"a, a -> Bool | a has Hash", "a, b -> Bool | a has Hash, b has Hash",
) )
} }

View file

@ -11,6 +11,5 @@ roc_region = { path = "../region" }
roc_module = { path = "../module" } roc_module = { path = "../module" }
roc_error_macros = {path="../../error_macros"} roc_error_macros = {path="../../error_macros"}
roc_debug_flags = {path="../debug_flags"} roc_debug_flags = {path="../debug_flags"}
ven_ena = { path = "../../vendor/ena" }
bumpalo = { version = "3.8.0", features = ["collections"] } bumpalo = { version = "3.8.0", features = ["collections"] }
static_assertions = "1.1.0" static_assertions = "1.1.0"

View file

@ -3,7 +3,7 @@ use crate::subs::{
UnionTags, Variable, UnionTags, Variable,
}; };
use crate::types::{name_type_var, RecordField}; 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_error_macros::internal_error;
use roc_module::ident::{Lowercase, TagName}; use roc_module::ident::{Lowercase, TagName};
use roc_module::symbol::{Interns, ModuleId, Symbol}; 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, /// We only care about whether it was a single time or multiple times,
/// because single appearances get a wildcard (*) and multiple times /// because single appearances get a wildcard (*) and multiple times
/// get a generated letter ("a" etc). /// get a generated letter ("a" etc).
#[derive(Debug)]
enum Appearances { enum Appearances {
Single, Single,
Multiple, Multiple,
@ -75,7 +76,7 @@ fn find_names_needed(
subs: &mut Subs, subs: &mut Subs,
roots: &mut Vec<Variable>, roots: &mut Vec<Variable>,
root_appearances: &mut MutMap<Variable, Appearances>, root_appearances: &mut MutMap<Variable, Appearances>,
names_taken: &mut MutSet<Lowercase>, names_taken: &mut MutMap<Lowercase, Variable>,
) { ) {
use crate::subs::Content::*; use crate::subs::Content::*;
use crate::subs::FlatType::*; use crate::subs::FlatType::*;
@ -151,19 +152,27 @@ fn find_names_needed(
.. ..
} }
| FlexVar(Some(name_index)) | FlexVar(Some(name_index))
| FlexAbleVar(Some(name_index), _) => { | FlexAbleVar(Some(name_index), _)
// This root already has a name. Nothing more to do here! | RigidVar(name_index)
| RigidAbleVar(name_index, _) => {
let root = subs.get_root_key_without_compacting(variable);
// User-defined names are already taken. // User-defined names are already taken.
// We must not accidentally generate names that collide with them! // We must not accidentally generate names that collide with them!
let name = subs.field_names[name_index.index as usize].clone(); 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)) => { Structure(Apply(_, args)) => {
for index in args.into_iter() { 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 roots = Vec::new();
let mut letters_used = 0; let mut letters_used = 0;
let mut appearances = MutMap::default(); let mut appearances = MutMap::default();
let mut taken = MutSet::default(); let mut taken = MutMap::default();
// Populate names_needed // Populate names_needed
find_names_needed(variable, subs, &mut roots, &mut appearances, &mut taken); find_names_needed(variable, subs, &mut roots, &mut appearances, &mut taken);
@ -290,14 +299,14 @@ fn name_root(
letters_used: u32, letters_used: u32,
root: Variable, root: Variable,
subs: &mut Subs, subs: &mut Subs,
taken: &mut MutSet<Lowercase>, taken: &mut MutMap<Lowercase, Variable>,
) -> u32 { ) -> u32 {
let (generated_name, new_letters_used) = 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 var.as_str() == str
}); });
taken.insert(generated_name.clone()); taken.insert(generated_name.clone(), root);
set_root_name(root, generated_name, subs); 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); let old_content = subs.get_content_without_compacting(root);
match old_content { match old_content {
FlexVar(None) => { FlexVar(_) => {
let name_index = SubsIndex::push_new(&mut subs.field_names, name); let name_index = SubsIndex::push_new(&mut subs.field_names, name);
let content = FlexVar(Some(name_index)); let content = FlexVar(Some(name_index));
subs.set_content(root, content); subs.set_content(root, content);
} }
&FlexAbleVar(None, ability) => { &FlexAbleVar(_, ability) => {
let name_index = SubsIndex::push_new(&mut subs.field_names, name); let name_index = SubsIndex::push_new(&mut subs.field_names, name);
let content = FlexAbleVar(Some(name_index), ability); let content = FlexAbleVar(Some(name_index), ability);
subs.set_content(root, content); subs.set_content(root, content);
@ -335,8 +344,7 @@ fn set_root_name(root: Variable, name: Lowercase, subs: &mut Subs) {
RecursionVar { RecursionVar {
opt_name: Some(_existing), 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!"); 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!");
} }

View file

@ -8,7 +8,6 @@ use roc_module::ident::{Lowercase, TagName, Uppercase};
use roc_module::symbol::Symbol; use roc_module::symbol::Symbol;
use std::fmt; use std::fmt;
use std::iter::{once, Iterator, Map}; use std::iter::{once, Iterator, Map};
use ven_ena::unify::UnifyKey;
use crate::unification_table::{Snapshot, UnificationTable}; 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 /// Used in SolvedType
#[derive(Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)] #[derive(Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
pub struct VarId(u32); pub struct VarId(u32);

View file

@ -240,6 +240,7 @@ pub enum Type {
name: Symbol, name: Symbol,
captures: Vec<Type>, captures: Vec<Type>,
}, },
UnspecializedLambdaSet(Uls),
DelayedAlias(AliasCommon), DelayedAlias(AliasCommon),
Alias { Alias {
symbol: Symbol, symbol: Symbol,
@ -264,6 +265,17 @@ pub enum Type {
Erroneous(Problem), 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 = static mut TYPE_CLONE_COUNT: std::sync::atomic::AtomicUsize =
std::sync::atomic::AtomicUsize::new(0); std::sync::atomic::AtomicUsize::new(0);
@ -297,6 +309,7 @@ impl Clone for Type {
name: *name, name: *name,
captures: captures.clone(), captures: captures.clone(),
}, },
Self::UnspecializedLambdaSet(uls) => Self::UnspecializedLambdaSet(*uls),
Self::DelayedAlias(arg0) => Self::DelayedAlias(arg0.clone()), Self::DelayedAlias(arg0) => Self::DelayedAlias(arg0.clone()),
Self::Alias { Self::Alias {
symbol, symbol,
@ -367,6 +380,14 @@ impl TypeExtension {
TypeExtension::Closed => true, 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 { impl<'a> IntoIterator for &'a TypeExtension {
@ -627,6 +648,9 @@ impl fmt::Debug for Type {
Type::RangedNumber(typ, range_vars) => { Type::RangedNumber(typ, range_vars) => {
write!(f, "Ranged({:?}, {:?})", 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, _) => { RangedNumber(typ, _) => {
stack.push(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(_) => {} EmptyRec | EmptyTagUnion | Erroneous(_) => {}
} }
@ -877,6 +907,12 @@ impl Type {
RangedNumber(typ, _) => { RangedNumber(typ, _) => {
stack.push(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(_) => {} EmptyRec | EmptyTagUnion | Erroneous(_) => {}
} }
@ -974,6 +1010,7 @@ impl Type {
Ok(()) Ok(())
} }
RangedNumber(typ, _) => typ.substitute_alias(rep_symbol, rep_args, actual), RangedNumber(typ, _) => typ.substitute_alias(rep_symbol, rep_args, actual),
UnspecializedLambdaSet(..) => Ok(()),
EmptyRec | EmptyTagUnion | ClosureTag { .. } | Erroneous(_) | Variable(_) => Ok(()), EmptyRec | EmptyTagUnion | ClosureTag { .. } | Erroneous(_) | Variable(_) => Ok(()),
} }
} }
@ -1030,6 +1067,7 @@ impl Type {
Apply(symbol, _, _) if *symbol == rep_symbol => true, Apply(symbol, _, _) if *symbol == rep_symbol => true,
Apply(_, args, _) => args.iter().any(|arg| arg.contains_symbol(rep_symbol)), Apply(_, args, _) => args.iter().any(|arg| arg.contains_symbol(rep_symbol)),
RangedNumber(typ, _) => typ.contains_symbol(rep_symbol), RangedNumber(typ, _) => typ.contains_symbol(rep_symbol),
UnspecializedLambdaSet(Uls(_, sym, _)) => *sym == rep_symbol,
EmptyRec | EmptyTagUnion | ClosureTag { .. } | Erroneous(_) | Variable(_) => false, EmptyRec | EmptyTagUnion | ClosureTag { .. } | Erroneous(_) | Variable(_) => false,
} }
} }
@ -1055,6 +1093,7 @@ impl Type {
ClosureTag { name: _, captures } => { ClosureTag { name: _, captures } => {
captures.iter().any(|t| t.contains_variable(rep_variable)) captures.iter().any(|t| t.contains_variable(rep_variable))
} }
UnspecializedLambdaSet(Uls(v, _, _)) => *v == rep_variable,
RecursiveTagUnion(_, tags, ext) | TagUnion(tags, ext) => { RecursiveTagUnion(_, tags, ext) | TagUnion(tags, ext) => {
Self::contains_variable_ext(ext, rep_variable) Self::contains_variable_ext(ext, rep_variable)
|| tags || tags
@ -1344,10 +1383,19 @@ impl Type {
RangedNumber(typ, _) => { RangedNumber(typ, _) => {
typ.instantiate_aliases(region, aliases, var_store, new_lambda_set_variables); typ.instantiate_aliases(region, aliases, var_store, new_lambda_set_variables);
} }
UnspecializedLambdaSet(..) => {}
EmptyRec | EmptyTagUnion | ClosureTag { .. } | Erroneous(_) | Variable(_) => {} 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 { pub fn is_tag_union_like(&self) -> bool {
matches!( matches!(
self, self,
@ -1473,6 +1521,9 @@ fn symbols_help(initial: &Type) -> Vec<Symbol> {
RangedNumber(typ, _) => { RangedNumber(typ, _) => {
stack.push(typ); stack.push(typ);
} }
UnspecializedLambdaSet(Uls(_, _sym, _)) => {
// ignore the member symbol because unspecialized lambda sets are internal-only
}
EmptyRec | EmptyTagUnion | ClosureTag { .. } | Erroneous(_) | Variable(_) => {} EmptyRec | EmptyTagUnion | ClosureTag { .. } | Erroneous(_) | Variable(_) => {}
} }
} }
@ -1520,6 +1571,9 @@ fn variables_help(tipe: &Type, accum: &mut ImSet<Variable>) {
variables_help(t, accum); variables_help(t, accum);
} }
} }
UnspecializedLambdaSet(Uls(v, _, _)) => {
accum.insert(*v);
}
TagUnion(tags, ext) => { TagUnion(tags, ext) => {
for (_, args) in tags { for (_, args) in tags {
for x in args { for x in args {
@ -1670,6 +1724,9 @@ fn variables_help_detailed(tipe: &Type, accum: &mut VariableDetail) {
variables_help_detailed(ext, accum); variables_help_detailed(ext, accum);
} }
} }
UnspecializedLambdaSet(Uls(var, _, _)) => {
accum.type_variables.insert(*var);
}
RecursiveTagUnion(rec, tags, ext) => { RecursiveTagUnion(rec, tags, ext) => {
for (_, args) in tags { for (_, args) in tags {
for x in args { for x in args {
@ -2665,3 +2722,170 @@ pub fn gather_tags(subs: &Subs, other_fields: UnionTags, var: Variable) -> TagUn
ext, 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!(),
}
}
}

View file

@ -99,13 +99,6 @@ bitflags! {
/// ///
/// For example, t1 += [A Str] says we should "add" the tag "A Str" to the type of "t1". /// For example, t1 += [A Str] says we should "add" the tag "A Str" to the type of "t1".
const PRESENT = 1 << 1; 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)] #[cfg(debug_assertions)]
fn pretty_print(&self) -> &str { fn pretty_print(&self) -> &str {
if self.contains(Mode::EQ | Mode::RIGID_AS_FLEX) { if self.contains(Mode::EQ) {
"~*"
} else if self.contains(Mode::PRESENT | Mode::RIGID_AS_FLEX) {
"+=*"
} else if self.contains(Mode::EQ) {
"~" "~"
} else if self.contains(Mode::PRESENT) { } 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. // This #[allow] is needed in release builds, where `result` is no longer used.
#[allow(clippy::let_and_return)] #[allow(clippy::let_and_return)]
let result = match &ctx.first_desc.content { let result = match &ctx.first_desc.content {
FlexVar(opt_name) => unify_flex(subs, &ctx, opt_name, None, &ctx.second_desc.content), FlexVar(opt_name) => unify_flex(subs, &ctx, opt_name, &ctx.second_desc.content),
FlexAbleVar(opt_name, ability) => unify_flex( FlexAbleVar(opt_name, ability) => {
subs, unify_flex_able(subs, &ctx, opt_name, *ability, &ctx.second_desc.content)
&ctx, }
opt_name,
Some(*ability),
&ctx.second_desc.content,
),
RecursionVar { RecursionVar {
opt_name, opt_name,
structure, structure,
@ -383,9 +368,9 @@ fn unify_context(subs: &mut Subs, pool: &mut Pool, ctx: Context) -> Outcome {
*structure, *structure,
&ctx.second_desc.content, &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) => { 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) => { Structure(flat_type) => {
unify_structure(subs, pool, &ctx, flat_type, &ctx.second_desc.content) unify_structure(subs, pool, &ctx, flat_type, &ctx.second_desc.content)
@ -1916,7 +1901,6 @@ fn unify_rigid(
subs: &mut Subs, subs: &mut Subs,
ctx: &Context, ctx: &Context,
name: &SubsIndex<Lowercase>, name: &SubsIndex<Lowercase>,
opt_able_bound: Option<Symbol>,
other: &Content, other: &Content,
) -> Outcome { ) -> Outcome {
match other { match other {
@ -1925,8 +1909,47 @@ fn unify_rigid(
merge(subs, ctx, RigidVar(*name)) merge(subs, ctx, RigidVar(*name))
} }
FlexAbleVar(_, other_ability) => { FlexAbleVar(_, other_ability) => {
match opt_able_bound { // Mismatch - Rigid can unify with FlexAble only when the Rigid has an ability
Some(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 { if ability == *other_ability {
// The ability bounds are the same, so rigid wins! // The ability bounds are the same, so rigid wins!
merge(subs, ctx, RigidAbleVar(*name, ability)) 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(_) RigidVar(_)
| RigidAbleVar(..) | RigidAbleVar(..)
@ -2014,27 +1990,57 @@ fn unify_flex(
subs: &mut Subs, subs: &mut Subs,
ctx: &Context, ctx: &Context,
opt_name: &Option<SubsIndex<Lowercase>>, opt_name: &Option<SubsIndex<Lowercase>>,
opt_able_bound: Option<Symbol>,
other: &Content, other: &Content,
) -> Outcome { ) -> Outcome {
match other { match other {
FlexVar(other_opt_name) => { FlexVar(other_opt_name) => {
// Prefer using right's name. // Prefer using right's name.
let opt_name = opt_name.or(*other_opt_name); let opt_name = opt_name.or(*other_opt_name);
match opt_able_bound { merge(subs, ctx, FlexVar(opt_name))
Some(ability) => merge(subs, ctx, FlexAbleVar(opt_name, ability)),
None => 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) => { FlexAbleVar(opt_other_name, other_ability) => {
// Prefer the right's name when possible. // Prefer the right's name when possible.
let opt_name = (opt_other_name).or(*opt_name); let opt_name = (opt_other_name).or(*opt_name);
match opt_able_bound {
Some(ability) => {
if ability == *other_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)) merge(subs, ctx, FlexAbleVar(opt_name, ability))
} else { } else {
// Ability names differ; mismatch for now. // 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. RigidAbleVar(_, other_ability) => {
merge(subs, ctx, FlexAbleVar(opt_name, *other_ability)) if ability == *other_ability {
} merge(subs, ctx, *other)
} else {
mismatch!(%not_able, ctx.second, ability, "RigidAble {:?} vs {:?}", ability, other_ability)
} }
} }
RigidVar(_) RigidVar(_) => mismatch!("FlexAble can never unify with non-able Rigid"),
| RigidAbleVar(_, _) RecursionVar { .. } => mismatch!("FlexAble with RecursionVar"),
| RecursionVar { .. } LambdaSet(..) => mismatch!("FlexAble with LambdaSet"),
| Structure(_)
| Alias(_, _, _, _) Alias(name, args, _real_var, AliasKind::Opaque) => {
| RangedNumber(..) if args.is_empty() {
| LambdaSet(..) => { // Opaque type wins
// TODO special-case boolean here let mut outcome = merge(subs, ctx, *other);
// In all other cases, if left is flex, defer to right. outcome.must_implement_ability.push(MustImplementAbility {
merge(subs, ctx, *other) 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), Error => merge(subs, ctx, Error),

View file

@ -6,4 +6,4 @@ struct RocStr {
size_t capacity; size_t capacity;
}; };
extern struct RocStr roc__mainForHost_1_exposed(); extern void roc__mainForHost_1_exposed_generic(const struct RocStr *data);

View file

@ -21,39 +21,42 @@ func rocRealloc(ptr: UInt, _oldSize: Int, newSize: Int, _alignment: UInt) -> UIn
return UInt(bitPattern: ptr) return UInt(bitPattern: ptr)
} }
extension RocStr { func isSmallString(rocStr: RocStr) -> Bool {
var isSmallString: Bool { return rocStr.capacity < 0
capacity < 0
} }
var length: Int { func getStrLen(rocStr: RocStr) -> Int {
if isSmallString { if isSmallString(rocStr: rocStr) {
// Small String length is last in the byte of capacity. // Small String length is last in the byte of capacity.
var cap = capacity var cap = rocStr.capacity
let count = MemoryLayout.size(ofValue: cap) let count = MemoryLayout.size(ofValue: cap)
let bytes = Data(bytes: &cap, count: count) let bytes = Data(bytes: &cap, count: count)
let lastByte = bytes[count - 1] let lastByte = bytes[count - 1]
return Int(lastByte ^ 0b1000_0000) return Int(lastByte ^ 0b1000_0000)
} else { } else {
return len return rocStr.len
} }
} }
var string: String { func getSwiftString(rocStr: RocStr) -> String {
if isSmallString { let length = getStrLen(rocStr: rocStr)
let data: Data = withUnsafePointer(to: self) { ptr in
if isSmallString(rocStr: rocStr) {
let data: Data = withUnsafePointer(to: rocStr) { ptr in
Data(bytes: ptr, count: length) Data(bytes: ptr, count: length)
} }
return String(data: data, encoding: .utf8)! return String(data: data, encoding: .utf8)!
} else { } else {
let data = Data(bytes: bytes, count: len) let data = Data(bytes: rocStr.bytes, count: length)
return String(data: data, encoding: .utf8)! return String(data: data, encoding: .utf8)!
} }
} }
}
@_cdecl("main") @_cdecl("main")
func main() -> UInt8 { 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 return 0
} }

View file

@ -98,7 +98,8 @@
debugir debugir
rust rust
]); ]);
in { in
{
devShell = pkgs.mkShell { devShell = pkgs.mkShell {
buildInputs = sharedInputs ++ darwinInputs ++ linuxInputs; buildInputs = sharedInputs ++ darwinInputs ++ linuxInputs;
@ -111,6 +112,7 @@
NIXPKGS_ALLOW_UNFREE = 1; # to run the editor with NVIDIA's closed source drivers NIXPKGS_ALLOW_UNFREE = 1; # to run the editor with NVIDIA's closed source drivers
}; };
formatter = pkgs.nixpkgs-fmt;
} }
); );
} }

13
vendor/ena/Cargo.toml vendored
View file

@ -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"

View file

@ -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.

View file

@ -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.

View file

@ -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
View file

@ -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;

View file

@ -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);
}

View file

@ -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]
}
}

View file

@ -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()
}
}