spade_hir_lowering/
lib.rs

1mod attributes;
2mod const_generic;
3pub mod error;
4mod linear_check;
5pub mod monomorphisation;
6pub mod name_map;
7pub mod passes;
8mod pattern;
9pub mod pipelines;
10mod statement_list;
11pub mod substitution;
12mod usefulness;
13
14use std::collections::BTreeMap;
15use std::sync::RwLock;
16
17use attributes::AttributeListExt;
18use attributes::LocAttributeExt;
19use const_generic::ConstGenericExt;
20use error::format_witnesses;
21use error::refutable_pattern_diagnostic;
22use error::undefined_variable;
23use hir::expression::CallKind;
24use hir::expression::TriLiteral;
25use hir::ArgumentList;
26use hir::Attribute;
27use hir::Parameter;
28use hir::TypeDeclKind;
29use hir::TypeSpec;
30use hir::WalTrace;
31use itertools::Itertools;
32use local_impl::local_impl;
33use mir::passes::MirPass;
34use num::ToPrimitive;
35
36use hir::param_util::{match_args_with_params, Argument};
37use hir::symbol_table::{FrozenSymtab, PatternableKind};
38use hir::{ItemList, Pattern, PatternArgument, UnitKind, UnitName};
39use mir::types::Type as MirType;
40use mir::MirInput;
41use mir::ValueNameSource;
42use mir::{ConstantValue, ValueName};
43use monomorphisation::MonoItem;
44use monomorphisation::MonoState;
45use name_map::NameSource;
46pub use name_map::NameSourceMap;
47use num::{BigUint, One, Zero};
48use pattern::DeconstructedPattern;
49use pipelines::lower_pipeline;
50use pipelines::MaybePipelineContext;
51use spade_common::id_tracker::ExprIdTracker;
52use spade_common::location_info::WithLocation;
53use spade_common::name::{Identifier, Path, PathSegment};
54use spade_common::num_ext::InfallibleToBigInt;
55use spade_common::num_ext::InfallibleToBigUint;
56use spade_diagnostics::codespan::Span;
57use spade_diagnostics::diag_anyhow;
58use spade_diagnostics::diagnostic::SuggestionParts;
59use spade_diagnostics::{diag_assert, diag_bail, Diagnostic};
60use spade_hir::expression::Safety;
61use spade_hir::UnitHead;
62use spade_typeinference::equation::TypeVar;
63use spade_typeinference::equation::TypedExpression;
64use spade_typeinference::traits::TraitImplList;
65use spade_typeinference::GenericListToken;
66use spade_typeinference::HasType;
67use spade_types::meta_types::MetaType;
68use spade_types::KnownType;
69use statement_list::StatementList;
70use substitution::Substitution;
71use substitution::Substitutions;
72use usefulness::{is_useful, PatStack, Usefulness};
73
74use crate::error::Result;
75use spade_common::{location_info::Loc, name::NameID};
76use spade_hir as hir;
77use spade_hir::{
78    expression::BinaryOperator, expression::UnaryOperator, ExprKind, Expression, Statement, Unit,
79};
80use spade_mir as mir;
81use spade_typeinference::TypeState;
82use spade_types::{ConcreteType, PrimitiveType};
83
84pub trait LocExprExt {
85    fn runtime_requirement_witness(&self, ctx: &mut Context) -> Option<Loc<Expression>>;
86}
87
88impl LocExprExt for Loc<hir::Expression> {
89    /// Checks if the expression is evaluable at compile time, returning a Loc of
90    /// a (sub)-expression which requires runtime, and None if it is comptime evaluable.
91    ///
92    /// If this method returns None, `.eval()` on the resulting list of mir statements is
93    /// guaranteed to work
94    fn runtime_requirement_witness(&self, ctx: &mut Context) -> Option<Loc<Expression>> {
95        match &self.kind {
96            ExprKind::Error => Some(self.clone()),
97            ExprKind::Identifier(_) => Some(self.clone()),
98            ExprKind::TypeLevelInteger(_) => None,
99            ExprKind::IntLiteral(_, _) => None,
100            ExprKind::BoolLiteral(_) => None,
101            ExprKind::TriLiteral(_) => Some(self.clone()),
102            ExprKind::TupleLiteral(inner) => inner
103                .iter()
104                .find_map(|e| e.runtime_requirement_witness(ctx)),
105            ExprKind::ArrayLiteral(inner) => inner
106                .iter()
107                .find_map(|e| e.runtime_requirement_witness(ctx)),
108            ExprKind::ArrayShorthandLiteral(inner, _) => inner.runtime_requirement_witness(ctx),
109            ExprKind::CreatePorts => Some(self.clone()),
110            ExprKind::Index(l, r) => l
111                .runtime_requirement_witness(ctx)
112                .or_else(|| r.runtime_requirement_witness(ctx)),
113            ExprKind::RangeIndex { .. } => Some(self.clone()),
114            ExprKind::TupleIndex(l, _) => l.runtime_requirement_witness(ctx),
115            ExprKind::FieldAccess(l, _) => l.runtime_requirement_witness(ctx),
116            ExprKind::Call {
117                kind: CallKind::Function,
118                callee,
119                args,
120                ..
121            } => match ctx.item_list.executables.get(callee) {
122                Some(hir::ExecutableItem::EnumInstance { .. })
123                | Some(hir::ExecutableItem::StructInstance) => args
124                    .inner
125                    .expressions()
126                    .iter()
127                    .find_map(|e| e.runtime_requirement_witness(ctx)),
128                Some(_) => Some(self.clone()),
129                None => {
130                    unreachable!("Instantiating an item which is not known ({callee})")
131                }
132            },
133            // NOTE: We probably shouldn't see this here since we'll have lowered
134            // methods at this point, but this function doesn't throw
135            ExprKind::MethodCall { .. } | ExprKind::Call { .. } => Some(self.clone()),
136            ExprKind::BinaryOperator(l, operator, r) => {
137                if let Some(witness) = l
138                    .runtime_requirement_witness(ctx)
139                    .or_else(|| r.runtime_requirement_witness(ctx))
140                {
141                    Some(witness)
142                } else {
143                    match &operator.inner {
144                        BinaryOperator::Add => None,
145                        BinaryOperator::Sub => None,
146                        BinaryOperator::Mul
147                        | BinaryOperator::Div
148                        | BinaryOperator::Mod
149                        | BinaryOperator::Eq
150                        | BinaryOperator::NotEq
151                        | BinaryOperator::Gt
152                        | BinaryOperator::Lt
153                        | BinaryOperator::Ge
154                        | BinaryOperator::Le
155                        | BinaryOperator::LeftShift
156                        | BinaryOperator::RightShift
157                        | BinaryOperator::ArithmeticRightShift
158                        | BinaryOperator::LogicalAnd
159                        | BinaryOperator::LogicalOr
160                        | BinaryOperator::LogicalXor
161                        | BinaryOperator::BitwiseOr
162                        | BinaryOperator::BitwiseAnd
163                        | BinaryOperator::BitwiseXor => Some(self.clone()),
164                    }
165                }
166            }
167            ExprKind::UnaryOperator(op, operand) => {
168                if let Some(witness) = operand.runtime_requirement_witness(ctx) {
169                    Some(witness)
170                } else {
171                    match op.inner {
172                        UnaryOperator::Sub => None,
173                        UnaryOperator::Not
174                        | UnaryOperator::BitwiseNot
175                        | UnaryOperator::Dereference
176                        | UnaryOperator::Reference => Some(self.clone()),
177                    }
178                }
179            }
180            ExprKind::Match(_, _) => Some(self.clone()),
181            ExprKind::Block(_) => Some(self.clone()),
182            ExprKind::If(_, _, _) => Some(self.clone()),
183            ExprKind::TypeLevelIf(_, _, _) => Some(self.clone()),
184            ExprKind::PipelineRef { .. } => Some(self.clone()),
185            ExprKind::StageReady => Some(self.clone()),
186            ExprKind::StageValid => Some(self.clone()),
187            ExprKind::LambdaDef { .. } => Some(self.clone()),
188            ExprKind::StaticUnreachable(_) => None,
189            ExprKind::Null => None,
190        }
191    }
192}
193
194pub trait Manglable {
195    fn mangled(&self) -> String;
196}
197impl Manglable for NameID {
198    fn mangled(&self) -> String {
199        let str_name = self.1.to_strings().join("_");
200        format!("{}_n{}", str_name, self.0)
201    }
202}
203
204pub trait UnitNameExt {
205    fn as_mir(&self) -> mir::UnitName;
206}
207
208impl UnitNameExt for UnitName {
209    fn as_mir(&self) -> mir::UnitName {
210        let kind = match self {
211            UnitName::WithID(name) => mir::unit_name::UnitNameKind::Escaped {
212                name: format!("{}[{}]", name.inner.1.to_strings().join("::"), name.inner.0),
213                path: name.inner.1.to_strings(),
214            },
215            UnitName::FullPath(name) => mir::unit_name::UnitNameKind::Escaped {
216                name: name.inner.1.to_strings().join("::"),
217                path: name.inner.1.to_strings(),
218            },
219            UnitName::Unmangled(name, _) => mir::unit_name::UnitNameKind::Unescaped(name.clone()),
220        };
221
222        mir::UnitName {
223            kind,
224            source: match self {
225                UnitName::WithID(name) => name.inner.clone(),
226                UnitName::FullPath(name) => name.inner.clone(),
227                UnitName::Unmangled(_, name) => name.inner.clone(),
228            },
229        }
230    }
231}
232
233pub trait MirLowerable {
234    fn to_mir_type(&self) -> mir::types::Type;
235}
236
237impl MirLowerable for ConcreteType {
238    fn to_mir_type(&self) -> mir::types::Type {
239        use mir::types::Type;
240        use ConcreteType as CType;
241
242        match self {
243            CType::Error => {
244                // Returning a 1 size int to avoid trouble with size checking
245                Type::uint(1)
246            }
247            CType::Tuple(inner) => {
248                Type::Tuple(inner.iter().map(MirLowerable::to_mir_type).collect())
249            }
250            CType::Array { inner, size } => Type::Array {
251                inner: Box::new(inner.to_mir_type()),
252                length: size.to_biguint().expect("Found negative array size"),
253            },
254            CType::Single {
255                base: PrimitiveType::Bool | PrimitiveType::Clock | PrimitiveType::Tri,
256                params: _,
257            } => Type::Bool,
258            CType::Single {
259                base: PrimitiveType::Int,
260                params,
261            } => match params.as_slice() {
262                [CType::Integer(val)] => {
263                    Type::Int(val.to_biguint().expect("Found negative int size"))
264                }
265                t => unreachable!("{:?} is an invalid generic parameter for an integer", t),
266            },
267            CType::Single {
268                base: PrimitiveType::Uint,
269                params,
270            } => match params.as_slice() {
271                [CType::Integer(val)] => {
272                    Type::UInt(val.to_biguint().expect("Found negative uint size"))
273                }
274                t => unreachable!("{:?} is an invalid generic parameter for an integer", t),
275            },
276            CType::Single {
277                base: PrimitiveType::Memory,
278                params,
279            } => match params.as_slice() {
280                [inner, CType::Integer(length)] => Type::Memory {
281                    inner: Box::new(inner.to_mir_type()),
282                    length: length.to_biguint().expect("Found negative uint "),
283                },
284                t => unreachable!("{:?} is an invalid generic parameter for a memory", t),
285            },
286            CType::Single {
287                base: PrimitiveType::InOut,
288                params,
289            } => match params.as_slice() {
290                [inner] => Type::InOut(Box::new(inner.to_mir_type())),
291                t => unreachable!("{:?} is an invalid generic parameter for inout", t),
292            },
293            CType::Integer(_) => {
294                unreachable!("Found an integer at the base level of a type")
295            }
296            CType::Bool(_) => {
297                unreachable!("Found a bool at the base level of a type")
298            }
299            CType::String(_) => {
300                unreachable!("Found a string at the base level of a type")
301            }
302            CType::Enum { options } => {
303                let inner = options
304                    .iter()
305                    .map(|o| o.1.iter().map(|t| t.1.to_mir_type()).collect())
306                    .collect();
307                Type::Enum(inner)
308            }
309            CType::Struct {
310                name: _,
311                is_port: _,
312                members,
313                field_translators: _,
314            } => {
315                let members = members
316                    .iter()
317                    .map(|(n, t)| (n.as_str().to_owned(), t.to_mir_type()))
318                    .collect();
319                Type::Struct(members)
320            }
321            CType::Backward(inner) => Type::Backward(Box::new(inner.to_mir_type())),
322            // At this point we no longer need to know if this is a Wire or not, it will
323            // behave exactly as a normal type
324            CType::Wire(inner) => inner.to_mir_type(),
325        }
326    }
327}
328
329pub trait NameIDExt {
330    /// Return the corresponding MIR value name for this NameID
331    fn value_name(&self) -> mir::ValueName;
332
333    fn value_name_with_alternate_source(&self, source: ValueNameSource) -> mir::ValueName;
334}
335
336impl NameIDExt for NameID {
337    fn value_name(&self) -> mir::ValueName {
338        self.value_name_with_alternate_source(ValueNameSource::Name(self.clone()))
339    }
340
341    fn value_name_with_alternate_source(&self, source: ValueNameSource) -> mir::ValueName {
342        let mangled = format!("{}", self.1.tail());
343        mir::ValueName::Named(self.0, mangled, source)
344    }
345}
346
347struct PatternCondition {
348    pub statements: Vec<mir::Statement>,
349    pub result_name: ValueName,
350}
351
352/// Returns a name which is `true` if all of `ops` are true, along with helper
353/// statements required for that computation. If `ops` is empt, a single `true` constant
354/// is returned
355pub fn all_conditions(ops: Vec<ValueName>, ctx: &mut Context) -> (Vec<mir::Statement>, ValueName) {
356    if ops.is_empty() {
357        let id = ctx.idtracker.next();
358        (
359            vec![mir::Statement::Constant(
360                id,
361                MirType::Bool,
362                ConstantValue::Bool(true),
363            )],
364            ValueName::Expr(id),
365        )
366    } else if ops.len() == 1 {
367        (vec![], ops[0].clone())
368    } else {
369        let mut result_name = ops[0].clone();
370        let mut statements = vec![];
371        for op in &ops[1..] {
372            let new_name = ValueName::Expr(ctx.idtracker.next());
373            statements.push(mir::Statement::Binding(mir::Binding {
374                name: new_name.clone(),
375                operator: mir::Operator::LogicalAnd,
376                operands: vec![result_name, op.clone()],
377                ty: MirType::Bool,
378                loc: None,
379            }));
380            result_name = new_name;
381        }
382        (statements, result_name)
383    }
384}
385
386#[local_impl]
387impl PatternLocal for Loc<Pattern> {
388    /// Does the same thing as lower but does not create an alias binding for
389    /// self_name. Used if this is handled elsewhere
390    fn lower_no_initial_binding(
391        &self,
392        self_name: ValueName,
393        ctx: &mut Context,
394    ) -> Result<StatementList> {
395        let mut result = StatementList::new();
396
397        if let ConcreteType::Error =
398            &ctx.types
399                .concrete_type_of(self, ctx.symtab.symtab(), &ctx.item_list.types)?
400        {
401            result.push_primary(spade_mir::Statement::Error, self);
402
403            return Ok(result);
404        }
405
406        match &self.kind {
407            hir::PatternKind::Integer(_) => {}
408            hir::PatternKind::Bool(_) => {}
409            hir::PatternKind::Name { .. } => {}
410            hir::PatternKind::Tuple(inner) => {
411                for (i, p) in inner.iter().enumerate() {
412                    let ty = ctx
413                        .types
414                        .concrete_type_of(p, ctx.symtab.symtab(), &ctx.item_list.types)?
415                        .to_mir_type();
416
417                    result.push_primary(
418                        mir::Statement::Binding(mir::Binding {
419                            name: p.value_name(),
420                            operator: mir::Operator::IndexTuple(i as u64),
421                            operands: vec![self_name.clone()],
422                            ty,
423                            loc: Some(self.loc()),
424                        }),
425                        p,
426                    );
427
428                    result.append(p.lower_no_initial_binding(p.value_name(), ctx)?)
429                }
430            }
431            hir::PatternKind::Array(inner) => {
432                let index_ty =
433                    MirType::Int((((inner.len() as f32).log2().floor() + 1.) as u128).to_biguint());
434                for (i, p) in inner.iter().enumerate() {
435                    let idx_id = ctx.idtracker.next();
436                    result.push_secondary(
437                        mir::Statement::Constant(
438                            idx_id,
439                            index_ty.clone(),
440                            mir::ConstantValue::Int(i.to_bigint()),
441                        ),
442                        p,
443                        "destructured array index",
444                    );
445                    result.push_primary(
446                        mir::Statement::Binding(mir::Binding {
447                            name: p.value_name(),
448                            operator: mir::Operator::IndexArray,
449                            operands: vec![self_name.clone(), ValueName::Expr(idx_id)],
450                            ty: ctx
451                                .types
452                                .concrete_type_of(p, ctx.symtab.symtab(), &ctx.item_list.types)?
453                                .to_mir_type(),
454                            loc: Some(self.loc()),
455                        }),
456                        p,
457                    );
458
459                    result.append(p.lower_no_initial_binding(p.value_name(), ctx)?)
460                }
461            }
462            hir::PatternKind::Type(path, args) => {
463                let patternable = ctx.symtab.symtab().patternable_type_by_id(path);
464                match patternable.kind {
465                    PatternableKind::Struct => {
466                        let s = ctx.symtab.symtab().struct_by_id(path);
467
468                        for PatternArgument {
469                            target,
470                            value,
471                            kind: _,
472                        } in args
473                        {
474                            let i = s.params.arg_index(target).unwrap();
475
476                            result.push_primary(
477                                mir::Statement::Binding(mir::Binding {
478                                    name: value.value_name(),
479                                    operator: mir::Operator::IndexTuple(i as u64),
480                                    operands: vec![self_name.clone()],
481                                    ty: ctx
482                                        .types
483                                        .concrete_type_of(
484                                            value,
485                                            ctx.symtab.symtab(),
486                                            &ctx.item_list.types,
487                                        )?
488                                        .to_mir_type(),
489                                    loc: Some(self.loc()),
490                                }),
491                                value,
492                            );
493
494                            result.append(value.lower_no_initial_binding(value.value_name(), ctx)?)
495                        }
496                    }
497                    PatternableKind::Enum => {
498                        let enum_variant = ctx.symtab.symtab().enum_variant_by_id(path);
499
500                        for (i, p) in args.iter().enumerate() {
501                            result.push_primary(
502                                mir::Statement::Binding(mir::Binding {
503                                    name: p.value.value_name(),
504                                    operator: mir::Operator::EnumMember {
505                                        variant: enum_variant.option,
506                                        member_index: i,
507                                    },
508                                    operands: vec![self_name.clone()],
509                                    ty: ctx
510                                        .types
511                                        .concrete_type_of(
512                                            &p.value,
513                                            ctx.symtab.symtab(),
514                                            &ctx.item_list.types,
515                                        )?
516                                        .to_mir_type(),
517                                    loc: Some(self.loc()),
518                                }),
519                                &p.value,
520                            );
521
522                            result.append(
523                                p.value
524                                    .lower_no_initial_binding(p.value.value_name(), ctx)?,
525                            )
526                        }
527                    }
528                }
529            }
530        }
531
532        Ok(result)
533    }
534
535    /// Lower a pattern to its individual parts. Requires the `Pattern::id` to be
536    /// present in the code before this
537    /// self_name is the name of the operand which this pattern matches
538    #[tracing::instrument(name = "Pattern::lower", level = "trace", skip(self, self_ty, ctx))]
539    fn lower(
540        &self,
541        self_name: ValueName,
542        self_ty: MirType,
543        ctx: &mut Context,
544    ) -> Result<StatementList> {
545        let mut result = StatementList::new();
546
547        result.push_primary(
548            mir::Statement::Binding(mir::Binding {
549                name: self.value_name(),
550                operator: mir::Operator::Alias,
551                operands: vec![self_name.clone()],
552                ty: self_ty.clone(),
553                loc: Some(self.loc()),
554            }),
555            self,
556        );
557
558        result.append(self.lower_no_initial_binding(self_name, ctx)?);
559
560        Ok(result)
561    }
562
563    /// Returns MIR code for a condition that must hold for `expr` to satisfy
564    /// this pattern.
565    #[tracing::instrument(level = "trace", skip(self, ctx))]
566    fn condition(
567        &self,
568        value_name: &ValueName,
569        if_cond_name: Option<ValueName>,
570        ctx: &mut Context,
571    ) -> Result<PatternCondition> {
572        let output_id = ctx.idtracker.next();
573        let result_name = ValueName::Expr(output_id);
574        match (&self.kind, if_cond_name) {
575            (hir::PatternKind::Integer(val), c) => {
576                let self_type =
577                    ctx.types
578                        .concrete_type_of(self, ctx.symtab.symtab(), &ctx.item_list.types)?;
579                let const_id = ctx.idtracker.next();
580                let statements = match c {
581                    Some(c) => {
582                        let intermediate_name = ValueName::Expr(ctx.idtracker.next());
583                        vec![
584                            mir::Statement::Constant(
585                                const_id,
586                                self_type.to_mir_type(),
587                                ConstantValue::Int(val.clone()),
588                            ),
589                            mir::Statement::Binding(mir::Binding {
590                                name: intermediate_name.clone(),
591                                ty: MirType::Bool,
592                                operator: mir::Operator::Eq,
593                                operands: vec![value_name.clone(), ValueName::Expr(const_id)],
594                                loc: None,
595                            }),
596                            mir::Statement::Binding(mir::Binding {
597                                name: result_name.clone(),
598                                ty: MirType::Bool,
599                                operator: mir::Operator::LogicalAnd,
600                                operands: vec![intermediate_name, c],
601                                loc: None,
602                            }),
603                        ]
604                    }
605                    None => vec![
606                        mir::Statement::Constant(
607                            const_id,
608                            self_type.to_mir_type(),
609                            ConstantValue::Int(val.clone()),
610                        ),
611                        mir::Statement::Binding(mir::Binding {
612                            name: result_name.clone(),
613                            ty: MirType::Bool,
614                            operator: mir::Operator::Eq,
615                            operands: vec![value_name.clone(), ValueName::Expr(const_id)],
616                            loc: None,
617                        }),
618                    ],
619                };
620
621                Ok(PatternCondition {
622                    statements,
623                    result_name,
624                })
625            }
626            (hir::PatternKind::Bool(true), Some(c)) => Ok(PatternCondition {
627                statements: vec![mir::Statement::Binding(mir::Binding {
628                    name: result_name.clone(),
629                    ty: MirType::Bool,
630                    operator: mir::Operator::LogicalAnd,
631                    operands: vec![value_name.clone(), c],
632                    loc: None,
633                })],
634                result_name: result_name,
635            }),
636            (hir::PatternKind::Bool(true), None) => Ok(PatternCondition {
637                statements: vec![],
638                result_name: value_name.clone(),
639            }),
640            (hir::PatternKind::Bool(false), Some(c)) => {
641                let negated_value_name = ValueName::Expr(ctx.idtracker.next());
642
643                Ok(PatternCondition {
644                    statements: vec![
645                        mir::Statement::Binding(mir::Binding {
646                            name: negated_value_name.clone(),
647                            ty: MirType::Bool,
648                            operator: mir::Operator::LogicalNot,
649                            operands: vec![value_name.clone()],
650                            loc: None,
651                        }),
652                        mir::Statement::Binding(mir::Binding {
653                            name: result_name.clone(),
654                            ty: MirType::Bool,
655                            operator: mir::Operator::LogicalAnd,
656                            operands: vec![negated_value_name, c],
657                            loc: None,
658                        }),
659                    ],
660                    result_name,
661                })
662            }
663            (hir::PatternKind::Bool(false), None) => Ok(PatternCondition {
664                statements: vec![mir::Statement::Binding(mir::Binding {
665                    name: result_name.clone(),
666                    ty: MirType::Bool,
667                    operator: mir::Operator::LogicalNot,
668                    operands: vec![value_name.clone()],
669                    loc: None,
670                })],
671                result_name,
672            }),
673            (hir::PatternKind::Name { .. }, Some(c)) => Ok(PatternCondition {
674                statements: vec![mir::Statement::Binding(mir::Binding {
675                    name: result_name.clone(),
676                    ty: MirType::Bool,
677                    operator: mir::Operator::Alias,
678                    operands: vec![c],
679                    loc: None,
680                })],
681                result_name,
682            }),
683            (hir::PatternKind::Name { .. }, None) => Ok(PatternCondition {
684                statements: vec![mir::Statement::Constant(
685                    output_id,
686                    MirType::Bool,
687                    mir::ConstantValue::Bool(true),
688                )],
689                result_name,
690            }),
691            (hir::PatternKind::Tuple(branches), c) | (hir::PatternKind::Array(branches), c) => {
692                let subpatterns = branches
693                    .iter()
694                    .map(|pat| pat.condition(&pat.value_name(), None, ctx))
695                    .collect::<Result<Vec<_>>>()?;
696
697                let conditions = subpatterns
698                    .iter()
699                    .map(|sub| sub.result_name.clone())
700                    .chain(c)
701                    .collect::<Vec<_>>();
702
703                let mut statements = subpatterns
704                    .into_iter()
705                    .flat_map(|sub| sub.statements.into_iter())
706                    .collect::<Vec<_>>();
707
708                let (mut new_statements, result_name) = all_conditions(conditions, ctx);
709                statements.append(&mut new_statements);
710
711                Ok(PatternCondition {
712                    statements,
713                    result_name,
714                })
715            }
716            (hir::PatternKind::Type(path, args), c) => {
717                let patternable = ctx.symtab.symtab().patternable_type_by_id(path);
718
719                let self_condition_id = ctx.idtracker.next();
720                let self_condition_name = ValueName::Expr(self_condition_id);
721                let self_condition = match patternable.kind {
722                    PatternableKind::Enum => {
723                        let enum_variant = ctx.symtab.symtab().enum_variant_by_id(path);
724
725                        mir::Statement::Binding(mir::Binding {
726                            name: self_condition_name.clone(),
727                            operator: mir::Operator::IsEnumVariant {
728                                variant: enum_variant.option,
729                            },
730                            operands: vec![value_name.clone()],
731                            ty: MirType::Bool,
732                            loc: None,
733                        })
734                    }
735                    PatternableKind::Struct => mir::Statement::Constant(
736                        self_condition_id,
737                        MirType::Bool,
738                        ConstantValue::Bool(true),
739                    ),
740                };
741
742                // let enum_variant = ctx.symtab.symtab().enum_variant_by_id(path);
743
744                let mut conditions = vec![self_condition_name];
745                let mut cond_statements = vec![];
746                cond_statements.push(self_condition);
747                for p in args.iter() {
748                    // NOTE: We know that `lower` will generate a binding for this
749                    // argument with the specified value_name, so we can just use that
750                    let value_name = p.value.value_name();
751
752                    let mut cond = p.value.condition(&value_name, None, ctx)?;
753                    conditions.push(cond.result_name.clone());
754                    cond_statements.append(&mut cond.statements);
755                }
756
757                if let Some(if_cond_name) = c {
758                    conditions.push(if_cond_name);
759                }
760
761                let (mut extra_statements, result_name) = all_conditions(conditions, ctx);
762                cond_statements.append(&mut extra_statements);
763
764                Ok(PatternCondition {
765                    statements: cond_statements,
766                    result_name,
767                })
768            }
769        }
770    }
771
772    /// Get the name which the whole pattern should be bound. In practice,
773    /// this will be the pattern ID unless the pattern is just a name, in
774    /// which case it will be a name
775    ///
776    /// This is done to avoid creating too many unnecessary ExprID variables
777    /// in the output code to make it more readable
778    fn value_name(&self) -> ValueName {
779        match &self.kind {
780            hir::PatternKind::Name {
781                name,
782                pre_declared: _,
783            } => return name.value_name(),
784            hir::PatternKind::Integer(_) => {}
785            hir::PatternKind::Bool(_) => {}
786            hir::PatternKind::Tuple(_) => {}
787            hir::PatternKind::Type(_, _) => {}
788            hir::PatternKind::Array(_) => {}
789        }
790        ValueName::Expr(self.id)
791    }
792
793    /// Return true if this pattern is just an alias for another name. If this is not
794    /// the case, an alias from its true name to `value_name` must be generated
795    fn is_alias(&self) -> bool {
796        match self.kind {
797            hir::PatternKind::Name { .. } => true,
798            hir::PatternKind::Integer(_) => false,
799            hir::PatternKind::Bool(_) => false,
800            hir::PatternKind::Tuple(_) => false,
801            hir::PatternKind::Type(_, _) => false,
802            hir::PatternKind::Array(_) => false,
803        }
804    }
805
806    fn is_trivially_irrefutable(&self) -> bool {
807        match &self.kind {
808            spade_hir::PatternKind::Integer(_) => false,
809            spade_hir::PatternKind::Bool(_) => false,
810            spade_hir::PatternKind::Name { .. } => true,
811            spade_hir::PatternKind::Tuple(inner) | spade_hir::PatternKind::Array(inner) => {
812                inner.iter().all(Self::is_trivially_irrefutable)
813            }
814            spade_hir::PatternKind::Type(_, inner) => {
815                inner.iter().all(|arg| arg.value.is_trivially_irrefutable())
816            }
817        }
818    }
819
820    /// Returns an error if the pattern is refutable, i.e. it does not match all possible
821    /// values it binds to
822    #[tracing::instrument(level = "trace", skip(self, ctx))]
823    fn is_refutable(&self, ctx: &Context) -> Usefulness {
824        let operand_ty = ctx.types.concrete_type_of_infallible(
825            self.id,
826            ctx.symtab.symtab(),
827            &ctx.item_list.types,
828        );
829
830        let pat_stacks = vec![PatStack::new(vec![DeconstructedPattern::from_hir(
831            self, ctx,
832        )])];
833
834        // The patterns which make a wildcard useful are the ones that are missing
835        // from the match statement
836        is_useful(
837            &PatStack::new(vec![DeconstructedPattern::wildcard(&operand_ty)]),
838            &usefulness::Matrix::new(&pat_stacks),
839        )
840    }
841
842    fn check_irrefutable(&self, binding_kind: &str, ctx: &Context) -> Result<()> {
843        if self.is_trivially_irrefutable() {
844            return Ok(());
845        }
846
847        let refutability = self.is_refutable(ctx);
848        if refutability.is_useful() {
849            Err(refutable_pattern_diagnostic(
850                self.loc(),
851                &refutability,
852                binding_kind,
853            ))
854        } else {
855            Ok(())
856        }
857    }
858}
859
860pub fn do_wal_trace_lowering(
861    pattern: &Loc<hir::Pattern>,
862    main_value_name: &ValueName,
863    wal_traceable: &Loc<hir::WalTraceable>,
864    wal_trace: &Loc<WalTrace>,
865    ty: &ConcreteType,
866    result: &mut StatementList,
867    ctx: &mut Context,
868) -> Result<()> {
869    let hir::WalTrace { clk, rst } = &wal_trace.inner;
870    let hir::WalTraceable {
871        suffix,
872        uses_clk,
873        uses_rst,
874    } = &wal_traceable.inner;
875
876    let mut check_clk_or_rst =
877        |signal: &Option<Loc<Expression>>, uses: bool, name, suffix| -> Result<()> {
878            match (signal, uses) {
879                (None, false) => {}
880                (None, true) => {
881                    return Err(Diagnostic::error(
882                        wal_trace,
883                        format!("The {name} signal for this trace must be provided"),
884                    ))
885                }
886                (Some(signal), false) => {
887                    return Err(
888                        Diagnostic::error(signal, format!("Unnecessary {name} signal"))
889                            .primary_label(format!("Unnecessary {name} signal"))
890                            .secondary_label(
891                                wal_traceable,
892                                format!("This struct does not need a {name} signal for tracing"),
893                            ),
894                    )
895                }
896                (Some(signal), true) => result.push_anonymous(mir::Statement::WalTrace {
897                    name: main_value_name.clone(),
898                    val: signal.variable(ctx)?,
899                    suffix: format!("__{suffix}__{}", wal_traceable.suffix.clone()),
900                    ty: MirType::Bool,
901                }),
902            }
903            Ok(())
904        };
905    check_clk_or_rst(clk, *uses_clk, "clock", "clk")?;
906    check_clk_or_rst(rst, *uses_rst, "reset", "rst")?;
907
908    if let ConcreteType::Struct {
909        name: _,
910        is_port: _,
911        members,
912        field_translators: _,
913    } = ty
914    {
915        // Sanity check that all fields are either pure input or pure output
916        for (n, ty) in members {
917            let mir_ty = ty.to_mir_type();
918            if mir_ty.backward_size() != BigUint::zero()
919                && ty.to_mir_type().size() != BigUint::zero()
920            {
921                return Err(Diagnostic::error(
922                    pattern,
923                    "Wal tracing does not work on types with mixed-direction fields",
924                )
925                .primary_label(format!(
926                    "The field '{n}' of the struct has both & and &mut wires"
927                )));
928            }
929        }
930
931        // If we have &mut wires, we need a flipped port to read the values from because
932        // we need to work around a small bug. Create an anonymous value for this
933        // lifeguard spade#252
934        let flipped_id = ctx.idtracker.next();
935        let flipped_ty = MirType::Struct(
936            members
937                .iter()
938                .filter_map(|(n, ty)| match ty.to_mir_type() {
939                    MirType::Backward(i) => Some((n.as_str().to_owned(), i.as_ref().clone())),
940                    _ => None,
941                })
942                .collect(),
943        );
944        let flipped_port = mir::Statement::Binding(mir::Binding {
945            name: ValueName::Expr(flipped_id),
946            operator: mir::Operator::ReadMutWires,
947            operands: vec![main_value_name.clone()],
948            ty: flipped_ty.clone(),
949            loc: None,
950        });
951        if !flipped_ty.size().is_zero() {
952            result.push_anonymous(flipped_port);
953        }
954
955        // The forward port has the backward variants included, so extracting `(a, &mut b, c)` so
956        // the forward fields will be [0] and [2]
957        // The backward copy only has the mut wire, so it is (&mut b) and the index is [0]
958        let mut i_all = 0;
959        let mut i_backward = 0;
960        for (n, ty) in members.iter() {
961            let new_id = ctx.idtracker.next();
962            let field_name = ValueName::Expr(new_id);
963
964            let (is_backward, mir_ty, operand, operator) = match ty.to_mir_type() {
965                MirType::Backward(b) => {
966                    let result = (
967                        true,
968                        *b,
969                        ValueName::Expr(flipped_id),
970                        mir::Operator::IndexTuple(i_backward),
971                    );
972                    i_backward += 1;
973                    i_all += 1;
974                    result
975                }
976                other => {
977                    let result = (
978                        false,
979                        other,
980                        main_value_name.clone(),
981                        mir::Operator::IndexTuple(i_all),
982                    );
983                    i_all += 1;
984                    result
985                }
986            };
987            // Insert an indexing operation, and a wal trace on the result.
988            result.push_anonymous(mir::Statement::Binding(mir::Binding {
989                name: field_name.clone(),
990                operator,
991                operands: vec![operand],
992                ty: mir_ty.clone(),
993                loc: None,
994            }));
995
996            // Add the wal trace statement
997            result.push_anonymous(mir::Statement::WalTrace {
998                name: main_value_name.clone(),
999                val: field_name,
1000                suffix: format!("__{n}__{}", suffix.clone()),
1001                ty: mir_ty,
1002            });
1003
1004            // Add the new expression to the type state so we can look it up
1005            // during translation. Doing this is a messy process however, because
1006            // we lost information. We'll cheat, and unify the expression with
1007            // indexing for the correct field on the pattern.
1008            // This is kind of a giant hack and makes quite a few assumptions about
1009            // the rest of the compiler
1010            // If problems occur in this code, it is probably a good idea to start
1011            // looking at refactoring this into a more sane state
1012
1013            let dummy_expr_id = ctx.idtracker.next();
1014            let dummy_expr = if is_backward {
1015                hir::Expression {
1016                    id: new_id,
1017                    kind: ExprKind::Call {
1018                        kind: CallKind::Entity(().nowhere()),
1019                        callee: ctx
1020                            .symtab
1021                            .symtab()
1022                            .lookup_unit(
1023                                &Path::from_strs(&["std", "ports", "read_mut_wire"]).nowhere(),
1024                            )
1025                            .expect("did not find std::ports::read_mut_wire in symtab")
1026                            .0
1027                            .nowhere(),
1028                        args: ArgumentList::Positional(vec![hir::Expression {
1029                            kind: ExprKind::FieldAccess(
1030                                Box::new(
1031                                    hir::Expression {
1032                                        kind: ExprKind::Null,
1033                                        id: dummy_expr_id,
1034                                    }
1035                                    .nowhere(),
1036                                ),
1037                                n.clone().nowhere(),
1038                            ),
1039                            id: ctx.idtracker.next(),
1040                        }
1041                        .nowhere()])
1042                        .nowhere(),
1043                        turbofish: None,
1044                        safety: Safety::Default,
1045                    },
1046                }
1047                .nowhere()
1048            } else {
1049                hir::Expression {
1050                    kind: ExprKind::FieldAccess(
1051                        Box::new(
1052                            hir::Expression {
1053                                kind: ExprKind::Null,
1054                                id: dummy_expr_id,
1055                            }
1056                            .nowhere(),
1057                        ),
1058                        n.clone().nowhere(),
1059                    ),
1060                    id: new_id,
1061                }
1062                .nowhere()
1063            };
1064
1065            let type_ctx = spade_typeinference::Context {
1066                symtab: ctx.symtab.symtab(),
1067                items: ctx.item_list,
1068                trait_impls: &ctx.trait_impls,
1069            };
1070            let generic_list = &ctx.types.create_generic_list(
1071                spade_typeinference::GenericListSource::Anonymous,
1072                &[],
1073                &[],
1074                None,
1075                &[],
1076            )?;
1077            ctx.types
1078                .visit_expression(&dummy_expr, &type_ctx, generic_list);
1079
1080            ctx.types
1081                .unify(
1082                    &TypedExpression::Id(pattern.id),
1083                    &TypedExpression::Id(dummy_expr_id),
1084                    &spade_typeinference::Context {
1085                        symtab: ctx.symtab.symtab(),
1086                        items: ctx.item_list,
1087                        trait_impls: ctx.trait_impls,
1088                    },
1089                )
1090                .unwrap(); // Unification with a completely generic expr
1091            ctx.types.check_requirements(true, &type_ctx).unwrap();
1092        }
1093    } else {
1094        diag_bail!(wal_trace, "Tracing on non-struct")
1095    }
1096
1097    Ok(())
1098}
1099
1100pub fn lower_wal_trace(
1101    pattern: &Loc<hir::Pattern>,
1102    wal_trace: &Loc<WalTrace>,
1103    ctx: &mut Context,
1104    result: &mut StatementList,
1105    concrete_ty: &ConcreteType,
1106) -> Result<()> {
1107    let hir_ty = pattern.get_type(ctx.types);
1108    match &hir_ty.resolve(&ctx.types) {
1109        TypeVar::Known(_, spade_types::KnownType::Named(name), _) => {
1110            let ty = ctx.item_list.types.get(name);
1111
1112            match ty.as_ref().map(|ty| &ty.inner.kind) {
1113                Some(TypeDeclKind::Struct(s)) => {
1114                    if let Some(suffix) = &s.wal_traceable {
1115                        do_wal_trace_lowering(
1116                            pattern,
1117                            &pattern.value_name(),
1118                            suffix,
1119                            wal_trace,
1120                            concrete_ty,
1121                            result,
1122                            ctx,
1123                        )?;
1124                    } else {
1125                        return Err(Diagnostic::error(
1126                            wal_trace,
1127                            "#[wal_trace] on struct without #[wal_traceable]",
1128                        )
1129                        .primary_label(format!("{} does not have #[wal_traceable]", name))
1130                        .secondary_label(
1131                            pattern,
1132                            format!("This has type {} which does not have #[wal_traceable]", hir_ty.display(ctx.types)),
1133                        )
1134                        .note("This most likely means that the struct can not be analyzed by a wal script"));
1135                    }
1136                }
1137                Some(other) => {
1138                    return Err(Diagnostic::error(
1139                        wal_trace,
1140                        "#[wal_trace] can only be applied to values of struct type",
1141                    )
1142                    .primary_label(format!("#[wal_trace] on {}", other.name()))
1143                    .secondary_label(
1144                        pattern,
1145                        format!(
1146                            "This has type {} which is {}",
1147                            hir_ty.display(ctx.types),
1148                            other.name()
1149                        ),
1150                    )
1151                    .into())
1152                }
1153                None => {
1154                    diag_bail!(wal_trace, "wal_trace on non-declared type")
1155                }
1156            }
1157        }
1158        other => {
1159            return Err(Diagnostic::error(
1160                wal_trace,
1161                "#[wal_trace] can only be applied to values of struct type",
1162            )
1163            .primary_label(format!("#[wal_trace] on {}", other.display(ctx.types)))
1164            .secondary_label(
1165                pattern,
1166                format!("This has type {}", other.display(ctx.types)),
1167            ))
1168        }
1169    }
1170    Ok(())
1171}
1172
1173#[local_impl]
1174impl StatementLocal for Statement {
1175    #[tracing::instrument(name = "Statement::lower", level = "trace", skip(self, ctx))]
1176    fn lower(&self, ctx: &mut Context) -> Result<StatementList> {
1177        let mut result = StatementList::new();
1178        match self {
1179            Statement::Error => result.push_anonymous(mir::Statement::Error),
1180            Statement::Binding(hir::Binding {
1181                pattern,
1182                ty: _,
1183                value,
1184                wal_trace,
1185            }) => {
1186                result.append(value.lower(ctx)?);
1187
1188                pattern.check_irrefutable("let", ctx)?;
1189
1190                let concrete_ty = ctx.types.concrete_type_of(
1191                    pattern,
1192                    ctx.symtab.symtab(),
1193                    &ctx.item_list.types,
1194                )?;
1195
1196                let mir_ty = concrete_ty.to_mir_type();
1197
1198                result.append(pattern.lower(value.variable(ctx)?, mir_ty, ctx)?);
1199
1200                if let Some(wal_trace) = wal_trace {
1201                    lower_wal_trace(pattern, wal_trace, ctx, &mut result, &concrete_ty)?;
1202                }
1203            }
1204            Statement::Expression(expr) => {
1205                result.append(expr.lower(ctx)?);
1206
1207                let concrete_ty =
1208                    ctx.types
1209                        .concrete_type_of(expr, ctx.symtab.symtab(), &ctx.item_list.types)?;
1210
1211                let mir_ty = concrete_ty.to_mir_type();
1212
1213                let Some(ty) = expr.try_get_type(&ctx.types) else {
1214                    diag_bail!(
1215                        expr,
1216                        "Did not find non-concrete type type that was concreteized"
1217                    );
1218                };
1219
1220                if concrete_ty != ConcreteType::Error && mir_ty.must_use() {
1221                    return Err(Diagnostic::error(
1222                        expr,
1223                        format!(
1224                            "Values of type {} must be used",
1225                            ty.resolve(&ctx.types).display(&ctx.types)
1226                        ),
1227                    )
1228                    .primary_label("This must be used")
1229                    .span_suggest_insert_before(
1230                        "consider discarding value explicitly",
1231                        expr,
1232                        "let _ = ",
1233                    ));
1234                }
1235            }
1236            Statement::Register(register) => {
1237                let hir::Register {
1238                    clock,
1239                    reset,
1240                    initial,
1241                    pattern,
1242                    value,
1243                    value_type: _,
1244                    attributes,
1245                } = &register;
1246                result.append(clock.lower(ctx)?);
1247
1248                if let Some((trig, value)) = &reset {
1249                    result.append(trig.lower(ctx)?);
1250                    result.append(value.lower(ctx)?);
1251                }
1252
1253                if let Some(initial) = initial {
1254                    result.append(initial.lower(ctx)?);
1255                }
1256
1257                result.append(value.lower(ctx)?);
1258
1259                pattern.check_irrefutable("reg", ctx)?;
1260
1261                let ty = ctx.types.concrete_type_of(
1262                    pattern,
1263                    ctx.symtab.symtab(),
1264                    &ctx.item_list.types,
1265                )?;
1266
1267                if ty.is_port() {
1268                    return Err(
1269                        Diagnostic::error(value, "Ports cannot be put in a register")
1270                            .primary_label("This is a port")
1271                            .note(format!("{ty} is a port")),
1272                    );
1273                }
1274
1275                let mut traced = None;
1276                attributes.lower(&mut |attr| match &attr.inner {
1277                    Attribute::Fsm { state } => {
1278                        traced = Some(state.value_name());
1279                        Ok(())
1280                    }
1281                    Attribute::VerilogAttrs { .. }
1282                    | Attribute::WalTraceable { .. }
1283                    | Attribute::Optimize { .. } => Err(attr.report_unused("register")),
1284                })?;
1285
1286                let initial = if let Some(init) = initial {
1287                    if let Some(witness) = init.runtime_requirement_witness(ctx) {
1288                        return Err(Diagnostic::error(
1289                            init,
1290                            "Register initial values must be known at compile time",
1291                        )
1292                        .primary_label("Value not known at compile time")
1293                        .secondary_label(
1294                            witness,
1295                            "This subexpression cannot be computed at compile time",
1296                        ));
1297                    };
1298
1299                    Some(init.lower(ctx)?.to_vec_no_source_map())
1300                } else {
1301                    None
1302                };
1303
1304                result.push_primary(
1305                    mir::Statement::Register(mir::Register {
1306                        name: pattern.value_name(),
1307                        ty: ctx
1308                            .types
1309                            .concrete_type_of(pattern, ctx.symtab.symtab(), &ctx.item_list.types)?
1310                            .to_mir_type(),
1311                        clock: clock.variable(ctx)?,
1312                        reset: reset
1313                            .as_ref()
1314                            .map::<Result<_>, _>(|(value, trig)| {
1315                                Ok((value.variable(ctx)?, trig.variable(ctx)?))
1316                            })
1317                            .transpose()?,
1318                        initial,
1319                        value: value.variable(ctx)?,
1320                        loc: Some(pattern.loc()),
1321                        traced,
1322                    }),
1323                    pattern,
1324                );
1325
1326                result.append(pattern.lower_no_initial_binding(pattern.value_name(), ctx)?);
1327            }
1328            Statement::Declaration(_) => {}
1329            Statement::PipelineRegMarker(_cond) => {
1330                // NOTE: Cond is handled by pipeline lowering
1331                ctx.subs.current_stage += 1;
1332            }
1333            Statement::Label(_) => {}
1334            Statement::Assert(expr) => {
1335                result.append(expr.lower(ctx)?);
1336                result.push_anonymous(mir::Statement::Assert(expr.variable(ctx)?.at_loc(expr)))
1337            }
1338            Statement::WalSuffixed { suffix, target } => {
1339                let ty = ctx
1340                    .types
1341                    .concrete_type_of_name(target, ctx.symtab.symtab(), &ctx.item_list.types)?
1342                    .to_mir_type();
1343
1344                result.push_anonymous(mir::Statement::WalTrace {
1345                    name: target.value_name(),
1346                    val: target.value_name(),
1347                    suffix: suffix.as_str().to_owned(),
1348                    ty,
1349                })
1350            }
1351            Statement::Set { target, value } => {
1352                result.append(target.lower(ctx)?);
1353                result.append(value.lower(ctx)?);
1354                result.push_anonymous(mir::Statement::Set {
1355                    target: target.variable(ctx)?.at_loc(target),
1356                    value: value.variable(ctx)?.at_loc(value),
1357                })
1358            }
1359        }
1360        Ok(result)
1361    }
1362}
1363
1364pub fn expr_to_mir(expr: Loc<Expression>, ctx: &mut Context) -> Result<StatementList> {
1365    expr.lower(ctx)
1366}
1367
1368#[local_impl]
1369impl ExprLocal for Loc<Expression> {
1370    /// If the verilog code for this expression is just an alias for another variable
1371    /// that is returned here. This allows us to skip generating wires that we don't
1372    /// really need
1373    fn alias(&self, ctx: &Context) -> Result<Option<mir::ValueName>> {
1374        let subs = &ctx.subs;
1375        match &self.kind {
1376            ExprKind::Error => Ok(None),
1377            ExprKind::Identifier(ident) => match subs.lookup(ident) {
1378                Substitution::Undefined => Err(undefined_variable(&ident.clone().at_loc(self))),
1379                Substitution::Waiting {
1380                    stages_left,
1381                    original_stage,
1382                    available_at,
1383                    definition: ref name,
1384                } => {
1385                    let plural = if stages_left == 1 { "" } else { "s" };
1386
1387                    Err(
1388                        Diagnostic::error(self, format!("Use of {name} before it is ready"))
1389                            .primary_label(format!(
1390                                "{name} is unavailable for another {stages_left} stage{plural}"
1391                            ))
1392                            .secondary_label(
1393                                self,
1394                                format!("This is stage {}", available_at - stages_left),
1395                            )
1396                            .secondary_label(
1397                                name,
1398                                format!(
1399                                    "{name} is defined here at stage {} with a latency of {}",
1400                                    original_stage,
1401                                    available_at - original_stage
1402                                ),
1403                            )
1404                            .note(format!(
1405                                "Requesting {name} at stage {}",
1406                                available_at - stages_left
1407                            ))
1408                            .note(format!(
1409                                "But it will not be available until stage {}",
1410                                available_at
1411                            ))
1412                            .help(format!(
1413                                "Consider adding more reg; statements between the definition and use of {name}"
1414                            )),
1415                    )
1416                }
1417                Substitution::Available(current) => Ok(Some(current.value_name())),
1418                Substitution::Port | Substitution::ZeroSized => Ok(Some(ident.value_name())),
1419            },
1420            ExprKind::IntLiteral(_, _) => Ok(None),
1421            ExprKind::TypeLevelInteger(_) => Ok(None),
1422            ExprKind::BoolLiteral(_) => Ok(None),
1423            ExprKind::TriLiteral(_) => Ok(None),
1424            ExprKind::TupleLiteral(_) => Ok(None),
1425            ExprKind::TupleIndex(_, _) => Ok(None),
1426            ExprKind::FieldAccess(_, _) => Ok(None),
1427            ExprKind::CreatePorts => Ok(None),
1428            ExprKind::ArrayLiteral { .. } => Ok(None),
1429            ExprKind::ArrayShorthandLiteral { .. } => Ok(None),
1430            ExprKind::RangeIndex { .. } => Ok(None),
1431            ExprKind::Index(_, _) => Ok(None),
1432            ExprKind::Block(block) => {
1433                if let Some(result) = &block.result {
1434                    result.variable(ctx).map(Some)
1435                } else {
1436                    Ok(None)
1437                }
1438            }
1439            ExprKind::If(_, _, _) => Ok(None),
1440            ExprKind::Match(_, _) => Ok(None),
1441            ExprKind::BinaryOperator(_, _, _) => Ok(None),
1442            ExprKind::UnaryOperator(_, _) => Ok(None),
1443            ExprKind::PipelineRef {
1444                stage,
1445                name,
1446                declares_name: _,
1447                depth_typeexpr_id,
1448            } => {
1449                let depth = match ctx.types.concrete_type_of(
1450                    depth_typeexpr_id.at_loc(stage),
1451                    ctx.symtab.symtab(),
1452                    &ctx.item_list.types
1453                ) {
1454                    Ok(ConcreteType::Integer(val)) => val.to_usize().ok_or_else(|| {
1455                        diag_anyhow!(stage, "Inferred a pipeline offset that does not fit in usize::MAX ({val})")
1456                    })?,
1457                    Ok(_) => diag_bail!(stage, "Inferred non-integer for pipeline ref offset"),
1458                    Err(_) => return Err(Diagnostic::error(stage, "Could not infer pipeline stage offset")
1459                        .primary_label("Unknown pipeline stage offset")
1460                        .help("This is likely caused by a type variable that is not fully known being used."))
1461                };
1462
1463                match subs.lookup_referenced(depth, name) {
1464                    Substitution::Undefined => Err(undefined_variable(name)),
1465                    Substitution::Waiting {
1466                        stages_left: _,
1467                        original_stage,
1468                        available_at,
1469                        definition,
1470                    } => {
1471                        // Available at is the amount of cycles left at the stage
1472                        // from which the variable is requested.
1473                        Err(Diagnostic::error(name, format!("Use of {name} before it is ready"))
1474                            .primary_label(format!("{name} is unavailable at stage {depth}"))
1475                            .secondary_label(stage, format!("This refers to stage {depth}"))
1476                            .secondary_label(definition, format!("{name} is originally defined here at stage {original_stage}"))
1477                            .note(format!("Since {name} is defined at stage {} with latency {}, it cannot be accessed before stage {}", original_stage, available_at - original_stage, available_at))
1478                        )
1479                    }
1480                    Substitution::Available(name) => Ok(Some(name.value_name())),
1481                    Substitution::Port | Substitution::ZeroSized => Ok(Some(name.value_name())),
1482                }
1483            }
1484            ExprKind::StageReady => Ok(None),
1485            ExprKind::StageValid => Ok(None),
1486            ExprKind::Call { .. } => Ok(None),
1487            ExprKind::TypeLevelIf(_, _, _) => diag_bail!(
1488                self,
1489                "Type level if should have been lowered to function by this point"
1490            ),
1491            ExprKind::MethodCall { .. } => diag_bail!(
1492                self,
1493                "method call should have been lowered to function by this point"
1494            ),
1495            ExprKind::LambdaDef { .. } => diag_bail!(
1496                self,
1497                "lambda def call should have been lowered to function by this point"
1498            ),
1499            ExprKind::Null => {
1500                diag_bail!(self, "Null expression found during hir lowering")
1501            }
1502            ExprKind::StaticUnreachable(_) => Ok(None),
1503        }
1504    }
1505
1506    // NOTE: this impl and a few others could be moved to a impl block that does not have
1507    // the Loc requirement if desired
1508    fn variable(&self, ctx: &Context) -> Result<mir::ValueName> {
1509        // If this expressions should not use the standard __expr__{} variable,
1510        // that is specified here
1511
1512        Ok(self.alias(ctx)?.unwrap_or(mir::ValueName::Expr(self.id)))
1513    }
1514
1515    fn lower(&self, ctx: &mut Context) -> Result<StatementList> {
1516        let mut result = StatementList::new();
1517
1518        let hir_type =
1519            ctx.types
1520                .concrete_type_of(self, ctx.symtab.symtab(), &ctx.item_list.types)?;
1521        let self_type = hir_type.to_mir_type();
1522
1523        match &self.kind {
1524            ExprKind::Error => result.push_primary(mir::Statement::Error, self),
1525            ExprKind::Identifier(_) => {
1526                // Empty. The identifier will be defined elsewhere
1527            }
1528            ExprKind::IntLiteral(value, _) => {
1529                let ty = self_type;
1530                result.push_primary(
1531                    mir::Statement::Constant(self.id, ty, mir::ConstantValue::Int(value.clone())),
1532                    self,
1533                );
1534            }
1535            ExprKind::TypeLevelInteger(name) => {
1536                let ty = self_type;
1537                if let Some(generic_list) = ctx.unit_generic_list {
1538                    let value = ctx.types.type_var_from_hir(
1539                        self.loc(),
1540                        &hir::TypeSpec::Generic(name.clone().nowhere()),
1541                        generic_list,
1542                    )?;
1543
1544                    let value = match ctx.types.ungenerify_type(
1545                        &value,
1546                        ctx.symtab.symtab(),
1547                        &ctx.item_list.types,
1548                    ) {
1549                        Some(ConcreteType::Integer(value)) => value,
1550                        Some(other) => diag_bail!(self, "Inferred {other} for type level integer"),
1551                        None => {
1552                            return Err(Diagnostic::error(
1553                                self,
1554                                "This type level value is not fully known",
1555                            )
1556                            .primary_label("Unknown type level value"))
1557                        }
1558                    };
1559
1560                    result.push_primary(
1561                        mir::Statement::Constant(
1562                            self.id,
1563                            ty,
1564                            mir::ConstantValue::Int(value.clone()),
1565                        ),
1566                        self,
1567                    )
1568                } else {
1569                    diag_bail!(
1570                        self,
1571                        "Attempted to use type level integer in non-generic function"
1572                    )
1573                }
1574            }
1575            ExprKind::BoolLiteral(value) => {
1576                let ty = self_type;
1577                result.push_primary(
1578                    mir::Statement::Constant(self.id, ty, mir::ConstantValue::Bool(*value)),
1579                    self,
1580                );
1581            }
1582            ExprKind::TriLiteral(value) => {
1583                let ty = self_type;
1584                let cv = match value {
1585                    TriLiteral::Low => mir::ConstantValue::Bool(false),
1586                    TriLiteral::High => mir::ConstantValue::Bool(true),
1587                    TriLiteral::HighImp => mir::ConstantValue::HighImp,
1588                };
1589                result.push_primary(mir::Statement::Constant(self.id, ty, cv), self);
1590            }
1591            ExprKind::BinaryOperator(lhs, op, rhs) => {
1592                macro_rules! binop_builder {
1593                    ($op:ident) => {{
1594                        result.append(lhs.lower(ctx)?);
1595                        result.append(rhs.lower(ctx)?);
1596
1597                        result.push_primary(
1598                            mir::Statement::Binding(mir::Binding {
1599                                name: self.variable(ctx)?,
1600                                operator: $op,
1601                                operands: vec![lhs.variable(ctx)?, rhs.variable(ctx)?],
1602                                ty: self_type,
1603                                loc: Some(self.loc()),
1604                            }),
1605                            self,
1606                        );
1607                    }};
1608                }
1609                macro_rules! dual_binop_builder {
1610                    ($sop:ident, $uop:ident) => {{
1611                        result.append(lhs.lower(ctx)?);
1612                        result.append(rhs.lower(ctx)?);
1613
1614                        let op = match &ctx
1615                            .types
1616                            .concrete_type_of(lhs, ctx.symtab.symtab(), &ctx.item_list.types)?
1617                            .to_mir_type()
1618                        {
1619                            mir::types::Type::Int(_) => $sop,
1620                            mir::types::Type::UInt(_) => $uop,
1621                            other => panic!("Dual binop on {other} which is not int or uint"),
1622                        };
1623
1624                        result.push_primary(
1625                            mir::Statement::Binding(mir::Binding {
1626                                name: self.variable(ctx)?,
1627                                operator: op,
1628                                operands: vec![lhs.variable(ctx)?, rhs.variable(ctx)?],
1629                                ty: self_type,
1630                                loc: Some(self.loc()),
1631                            }),
1632                            self,
1633                        );
1634                    }};
1635                }
1636
1637                use mir::Operator::*;
1638                match op.inner {
1639                    BinaryOperator::Add => dual_binop_builder!(Add, UnsignedAdd),
1640                    BinaryOperator::Sub => dual_binop_builder!(Sub, UnsignedSub),
1641                    BinaryOperator::Mul => dual_binop_builder!(Mul, UnsignedMul),
1642                    BinaryOperator::Eq => binop_builder!(Eq),
1643                    BinaryOperator::NotEq => binop_builder!(NotEq),
1644                    BinaryOperator::Gt => dual_binop_builder!(Gt, UnsignedGt),
1645                    BinaryOperator::Lt => dual_binop_builder!(Lt, UnsignedLt),
1646                    BinaryOperator::Ge => dual_binop_builder!(Ge, UnsignedGe),
1647                    BinaryOperator::Le => dual_binop_builder!(Le, UnsignedLe),
1648                    BinaryOperator::LogicalXor => binop_builder!(LogicalXor),
1649                    BinaryOperator::LeftShift => binop_builder!(LeftShift),
1650                    BinaryOperator::RightShift => binop_builder!(RightShift),
1651                    BinaryOperator::ArithmeticRightShift => binop_builder!(ArithmeticRightShift),
1652                    BinaryOperator::LogicalAnd => binop_builder!(LogicalAnd),
1653                    BinaryOperator::LogicalOr => binop_builder!(LogicalOr),
1654                    BinaryOperator::BitwiseAnd => binop_builder!(BitwiseAnd),
1655                    BinaryOperator::BitwiseOr => binop_builder!(BitwiseOr),
1656                    BinaryOperator::BitwiseXor => binop_builder!(BitwiseXor),
1657                    BinaryOperator::Div => {
1658                        match &rhs.inner.kind {
1659                            ExprKind::IntLiteral(val, _) => {
1660                                let val_u128 = val.to_u128()
1661                                    .ok_or_else(|| Diagnostic::error(
1662                                        rhs.loc(),
1663                                        "Division by constants larger than 2^128 is unsupported"
1664                                    ))?;
1665
1666                                if val_u128.count_ones() == 1 {
1667                                    dual_binop_builder!(Div, UnsignedDiv)
1668                                } else {
1669                                    return Err(Diagnostic::error(rhs.loc(), "Division can only be performed on powers of two")
1670                                    .primary_label("Division by non-power-of-two value")
1671                                    .help("Non-power-of-two division is generally slow and should usually be done over multiple cycles.")
1672                                    .span_suggest_replace(
1673                                        format!("If you are sure you want to divide by {val}, use `std::ops::comb_div`"),
1674                                        op,
1675                                        "`std::ops::comb_div`"
1676                                    ))
1677                                }
1678                            }
1679                            _ => {
1680                                return Err(Diagnostic::error(self, "Division can only be performed on constant powers of two")
1681                                    .primary_label("Division by non-constant value")
1682                                    .help("Division is generally slow and should be done over multiple cycles.")
1683                                    .span_suggest_replace(
1684                                        "If you are sure you want to divide by a non-constant, use `std::ops::comb_div`",
1685                                        op,
1686                                        "`std::ops::comb_div`"
1687                                    ))
1688                            }
1689                        }
1690                    }
1691                    BinaryOperator::Mod => {
1692                        match &rhs.inner.kind {
1693                            ExprKind::IntLiteral(val, _) => {
1694                                let val_u128 = val.to_u128()
1695                                    .ok_or_else(|| Diagnostic::error(
1696                                        rhs.loc(),
1697                                        "Modulo by constants larger than 2^128 is unsupported"
1698                                    ))?;
1699
1700                                if val_u128.count_ones() == 1 {
1701                                    dual_binop_builder!(Mod, UnsignedMod)
1702                                } else {
1703                                    return Err(Diagnostic::error(rhs.loc(), "Modulo can only be performed on powers of two")
1704                                    .primary_label("Modulo by non-power-of-two value")
1705                                    .help("Non-power-of-two modulo is generally slow and should usually be done over multiple cycles.")
1706                                    .span_suggest_replace(
1707                                        format!("If you are sure you want to divide by {val}, use `std::ops::comb_mod`"),
1708                                        op,
1709                                        "`std::ops::comb_mod`"
1710                                    ))
1711                                }
1712                            }
1713                            _ => {
1714                                return Err(Diagnostic::error(self, "Modulo can only be performed on constant powers of two")
1715                                    .primary_label("Modulo by non-constant value")
1716                                    .help("Modulo is generally slow and should be done over multiple cycles.")
1717                                    .span_suggest_replace(
1718                                        "If you are sure you want to divide by a non-constant, use `std::ops::comb_mod`",
1719                                        op,
1720                                        "`std::ops::comb_mod`"
1721                                    ))
1722                            }
1723                        }
1724                    }
1725                };
1726            }
1727            ExprKind::UnaryOperator(op, operand) => {
1728                let unop_builder = |op| -> Result<()> {
1729                    result.append(operand.lower(ctx)?);
1730
1731                    result.push_primary(
1732                        mir::Statement::Binding(mir::Binding {
1733                            name: self.variable(ctx)?,
1734                            operator: op,
1735                            operands: vec![operand.variable(ctx)?],
1736                            ty: self_type,
1737                            loc: Some(self.loc()),
1738                        }),
1739                        self,
1740                    );
1741                    Ok(())
1742                };
1743                use mir::Operator::*;
1744                match &op.inner {
1745                    hir::expression::UnaryOperator::Sub => unop_builder(USub)?,
1746                    hir::expression::UnaryOperator::Not => unop_builder(Not)?,
1747                    hir::expression::UnaryOperator::BitwiseNot => unop_builder(BitwiseNot)?,
1748                    // Dereferences do nothing for codegen of the actual operator. It only
1749                    // prevents pipelining, hence Alias is fine here
1750                    hir::expression::UnaryOperator::Dereference => unop_builder(Alias)?,
1751                    hir::expression::UnaryOperator::Reference => unop_builder(Alias)?,
1752                };
1753            }
1754            ExprKind::TupleLiteral(elems) => {
1755                for elem in elems {
1756                    result.append(elem.lower(ctx)?)
1757                }
1758
1759                result.push_primary(
1760                    mir::Statement::Binding(mir::Binding {
1761                        name: self.variable(ctx)?,
1762                        operator: mir::Operator::ConstructTuple,
1763                        operands: elems
1764                            .iter()
1765                            .map(|e| e.variable(ctx))
1766                            .collect::<Result<_>>()?,
1767                        ty: self_type,
1768                        loc: Some(self.loc()),
1769                    }),
1770                    self,
1771                )
1772            }
1773            ExprKind::TupleIndex(tup, idx) => {
1774                result.append(tup.lower(ctx)?);
1775
1776                result.push_primary(
1777                    mir::Statement::Binding(mir::Binding {
1778                        name: self.variable(ctx)?,
1779                        operator: mir::Operator::IndexTuple(idx.inner as u64),
1780                        operands: vec![tup.variable(ctx)?],
1781                        ty: self_type,
1782                        loc: Some(self.loc()),
1783                    }),
1784                    self,
1785                )
1786            }
1787            ExprKind::CreatePorts => {
1788                let (inner_type, right_mir_type) = match &hir_type {
1789                    ConcreteType::Tuple(inner) => {
1790                        if inner.len() != 2 {
1791                            diag_bail!(self, "port type was not 2-tuple. Got {hir_type}")
1792                        }
1793
1794                        (&inner[0], inner[1].to_mir_type())
1795                    }
1796                    _ => {
1797                        diag_bail!(self, "port type was not tuple. Got {hir_type}")
1798                    }
1799                };
1800
1801                if !inner_type.is_port() {
1802                    // For good diagnostics, we also need to look up the TypeVars
1803                    let self_tvar = ctx.types.type_of(&TypedExpression::Id(self.id));
1804
1805                    let inner_tvar = match self_tvar.resolve(ctx.types) {
1806                        TypeVar::Known(_, KnownType::Tuple, inner) => {
1807                            if inner.len() != 2 {
1808                                diag_bail!(self, "port type was not 2-tuple. Got {hir_type}")
1809                            }
1810
1811                            inner[0]
1812                        }
1813                        _ => {
1814                            diag_bail!(self, "port type was not tuple. Got {hir_type}")
1815                        }
1816                    };
1817
1818                    return Err(Diagnostic::error(
1819                        self,
1820                        "A port expression cannot create non-port values",
1821                    )
1822                    .primary_label(format!(
1823                        "{inner_tvar} is not a port type",
1824                        inner_tvar = inner_tvar.display(ctx.types)
1825                    ))
1826                    .note(format!(
1827                        "The port expression creates a {self_tvar}",
1828                        self_tvar = self_tvar.display(ctx.types)
1829                    )));
1830                }
1831
1832                let inner_mir_type = inner_type.to_mir_type();
1833
1834                let lname = mir::ValueName::Expr(ctx.idtracker.next());
1835                let rname = mir::ValueName::Expr(ctx.idtracker.next());
1836
1837                result.append_secondary(
1838                    vec![
1839                        mir::Statement::Binding(mir::Binding {
1840                            name: lname.clone(),
1841                            operator: mir::Operator::Nop,
1842                            operands: vec![],
1843                            ty: inner_mir_type,
1844                            loc: Some(self.loc()),
1845                        }),
1846                        mir::Statement::Binding(mir::Binding {
1847                            name: rname.clone(),
1848                            operator: mir::Operator::FlipPort,
1849                            operands: vec![lname.clone()],
1850                            ty: right_mir_type,
1851                            loc: Some(self.loc()),
1852                        }),
1853                    ],
1854                    self,
1855                    "Port construction",
1856                );
1857                result.push_primary(
1858                    mir::Statement::Binding(mir::Binding {
1859                        name: self.variable(ctx)?,
1860                        operator: mir::Operator::ConstructTuple,
1861                        operands: vec![lname, rname],
1862                        ty: self_type,
1863                        loc: Some(self.loc()),
1864                    }),
1865                    self,
1866                )
1867            }
1868            ExprKind::FieldAccess(target, field) => {
1869                result.append(target.lower(ctx)?);
1870
1871                let ctype = ctx.types.concrete_type_of(
1872                    target,
1873                    ctx.symtab.symtab(),
1874                    &ctx.item_list.types,
1875                )?;
1876
1877                let field_index = if let ConcreteType::Struct {
1878                    name: _,
1879                    is_port: _,
1880                    members,
1881                    field_translators: _,
1882                } = ctype
1883                {
1884                    let field_indices = members
1885                        .iter()
1886                        .enumerate()
1887                        .filter_map(
1888                            |(i, (name, _))| {
1889                                if name == &field.inner {
1890                                    Some(i)
1891                                } else {
1892                                    None
1893                                }
1894                            },
1895                        )
1896                        .collect::<Vec<_>>();
1897
1898                    diag_assert!(
1899                        self,
1900                        field_indices.len() == 1,
1901                        "Expected exactly 1 field with the name {field}, got {}",
1902                        field_indices.len()
1903                    );
1904
1905                    *field_indices.first().unwrap()
1906                } else {
1907                    unreachable!("Field access on non-struct {:?}", self_type)
1908                };
1909
1910                result.push_primary(
1911                    mir::Statement::Binding(mir::Binding {
1912                        name: self.variable(ctx)?,
1913                        operator: mir::Operator::IndexTuple(field_index as u64),
1914                        operands: vec![target.variable(ctx)?],
1915                        ty: self_type,
1916                        loc: Some(self.loc()),
1917                    }),
1918                    self,
1919                )
1920            }
1921            ExprKind::ArrayLiteral(values) => {
1922                for elem in values {
1923                    result.append(elem.lower(ctx)?);
1924                }
1925                result.push_primary(
1926                    mir::Statement::Binding(mir::Binding {
1927                        name: self.variable(ctx)?,
1928                        operator: mir::Operator::ConstructArray,
1929                        operands: values
1930                            .iter()
1931                            .map(|v| v.variable(ctx))
1932                            .collect::<Result<_>>()?,
1933                        ty: self_type,
1934                        loc: Some(self.loc()),
1935                    }),
1936                    self,
1937                )
1938            }
1939            ExprKind::ArrayShorthandLiteral(value, amount) => {
1940                // This could be caught earlier, but if we for some reason want to allow
1941                // arrays longer than usize::MAX elements (why?) we should report
1942                // it as a diagnostic when we actually want to _use_ the amount as a usize.
1943                let amount = amount.resolve_int(ctx)?.to_usize().ok_or_else(|| {
1944                    Diagnostic::error(
1945                        amount,
1946                        format!(
1947                            "Array using shorthand initialization cannot contain more than usize::max ({}) elements",
1948                            usize::MAX
1949                        ),
1950                    )
1951                })?;
1952                result.append(value.lower(ctx)?);
1953                result.push_primary(
1954                    mir::Statement::Binding(mir::Binding {
1955                        name: self.variable(ctx)?,
1956                        operator: mir::Operator::ConstructArray,
1957                        operands: (0..amount)
1958                            .map(|_| value.variable(ctx))
1959                            .collect::<Result<_>>()?,
1960                        ty: self_type,
1961                        loc: Some(self.loc()),
1962                    }),
1963                    self,
1964                )
1965            }
1966            ExprKind::Index(target, index) => {
1967                result.append(target.lower(ctx)?);
1968                result.append(index.lower(ctx)?);
1969
1970                result.push_primary(
1971                    mir::Statement::Binding(mir::Binding {
1972                        name: self.variable(ctx)?,
1973                        operator: mir::Operator::IndexArray,
1974                        operands: vec![target.variable(ctx)?, index.variable(ctx)?],
1975                        ty: self_type,
1976                        loc: Some(self.loc()),
1977                    }),
1978                    self,
1979                )
1980            }
1981            ExprKind::RangeIndex { target, start, end } => {
1982                result.append(target.lower(ctx)?);
1983
1984                let start_val = start.resolve_int(ctx)?;
1985                let start = start_val.to_biguint().ok_or_else(|| {
1986                    Diagnostic::error(start, "The start of a range cannot be negative")
1987                        .primary_label(format!("Inferred negative range start ({start_val})"))
1988                })?;
1989                let end_val = end.resolve_int(ctx)?;
1990                let end = end_val.to_biguint().ok_or_else(|| {
1991                    Diagnostic::error(end, "The end of a range cannot be negative")
1992                        .primary_label(format!("Inferred negative range end ({end_val})"))
1993                })?;
1994
1995                result.push_primary(
1996                    mir::Statement::Binding(mir::Binding {
1997                        name: self.variable(ctx)?,
1998                        operator: mir::Operator::RangeIndexArray {
1999                            start: start.clone(),
2000                            end_exclusive: end.clone(),
2001                        },
2002                        operands: vec![target.variable(ctx)?],
2003                        ty: self_type,
2004                        loc: Some(self.loc()),
2005                    }),
2006                    self,
2007                )
2008            }
2009            ExprKind::Block(block) => {
2010                for statement in &block.statements {
2011                    result.append(statement.lower(ctx)?);
2012                }
2013                if let Some(block_result) = &block.result {
2014                    result.append(block_result.lower(ctx)?);
2015                }
2016
2017                // Empty. The block result will always be the last expression
2018            }
2019            ExprKind::If(cond, on_true, on_false) => {
2020                result.append(cond.lower(ctx)?);
2021                result.append(on_true.lower(ctx)?);
2022                result.append(on_false.lower(ctx)?);
2023
2024                result.push_primary(
2025                    mir::Statement::Binding(mir::Binding {
2026                        name: self.variable(ctx)?,
2027                        operator: mir::Operator::Select,
2028                        operands: vec![
2029                            cond.variable(ctx)?,
2030                            on_true.variable(ctx)?,
2031                            on_false.variable(ctx)?,
2032                        ],
2033                        ty: ctx
2034                            .types
2035                            .concrete_type_of(self, ctx.symtab.symtab(), &ctx.item_list.types)?
2036                            .to_mir_type(),
2037                        loc: Some(self.loc()),
2038                    }),
2039                    self,
2040                );
2041            }
2042            ExprKind::Match(operand, branches) => {
2043                let operand_ty = ctx.types.concrete_type_of(
2044                    operand,
2045                    ctx.symtab.symtab(),
2046                    &ctx.item_list.types,
2047                )?;
2048
2049                if !operand_ty.is_error_recursively() {
2050                    // Check for missing branches
2051                    let mut pat_stacks = branches
2052                        .iter()
2053                        .filter_map(|(pat, if_cond, _)| match if_cond {
2054                            Some(_) => None,
2055                            None => Some(PatStack::new(vec![DeconstructedPattern::from_hir(
2056                                pat, ctx,
2057                            )])),
2058                        })
2059                        .collect::<Vec<_>>();
2060
2061                    // The patterns which make a wildcard useful are the ones that are missing
2062                    // from the match statement
2063                    let wildcard_useful = is_useful(
2064                        &PatStack::new(vec![DeconstructedPattern::wildcard(&operand_ty)]),
2065                        &usefulness::Matrix::new(&pat_stacks),
2066                    );
2067
2068                    let mut predicated_branch_diagnosed = false;
2069
2070                    if wildcard_useful.is_useful() {
2071                        let witness_string = format_witnesses(&wildcard_useful.witnesses);
2072
2073                        let mut diagnostics = Diagnostic::error(
2074                            self.loc(),
2075                            format!("Non-exhaustive match: {witness_string} not covered"),
2076                        )
2077                        .primary_label(format!("{witness_string} not covered",));
2078
2079                        for witness in &wildcard_useful.witnesses {
2080                            for (pat, _, _) in branches.iter().filter(|(_, c, _)| c.is_some()) {
2081                                // Try adding each predicated branch to the end and checking if the
2082                                // witness pattern stacks stop being useful. That means this branch,
2083                                // if unpredicated, could prevent the witness from existing.
2084
2085                                let extra_pat_stack =
2086                                    PatStack::new(vec![DeconstructedPattern::from_hir(pat, ctx)]);
2087
2088                                pat_stacks.push(extra_pat_stack);
2089
2090                                let witness_useful = is_useful(
2091                                    &PatStack::new(witness.0.clone()),
2092                                    &usefulness::Matrix::new(&pat_stacks),
2093                                );
2094
2095                                pat_stacks.pop();
2096
2097                                if !witness_useful.is_useful() {
2098                                    diagnostics = diagnostics.secondary_label(
2099                                        pat,
2100                                        format!("This provides {witness} but the branch has a predicate"),
2101                                    );
2102                                    predicated_branch_diagnosed = true;
2103                                }
2104                            }
2105                        }
2106
2107                        if predicated_branch_diagnosed {
2108                            diagnostics.add_note(
2109                                "Predicated branches are ignored when checking exhaustiveness",
2110                            );
2111                        }
2112
2113                        return Err(diagnostics);
2114                    }
2115                }
2116
2117                result.append(operand.lower(ctx)?);
2118                let mut operands = vec![];
2119                for (pat, if_cond, result_expr) in branches {
2120                    let pat_ty = ctx
2121                        .types
2122                        .concrete_type_of(pat, ctx.symtab.symtab(), &ctx.item_list.types)?
2123                        .to_mir_type();
2124                    result.append(pat.lower(operand.variable(ctx)?, pat_ty, ctx)?);
2125
2126                    let if_cond = match if_cond {
2127                        Some(c) => {
2128                            result.append(c.lower(ctx)?);
2129                            Some(c.variable(ctx)?)
2130                        }
2131                        None => None,
2132                    };
2133
2134                    let cond = pat.condition(&operand.variable(ctx)?, if_cond, ctx)?;
2135
2136                    result.append_secondary(cond.statements, pat, "Pattern condition");
2137
2138                    result.append(result_expr.lower(ctx)?);
2139
2140                    operands.push(cond.result_name);
2141                    operands.push(result_expr.variable(ctx)?);
2142                }
2143
2144                result.push_primary(
2145                    mir::Statement::Binding(mir::Binding {
2146                        name: self.variable(ctx)?,
2147                        operator: mir::Operator::Match,
2148                        operands,
2149                        ty: ctx
2150                            .types
2151                            .concrete_type_of(self, ctx.symtab.symtab(), &ctx.item_list.types)?
2152                            .to_mir_type(),
2153                        loc: Some(self.loc()),
2154                    }),
2155                    self,
2156                )
2157            }
2158            ExprKind::Call {
2159                kind,
2160                callee,
2161                args,
2162                turbofish: _,
2163                safety,
2164            } => {
2165                let head = ctx.symtab.symtab().unit_by_id(callee);
2166                let args = match_args_with_params(args, &head.inputs.inner, false)
2167                    .map_err(Diagnostic::from)?;
2168
2169                if *safety == Safety::Default && head.unsafe_marker.is_some() {
2170                    return Err(Diagnostic::error(
2171                        callee,
2172                        "You cannot call unsafe code in safe context",
2173                    )
2174                    .primary_label("This is missing an unsafe block")
2175                    .span_suggest_multipart(
2176                        "Consider wrapping the code in an unsafe block",
2177                        SuggestionParts::new()
2178                            .part(
2179                                (
2180                                    Span::new(self.span.start(), self.span.start()),
2181                                    self.file_id,
2182                                ),
2183                                "unsafe { ",
2184                            )
2185                            .part(
2186                                (Span::new(self.span.end(), self.span.end()), self.file_id),
2187                                " }",
2188                            ),
2189                    ));
2190                }
2191
2192                match (&head.unit_kind.inner, kind) {
2193                    (UnitKind::Function(_), CallKind::Function)
2194                    | (UnitKind::Entity, CallKind::Entity(_)) => {
2195                        result.append(self.handle_call(callee, &args, &head, ctx)?);
2196                    }
2197                    (
2198                        UnitKind::Pipeline {
2199                            depth: _,
2200                            depth_typeexpr_id: _,
2201                        },
2202                        CallKind::Pipeline {
2203                            inst_loc: _,
2204                            depth: _,
2205                            depth_typeexpr_id: _,
2206                        },
2207                    ) => {
2208                        result.append(self.handle_call(callee, &args, &head, ctx)?);
2209                    }
2210                    (
2211                        UnitKind::Function(_),
2212                        CallKind::Entity(loc) | CallKind::Pipeline { inst_loc: loc, .. },
2213                    ) => {
2214                        return Err(
2215                            Diagnostic::error(loc, "Unexpected `inst` for function call")
2216                                .primary_label("Unexpected `inst`")
2217                                .secondary_label(
2218                                    callee,
2219                                    format!("Because {} is a function", head.name),
2220                                )
2221                                .secondary_label(
2222                                    &head.unit_kind,
2223                                    format!("{} is defined as a function here", head.name),
2224                                )
2225                                .span_suggest_remove("Consider removing the `inst`", loc),
2226                        );
2227                    }
2228                    (UnitKind::Entity, CallKind::Function) => {
2229                        return Err(Diagnostic::error(
2230                            callee,
2231                            "Expected `inst` to instantiate entity",
2232                        )
2233                        .primary_label("Expected `inst`")
2234                        .secondary_label(callee, format!("Because {} is an entity", head.name))
2235                        .secondary_label(
2236                            &head.unit_kind,
2237                            format!("{} is defined as an entity here", head.name),
2238                        )
2239                        .span_suggest_insert_before(
2240                            "Consider adding `inst`",
2241                            callee,
2242                            "inst ",
2243                        ))
2244                    }
2245                    (UnitKind::Entity, CallKind::Pipeline { inst_loc, .. }) => {
2246                        return Err(Diagnostic::error(
2247                            inst_loc,
2248                            "Unexpected pipeline depth for entity",
2249                        )
2250                        .primary_label("Expected plain `inst`")
2251                        .secondary_label(callee, format!("Because {} is an entity", head.name))
2252                        .secondary_label(
2253                            &head.unit_kind,
2254                            format!("{} is defined as an entity here", head.name),
2255                        )
2256                        .span_suggest_replace(
2257                            "Consider removing the pipeline depth",
2258                            inst_loc,
2259                            "inst",
2260                        ))
2261                    }
2262                    (UnitKind::Pipeline { .. }, CallKind::Function) => {
2263                        return Err(Diagnostic::error(
2264                            callee,
2265                            "Expected `inst` and pipeline depth for pipeline instantiation",
2266                        )
2267                        .primary_label("Expected pipeline instantiation")
2268                        .secondary_label(callee, format!("Because {} is a pipeline", head.name))
2269                        .secondary_label(
2270                            &head.unit_kind,
2271                            format!("{} is defined as a pipeline here", head.name),
2272                        )
2273                        .span_suggest_insert_before(
2274                            "Consider instantiating the pipeline with a depth",
2275                            callee,
2276                            "inst(/*depth*/) ",
2277                        ))
2278                    }
2279                    (UnitKind::Pipeline { .. }, CallKind::Entity(loc)) => {
2280                        return Err(Diagnostic::error(loc, "Expected pipeline depth")
2281                            .primary_label("Expected pipeline depth")
2282                            .secondary_label(callee, format!("Because {} is a pipeline", head.name))
2283                            .secondary_label(
2284                                &head.unit_kind,
2285                                format!("{} is defined as a pipeline here", head.name),
2286                            )
2287                            .span_suggest_insert_after(
2288                                "Consider specifying the pipeline depth",
2289                                loc,
2290                                "(/*depth*/)",
2291                            ))
2292                    }
2293                }
2294            }
2295            ExprKind::PipelineRef { .. } => {
2296                // Empty: Pipeline refs are lowered in the alias checking
2297            }
2298            ExprKind::StageReady => {
2299                let signal = ctx
2300                    .pipeline_context
2301                    .get(self)?
2302                    .ready_signals
2303                    .get(ctx.subs.current_stage)
2304                    .ok_or_else(|| Diagnostic::bug(self, "Pipeline ready signal overflow"))?
2305                    .clone();
2306
2307                // If there is no enable signal, valid will be true, generate a constant
2308                match signal {
2309                    Some(signal_name) => {
2310                        // NOTE: we could use the aliases method here, but that
2311                        // would require duplicating the logic to check if the enable
2312                        // signal is set
2313                        result.push_primary(
2314                            mir::Statement::Binding(mir::Binding {
2315                                name: self.variable(ctx)?,
2316                                operator: mir::Operator::Alias,
2317                                operands: vec![signal_name.clone()],
2318                                ty: mir::types::Type::Bool,
2319                                loc: Some(self.loc()),
2320                            }),
2321                            self,
2322                        )
2323                    }
2324                    None => result.push_primary(
2325                        mir::Statement::Constant(
2326                            self.id,
2327                            mir::types::Type::Bool,
2328                            mir::ConstantValue::Bool(true),
2329                        ),
2330                        self,
2331                    ),
2332                }
2333            }
2334            ExprKind::StageValid => {
2335                let signal = ctx
2336                    .pipeline_context
2337                    .get(self)?
2338                    .valid_signals
2339                    .get(ctx.subs.current_stage)
2340                    .ok_or_else(|| Diagnostic::bug(self, "Pipeline valid signal overflow"))?
2341                    .clone();
2342
2343                match signal {
2344                    Some(signal_name) => {
2345                        // NOTE: we could use the aliases method here, but that
2346                        // would require duplicating the logic to check if the enable
2347                        // signal is set
2348                        result.push_primary(
2349                            mir::Statement::Binding(mir::Binding {
2350                                name: self.variable(ctx)?,
2351                                operator: mir::Operator::Alias,
2352                                operands: vec![signal_name.clone()],
2353                                ty: mir::types::Type::Bool,
2354                                loc: Some(self.loc()),
2355                            }),
2356                            self,
2357                        )
2358                    }
2359                    None => result.push_primary(
2360                        mir::Statement::Constant(
2361                            self.id,
2362                            mir::types::Type::Bool,
2363                            mir::ConstantValue::Bool(true),
2364                        ),
2365                        self,
2366                    ),
2367                }
2368            }
2369            ExprKind::TypeLevelIf(_, _, _) => {
2370                diag_bail!(
2371                    self,
2372                    "Type level if should already have been lowered at this point"
2373                )
2374            }
2375            ExprKind::MethodCall { .. } => {
2376                diag_bail!(
2377                    self,
2378                    "Method should already have been lowered at this point"
2379                )
2380            }
2381            ExprKind::LambdaDef { .. } => {
2382                diag_bail!(self, "Lambda def expression found during hir lowering")
2383            }
2384            ExprKind::Null => {
2385                diag_bail!(self, "Null expression found during hir lowering")
2386            }
2387            ExprKind::StaticUnreachable(message) => {
2388                return Err(Diagnostic::error(
2389                    message,
2390                    format!("Reached unreachable code '{message}'"),
2391                )
2392                .primary_label(message.inner.clone()))
2393            }
2394        }
2395
2396        Ok(result)
2397    }
2398
2399    fn handle_call(
2400        &self,
2401        name: &Loc<NameID>,
2402        args: &[Argument<Expression, TypeSpec>],
2403        unit_head: &Loc<UnitHead>,
2404        ctx: &mut Context,
2405    ) -> Result<StatementList> {
2406        let mut result = StatementList::new();
2407        for param in args {
2408            result.append(param.value.lower(ctx)?);
2409        }
2410
2411        let tok = GenericListToken::Expression(self.id);
2412        let instance_list = &ctx
2413            .types
2414            .get_generic_list(&tok)
2415            .ok_or_else(|| diag_anyhow!(name, "Found no generic list for call"))?;
2416
2417        for (param, var) in unit_head
2418            .get_type_params()
2419            .iter()
2420            .map(|p| (p, instance_list.get(&p.name_id)))
2421        {
2422            let param_name = &param.name_id;
2423            let Some(var) = var else {
2424                diag_bail!(self, "Did not find a type for {param_name}");
2425            };
2426
2427            if ctx
2428                .types
2429                .ungenerify_type(var, ctx.symtab.symtab(), &ctx.item_list.types)
2430                .is_none()
2431            {
2432                return Err(Diagnostic::error(
2433                    self,
2434                    format!("The type of {param_name} is not fully known in this call"),
2435                )
2436                .primary_label(format!("Type of type parameter {param_name} is not fully known"))
2437                .secondary_label(param, format!("{param_name} is defined here"))
2438                .help("Consider specifying the type parameters explicitly https://docs.spade-lang.org/units.html#brief-intro-to-generic-parameters"));
2439            };
2440        }
2441
2442        let generic_port_check = || {
2443            // Check if this is a call to something generic. If so we need to ensure that the
2444            // generic arguments were not mapped to ports
2445            for (name, ty) in instance_list {
2446                let actual =
2447                    ctx.types
2448                        .ungenerify_type(&ty, ctx.symtab.symtab(), &ctx.item_list.types);
2449                if actual.as_ref().map(|t| t.is_port()).unwrap_or(false) {
2450                    return Err(
2451                        Diagnostic::error(self.loc(), "Generic types cannot be ports")
2452                            .primary_label(format!(
2453                                "Parameter {name} is {actual} which is a port type",
2454                                actual = actual.unwrap()
2455                            )),
2456                    );
2457                }
2458            }
2459            Ok(())
2460        };
2461
2462        // Check if this is a standard library function which we are supposed to
2463        // handle
2464        macro_rules! handle_special_function {
2465            ([$($path:expr),*] $allow_port:expr => $handler:ident {allow_port}) => {
2466                handle_special_function!([$($path),*] => $handler true)
2467            };
2468            ([$($path:expr),*] $allow_port:expr => $handler:ident) => {
2469                handle_special_function!([$($path),*] => $handler false)
2470            };
2471            ([$($path:expr),*] => $handler:ident $allow_port:expr) => {
2472                let path = Path(vec![$(PathSegment::Named(Identifier::intern($path).nowhere())),*]).nowhere();
2473                let final_id = ctx.symtab.symtab().try_lookup_id(&path);
2474                if final_id
2475                    .map(|n| &n == &name.inner)
2476                    .unwrap_or(false)
2477                {
2478                    if !$allow_port {
2479                        generic_port_check()?;
2480                    }
2481
2482                    return self.$handler(&name, result, args, ctx);
2483                };
2484            }
2485        }
2486        macro_rules! handle_special_functions {
2487            ($([$($path:expr),*] => $handler:ident $({$extra:tt})?),*) => {
2488                $(
2489                    handle_special_function!([$($path),*] true => $handler $({$extra})?)
2490                );*
2491            };
2492        }
2493
2494        handle_special_functions! {
2495            ["std", "mem", "clocked_memory"] => handle_clocked_memory_decl,
2496            ["std", "mem", "clocked_memory_init"] => handle_clocked_memory_initial_decl,
2497            ["std", "mem", "read_memory"] => handle_read_memory,
2498            ["std", "conv", "trunc"] => handle_trunc,
2499            ["std", "conv", "sext"] => handle_sext,
2500            ["std", "conv", "zext"] => handle_zext,
2501            ["std", "conv", "concat"] => handle_concat,
2502            ["std", "conv", "transmute"] => handle_transmute {allow_port},
2503            ["std", "ops", "div_pow2"] => handle_div_pow2,
2504            ["std", "ops", "reduce_and"] => handle_reduce_and,
2505            ["std", "ops", "reduce_or"] => handle_reduce_or,
2506            ["std", "ops", "reduce_xor"] => handle_reduce_xor,
2507            ["std", "ops", "comb_div"] => handle_comb_div,
2508            ["std", "ops", "comb_mod"] => handle_comb_mod,
2509            ["std", "ports", "read_mut_wire"] => handle_read_mut_wire,
2510            ["std", "ports", "read_write_inout"] => handle_read_write_inout
2511        }
2512
2513        generic_port_check()?;
2514
2515        // Look up the name in the executable list to see if this is a type instantiation
2516        match ctx.item_list.executables.get(name) {
2517            Some(hir::ExecutableItem::EnumInstance {
2518                base_enum: _,
2519                variant,
2520            }) => result.push_primary(
2521                mir::Statement::Binding(mir::Binding {
2522                    name: self.variable(ctx)?,
2523                    ty: ctx
2524                        .types
2525                        .concrete_type_of(self, ctx.symtab.symtab(), &ctx.item_list.types)?
2526                        .to_mir_type(),
2527                    operator: mir::Operator::ConstructEnum { variant: *variant },
2528                    operands: args
2529                        .iter()
2530                        .map(|arg| arg.value.variable(ctx))
2531                        .collect::<Result<_>>()?,
2532                    loc: Some(self.loc()),
2533                }),
2534                self,
2535            ),
2536            Some(hir::ExecutableItem::StructInstance) => result.push_primary(
2537                mir::Statement::Binding(mir::Binding {
2538                    name: self.variable(ctx)?,
2539                    ty: ctx
2540                        .types
2541                        .concrete_type_of(self, ctx.symtab.symtab(), &ctx.item_list.types)?
2542                        .to_mir_type(),
2543                    operator: mir::Operator::ConstructTuple,
2544                    operands: args
2545                        .iter()
2546                        .map(|arg| arg.value.variable(ctx))
2547                        .collect::<Result<Vec<_>>>()?,
2548                    loc: Some(self.loc()),
2549                }),
2550                self,
2551            ),
2552            Some(hir::ExecutableItem::Unit(u)) => {
2553                let (type_params, unit_name) = (&u.head.get_type_params(), u.name.clone());
2554
2555                let instance_name = if !type_params.is_empty() {
2556                    let t = type_params
2557                        .iter()
2558                        .map(|param| {
2559                            let name = param.name_id();
2560
2561                            let t = instance_list[&name].clone().resolve(&ctx.types);
2562                            t.into_known(&ctx.types)
2563                                .ok_or_else(|| {
2564                                    diag_anyhow!(
2565                                        param,
2566                                        "This type is not fully known, it is {}",
2567                                        t.display(&ctx.types),
2568                                    )
2569                                })
2570                                .clone()
2571                        })
2572                        .collect::<Result<_>>()?;
2573
2574                    UnitName::WithID(
2575                        ctx.mono_state
2576                            .request_compilation(
2577                                unit_name,
2578                                false,
2579                                t,
2580                                ctx.symtab,
2581                                ctx.self_mono_item.clone().map(|item| (item, self.loc())),
2582                            )
2583                            .nowhere(),
2584                    )
2585                } else {
2586                    unit_name
2587                };
2588
2589                let argument_names = args
2590                    .iter()
2591                    .zip(&u.head.inputs.0)
2592                    .map(|(_, input)| mir::ParamName {
2593                        name: input.name.inner.as_str().to_owned(),
2594                        no_mangle: input.no_mangle,
2595                    })
2596                    .collect();
2597
2598                result.push_primary(
2599                    mir::Statement::Binding(mir::Binding {
2600                        name: self.variable(ctx)?,
2601                        operator: mir::Operator::Instance {
2602                            name: instance_name.as_mir(),
2603                            params: vec![],
2604                            argument_names,
2605                            loc: Some(self.loc()),
2606                        },
2607                        operands: args
2608                            .iter()
2609                            .map(|arg| arg.value.variable(ctx))
2610                            .collect::<Result<_>>()?,
2611                        ty: ctx
2612                            .types
2613                            .concrete_type_of(self, ctx.symtab.symtab(), &ctx.item_list.types)?
2614                            .to_mir_type(),
2615                        loc: Some(self.loc()),
2616                    }),
2617                    self,
2618                );
2619            }
2620            Some(hir::ExecutableItem::ExternUnit(name, head)) => {
2621                let (unit_name, type_params) = (name, &head.get_type_params());
2622
2623                // Grab the number-like type vars and pair them with their monomorphized values
2624                let verilog_parameters = type_params
2625                    .iter()
2626                    .flat_map(|type_param| match type_param.inner.meta {
2627                        MetaType::Number | MetaType::Int | MetaType::Uint => {
2628                            instance_list.get(&type_param.name_id).and_then(|type_var| {
2629                                type_var
2630                                    .resolve(&ctx.types)
2631                                    .expect_integer(
2632                                        BigUint::try_from,
2633                                        || unreachable!(),
2634                                        |_| unreachable!(),
2635                                        || Ok(BigUint::ZERO),
2636                                    )
2637                                    .ok()
2638                                    .map(|value| {
2639                                        (
2640                                            type_param.ident.inner.as_str().to_owned(),
2641                                            ConstantValue::Int(value.to_bigint()),
2642                                        )
2643                                    })
2644                            })
2645                        }
2646
2647                        MetaType::Str => {
2648                            instance_list.get(&type_param.name_id).and_then(|type_var| {
2649                                type_var
2650                                    .resolve(&ctx.types)
2651                                    .expect_string(
2652                                        Result::Ok,
2653                                        || unreachable!(),
2654                                        |_| unreachable!(),
2655                                        || Ok(String::new()),
2656                                    )
2657                                    .ok()
2658                                    .map(|value| {
2659                                        (
2660                                            type_param.ident.inner.as_str().to_owned(),
2661                                            ConstantValue::String(value),
2662                                        )
2663                                    })
2664                            })
2665                        }
2666
2667                        _ => None,
2668                    })
2669                    .collect_vec();
2670
2671                // NOTE: Ideally this check would be done earlier, when defining the generic
2672                // extern. However, at the moment, the compiler does not know if the generic
2673                // is an intrinsic until here when it has gone through the list of intrinsics
2674                if type_params.len() > verilog_parameters.len() {
2675                    let mut error = Diagnostic::error(
2676                        self.loc(),
2677                        "Generic `extern`s can only be instantiated with number or string parameters",
2678                    )
2679                    .primary_label("Invalid instance")
2680                    .secondary_label(head, "Because this generic `extern` has a type parameter");
2681
2682                    if let Some(example) = type_params.iter().find(|type_param| {
2683                        !matches!(
2684                            type_param.inner.meta,
2685                            MetaType::Int | MetaType::Uint | MetaType::Number | MetaType::Str,
2686                        )
2687                    }) {
2688                        error = error.span_suggest_remove("Remove this parameter", example);
2689                    }
2690
2691                    return Err(error);
2692                }
2693
2694                let argument_names = args
2695                    .iter()
2696                    .zip(&head.inputs.0)
2697                    .map(|(_, input)| mir::ParamName {
2698                        name: input.name.inner.as_str().to_string(),
2699                        no_mangle: input.no_mangle,
2700                    })
2701                    .collect();
2702
2703                // NOTE: Extern entities are not part of the item list, but we
2704                // should still emit the code for instantiating them
2705                result.push_primary(
2706                    mir::Statement::Binding(mir::Binding {
2707                        name: self.variable(ctx)?,
2708                        operator: mir::Operator::Instance {
2709                            name: unit_name.as_mir(),
2710                            params: verilog_parameters,
2711                            argument_names,
2712                            loc: Some(self.loc()),
2713                        },
2714                        operands: args
2715                            .iter()
2716                            .map(|arg| arg.value.variable(ctx))
2717                            .collect::<Result<_>>()?,
2718                        ty: ctx
2719                            .types
2720                            .concrete_type_of(self, ctx.symtab.symtab(), &ctx.item_list.types)?
2721                            .to_mir_type(),
2722                        loc: Some(self.loc()),
2723                    }),
2724                    self,
2725                );
2726            }
2727            None => {
2728                diag_bail!(name, "Instantiating an item which is not known ({name})")
2729            }
2730        };
2731
2732        Ok(result)
2733    }
2734
2735    /// Result is the initial statement list to expand and return
2736    fn handle_clocked_memory_decl(
2737        &self,
2738        _path: &Loc<NameID>,
2739        result: StatementList,
2740        args: &[Argument<Expression, TypeSpec>],
2741        ctx: &mut Context,
2742    ) -> Result<StatementList> {
2743        self.handle_clocked_memory(result, args, ctx, false)
2744    }
2745
2746    fn handle_clocked_memory_initial_decl(
2747        &self,
2748        _path: &Loc<NameID>,
2749        result: StatementList,
2750        args: &[Argument<Expression, TypeSpec>],
2751        ctx: &mut Context,
2752    ) -> Result<StatementList> {
2753        self.handle_clocked_memory(result, args, ctx, true)
2754    }
2755
2756    fn handle_clocked_memory(
2757        &self,
2758        result: StatementList,
2759        args: &[Argument<Expression, TypeSpec>],
2760        ctx: &mut Context,
2761        has_initial: bool,
2762    ) -> Result<StatementList> {
2763        // The localimpl macro is a bit stupid
2764        let mut result = result;
2765
2766        let initial = if has_initial {
2767            let initial_arg = &args[2];
2768
2769            if let Some(witness) = initial_arg.value.runtime_requirement_witness(ctx) {
2770                return Err(Diagnostic::error(
2771                    initial_arg.value,
2772                    "Memory initial values must be known at compile time",
2773                )
2774                .primary_label("Value not known at compile time")
2775                .secondary_label(
2776                    witness,
2777                    "This subexpression cannot be computed at compile time",
2778                ));
2779            }
2780
2781            match &initial_arg.value.kind {
2782                ExprKind::ArrayLiteral(elems) => {
2783                    let values = elems
2784                        .iter()
2785                        .map(|e| Ok(e.lower(ctx)?.to_vec_no_source_map()))
2786                        .collect::<Result<Vec<_>>>()?;
2787
2788                    Some(values)
2789                }
2790                ExprKind::ArrayShorthandLiteral(elem, amount) => {
2791                    let value = elem.lower(ctx)?.to_vec_no_source_map();
2792
2793                    let amount = amount.resolve_int(ctx)?.to_usize().ok_or_else(|| {
2794                        Diagnostic::error(
2795                            amount,
2796                            format!(
2797                                "Array using shorthand initialization cannot contain more than usize::max ({}) elements",
2798                                usize::MAX
2799                            ),
2800                        )
2801                    })?;
2802
2803                    Some(vec![value; amount])
2804                }
2805                _ => diag_bail!(initial_arg.value, "Memory initial value was not array"),
2806            }
2807        } else {
2808            None
2809        };
2810
2811        result.push_primary(
2812            mir::Statement::Binding(mir::Binding {
2813                name: self.variable(ctx)?,
2814                operator: mir::Operator::DeclClockedMemory { initial },
2815                operands: args
2816                    .iter()
2817                    // The third argument (if present) is the initial values which
2818                    // are passed in the operand
2819                    .take(2)
2820                    .map(|arg| arg.value.variable(ctx))
2821                    .collect::<Result<Vec<_>>>()?,
2822                ty: ctx
2823                    .types
2824                    .concrete_type_of(self, ctx.symtab.symtab(), &ctx.item_list.types)?
2825                    .to_mir_type(),
2826                loc: Some(self.loc()),
2827            }),
2828            self,
2829        );
2830
2831        Ok(result)
2832    }
2833
2834    /// Result is the initial statement list to expand and return
2835    fn handle_read_memory(
2836        &self,
2837        _path: &Loc<NameID>,
2838        result: StatementList,
2839        args: &[Argument<Expression, TypeSpec>],
2840        ctx: &mut Context,
2841    ) -> Result<StatementList> {
2842        // The localimpl macro is a bit stupid
2843        let mut result = result;
2844
2845        let target = &args[0].value;
2846        let index = &args[1].value;
2847
2848        let self_type = ctx
2849            .types
2850            .concrete_type_of(self, ctx.symtab.symtab(), &ctx.item_list.types)?
2851            .to_mir_type();
2852
2853        result.push_primary(
2854            mir::Statement::Binding(mir::Binding {
2855                name: self.variable(ctx)?,
2856                operator: mir::Operator::IndexMemory,
2857                operands: vec![target.variable(ctx)?, index.variable(ctx)?],
2858                ty: self_type,
2859                loc: Some(self.loc()),
2860            }),
2861            self,
2862        );
2863
2864        Ok(result)
2865    }
2866
2867    fn handle_trunc(
2868        &self,
2869        _path: &Loc<NameID>,
2870        result: StatementList,
2871        args: &[Argument<Expression, TypeSpec>],
2872        ctx: &mut Context,
2873    ) -> Result<StatementList> {
2874        let mut result = result;
2875
2876        let self_type = ctx
2877            .types
2878            .concrete_type_of(self, ctx.symtab.symtab(), &ctx.item_list.types)?
2879            .to_mir_type();
2880
2881        let input_type = ctx
2882            .types
2883            .concrete_type_of(args[0].value, ctx.symtab.symtab(), &ctx.item_list.types)?
2884            .to_mir_type();
2885
2886        if self_type.size() > input_type.size() {
2887            let input_loc = args[0].value.loc();
2888            return Err(Diagnostic::error(input_loc, "Truncating to a larger value")
2889                .primary_label(format!("This value is {}", bits_str(input_type.size()),))
2890                .secondary_label(
2891                    self,
2892                    format!("The value is truncated to {} bits here", self_type.size()),
2893                )
2894                .note("Truncation can only remove bits"));
2895        }
2896
2897        result.push_primary(
2898            mir::Statement::Binding(mir::Binding {
2899                name: self.variable(ctx)?,
2900                operator: mir::Operator::Truncate,
2901                operands: vec![args[0].value.variable(ctx)?],
2902                ty: ctx
2903                    .types
2904                    .concrete_type_of(self, ctx.symtab.symtab(), &ctx.item_list.types)?
2905                    .to_mir_type(),
2906                loc: Some(self.loc()),
2907            }),
2908            self,
2909        );
2910
2911        Ok(result)
2912    }
2913
2914    fn handle_sext(
2915        &self,
2916        path: &Loc<NameID>,
2917        result: StatementList,
2918        args: &[Argument<Expression, TypeSpec>],
2919        ctx: &mut Context,
2920    ) -> Result<StatementList> {
2921        let mut result = result;
2922
2923        let self_type = ctx
2924            .types
2925            .concrete_type_of(self, ctx.symtab.symtab(), &ctx.item_list.types)?
2926            .to_mir_type();
2927
2928        let input_type = ctx
2929            .types
2930            .concrete_type_of(args[0].value, ctx.symtab.symtab(), &ctx.item_list.types)?
2931            .to_mir_type();
2932
2933        if self_type.size() < input_type.size() {
2934            let input_loc = args[0].value.loc();
2935            return Err(Diagnostic::error(self, "Sign-extending to a shorter value")
2936                .primary_label(format!(
2937                    "The value is sign-extended to {} here",
2938                    bits_str(self_type.size()),
2939                ))
2940                .secondary_label(
2941                    input_loc,
2942                    format!("This value is {} bits", input_type.size()),
2943                )
2944                .note("")
2945                .span_suggest_replace(
2946                    "Sign-extension cannot decrease width, use trunc instead",
2947                    path,
2948                    "trunc",
2949                ));
2950        }
2951
2952        result.push_primary(
2953            mir::Statement::Binding(mir::Binding {
2954                name: self.variable(ctx)?,
2955                operator: mir::Operator::SignExtend,
2956                operands: vec![args[0].value.variable(ctx)?],
2957                ty: self_type,
2958                loc: Some(self.loc()),
2959            }),
2960            self,
2961        );
2962
2963        Ok(result)
2964    }
2965
2966    fn handle_zext(
2967        &self,
2968        path: &Loc<NameID>,
2969        result: StatementList,
2970        args: &[Argument<Expression, TypeSpec>],
2971        ctx: &mut Context,
2972    ) -> Result<StatementList> {
2973        let mut result = result;
2974
2975        let self_type = ctx
2976            .types
2977            .concrete_type_of(self, ctx.symtab.symtab(), &ctx.item_list.types)?
2978            .to_mir_type();
2979
2980        let input_type = ctx
2981            .types
2982            .concrete_type_of(args[0].value, ctx.symtab.symtab(), &ctx.item_list.types)?
2983            .to_mir_type();
2984
2985        if self_type.size() < input_type.size() {
2986            let input_loc = args[0].value.loc();
2987            return Err(Diagnostic::error(self, "Zero-extending to a shorter value")
2988                .primary_label(format!(
2989                    "The value is zero-extended to {} here",
2990                    bits_str(self_type.size()),
2991                ))
2992                .secondary_label(
2993                    input_loc,
2994                    format!("This value is {}", bits_str(input_type.size())),
2995                )
2996                .span_suggest_replace(
2997                    "Zero-extension cannot decrease width, use trunc instead",
2998                    path,
2999                    "trunc",
3000                ));
3001        }
3002
3003        result.push_primary(
3004            mir::Statement::Binding(mir::Binding {
3005                name: self.variable(ctx)?,
3006                operator: mir::Operator::ZeroExtend,
3007                operands: vec![args[0].value.variable(ctx)?],
3008                ty: self_type,
3009                loc: None,
3010            }),
3011            self,
3012        );
3013
3014        Ok(result)
3015    }
3016
3017    fn handle_transmute(
3018        &self,
3019        _path: &Loc<NameID>,
3020        result: StatementList,
3021        args: &[Argument<Expression, TypeSpec>],
3022        ctx: &mut Context,
3023    ) -> Result<StatementList> {
3024        let mut result = result;
3025
3026        let self_type_hir =
3027            ctx.types
3028                .concrete_type_of(self, ctx.symtab.symtab(), &ctx.item_list.types)?;
3029        let self_type = self_type_hir.to_mir_type();
3030
3031        let input_type_hir =
3032            ctx.types
3033                .concrete_type_of(args[0].value, ctx.symtab.symtab(), &ctx.item_list.types)?;
3034        let input_type = input_type_hir.to_mir_type();
3035
3036        if self_type.backward_size() != BigUint::zero() {
3037            return Err(Diagnostic::error(
3038                self,
3039                format!("Attempting to transmute to type containing inv & value"),
3040            )
3041            .primary_label(format!("{self_type_hir} has an inv & wire")));
3042        }
3043        if input_type.backward_size() != BigUint::zero() {
3044            return Err(Diagnostic::error(
3045                args[0].value,
3046                format!("Attempting to transmute from type containing inv & value"),
3047            )
3048            .primary_label(format!("{input_type_hir} has an inv & wire")));
3049        }
3050
3051        if self_type.size() != input_type.size() {
3052            let input_loc = args[0].value.loc();
3053            return Err(Diagnostic::error(
3054                self,
3055                format!(
3056                    "Type size mismatch. Attempting to transmute {} to {}",
3057                    bits_str(input_type.size()),
3058                    bits_str(self_type.size())
3059                ),
3060            )
3061            .primary_label(format!(
3062                "The output type has {}",
3063                bits_str(self_type.size())
3064            ))
3065            .secondary_label(
3066                input_loc,
3067                format!("The source has {}", bits_str(input_type.size()),),
3068            )
3069            .note("transmute can only convert between types of identical size"));
3070        }
3071
3072        result.push_primary(
3073            mir::Statement::Binding(mir::Binding {
3074                name: self.variable(ctx)?,
3075                operator: mir::Operator::ZeroExtend,
3076                operands: vec![args[0].value.variable(ctx)?],
3077                ty: self_type,
3078                loc: None,
3079            }),
3080            self,
3081        );
3082
3083        Ok(result)
3084    }
3085
3086    fn handle_concat(
3087        &self,
3088        path: &Loc<NameID>,
3089        result: StatementList,
3090        args: &[Argument<Expression, TypeSpec>],
3091        ctx: &mut Context,
3092    ) -> Result<StatementList> {
3093        let mut result = result;
3094
3095        let arg0_type = ctx
3096            .types
3097            .concrete_type_of(args[0].value, ctx.symtab.symtab(), &ctx.item_list.types)?
3098            .to_mir_type();
3099        let arg1_type = ctx
3100            .types
3101            .concrete_type_of(args[1].value, ctx.symtab.symtab(), &ctx.item_list.types)?
3102            .to_mir_type();
3103
3104        let self_type = ctx
3105            .types
3106            .concrete_type_of(self, ctx.symtab.symtab(), &ctx.item_list.types)?
3107            .to_mir_type();
3108
3109        if self_type.size() != arg0_type.size() + arg1_type.size() {
3110            Err(Diagnostic::bug(
3111                self_type.size().at_loc(self),
3112                format!(
3113                    "Concatenation produces {result} bits, expected {expected}",
3114                    expected = arg0_type.size() + arg1_type.size()
3115                ),
3116            ))
3117        } else {
3118            result.push_primary(
3119                mir::Statement::Binding(mir::Binding {
3120                    name: self.variable(ctx)?,
3121                    operator: mir::Operator::Concat,
3122                    operands: vec![args[0].value.variable(ctx)?, args[1].value.variable(ctx)?],
3123                    ty: self_type,
3124                    loc: Some(path.loc()),
3125                }),
3126                self,
3127            );
3128
3129            Ok(result)
3130        }
3131    }
3132
3133    fn handle_div_pow2(
3134        &self,
3135        _path: &Loc<NameID>,
3136        result: StatementList,
3137        args: &[Argument<Expression, TypeSpec>],
3138        ctx: &mut Context,
3139    ) -> Result<StatementList> {
3140        let mut result = result;
3141
3142        let self_type = ctx
3143            .types
3144            .concrete_type_of(self, ctx.symtab.symtab(), &ctx.item_list.types)?
3145            .to_mir_type();
3146
3147        result.push_primary(
3148            mir::Statement::Binding(mir::Binding {
3149                name: self.variable(ctx)?,
3150                operator: mir::Operator::DivPow2,
3151                operands: vec![args[0].value.variable(ctx)?, args[1].value.variable(ctx)?],
3152                ty: self_type,
3153                loc: Some(self.loc()),
3154            }),
3155            self,
3156        );
3157
3158        Ok(result)
3159    }
3160
3161    fn handle_reduce_and(
3162        &self,
3163        _path: &Loc<NameID>,
3164        result: StatementList,
3165        args: &[Argument<Expression, TypeSpec>],
3166        ctx: &mut Context,
3167    ) -> Result<StatementList> {
3168        let mut result = result;
3169
3170        let self_type = ctx
3171            .types
3172            .concrete_type_of(self, ctx.symtab.symtab(), &ctx.item_list.types)?
3173            .to_mir_type();
3174
3175        result.push_primary(
3176            mir::Statement::Binding(mir::Binding {
3177                name: self.variable(ctx)?,
3178                operator: mir::Operator::ReduceAnd,
3179                operands: vec![args[0].value.variable(ctx)?],
3180                ty: self_type,
3181                loc: Some(self.loc()),
3182            }),
3183            self,
3184        );
3185
3186        Ok(result)
3187    }
3188
3189    fn handle_reduce_or(
3190        &self,
3191        _path: &Loc<NameID>,
3192        result: StatementList,
3193        args: &[Argument<Expression, TypeSpec>],
3194        ctx: &mut Context,
3195    ) -> Result<StatementList> {
3196        let mut result = result;
3197
3198        let self_type = ctx
3199            .types
3200            .concrete_type_of(self, ctx.symtab.symtab(), &ctx.item_list.types)?
3201            .to_mir_type();
3202
3203        result.push_primary(
3204            mir::Statement::Binding(mir::Binding {
3205                name: self.variable(ctx)?,
3206                operator: mir::Operator::ReduceOr,
3207                operands: vec![args[0].value.variable(ctx)?],
3208                ty: self_type,
3209                loc: Some(self.loc()),
3210            }),
3211            self,
3212        );
3213
3214        Ok(result)
3215    }
3216
3217    fn handle_reduce_xor(
3218        &self,
3219        _path: &Loc<NameID>,
3220        result: StatementList,
3221        args: &[Argument<Expression, TypeSpec>],
3222        ctx: &mut Context,
3223    ) -> Result<StatementList> {
3224        let mut result = result;
3225
3226        let self_type = ctx
3227            .types
3228            .concrete_type_of(self, ctx.symtab.symtab(), &ctx.item_list.types)?
3229            .to_mir_type();
3230
3231        result.push_primary(
3232            mir::Statement::Binding(mir::Binding {
3233                name: self.variable(ctx)?,
3234                operator: mir::Operator::ReduceXor,
3235                operands: vec![args[0].value.variable(ctx)?],
3236                ty: self_type,
3237                loc: Some(self.loc()),
3238            }),
3239            self,
3240        );
3241
3242        Ok(result)
3243    }
3244
3245    fn handle_comb_div(
3246        &self,
3247        _path: &Loc<NameID>,
3248        result: StatementList,
3249        args: &[Argument<Expression, TypeSpec>],
3250        ctx: &mut Context,
3251    ) -> Result<StatementList> {
3252        let mut result = result;
3253
3254        let self_type = ctx
3255            .types
3256            .concrete_type_of(self, ctx.symtab.symtab(), &ctx.item_list.types)?
3257            .to_mir_type();
3258
3259        let operator = match self_type {
3260            mir::types::Type::Int(_) => mir::Operator::Div,
3261            mir::types::Type::UInt(_) => mir::Operator::UnsignedDiv,
3262            other => {
3263                return Err(Diagnostic::bug(
3264                    self,
3265                    format!("Inferred non-integer type ({other}) for division operand"),
3266                ))
3267            }
3268        };
3269
3270        result.push_primary(
3271            mir::Statement::Binding(mir::Binding {
3272                name: self.variable(ctx)?,
3273                operator,
3274                operands: vec![args[0].value.variable(ctx)?, args[1].value.variable(ctx)?],
3275                ty: self_type,
3276                loc: Some(self.loc()),
3277            }),
3278            self,
3279        );
3280
3281        Ok(result)
3282    }
3283
3284    fn handle_comb_mod(
3285        &self,
3286        _path: &Loc<NameID>,
3287        result: StatementList,
3288        args: &[Argument<Expression, TypeSpec>],
3289        ctx: &mut Context,
3290    ) -> Result<StatementList> {
3291        let mut result = result;
3292
3293        let self_type = ctx
3294            .types
3295            .concrete_type_of(self, ctx.symtab.symtab(), &ctx.item_list.types)?
3296            .to_mir_type();
3297
3298        let operator = match self_type {
3299            mir::types::Type::Int(_) => mir::Operator::Mod,
3300            mir::types::Type::UInt(_) => mir::Operator::UnsignedMod,
3301            other => {
3302                return Err(Diagnostic::bug(
3303                    self,
3304                    format!("Inferred non-integer type ({other}) for modulo operand"),
3305                ))
3306            }
3307        };
3308
3309        result.push_primary(
3310            mir::Statement::Binding(mir::Binding {
3311                name: self.variable(ctx)?,
3312                operator,
3313                operands: vec![args[0].value.variable(ctx)?, args[1].value.variable(ctx)?],
3314                ty: self_type,
3315                loc: Some(self.loc()),
3316            }),
3317            self,
3318        );
3319
3320        Ok(result)
3321    }
3322
3323    fn handle_read_mut_wire(
3324        &self,
3325        path: &Loc<NameID>,
3326        result: StatementList,
3327        args: &[Argument<Expression, TypeSpec>],
3328        ctx: &mut Context,
3329    ) -> Result<StatementList> {
3330        let mut result = result;
3331
3332        assert_eq!(args.len(), 1);
3333
3334        let self_type = ctx
3335            .types
3336            .concrete_type_of(self, ctx.symtab.symtab(), &ctx.item_list.types)?
3337            .to_mir_type();
3338
3339        result.push_primary(
3340            mir::Statement::Binding(mir::Binding {
3341                name: self.variable(ctx)?,
3342                operator: mir::Operator::ReadPort,
3343                operands: vec![args[0].value.variable(ctx)?],
3344                ty: self_type,
3345                loc: Some(path.loc()),
3346            }),
3347            self,
3348        );
3349
3350        Ok(result)
3351    }
3352
3353    fn handle_read_write_inout(
3354        &self,
3355        path: &Loc<NameID>,
3356        result: StatementList,
3357        args: &[Argument<Expression, TypeSpec>],
3358        ctx: &mut Context,
3359    ) -> Result<StatementList> {
3360        let mut result = result;
3361
3362        if args.len() != 1 {
3363            diag_bail!(
3364                path,
3365                "Invalid number of arguments (expected 1, got {})",
3366                args.len()
3367            );
3368        }
3369
3370        let self_type = ctx
3371            .types
3372            .concrete_type_of(self, ctx.symtab.symtab(), &ctx.item_list.types)?
3373            .to_mir_type();
3374
3375        result.push_primary(
3376            mir::Statement::Binding(mir::Binding {
3377                name: self.variable(ctx)?,
3378                operator: mir::Operator::ReadWriteInOut,
3379                operands: vec![args[0].value.variable(ctx)?],
3380                ty: self_type,
3381                loc: Some(path.loc()),
3382            }),
3383            self,
3384        );
3385
3386        Ok(result)
3387    }
3388}
3389
3390pub fn bits_str(bits: BigUint) -> String {
3391    format!("{} bit{}", bits, if bits == One::one() { "" } else { "s" })
3392}
3393
3394pub struct Context<'a> {
3395    pub symtab: &'a FrozenSymtab,
3396    pub idtracker: &'a ExprIdTracker,
3397    pub types: &'a mut TypeState,
3398    pub item_list: &'a ItemList,
3399    pub mono_state: &'a MonoState,
3400    // The generic list token of the currently generated uint if it is generic, otherwise None
3401    pub unit_generic_list: &'a Option<GenericListToken>,
3402    pub subs: &'a mut Substitutions,
3403    pub pipeline_context: &'a mut MaybePipelineContext,
3404    pub self_mono_item: Option<MonoItem>,
3405    pub trait_impls: &'a TraitImplList,
3406}
3407
3408pub fn generate_unit<'a>(
3409    unit: &Unit,
3410    name: UnitName,
3411    types: &mut TypeState,
3412    symtab: &FrozenSymtab,
3413    idtracker: &ExprIdTracker,
3414    item_list: &ItemList,
3415    unit_generic_list: &Option<GenericListToken>,
3416    // Map of names generated by codegen to the original name in the source code.
3417    name_map: &mut BTreeMap<NameID, NameID>,
3418    mono_state: &MonoState,
3419    name_source_map: &RwLock<NameSourceMap>,
3420    self_mono_item: Option<MonoItem>,
3421    opt_passes: &[&(dyn MirPass + Send + Sync)],
3422    trait_impls: &TraitImplList,
3423) -> Result<mir::Entity> {
3424    let mir_inputs = unit
3425        .head
3426        .inputs
3427        .0
3428        .iter()
3429        .zip(&unit.inputs)
3430        .map(
3431            |(
3432                Parameter {
3433                    name: _,
3434                    ty: type_spec,
3435                    no_mangle,
3436                    field_translator: _,
3437                },
3438                (name_id, _),
3439            )| {
3440                let name = name_id.1.tail().to_string();
3441                let val_name = name_id.value_name();
3442                let ty = types
3443                    .concrete_type_of_name(name_id, symtab.symtab(), &item_list.types)?
3444                    .to_mir_type();
3445
3446                if ty.backward_size() != BigUint::zero() && ty.size() != BigUint::zero() {
3447                    if let Some(no_mangle) = no_mangle {
3448                        return Err(Diagnostic::error(
3449                            no_mangle,
3450                            "Ports with both & and inv & cannot be #[no_mangle]",
3451                        )
3452                        .primary_label("Not allowed on mixed-direction ports")
3453                        .secondary_label(type_spec, "This has both & and inv & components"));
3454                    }
3455                }
3456
3457                name_source_map
3458                    .write()
3459                    .unwrap()
3460                    .insert_primary(&val_name, NameSource::Name(name_id.clone()));
3461
3462                Ok(MirInput {
3463                    name,
3464                    val_name,
3465                    ty,
3466                    no_mangle: *no_mangle,
3467                })
3468            },
3469        )
3470        .collect::<Result<_>>()?;
3471
3472    let mut statements = StatementList::new();
3473    let subs = &mut Substitutions::new();
3474    let pipeline_context = &mut MaybePipelineContext::NotPipeline;
3475
3476    let mut ctx = Context {
3477        symtab,
3478        idtracker,
3479        types,
3480        subs,
3481        item_list,
3482        unit_generic_list,
3483        mono_state,
3484        pipeline_context,
3485        self_mono_item,
3486        trait_impls,
3487    };
3488
3489    if let UnitKind::Pipeline {
3490        depth: _,
3491        depth_typeexpr_id: _,
3492    } = unit.head.unit_kind.inner
3493    {
3494        lower_pipeline(
3495            &unit.inputs,
3496            &unit.body,
3497            &mut statements,
3498            &mut ctx,
3499            name_map,
3500            unit.head.is_nonstatic_method,
3501        )?;
3502    }
3503
3504    statements.append(unit.body.lower(&mut ctx)?);
3505
3506    let output_t = ctx
3507        .types
3508        .concrete_type_of(&unit.body, ctx.symtab.symtab(), &item_list.types)?
3509        .to_mir_type();
3510
3511    linear_check::check_linear_types(
3512        &unit.inputs,
3513        &unit.body,
3514        ctx.types,
3515        ctx.symtab.symtab(),
3516        &item_list.types,
3517    )?;
3518
3519    let mut local_passes = opt_passes.to_vec();
3520    let mut verilog_attr_groups = vec![];
3521    let pass_impls = spade_mir::passes::mir_passes();
3522    unit.attributes.lower(&mut |attr| match &attr.inner {
3523        Attribute::Optimize { passes: new_passes } => {
3524            for new_pass in new_passes {
3525                if let Some(pass) = pass_impls.get(new_pass.inner.as_str()) {
3526                    local_passes.push(pass.as_ref());
3527                } else {
3528                    return Err(Diagnostic::error(
3529                        new_pass,
3530                        format!("There is no optimization pass named {new_pass}"),
3531                    )
3532                    .primary_label("No such pass"))?;
3533                }
3534            }
3535            Ok(())
3536        }
3537        Attribute::VerilogAttrs { entries } => {
3538            let group = entries
3539                .iter()
3540                .map(|(key, value)| {
3541                    let key = key.inner.to_string();
3542                    let value = value.clone().map(|v| v.inner);
3543                    (key, value)
3544                })
3545                .collect();
3546
3547            verilog_attr_groups.push(group);
3548            Ok(())
3549        }
3550        Attribute::Fsm { .. } | Attribute::WalTraceable { .. } => Err(attr.report_unused("unit")),
3551    })?;
3552
3553    let mut statements = statements.to_vec(&mut *name_source_map.write().unwrap());
3554
3555    for pass in local_passes.iter().chain(opt_passes) {
3556        statements = pass.transform_statements(&statements, ctx.idtracker);
3557    }
3558
3559    Ok(mir::Entity {
3560        name: name.as_mir(),
3561        inputs: mir_inputs,
3562        output: unit.body.variable(&ctx)?,
3563        output_type: output_t,
3564        verilog_attr_groups,
3565        statements,
3566    })
3567}