use crate::{crate_prelude::*, hir::HirNode, ParamEnv};
use std::fmt::{self, Display, Formatter};
pub type Type<'t> = &'t TypeKind<'t>;
#[derive(Debug, PartialEq, Eq, Hash)]
pub enum TypeKind<'t> {
Error,
Void,
Time,
Bit(Domain),
Int(usize, Domain),
Named(Spanned<Name>, NodeId, Type<'t>),
Struct(NodeId),
PackedArray(usize, Type<'t>),
BitScalar { domain: Domain, sign: Sign },
BitVector {
domain: Domain,
sign: Sign,
range: Range,
dubbed: bool,
},
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub enum Domain {
TwoValued,
FourValued,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
#[allow(missing_docs)]
pub enum Sign {
Signed,
Unsigned,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub struct Range {
pub size: usize,
pub dir: RangeDir,
pub offset: isize,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub enum RangeDir {
Up,
Down,
}
impl<'t> TypeKind<'t> {
pub fn is_error(&self) -> bool {
match *self {
TypeKind::Named(_, _, ty) => ty.is_error(),
TypeKind::Error => true,
_ => false,
}
}
pub fn is_void(&self) -> bool {
match *self {
TypeKind::Named(_, _, ty) => ty.is_void(),
TypeKind::Void => true,
_ => false,
}
}
pub fn is_struct(&self) -> bool {
match *self {
TypeKind::Named(_, _, ty) => ty.is_struct(),
TypeKind::Struct(..) => true,
_ => false,
}
}
pub fn is_array(&self) -> bool {
match *self {
TypeKind::Named(_, _, ty) => ty.is_array(),
TypeKind::PackedArray(..) => true,
_ => false,
}
}
pub fn get_struct_def(&self) -> Option<NodeId> {
match *self {
TypeKind::Named(_, _, ty) => ty.get_struct_def(),
TypeKind::Struct(id) => Some(id),
_ => None,
}
}
pub fn get_array_element(&self) -> Option<Type<'t>> {
match *self {
TypeKind::Named(_, _, ty) => ty.get_array_element(),
TypeKind::PackedArray(_, e) => Some(e),
_ => None,
}
}
pub fn get_array_length(&self) -> Option<usize> {
match *self {
TypeKind::Named(_, _, ty) => ty.get_array_length(),
TypeKind::PackedArray(l, _) => Some(l),
_ => None,
}
}
pub fn width(&self) -> usize {
match *self {
TypeKind::Bit(_) => 1,
TypeKind::Int(w, _) => w,
TypeKind::Named(_, _, ty) => ty.width(),
TypeKind::BitScalar { .. } => 1,
TypeKind::BitVector { range, .. } => range.size,
_ => panic!("{:?} has no width", self),
}
}
pub fn is_bit_vector(&self) -> bool {
match *self {
TypeKind::Named(_, _, ty) => ty.is_bit_vector(),
TypeKind::BitVector { .. } => true,
_ => false,
}
}
pub fn is_bit_scalar(&self) -> bool {
match *self {
TypeKind::Named(_, _, ty) => ty.is_bit_scalar(),
TypeKind::BitScalar { .. } => true,
_ => false,
}
}
pub fn resolve_name(&'t self) -> Type<'t> {
match *self {
TypeKind::Named(_, _, ty) => ty.resolve_name(),
_ => self,
}
}
pub fn get_value_domain(&self) -> Option<Domain> {
match *self {
TypeKind::Bit(d) => Some(d),
TypeKind::Int(_, d) => Some(d),
TypeKind::BitScalar { domain, .. } => Some(domain),
TypeKind::BitVector { domain, .. } => Some(domain),
_ => None,
}
}
pub fn get_sign(&self) -> Option<Sign> {
match *self {
TypeKind::Bit(..) => Some(Sign::Unsigned),
TypeKind::Int(..) => Some(Sign::Unsigned),
TypeKind::BitScalar { sign, .. } => Some(sign),
TypeKind::BitVector { sign, .. } => Some(sign),
_ => None,
}
}
pub fn is_unsigned(&self) -> bool {
self.get_sign() == Some(Sign::Unsigned)
}
pub fn is_signed(&self) -> bool {
self.get_sign() == Some(Sign::Signed)
}
pub fn change_sign<'gcx>(&'gcx self, cx: &impl Context<'gcx>, sign: Sign) -> Type<'gcx> {
match *self {
TypeKind::BitScalar { domain, .. } => {
cx.intern_type(TypeKind::BitScalar { domain, sign })
}
TypeKind::BitVector {
domain,
range,
dubbed,
..
} => cx.intern_type(TypeKind::BitVector {
domain,
sign,
range,
dubbed,
}),
_ => self,
}
}
}
impl<'t> Display for TypeKind<'t> {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
match *self {
TypeKind::Error => write!(f, "<error>"),
TypeKind::Void => write!(f, "void"),
TypeKind::Time => write!(f, "time"),
TypeKind::Bit(Domain::TwoValued) => write!(f, "bit"),
TypeKind::Bit(Domain::FourValued) => write!(f, "logic"),
TypeKind::Int(32, Domain::TwoValued) => write!(f, "int"),
TypeKind::Int(32, Domain::FourValued) => write!(f, "integer"),
TypeKind::Int(width, Domain::TwoValued) => write!(f, "int<{}>", width),
TypeKind::Int(width, Domain::FourValued) => write!(f, "integer<{}>", width),
TypeKind::Named(name, ..) => write!(f, "{}", name.value),
TypeKind::Struct(_) => write!(f, "struct"),
TypeKind::PackedArray(length, ty) => write!(f, "{} [{}:0]", ty, length - 1),
TypeKind::BitScalar { domain, sign } => {
write!(f, "{}", domain.bit_name())?;
if sign == Sign::Signed {
write!(f, " signed")?;
}
Ok(())
}
TypeKind::BitVector {
domain,
sign,
range,
dubbed,
} => {
if dubbed {
let dub = match range.size {
8 if domain == Domain::TwoValued => Some("byte"),
16 if domain == Domain::TwoValued => Some("shortint"),
32 if domain == Domain::TwoValued => Some("int"),
32 if domain == Domain::FourValued => Some("integer"),
64 if domain == Domain::TwoValued => Some("longint"),
_ => None,
};
if let Some(dub) = dub {
write!(f, "{}", dub)?;
if sign != Sign::Signed {
write!(f, " {}", sign)?;
}
return Ok(());
}
}
write!(f, "{}", domain.bit_name())?;
if sign != Sign::Unsigned {
write!(f, " {}", sign)?;
}
write!(f, " {}", range)
}
}
}
}
impl Domain {
pub fn bit_name(&self) -> &'static str {
match self {
Domain::TwoValued => "bit",
Domain::FourValued => "logic",
}
}
pub fn bit_type(&self) -> &'static TypeKind<'static> {
match self {
Domain::TwoValued => &BIT_TYPE,
Domain::FourValued => &LOGIC_TYPE,
}
}
}
impl Sign {
pub fn is_unsigned(&self) -> bool {
*self == Sign::Unsigned
}
pub fn is_signed(&self) -> bool {
*self == Sign::Signed
}
}
impl Display for Sign {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
match self {
Sign::Signed => write!(f, "signed"),
Sign::Unsigned => write!(f, "unsigned"),
}
}
}
impl Display for Range {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
let lo = self.offset;
let hi = lo + self.size as isize - 1;
let (lhs, rhs) = match self.dir {
RangeDir::Up => (lo, hi),
RangeDir::Down => (hi, lo),
};
write!(f, "[{}:{}]", lhs, rhs)
}
}
pub static ERROR_TYPE: TypeKind<'static> = TypeKind::Error;
pub static VOID_TYPE: TypeKind<'static> = TypeKind::Void;
pub static TIME_TYPE: TypeKind<'static> = TypeKind::Time;
pub static BIT_TYPE: TypeKind<'static> = TypeKind::BitScalar {
domain: ty::Domain::TwoValued,
sign: Sign::Unsigned,
};
pub static LOGIC_TYPE: TypeKind<'static> = TypeKind::BitScalar {
domain: ty::Domain::FourValued,
sign: Sign::Unsigned,
};
pub static BYTE_TYPE: TypeKind<'static> = TypeKind::BitVector {
domain: Domain::TwoValued,
sign: Sign::Signed,
range: Range {
size: 8,
dir: RangeDir::Down,
offset: 0isize,
},
dubbed: true,
};
pub static SHORTINT_TYPE: TypeKind<'static> = TypeKind::BitVector {
domain: Domain::TwoValued,
sign: Sign::Signed,
range: Range {
size: 16,
dir: RangeDir::Down,
offset: 0isize,
},
dubbed: true,
};
pub static INT_TYPE: TypeKind<'static> = TypeKind::BitVector {
domain: Domain::TwoValued,
sign: Sign::Signed,
range: Range {
size: 32,
dir: RangeDir::Down,
offset: 0isize,
},
dubbed: true,
};
pub static INTEGER_TYPE: TypeKind<'static> = TypeKind::BitVector {
domain: Domain::FourValued,
sign: Sign::Signed,
range: Range {
size: 32,
dir: RangeDir::Down,
offset: 0isize,
},
dubbed: true,
};
pub static LONGINT_TYPE: TypeKind<'static> = TypeKind::BitVector {
domain: Domain::TwoValued,
sign: Sign::Signed,
range: Range {
size: 64,
dir: RangeDir::Down,
offset: 0isize,
},
dubbed: true,
};
pub fn bit_size_of_type<'gcx>(
cx: &impl Context<'gcx>,
ty: Type<'gcx>,
env: ParamEnv,
) -> Result<usize> {
match *ty {
TypeKind::Error | TypeKind::Void => Ok(0),
TypeKind::Time => panic!("time value has no bit size"),
TypeKind::Bit(_) => Ok(1),
TypeKind::Int(width, _) => Ok(width),
TypeKind::Named(_, _, ty) => bit_size_of_type(cx, ty, env),
TypeKind::Struct(struct_id) => {
let fields = match cx.hir_of(struct_id)? {
HirNode::Type(hir::Type {
kind: hir::TypeKind::Struct(ref fields),
..
}) => fields,
_ => unreachable!(),
};
let mut size = 0;
for &field in fields {
size += bit_size_of_type(cx, cx.type_of(field, env)?, env)?;
}
Ok(size)
}
TypeKind::PackedArray(elements, ty) => Ok(elements * bit_size_of_type(cx, ty, env)?),
TypeKind::BitScalar { .. } => Ok(1),
TypeKind::BitVector {
range: Range { size, .. },
..
} => Ok(size),
}
}
pub fn identical(a: Type, b: Type) -> bool {
let a = a.resolve_name();
let b = b.resolve_name();
match (a, b) {
(
TypeKind::BitVector {
domain: da,
sign: sa,
range: ra,
..
},
TypeKind::BitVector {
domain: db,
sign: sb,
range: rb,
..
},
) => da == db && sa == sb && ra == rb,
(TypeKind::PackedArray(sa, ta), TypeKind::PackedArray(sb, tb)) => {
sa == sb && identical(ta, tb)
}
_ => a == b,
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn builtin_type_names() {
assert_eq!(format!("{}", BYTE_TYPE), "byte");
assert_eq!(format!("{}", SHORTINT_TYPE), "shortint");
assert_eq!(format!("{}", INT_TYPE), "int");
assert_eq!(format!("{}", INTEGER_TYPE), "integer");
assert_eq!(format!("{}", LONGINT_TYPE), "longint");
assert_eq!(
format!(
"{}",
TypeKind::BitVector {
domain: Domain::TwoValued,
sign: Sign::Unsigned,
range: Range {
size: 42,
dir: RangeDir::Up,
offset: 0isize,
},
dubbed: false,
}
),
"bit [0:41]"
);
assert_eq!(
format!(
"{}",
TypeKind::BitVector {
domain: Domain::TwoValued,
sign: Sign::Unsigned,
range: Range {
size: 42,
dir: RangeDir::Down,
offset: 0isize,
},
dubbed: false,
}
),
"bit [41:0]"
);
assert_eq!(
format!(
"{}",
TypeKind::BitVector {
domain: Domain::TwoValued,
sign: Sign::Unsigned,
range: Range {
size: 42,
dir: RangeDir::Down,
offset: -2isize,
},
dubbed: false,
}
),
"bit [39:-2]"
);
assert_eq!(
format!(
"{}",
TypeKind::BitVector {
domain: Domain::TwoValued,
sign: Sign::Unsigned,
range: Range {
size: 42,
dir: RangeDir::Down,
offset: 3isize,
},
dubbed: false,
}
),
"bit [44:3]"
);
assert_eq!(
format!(
"{}",
TypeKind::BitVector {
domain: Domain::FourValued,
sign: Sign::Unsigned,
range: Range {
size: 42,
dir: RangeDir::Down,
offset: 0isize,
},
dubbed: false,
}
),
"logic [41:0]"
);
assert_eq!(
format!(
"{}",
TypeKind::BitVector {
domain: Domain::FourValued,
sign: Sign::Signed,
range: Range {
size: 42,
dir: RangeDir::Down,
offset: 0isize,
},
dubbed: false,
}
),
"logic signed [41:0]"
);
}
}