Skip to main content

dada_ir_sym/ir/
exprs.rs

1//! The "object IR" is an intermediate IR that we create
2//! as a first pass for type checking. The name "object"
3//! derives from the fact that it doesn't track precise
4//! types, but rather just the type of the underlying
5//! object without any permissions (i.e., what class/struct/enum/etc is it?).
6//! This can then be used to bootstrap full type checking.
7//!
8//! We need to create this IR first because full type checking will
9//! require knowing which variables are live. Knowing that
10//! requires that we have fully parsed the AST. But fully parsing
11//! the AST requires being able to disambiguate things like `x.foo[..]()`,
12//! which could be either indexing a field `foo` and then calling the
13//! result or invoking a method `foo` with generic arguments.
14//! The object IR gives us enough information to make those determinations.
15
16use crate::{
17    ir::classes::SymField,
18    ir::functions::SymFunction,
19    ir::types::{SymGenericTerm, SymPlace, SymTy},
20    ir::variables::{FromVar, SymVariable},
21};
22use dada_ir_ast::{
23    ast::{AstBinaryOp, PermissionOp},
24    diagnostic::{Err, Reported},
25    span::{SourceSpanned, Span},
26};
27use dada_util::SalsaSerialize;
28use ordered_float::OrderedFloat;
29use salsa::Update;
30use serde::Serialize;
31
32#[derive(SalsaSerialize)]
33#[salsa::tracked(debug)]
34pub struct SymExpr<'db> {
35    /// Span of this expression in the source file.
36    ///
37    /// This must represent the entire expression for
38    /// IDE integration to work, as we use it to compute
39    /// hover information and things like that.
40    pub span: Span<'db>,
41
42    /// Type of the value produced by this expression.
43    pub ty: SymTy<'db>,
44
45    /// Expression kind.
46    #[return_ref]
47    pub kind: SymExprKind<'db>,
48}
49
50impl<'db> SymExpr<'db> {
51    /// Create an expression like `false`
52    pub(crate) fn false_literal(db: &'db dyn crate::Db, span: Span<'db>) -> SymExpr<'db> {
53        SymExpr::new(
54            db,
55            span,
56            SymTy::boolean(db),
57            SymExprKind::Primitive(SymLiteral::Integral { bits: 0 }),
58        )
59    }
60
61    /// Create an expression like `true`
62    pub(crate) fn true_literal(db: &'db dyn crate::Db, span: Span<'db>) -> SymExpr<'db> {
63        SymExpr::new(
64            db,
65            span,
66            SymTy::boolean(db),
67            SymExprKind::Primitive(SymLiteral::Integral { bits: 1 }),
68        )
69    }
70
71    /// Create an expression like `if $condition { $if_true } else { $if_false }`
72    pub(crate) fn if_then_else(
73        db: &'db dyn crate::Db,
74        span: Span<'db>,
75        condition: SymExpr<'db>,
76        if_true: SymExpr<'db>,
77        if_false: SymExpr<'db>,
78    ) -> SymExpr<'db> {
79        SymExpr::new(
80            db,
81            span,
82            SymTy::boolean(db),
83            SymExprKind::Match {
84                arms: vec![
85                    SymMatchArm {
86                        condition: Some(condition),
87                        body: if_true,
88                    },
89                    SymMatchArm {
90                        condition: None,
91                        body: if_false,
92                    },
93                ],
94            },
95        )
96    }
97}
98
99impl<'db> SourceSpanned<'db> for SymExpr<'db> {
100    fn source_span(&self, db: &'db dyn dada_ir_ast::Db) -> Span<'db> {
101        self.span(db)
102    }
103}
104
105impl<'db> Err<'db> for SymExpr<'db> {
106    fn err(db: &'db dyn dada_ir_ast::Db, r: Reported) -> Self {
107        SymExpr::new(db, r.span(db), SymTy::err(db, r), SymExprKind::Error(r))
108    }
109}
110
111#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug, Update, Serialize)]
112pub enum SymExprKind<'db> {
113    /// `$expr1; $expr2`
114    Semi(SymExpr<'db>, SymExpr<'db>),
115
116    /// `(...)`
117    Tuple(Vec<SymExpr<'db>>),
118
119    /// `22` etc
120    Primitive(SymLiteral),
121
122    /// `b"..."`
123    ByteLiteral(SymByteLiteral<'db>),
124
125    /// `let $lv: $ty [= $initializer] in $body`
126    LetIn {
127        lv: SymVariable<'db>,
128        ty: SymTy<'db>,
129        initializer: Option<SymExpr<'db>>,
130        body: SymExpr<'db>,
131    },
132
133    /// `future.await`
134    Await {
135        future: SymExpr<'db>,
136        await_keyword: Span<'db>,
137    },
138
139    /// `$place = $expr`
140    Assign {
141        place: SymPlaceExpr<'db>,
142        value: SymExpr<'db>,
143    },
144
145    /// `$0.lease` etc
146    PermissionOp(PermissionOp, SymPlaceExpr<'db>),
147
148    /// `$0[$1..]($2..)`
149    ///
150    /// During construction we ensure that the arities match and terms are well-kinded
151    /// (or generate errors).
152    Call {
153        function: SymFunction<'db>,
154        substitution: Vec<SymGenericTerm<'db>>,
155        arg_temps: Vec<SymVariable<'db>>,
156    },
157
158    /// Return a value from this function
159    Return(SymExpr<'db>),
160
161    /// Boolean not
162    Not {
163        operand: SymExpr<'db>,
164        op_span: Span<'db>,
165    },
166
167    /// `a + b` etc
168    BinaryOp(SymBinaryOp, SymExpr<'db>, SymExpr<'db>),
169
170    /// Something like `Point { x: ..., y: ... }`
171    Aggregate {
172        ty: SymTy<'db>,
173        fields: Vec<SymExpr<'db>>,
174    },
175
176    /// Match, if/else-if chain, etc
177    Match { arms: Vec<SymMatchArm<'db>> },
178
179    /// Error occurred somewhere.
180    Error(Reported),
181}
182
183#[derive(SalsaSerialize)]
184#[salsa::tracked(debug)]
185pub struct SymByteLiteral<'db> {
186    pub span: Span<'db>,
187    pub data: SymByteLiteralData<'db>,
188}
189
190#[derive(SalsaSerialize)]
191#[salsa::interned(debug)]
192pub struct SymByteLiteralData<'db> {
193    pub value: Vec<u8>,
194}
195
196#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug, Update, Serialize)]
197pub enum SymLiteral {
198    /// Have to check the type of the expression to determine how to interpret these bits
199    Integral { bits: u64 },
200
201    /// Have to check the type of the expression to determine how to interpret these bits
202    Float { bits: OrderedFloat<f64> },
203}
204
205#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Update, Debug, Serialize)]
206pub enum SymBinaryOp {
207    Add,
208    Sub,
209    Mul,
210    Div,
211    GreaterThan,
212    LessThan,
213    GreaterEqual,
214    LessEqual,
215    EqualEqual,
216}
217
218impl TryFrom<AstBinaryOp> for SymBinaryOp {
219    type Error = dada_util::Error;
220
221    fn try_from(value: AstBinaryOp) -> Result<Self, Self::Error> {
222        match value {
223            AstBinaryOp::Add => Ok(SymBinaryOp::Add),
224            AstBinaryOp::Sub => Ok(SymBinaryOp::Sub),
225            AstBinaryOp::Mul => Ok(SymBinaryOp::Mul),
226            AstBinaryOp::Div => Ok(SymBinaryOp::Div),
227            AstBinaryOp::GreaterThan => Ok(SymBinaryOp::GreaterThan),
228            AstBinaryOp::LessThan => Ok(SymBinaryOp::LessThan),
229            AstBinaryOp::GreaterEqual => Ok(SymBinaryOp::GreaterEqual),
230            AstBinaryOp::LessEqual => Ok(SymBinaryOp::LessEqual),
231            AstBinaryOp::EqualEqual => Ok(SymBinaryOp::EqualEqual),
232            AstBinaryOp::AndAnd | AstBinaryOp::OrOr | AstBinaryOp::Assign => {
233                dada_util::bail!("no equivalent object binary op")
234            }
235        }
236    }
237}
238
239/// A match arm is one part of a match statement.
240#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug, Update, Serialize)]
241pub struct SymMatchArm<'db> {
242    // FIXME: patterns
243    /// Condition to evaluate; if `None` then it always applies
244    pub condition: Option<SymExpr<'db>>,
245
246    /// Body to evaluate.
247    pub body: SymExpr<'db>,
248}
249
250#[derive(SalsaSerialize)]
251#[salsa::tracked(debug)]
252pub struct SymPlaceExpr<'db> {
253    pub span: Span<'db>,
254    pub ty: SymTy<'db>,
255
256    #[return_ref]
257    pub kind: SymPlaceExprKind<'db>,
258}
259
260impl<'db> Err<'db> for SymPlaceExpr<'db> {
261    fn err(db: &'db dyn dada_ir_ast::Db, r: Reported) -> Self {
262        SymPlaceExpr::new(
263            db,
264            r.span(db),
265            SymTy::err(db, r),
266            SymPlaceExprKind::Error(r),
267        )
268    }
269}
270
271impl<'db> SymPlaceExpr<'db> {
272    pub fn give(self, db: &'db dyn crate::Db) -> SymExpr<'db> {
273        SymExpr::new(
274            db,
275            self.span(db),
276            self.ty(db),
277            SymExprKind::PermissionOp(PermissionOp::Give, self),
278        )
279    }
280
281    pub fn into_sym_place(self, db: &'db dyn crate::Db) -> SymPlace<'db> {
282        match *self.kind(db) {
283            SymPlaceExprKind::Var(lv) => SymPlace::var(db, lv),
284            SymPlaceExprKind::Field(place, field) => place.into_sym_place(db).field(db, field),
285            SymPlaceExprKind::Error(r) => SymPlace::err(db, r),
286        }
287    }
288}
289
290#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug, Update, Serialize)]
291pub enum SymPlaceExprKind<'db> {
292    Var(SymVariable<'db>),
293    Field(SymPlaceExpr<'db>, SymField<'db>),
294    Error(Reported),
295}