use crate::crate_prelude::*;
use crate::{common::arenas::TypedArena, ParamEnv};
use once_cell::sync::Lazy;
use std::{
cell::RefCell,
collections::HashSet,
fmt::{Debug, Display},
hash::{Hash, Hasher},
};
#[derive(Clone)]
pub struct PackedType<'a> {
pub core: PackedCore<'a>,
pub sign: Sign,
pub sign_explicit: bool,
pub dims: Vec<PackedDim>,
resolved: Option<&'a Self>,
resolved_full: Option<&'a Self>,
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub enum PackedCore<'a> {
Error,
Void,
IntVec(IntVecType),
IntAtom(IntAtomType),
Struct(StructType<'a>),
Enum(EnumType<'a>),
Named {
name: Spanned<Name>,
ty: &'a PackedType<'a>,
},
Ref {
span: Span,
ty: &'a PackedType<'a>,
},
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum PackedDim {
Range(Range),
Unsized,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum IntVecType {
Bit,
Logic,
Reg,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum IntAtomType {
Byte,
ShortInt,
Int,
LongInt,
Integer,
Time,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub enum Domain {
TwoValued,
FourValued,
}
#[derive(Clone, Copy, PartialEq, Eq, Hash)]
pub enum Sign {
Signed,
Unsigned,
}
#[derive(Clone, Copy, 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,
}
#[derive(Clone)]
pub struct UnpackedType<'a> {
pub core: UnpackedCore<'a>,
pub dims: Vec<UnpackedDim<'a>>,
resolved: Option<&'a Self>,
resolved_full: Option<&'a Self>,
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub enum UnpackedCore<'a> {
Error,
Packed(&'a PackedType<'a>),
Real(RealType),
Struct(StructType<'a>),
String,
Chandle,
Event,
Named {
name: Spanned<Name>,
ty: &'a UnpackedType<'a>,
},
Ref {
span: Span,
ty: &'a UnpackedType<'a>,
},
Module(ModuleType<'a>),
Interface(InterfaceType<'a>),
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum UnpackedDim<'a> {
Unsized,
Array(usize),
Range(Range),
Assoc(Option<&'a UnpackedType<'a>>),
Queue(Option<usize>),
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum RealType {
ShortReal,
Real,
RealTime,
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct StructType<'a> {
pub ast: &'a ast::Struct<'a>,
pub ast_type: &'a ast::Type<'a>,
pub kind: ast::StructKind,
pub members: Vec<StructMember<'a>>,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct StructMember<'a> {
pub name: Spanned<Name>,
pub ty: &'a UnpackedType<'a>,
pub ast_member: &'a ast::StructMember<'a>,
pub ast_name: &'a ast::VarDeclName<'a>,
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct EnumType<'a> {
pub ast: &'a ast::Enum<'a>,
pub base: &'a PackedType<'a>,
pub base_explicit: bool,
pub variants: Vec<(Spanned<Name>, &'a ast::EnumName<'a>)>,
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct ModuleType<'a> {
pub ast: &'a ast::Module<'a>,
pub env: ParamEnv,
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct InterfaceType<'a> {
pub ast: &'a ast::Interface<'a>,
pub env: ParamEnv,
pub modport: Option<&'a ast::ModportName<'a>>,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct SbvType {
pub domain: Domain,
pub used_atom: bool,
pub sign: Sign,
pub sign_explicit: bool,
pub size: usize,
pub size_explicit: bool,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum Dim<'a> {
Packed(PackedDim),
Unpacked(UnpackedDim<'a>),
}
impl<'a> PackedType<'a> {
pub fn new(core: impl Into<PackedCore<'a>>) -> Self {
Self::with_dims(core, vec![])
}
pub fn with_dims(core: impl Into<PackedCore<'a>>, dims: Vec<PackedDim>) -> Self {
let core = core.into();
let sign = match &core {
PackedCore::IntAtom(IntAtomType::Time)
| PackedCore::IntVec(_)
| PackedCore::Error
| PackedCore::Void
| PackedCore::Struct(_)
| PackedCore::Enum(_)
| PackedCore::Named { .. }
| PackedCore::Ref { .. } => Sign::Unsigned,
PackedCore::IntAtom(_) => Sign::Signed,
};
Self::with_sign_and_dims(core, sign, false, dims)
}
pub fn with_sign(core: impl Into<PackedCore<'a>>, sign: Sign, sign_explicit: bool) -> Self {
Self::with_sign_and_dims(core, sign, sign_explicit, vec![])
}
pub fn with_sign_and_dims(
core: impl Into<PackedCore<'a>>,
sign: Sign,
sign_explicit: bool,
dims: Vec<PackedDim>,
) -> Self {
Self {
core: core.into(),
sign,
sign_explicit,
dims,
resolved: None,
resolved_full: None,
}
}
pub fn with_simple_bit_vector(sbv: SbvType) -> Self {
let atom = match (sbv.size, sbv.domain) {
(8, Domain::TwoValued) => Some(IntAtomType::Byte),
(16, Domain::TwoValued) => Some(IntAtomType::ShortInt),
(32, Domain::TwoValued) => Some(IntAtomType::Int),
(32, Domain::FourValued) => Some(IntAtomType::Integer),
(64, Domain::TwoValued) => Some(IntAtomType::LongInt),
_ => None,
};
match atom {
Some(atom) if sbv.used_atom => Self::with_sign(atom, sbv.sign, sbv.sign_explicit),
_ => Self::with_sign_and_dims(
match sbv.domain {
Domain::TwoValued => IntVecType::Bit,
Domain::FourValued => IntVecType::Logic,
},
sbv.sign,
sbv.sign_explicit,
if sbv.size > 1 || sbv.size_explicit {
vec![PackedDim::Range(sbv.range())]
} else {
vec![]
},
),
}
}
pub fn make(cx: &impl TypeContext<'a>, core: impl Into<PackedCore<'a>>) -> &'a Self {
Self::new(core).intern(cx)
}
pub fn make_dims(
cx: &impl TypeContext<'a>,
core: impl Into<PackedCore<'a>>,
dims: Vec<PackedDim>,
) -> &'a Self {
Self::with_dims(core, dims).intern(cx)
}
pub fn make_sign(
cx: &impl TypeContext<'a>,
core: impl Into<PackedCore<'a>>,
sign: Sign,
sign_explicit: bool,
) -> &'a Self {
Self::with_sign(core, sign, sign_explicit).intern(cx)
}
pub fn make_sign_and_dims(
cx: &impl TypeContext<'a>,
core: impl Into<PackedCore<'a>>,
sign: Sign,
sign_explicit: bool,
dims: Vec<PackedDim>,
) -> &'a Self {
Self::with_sign_and_dims(core, sign, sign_explicit, dims).intern(cx)
}
pub fn make_simple_bit_vector(cx: &impl TypeContext<'a>, sbv: SbvType) -> &'a Self {
Self::with_simple_bit_vector(sbv).intern(cx)
}
pub fn make_error() -> &'a Self {
static TYPE: Lazy<PackedType> = Lazy::new(|| PackedType::new(PackedCore::Error));
let ty: &PackedType = &TYPE;
unsafe { std::mem::transmute(ty) }
}
pub fn make_void() -> &'a Self {
static TYPE: Lazy<PackedType> = Lazy::new(|| PackedType::new(PackedCore::Void));
let ty: &PackedType = &TYPE;
unsafe { std::mem::transmute(ty) }
}
pub fn make_logic() -> &'a Self {
static TYPE: Lazy<PackedType> = Lazy::new(|| PackedType::new(IntVecType::Logic));
let ty: &PackedType = &TYPE;
unsafe { std::mem::transmute(ty) }
}
pub fn make_time() -> &'a Self {
static TYPE: Lazy<PackedType> = Lazy::new(|| PackedType::new(IntAtomType::Time));
let ty: &PackedType = &TYPE;
unsafe { std::mem::transmute(ty) }
}
pub fn intern(mut self, cx: &impl TypeContext<'a>) -> &'a Self {
let inner = match self.core {
PackedCore::Named { ty, .. } => Some(ty),
PackedCore::Ref { ty, .. } => Some(ty),
_ => None,
};
self.resolved = inner.map(|ty| self.apply_to_inner(cx, ty));
self.resolved_full = inner.map(|ty| self.apply_to_inner(cx, ty.resolve_full()));
if let Some(x) = self.resolved {
trace!("Type `{}` resolves to `{}`", self, x);
}
if let Some(x) = self.resolved_full {
trace!("Type `{}` fully resolves to `{}`", self, x);
}
cx.intern_packed(self)
}
fn apply_to_inner(&self, cx: &impl TypeContext<'a>, inner: &'a Self) -> &'a Self {
if self.dims.is_empty()
&& self.sign == inner.sign
&& self.sign_explicit == inner.sign_explicit
{
return inner;
}
let out = Self {
core: inner.core.clone(),
sign: self.sign,
sign_explicit: self.sign_explicit,
dims: self
.dims
.iter()
.cloned()
.chain(inner.dims.iter().cloned())
.collect(),
resolved: None,
resolved_full: None,
};
out.intern(cx)
}
pub fn to_unpacked(&'a self, cx: &impl TypeContext<'a>) -> &'a UnpackedType<'a> {
UnpackedType::make(cx, self)
}
pub fn resolve(&self) -> &Self {
self.resolved.unwrap_or(self)
}
pub fn resolve_full(&self) -> &Self {
self.resolved_full.unwrap_or(self)
}
pub fn is_error(&self) -> bool {
self.core.is_error()
}
pub fn is_identical(&self, other: &Self) -> bool {
let a = self.resolve_full();
let b = other.resolve_full();
if a.coalesces_to_llhd_scalar() && b.coalesces_to_llhd_scalar() {
a.get_simple_bit_vector().map(|x| x.forget())
== b.get_simple_bit_vector().map(|x| x.forget())
} else {
a.is_strictly_identical(b)
}
}
pub fn is_strictly_identical(&self, other: &Self) -> bool {
let a = self.resolve_full();
let b = other.resolve_full();
a.core.is_identical(&b.core) && a.sign == b.sign && a.dims == b.dims
}
pub fn domain(&self) -> Domain {
match &self.core {
PackedCore::Error => Domain::TwoValued,
PackedCore::Void => Domain::TwoValued,
PackedCore::IntVec(x) => x.domain(),
PackedCore::IntAtom(x) => x.domain(),
PackedCore::Struct(x) => x.domain(),
PackedCore::Enum(x) => x.base.domain(),
PackedCore::Named { ty, .. } | PackedCore::Ref { ty, .. } => ty.domain(),
}
}
pub fn sign(&self) -> Sign {
self.sign
}
pub fn get_bit_size(&self) -> Option<usize> {
let ty = self.resolve_full();
let mut size = match &ty.core {
PackedCore::Error => 0,
PackedCore::Void => 0,
PackedCore::IntVec(..) => 1,
PackedCore::IntAtom(x) => x.bit_size(),
PackedCore::Struct(x) => x.get_bit_size()?,
PackedCore::Enum(x) => x.base.get_bit_size()?,
PackedCore::Named { ty, .. } | PackedCore::Ref { ty, .. } => ty.get_bit_size()?,
};
for &dim in &self.dims {
match dim {
PackedDim::Unsized => return None,
PackedDim::Range(r) => size *= r.size,
}
}
Some(size)
}
pub fn is_void(&self) -> bool {
let ty = self.resolve_full();
match ty.core {
PackedCore::Void => ty.dims.is_empty(),
_ => false,
}
}
pub fn is_simple_bit_vector(&self) -> bool {
match self.core {
PackedCore::IntVec(..) if self.dims.len() == 1 => match self.dims[0] {
PackedDim::Range(Range {
offset: 0,
dir: RangeDir::Down,
..
}) => true,
_ => false,
},
_ => false,
}
}
pub fn is_integer_vec(&self) -> bool {
let ty = self.resolve_full();
match ty.core {
PackedCore::IntVec(..) => ty.dims.len() <= 1,
_ => false,
}
}
pub fn is_integer_atom(&self) -> bool {
let ty = self.resolve_full();
match ty.core {
PackedCore::IntAtom(..) => ty.dims.len() == 0,
_ => false,
}
}
pub fn is_single_bit(&self) -> bool {
let ty = self.resolve_full();
match ty.core {
PackedCore::IntVec(..) => ty.dims.len() == 0,
_ => false,
}
}
pub fn is_time(&self) -> bool {
let ty = self.resolve_full();
match ty.core {
PackedCore::IntAtom(IntAtomType::Time) => ty.dims.len() == 0,
_ => false,
}
}
pub fn coalesces_to_llhd_scalar(&self) -> bool {
if let Some(enm) = self.get_enum() {
enm.base.coalesces_to_llhd_scalar()
} else {
!self.is_time()
&& (self.is_integer_vec() || self.is_integer_atom() || self.is_single_bit())
}
}
pub fn get_simple_bit_vector(&self) -> Option<SbvType> {
let ty = self.resolve_full();
Some(SbvType {
domain: ty.domain(),
used_atom: match self.core {
PackedCore::IntAtom(..) => true,
_ => false,
},
sign: ty.sign,
sign_explicit: ty.sign_explicit,
size: ty.get_bit_size()?,
size_explicit: !ty.dims.is_empty(),
})
}
pub fn simple_bit_vector(&self, cx: &impl DiagEmitter, span: Span) -> SbvType {
match self.get_simple_bit_vector() {
Some(sbv) => sbv,
None => bug_span!(span, cx, "`{}` has no simple bit vector equivalent", self),
}
}
pub fn pop_dim(&self, cx: &impl TypeContext<'a>) -> Option<&'a Self> {
let ty = self.resolve_full();
if !ty.dims.is_empty() {
let mut new = ty.clone();
new.dims.remove(0);
Some(new.intern(cx))
} else {
None
}
}
pub fn replace_dim(&self, cx: &impl TypeContext<'a>, dim: PackedDim) -> &'a Self {
let ty = self.resolve_full();
if !ty.dims.is_empty() {
let mut new = ty.clone();
new.dims[0] = dim;
new.intern(cx)
} else {
panic!("no dimension in `{}` to substitute with `{}`", self, dim);
}
}
pub fn outermost_dim(&self) -> Option<PackedDim> {
self.resolve_full().dims.iter().cloned().next()
}
pub fn get_struct(&self) -> Option<&StructType<'a>> {
let ty = self.resolve_full();
match ty.core {
PackedCore::Struct(ref x) if ty.dims.is_empty() => Some(x),
_ => None,
}
}
pub fn get_enum(&self) -> Option<&EnumType<'a>> {
let ty = self.resolve_full();
match ty.core {
PackedCore::Enum(ref x) if ty.dims.is_empty() => Some(x),
_ => None,
}
}
}
impl Eq for PackedType<'_> {}
impl PartialEq for PackedType<'_> {
fn eq(&self, other: &Self) -> bool {
std::ptr::eq(self, other)
}
}
impl Hash for PackedType<'_> {
fn hash<H: Hasher>(&self, h: &mut H) {
std::ptr::hash(self, h)
}
}
impl Eq for Intern<PackedType<'_>> {}
impl PartialEq for Intern<PackedType<'_>> {
fn eq(&self, other: &Self) -> bool {
self.0.core == other.0.core
&& self.0.sign == other.0.sign
&& self.0.sign_explicit == other.0.sign_explicit
&& self.0.dims == other.0.dims
&& self.0.resolved == other.0.resolved
&& self.0.resolved_full == other.0.resolved_full
}
}
impl Hash for Intern<PackedType<'_>> {
fn hash<H: Hasher>(&self, h: &mut H) {
self.0.core.hash(h);
self.0.sign.hash(h);
self.0.sign_explicit.hash(h);
self.0.dims.hash(h);
self.0.resolved.hash(h);
self.0.resolved_full.hash(h);
}
}
impl Display for PackedType<'_> {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
self.core.format(
f,
if self.sign != self.core.default_sign() || self.sign_explicit {
Some(self.sign)
} else {
None
},
)?;
if !self.dims.is_empty() {
write!(f, " ")?;
for dim in &self.dims {
write!(f, "{}", dim)?;
}
}
Ok(())
}
}
impl Debug for PackedType<'_> {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
Display::fmt(self.resolve_full(), f)
}
}
impl<'a> PackedCore<'a> {
pub fn is_error(&self) -> bool {
match self {
Self::Error => true,
Self::Named { ty, .. } | Self::Ref { ty, .. } => ty.is_error(),
_ => false,
}
}
pub fn default_sign(&self) -> Sign {
match self {
Self::Error => Sign::Unsigned,
Self::Void => Sign::Unsigned,
Self::IntVec(_) => Sign::Unsigned,
Self::IntAtom(x) => x.default_sign(),
Self::Struct(_) => Sign::Unsigned,
Self::Enum(_) => Sign::Unsigned,
Self::Named { ty, .. } => ty.sign,
Self::Ref { ty, .. } => ty.sign,
}
}
pub fn is_identical(&self, other: &Self) -> bool {
match (self, other) {
(Self::Error, Self::Error) => true,
(Self::Void, Self::Void) => true,
(Self::IntVec(a), Self::IntVec(b)) => {
(match *a {
IntVecType::Reg => IntVecType::Logic,
x => x,
}) == (match *b {
IntVecType::Reg => IntVecType::Logic,
x => x,
})
}
(Self::IntAtom(a), Self::IntAtom(b)) => a == b,
(Self::Struct(a), Self::Struct(b)) => a == b,
(Self::Enum(a), Self::Enum(b)) => a == b,
(Self::Named { ty: a, .. }, Self::Named { ty: b, .. }) => a.is_identical(b),
(Self::Ref { ty: a, .. }, Self::Ref { ty: b, .. }) => a.is_identical(b),
_ => false,
}
}
fn format(&self, f: &mut std::fmt::Formatter, sign: Option<Sign>) -> std::fmt::Result {
match self {
Self::Error => write!(f, "<error>"),
Self::Void => write!(f, "void"),
Self::IntVec(x) => {
write!(f, "{}", x)?;
if let Some(sign) = sign {
write!(f, " {}", sign)?;
}
Ok(())
}
Self::IntAtom(x) => {
write!(f, "{}", x)?;
if let Some(sign) = sign {
write!(f, " {}", sign)?;
}
Ok(())
}
Self::Struct(x) => x.format(f, true, sign),
Self::Enum(x) => write!(f, "{}", x),
Self::Named { name, .. } => write!(f, "{}", name),
Self::Ref { span, .. } => write!(f, "{}", span.extract()),
}
}
}
impl<'a> From<IntVecType> for PackedCore<'a> {
fn from(inner: IntVecType) -> Self {
PackedCore::IntVec(inner)
}
}
impl<'a> From<IntAtomType> for PackedCore<'a> {
fn from(inner: IntAtomType) -> Self {
PackedCore::IntAtom(inner)
}
}
impl<'a> From<StructType<'a>> for PackedCore<'a> {
fn from(inner: StructType<'a>) -> Self {
PackedCore::Struct(inner)
}
}
impl<'a> From<EnumType<'a>> for PackedCore<'a> {
fn from(inner: EnumType<'a>) -> Self {
PackedCore::Enum(inner)
}
}
impl Display for PackedCore<'_> {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
self.format(f, None)
}
}
impl PackedDim {
pub fn get_range(&self) -> Option<Range> {
match *self {
Self::Range(x) => Some(x),
Self::Unsized => None,
}
}
pub fn get_size(&self) -> Option<usize> {
self.get_range().map(|r| r.size)
}
}
impl From<Range> for PackedDim {
fn from(range: Range) -> Self {
Self::Range(range)
}
}
impl Display for PackedDim {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
match self {
Self::Range(x) => write!(f, "{}", x),
Self::Unsized => write!(f, "[]"),
}
}
}
impl IntVecType {
pub fn domain(&self) -> Domain {
match self {
Self::Bit => Domain::TwoValued,
Self::Logic => Domain::FourValued,
Self::Reg => Domain::FourValued,
}
}
}
impl Display for IntVecType {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
match self {
Self::Bit => write!(f, "bit"),
Self::Logic => write!(f, "logic"),
Self::Reg => write!(f, "reg"),
}
}
}
impl IntAtomType {
pub fn domain(&self) -> Domain {
match self {
Self::Byte => Domain::TwoValued,
Self::ShortInt => Domain::TwoValued,
Self::Int => Domain::TwoValued,
Self::LongInt => Domain::TwoValued,
Self::Integer => Domain::FourValued,
Self::Time => Domain::TwoValued,
}
}
pub fn bit_size(&self) -> usize {
match self {
Self::Byte => 8,
Self::ShortInt => 16,
Self::Int => 32,
Self::LongInt => 64,
Self::Integer => 32,
Self::Time => 64,
}
}
pub fn default_sign(&self) -> Sign {
match self {
Self::Byte => Sign::Signed,
Self::ShortInt => Sign::Signed,
Self::Int => Sign::Signed,
Self::LongInt => Sign::Signed,
Self::Integer => Sign::Signed,
Self::Time => Sign::Unsigned,
}
}
}
impl Display for IntAtomType {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
match self {
Self::Byte => write!(f, "byte"),
Self::ShortInt => write!(f, "shortint"),
Self::Int => write!(f, "int"),
Self::LongInt => write!(f, "longint"),
Self::Integer => write!(f, "integer"),
Self::Time => write!(f, "time"),
}
}
}
impl Domain {
pub fn bit_type(&self) -> IntVecType {
match self {
Domain::TwoValued => IntVecType::Bit,
Domain::FourValued => IntVecType::Logic,
}
}
}
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 std::fmt::Formatter) -> std::fmt::Result {
match self {
Sign::Signed => write!(f, "signed"),
Sign::Unsigned => write!(f, "unsigned"),
}
}
}
impl Debug for Sign {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
Display::fmt(self, f)
}
}
impl Range {
pub fn with_size(size: usize) -> Self {
Self {
size,
dir: RangeDir::Down,
offset: 0,
}
}
pub fn left(self) -> isize {
match self.dir {
RangeDir::Up => self.low(),
RangeDir::Down => self.high(),
}
}
pub fn right(self) -> isize {
match self.dir {
RangeDir::Up => self.high(),
RangeDir::Down => self.low(),
}
}
pub fn low(self) -> isize {
self.offset
}
pub fn high(self) -> isize {
self.offset + self.size as isize - 1
}
pub fn increment(self) -> isize {
match self.dir {
RangeDir::Up => 1,
RangeDir::Down => -1,
}
}
}
impl Display for Range {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::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)
}
}
impl Debug for Range {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
Display::fmt(self, f)
}
}
impl<'a> UnpackedType<'a> {
pub fn new(core: impl Into<UnpackedCore<'a>>) -> Self {
Self::with_dims(core, vec![])
}
pub fn with_dims(core: impl Into<UnpackedCore<'a>>, dims: Vec<UnpackedDim<'a>>) -> Self {
Self {
core: core.into(),
dims,
resolved: None,
resolved_full: None,
}
}
pub fn make(cx: &impl TypeContext<'a>, core: impl Into<UnpackedCore<'a>>) -> &'a Self {
Self::make_dims(cx, core, vec![])
}
pub fn make_dims(
cx: &impl TypeContext<'a>,
core: impl Into<UnpackedCore<'a>>,
dims: Vec<UnpackedDim<'a>>,
) -> &'a Self {
Self::with_dims(core, dims).intern(cx)
}
pub fn make_error() -> &'a Self {
static TYPE: Lazy<UnpackedType> = Lazy::new(|| UnpackedType::new(UnpackedCore::Error));
let ty: &UnpackedType = &TYPE;
unsafe { std::mem::transmute(ty) }
}
pub fn make_void() -> &'a Self {
static TYPE: Lazy<UnpackedType> = Lazy::new(|| UnpackedType::new(PackedType::make_void()));
let ty: &UnpackedType = &TYPE;
unsafe { std::mem::transmute(ty) }
}
pub fn make_logic() -> &'a Self {
static TYPE: Lazy<UnpackedType> = Lazy::new(|| UnpackedType::new(PackedType::make_logic()));
let ty: &UnpackedType = &TYPE;
unsafe { std::mem::transmute(ty) }
}
pub fn make_time() -> &'a Self {
static TYPE: Lazy<UnpackedType> = Lazy::new(|| UnpackedType::new(PackedType::make_time()));
let ty: &UnpackedType = &TYPE;
unsafe { std::mem::transmute(ty) }
}
pub fn intern(mut self, cx: &impl TypeContext<'a>) -> &'a Self {
let inner = match self.core {
UnpackedCore::Named { ty, .. } | UnpackedCore::Ref { ty, .. } => {
(Some(ty), Some(ty.resolve_full()))
}
UnpackedCore::Packed(ty) => (
ty.resolved.map(|p| p.to_unpacked(cx)),
ty.resolved_full.map(|p| p.to_unpacked(cx)),
),
_ => (None, None),
};
self.resolved = inner.0.map(|ty| self.apply_to_inner(cx, ty));
self.resolved_full = inner.1.map(|ty| self.apply_to_inner(cx, ty));
if let Some(x) = self.resolved {
trace!("Type `{}` resolves to `{}`", self, x);
}
if let Some(x) = self.resolved_full {
trace!("Type `{}` fully resolves to `{}`", self, x);
}
cx.intern_unpacked(self)
}
fn apply_to_inner(&self, cx: &impl TypeContext<'a>, inner: &'a Self) -> &'a Self {
if self.dims.is_empty() {
return inner;
}
let out = Self {
core: inner.core.clone(),
dims: self
.dims
.iter()
.cloned()
.chain(inner.dims.iter().cloned())
.collect(),
resolved: None,
resolved_full: None,
};
out.intern(cx)
}
pub fn resolve(&self) -> &Self {
self.resolved.unwrap_or(self)
}
pub fn resolve_full(&self) -> &Self {
self.resolved_full.unwrap_or(self)
}
pub fn is_error(&self) -> bool {
self.core.is_error()
}
pub fn is_identical(&self, other: &Self) -> bool {
let a = self.resolve_full();
let b = other.resolve_full();
a.core.is_identical(&b.core) && a.dims == b.dims
}
pub fn is_strictly_identical(&self, other: &Self) -> bool {
let a = self.resolve_full();
let b = other.resolve_full();
a.core.is_strictly_identical(&b.core) && a.dims == b.dims
}
pub fn is_packed(&self) -> bool {
self.get_packed().is_some()
}
pub fn get_packed(&self) -> Option<&'a PackedType<'a>> {
if self.dims.is_empty() {
self.core.get_packed()
} else {
None
}
}
pub fn domain(&self) -> Domain {
match &self.core {
UnpackedCore::Packed(x) => x.domain(),
UnpackedCore::Struct(x) => x.domain(),
UnpackedCore::Named { ty, .. } | UnpackedCore::Ref { ty, .. } => ty.domain(),
UnpackedCore::Error
| UnpackedCore::Real(_)
| UnpackedCore::String
| UnpackedCore::Chandle
| UnpackedCore::Event
| UnpackedCore::Module { .. }
| UnpackedCore::Interface { .. } => Domain::TwoValued,
}
}
pub fn sign(&self) -> Sign {
match &self.core {
UnpackedCore::Packed(x) => x.sign(),
UnpackedCore::Struct(_) => Sign::Unsigned, UnpackedCore::Named { ty, .. } | UnpackedCore::Ref { ty, .. } => ty.sign(),
UnpackedCore::Error
| UnpackedCore::Real(_)
| UnpackedCore::String
| UnpackedCore::Chandle
| UnpackedCore::Event
| UnpackedCore::Module { .. }
| UnpackedCore::Interface { .. } => Sign::Unsigned,
}
}
pub fn get_bit_size(&self) -> Option<usize> {
let ty = self.resolve_full();
let mut size = match &ty.core {
UnpackedCore::Packed(x) => x.get_bit_size()?,
UnpackedCore::Real(x) => x.bit_size(),
UnpackedCore::Struct(x) => x.get_bit_size()?,
UnpackedCore::Named { ty, .. } | UnpackedCore::Ref { ty, .. } => ty.get_bit_size()?,
UnpackedCore::Error
| UnpackedCore::String
| UnpackedCore::Chandle
| UnpackedCore::Event
| UnpackedCore::Module { .. }
| UnpackedCore::Interface { .. } => return None,
};
for &dim in &self.dims {
match dim {
UnpackedDim::Array(r) => size *= r,
UnpackedDim::Range(r) => size *= r.size,
UnpackedDim::Unsized | UnpackedDim::Assoc(..) | UnpackedDim::Queue(..) => {
return None
}
}
}
Some(size)
}
pub fn is_void(&self) -> bool {
self.get_packed().map(|ty| ty.is_void()).unwrap_or(false)
}
pub fn is_simple_bit_vector(&self) -> bool {
self.get_packed()
.map(|ty| ty.is_simple_bit_vector())
.unwrap_or(false)
}
pub fn is_integer_atom(&self) -> bool {
self.get_packed()
.map(|ty| ty.is_integer_atom())
.unwrap_or(false)
}
pub fn is_single_bit(&self) -> bool {
self.get_packed()
.map(|ty| ty.is_single_bit())
.unwrap_or(false)
}
pub fn is_string(&self) -> bool {
self.dims.is_empty() && self.resolve_full().core == UnpackedCore::String
}
pub fn coalesces_to_llhd_scalar(&self) -> bool {
self.get_packed()
.map(|ty| ty.coalesces_to_llhd_scalar())
.unwrap_or(false)
}
pub fn get_simple_bit_vector(&self) -> Option<SbvType> {
self.get_packed().and_then(|ty| ty.get_simple_bit_vector())
}
pub fn simple_bit_vector(&self, cx: &impl DiagEmitter, span: Span) -> SbvType {
match self.get_simple_bit_vector() {
Some(sbv) => sbv,
None => bug_span!(span, cx, "`{}` has no simple bit vector equivalent", self),
}
}
pub fn packed_dims(&self) -> impl Iterator<Item = PackedDim> + 'a {
self.resolve_full()
.core
.get_packed()
.into_iter()
.flat_map(|p| p.dims.iter().cloned())
}
pub fn unpacked_dims<'s>(&'s self) -> impl Iterator<Item = UnpackedDim<'a>> + 's {
self.resolve_full().dims.iter().cloned()
}
pub fn dims<'s>(&'s self) -> impl Iterator<Item = Dim<'a>> + 's {
self.unpacked_dims()
.map(Dim::Unpacked)
.chain(self.packed_dims().map(Dim::Packed))
}
pub fn pop_dim(&self, cx: &impl TypeContext<'a>) -> Option<&'a Self> {
let ty = self.resolve_full();
if !ty.dims.is_empty() {
let mut new = ty.clone();
new.dims.remove(0);
Some(new.intern(cx))
} else {
self.get_packed()
.and_then(|p| p.pop_dim(cx))
.map(|p| p.to_unpacked(cx))
}
}
pub fn replace_dim(&self, cx: &impl TypeContext<'a>, dim: Dim<'a>) -> &'a Self {
let ty = self.resolve_full();
if !ty.dims.is_empty() {
let mut new = ty.clone();
let dim = match dim {
Dim::Unpacked(d) => d,
Dim::Packed(d) => panic!(
"substituting {:?} for type `{}` whose outermost dimension is unpacked",
d, self
),
};
new.dims[0] = dim;
new.intern(cx)
} else {
let packed = match self.get_packed() {
Some(packed) => packed,
None => panic!("no dimension in `{}` to substitute with `{}`", self, dim),
};
let dim = match dim {
Dim::Packed(d) => d,
Dim::Unpacked(d) => panic!(
"substituting {:?} for type `{}` whose outermost dimension is packed",
d, self
),
};
packed.replace_dim(cx, dim).to_unpacked(cx)
}
}
pub fn outermost_dim(&self) -> Option<Dim<'a>> {
self.dims().next()
}
pub fn get_struct(&self) -> Option<&StructType<'a>> {
if self.dims.is_empty() {
self.resolve_full().core.get_struct()
} else {
None
}
}
pub fn get_enum(&self) -> Option<&EnumType<'a>> {
if self.dims.is_empty() {
self.resolve_full().core.get_enum()
} else {
None
}
}
pub fn get_module(&self) -> Option<&ModuleType<'a>> {
if self.dims.is_empty() {
self.resolve_full().core.get_module()
} else {
None
}
}
pub fn get_interface(&self) -> Option<&InterfaceType<'a>> {
if self.dims.is_empty() {
self.resolve_full().core.get_interface()
} else {
None
}
}
fn format_around(
&self,
f: &mut std::fmt::Formatter,
around: Option<impl Display>,
) -> std::fmt::Result {
write!(f, "{}", self.core)?;
if let Some(around) = around {
write!(f, " {}", around)?;
}
if !self.dims.is_empty() {
write!(f, " ")?;
for dim in &self.dims {
write!(f, "{}", dim)?;
}
}
Ok(())
}
}
impl Eq for UnpackedType<'_> {}
impl PartialEq for UnpackedType<'_> {
fn eq(&self, other: &Self) -> bool {
std::ptr::eq(self, other)
}
}
impl Hash for UnpackedType<'_> {
fn hash<H: Hasher>(&self, h: &mut H) {
std::ptr::hash(self, h)
}
}
impl Eq for Intern<UnpackedType<'_>> {}
impl PartialEq for Intern<UnpackedType<'_>> {
fn eq(&self, other: &Self) -> bool {
self.0.core == other.0.core
&& self.0.dims == other.0.dims
&& self.0.resolved == other.0.resolved
&& self.0.resolved_full == other.0.resolved_full
}
}
impl Hash for Intern<UnpackedType<'_>> {
fn hash<H: Hasher>(&self, h: &mut H) {
self.0.core.hash(h);
self.0.dims.hash(h);
self.0.resolved.hash(h);
self.0.resolved_full.hash(h);
}
}
impl Display for UnpackedType<'_> {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
self.format_around(
f,
if self.dims.is_empty() {
None
} else {
Some("$")
},
)
}
}
impl Debug for UnpackedType<'_> {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
Display::fmt(self.resolve_full(), f)
}
}
impl<'a> UnpackedCore<'a> {
pub fn is_error(&self) -> bool {
match self {
Self::Error => true,
Self::Packed(ty) => ty.is_error(),
Self::Named { ty, .. } | Self::Ref { ty, .. } => ty.is_error(),
_ => false,
}
}
pub fn is_identical(&self, other: &Self) -> bool {
match (self, other) {
(Self::Error, Self::Error) => true,
(Self::Packed(a), Self::Packed(b)) => a.is_identical(b),
(Self::Real(a), Self::Real(b)) => a == b,
(Self::Struct(a), Self::Struct(b)) => a == b,
(Self::String, Self::String) => true,
(Self::Chandle, Self::Chandle) => true,
(Self::Event, Self::Event) => true,
(Self::Named { ty: a, .. }, Self::Named { ty: b, .. }) => a.is_identical(b),
(Self::Ref { ty: a, .. }, Self::Ref { ty: b, .. }) => a.is_identical(b),
(Self::Module(a), Self::Module(b)) => a == b,
(Self::Interface(a), Self::Interface(b)) => a == b,
_ => false,
}
}
pub fn is_strictly_identical(&self, other: &Self) -> bool {
match (self, other) {
(Self::Error, Self::Error) => true,
(Self::Packed(a), Self::Packed(b)) => a.is_strictly_identical(b),
(Self::Real(a), Self::Real(b)) => a == b,
(Self::Struct(a), Self::Struct(b)) => a == b,
(Self::String, Self::String) => true,
(Self::Chandle, Self::Chandle) => true,
(Self::Event, Self::Event) => true,
(Self::Named { ty: a, .. }, Self::Named { ty: b, .. }) => a.is_strictly_identical(b),
(Self::Ref { ty: a, .. }, Self::Ref { ty: b, .. }) => a.is_strictly_identical(b),
(Self::Module(a), Self::Module(b)) => a == b,
(Self::Interface(a), Self::Interface(b)) => a == b,
_ => false,
}
}
pub fn get_packed(&self) -> Option<&'a PackedType<'a>> {
match *self {
UnpackedCore::Packed(inner) => Some(inner),
UnpackedCore::Named { ty, .. } | UnpackedCore::Ref { ty, .. } => ty.get_packed(),
_ => None,
}
}
pub fn get_struct(&self) -> Option<&StructType<'a>> {
match *self {
UnpackedCore::Packed(x) => x.get_struct(),
UnpackedCore::Struct(ref x) => Some(x),
UnpackedCore::Named { ty, .. } | UnpackedCore::Ref { ty, .. } => ty.get_struct(),
_ => None,
}
}
pub fn get_enum(&self) -> Option<&EnumType<'a>> {
self.get_packed().and_then(|packed| packed.get_enum())
}
pub fn get_module(&self) -> Option<&ModuleType<'a>> {
match *self {
UnpackedCore::Module(ref x) => Some(x),
UnpackedCore::Named { ty, .. } | UnpackedCore::Ref { ty, .. } => ty.get_module(),
_ => None,
}
}
pub fn get_interface(&self) -> Option<&InterfaceType<'a>> {
match *self {
UnpackedCore::Interface(ref x) => Some(x),
UnpackedCore::Named { ty, .. } | UnpackedCore::Ref { ty, .. } => ty.get_interface(),
_ => None,
}
}
}
impl<'a> From<&'a PackedType<'a>> for UnpackedCore<'a> {
fn from(inner: &'a PackedType<'a>) -> Self {
Self::Packed(inner)
}
}
impl<'a> From<RealType> for UnpackedCore<'a> {
fn from(inner: RealType) -> Self {
Self::Real(inner)
}
}
impl<'a> From<StructType<'a>> for UnpackedCore<'a> {
fn from(inner: StructType<'a>) -> Self {
Self::Struct(inner)
}
}
impl<'a> From<ModuleType<'a>> for UnpackedCore<'a> {
fn from(inner: ModuleType<'a>) -> Self {
Self::Module(inner)
}
}
impl<'a> From<InterfaceType<'a>> for UnpackedCore<'a> {
fn from(inner: InterfaceType<'a>) -> Self {
Self::Interface(inner)
}
}
impl Display for UnpackedCore<'_> {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
match self {
Self::Error => write!(f, "<error>"),
Self::Packed(x) => write!(f, "{}", x),
Self::Real(x) => write!(f, "{}", x),
Self::Struct(x) => x.format(f, false, None),
Self::String => write!(f, "string"),
Self::Chandle => write!(f, "chandle"),
Self::Event => write!(f, "event"),
Self::Module(x) => write!(f, "{}", x.ast.name),
Self::Interface(x) => match x.modport {
Some(y) => write!(f, "{}.{}", x.ast.name, y.name),
None => write!(f, "{}", x.ast.name),
},
Self::Named { name, .. } => write!(f, "{}", name),
Self::Ref { span, .. } => write!(f, "{}", span.extract()),
}
}
}
impl UnpackedDim<'_> {
pub fn get_range(&self) -> Option<Range> {
match *self {
Self::Range(x) => Some(x),
_ => None,
}
}
pub fn get_size(&self) -> Option<usize> {
match *self {
Self::Array(x) => Some(x),
Self::Range(x) => Some(x.size),
_ => None,
}
}
}
impl From<usize> for UnpackedDim<'_> {
fn from(size: usize) -> Self {
Self::Array(size)
}
}
impl From<Range> for UnpackedDim<'_> {
fn from(range: Range) -> Self {
Self::Range(range)
}
}
impl Display for UnpackedDim<'_> {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
match self {
Self::Unsized => write!(f, "[]"),
Self::Array(x) => write!(f, "[{}]", x),
Self::Range(x) => write!(f, "{}", x),
Self::Assoc(Some(x)) => write!(f, "[{}]", x),
Self::Assoc(None) => write!(f, "[*]"),
Self::Queue(Some(x)) => write!(f, "[$:{}]", x),
Self::Queue(None) => write!(f, "[$]"),
}
}
}
impl RealType {
pub fn bit_size(&self) -> usize {
match self {
Self::ShortReal => 32,
Self::Real => 64,
Self::RealTime => 64,
}
}
}
impl Display for RealType {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
match self {
Self::ShortReal => write!(f, "shortreal"),
Self::Real => write!(f, "real"),
Self::RealTime => write!(f, "realtime"),
}
}
}
impl<'a> StructType<'a> {
pub fn domain(&self) -> Domain {
let any_four_valued = self
.members
.iter()
.any(|m| m.ty.domain() == Domain::FourValued);
match any_four_valued {
true => Domain::FourValued,
false => Domain::TwoValued,
}
}
pub fn get_bit_size(&self) -> Option<usize> {
let mut size = 0;
for m in &self.members {
size += m.ty.get_bit_size()?;
}
Some(size)
}
fn format(
&self,
f: &mut std::fmt::Formatter,
packed: bool,
sign: Option<Sign>,
) -> std::fmt::Result {
write!(f, "{}", self.kind)?;
if packed {
write!(f, " packed")?;
if let Some(sign) = sign {
write!(f, " {}", sign)?;
}
}
write!(f, " {{ ")?;
for member in &self.members {
write!(f, "{}; ", member)?;
}
write!(f, "}}")?;
Ok(())
}
}
impl Display for StructType<'_> {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
self.format(f, false, None)
}
}
impl Display for StructMember<'_> {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
self.ty.format_around(f, Some(self.name))
}
}
impl Display for EnumType<'_> {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
write!(f, "enum")?;
if self.base_explicit {
write!(f, " {}", self.base)?;
}
write!(f, " {{")?;
let mut first = true;
for (name, _) in &self.variants {
if !first {
write!(f, ",")?;
}
write!(f, " ")?;
first = false;
write!(f, "{}", name)?;
}
write!(f, " }}")?;
Ok(())
}
}
impl SbvType {
pub fn new(domain: Domain, sign: Sign, size: usize) -> Self {
Self {
domain,
used_atom: false,
sign,
sign_explicit: false,
size,
size_explicit: true,
}
}
pub fn nice(domain: Domain, sign: Sign, size: usize) -> Self {
Self {
domain,
used_atom: true,
sign,
sign_explicit: false,
size,
size_explicit: false,
}
}
pub fn to_packed<'a>(&self, cx: &impl TypeContext<'a>) -> &'a PackedType<'a> {
PackedType::make_simple_bit_vector(cx, *self)
}
pub fn to_unpacked<'a>(&self, cx: &impl TypeContext<'a>) -> &'a UnpackedType<'a> {
self.to_packed(cx).to_unpacked(cx)
}
pub fn is_unsigned(&self) -> bool {
self.sign == Sign::Unsigned
}
pub fn is_signed(&self) -> bool {
self.sign == Sign::Signed
}
pub fn range(&self) -> Range {
Range {
size: self.size,
dir: RangeDir::Down,
offset: 0,
}
}
pub fn change_size(&self, size: usize) -> SbvType {
SbvType {
used_atom: self.used_atom && self.size == size,
size: size,
..*self
}
}
pub fn change_domain(&self, domain: Domain) -> SbvType {
SbvType { domain, ..*self }
}
pub fn change_sign(&self, sign: Sign) -> SbvType {
SbvType {
sign,
sign_explicit: self.sign_explicit || self.sign != sign,
..*self
}
}
pub fn is_identical(&self, other: &Self) -> bool {
self.domain == other.domain && self.sign == other.sign && self.size == other.size
}
pub fn forget(&self) -> SbvType {
SbvType {
used_atom: false,
sign_explicit: false,
size_explicit: true,
..*self
}
}
}
impl Display for SbvType {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
match self.domain {
Domain::TwoValued => write!(f, "bit")?,
Domain::FourValued => write!(f, "logic")?,
}
if self.sign != Sign::Unsigned || self.sign_explicit {
write!(f, " {}", self.sign)?;
}
if self.size > 1 || self.size_explicit {
write!(f, " {}", self.range())?;
}
Ok(())
}
}
impl Dim<'_> {
pub fn get_range(&self) -> Option<Range> {
match self {
Self::Packed(x) => x.get_range(),
Self::Unpacked(x) => x.get_range(),
}
}
pub fn get_size(&self) -> Option<usize> {
match self {
Self::Packed(x) => x.get_size(),
Self::Unpacked(x) => x.get_size(),
}
}
}
impl Display for Dim<'_> {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
match self {
Self::Packed(x) => write!(f, "{}", x),
Self::Unpacked(x) => write!(f, "{}", x),
}
}
}
pub trait TypeContext<'a> {
fn intern_packed(&self, ty: PackedType<'a>) -> &'a PackedType<'a>;
fn intern_unpacked(&self, ty: UnpackedType<'a>) -> &'a UnpackedType<'a>;
}
#[derive(Default)]
pub struct TypeStorage<'a> {
packed: TypedArena<Intern<PackedType<'a>>>,
unpacked: TypedArena<Intern<UnpackedType<'a>>>,
cached_packed: RefCell<HashSet<&'a Intern<PackedType<'a>>>>,
cached_unpacked: RefCell<HashSet<&'a Intern<UnpackedType<'a>>>>,
}
pub trait HasTypeStorage<'a> {
fn type_storage(&self) -> &'a TypeStorage<'a>;
}
struct Intern<T>(T);
impl<'a, T> TypeContext<'a> for T
where
T: HasTypeStorage<'a>,
{
fn intern_packed(&self, ty: PackedType<'a>) -> &'a PackedType<'a> {
let ty = Intern(ty);
let st = self.type_storage();
if let Some(x) = st.cached_packed.borrow().get(&ty) {
return &x.0;
}
let ty = st.packed.alloc(ty);
st.cached_packed.borrow_mut().insert(ty);
&ty.0
}
fn intern_unpacked(&self, ty: UnpackedType<'a>) -> &'a UnpackedType<'a> {
let ty = Intern(ty);
let st = self.type_storage();
if let Some(x) = st.cached_unpacked.borrow().get(&ty) {
return &x.0;
}
let ty = st.unpacked.alloc(ty);
st.cached_unpacked.borrow_mut().insert(ty);
&ty.0
}
}
impl<'a> HasTypeStorage<'a> for &'a TypeStorage<'a> {
fn type_storage(&self) -> &'a TypeStorage<'a> {
*self
}
}