Skip to main content

dada_codegen/cx/
generate_expr.rs

1use std::sync::Arc;
2
3use dada_ir_ast::{ast::PermissionOp, diagnostic::Reported};
4use dada_ir_sym::ir::exprs::{SymBinaryOp, SymExpr, SymExprKind, SymLiteral, SymMatchArm};
5use dada_ir_sym::ir::types::{SymGenericTerm, SymTy, SymTyKind};
6use dada_ir_sym::{
7    ir::primitive::SymPrimitiveKind, ir::subst::Subst, ir::types::SymTyName,
8    ir::variables::SymVariable,
9};
10use dada_util::Map;
11use wasm_encoder::{Instruction, ValType};
12use wasm_place_repr::{WasmLocal, WasmPlaceRepr};
13
14use super::wasm_repr::WasmReprCx;
15use super::{Cx, wasm_repr::WasmRepr};
16
17pub(crate) mod wasm_place_repr;
18
19pub(crate) struct ExprCodegen<'cx, 'db> {
20    cx: &'cx mut Cx<'db>,
21
22    /// Values of any generic variables
23    generics: Map<SymVariable<'db>, SymGenericTerm<'db>>,
24
25    /// Accumulates wasm locals. We make no effort to reduce the number of local variables created.
26    wasm_locals: Vec<wasm_encoder::ValType>,
27
28    /// Local variable that stores starting address in our stack frame
29    wasm_stack_pointer: WasmLocal,
30
31    /// Values we are putting onto the stack frame (actually located in the WASM heap)
32    wasm_stack_frame_size: u32,
33
34    /// Maps each Dada variable to a range of wasm locals. Note that a single value can be inlined into multiple wasm locals.
35    variables: Map<SymVariable<'db>, Arc<WasmPlaceRepr>>,
36
37    /// Accumulates wasm instructions.
38    instructions: Vec<Instruction<'static>>,
39}
40
41impl<'cx, 'db> ExprCodegen<'cx, 'db> {
42    pub fn new(cx: &'cx mut Cx<'db>, generics: Map<SymVariable<'db>, SymGenericTerm<'db>>) -> Self {
43        // Initially there is one local variable, the stack pointer.
44        Self {
45            cx,
46            generics,
47            wasm_locals: vec![ValType::I32],
48            variables: Default::default(),
49            instructions: Default::default(),
50            wasm_stack_frame_size: 0,
51            wasm_stack_pointer: WasmLocal { index: 0 },
52        }
53    }
54
55    pub fn into_function(self) -> wasm_encoder::Function {
56        let mut f = wasm_encoder::Function::new_with_locals_types(self.wasm_locals);
57        for instruction in self.instructions {
58            f.instruction(&instruction);
59        }
60        f
61    }
62
63    /// Returns the [`WasmRepr`][] for a Dada type.
64    pub fn wasm_repr_of_type(&self, ty: SymTy<'db>) -> WasmRepr {
65        let db = self.cx.db;
66        let mut wrcx = WasmReprCx::new(db, &self.generics);
67        wrcx.wasm_repr_of_type(ty)
68    }
69
70    pub fn pop_arguments(&mut self, inputs: &[SymVariable<'db>], input_tys: &[SymTy<'db>]) {
71        assert_eq!(inputs.len(), input_tys.len());
72        for (&input, &input_ty) in inputs.iter().zip(input_tys).rev() {
73            self.insert_variable(input, input_ty);
74            self.pop_and_store(&self.place_for_local(input));
75        }
76        self.instructions
77            .push(Instruction::LocalSet(self.wasm_stack_pointer.index));
78    }
79
80    /// Generate code to execute the expression, leaving the result on the top of the wasm stack.
81    pub fn push_expr(&mut self, expr: SymExpr<'db>) {
82        let db = self.cx.db;
83        match *expr.kind(db) {
84            SymExprKind::Semi(object_expr, object_expr1) => {
85                self.push_expr(object_expr);
86                self.pop_and_drop(object_expr.ty(db));
87                self.push_expr(object_expr1);
88            }
89            SymExprKind::Tuple(ref elements) => {
90                // the representation of a tuple is inlined onto the stack (like any other struct type)
91                for &element in elements {
92                    self.push_expr(element);
93                }
94            }
95            SymExprKind::Primitive(literal) => self.push_literal(expr.ty(db), literal),
96            SymExprKind::LetIn {
97                lv,
98                ty,
99                initializer,
100                body,
101            } => {
102                self.insert_variable(lv, ty);
103
104                if let Some(initializer) = initializer {
105                    self.push_expr(initializer);
106                    self.pop_and_store(&self.variables[&lv].clone());
107                } else {
108                    // FIXME: should zero out the values
109                }
110
111                self.push_expr(body);
112            }
113            SymExprKind::Await {
114                future,
115                await_keyword: _,
116            } => {
117                self.push_expr(future);
118                // FIXME: for now we just ignore futures and execute everything synchronously
119            }
120            SymExprKind::Assign { place, value } => {
121                let wasm_place = self.place(place);
122                self.push_expr(value);
123
124                // FIXME: have to drop the old value
125
126                self.pop_and_store(&wasm_place);
127            }
128            SymExprKind::PermissionOp(permission_op, object_place_expr) => {
129                let wasm_place_repr = self.place(object_place_expr);
130                match permission_op {
131                    PermissionOp::Mutate => {
132                        self.push_leased_from(&wasm_place_repr);
133                    }
134
135                    PermissionOp::Reference => {
136                        self.push_shared_from(&wasm_place_repr);
137                    }
138
139                    PermissionOp::Give => {
140                        self.push_from(&wasm_place_repr);
141                    }
142
143                    PermissionOp::Share => {
144                        todo!()
145                    }
146                }
147            }
148            SymExprKind::Call {
149                function,
150                ref substitution,
151                ref arg_temps,
152            } => {
153                let fn_args = substitution.subst_vars(db, &self.generics);
154                let fn_index = self.cx.declare_fn(function, fn_args);
155
156                // First push the stack pointer for the new function;
157                self.push_pointer(self.next_stack_frame());
158
159                // Now push each of the arguments in turn.
160                for arg_temp in arg_temps {
161                    let place = self.variables[arg_temp].clone();
162                    self.push_from(&place);
163                }
164
165                self.instructions.push(Instruction::Call(fn_index.0));
166            }
167            SymExprKind::Return(object_expr) => {
168                self.push_expr(object_expr);
169                self.instructions.push(Instruction::Return);
170            }
171            SymExprKind::Not {
172                operand,
173                op_span: _,
174            } => {
175                self.push_expr(operand);
176                self.instructions.push(Instruction::I32Const(1));
177                self.instructions.push(Instruction::I32Xor);
178            }
179            SymExprKind::BinaryOp(binary_op, object_expr, object_expr1) => {
180                self.push_expr(object_expr);
181                self.push_expr(object_expr1);
182                self.execute_binary_op(binary_op, object_expr.ty(db), object_expr.ty(db));
183            }
184            SymExprKind::Aggregate { ty, ref fields } => {
185                let wasm_repr = self.wasm_repr_of_type(ty);
186                match wasm_repr {
187                    WasmRepr::Struct(field_reprs) => {
188                        assert_eq!(fields.len(), field_reprs.len());
189                        for &field in fields {
190                            self.push_expr(field);
191                        }
192                    }
193                    WasmRepr::Class(field_reprs) => {
194                        assert_eq!(fields.len(), field_reprs.len());
195
196                        // push flag word
197                        self.instructions.push(Instruction::I32Const(1));
198
199                        for &field in fields {
200                            self.push_expr(field);
201                        }
202                    }
203                    WasmRepr::Val(_) | WasmRepr::Nothing => {
204                        panic!("not an aggregate: {ty:?}")
205                    }
206                }
207            }
208            SymExprKind::Match { ref arms } => {
209                self.push_match_expr(expr.ty(db), arms);
210            }
211            SymExprKind::Error(reported) => self.push_error(reported),
212            #[expect(unused_variables)]
213            SymExprKind::ByteLiteral(sym_byte_literal) => todo!(),
214        }
215    }
216
217    fn pop_and_drop(&mut self, _of_type: SymTy<'db>) {
218        // currently everything is stack allocated, no dropping required
219    }
220
221    pub(super) fn pop_and_return(&mut self, _of_type: SymTy<'db>) {
222        self.instructions.push(Instruction::Return);
223    }
224
225    /// Push the correct instructions to execute `binary_op` on operands of type `lhs_ty` and `rhs_ty`
226    fn execute_binary_op(
227        &mut self,
228        binary_op: SymBinaryOp,
229        lhs_ty: SymTy<'db>,
230        rhs_ty: SymTy<'db>,
231    ) {
232        match self.primitive_kind(lhs_ty) {
233            Ok(prim_kind) => {
234                assert_eq!(self.primitive_kind(rhs_ty), Ok(prim_kind));
235                self.execute_binary_op_on_primitives(binary_op, prim_kind)
236            }
237            Err(e) => match e {
238                NotPrimitive::DeadCode => (),
239                NotPrimitive::OtherType => {
240                    panic!("don't know how to execute a binary op on ({lhs_ty:?}, {rhs_ty:?})")
241                }
242            },
243        }
244    }
245
246    /// Push the correct instructions to execute `binary_op` on operands of type `prim_kind`
247    fn execute_binary_op_on_primitives(
248        &mut self,
249        binary_op: SymBinaryOp,
250        prim_kind: SymPrimitiveKind,
251    ) {
252        let instruction = match (prim_kind, binary_op) {
253            (SymPrimitiveKind::Char, SymBinaryOp::Add)
254            | (SymPrimitiveKind::Char, SymBinaryOp::Sub)
255            | (SymPrimitiveKind::Char, SymBinaryOp::Mul)
256            | (SymPrimitiveKind::Char, SymBinaryOp::Div)
257            | (SymPrimitiveKind::Bool, SymBinaryOp::Add)
258            | (SymPrimitiveKind::Bool, SymBinaryOp::Sub)
259            | (SymPrimitiveKind::Bool, SymBinaryOp::Mul)
260            | (SymPrimitiveKind::Bool, SymBinaryOp::Div) => {
261                panic!("invalid primitive binary op: {binary_op:?}, {prim_kind:?}")
262            }
263
264            (SymPrimitiveKind::Char, SymBinaryOp::GreaterThan)
265            | (SymPrimitiveKind::Bool, SymBinaryOp::GreaterThan) => Instruction::I32GtU,
266
267            (SymPrimitiveKind::Char, SymBinaryOp::LessThan)
268            | (SymPrimitiveKind::Bool, SymBinaryOp::LessThan) => Instruction::I32LtU,
269
270            (SymPrimitiveKind::Char, SymBinaryOp::GreaterEqual)
271            | (SymPrimitiveKind::Bool, SymBinaryOp::GreaterEqual) => Instruction::I32GeU,
272
273            (SymPrimitiveKind::Char, SymBinaryOp::LessEqual)
274            | (SymPrimitiveKind::Bool, SymBinaryOp::LessEqual) => Instruction::I32GeU,
275
276            (SymPrimitiveKind::Char, SymBinaryOp::EqualEqual)
277            | (SymPrimitiveKind::Bool, SymBinaryOp::EqualEqual) => Instruction::I32Eq,
278
279            (SymPrimitiveKind::Int { bits }, SymBinaryOp::Add) if bits <= 32 => Instruction::I32Add,
280            (SymPrimitiveKind::Int { bits }, SymBinaryOp::Sub) if bits <= 32 => Instruction::I32Sub,
281            (SymPrimitiveKind::Int { bits }, SymBinaryOp::Mul) if bits <= 32 => Instruction::I32Mul,
282            (SymPrimitiveKind::Int { bits }, SymBinaryOp::Div) if bits <= 32 => {
283                Instruction::I32DivS
284            }
285            (SymPrimitiveKind::Int { bits }, SymBinaryOp::GreaterThan) if bits <= 32 => {
286                Instruction::I32GtS
287            }
288            (SymPrimitiveKind::Int { bits }, SymBinaryOp::LessThan) if bits <= 32 => {
289                Instruction::I32LtS
290            }
291            (SymPrimitiveKind::Int { bits }, SymBinaryOp::GreaterEqual) if bits <= 32 => {
292                Instruction::I32GeS
293            }
294            (SymPrimitiveKind::Int { bits }, SymBinaryOp::LessEqual) if bits <= 32 => {
295                Instruction::I32LeS
296            }
297            (SymPrimitiveKind::Int { bits }, SymBinaryOp::EqualEqual) if bits <= 32 => {
298                Instruction::I32Eq
299            }
300
301            (SymPrimitiveKind::Int { bits }, SymBinaryOp::Add) if bits <= 64 => Instruction::I64Add,
302            (SymPrimitiveKind::Int { bits }, SymBinaryOp::Sub) if bits <= 64 => Instruction::I64Sub,
303            (SymPrimitiveKind::Int { bits }, SymBinaryOp::Mul) if bits <= 64 => Instruction::I64Mul,
304            (SymPrimitiveKind::Int { bits }, SymBinaryOp::Div) if bits <= 64 => {
305                Instruction::I64DivS
306            }
307            (SymPrimitiveKind::Int { bits }, SymBinaryOp::GreaterThan) if bits <= 64 => {
308                Instruction::I64GtS
309            }
310            (SymPrimitiveKind::Int { bits }, SymBinaryOp::LessThan) if bits <= 64 => {
311                Instruction::I64LtS
312            }
313            (SymPrimitiveKind::Int { bits }, SymBinaryOp::GreaterEqual) if bits <= 64 => {
314                Instruction::I64GeS
315            }
316            (SymPrimitiveKind::Int { bits }, SymBinaryOp::LessEqual) if bits <= 64 => {
317                Instruction::I64LeS
318            }
319            (SymPrimitiveKind::Int { bits }, SymBinaryOp::EqualEqual) if bits <= 64 => {
320                Instruction::I64Eq
321            }
322
323            (SymPrimitiveKind::Isize, SymBinaryOp::Add) => Instruction::I32Add,
324            (SymPrimitiveKind::Isize, SymBinaryOp::Sub) => Instruction::I32Sub,
325            (SymPrimitiveKind::Isize, SymBinaryOp::Mul) => Instruction::I32Mul,
326            (SymPrimitiveKind::Isize, SymBinaryOp::Div) => Instruction::I32DivS,
327            (SymPrimitiveKind::Isize, SymBinaryOp::GreaterThan) => Instruction::I32GtS,
328            (SymPrimitiveKind::Isize, SymBinaryOp::LessThan) => Instruction::I32LtS,
329            (SymPrimitiveKind::Isize, SymBinaryOp::GreaterEqual) => Instruction::I32GeS,
330            (SymPrimitiveKind::Isize, SymBinaryOp::LessEqual) => Instruction::I32LeS,
331            (SymPrimitiveKind::Isize, SymBinaryOp::EqualEqual) => Instruction::I32Eq,
332
333            (SymPrimitiveKind::Uint { bits }, SymBinaryOp::Add) if bits <= 32 => {
334                Instruction::I32Add
335            }
336            (SymPrimitiveKind::Uint { bits }, SymBinaryOp::Sub) if bits <= 32 => {
337                Instruction::I32Sub
338            }
339            (SymPrimitiveKind::Uint { bits }, SymBinaryOp::Mul) if bits <= 32 => {
340                Instruction::I32Mul
341            }
342            (SymPrimitiveKind::Uint { bits }, SymBinaryOp::Div) if bits <= 32 => {
343                Instruction::I32DivU
344            }
345            (SymPrimitiveKind::Uint { bits }, SymBinaryOp::GreaterThan) if bits <= 32 => {
346                Instruction::I32GtU
347            }
348            (SymPrimitiveKind::Uint { bits }, SymBinaryOp::LessThan) if bits <= 32 => {
349                Instruction::I32LtU
350            }
351            (SymPrimitiveKind::Uint { bits }, SymBinaryOp::GreaterEqual) if bits <= 32 => {
352                Instruction::I32GeU
353            }
354            (SymPrimitiveKind::Uint { bits }, SymBinaryOp::LessEqual) if bits <= 32 => {
355                Instruction::I32LeU
356            }
357            (SymPrimitiveKind::Uint { bits }, SymBinaryOp::EqualEqual) if bits <= 32 => {
358                Instruction::I32Eq
359            }
360
361            (SymPrimitiveKind::Uint { bits }, SymBinaryOp::Add) if bits <= 64 => {
362                Instruction::I64Add
363            }
364            (SymPrimitiveKind::Uint { bits }, SymBinaryOp::Sub) if bits <= 64 => {
365                Instruction::I64Sub
366            }
367            (SymPrimitiveKind::Uint { bits }, SymBinaryOp::Mul) if bits <= 64 => {
368                Instruction::I64Mul
369            }
370            (SymPrimitiveKind::Uint { bits }, SymBinaryOp::Div) if bits <= 64 => {
371                Instruction::I64DivU
372            }
373            (SymPrimitiveKind::Uint { bits }, SymBinaryOp::GreaterThan) if bits <= 64 => {
374                Instruction::I64GtU
375            }
376            (SymPrimitiveKind::Uint { bits }, SymBinaryOp::LessThan) if bits <= 64 => {
377                Instruction::I64LtU
378            }
379            (SymPrimitiveKind::Uint { bits }, SymBinaryOp::GreaterEqual) if bits <= 64 => {
380                Instruction::I64GeU
381            }
382            (SymPrimitiveKind::Uint { bits }, SymBinaryOp::LessEqual) if bits <= 64 => {
383                Instruction::I64LeU
384            }
385            (SymPrimitiveKind::Uint { bits }, SymBinaryOp::EqualEqual) if bits <= 64 => {
386                Instruction::I64Eq
387            }
388
389            (SymPrimitiveKind::Usize, SymBinaryOp::Add) => Instruction::I32Add,
390            (SymPrimitiveKind::Usize, SymBinaryOp::Sub) => Instruction::I32Sub,
391            (SymPrimitiveKind::Usize, SymBinaryOp::Mul) => Instruction::I32Mul,
392            (SymPrimitiveKind::Usize, SymBinaryOp::Div) => Instruction::I32DivU,
393            (SymPrimitiveKind::Usize, SymBinaryOp::GreaterThan) => Instruction::I32GtU,
394            (SymPrimitiveKind::Usize, SymBinaryOp::LessThan) => Instruction::I32LtU,
395            (SymPrimitiveKind::Usize, SymBinaryOp::GreaterEqual) => Instruction::I32GeU,
396            (SymPrimitiveKind::Usize, SymBinaryOp::LessEqual) => Instruction::I32LeU,
397            (SymPrimitiveKind::Usize, SymBinaryOp::EqualEqual) => Instruction::I32Eq,
398
399            (SymPrimitiveKind::Float { bits }, SymBinaryOp::Add) if bits <= 32 => {
400                Instruction::F32Add
401            }
402            (SymPrimitiveKind::Float { bits }, SymBinaryOp::Sub) if bits <= 32 => {
403                Instruction::F32Sub
404            }
405            (SymPrimitiveKind::Float { bits }, SymBinaryOp::Mul) if bits <= 32 => {
406                Instruction::F32Mul
407            }
408            (SymPrimitiveKind::Float { bits }, SymBinaryOp::Div) if bits <= 32 => {
409                Instruction::F32Div
410            }
411            (SymPrimitiveKind::Float { bits }, SymBinaryOp::GreaterThan) if bits <= 32 => {
412                Instruction::F32Gt
413            }
414            (SymPrimitiveKind::Float { bits }, SymBinaryOp::LessThan) if bits <= 32 => {
415                Instruction::F32Lt
416            }
417            (SymPrimitiveKind::Float { bits }, SymBinaryOp::GreaterEqual) if bits <= 32 => {
418                Instruction::F32Ge
419            }
420            (SymPrimitiveKind::Float { bits }, SymBinaryOp::LessEqual) if bits <= 32 => {
421                Instruction::F32Le
422            }
423            (SymPrimitiveKind::Float { bits }, SymBinaryOp::EqualEqual) if bits <= 32 => {
424                Instruction::F32Eq
425            }
426
427            (SymPrimitiveKind::Float { bits }, SymBinaryOp::Add) if bits <= 64 => {
428                Instruction::F64Add
429            }
430            (SymPrimitiveKind::Float { bits }, SymBinaryOp::Sub) if bits <= 64 => {
431                Instruction::F64Sub
432            }
433            (SymPrimitiveKind::Float { bits }, SymBinaryOp::Mul) if bits <= 64 => {
434                Instruction::F64Mul
435            }
436            (SymPrimitiveKind::Float { bits }, SymBinaryOp::Div) if bits <= 64 => {
437                Instruction::F64Div
438            }
439            (SymPrimitiveKind::Float { bits }, SymBinaryOp::GreaterThan) if bits <= 64 => {
440                Instruction::F64Gt
441            }
442            (SymPrimitiveKind::Float { bits }, SymBinaryOp::LessThan) if bits <= 64 => {
443                Instruction::F64Lt
444            }
445            (SymPrimitiveKind::Float { bits }, SymBinaryOp::GreaterEqual) if bits <= 64 => {
446                Instruction::F64Ge
447            }
448            (SymPrimitiveKind::Float { bits }, SymBinaryOp::LessEqual) if bits <= 64 => {
449                Instruction::F64Le
450            }
451            (SymPrimitiveKind::Float { bits }, SymBinaryOp::EqualEqual) if bits <= 64 => {
452                Instruction::F64Eq
453            }
454
455            (SymPrimitiveKind::Int { bits: _ }, _)
456            | (SymPrimitiveKind::Uint { bits: _ } | SymPrimitiveKind::Float { bits: _ }, _) => {
457                panic!("invalid number of bits for scalar: {prim_kind:?}")
458            }
459        };
460
461        self.instructions.push(instruction);
462    }
463
464    /// Return the primitive kind that represents `ty` or `Err` if `ty` is not a primitive.
465    fn primitive_kind(&self, ty: SymTy<'db>) -> Result<SymPrimitiveKind, NotPrimitive> {
466        let db = self.cx.db;
467        match ty.kind(db) {
468            SymTyKind::Named(ty_name, _ty_args) => match ty_name {
469                SymTyName::Primitive(sym_primitive) => Ok(sym_primitive.kind(db)),
470                SymTyName::Aggregate(_) | SymTyName::Future | SymTyName::Tuple { arity: _ } => {
471                    Err(NotPrimitive::OtherType)
472                }
473            },
474            SymTyKind::Var(sym_variable) => {
475                self.primitive_kind(self.generics[sym_variable].assert_type(db))
476            }
477            SymTyKind::Never | SymTyKind::Error(_) => Err(NotPrimitive::DeadCode),
478            SymTyKind::Infer(_) => panic!("unexpected inference variable"),
479            #[expect(unused_variables)]
480            SymTyKind::Perm(sym_perm, sym_ty) => todo!(),
481        }
482    }
483
484    fn push_match_expr(&mut self, match_ty: SymTy<'db>, arms: &[SymMatchArm<'db>]) {
485        let Some((if_arm, else_arms)) = arms.split_first() else {
486            return;
487        };
488
489        if let Some(condition) = if_arm.condition {
490            // Evaluate the condition.
491            self.push_expr(condition);
492
493            // The `If` block will execute the next set of instructions
494            // if the condition was true. Otherwise it will skip to the `Else` or `End.`
495            let block_type = self.block_type(match_ty);
496            self.instructions.push(Instruction::If(block_type));
497
498            // Code to execute if true.
499            self.push_expr(if_arm.body);
500
501            // If false push an `Else` and evaluate it recursively.
502            self.instructions.push(Instruction::Else);
503            self.push_match_expr(match_ty, else_arms);
504
505            // End the if.
506            self.instructions.push(Instruction::End);
507        } else {
508            // Execute body unconditionally.
509            self.push_expr(if_arm.body);
510
511            // Any remaining arms are ignored.
512            let _ = else_arms;
513        }
514    }
515
516    /// [Block control-flow instructions][cfi] like `if` and friends
517    /// come equipped with an associated "block type". This is a function
518    /// type indicating the *inputs* they consume from the stack (in our case,
519    /// always none) and the *outputs* they produce. As a shorthand, if they produce
520    /// nothing or a single value, there is a shorthand form. This function converts
521    /// an object-type into this form.
522    ///
523    /// [cfi]: https://webassembly.github.io/spec/core/syntax/instructions.html#control-instructions
524    fn block_type(&mut self, match_ty: SymTy<'db>) -> wasm_encoder::BlockType {
525        let val_types = self.wasm_repr_of_type(match_ty).flatten();
526        match val_types.len() {
527            0 => wasm_encoder::BlockType::Empty,
528            1 => wasm_encoder::BlockType::Result(val_types[0]),
529            _ => wasm_encoder::BlockType::FunctionType(u32::from(
530                self.cx.declare_fn_type(vec![], val_types),
531            )),
532        }
533    }
534
535    fn push_literal(&mut self, ty: SymTy<'db>, literal: SymLiteral) {
536        let db = self.cx.db;
537        let kind = match ty.kind(db) {
538            SymTyKind::Named(sym_ty_name, _) => match sym_ty_name {
539                SymTyName::Primitive(sym_primitive) => sym_primitive.kind(db),
540                SymTyName::Aggregate(_) | SymTyName::Future | SymTyName::Tuple { arity: _ } => {
541                    panic!("unexpected type for literal {literal:?}: {ty:?}")
542                }
543            },
544            SymTyKind::Var(sym_variable) => {
545                return self.push_literal(self.generics[sym_variable].assert_type(db), literal);
546            }
547            SymTyKind::Infer(_) | SymTyKind::Never => {
548                panic!("unexpected type for literal {literal:?}: {ty:?}")
549            }
550            SymTyKind::Error(reported) => {
551                return self.push_error(*reported);
552            }
553            #[expect(unused_variables)]
554            SymTyKind::Perm(sym_perm, sym_ty) => todo!(),
555        };
556        match kind {
557            SymPrimitiveKind::Bool
558            | SymPrimitiveKind::Isize
559            | SymPrimitiveKind::Usize
560            | SymPrimitiveKind::Char => {
561                let SymLiteral::Integral { bits } = literal else {
562                    panic!("expected integral {literal:?}");
563                };
564                self.instructions.push(Instruction::I32Const(bits as i32));
565            }
566            SymPrimitiveKind::Int { bits } | SymPrimitiveKind::Uint { bits } if bits <= 32 => {
567                let SymLiteral::Integral { bits } = literal else {
568                    panic!("expected integral {literal:?}");
569                };
570                self.instructions.push(Instruction::I32Const(bits as i32));
571            }
572            SymPrimitiveKind::Int { bits } | SymPrimitiveKind::Uint { bits } if bits <= 64 => {
573                let SymLiteral::Integral { bits } = literal else {
574                    panic!("expected integral {literal:?}");
575                };
576                self.instructions.push(Instruction::I64Const(bits as i64));
577            }
578            SymPrimitiveKind::Float { bits } if bits <= 32 => {
579                let SymLiteral::Float { bits } = literal else {
580                    panic!("expected float {literal:?}");
581                };
582                self.instructions.push(Instruction::F32Const(bits.0 as f32));
583            }
584            SymPrimitiveKind::Float { bits } if bits <= 32 => {
585                let SymLiteral::Float { bits } = literal else {
586                    panic!("expected float {literal:?}");
587                };
588                self.instructions.push(Instruction::F64Const(bits.0));
589            }
590            SymPrimitiveKind::Int { .. }
591            | SymPrimitiveKind::Uint { .. }
592            | SymPrimitiveKind::Float { .. } => {
593                panic!("unexpected kind: {kind:?}");
594            }
595        }
596    }
597
598    fn push_error(&mut self, _reported: Reported) {
599        self.instructions.push(Instruction::Unreachable);
600    }
601}
602
603/// Error `enum` for [`ExprCodegen::primitive_kind`].
604#[derive(Debug, PartialEq, Eq, Copy, Clone)]
605enum NotPrimitive {
606    DeadCode,
607    OtherType,
608}