lyte/
type.rs

1use crate::{Generics, RefID, TypesBuf};
2use itertools::Itertools;
3use std::fmt;
4use std::hash::Hash;
5
6pub trait TypeData: Sized + fmt::Debug + Clone + PartialEq {
7    type Concrete: Key;
8    type Generic: Key;
9    type Trait: Key;
10    type Association: Key;
11
12    type Meta: Clone + fmt::Debug + Default;
13
14    /// Gets instantiated during formatting so that fancy containers like tuples can be pretty printed
15    fn fmt_specific(
16        constr: &Self::Concrete,
17        t: &Type<Self>,
18        f: &mut fmt::Formatter,
19    ) -> fmt::Result {
20        t.fmt_with_params(constr, f)
21    }
22
23    /// Generate the next unused generic. Used when lifting infered types into top-level declerations
24    fn first_available(forall: &Generics<Self>) -> Self::Generic;
25}
26
27pub trait Key: PartialEq + Eq + fmt::Debug + Clone + Hash + fmt::Display {}
28
29impl<'a> Key for &'a str {}
30impl Key for usize {}
31impl Key for u32 {}
32impl Key for u16 {}
33impl Key for u8 {}
34impl Key for char {}
35impl Key for String {}
36
37#[derive(Debug, Clone)]
38pub struct Type<D: TypeData> {
39    pub constr: TypeKind<D>,
40    pub meta: D::Meta,
41    pub params: Vec<Self>,
42}
43
44#[derive(Debug, Clone, PartialEq, Eq)]
45pub enum TypeKind<D: TypeData> {
46    Generic(D::Generic),
47    Concrete(D::Concrete),
48    Ref(RefID),
49    Self_,
50    // Association(usize),
51    // REMEMBER: Object-safety rules must be rather strict
52    //
53    // fn foo(self: Option<&Self>);
54    //
55    // this would not be allowed because there's no V-table for `None`
56    Object(D::Trait),
57}
58
59impl<D: TypeData> Type<D> {
60    #[must_use]
61    pub fn with_params(mut self, params: TypesBuf<D>) -> Self {
62        assert!(
63            std::mem::replace(&mut self.params, params).is_empty(),
64            "`with_params` called onto type already containing parameters"
65        );
66        self
67    }
68    pub fn concrete(meta: D::Meta, constr: D::Concrete, params: TypesBuf<D>) -> Self {
69        Self {
70            params,
71            meta,
72            constr: TypeKind::Concrete(constr),
73        }
74    }
75
76    pub fn generic(meta: D::Meta, constr: D::Generic, params: TypesBuf<D>) -> Self {
77        Self {
78            params,
79            meta,
80            constr: TypeKind::Generic(constr),
81        }
82    }
83
84    pub fn object(meta: D::Meta, trait_: D::Trait, params: TypesBuf<D>) -> Self {
85        Self {
86            constr: TypeKind::Object(trait_),
87            meta,
88            params,
89        }
90    }
91
92    pub fn self_(meta: D::Meta, params: TypesBuf<D>) -> Self {
93        Self {
94            constr: TypeKind::Self_,
95            meta,
96            params,
97        }
98    }
99
100    pub fn reference(meta: D::Meta, rid: RefID, hkt: TypesBuf<D>) -> Self {
101        Self {
102            params: hkt,
103            meta,
104            constr: TypeKind::Ref(rid),
105        }
106    }
107
108    pub fn direct_eq(&self, other: &Self) -> bool {
109        self.constr == other.constr
110            && self
111                .params
112                .iter()
113                .zip(&other.params)
114                .all(|(s, o)| s.direct_eq(o))
115    }
116}
117
118impl<D: TypeData> TypeKind<D> {
119    pub fn describe(&self) -> &'static str {
120        match self {
121            TypeKind::Concrete(_) => "concrete",
122            TypeKind::Ref(_) => "inference",
123            TypeKind::Object(_) => "trait object",
124            TypeKind::Generic(_) => "generic",
125            TypeKind::Self_ => "self",
126        }
127    }
128}
129
130impl<D: TypeData> fmt::Display for Type<D> {
131    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
132        match &self.constr {
133            TypeKind::Concrete(c) => D::fmt_specific(c, self, f),
134            TypeKind::Generic(g) => self.fmt_with_params(g, f),
135            TypeKind::Object(trid) => self.fmt_with_params(format!("dyn {}", trid), f),
136            TypeKind::Ref(rid) if *rid > 25 => self.fmt_with_params(format!("'{}", rid), f),
137            TypeKind::Ref(rid) => {
138                self.fmt_with_params(format!("'{}", (*rid as u8 + b'a') as char), f)
139            }
140            TypeKind::Self_ => self.fmt_with_params("self", f),
141        }
142    }
143}
144
145impl<D: TypeData> fmt::Display for TypeKind<D> {
146    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
147        match &self {
148            TypeKind::Concrete(c) => c.fmt(f),
149            TypeKind::Generic(g) => g.fmt(f),
150            TypeKind::Object(trid) => trid.fmt(f),
151            TypeKind::Ref(rid) if *rid > 25 => write!(f, "'{}", rid),
152            TypeKind::Ref(rid) => {
153                write!(f, "'{}", (*rid as u8 + b'a') as char)
154            }
155            TypeKind::Self_ => "self".fmt(f),
156        }
157    }
158}
159
160impl<D: TypeData> Type<D> {
161    // TODO: we don't want stuff like ((f a) -> a) when we can have (f a -> a)
162    //
163    // i guess we can handle that in fmt_specific?
164    pub fn fmt_with_params(&self, v: impl fmt::Display, f: &mut fmt::Formatter) -> fmt::Result {
165        if self.params.is_empty() {
166            v.fmt(f)
167        } else {
168            write!(f, "({} {})", v, self.params.iter().format(" "))
169        }
170    }
171}