use crate::{Aggregate, Const, Type};
use std::sync::atomic::{AtomicUsize, Ordering, ATOMIC_USIZE_INIT};
pub trait Value {
fn id(&self) -> ValueId;
fn ty(&self) -> Type;
fn name(&self) -> Option<&str> {
None
}
fn is_global(&self) -> bool {
false
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum ValueRef {
Inst(InstRef),
Block(BlockRef),
Argument(ArgumentRef),
Function(FunctionRef),
Process(ProcessRef),
Entity(EntityRef),
Global,
Const(Const),
Aggregate(Aggregate),
}
impl ValueRef {
fn desc(&self) -> &'static str {
match *self {
ValueRef::Inst(_) => "ValueRef::Inst",
ValueRef::Block(_) => "ValueRef::Block",
ValueRef::Argument(_) => "ValueRef::Argument",
ValueRef::Function(_) => "ValueRef::Function",
ValueRef::Process(_) => "ValueRef::Process",
ValueRef::Entity(_) => "ValueRef::Entity",
ValueRef::Global => "ValueRef::Global",
ValueRef::Const(_) => "ValueRef::Const",
ValueRef::Aggregate(_) => "ValueRef::Aggregate",
}
}
pub fn into_const(self) -> Const {
match self {
ValueRef::Const(k) => k,
x => panic!("into_const called on {}", x.desc()),
}
}
pub fn as_const(&self) -> &Const {
match *self {
ValueRef::Const(ref k) => k,
_ => panic!("as_const called on {}", self.desc()),
}
}
pub fn maybe_const(&self) -> Option<&Const> {
match *self {
ValueRef::Const(ref k) => Some(k),
_ => None,
}
}
pub fn id(&self) -> Option<ValueId> {
match *self {
ValueRef::Inst(InstRef(id))
| ValueRef::Block(BlockRef(id))
| ValueRef::Argument(ArgumentRef(id))
| ValueRef::Function(FunctionRef(id))
| ValueRef::Process(ProcessRef(id))
| ValueRef::Entity(EntityRef(id)) => Some(id),
_ => None,
}
}
pub fn unwrap_inst(&self) -> InstRef {
match *self {
ValueRef::Inst(x) => x,
_ => panic!("unwrap_inst called on {}", self.desc()),
}
}
pub fn unwrap_block(&self) -> BlockRef {
match *self {
ValueRef::Block(x) => x,
_ => panic!("unwrap_block called on {}", self.desc()),
}
}
pub fn unwrap_argument(&self) -> ArgumentRef {
match *self {
ValueRef::Argument(x) => x,
_ => panic!("unwrap_argument called on {}", self.desc()),
}
}
pub fn unwrap_function(&self) -> FunctionRef {
match *self {
ValueRef::Function(x) => x,
_ => panic!("unwrap_function called on {}", self.desc()),
}
}
pub fn unwrap_process(&self) -> ProcessRef {
match *self {
ValueRef::Process(x) => x,
_ => panic!("unwrap_process called on {}", self.desc()),
}
}
pub fn unwrap_entity(&self) -> EntityRef {
match *self {
ValueRef::Entity(x) => x,
_ => panic!("unwrap_entity called on {}", self.desc()),
}
}
pub fn unwrap_const(&self) -> &Const {
match *self {
ValueRef::Const(ref x) => x,
_ => panic!("unwrap_const called on {}", self.desc()),
}
}
pub fn unwrap_aggregate(&self) -> &Aggregate {
match *self {
ValueRef::Aggregate(ref x) => x,
_ => panic!("unwrap_aggregate called on {}", self.desc()),
}
}
}
#[derive(Clone, Copy, Eq, PartialEq, Ord, PartialOrd, Hash)]
pub struct ValueId(usize);
impl ValueId {
pub fn alloc() -> ValueId {
ValueId(NEXT_VALUE_ID.fetch_add(1, Ordering::SeqCst) + 1)
}
pub fn as_usize(self) -> usize {
self.0
}
}
impl std::fmt::Debug for ValueId {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
write!(f, "{}", self)
}
}
impl std::fmt::Display for ValueId {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
write!(f, "{}", self.as_usize())
}
}
static NEXT_VALUE_ID: AtomicUsize = ATOMIC_USIZE_INIT;
pub const INLINE_VALUE_ID: ValueId = ValueId(0);
macro_rules! declare_ref {
($name:ident, $variant:ident) => {
#[derive(Clone, Copy, Eq, PartialEq, Ord, PartialOrd, Hash)]
pub struct $name(ValueId);
impl $name {
pub fn new(id: ValueId) -> $name {
$name(id)
}
}
impl Into<ValueRef> for $name {
fn into(self) -> ValueRef {
ValueRef::$variant(self)
}
}
impl Into<ValueId> for $name {
fn into(self) -> ValueId {
self.0
}
}
impl std::fmt::Display for $name {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
write!(f, "{}", self.0)
}
}
impl std::fmt::Debug for $name {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
write!(f, "{}({})", stringify!($name), self.0)
}
}
};
}
declare_ref!(FunctionRef, Function);
declare_ref!(ProcessRef, Process);
declare_ref!(EntityRef, Entity);
declare_ref!(ArgumentRef, Argument);
declare_ref!(BlockRef, Block);
declare_ref!(InstRef, Inst);
pub trait Context: AsContext {
fn try_value(&self, value: &ValueRef) -> Option<&Value>;
fn parent(&self) -> Option<&Context> {
None
}
fn value(&self, value: &ValueRef) -> &Value {
self.try_value(value)
.or_else(|| self.parent().map(|p| p.value(value)))
.expect("unable to resolve ValueRef")
}
fn ty(&self, value: &ValueRef) -> Type {
value
.maybe_const()
.map(|k| k.ty())
.unwrap_or_else(|| self.value(value).ty())
}
fn name(&self, value: &ValueRef) -> Option<&str> {
self.value(value).name()
}
}
pub trait AsContext {
fn as_context(&self) -> &Context;
}
impl<T: Context> AsContext for T {
fn as_context(&self) -> &Context {
self
}
}
#[cfg(test)]
mod tests {
use crate::*;
#[test]
fn type_of_const() {
let m = Module::new();
let ctx = ModuleContext::new(&m);
let value: ValueRef = const_int(32, 0.into()).into();
assert_eq!(ctx.ty(&value), int_ty(32));
}
}