279 lines
8.2 KiB
Rust
279 lines
8.2 KiB
Rust
use std::fmt::Debug;
|
|
/// Alias for &TypeStruct to reduce repetition
|
|
/// and make futura migration to interning
|
|
/// easier
|
|
pub type Type = InternedType;
|
|
|
|
#[derive(Copy, Clone, PartialEq)]
|
|
pub struct InternedType(usize);
|
|
|
|
impl Debug for InternedType {
|
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
|
write!(f, "Type({})", self.0)
|
|
}
|
|
}
|
|
|
|
#[derive(Debug)]
|
|
pub struct TypeStruct {
|
|
kind: TypeKind,
|
|
}
|
|
|
|
#[derive(Debug)]
|
|
enum TypeKind {
|
|
/// Elaboration-time types
|
|
ElabType(ElabKind),
|
|
/// Signal/Wire of generic width
|
|
Logic(ElabData),
|
|
/// UInt of generic width
|
|
UInt(ElabData),
|
|
/// Callable
|
|
Callable(FnSig),
|
|
/// A type that was not given and needs to be inferred
|
|
Infer,
|
|
/// A reference to a type variable as DeBruijn index
|
|
/// (scope, param)
|
|
TypeVar(u32, u32),
|
|
}
|
|
|
|
#[derive(Debug, Clone)]
|
|
pub struct FnSig {
|
|
params: Vec<Type>,
|
|
ret: Type,
|
|
}
|
|
|
|
#[derive(Debug, Clone)]
|
|
pub struct ElabData {
|
|
pub typ: Type,
|
|
value: ElabValue,
|
|
}
|
|
|
|
#[derive(Debug, Clone)]
|
|
enum ElabValue {
|
|
/// the value is not given and has to be inferred
|
|
Infer,
|
|
/// the value is given as some byte representation
|
|
Concrete(ElabValueData),
|
|
}
|
|
|
|
#[derive(Debug, Clone)]
|
|
enum ElabValueData {
|
|
U32(u32),
|
|
Bytes(Vec<u8>),
|
|
}
|
|
|
|
/// Types that are only valid during Elaboration
|
|
#[derive(Debug)]
|
|
enum ElabKind {
|
|
/// general, unsized number type
|
|
Num,
|
|
}
|
|
|
|
#[derive(Debug)]
|
|
pub enum GenericArg {
|
|
Elab(ElabData),
|
|
Type(Type),
|
|
}
|
|
|
|
#[derive(Debug)]
|
|
pub enum InferenceResult {
|
|
/// The first type was inferred
|
|
First(Type),
|
|
/// The second type was inferred
|
|
Second(Type),
|
|
/// A typevar was inferred
|
|
TypeVar(u32, u32, Type),
|
|
/// The types were incompatible
|
|
Incompatible,
|
|
/// Neither of the types were complete
|
|
Ambigous,
|
|
}
|
|
|
|
pub struct PrimitiveTypes {
|
|
pub elabnum: Type,
|
|
pub logic: Type,
|
|
pub infer: Type,
|
|
}
|
|
|
|
pub struct TypingContext {
|
|
types: Vec<TypeStruct>,
|
|
pub primitives: PrimitiveTypes,
|
|
}
|
|
|
|
impl TypingContext {
|
|
pub fn new() -> Self {
|
|
let primitives = PrimitiveTypes {
|
|
elabnum: InternedType(0),
|
|
logic: InternedType(1),
|
|
infer: InternedType(2),
|
|
};
|
|
Self {
|
|
types: vec![
|
|
TypeStruct {
|
|
kind: TypeKind::ElabType(ElabKind::Num),
|
|
},
|
|
TypeStruct {
|
|
kind: TypeKind::Logic(ElabData {
|
|
typ: primitives.elabnum,
|
|
value: ElabValue::Infer,
|
|
}),
|
|
},
|
|
TypeStruct {
|
|
kind: TypeKind::Infer,
|
|
},
|
|
],
|
|
primitives,
|
|
}
|
|
}
|
|
|
|
pub fn add(&mut self, typ: TypeStruct) -> Type {
|
|
let id = self.types.len();
|
|
self.types.push(typ);
|
|
InternedType(id)
|
|
}
|
|
|
|
pub fn get(&self, typ: Type) -> &TypeStruct {
|
|
&self.types[typ.0]
|
|
}
|
|
|
|
pub fn make_elabnum_u32(&self, num: u32) -> ElabData {
|
|
ElabData {
|
|
typ: self.primitives.elabnum,
|
|
value: ElabValue::Concrete(ElabValueData::U32(num)),
|
|
}
|
|
}
|
|
|
|
pub fn make_logic_size(&mut self, width: u32) -> Type {
|
|
let widthnum = self.make_elabnum_u32(width);
|
|
self.parameterize(self.primitives.logic, &[GenericArg::Elab(widthnum)])
|
|
.unwrap()
|
|
}
|
|
|
|
pub fn make_typevar(&mut self, dbi: u32, tvar: u32) -> Type {
|
|
self.add(TypeStruct {
|
|
kind: TypeKind::TypeVar(dbi, tvar),
|
|
})
|
|
}
|
|
|
|
pub fn get_width(&self, typ: Type) -> Option<u32> {
|
|
match &self.get(typ).kind {
|
|
TypeKind::ElabType(_) => None,
|
|
TypeKind::Logic(data) => match &data.value {
|
|
ElabValue::Infer => None,
|
|
ElabValue::Concrete(val) => match val {
|
|
ElabValueData::U32(val) => Some(*val),
|
|
ElabValueData::Bytes(_) => None,
|
|
},
|
|
},
|
|
TypeKind::UInt(_) => todo!(),
|
|
TypeKind::Callable(_) => None,
|
|
TypeKind::Infer => None,
|
|
TypeKind::TypeVar(_, _) => None,
|
|
}
|
|
}
|
|
|
|
pub fn parameterize(&mut self, typ: Type, params: &[GenericArg]) -> Option<Type> {
|
|
// TODO: return proper error type here
|
|
match &self.get(typ).kind {
|
|
// Elab types have no params yet
|
|
TypeKind::ElabType(_) => None,
|
|
TypeKind::Logic(_) => {
|
|
if params.len() != 1 {
|
|
// invalid number of typeargs
|
|
return None;
|
|
};
|
|
let param = ¶ms[0];
|
|
if let GenericArg::Elab(data) = param {
|
|
if data.typ == self.primitives.elabnum {
|
|
Some(self.add(TypeStruct {
|
|
kind: TypeKind::Logic(data.clone()),
|
|
}))
|
|
} else {
|
|
// arg must be elabnum
|
|
None
|
|
}
|
|
} else {
|
|
// arg must be an elab value
|
|
None
|
|
}
|
|
}
|
|
TypeKind::UInt(_) => todo!(),
|
|
TypeKind::Callable(_sig) => todo!("callable generic params"),
|
|
// need to know what the type is to parameterize it
|
|
TypeKind::Infer | &TypeKind::TypeVar(_, _) => None,
|
|
}
|
|
}
|
|
|
|
/// Given the type of a variable in two locations, infer what the true type should be
|
|
pub fn infer_type(&mut self, typ_a: Type, typ_b: Type) -> InferenceResult {
|
|
match (&self.get(typ_a).kind, &self.get(typ_b).kind) {
|
|
(TypeKind::TypeVar(dbi, tvar), _) => InferenceResult::TypeVar(*dbi, *tvar, typ_b),
|
|
(_, TypeKind::TypeVar(dbi, tvar)) => InferenceResult::TypeVar(*dbi, *tvar, typ_a),
|
|
(a, b) => {
|
|
let a_full = self.is_fully_typed_kind(a);
|
|
let b_full = self.is_fully_typed_kind(b);
|
|
if a_full && b_full {
|
|
InferenceResult::Incompatible
|
|
} else if a_full && !b_full {
|
|
InferenceResult::Second(typ_a)
|
|
} else if !a_full && b_full {
|
|
InferenceResult::First(typ_b)
|
|
} else {
|
|
InferenceResult::Ambigous
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
fn is_fully_typed_kind(&self, kind: &TypeKind) -> bool {
|
|
match kind {
|
|
TypeKind::ElabType(_) => todo!(),
|
|
TypeKind::Logic(data) => {
|
|
if let ElabValue::Concrete(_) = data.value {
|
|
true
|
|
} else {
|
|
false
|
|
}
|
|
}
|
|
TypeKind::UInt(_) => todo!(),
|
|
TypeKind::Callable(_) => todo!(),
|
|
TypeKind::Infer => false,
|
|
TypeKind::TypeVar(dbi, _tvar) => {
|
|
// if the DeBruijn index is 0, there is no further information to gain
|
|
// from a surrounding scope
|
|
*dbi != 0
|
|
}
|
|
}
|
|
}
|
|
|
|
/// return whether the type has no unfilled parameters
|
|
pub fn is_fully_typed(&self, typ: Type) -> bool {
|
|
self.is_fully_typed_kind(&self.get(typ).kind)
|
|
}
|
|
|
|
pub fn pretty_value(&self, w: &mut dyn std::fmt::Write, data: &ElabData) -> std::fmt::Result {
|
|
match &data.value {
|
|
ElabValue::Infer => write!(w, "?: ")?,
|
|
ElabValue::Concrete(val) => match val {
|
|
ElabValueData::U32(val) => write!(w, "{:?}: ", val)?,
|
|
ElabValueData::Bytes(val) => write!(w, "{:?}: ", val)?,
|
|
},
|
|
}
|
|
self.pretty_type(w, data.typ)
|
|
}
|
|
|
|
pub fn pretty_type(&self, w: &mut dyn std::fmt::Write, typ: Type) -> std::fmt::Result {
|
|
match &self.get(typ).kind {
|
|
TypeKind::ElabType(val) => write!(w, "{:?}", val),
|
|
TypeKind::Logic(val) => {
|
|
let mut width = String::new();
|
|
self.pretty_value(&mut width, val)?;
|
|
write!(w, "Logic<{}>", width)
|
|
}
|
|
TypeKind::Infer => write!(w, "?"),
|
|
TypeKind::UInt(_) => todo!("print uint"),
|
|
TypeKind::Callable(_sig) => todo!("print callable"),
|
|
TypeKind::TypeVar(_, tvar) => write!(w, "T{}", tvar),
|
|
}
|
|
}
|
|
}
|