use crate::types::Type;
use crate::value::{valref, SubclassKind, Use, Value, ValueRef, WeakValueRef};
use std::cell::RefCell;
use std::collections::HashMap;
use std::rc::{Rc, Weak};
#[derive(Debug, Clone)]
pub struct User {
pub value: ValueRef,
}
impl User {
pub fn new(ty: Type, vid: u64, name: String) -> Self {
let mut v = Value::new(ty).named(name).with_subclass(SubclassKind::User);
v.vid = vid;
Self { value: valref(v) }
}
pub fn from_value(val: ValueRef) -> Self {
Self { value: val }
}
pub fn named(name: &str, ty: Type) -> Self {
let v = Value::new(ty).named(name).with_subclass(SubclassKind::User);
Self { value: valref(v) }
}
pub fn get_operand(&self, idx: usize) -> Option<ValueRef> {
self.value.borrow().operand(idx)
}
pub fn get_num_operands(&self) -> usize {
self.value.borrow().get_num_operands()
}
pub fn set_operand(&mut self, idx: usize, val: ValueRef) {
let mut v = self.value.borrow_mut();
while v.operands.len() <= idx {
v.operands.push(valref(Value::new(Type::void()))); }
if idx < v.operands.len() {
let old_val = &v.operands[idx];
old_val.borrow_mut().remove_use(&self.value);
}
v.operands[idx] = val.clone();
v.num_operands = v.operands.len();
val.borrow_mut().add_use(Rc::downgrade(&self.value), idx);
}
pub fn add_operand(&mut self, val: ValueRef) {
let mut v = self.value.borrow_mut();
let idx = v.operands.len();
v.operands.push(val.clone());
v.num_operands = v.operands.len();
drop(v);
val.borrow_mut().add_use(Rc::downgrade(&self.value), idx);
}
pub fn remove_operand(&mut self, idx: usize) {
let mut v = self.value.borrow_mut();
if idx >= v.operands.len() {
return;
}
v.operands[idx].borrow_mut().remove_use(&self.value);
v.operands.remove(idx);
for (i, op) in v.operands.iter().enumerate().skip(idx) {
let uses = &mut op.borrow_mut().uses;
for u in uses.iter_mut() {
if let Some(user_rc) = u.user.upgrade() {
if Rc::ptr_eq(&user_rc, &self.value) && u.operand_no > idx {
u.operand_no = i;
}
}
}
}
v.num_operands = v.operands.len();
}
pub fn replace_operand(&mut self, old: ValueRef, new: ValueRef) -> usize {
let mut count = 0;
let mut v = self.value.borrow_mut();
for i in 0..v.operands.len() {
if Rc::ptr_eq(&v.operands[i], &old) {
old.borrow_mut().remove_use(&self.value);
v.operands[i] = Rc::clone(&new);
new.borrow_mut().add_use(Rc::downgrade(&self.value), i);
count += 1;
}
}
count
}
pub fn has_operand(&self, val: &ValueRef) -> bool {
let v = self.value.borrow();
v.operands.iter().any(|op| Rc::ptr_eq(op, val))
}
pub fn get_all_operands(&self) -> Vec<ValueRef> {
self.value.borrow().get_all_operands()
}
pub fn drop_all_references(&mut self) {
let mut v = self.value.borrow_mut();
for op in &v.operands {
op.borrow_mut().remove_use(&self.value);
}
v.operands.clear();
v.num_operands = 0;
}
pub fn is_user_of(&self, val: &ValueRef) -> bool {
self.has_operand(val)
}
pub fn as_value_ref(&self) -> ValueRef {
Rc::clone(&self.value)
}
pub fn get_name(&self) -> String {
self.value.borrow().name.clone()
}
pub fn set_name(&mut self, name: &str) {
self.value.borrow_mut().name = name.to_string();
}
pub fn get_type(&self) -> Type {
self.value.borrow().ty.clone()
}
pub fn get_users(&self) -> Vec<ValueRef> {
self.value.borrow().get_uses()
}
pub fn get_num_users(&self) -> usize {
self.value.borrow().num_uses()
}
pub fn has_users(&self) -> bool {
!self.value.borrow().use_empty()
}
pub fn replace_all_uses_with(&mut self, new_value: &ValueRef) {
self.value.borrow_mut().replace_all_uses_with(new_value);
}
}
impl User {
pub fn use_begin(&self) -> UseIterator {
UseIterator {
uses: self.value.borrow().uses.clone(),
index: 0,
}
}
pub fn use_end(&self) -> UseIterator {
UseIterator {
uses: self.value.borrow().uses.clone(),
index: self.value.borrow().uses.len(),
}
}
pub fn get_num_uses(&self) -> usize {
self.value.borrow().num_uses()
}
pub fn has_n_uses(&self, n: usize) -> bool {
self.value.borrow().has_n_uses(n)
}
pub fn has_n_uses_or_more(&self, n: usize) -> bool {
self.value.borrow().num_uses() >= n
}
pub fn has_one_use(&self) -> bool {
self.value.borrow().has_one_use()
}
pub fn get_unique_use(&self) -> Option<ValueRef> {
self.value.borrow().get_unique_use()
}
pub fn use_empty(&self) -> bool {
self.value.borrow().use_empty()
}
pub fn has_no_uses(&self) -> bool {
self.use_empty()
}
pub fn replace_uses_of_with(&mut self, from: &ValueRef, to: &ValueRef) -> usize {
let mut count = 0;
let mut v = self.value.borrow_mut();
for i in 0..v.operands.len() {
if Rc::ptr_eq(&v.operands[i], from) {
from.borrow_mut().remove_use(&self.value);
v.operands[i] = Rc::clone(to);
to.borrow_mut().add_use(Rc::downgrade(&self.value), i);
count += 1;
}
}
count
}
pub fn replace_all_uses_with_map(&mut self, new_value: &ValueRef, value_map: &mut ValueMap) {
let old_vid = self.value.borrow().vid;
let new_vid = new_value.borrow().vid;
value_map.insert(old_vid, new_vid);
self.value.borrow_mut().replace_all_uses_with(new_value);
}
pub fn has_value_handle(&self) -> bool {
self.value.borrow().has_value_handle()
}
pub fn add_value_handle(&mut self, handle: ValueHandle) {
self.value.borrow_mut().add_value_handle(handle);
}
pub fn remove_value_handle(&mut self, handle_id: u64) -> bool {
self.value.borrow_mut().remove_value_handle(handle_id)
}
pub fn get_num_value_handles(&self) -> usize {
self.value.borrow().get_num_value_handles()
}
}
#[derive(Debug, Clone)]
pub struct UseIterator {
uses: Vec<Use>,
index: usize,
}
impl UseIterator {
pub fn new(uses: Vec<Use>) -> Self {
Self { uses, index: 0 }
}
pub fn get(&self) -> Option<&Use> {
self.uses.get(self.index)
}
pub fn get_user(&self) -> Option<ValueRef> {
self.uses.get(self.index).and_then(|u| u.user.upgrade())
}
pub fn get_operand_no(&self) -> Option<usize> {
self.uses.get(self.index).map(|u| u.operand_no)
}
pub fn advance(&mut self) {
self.index += 1;
}
pub fn is_end(&self) -> bool {
self.index >= self.uses.len()
}
pub fn is_begin(&self) -> bool {
self.index == 0
}
pub fn get_use(&self) -> Option<&Use> {
self.uses.get(self.index)
}
pub fn set_operand_no(&mut self, op_no: usize) {
if let Some(u) = self.uses.get_mut(self.index) {
u.operand_no = op_no;
}
}
}
impl Iterator for UseIterator {
type Item = ValueRef;
fn next(&mut self) -> Option<Self::Item> {
while self.index < self.uses.len() {
let u = &self.uses[self.index];
self.index += 1;
if let Some(user) = u.user.upgrade() {
return Some(user);
}
}
None
}
fn size_hint(&self) -> (usize, Option<usize>) {
let remaining = self.uses.len().saturating_sub(self.index);
(0, Some(remaining))
}
}
#[derive(Debug, Clone)]
pub struct OperandIterator {
operands: Vec<ValueRef>,
index: usize,
}
impl OperandIterator {
pub fn new(operands: Vec<ValueRef>) -> Self {
Self { operands, index: 0 }
}
pub fn get(&self) -> Option<&ValueRef> {
self.operands.get(self.index)
}
pub fn get_operand_use(&self, user: &ValueRef) -> Option<Use> {
self.operands.get(self.index).map(|_| Use {
user: Rc::downgrade(user),
operand_no: self.index,
})
}
pub fn advance(&mut self) {
self.index += 1;
}
pub fn is_end(&self) -> bool {
self.index >= self.operands.len()
}
pub fn is_begin(&self) -> bool {
self.index == 0
}
pub fn reset(&mut self) {
self.index = 0;
}
}
impl Iterator for OperandIterator {
type Item = ValueRef;
fn next(&mut self) -> Option<Self::Item> {
if self.index < self.operands.len() {
let val = Rc::clone(&self.operands[self.index]);
self.index += 1;
Some(val)
} else {
None
}
}
fn size_hint(&self) -> (usize, Option<usize>) {
let remaining = self.operands.len().saturating_sub(self.index);
(remaining, Some(remaining))
}
fn count(self) -> usize {
self.operands.len().saturating_sub(self.index)
}
}
impl ExactSizeIterator for OperandIterator {}
impl User {
pub fn operand_begin(&self) -> OperandIterator {
OperandIterator::new(self.value.borrow().operands.clone())
}
pub fn operand_end(&self) -> OperandIterator {
let ops = self.value.borrow().operands.clone();
OperandIterator {
index: ops.len(),
operands: ops,
}
}
pub fn get_operand_use(&self, idx: usize) -> Option<Use> {
let v = self.value.borrow();
if idx < v.operands.len() {
Some(Use {
user: Rc::downgrade(&self.value),
operand_no: idx,
})
} else {
None
}
}
pub fn has_operand_value(&self, val: &ValueRef) -> bool {
self.has_operand(val)
}
pub fn get_use(&self, idx: usize) -> Option<Use> {
self.value.borrow().uses.get(idx).cloned()
}
pub fn get_user(&self, idx: usize) -> Option<ValueRef> {
self.value
.borrow()
.uses
.get(idx)
.and_then(|u| u.user.upgrade())
}
pub fn get_use_operand_no(&self, idx: usize) -> Option<usize> {
self.value.borrow().uses.get(idx).map(|u| u.operand_no)
}
pub fn mutate_types(&mut self, new_type: Type) {
let mut v = self.value.borrow_mut();
v.mutate_type(new_type.clone());
for op in &v.operands {
op.borrow_mut().mutate_type(new_type.clone());
}
}
pub fn mutate_operand_types(&mut self, new_type: Type) {
let v = self.value.borrow();
for op in &v.operands {
op.borrow_mut().mutate_type(new_type.clone());
}
}
}
#[derive(Debug, Clone, Default)]
pub struct ValueMap {
map: HashMap<u64, u64>,
}
impl ValueMap {
pub fn new() -> Self {
Self {
map: HashMap::new(),
}
}
pub fn insert(&mut self, old_vid: u64, new_vid: u64) {
self.map.insert(old_vid, new_vid);
}
pub fn lookup(&self, vid: u64) -> u64 {
let mut current = vid;
for _ in 0..64 {
match self.map.get(¤t) {
Some(&next) => current = next,
None => break,
}
}
current
}
pub fn contains(&self, vid: u64) -> bool {
self.map.contains_key(&vid)
}
pub fn len(&self) -> usize {
self.map.len()
}
pub fn is_empty(&self) -> bool {
self.map.is_empty()
}
pub fn clear(&mut self) {
self.map.clear();
}
pub fn resolve_all(&mut self) {
let keys: Vec<u64> = self.map.keys().cloned().collect();
for key in keys {
let resolved = self.lookup(key);
self.map.insert(key, resolved);
}
}
}
impl Use {
pub fn get_user(&self) -> Option<ValueRef> {
self.user.upgrade()
}
pub fn get_operand_no(&self) -> usize {
self.operand_no
}
pub fn set_operand_no(&mut self, op_no: usize) {
self.operand_no = op_no;
}
pub fn get(&self) -> Option<ValueRef> {
self.user
.upgrade()
.and_then(|user| user.borrow().operand(self.operand_no))
}
pub fn set(&self, val: ValueRef) -> bool {
if let Some(user_rc) = self.user.upgrade() {
let mut user = user_rc.borrow_mut();
if self.operand_no < user.operands.len() {
user.operands[self.operand_no]
.borrow_mut()
.remove_use(&user_rc);
user.operands[self.operand_no] = Rc::clone(&val);
val.borrow_mut()
.add_use(Rc::downgrade(&user_rc), self.operand_no);
return true;
}
}
false
}
pub fn get_value_type(&self) -> Option<Type> {
self.get().map(|v| v.borrow().ty.clone())
}
pub fn is_valid(&self) -> bool {
self.user.upgrade().is_some()
}
pub fn new(user: &ValueRef, operand_no: usize) -> Self {
Self {
user: Rc::downgrade(user),
operand_no,
}
}
pub fn get_user_vid(&self) -> Option<u64> {
self.user.upgrade().map(|u| u.borrow().vid)
}
pub fn get_user_name(&self) -> Option<String> {
self.user.upgrade().map(|u| u.borrow().name.clone())
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum ValueHandleKind {
Weak,
Callback,
Asserting,
Tracking,
Poisoning,
}
#[derive(Clone)]
pub enum ValueHandleCallback {
None,
#[allow(clippy::type_complexity)]
Func(Rc<dyn Fn(&ValueRef, Option<&ValueRef>)>),
Assert(String),
Tracking(Rc<RefCell<Option<ValueRef>>>),
}
impl std::fmt::Debug for ValueHandleCallback {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::None => write!(f, "None"),
Self::Func(_) => write!(f, "Func(...)"),
Self::Assert(msg) => write!(f, "Assert({})", msg),
Self::Tracking(_) => write!(f, "Tracking(...)"),
}
}
}
#[derive(Debug, Clone)]
pub struct ValueHandle {
pub id: u64,
pub kind: ValueHandleKind,
pub callback: ValueHandleCallback,
}
impl ValueHandle {
pub fn new_weak(id: u64) -> Self {
Self {
id,
kind: ValueHandleKind::Weak,
callback: ValueHandleCallback::None,
}
}
pub fn new_callback<F>(id: u64, f: F) -> Self
where
F: Fn(&ValueRef, Option<&ValueRef>) + 'static,
{
Self {
id,
kind: ValueHandleKind::Callback,
callback: ValueHandleCallback::Func(Rc::new(f)),
}
}
pub fn new_asserting(id: u64, msg: impl Into<String>) -> Self {
Self {
id,
kind: ValueHandleKind::Asserting,
callback: ValueHandleCallback::Assert(msg.into()),
}
}
pub fn new_tracking(id: u64, cell: Rc<RefCell<Option<ValueRef>>>) -> Self {
Self {
id,
kind: ValueHandleKind::Tracking,
callback: ValueHandleCallback::Tracking(cell),
}
}
pub fn new_poisoning(id: u64) -> Self {
Self {
id,
kind: ValueHandleKind::Poisoning,
callback: ValueHandleCallback::None,
}
}
pub fn on_rauw(&self, old_val: &ValueRef, new_val: &ValueRef) {
match &self.callback {
ValueHandleCallback::Func(f) => f(old_val, Some(new_val)),
ValueHandleCallback::Tracking(cell) => {
*cell.borrow_mut() = Some(Rc::clone(new_val));
}
ValueHandleCallback::Assert(_) => {
}
ValueHandleCallback::None => {}
}
}
pub fn on_delete(&self, old_val: &ValueRef) {
match &self.callback {
ValueHandleCallback::Func(f) => f(old_val, None),
ValueHandleCallback::Assert(msg) => {
panic!(
"AssertingVH: value '{}' (vid={}) was deleted without being RAUW'd: {}",
old_val.borrow().name,
old_val.borrow().vid,
msg
);
}
ValueHandleCallback::Tracking(cell) => {
*cell.borrow_mut() = None;
}
ValueHandleCallback::None => {}
}
}
}
#[derive(Debug, Clone)]
pub struct WeakVH {
weak_ref: WeakValueRef,
}
impl WeakVH {
pub fn new(val: &ValueRef) -> Self {
Self {
weak_ref: Rc::downgrade(val),
}
}
pub fn get(&self) -> Option<ValueRef> {
self.weak_ref.upgrade()
}
pub fn is_alive(&self) -> bool {
self.weak_ref.upgrade().is_some()
}
pub fn reset(&mut self, val: &ValueRef) {
self.weak_ref = Rc::downgrade(val);
}
pub fn all_uses_replaced_with(&mut self, _new_val: &ValueRef) {
}
}
#[derive(Clone)]
pub struct CallbackVH {
handle: ValueHandle,
tracked: WeakValueRef,
}
impl CallbackVH {
pub fn new<F>(id: u64, val: &ValueRef, f: F) -> Self
where
F: Fn(&ValueRef, Option<&ValueRef>) + 'static,
{
Self {
handle: ValueHandle::new_callback(id, f),
tracked: Rc::downgrade(val),
}
}
pub fn get(&self) -> Option<ValueRef> {
self.tracked.upgrade()
}
pub fn is_alive(&self) -> bool {
self.tracked.upgrade().is_some()
}
pub fn reset(&mut self, val: &ValueRef) {
self.tracked = Rc::downgrade(val);
}
pub fn all_uses_replaced_with(&self, new_val: &ValueRef) {
if let Some(old) = self.tracked.upgrade() {
self.handle.on_rauw(&old, new_val);
}
}
pub fn deleted(&self) {
if let Some(old) = self.tracked.upgrade() {
self.handle.on_delete(&old);
}
}
}
impl std::fmt::Debug for CallbackVH {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("CallbackVH")
.field("handle_id", &self.handle.id)
.field("alive", &self.is_alive())
.finish()
}
}
#[derive(Debug, Clone)]
pub struct AssertingVH {
handle: ValueHandle,
tracked: WeakValueRef,
was_rauwed: Rc<RefCell<bool>>,
}
impl AssertingVH {
pub fn new(id: u64, val: &ValueRef, msg: impl Into<String>) -> Self {
Self {
handle: ValueHandle::new_asserting(id, msg),
tracked: Rc::downgrade(val),
was_rauwed: Rc::new(RefCell::new(false)),
}
}
pub fn new_default(id: u64, val: &ValueRef) -> Self {
let name = val.borrow().name.clone();
Self::new(id, val, format!("AssertingVH: value '{}' deleted", name))
}
pub fn get(&self) -> Option<ValueRef> {
self.tracked.upgrade()
}
pub fn is_alive(&self) -> bool {
self.tracked.upgrade().is_some()
}
pub fn all_uses_replaced_with(&mut self, _new_val: &ValueRef) {
*self.was_rauwed.borrow_mut() = true;
}
pub fn deleted(&self) {
if !*self.was_rauwed.borrow() {
if let Some(old) = self.tracked.upgrade() {
self.handle.on_delete(&old);
}
}
}
}
#[derive(Debug, Clone)]
pub struct TrackingVH {
cell: Rc<RefCell<Option<ValueRef>>>,
}
impl TrackingVH {
pub fn new(val: &ValueRef) -> Self {
let cell = Rc::new(RefCell::new(Some(Rc::clone(val))));
let mut v = val.borrow_mut();
let handle = ValueHandle::new_tracking(v.vid, Rc::clone(&cell));
v.add_value_handle(handle);
Self { cell }
}
pub fn get(&self) -> Option<ValueRef> {
self.cell.borrow().clone()
}
pub fn is_alive(&self) -> bool {
self.cell.borrow().is_some()
}
pub fn reset(&mut self, val: &ValueRef) {
let mut v = val.borrow_mut();
let handle = ValueHandle::new_tracking(v.vid, Rc::clone(&self.cell));
v.add_value_handle(handle);
*self.cell.borrow_mut() = Some(Rc::clone(val));
}
pub fn all_uses_replaced_with(&self, new_val: &ValueRef) {
*self.cell.borrow_mut() = Some(Rc::clone(new_val));
}
pub fn deleted(&self) {
*self.cell.borrow_mut() = None;
}
pub fn unwrap(&self) -> ValueRef {
self.get().expect("TrackingVH: value has been deleted")
}
}
#[derive(Debug, Clone)]
pub struct PoisoningVH {
value: WeakValueRef,
poisoned: Rc<RefCell<bool>>,
}
impl PoisoningVH {
pub fn new(val: &ValueRef) -> Self {
Self {
value: Rc::downgrade(val),
poisoned: Rc::new(RefCell::new(false)),
}
}
pub fn get(&self) -> Option<ValueRef> {
if *self.poisoned.borrow() {
return None;
}
self.value.upgrade()
}
pub fn is_poisoned(&self) -> bool {
*self.poisoned.borrow()
}
pub fn all_uses_replaced_with(&self, _new_val: &ValueRef) {
*self.poisoned.borrow_mut() = true;
}
pub fn deleted(&self) {
*self.poisoned.borrow_mut() = true;
}
pub fn reset(&mut self, val: &ValueRef) {
self.value = Rc::downgrade(val);
*self.poisoned.borrow_mut() = false;
}
}
impl crate::value::Value {
pub fn has_value_handle(&self) -> bool {
(self.subclass_data & 0x8000_0000) != 0
}
pub fn get_num_value_handles(&self) -> usize {
if self.has_value_handle() {
self.metadata.get(&0xFFFF_FFFE).copied().unwrap_or(0) as usize
} else {
0
}
}
pub fn add_value_handle(&mut self, _handle: ValueHandle) {
self.subclass_data |= 0x8000_0000;
let count = self.get_num_value_handles() + 1;
self.metadata.insert(0xFFFF_FFFE, count as u32);
}
pub fn remove_value_handle(&mut self, _handle_id: u64) -> bool {
if !self.has_value_handle() {
return false;
}
let count = self.get_num_value_handles();
if count > 0 {
let new_count = count - 1;
if new_count == 0 {
self.subclass_data &= !0x8000_0000;
self.metadata.remove(&0xFFFF_FFFE);
} else {
self.metadata.insert(0xFFFF_FFFE, new_count as u32);
}
true
} else {
false
}
}
pub fn notify_handles_rauw(&self, _new_val: &ValueRef) {
if !self.has_value_handle() {
return;
}
}
pub fn notify_handles_deleted(&self) {
if !self.has_value_handle() {
return;
}
}
}
pub fn batch_replace_all_uses_with(
replacements: &HashMap<u64, ValueRef>,
value_map: &mut ValueMap,
) {
let mut ordered: Vec<(u64, ValueRef)> = replacements
.iter()
.map(|(k, v)| (*k, Rc::clone(v)))
.collect();
for (old_vid, new_val) in &ordered {
value_map.insert(*old_vid, new_val.borrow().vid);
}
value_map.resolve_all();
}
#[cfg(test)]
mod tests {
use super::*;
fn make_val(name: &str, ty: Type) -> ValueRef {
valref(Value::new(ty).named(name))
}
#[test]
fn test_user_new() {
let u = User::new(Type::i32(), 1, "myuser".to_string());
assert_eq!(u.get_name(), "myuser");
assert_eq!(u.get_num_operands(), 0);
}
#[test]
fn test_user_named() {
let u = User::named("hello", Type::void());
assert_eq!(u.get_name(), "hello");
}
#[test]
fn test_from_value() {
let val = valref(
Value::new(Type::i32())
.named("val")
.with_subclass(SubclassKind::User),
);
let u = User::from_value(val.clone());
assert!(Rc::ptr_eq(&u.value, &val));
}
#[test]
fn test_add_and_get_operand() {
let mut u = User::named("user", Type::void());
let op1 = make_val("op1", Type::i32());
let op2 = make_val("op2", Type::i64());
u.add_operand(op1.clone());
u.add_operand(op2.clone());
assert_eq!(u.get_num_operands(), 2);
let got0 = u.get_operand(0).unwrap();
assert!(Rc::ptr_eq(&got0, &op1));
let got1 = u.get_operand(1).unwrap();
assert!(Rc::ptr_eq(&got1, &op2));
assert!(u.get_operand(2).is_none());
}
#[test]
fn test_set_operand() {
let mut u = User::named("user", Type::void());
let op1 = make_val("op1", Type::i32());
let op2 = make_val("op2", Type::i32());
u.add_operand(op1.clone());
assert_eq!(u.get_num_operands(), 1);
u.set_operand(0, op2.clone());
assert!(Rc::ptr_eq(&u.get_operand(0).unwrap(), &op2));
}
#[test]
fn test_set_operand_grows() {
let mut u = User::named("user", Type::void());
let op = make_val("op", Type::i32());
u.set_operand(2, op.clone());
assert!(u.get_num_operands() >= 3);
assert!(Rc::ptr_eq(&u.get_operand(2).unwrap(), &op));
}
#[test]
fn test_remove_operand() {
let mut u = User::named("user", Type::void());
let op1 = make_val("op1", Type::i32());
let op2 = make_val("op2", Type::i32());
let op3 = make_val("op3", Type::i32());
u.add_operand(op1.clone());
u.add_operand(op2.clone());
u.add_operand(op3.clone());
assert_eq!(u.get_num_operands(), 3);
u.remove_operand(1);
assert_eq!(u.get_num_operands(), 2);
assert!(Rc::ptr_eq(&u.get_operand(0).unwrap(), &op1));
assert!(Rc::ptr_eq(&u.get_operand(1).unwrap(), &op3));
}
#[test]
fn test_remove_operand_out_of_bounds_no_panic() {
let mut u = User::named("user", Type::void());
let op = make_val("op", Type::i32());
u.add_operand(op);
u.remove_operand(99); assert_eq!(u.get_num_operands(), 1);
}
#[test]
fn test_has_operand() {
let mut u = User::named("user", Type::void());
let op1 = make_val("op1", Type::i32());
let op2 = make_val("op2", Type::i32());
u.add_operand(op1.clone());
assert!(u.has_operand(&op1));
assert!(!u.has_operand(&op2));
}
#[test]
fn test_replace_operand() {
let mut u = User::named("user", Type::void());
let old1 = make_val("old1", Type::i32());
let old2 = make_val("old2", Type::i32());
let new = make_val("new", Type::i32());
u.add_operand(old1.clone());
u.add_operand(old2.clone());
u.add_operand(old1.clone());
let count = u.replace_operand(old1.clone(), new.clone());
assert_eq!(count, 2);
assert!(!u.has_operand(&old1));
assert!(u.has_operand(&old2));
assert!(u.has_operand(&new));
}
#[test]
fn test_drop_all_references() {
let mut u = User::named("user", Type::void());
let op1 = make_val("op1", Type::i32());
let op2 = make_val("op2", Type::i32());
u.add_operand(op1.clone());
u.add_operand(op2.clone());
assert!(op1.borrow().num_uses() > 0);
assert!(op2.borrow().num_uses() > 0);
u.drop_all_references();
assert_eq!(u.get_num_operands(), 0);
}
#[test]
fn test_is_user_of() {
let mut u = User::named("user", Type::void());
let op = make_val("op", Type::i32());
u.add_operand(op.clone());
assert!(u.is_user_of(&op));
let other = make_val("other", Type::i32());
assert!(!u.is_user_of(&other));
}
#[test]
fn test_get_users() {
let u = User::named("inner", Type::void());
let outer = User::named("outer", Type::void());
let mut outer_mut = outer;
outer_mut.add_operand(u.as_value_ref());
let users = u.get_users();
assert_eq!(users.len(), 1);
}
#[test]
fn test_replace_all_uses_with() {
let mut old_user = User::named("old", Type::i32());
let new_val = make_val("new_val", Type::i32());
let mut consumer = User::named("consumer", Type::void());
consumer.add_operand(old_user.as_value_ref());
old_user.replace_all_uses_with(&new_val);
assert!(consumer.has_operand(&new_val));
}
#[test]
fn test_get_name_and_set_name() {
let mut u = User::named("original", Type::void());
assert_eq!(u.get_name(), "original");
u.set_name("renamed");
assert_eq!(u.get_name(), "renamed");
}
#[test]
fn test_get_type() {
let u = User::named("user", Type::i64());
let ty = u.get_type();
assert!(ty.is_integer());
assert_eq!(ty.integer_bit_width(), 64);
}
#[test]
fn test_get_all_operands() {
let mut u = User::named("user", Type::void());
let op1 = make_val("op1", Type::i32());
let op2 = make_val("op2", Type::i32());
u.add_operand(op1.clone());
u.add_operand(op2.clone());
let all = u.get_all_operands();
assert_eq!(all.len(), 2);
assert!(Rc::ptr_eq(&all[0], &op1));
assert!(Rc::ptr_eq(&all[1], &op2));
}
#[test]
fn test_use_list_consistency() {
let mut u = User::named("user", Type::void());
let op = make_val("op", Type::i32());
assert_eq!(op.borrow().num_uses(), 0);
u.add_operand(op.clone());
assert_eq!(op.borrow().num_uses(), 1);
u.remove_operand(0);
assert_eq!(op.borrow().num_uses(), 0);
}
}