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 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 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 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 CType::Wire(inner) => inner.to_mir_type(),
325 }
326 }
327}
328
329pub trait NameIDExt {
330 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
352pub 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 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 #[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 #[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 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 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 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 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 #[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 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 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 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 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 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 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 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(); 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 } = ®ister;
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 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 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 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 fn variable(&self, ctx: &Context) -> Result<mir::ValueName> {
1509 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 }
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 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 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 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 }
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 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 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 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 }
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 match signal {
2309 Some(signal_name) => {
2310 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 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 = ¶m.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 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 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 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 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 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 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 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 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 .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 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 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 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 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}