forked from libre-chip/fayalite
WIP adding TraceAsString
This commit is contained in:
parent
26224abe1c
commit
27f3733573
11 changed files with 962 additions and 12 deletions
|
|
@ -225,6 +225,8 @@ expr_enum! {
|
||||||
RegSync(Reg<CanonicalType, SyncReset>),
|
RegSync(Reg<CanonicalType, SyncReset>),
|
||||||
RegAsync(Reg<CanonicalType, AsyncReset>),
|
RegAsync(Reg<CanonicalType, AsyncReset>),
|
||||||
MemPort(MemPort<DynPortType>),
|
MemPort(MemPort<DynPortType>),
|
||||||
|
AsTraceAsString(ops::AsTraceAsString),
|
||||||
|
TraceAsStringAsInner(ops::TraceAsStringAsInner),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -27,7 +27,7 @@ use crate::{
|
||||||
ToSyncReset,
|
ToSyncReset,
|
||||||
},
|
},
|
||||||
sim::value::{SimValue, ToSimValue},
|
sim::value::{SimValue, ToSimValue},
|
||||||
ty::{CanonicalType, StaticType, Type},
|
ty::{CanonicalType, StaticType, TraceAsString, Type},
|
||||||
util::ConstUsize,
|
util::ConstUsize,
|
||||||
};
|
};
|
||||||
use bitvec::{order::Lsb0, slice::BitSlice, vec::BitVec, view::BitView};
|
use bitvec::{order::Lsb0, slice::BitSlice, vec::BitVec, view::BitView};
|
||||||
|
|
@ -4694,3 +4694,172 @@ impl<This: ExprFromIterator<A>, A> FromIterator<A> for Expr<This> {
|
||||||
This::expr_from_iter(iter)
|
This::expr_from_iter(iter)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Copy, Clone, PartialEq, Eq, Hash)]
|
||||||
|
pub struct AsTraceAsString<T: Type = CanonicalType> {
|
||||||
|
inner: Expr<CanonicalType>,
|
||||||
|
ty: TraceAsString<T>,
|
||||||
|
literal_bits: Result<Interned<BitSlice>, NotALiteralExpr>,
|
||||||
|
target: Option<Interned<Target>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: Type> fmt::Debug for AsTraceAsString<T> {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
let Self {
|
||||||
|
inner,
|
||||||
|
ty: _,
|
||||||
|
literal_bits: _,
|
||||||
|
target: _,
|
||||||
|
} = self;
|
||||||
|
f.debug_struct("AsTraceAsString")
|
||||||
|
.field("inner", inner)
|
||||||
|
.finish_non_exhaustive()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: Type> AsTraceAsString<T> {
|
||||||
|
pub fn new(inner: Expr<CanonicalType>, ty: TraceAsString<T>) -> Self {
|
||||||
|
assert_eq!(inner.ty(), ty.inner_ty().canonical());
|
||||||
|
let literal_bits = inner.to_literal_bits();
|
||||||
|
let target = inner.target();
|
||||||
|
Self {
|
||||||
|
inner,
|
||||||
|
ty,
|
||||||
|
literal_bits,
|
||||||
|
target,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pub fn inner(self) -> Expr<CanonicalType> {
|
||||||
|
self.inner
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: Type> GetTarget for AsTraceAsString<T> {
|
||||||
|
fn target(&self) -> Option<Interned<Target>> {
|
||||||
|
self.target
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: Type> ToLiteralBits for AsTraceAsString<T> {
|
||||||
|
fn to_literal_bits(&self) -> Result<Interned<BitSlice>, NotALiteralExpr> {
|
||||||
|
self.literal_bits
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: Type> ValueType for AsTraceAsString<T> {
|
||||||
|
type Type = TraceAsString<T>;
|
||||||
|
type ValueCategory = ValueCategoryExpr;
|
||||||
|
|
||||||
|
fn ty(&self) -> Self::Type {
|
||||||
|
self.ty
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: Type> ToExpr for AsTraceAsString<T> {
|
||||||
|
fn to_expr(&self) -> Expr<Self::Type> {
|
||||||
|
Expr {
|
||||||
|
__enum: ExprEnum::AsTraceAsString(AsTraceAsString {
|
||||||
|
inner: self.inner,
|
||||||
|
ty: self.ty.canonical_trace_as_string(),
|
||||||
|
literal_bits: self.literal_bits,
|
||||||
|
target: self.target,
|
||||||
|
})
|
||||||
|
.intern(),
|
||||||
|
__ty: self.ty,
|
||||||
|
__flow: Expr::flow(self.inner),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Copy, Clone, PartialEq, Eq, Hash)]
|
||||||
|
pub struct TraceAsStringAsInner<T: Type = CanonicalType> {
|
||||||
|
arg: Expr<TraceAsString<CanonicalType>>,
|
||||||
|
ty: T,
|
||||||
|
literal_bits: Result<Interned<BitSlice>, NotALiteralExpr>,
|
||||||
|
target: Option<Interned<Target>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: Type> fmt::Debug for TraceAsStringAsInner<T> {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
let Self {
|
||||||
|
arg,
|
||||||
|
ty: _,
|
||||||
|
literal_bits: _,
|
||||||
|
target: _,
|
||||||
|
} = self;
|
||||||
|
f.debug_struct("TraceAsStringAsInner")
|
||||||
|
.field("arg", arg)
|
||||||
|
.finish_non_exhaustive()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: Type> TraceAsStringAsInner<T> {
|
||||||
|
pub fn from_arg_and_ty(arg: Expr<TraceAsString<CanonicalType>>, ty: T) -> Self {
|
||||||
|
assert_eq!(arg.ty().inner_ty(), ty.canonical());
|
||||||
|
let literal_bits = arg.to_literal_bits();
|
||||||
|
let target = arg.target();
|
||||||
|
Self {
|
||||||
|
arg,
|
||||||
|
ty,
|
||||||
|
literal_bits,
|
||||||
|
target,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pub fn new(arg: Expr<TraceAsString<T>>) -> Self {
|
||||||
|
Self::from_arg_and_ty(
|
||||||
|
Expr {
|
||||||
|
__enum: arg.__enum,
|
||||||
|
__ty: arg.__ty.canonical_trace_as_string(),
|
||||||
|
__flow: arg.__flow,
|
||||||
|
},
|
||||||
|
arg.ty().inner_ty(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
pub fn arg(self) -> Expr<TraceAsString<CanonicalType>> {
|
||||||
|
self.arg
|
||||||
|
}
|
||||||
|
pub fn arg_typed(self) -> Expr<TraceAsString<T>> {
|
||||||
|
Expr {
|
||||||
|
__enum: self.arg.__enum,
|
||||||
|
__ty: TraceAsString::from_canonical_trace_as_string(self.arg.__ty),
|
||||||
|
__flow: self.arg.__flow,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: Type> GetTarget for TraceAsStringAsInner<T> {
|
||||||
|
fn target(&self) -> Option<Interned<Target>> {
|
||||||
|
self.target
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: Type> ToLiteralBits for TraceAsStringAsInner<T> {
|
||||||
|
fn to_literal_bits(&self) -> Result<Interned<BitSlice>, NotALiteralExpr> {
|
||||||
|
self.literal_bits
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: Type> ValueType for TraceAsStringAsInner<T> {
|
||||||
|
type Type = T;
|
||||||
|
type ValueCategory = ValueCategoryExpr;
|
||||||
|
|
||||||
|
fn ty(&self) -> Self::Type {
|
||||||
|
self.ty
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: Type> ToExpr for TraceAsStringAsInner<T> {
|
||||||
|
fn to_expr(&self) -> Expr<Self::Type> {
|
||||||
|
Expr {
|
||||||
|
__enum: ExprEnum::TraceAsStringAsInner(TraceAsStringAsInner {
|
||||||
|
arg: self.arg,
|
||||||
|
ty: self.ty.canonical(),
|
||||||
|
literal_bits: self.literal_bits,
|
||||||
|
target: self.target,
|
||||||
|
})
|
||||||
|
.intern(),
|
||||||
|
__ty: self.ty,
|
||||||
|
__flow: Expr::flow(self.arg),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -1537,7 +1537,7 @@ impl TargetState {
|
||||||
fn new(target: Interned<Target>, declared_in_block: usize) -> Self {
|
fn new(target: Interned<Target>, declared_in_block: usize) -> Self {
|
||||||
Self {
|
Self {
|
||||||
target,
|
target,
|
||||||
inner: match target.canonical_ty() {
|
inner: match target.canonical_ty().unwrap_transparent_types() {
|
||||||
CanonicalType::Bundle(ty) => TargetStateInner::Decomposed {
|
CanonicalType::Bundle(ty) => TargetStateInner::Decomposed {
|
||||||
subtargets: ty
|
subtargets: ty
|
||||||
.fields()
|
.fields()
|
||||||
|
|
@ -1586,6 +1586,9 @@ impl TargetState {
|
||||||
declared_in_block,
|
declared_in_block,
|
||||||
written_in_blocks: RefCell::default(),
|
written_in_blocks: RefCell::default(),
|
||||||
},
|
},
|
||||||
|
CanonicalType::TraceAsString(_) => {
|
||||||
|
unreachable!("handled by unwrap_transparent_types")
|
||||||
|
}
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -38,7 +38,7 @@ use crate::{
|
||||||
},
|
},
|
||||||
ty::{
|
ty::{
|
||||||
OpaqueSimValue, OpaqueSimValueSize, OpaqueSimValueSizeRange, OpaqueSimValueSlice,
|
OpaqueSimValue, OpaqueSimValueSize, OpaqueSimValueSizeRange, OpaqueSimValueSlice,
|
||||||
OpaqueSimValueWriter,
|
OpaqueSimValueWriter, TraceAsString,
|
||||||
},
|
},
|
||||||
util::{BitSliceWriteWithBase, DebugAsDisplay, HashMap, HashSet, copy_le_bytes_to_bitslice},
|
util::{BitSliceWriteWithBase, DebugAsDisplay, HashMap, HashSet, copy_le_bytes_to_bitslice},
|
||||||
};
|
};
|
||||||
|
|
@ -432,6 +432,15 @@ impl_trace_decl! {
|
||||||
ty: DynSimOnly,
|
ty: DynSimOnly,
|
||||||
flow: Flow,
|
flow: Flow,
|
||||||
}),
|
}),
|
||||||
|
TraceAsString(TraceTraceAsString {
|
||||||
|
fn location(self) -> _ {
|
||||||
|
self.location
|
||||||
|
}
|
||||||
|
location: TraceLocation,
|
||||||
|
name: Interned<str>,
|
||||||
|
ty: TraceAsString,
|
||||||
|
flow: Flow,
|
||||||
|
}),
|
||||||
}),
|
}),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -543,6 +552,7 @@ pub trait TraceWriter: fmt::Debug + 'static {
|
||||||
id: TraceScalarId,
|
id: TraceScalarId,
|
||||||
value: &DynSimOnlyValue,
|
value: &DynSimOnlyValue,
|
||||||
) -> Result<(), Self::Error>;
|
) -> Result<(), Self::Error>;
|
||||||
|
fn set_signal_string(&mut self, id: TraceScalarId, value: &str) -> Result<(), Self::Error>;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct DynTraceWriterDecls(Box<dyn TraceWriterDeclsDynTrait>);
|
pub struct DynTraceWriterDecls(Box<dyn TraceWriterDeclsDynTrait>);
|
||||||
|
|
@ -607,6 +617,7 @@ trait TraceWriterDynTrait: fmt::Debug + 'static {
|
||||||
id: TraceScalarId,
|
id: TraceScalarId,
|
||||||
value: &DynSimOnlyValue,
|
value: &DynSimOnlyValue,
|
||||||
) -> std::io::Result<()>;
|
) -> std::io::Result<()>;
|
||||||
|
fn set_signal_string_dyn(&mut self, id: TraceScalarId, value: &str) -> std::io::Result<()>;
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T: TraceWriter> TraceWriterDynTrait for T {
|
impl<T: TraceWriter> TraceWriterDynTrait for T {
|
||||||
|
|
@ -680,6 +691,9 @@ impl<T: TraceWriter> TraceWriterDynTrait for T {
|
||||||
) -> std::io::Result<()> {
|
) -> std::io::Result<()> {
|
||||||
Ok(TraceWriter::set_signal_sim_only_value(self, id, value).map_err(err_into_io)?)
|
Ok(TraceWriter::set_signal_sim_only_value(self, id, value).map_err(err_into_io)?)
|
||||||
}
|
}
|
||||||
|
fn set_signal_string_dyn(&mut self, id: TraceScalarId, value: &str) -> std::io::Result<()> {
|
||||||
|
Ok(TraceWriter::set_signal_string(self, id, value).map_err(err_into_io)?)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct DynTraceWriter(Box<dyn TraceWriterDynTrait>);
|
pub struct DynTraceWriter(Box<dyn TraceWriterDynTrait>);
|
||||||
|
|
@ -758,6 +772,9 @@ impl TraceWriter for DynTraceWriter {
|
||||||
) -> Result<(), Self::Error> {
|
) -> Result<(), Self::Error> {
|
||||||
self.0.set_signal_sim_only_value_dyn(id, value)
|
self.0.set_signal_sim_only_value_dyn(id, value)
|
||||||
}
|
}
|
||||||
|
fn set_signal_string(&mut self, id: TraceScalarId, value: &str) -> Result<(), Self::Error> {
|
||||||
|
self.0.set_signal_string_dyn(id, value)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
|
|
@ -934,6 +951,41 @@ pub(crate) enum SimTraceKind {
|
||||||
PhantomConst {
|
PhantomConst {
|
||||||
ty: PhantomConst,
|
ty: PhantomConst,
|
||||||
},
|
},
|
||||||
|
TraceAsString {
|
||||||
|
layout: compiler::CompiledTypeLayout<CanonicalType>,
|
||||||
|
range: TypeIndexRange,
|
||||||
|
ty: TraceAsString,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(PartialEq, Eq)]
|
||||||
|
pub(crate) struct SimTraceStateOpaqueSimValue {
|
||||||
|
bits: BitVec,
|
||||||
|
sim_only: Vec<DynSimOnlyValue>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Debug for SimTraceStateOpaqueSimValue {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
let Self { bits, sim_only } = self;
|
||||||
|
f.debug_struct("SimTraceStateOpaqueSimValue")
|
||||||
|
.field("bits", &BitSliceWriteWithBase(bits))
|
||||||
|
.field("sim_only", sim_only)
|
||||||
|
.finish()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Clone for SimTraceStateOpaqueSimValue {
|
||||||
|
fn clone(&self) -> Self {
|
||||||
|
Self {
|
||||||
|
bits: self.bits.clone(),
|
||||||
|
sim_only: self.sim_only.clone(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fn clone_from(&mut self, source: &Self) {
|
||||||
|
let Self { bits, sim_only } = self;
|
||||||
|
bits.clone_from(&source.bits);
|
||||||
|
sim_only.clone_from(&source.sim_only);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(PartialEq, Eq)]
|
#[derive(PartialEq, Eq)]
|
||||||
|
|
@ -941,6 +993,7 @@ pub(crate) enum SimTraceState {
|
||||||
Bits(BitVec),
|
Bits(BitVec),
|
||||||
SimOnly(DynSimOnlyValue),
|
SimOnly(DynSimOnlyValue),
|
||||||
PhantomConst,
|
PhantomConst,
|
||||||
|
OpaqueSimValue(Option<SimTraceStateOpaqueSimValue>),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Clone for SimTraceState {
|
impl Clone for SimTraceState {
|
||||||
|
|
@ -949,6 +1002,7 @@ impl Clone for SimTraceState {
|
||||||
Self::Bits(v) => Self::Bits(v.clone()),
|
Self::Bits(v) => Self::Bits(v.clone()),
|
||||||
Self::SimOnly(v) => Self::SimOnly(v.clone()),
|
Self::SimOnly(v) => Self::SimOnly(v.clone()),
|
||||||
Self::PhantomConst => Self::PhantomConst,
|
Self::PhantomConst => Self::PhantomConst,
|
||||||
|
Self::OpaqueSimValue(v) => Self::OpaqueSimValue(v.clone()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
fn clone_from(&mut self, source: &Self) {
|
fn clone_from(&mut self, source: &Self) {
|
||||||
|
|
@ -956,6 +1010,9 @@ impl Clone for SimTraceState {
|
||||||
(SimTraceState::Bits(dest), SimTraceState::Bits(source)) => {
|
(SimTraceState::Bits(dest), SimTraceState::Bits(source)) => {
|
||||||
dest.clone_from_bitslice(source);
|
dest.clone_from_bitslice(source);
|
||||||
}
|
}
|
||||||
|
(SimTraceState::OpaqueSimValue(dest), SimTraceState::OpaqueSimValue(source)) => {
|
||||||
|
dest.clone_from(source);
|
||||||
|
}
|
||||||
_ => *self = source.clone(),
|
_ => *self = source.clone(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -990,6 +1047,20 @@ impl SimTraceState {
|
||||||
unreachable!()
|
unreachable!()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
fn unwrap_opaque_sim_value_ref(&self) -> &Option<SimTraceStateOpaqueSimValue> {
|
||||||
|
if let SimTraceState::OpaqueSimValue(v) = self {
|
||||||
|
v
|
||||||
|
} else {
|
||||||
|
unreachable!()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fn unwrap_opaque_sim_value_mut(&mut self) -> &mut Option<SimTraceStateOpaqueSimValue> {
|
||||||
|
if let SimTraceState::OpaqueSimValue(v) = self {
|
||||||
|
v
|
||||||
|
} else {
|
||||||
|
unreachable!()
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl fmt::Debug for SimTraceState {
|
impl fmt::Debug for SimTraceState {
|
||||||
|
|
@ -998,6 +1069,7 @@ impl fmt::Debug for SimTraceState {
|
||||||
SimTraceState::Bits(v) => BitSliceWriteWithBase(v).fmt(f),
|
SimTraceState::Bits(v) => BitSliceWriteWithBase(v).fmt(f),
|
||||||
SimTraceState::SimOnly(v) => v.fmt(f),
|
SimTraceState::SimOnly(v) => v.fmt(f),
|
||||||
SimTraceState::PhantomConst => f.debug_tuple("PhantomConst").finish(),
|
SimTraceState::PhantomConst => f.debug_tuple("PhantomConst").finish(),
|
||||||
|
SimTraceState::OpaqueSimValue(v) => v.fmt(f),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1026,6 +1098,7 @@ impl SimTraceKind {
|
||||||
}
|
}
|
||||||
SimTraceKind::PhantomConst { .. } => SimTraceState::PhantomConst,
|
SimTraceKind::PhantomConst { .. } => SimTraceState::PhantomConst,
|
||||||
SimTraceKind::SimOnly { index: _, ty } => SimTraceState::SimOnly(ty.default_value()),
|
SimTraceKind::SimOnly { index: _, ty } => SimTraceState::SimOnly(ty.default_value()),
|
||||||
|
SimTraceKind::TraceAsString { .. } => SimTraceState::OpaqueSimValue(None),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1273,7 +1346,8 @@ impl SimulationModuleState {
|
||||||
| CanonicalType::Reset(_)
|
| CanonicalType::Reset(_)
|
||||||
| CanonicalType::Clock(_)
|
| CanonicalType::Clock(_)
|
||||||
| CanonicalType::PhantomConst(_)
|
| CanonicalType::PhantomConst(_)
|
||||||
| CanonicalType::DynSimOnly(_) => unreachable!(),
|
| CanonicalType::DynSimOnly(_)
|
||||||
|
| CanonicalType::TraceAsString(_) => unreachable!(),
|
||||||
CanonicalType::AsyncReset(_) => true,
|
CanonicalType::AsyncReset(_) => true,
|
||||||
CanonicalType::SyncReset(_) => false,
|
CanonicalType::SyncReset(_) => false,
|
||||||
}
|
}
|
||||||
|
|
@ -2181,6 +2255,9 @@ impl SimulationImpl {
|
||||||
SimTraceKind::SimOnly { .. } => {
|
SimTraceKind::SimOnly { .. } => {
|
||||||
trace_writer.set_signal_sim_only_value(id, state.unwrap_sim_only_ref())?
|
trace_writer.set_signal_sim_only_value(id, state.unwrap_sim_only_ref())?
|
||||||
}
|
}
|
||||||
|
SimTraceKind::TraceAsString { layout, range, ty } => {
|
||||||
|
trace_writer.set_signal_sim_only_value(id, state.unwrap_sim_only_ref())?
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Ok(trace_writer)
|
Ok(trace_writer)
|
||||||
|
|
|
||||||
|
|
@ -29,7 +29,8 @@ use crate::{
|
||||||
TraceBool, TraceBundle, TraceClock, TraceDecl, TraceEnumDiscriminant, TraceEnumWithFields,
|
TraceBool, TraceBundle, TraceClock, TraceDecl, TraceEnumDiscriminant, TraceEnumWithFields,
|
||||||
TraceFieldlessEnum, TraceInstance, TraceLocation, TraceMem, TraceMemPort, TraceMemoryId,
|
TraceFieldlessEnum, TraceInstance, TraceLocation, TraceMem, TraceMemPort, TraceMemoryId,
|
||||||
TraceMemoryLocation, TraceModule, TraceModuleIO, TracePhantomConst, TraceReg, TraceSInt,
|
TraceMemoryLocation, TraceModule, TraceModuleIO, TracePhantomConst, TraceReg, TraceSInt,
|
||||||
TraceScalarId, TraceScope, TraceSimOnly, TraceSyncReset, TraceUInt, TraceWire,
|
TraceScalarId, TraceScope, TraceSimOnly, TraceSyncReset, TraceTraceAsString, TraceUInt,
|
||||||
|
TraceWire,
|
||||||
interpreter::{
|
interpreter::{
|
||||||
self, Insn, InsnField, InsnFieldKind, InsnFieldType, InsnOrLabel, Insns, InsnsBuilding,
|
self, Insn, InsnField, InsnFieldKind, InsnFieldType, InsnOrLabel, Insns, InsnsBuilding,
|
||||||
InsnsBuildingDone, InsnsBuildingKind, Label, PrefixLinesWrapper, SmallUInt,
|
InsnsBuildingDone, InsnsBuildingKind, Label, PrefixLinesWrapper, SmallUInt,
|
||||||
|
|
@ -265,6 +266,7 @@ impl<T: Type> CompiledTypeLayout<T> {
|
||||||
body: CompiledTypeLayoutBody::Scalar,
|
body: CompiledTypeLayoutBody::Scalar,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
CanonicalType::TraceAsString(ty) => CompiledTypeLayout::get(ty.inner_ty()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1977,6 +1979,38 @@ macro_rules! impl_compiler {
|
||||||
flow,
|
flow,
|
||||||
}
|
}
|
||||||
.into(),
|
.into(),
|
||||||
|
CanonicalType::TraceAsString(ty) => {
|
||||||
|
let location = match target {
|
||||||
|
MakeTraceDeclTarget::Expr(target) => {
|
||||||
|
let compiled_value = self.compile_expr(instantiated_module, target);
|
||||||
|
let compiled_value =
|
||||||
|
self.compiled_expr_to_value(compiled_value, source_location);
|
||||||
|
TraceLocation::Scalar(self.new_sim_trace(SimTraceKind::TraceAsString {
|
||||||
|
ty,
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
MakeTraceDeclTarget::Memory {
|
||||||
|
id,
|
||||||
|
depth,
|
||||||
|
stride,
|
||||||
|
start,
|
||||||
|
ty: _,
|
||||||
|
} => TraceLocation::Memory(TraceMemoryLocation {
|
||||||
|
id,
|
||||||
|
depth,
|
||||||
|
stride,
|
||||||
|
start,
|
||||||
|
len: ty.type_properties().bit_width,
|
||||||
|
}),
|
||||||
|
};
|
||||||
|
TraceTraceAsString {
|
||||||
|
location,
|
||||||
|
name,
|
||||||
|
ty,
|
||||||
|
flow,
|
||||||
|
}
|
||||||
|
.into()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
fn compiled_expr_to_value(
|
fn compiled_expr_to_value(
|
||||||
|
|
|
||||||
|
|
@ -1098,7 +1098,8 @@ impl ToSimValueWithType<CanonicalType> for bool {
|
||||||
| CanonicalType::Enum(_)
|
| CanonicalType::Enum(_)
|
||||||
| CanonicalType::Bundle(_)
|
| CanonicalType::Bundle(_)
|
||||||
| CanonicalType::PhantomConst(_)
|
| CanonicalType::PhantomConst(_)
|
||||||
| CanonicalType::DynSimOnly(_) => {
|
| CanonicalType::DynSimOnly(_)
|
||||||
|
| CanonicalType::TraceAsString(_) => {
|
||||||
panic!("can't create SimValue from bool: expected value of type: {ty:?}");
|
panic!("can't create SimValue from bool: expected value of type: {ty:?}");
|
||||||
}
|
}
|
||||||
CanonicalType::Bool(_)
|
CanonicalType::Bool(_)
|
||||||
|
|
|
||||||
|
|
@ -6,14 +6,18 @@ use crate::{
|
||||||
bundle::Bundle,
|
bundle::Bundle,
|
||||||
clock::Clock,
|
clock::Clock,
|
||||||
enum_::Enum,
|
enum_::Enum,
|
||||||
expr::Expr,
|
expr::{Expr, ToExpr, ValueType, ops},
|
||||||
int::{Bool, SInt, UInt, UIntValue},
|
int::{Bool, SInt, UInt, UIntValue},
|
||||||
intern::{Intern, Interned},
|
intern::{Intern, Interned, LazyInterned, SupportsPtrEqWithTypeId},
|
||||||
phantom_const::PhantomConst,
|
phantom_const::PhantomConst,
|
||||||
reset::{AsyncReset, Reset, SyncReset},
|
reset::{AsyncReset, Reset, SyncReset},
|
||||||
sim::value::{DynSimOnly, DynSimOnlyValue, SimValue, ToSimValueWithType},
|
sim::value::{DynSimOnly, DynSimOnlyValue, SimValue, ToSimValue, ToSimValueWithType},
|
||||||
source_location::SourceLocation,
|
source_location::SourceLocation,
|
||||||
util::{ConstUsize, slice_range, try_slice_range},
|
util::{
|
||||||
|
ConstUsize,
|
||||||
|
serde_by_id::{SerdeByIdProperties, SerdeByIdTable, SerdeByIdTrait},
|
||||||
|
slice_range, try_slice_range,
|
||||||
|
},
|
||||||
};
|
};
|
||||||
use bitvec::{slice::BitSlice, vec::BitVec};
|
use bitvec::{slice::BitSlice, vec::BitVec};
|
||||||
use serde::{Deserialize, Deserializer, Serialize, Serializer, de::DeserializeOwned};
|
use serde::{Deserialize, Deserializer, Serialize, Serializer, de::DeserializeOwned};
|
||||||
|
|
@ -69,6 +73,7 @@ pub enum CanonicalType {
|
||||||
Clock(Clock),
|
Clock(Clock),
|
||||||
PhantomConst(PhantomConst),
|
PhantomConst(PhantomConst),
|
||||||
DynSimOnly(DynSimOnly),
|
DynSimOnly(DynSimOnly),
|
||||||
|
TraceAsString(TraceAsString),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl fmt::Debug for CanonicalType {
|
impl fmt::Debug for CanonicalType {
|
||||||
|
|
@ -86,6 +91,7 @@ impl fmt::Debug for CanonicalType {
|
||||||
Self::Clock(v) => v.fmt(f),
|
Self::Clock(v) => v.fmt(f),
|
||||||
Self::PhantomConst(v) => v.fmt(f),
|
Self::PhantomConst(v) => v.fmt(f),
|
||||||
Self::DynSimOnly(v) => v.fmt(f),
|
Self::DynSimOnly(v) => v.fmt(f),
|
||||||
|
Self::TraceAsString(v) => v.fmt(f),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -123,6 +129,7 @@ impl CanonicalType {
|
||||||
CanonicalType::Clock(v) => v.type_properties(),
|
CanonicalType::Clock(v) => v.type_properties(),
|
||||||
CanonicalType::PhantomConst(v) => v.type_properties(),
|
CanonicalType::PhantomConst(v) => v.type_properties(),
|
||||||
CanonicalType::DynSimOnly(v) => v.type_properties(),
|
CanonicalType::DynSimOnly(v) => v.type_properties(),
|
||||||
|
CanonicalType::TraceAsString(v) => v.type_properties(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
pub fn is_passive(self) -> bool {
|
pub fn is_passive(self) -> bool {
|
||||||
|
|
@ -217,11 +224,37 @@ impl CanonicalType {
|
||||||
};
|
};
|
||||||
lhs.can_connect(rhs)
|
lhs.can_connect(rhs)
|
||||||
}
|
}
|
||||||
|
CanonicalType::TraceAsString(lhs) => {
|
||||||
|
let CanonicalType::TraceAsString(rhs) = rhs else {
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
lhs.can_connect(rhs)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
pub(crate) fn as_serde_unexpected_str(self) -> &'static str {
|
pub(crate) fn as_serde_unexpected_str(self) -> &'static str {
|
||||||
serde_impls::SerdeCanonicalType::from(self).as_serde_unexpected_str()
|
serde_impls::SerdeCanonicalType::from(self).as_serde_unexpected_str()
|
||||||
}
|
}
|
||||||
|
/// Unwrap transparent types until reaching a non-transparent type. Currently [`TraceAsString`] is the only transparent type.
|
||||||
|
pub fn unwrap_transparent_types(mut self) -> Self {
|
||||||
|
loop {
|
||||||
|
self = match self {
|
||||||
|
Self::UInt(_)
|
||||||
|
| Self::SInt(_)
|
||||||
|
| Self::Bool(_)
|
||||||
|
| Self::Array(_)
|
||||||
|
| Self::Enum(_)
|
||||||
|
| Self::Bundle(_)
|
||||||
|
| Self::AsyncReset(_)
|
||||||
|
| Self::SyncReset(_)
|
||||||
|
| Self::Reset(_)
|
||||||
|
| Self::Clock(_)
|
||||||
|
| Self::PhantomConst(_)
|
||||||
|
| Self::DynSimOnly(_) => return self,
|
||||||
|
Self::TraceAsString(ty) => ty.inner_ty(),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub trait MatchVariantAndInactiveScope: Sized {
|
pub trait MatchVariantAndInactiveScope: Sized {
|
||||||
|
|
@ -328,6 +361,7 @@ impl_base_type!(Reset);
|
||||||
impl_base_type!(Clock);
|
impl_base_type!(Clock);
|
||||||
impl_base_type!(PhantomConst);
|
impl_base_type!(PhantomConst);
|
||||||
impl_base_type!(DynSimOnly);
|
impl_base_type!(DynSimOnly);
|
||||||
|
impl_base_type!(TraceAsString);
|
||||||
|
|
||||||
impl_base_type_serde!(Bool, "a Bool");
|
impl_base_type_serde!(Bool, "a Bool");
|
||||||
impl_base_type_serde!(Enum, "an Enum");
|
impl_base_type_serde!(Enum, "an Enum");
|
||||||
|
|
@ -336,6 +370,7 @@ impl_base_type_serde!(AsyncReset, "an AsyncReset");
|
||||||
impl_base_type_serde!(SyncReset, "a SyncReset");
|
impl_base_type_serde!(SyncReset, "a SyncReset");
|
||||||
impl_base_type_serde!(Reset, "a Reset");
|
impl_base_type_serde!(Reset, "a Reset");
|
||||||
impl_base_type_serde!(Clock, "a Clock");
|
impl_base_type_serde!(Clock, "a Clock");
|
||||||
|
impl_base_type_serde!(TraceAsString, "a TraceAsString");
|
||||||
|
|
||||||
impl sealed::BaseTypeSealed for CanonicalType {}
|
impl sealed::BaseTypeSealed for CanonicalType {}
|
||||||
|
|
||||||
|
|
@ -473,6 +508,7 @@ impl Type for CanonicalType {
|
||||||
CanonicalType::Clock(v) => v.mask_type().canonical(),
|
CanonicalType::Clock(v) => v.mask_type().canonical(),
|
||||||
CanonicalType::PhantomConst(v) => v.mask_type().canonical(),
|
CanonicalType::PhantomConst(v) => v.mask_type().canonical(),
|
||||||
CanonicalType::DynSimOnly(v) => v.mask_type().canonical(),
|
CanonicalType::DynSimOnly(v) => v.mask_type().canonical(),
|
||||||
|
CanonicalType::TraceAsString(v) => v.mask_type().canonical(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
fn canonical(&self) -> CanonicalType {
|
fn canonical(&self) -> CanonicalType {
|
||||||
|
|
@ -1143,3 +1179,347 @@ impl<T: Type> Index<T> for AsMaskWithoutGenerics {
|
||||||
Interned::into_inner(Intern::intern_sized(ty.mask_type()))
|
Interned::into_inner(Intern::intern_sized(ty.mask_type()))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
trait TraceAsStringTrait: fmt::Debug + 'static + Send + Sync + SupportsPtrEqWithTypeId {
|
||||||
|
fn trace_fmt(&self, opaque: OpaqueSimValueSlice<'_>, f: &mut fmt::Formatter<'_>)
|
||||||
|
-> fmt::Result;
|
||||||
|
fn serde_by_id_properties(&self) -> SerdeByIdProperties<Interned<dyn TraceAsStringTrait>> {
|
||||||
|
SerdeByIdProperties::of::<Self>()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: Type> TraceAsStringTrait for T {
|
||||||
|
fn trace_fmt(
|
||||||
|
&self,
|
||||||
|
opaque: OpaqueSimValueSlice<'_>,
|
||||||
|
f: &mut fmt::Formatter<'_>,
|
||||||
|
) -> fmt::Result {
|
||||||
|
fmt::Debug::fmt(&Type::sim_value_from_opaque(self, opaque), f)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl crate::intern::InternedCompare for dyn TraceAsStringTrait {
|
||||||
|
type InternedCompareKey = crate::intern::PtrEqWithTypeId;
|
||||||
|
|
||||||
|
fn interned_compare_key_ref(this: &Self) -> Self::InternedCompareKey {
|
||||||
|
this.get_ptr_eq_with_type_id()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl SerdeByIdTrait for Interned<dyn TraceAsStringTrait> {
|
||||||
|
fn serde_by_id_properties(&self) -> SerdeByIdProperties<Self> {
|
||||||
|
TraceAsStringTrait::serde_by_id_properties(&**self)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn static_table() -> &'static SerdeByIdTable<Self> {
|
||||||
|
static TABLE: SerdeByIdTable<Interned<dyn TraceAsStringTrait>> = SerdeByIdTable::new();
|
||||||
|
&TABLE
|
||||||
|
}
|
||||||
|
|
||||||
|
const NAME: &'static str = "dyn TraceAsStringTrait";
|
||||||
|
}
|
||||||
|
|
||||||
|
/// When running the fayalite simulator, outputs a single string signal containing a formatted version of the inner value (uses [`fmt::Debug`] by default).
|
||||||
|
/// This is a transparent type, meaning [`CanonicalType::unwrap_transparent_types`] will unwrap this type.
|
||||||
|
#[derive(Copy, Clone, PartialEq, Eq, Hash)]
|
||||||
|
pub struct TraceAsString<T: Type = CanonicalType> {
|
||||||
|
inner_ty: LazyInterned<T>,
|
||||||
|
trace_as_string: LazyInterned<dyn TraceAsStringTrait>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[expect(non_upper_case_globals)]
|
||||||
|
pub const TraceAsString: TraceAsStringWithoutGenerics = TraceAsStringWithoutGenerics;
|
||||||
|
|
||||||
|
impl<T: Type> fmt::Debug for TraceAsString<T> {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
let Self {
|
||||||
|
inner_ty,
|
||||||
|
trace_as_string: _,
|
||||||
|
} = self;
|
||||||
|
f.debug_struct("TraceAsString")
|
||||||
|
.field("inner_ty", &inner_ty.interned())
|
||||||
|
.finish_non_exhaustive()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: Type> TraceAsString<T> {
|
||||||
|
pub fn new(inner_ty: T) -> Self {
|
||||||
|
Self::new_interned(inner_ty.intern_sized())
|
||||||
|
}
|
||||||
|
pub fn new_interned(inner_ty: Interned<T>) -> Self {
|
||||||
|
Self {
|
||||||
|
inner_ty: LazyInterned::Interned(inner_ty),
|
||||||
|
trace_as_string: LazyInterned::Interned(Interned::cast_unchecked(
|
||||||
|
inner_ty,
|
||||||
|
|v| -> &dyn TraceAsStringTrait { v },
|
||||||
|
)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pub fn interned_inner_ty(self) -> Interned<T> {
|
||||||
|
self.inner_ty.interned()
|
||||||
|
}
|
||||||
|
pub fn inner_ty(self) -> T {
|
||||||
|
*self.interned_inner_ty()
|
||||||
|
}
|
||||||
|
pub fn canonical_trace_as_string(self) -> TraceAsString<CanonicalType> {
|
||||||
|
let Self {
|
||||||
|
inner_ty,
|
||||||
|
trace_as_string,
|
||||||
|
} = self;
|
||||||
|
TraceAsString {
|
||||||
|
inner_ty: LazyInterned::Interned(inner_ty.interned().canonical().intern_sized()),
|
||||||
|
trace_as_string,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pub fn from_canonical_trace_as_string(canonical: TraceAsString<CanonicalType>) -> Self {
|
||||||
|
let TraceAsString {
|
||||||
|
inner_ty,
|
||||||
|
trace_as_string,
|
||||||
|
} = canonical;
|
||||||
|
Self {
|
||||||
|
inner_ty: LazyInterned::Interned(
|
||||||
|
T::from_canonical(*inner_ty.interned()).intern_sized(),
|
||||||
|
),
|
||||||
|
trace_as_string,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pub fn type_properties(self) -> TypeProperties {
|
||||||
|
self.interned_inner_ty().canonical().type_properties()
|
||||||
|
}
|
||||||
|
pub fn can_connect<T2: Type>(self, rhs: TraceAsString<T2>) -> bool {
|
||||||
|
self.interned_inner_ty()
|
||||||
|
.canonical()
|
||||||
|
.can_connect(rhs.interned_inner_ty().canonical())
|
||||||
|
}
|
||||||
|
pub fn trace_fmt(
|
||||||
|
self,
|
||||||
|
opaque: OpaqueSimValueSlice<'_>,
|
||||||
|
f: &mut fmt::Formatter<'_>,
|
||||||
|
) -> fmt::Result {
|
||||||
|
self.trace_as_string.interned().trace_fmt(opaque, f)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: Type> SimValueDebug for TraceAsString<T> {
|
||||||
|
fn sim_value_debug(
|
||||||
|
value: &<Self as Type>::SimValue,
|
||||||
|
f: &mut fmt::Formatter<'_>,
|
||||||
|
) -> fmt::Result {
|
||||||
|
T::sim_value_debug(value, f)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: Type> Type for TraceAsString<T> {
|
||||||
|
type BaseType = TraceAsString<CanonicalType>;
|
||||||
|
type MaskType = T::MaskType;
|
||||||
|
type SimValue = TraceAsStringSimValue<T::SimValue>;
|
||||||
|
type MatchVariant = T::MatchVariant;
|
||||||
|
type MatchActiveScope = T::MatchActiveScope;
|
||||||
|
type MatchVariantAndInactiveScope = T::MatchVariantAndInactiveScope;
|
||||||
|
type MatchVariantsIter = T::MatchVariantsIter;
|
||||||
|
|
||||||
|
fn match_variants(
|
||||||
|
this: Expr<Self>,
|
||||||
|
source_location: SourceLocation,
|
||||||
|
) -> Self::MatchVariantsIter {
|
||||||
|
T::match_variants(
|
||||||
|
ops::TraceAsStringAsInner::new(this).to_expr(),
|
||||||
|
source_location,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn mask_type(&self) -> Self::MaskType {
|
||||||
|
self.inner_ty.mask_type()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn canonical(&self) -> CanonicalType {
|
||||||
|
CanonicalType::TraceAsString(self.canonical_trace_as_string())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn from_canonical(canonical_type: CanonicalType) -> Self {
|
||||||
|
let CanonicalType::TraceAsString(canonical) = canonical_type else {
|
||||||
|
panic!("expected TraceAsString");
|
||||||
|
};
|
||||||
|
Self::from_canonical_trace_as_string(canonical)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn source_location() -> SourceLocation {
|
||||||
|
SourceLocation::builtin()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn sim_value_from_opaque(&self, opaque: OpaqueSimValueSlice<'_>) -> Self::SimValue {
|
||||||
|
TraceAsStringSimValue {
|
||||||
|
inner: self.inner_ty.sim_value_from_opaque(opaque),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn sim_value_clone_from_opaque(
|
||||||
|
&self,
|
||||||
|
value: &mut Self::SimValue,
|
||||||
|
opaque: OpaqueSimValueSlice<'_>,
|
||||||
|
) {
|
||||||
|
self.inner_ty
|
||||||
|
.sim_value_clone_from_opaque(&mut value.inner, opaque);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn sim_value_to_opaque<'w>(
|
||||||
|
&self,
|
||||||
|
value: &Self::SimValue,
|
||||||
|
writer: OpaqueSimValueWriter<'w>,
|
||||||
|
) -> OpaqueSimValueWritten<'w> {
|
||||||
|
self.inner_ty.sim_value_to_opaque(&value.inner, writer)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct TraceAsStringStaticTypeHelper<T: StaticType>(PhantomData<T>);
|
||||||
|
|
||||||
|
impl<T: StaticType> Default for TraceAsStringStaticTypeHelper<T> {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self(PhantomData)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: StaticType> From<TraceAsStringStaticTypeHelper<T>> for Interned<dyn TraceAsStringTrait> {
|
||||||
|
fn from(_value: TraceAsStringStaticTypeHelper<T>) -> Self {
|
||||||
|
Interned::cast_unchecked(T::TYPE.intern_sized(), |v| -> &dyn TraceAsStringTrait { v })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: StaticType> Default for TraceAsString<T> {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self::TYPE
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct MakeType<T: StaticType>(Interned<T>);
|
||||||
|
|
||||||
|
impl<T: StaticType> From<MakeType<T>> for Interned<T> {
|
||||||
|
fn from(value: MakeType<T>) -> Self {
|
||||||
|
value.0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: StaticType> Default for MakeType<T> {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self(T::TYPE.intern_sized())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: StaticType> StaticType for TraceAsString<T> {
|
||||||
|
const TYPE: Self = Self {
|
||||||
|
inner_ty: LazyInterned::new_const::<MakeType<T>>(),
|
||||||
|
trace_as_string: LazyInterned::new_const::<TraceAsStringStaticTypeHelper<T>>(),
|
||||||
|
};
|
||||||
|
const MASK_TYPE: Self::MaskType = T::MASK_TYPE;
|
||||||
|
const TYPE_PROPERTIES: TypeProperties = T::TYPE_PROPERTIES;
|
||||||
|
const MASK_TYPE_PROPERTIES: TypeProperties = T::MASK_TYPE_PROPERTIES;
|
||||||
|
}
|
||||||
|
|
||||||
|
#[doc(hidden)]
|
||||||
|
pub struct TraceAsStringWithoutGenerics;
|
||||||
|
|
||||||
|
impl<T: Type> Index<T> for TraceAsStringWithoutGenerics {
|
||||||
|
type Output = TraceAsString<T>;
|
||||||
|
|
||||||
|
fn index(&self, inner_ty: T) -> &Self::Output {
|
||||||
|
Interned::into_inner(TraceAsString::new(inner_ty).intern_sized())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Copy, Clone, Eq, Ord, Hash, Default, Serialize, Deserialize)]
|
||||||
|
pub struct TraceAsStringSimValue<T> {
|
||||||
|
inner: T,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> TraceAsStringSimValue<T> {
|
||||||
|
pub const fn new(inner: T) -> Self {
|
||||||
|
Self { inner }
|
||||||
|
}
|
||||||
|
pub fn into_inner(this: Self) -> T {
|
||||||
|
let Self { inner } = this;
|
||||||
|
inner
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: ValueType> ValueType for TraceAsStringSimValue<T> {
|
||||||
|
type Type = TraceAsString<T::Type>;
|
||||||
|
type ValueCategory = T::ValueCategory;
|
||||||
|
|
||||||
|
fn ty(&self) -> Self::Type {
|
||||||
|
TraceAsString::new(self.inner.ty())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: ToExpr> ToExpr for TraceAsStringSimValue<T> {
|
||||||
|
fn to_expr(&self) -> Expr<Self::Type> {
|
||||||
|
let inner = self.inner.to_expr();
|
||||||
|
ops::AsTraceAsString::new(Expr::canonical(inner), TraceAsString::new(inner.ty())).to_expr()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: ToSimValueWithType<Ty>, Ty: Type> ToSimValueWithType<TraceAsString<Ty>>
|
||||||
|
for TraceAsStringSimValue<T>
|
||||||
|
{
|
||||||
|
fn to_sim_value_with_type(&self, ty: TraceAsString<Ty>) -> SimValue<TraceAsString<Ty>> {
|
||||||
|
let inner = self.inner.to_sim_value_with_type(ty.inner_ty());
|
||||||
|
let inner = SimValue::into_value(inner);
|
||||||
|
SimValue::from_value(ty, TraceAsStringSimValue { inner })
|
||||||
|
}
|
||||||
|
fn into_sim_value_with_type(self, ty: TraceAsString<Ty>) -> SimValue<TraceAsString<Ty>> {
|
||||||
|
let inner = self.inner.into_sim_value_with_type(ty.inner_ty());
|
||||||
|
let inner = SimValue::into_value(inner);
|
||||||
|
SimValue::from_value(ty, TraceAsStringSimValue { inner })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: ToSimValue> ToSimValue for TraceAsStringSimValue<T> {
|
||||||
|
fn to_sim_value(&self) -> SimValue<Self::Type> {
|
||||||
|
self.to_sim_value_with_type(self.ty())
|
||||||
|
}
|
||||||
|
fn into_sim_value(self) -> SimValue<Self::Type> {
|
||||||
|
let ty = self.ty();
|
||||||
|
self.into_sim_value_with_type(ty)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> std::ops::Deref for TraceAsStringSimValue<T> {
|
||||||
|
type Target = T;
|
||||||
|
|
||||||
|
fn deref(&self) -> &Self::Target {
|
||||||
|
&self.inner
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> std::ops::DerefMut for TraceAsStringSimValue<T> {
|
||||||
|
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||||
|
&mut self.inner
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: fmt::Debug> fmt::Debug for TraceAsStringSimValue<T> {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
let Self { inner } = self;
|
||||||
|
fmt::Debug::fmt(inner, f)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: fmt::Display> fmt::Display for TraceAsStringSimValue<T> {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
let Self { inner } = self;
|
||||||
|
fmt::Display::fmt(inner, f)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: PartialOrd<U>, U> PartialOrd<TraceAsStringSimValue<U>> for TraceAsStringSimValue<T> {
|
||||||
|
fn partial_cmp(&self, other: &TraceAsStringSimValue<U>) -> Option<std::cmp::Ordering> {
|
||||||
|
let Self { inner } = self;
|
||||||
|
inner.partial_cmp(&other.inner)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: PartialEq<U>, U> PartialEq<TraceAsStringSimValue<U>> for TraceAsStringSimValue<T> {
|
||||||
|
fn eq(&self, other: &TraceAsStringSimValue<U>) -> bool {
|
||||||
|
let Self { inner } = self;
|
||||||
|
*inner == other.inner
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -12,7 +12,8 @@ use crate::{
|
||||||
prelude::PhantomConst,
|
prelude::PhantomConst,
|
||||||
reset::{AsyncReset, Reset, SyncReset},
|
reset::{AsyncReset, Reset, SyncReset},
|
||||||
sim::value::DynSimOnly,
|
sim::value::DynSimOnly,
|
||||||
ty::{BaseType, CanonicalType},
|
ty::{BaseType, CanonicalType, TraceAsString, TraceAsStringTrait},
|
||||||
|
util::serde_by_id::SerdeById,
|
||||||
};
|
};
|
||||||
use serde::{Deserialize, Deserializer, Serialize, Serializer};
|
use serde::{Deserialize, Deserializer, Serialize, Serializer};
|
||||||
|
|
||||||
|
|
@ -65,6 +66,10 @@ pub(crate) enum SerdeCanonicalType<
|
||||||
Clock,
|
Clock,
|
||||||
PhantomConst(ThePhantomConst),
|
PhantomConst(ThePhantomConst),
|
||||||
DynSimOnly(DynSimOnly),
|
DynSimOnly(DynSimOnly),
|
||||||
|
TraceAsString {
|
||||||
|
inner_ty: Interned<CanonicalType>,
|
||||||
|
trace_as_string: SerdeById<Interned<dyn TraceAsStringTrait>>,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<ArrayElement, PhantomConstInner> SerdeCanonicalType<ArrayElement, PhantomConstInner> {
|
impl<ArrayElement, PhantomConstInner> SerdeCanonicalType<ArrayElement, PhantomConstInner> {
|
||||||
|
|
@ -82,6 +87,7 @@ impl<ArrayElement, PhantomConstInner> SerdeCanonicalType<ArrayElement, PhantomCo
|
||||||
Self::Clock => "a Clock",
|
Self::Clock => "a Clock",
|
||||||
Self::PhantomConst(_) => "a PhantomConst",
|
Self::PhantomConst(_) => "a PhantomConst",
|
||||||
Self::DynSimOnly(_) => "a SimOnlyValue",
|
Self::DynSimOnly(_) => "a SimOnlyValue",
|
||||||
|
Self::TraceAsString { .. } => "a TraceAsString",
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -109,6 +115,15 @@ impl<T: BaseType> From<T> for SerdeCanonicalType {
|
||||||
CanonicalType::Clock(Clock {}) => Self::Clock,
|
CanonicalType::Clock(Clock {}) => Self::Clock,
|
||||||
CanonicalType::PhantomConst(ty) => Self::PhantomConst(SerdePhantomConst(ty.get())),
|
CanonicalType::PhantomConst(ty) => Self::PhantomConst(SerdePhantomConst(ty.get())),
|
||||||
CanonicalType::DynSimOnly(ty) => Self::DynSimOnly(ty),
|
CanonicalType::DynSimOnly(ty) => Self::DynSimOnly(ty),
|
||||||
|
CanonicalType::TraceAsString(TraceAsString {
|
||||||
|
inner_ty,
|
||||||
|
trace_as_string,
|
||||||
|
}) => Self::TraceAsString {
|
||||||
|
inner_ty: inner_ty.interned(),
|
||||||
|
trace_as_string: SerdeById {
|
||||||
|
inner: trace_as_string.interned(),
|
||||||
|
},
|
||||||
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -130,6 +145,13 @@ impl From<SerdeCanonicalType> for CanonicalType {
|
||||||
Self::PhantomConst(PhantomConst::new_interned(value.0))
|
Self::PhantomConst(PhantomConst::new_interned(value.0))
|
||||||
}
|
}
|
||||||
SerdeCanonicalType::DynSimOnly(value) => Self::DynSimOnly(value),
|
SerdeCanonicalType::DynSimOnly(value) => Self::DynSimOnly(value),
|
||||||
|
SerdeCanonicalType::TraceAsString {
|
||||||
|
inner_ty,
|
||||||
|
trace_as_string,
|
||||||
|
} => Self::TraceAsString(TraceAsString {
|
||||||
|
inner_ty: crate::intern::LazyInterned::Interned(inner_ty),
|
||||||
|
trace_as_string: crate::intern::LazyInterned::Interned(trace_as_string.inner),
|
||||||
|
}),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -46,3 +46,4 @@ pub(crate) use misc::{InternedStrCompareAsStr, chain, copy_le_bytes_to_bitslice}
|
||||||
pub mod job_server;
|
pub mod job_server;
|
||||||
pub mod prefix_sum;
|
pub mod prefix_sum;
|
||||||
pub mod ready_valid;
|
pub mod ready_valid;
|
||||||
|
pub(crate) mod serde_by_id;
|
||||||
|
|
|
||||||
234
crates/fayalite/src/util/serde_by_id.rs
Normal file
234
crates/fayalite/src/util/serde_by_id.rs
Normal file
|
|
@ -0,0 +1,234 @@
|
||||||
|
// SPDX-License-Identifier: LGPL-3.0-or-later
|
||||||
|
// See Notices.txt for copyright information
|
||||||
|
|
||||||
|
use crate::util::HashMap;
|
||||||
|
use hashbrown::hash_map::Entry;
|
||||||
|
use serde::{Deserialize, Serialize, de::Error};
|
||||||
|
use std::{
|
||||||
|
any::TypeId,
|
||||||
|
borrow::Cow,
|
||||||
|
fmt::Write,
|
||||||
|
hash::{BuildHasher, Hash, Hasher},
|
||||||
|
marker::PhantomData,
|
||||||
|
sync::Mutex,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub(crate) struct SerdeByIdProperties<T: SerdeByIdTrait> {
|
||||||
|
type_id: TypeId,
|
||||||
|
type_name: &'static str,
|
||||||
|
_phantom: PhantomData<fn(T) -> T>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: SerdeByIdTrait> Clone for SerdeByIdProperties<T> {
|
||||||
|
fn clone(&self) -> Self {
|
||||||
|
*self
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: SerdeByIdTrait> Copy for SerdeByIdProperties<T> {}
|
||||||
|
|
||||||
|
impl<T: SerdeByIdTrait> SerdeByIdProperties<T> {
|
||||||
|
pub fn of<U: ?Sized + 'static>() -> Self {
|
||||||
|
Self {
|
||||||
|
type_id: TypeId::of::<U>(),
|
||||||
|
type_name: std::any::type_name::<U>(),
|
||||||
|
_phantom: PhantomData,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) trait SerdeByIdTrait: Hash + Eq + Clone + 'static + Send {
|
||||||
|
fn serde_by_id_properties(&self) -> SerdeByIdProperties<Self>;
|
||||||
|
fn static_table() -> &'static SerdeByIdTable<Self>;
|
||||||
|
const NAME: &'static str;
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug, Serialize, Deserialize)]
|
||||||
|
#[serde(transparent)]
|
||||||
|
struct SerdeRandomId([u32; 4]);
|
||||||
|
|
||||||
|
#[derive(Serialize, Deserialize)]
|
||||||
|
pub(crate) struct SerdeId<'a, T: SerdeByIdTrait> {
|
||||||
|
random_id: SerdeRandomId,
|
||||||
|
#[serde(borrow)]
|
||||||
|
type_name: Cow<'a, str>,
|
||||||
|
#[serde(skip)]
|
||||||
|
_phantom: PhantomData<fn(T) -> T>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a, T: SerdeByIdTrait> Clone for SerdeId<'a, T> {
|
||||||
|
fn clone(&self) -> Self {
|
||||||
|
Self {
|
||||||
|
random_id: self.random_id,
|
||||||
|
type_name: self.type_name.clone(),
|
||||||
|
_phantom: PhantomData,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a, T: SerdeByIdTrait> Eq for SerdeId<'a, T> {}
|
||||||
|
|
||||||
|
impl<'a, 'b, T: SerdeByIdTrait> PartialEq<SerdeId<'b, T>> for SerdeId<'a, T> {
|
||||||
|
fn eq(&self, other: &SerdeId<'b, T>) -> bool {
|
||||||
|
let Self {
|
||||||
|
random_id,
|
||||||
|
type_name,
|
||||||
|
_phantom: _,
|
||||||
|
} = self;
|
||||||
|
*random_id == other.random_id && *type_name == other.type_name
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a, T: SerdeByIdTrait> Hash for SerdeId<'a, T> {
|
||||||
|
fn hash<H: Hasher>(&self, state: &mut H) {
|
||||||
|
let Self {
|
||||||
|
random_id,
|
||||||
|
type_name: _,
|
||||||
|
_phantom: _,
|
||||||
|
} = self;
|
||||||
|
random_id.hash(state);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct SerdeByIdTableRest<T: SerdeByIdTrait> {
|
||||||
|
from_serde: HashMap<SerdeId<'static, T>, T>,
|
||||||
|
serde_id_random_state: std::hash::RandomState,
|
||||||
|
buffer: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: SerdeByIdTrait> Default for SerdeByIdTableRest<T> {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self {
|
||||||
|
from_serde: Default::default(),
|
||||||
|
serde_id_random_state: Default::default(),
|
||||||
|
buffer: Default::default(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: SerdeByIdTrait> SerdeByIdTableRest<T> {
|
||||||
|
fn add_new(&mut self, value: T) -> SerdeId<'static, T> {
|
||||||
|
let properties = value.serde_by_id_properties();
|
||||||
|
let mut try_number = 0u64;
|
||||||
|
let mut hasher = self.serde_id_random_state.build_hasher();
|
||||||
|
// extract more bits of randomness from TypeId -- its Hash impl only hashes 64-bits
|
||||||
|
write!(self.buffer, "{:?}", properties.type_id).expect("shouldn't ever fail");
|
||||||
|
self.buffer.hash(&mut hasher);
|
||||||
|
loop {
|
||||||
|
let mut hasher = hasher.clone();
|
||||||
|
try_number.hash(&mut hasher);
|
||||||
|
try_number += 1;
|
||||||
|
let key = SerdeId {
|
||||||
|
random_id: SerdeRandomId(std::array::from_fn(|i| {
|
||||||
|
let mut hasher = hasher.clone();
|
||||||
|
i.hash(&mut hasher);
|
||||||
|
hasher.finish() as u32
|
||||||
|
})),
|
||||||
|
type_name: Cow::Borrowed(properties.type_name),
|
||||||
|
_phantom: PhantomData,
|
||||||
|
};
|
||||||
|
match self.from_serde.entry(key) {
|
||||||
|
Entry::Occupied(_) => continue,
|
||||||
|
Entry::Vacant(e) => {
|
||||||
|
let key = e.key().clone();
|
||||||
|
e.insert(value);
|
||||||
|
return key;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) struct SerdeByIdTableMut<T: SerdeByIdTrait> {
|
||||||
|
to_serde: HashMap<T, SerdeId<'static, T>>,
|
||||||
|
rest: SerdeByIdTableRest<T>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: SerdeByIdTrait> Default for SerdeByIdTableMut<T> {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self {
|
||||||
|
to_serde: Default::default(),
|
||||||
|
rest: Default::default(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: SerdeByIdTrait> SerdeByIdTableMut<T> {
|
||||||
|
pub(crate) fn to_serde(&mut self, value: &T) -> SerdeId<'static, T> {
|
||||||
|
if let Some(retval) = self.to_serde.get(value) {
|
||||||
|
return retval.clone();
|
||||||
|
}
|
||||||
|
self.to_serde_insert(value)
|
||||||
|
}
|
||||||
|
#[cold]
|
||||||
|
fn to_serde_insert(&mut self, value: &T) -> SerdeId<'static, T> {
|
||||||
|
let value = value.clone();
|
||||||
|
let retval = self.rest.add_new(value.clone());
|
||||||
|
self.to_serde.insert(value, retval.clone());
|
||||||
|
retval
|
||||||
|
}
|
||||||
|
pub(crate) fn from_serde(&self, id: &SerdeId<'_, T>) -> Option<T> {
|
||||||
|
self.rest.from_serde.get(id).cloned()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) struct SerdeByIdTable<T: SerdeByIdTrait>(Mutex<Option<SerdeByIdTableMut<T>>>);
|
||||||
|
|
||||||
|
impl<T: SerdeByIdTrait> SerdeByIdTable<T> {
|
||||||
|
pub(crate) const fn new() -> Self {
|
||||||
|
Self(Mutex::new(None))
|
||||||
|
}
|
||||||
|
pub(crate) fn to_serde(&self, value: &T) -> SerdeId<'static, T> {
|
||||||
|
self.0
|
||||||
|
.lock()
|
||||||
|
.expect("shouldn't be poison")
|
||||||
|
.get_or_insert_with(
|
||||||
|
#[cold]
|
||||||
|
|| Default::default(),
|
||||||
|
)
|
||||||
|
.to_serde(value)
|
||||||
|
}
|
||||||
|
pub(crate) fn from_serde(&self, id: &SerdeId<'_, T>) -> Option<T> {
|
||||||
|
self.0
|
||||||
|
.lock()
|
||||||
|
.expect("shouldn't be poison")
|
||||||
|
.get_or_insert_with(
|
||||||
|
#[cold]
|
||||||
|
|| Default::default(),
|
||||||
|
)
|
||||||
|
.from_serde(id)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug, Default, Ord, PartialOrd)]
|
||||||
|
pub(crate) struct SerdeById<T: SerdeByIdTrait> {
|
||||||
|
pub(crate) inner: T,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'de, T: SerdeByIdTrait> Deserialize<'de> for SerdeById<T> {
|
||||||
|
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
||||||
|
where
|
||||||
|
D: serde::Deserializer<'de>,
|
||||||
|
{
|
||||||
|
let id = SerdeId::deserialize(deserializer)?;
|
||||||
|
let inner = T::static_table().from_serde(&id).ok_or_else(|| {
|
||||||
|
D::Error::custom(format_args!(
|
||||||
|
"doesn't match any {} that was serialized this time this program was run: type_name={:?}",
|
||||||
|
T::NAME,
|
||||||
|
id.type_name,
|
||||||
|
))
|
||||||
|
})?;
|
||||||
|
Ok(Self { inner })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: SerdeByIdTrait> Serialize for SerdeById<T> {
|
||||||
|
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
||||||
|
where
|
||||||
|
S: serde::Serializer,
|
||||||
|
{
|
||||||
|
T::static_table()
|
||||||
|
.to_serde(&self.inner)
|
||||||
|
.serialize(serializer)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -51,7 +51,8 @@
|
||||||
"Reset": "Visible",
|
"Reset": "Visible",
|
||||||
"Clock": "Visible",
|
"Clock": "Visible",
|
||||||
"PhantomConst": "Visible",
|
"PhantomConst": "Visible",
|
||||||
"DynSimOnly": "Visible"
|
"DynSimOnly": "Visible",
|
||||||
|
"TraceAsString": "Visible"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"Bundle": {
|
"Bundle": {
|
||||||
|
|
@ -1021,6 +1022,27 @@
|
||||||
"fold_where": "T: Fold<State>",
|
"fold_where": "T: Fold<State>",
|
||||||
"visit_where": "T: Visit<State>"
|
"visit_where": "T: Visit<State>"
|
||||||
},
|
},
|
||||||
|
"ops::AsTraceAsString": {
|
||||||
|
"data": {
|
||||||
|
"$kind": "Struct",
|
||||||
|
"$constructor": "ops::AsTraceAsString::new",
|
||||||
|
"inner()": "Visible",
|
||||||
|
"ty()": "Visible"
|
||||||
|
},
|
||||||
|
"generics": "<T: Type>",
|
||||||
|
"fold_where": "T: Fold<State>",
|
||||||
|
"visit_where": "T: Visit<State>"
|
||||||
|
},
|
||||||
|
"ops::TraceAsStringAsInner": {
|
||||||
|
"data": {
|
||||||
|
"$kind": "Struct",
|
||||||
|
"$constructor": "ops::TraceAsStringAsInner::new",
|
||||||
|
"arg_typed()": "Visible"
|
||||||
|
},
|
||||||
|
"generics": "<T: Type>",
|
||||||
|
"fold_where": "T: Fold<State>",
|
||||||
|
"visit_where": "T: Visit<State>"
|
||||||
|
},
|
||||||
"BlockId": {
|
"BlockId": {
|
||||||
"data": {
|
"data": {
|
||||||
"$kind": "Opaque"
|
"$kind": "Opaque"
|
||||||
|
|
@ -1306,6 +1328,11 @@
|
||||||
"data": {
|
"data": {
|
||||||
"$kind": "ManualImpl"
|
"$kind": "ManualImpl"
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
"TraceAsString": {
|
||||||
|
"data": {
|
||||||
|
"$kind": "ManualImpl"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Loading…
Add table
Add a link
Reference in a new issue