1use alloc::{boxed::Box, string::String, vec::Vec};
2
3use miden_debug_types::{SourceSpan, Span, Spanned};
4pub use midenc_hir_type as types;
5use midenc_hir_type::{AddressSpace, Type, TypeRepr};
6
7use super::{ConstantExpr, DocString, Ident};
8
9#[derive(Debug, Clone, PartialEq, Eq)]
14pub enum TypeDecl {
15 Alias(TypeAlias),
17 Enum(EnumType),
19}
20
21impl TypeDecl {
22 pub fn name(&self) -> &Ident {
24 match self {
25 Self::Alias(ty) => &ty.name,
26 Self::Enum(ty) => &ty.name,
27 }
28 }
29
30 pub fn ty(&self) -> TypeExpr {
32 match self {
33 Self::Alias(ty) => ty.ty.clone(),
34 Self::Enum(ty) => TypeExpr::Primitive(Span::new(ty.span, ty.ty.clone())),
35 }
36 }
37}
38
39impl Spanned for TypeDecl {
40 fn span(&self) -> SourceSpan {
41 match self {
42 Self::Alias(spanned) => spanned.span,
43 Self::Enum(spanned) => spanned.span,
44 }
45 }
46}
47
48impl From<TypeAlias> for TypeDecl {
49 fn from(value: TypeAlias) -> Self {
50 Self::Alias(value)
51 }
52}
53
54impl From<EnumType> for TypeDecl {
55 fn from(value: EnumType) -> Self {
56 Self::Enum(value)
57 }
58}
59
60impl crate::prettier::PrettyPrint for TypeDecl {
61 fn render(&self) -> crate::prettier::Document {
62 match self {
63 Self::Alias(ty) => ty.render(),
64 Self::Enum(ty) => ty.render(),
65 }
66 }
67}
68
69#[derive(Debug, Clone)]
74pub struct FunctionType {
75 pub span: SourceSpan,
76 pub cc: types::CallConv,
77 pub args: Vec<TypeExpr>,
78 pub results: Vec<TypeExpr>,
79}
80
81impl Eq for FunctionType {}
82
83impl PartialEq for FunctionType {
84 fn eq(&self, other: &Self) -> bool {
85 self.cc == other.cc && self.args == other.args && self.results == other.results
86 }
87}
88
89impl core::hash::Hash for FunctionType {
90 fn hash<H: core::hash::Hasher>(&self, state: &mut H) {
91 self.cc.hash(state);
92 self.args.hash(state);
93 self.results.hash(state);
94 }
95}
96
97impl Spanned for FunctionType {
98 fn span(&self) -> SourceSpan {
99 self.span
100 }
101}
102
103impl FunctionType {
104 pub fn new(cc: types::CallConv, args: Vec<TypeExpr>, results: Vec<TypeExpr>) -> Self {
105 Self {
106 span: SourceSpan::UNKNOWN,
107 cc,
108 args,
109 results,
110 }
111 }
112
113 #[inline]
115 pub fn with_span(mut self, span: SourceSpan) -> Self {
116 self.span = span;
117 self
118 }
119}
120
121impl crate::prettier::PrettyPrint for FunctionType {
122 fn render(&self) -> crate::prettier::Document {
123 use crate::prettier::*;
124
125 let singleline_args = self
126 .args
127 .iter()
128 .map(|arg| arg.render())
129 .reduce(|acc, arg| acc + const_text(", ") + arg)
130 .unwrap_or(Document::Empty);
131 let multiline_args = indent(
132 4,
133 nl() + self
134 .args
135 .iter()
136 .map(|arg| arg.render())
137 .reduce(|acc, arg| acc + const_text(",") + nl() + arg)
138 .unwrap_or(Document::Empty),
139 ) + nl();
140 let args = singleline_args | multiline_args;
141 let args = const_text("(") + args + const_text(")");
142
143 match self.results.len() {
144 0 => args,
145 1 => args + const_text(" -> ") + self.results[0].render(),
146 _ => {
147 let results = self
148 .results
149 .iter()
150 .map(|r| r.render())
151 .reduce(|acc, r| acc + const_text(", ") + r)
152 .unwrap_or(Document::Empty);
153 args + const_text(" -> ") + const_text("(") + results + const_text(")")
154 },
155 }
156 }
157}
158
159#[derive(Debug, Clone, Eq, PartialEq, Hash)]
164pub enum TypeExpr {
165 Primitive(Span<Type>),
167 Ptr(PointerType),
169 Array(ArrayType),
171 Struct(StructType),
173 Ref(Ident),
175}
176
177impl From<Type> for TypeExpr {
178 fn from(ty: Type) -> Self {
179 match ty {
180 Type::Array(t) => Self::Array(ArrayType::new(t.element_type().clone().into(), t.len())),
181 Type::Struct(t) => {
182 Self::Struct(StructType::new(t.fields().iter().enumerate().map(|(i, ft)| {
183 let name = Ident::new(format!("field{i}")).unwrap();
184 StructField {
185 span: SourceSpan::UNKNOWN,
186 name,
187 ty: ft.ty.clone().into(),
188 }
189 })))
190 },
191 Type::Ptr(t) => Self::Ptr((*t).clone().into()),
192 Type::Function(_) => {
193 Self::Ptr(PointerType::new(TypeExpr::Primitive(Span::unknown(Type::Felt))))
194 },
195 Type::List(t) => Self::Ptr(
196 PointerType::new((*t).clone().into()).with_address_space(AddressSpace::Byte),
197 ),
198 Type::I128 | Type::U128 => Self::Array(ArrayType::new(Type::U32.into(), 4)),
199 Type::I64 | Type::U64 => Self::Array(ArrayType::new(Type::U32.into(), 2)),
200 Type::Unknown | Type::Never | Type::F64 => panic!("unrepresentable type value: {ty}"),
201 ty => Self::Primitive(Span::unknown(ty)),
202 }
203 }
204}
205
206impl Spanned for TypeExpr {
207 fn span(&self) -> SourceSpan {
208 match self {
209 Self::Primitive(spanned) => spanned.span(),
210 Self::Ptr(spanned) => spanned.span(),
211 Self::Array(spanned) => spanned.span(),
212 Self::Struct(spanned) => spanned.span(),
213 Self::Ref(spanned) => spanned.span(),
214 }
215 }
216}
217
218impl crate::prettier::PrettyPrint for TypeExpr {
219 fn render(&self) -> crate::prettier::Document {
220 use crate::prettier::*;
221
222 match self {
223 Self::Primitive(ty) => display(ty),
224 Self::Ptr(ty) => ty.render(),
225 Self::Array(ty) => ty.render(),
226 Self::Struct(ty) => ty.render(),
227 Self::Ref(ty) => display(ty),
228 }
229 }
230}
231
232#[derive(Debug, Clone)]
236pub struct PointerType {
237 pub span: SourceSpan,
238 pub pointee: Box<TypeExpr>,
239 addrspace: Option<AddressSpace>,
240}
241
242impl From<types::PointerType> for PointerType {
243 fn from(ty: types::PointerType) -> Self {
244 let types::PointerType { addrspace, pointee } = ty;
245 let pointee = Box::new(TypeExpr::from(pointee));
246 Self {
247 span: SourceSpan::UNKNOWN,
248 pointee,
249 addrspace: Some(addrspace),
250 }
251 }
252}
253
254impl Eq for PointerType {}
255
256impl PartialEq for PointerType {
257 fn eq(&self, other: &Self) -> bool {
258 self.address_space() == other.address_space() && self.pointee == other.pointee
259 }
260}
261
262impl core::hash::Hash for PointerType {
263 fn hash<H: core::hash::Hasher>(&self, state: &mut H) {
264 self.pointee.hash(state);
265 self.addrspace.hash(state);
266 }
267}
268
269impl Spanned for PointerType {
270 fn span(&self) -> SourceSpan {
271 self.span
272 }
273}
274
275impl PointerType {
276 pub fn new(pointee: TypeExpr) -> Self {
277 Self {
278 span: SourceSpan::UNKNOWN,
279 pointee: Box::new(pointee),
280 addrspace: None,
281 }
282 }
283
284 #[inline]
286 pub fn with_span(mut self, span: SourceSpan) -> Self {
287 self.span = span;
288 self
289 }
290
291 #[inline]
293 pub fn with_address_space(mut self, addrspace: AddressSpace) -> Self {
294 self.addrspace = Some(addrspace);
295 self
296 }
297
298 #[inline]
300 pub fn address_space(&self) -> AddressSpace {
301 self.addrspace.unwrap_or(AddressSpace::Element)
302 }
303}
304
305impl crate::prettier::PrettyPrint for PointerType {
306 fn render(&self) -> crate::prettier::Document {
307 use crate::prettier::*;
308
309 let doc = const_text("ptr<") + self.pointee.render();
310 if let Some(addrspace) = self.addrspace.as_ref() {
311 doc + const_text(", ") + text(format!("addrspace({})", addrspace)) + const_text(">")
312 } else {
313 doc + const_text(">")
314 }
315 }
316}
317
318#[derive(Debug, Clone)]
322pub struct ArrayType {
323 pub span: SourceSpan,
324 pub elem: Box<TypeExpr>,
325 pub arity: usize,
326}
327
328impl Eq for ArrayType {}
329
330impl PartialEq for ArrayType {
331 fn eq(&self, other: &Self) -> bool {
332 self.arity == other.arity && self.elem == other.elem
333 }
334}
335
336impl core::hash::Hash for ArrayType {
337 fn hash<H: core::hash::Hasher>(&self, state: &mut H) {
338 self.elem.hash(state);
339 self.arity.hash(state);
340 }
341}
342
343impl Spanned for ArrayType {
344 fn span(&self) -> SourceSpan {
345 self.span
346 }
347}
348
349impl ArrayType {
350 pub fn new(elem: TypeExpr, arity: usize) -> Self {
351 Self {
352 span: SourceSpan::UNKNOWN,
353 elem: Box::new(elem),
354 arity,
355 }
356 }
357
358 #[inline]
360 pub fn with_span(mut self, span: SourceSpan) -> Self {
361 self.span = span;
362 self
363 }
364}
365
366impl crate::prettier::PrettyPrint for ArrayType {
367 fn render(&self) -> crate::prettier::Document {
368 use crate::prettier::*;
369
370 const_text("[")
371 + self.elem.render()
372 + const_text("; ")
373 + display(self.arity)
374 + const_text("]")
375 }
376}
377
378#[derive(Debug, Clone)]
382pub struct StructType {
383 pub span: SourceSpan,
384 pub repr: Span<TypeRepr>,
385 pub fields: Vec<StructField>,
386}
387
388impl Eq for StructType {}
389
390impl PartialEq for StructType {
391 fn eq(&self, other: &Self) -> bool {
392 self.repr == other.repr && self.fields == other.fields
393 }
394}
395
396impl core::hash::Hash for StructType {
397 fn hash<H: core::hash::Hasher>(&self, state: &mut H) {
398 self.repr.hash(state);
399 self.fields.hash(state);
400 }
401}
402
403impl Spanned for StructType {
404 fn span(&self) -> SourceSpan {
405 self.span
406 }
407}
408
409impl StructType {
410 pub fn new(fields: impl IntoIterator<Item = StructField>) -> Self {
411 Self {
412 span: SourceSpan::UNKNOWN,
413 repr: Span::unknown(TypeRepr::Default),
414 fields: fields.into_iter().collect(),
415 }
416 }
417
418 #[inline]
420 pub fn with_repr(mut self, repr: Span<TypeRepr>) -> Self {
421 self.repr = repr;
422 self
423 }
424
425 #[inline]
427 pub fn with_span(mut self, span: SourceSpan) -> Self {
428 self.span = span;
429 self
430 }
431}
432
433impl crate::prettier::PrettyPrint for StructType {
434 fn render(&self) -> crate::prettier::Document {
435 use crate::prettier::*;
436
437 let repr = match &*self.repr {
438 TypeRepr::Default => Document::Empty,
439 TypeRepr::BigEndian => const_text("@bigendian "),
440 repr @ (TypeRepr::Align(_) | TypeRepr::Packed(_) | TypeRepr::Transparent) => {
441 text(format!("@{repr} "))
442 },
443 };
444
445 let singleline_body = self
446 .fields
447 .iter()
448 .map(|field| field.render())
449 .reduce(|acc, field| acc + const_text(", ") + field)
450 .unwrap_or(Document::Empty);
451 let multiline_body = indent(
452 4,
453 nl() + self
454 .fields
455 .iter()
456 .map(|field| field.render())
457 .reduce(|acc, field| acc + const_text(",") + nl() + field)
458 .unwrap_or(Document::Empty),
459 ) + nl();
460 let body = singleline_body | multiline_body;
461
462 repr + const_text("struct") + const_text(" { ") + body + const_text(" }")
463 }
464}
465
466#[derive(Debug, Clone)]
470pub struct StructField {
471 pub span: SourceSpan,
472 pub name: Ident,
473 pub ty: TypeExpr,
474}
475
476impl Eq for StructField {}
477
478impl PartialEq for StructField {
479 fn eq(&self, other: &Self) -> bool {
480 self.name == other.name && self.ty == other.ty
481 }
482}
483
484impl core::hash::Hash for StructField {
485 fn hash<H: core::hash::Hasher>(&self, state: &mut H) {
486 self.name.hash(state);
487 self.ty.hash(state);
488 }
489}
490
491impl Spanned for StructField {
492 fn span(&self) -> SourceSpan {
493 self.span
494 }
495}
496
497impl crate::prettier::PrettyPrint for StructField {
498 fn render(&self) -> crate::prettier::Document {
499 use crate::prettier::*;
500
501 display(&self.name) + const_text(": ") + self.ty.render()
502 }
503}
504
505#[derive(Debug, Clone)]
514pub struct TypeAlias {
515 span: SourceSpan,
516 docs: Option<DocString>,
518 pub name: Ident,
520 pub ty: TypeExpr,
522}
523
524impl TypeAlias {
525 pub fn new(name: Ident, ty: TypeExpr) -> Self {
527 Self { span: name.span(), docs: None, name, ty }
528 }
529
530 pub fn with_docs(mut self, docs: Option<Span<String>>) -> Self {
532 self.docs = docs.map(DocString::new);
533 self
534 }
535
536 #[inline]
538 pub fn with_span(mut self, span: SourceSpan) -> Self {
539 self.span = span;
540 self
541 }
542
543 #[inline]
545 pub fn set_span(&mut self, span: SourceSpan) {
546 self.span = span;
547 }
548}
549
550impl Eq for TypeAlias {}
551
552impl PartialEq for TypeAlias {
553 fn eq(&self, other: &Self) -> bool {
554 self.name == other.name && self.docs == other.docs && self.ty == other.ty
555 }
556}
557
558impl core::hash::Hash for TypeAlias {
559 fn hash<H: core::hash::Hasher>(&self, state: &mut H) {
560 let Self { span: _, docs, name, ty } = self;
561 docs.hash(state);
562 name.hash(state);
563 ty.hash(state);
564 }
565}
566
567impl Spanned for TypeAlias {
568 fn span(&self) -> SourceSpan {
569 self.span
570 }
571}
572
573impl crate::prettier::PrettyPrint for TypeAlias {
574 fn render(&self) -> crate::prettier::Document {
575 use crate::prettier::*;
576
577 let doc = self
578 .docs
579 .as_ref()
580 .map(|docstring| docstring.render())
581 .unwrap_or(Document::Empty);
582
583 doc + const_text("type")
584 + const_text(" ")
585 + display(&self.name)
586 + const_text(" = ")
587 + self.ty.render()
588 }
589}
590
591#[derive(Debug, Clone)]
605pub struct EnumType {
606 span: SourceSpan,
607 docs: Option<DocString>,
609 name: Ident,
611 ty: Type,
615 variants: Vec<Variant>,
617}
618
619impl EnumType {
620 pub fn new(name: Ident, ty: Type, variants: impl IntoIterator<Item = Variant>) -> Self {
625 assert!(ty.is_integer(), "only integer types are allowed in enum type definitions");
626 Self {
627 span: name.span(),
628 docs: None,
629 name,
630 ty,
631 variants: Vec::from_iter(variants),
632 }
633 }
634
635 pub fn with_docs(mut self, docs: Option<Span<String>>) -> Self {
637 self.docs = docs.map(DocString::new);
638 self
639 }
640
641 pub fn with_span(mut self, span: SourceSpan) -> Self {
643 self.span = span;
644 self
645 }
646
647 pub fn set_span(&mut self, span: SourceSpan) {
649 self.span = span;
650 }
651
652 pub fn name(&self) -> &Ident {
654 &self.name
655 }
656
657 pub fn ty(&self) -> &Type {
659 &self.ty
660 }
661
662 pub fn variants(&self) -> &[Variant] {
664 &self.variants
665 }
666
667 pub fn variants_mut(&mut self) -> &mut Vec<Variant> {
669 &mut self.variants
670 }
671
672 pub fn into_parts(self) -> (TypeAlias, Vec<Variant>) {
674 let Self { span, docs, name, ty, variants } = self;
675 let alias = TypeAlias {
676 span,
677 docs,
678 name,
679 ty: TypeExpr::Primitive(Span::new(span, ty)),
680 };
681 (alias, variants)
682 }
683}
684
685impl Spanned for EnumType {
686 fn span(&self) -> SourceSpan {
687 self.span
688 }
689}
690
691impl Eq for EnumType {}
692
693impl PartialEq for EnumType {
694 fn eq(&self, other: &Self) -> bool {
695 self.name == other.name
696 && self.docs == other.docs
697 && self.ty == other.ty
698 && self.variants == other.variants
699 }
700}
701
702impl core::hash::Hash for EnumType {
703 fn hash<H: core::hash::Hasher>(&self, state: &mut H) {
704 let Self { span: _, docs, name, ty, variants } = self;
705 docs.hash(state);
706 name.hash(state);
707 ty.hash(state);
708 variants.hash(state);
709 }
710}
711
712impl crate::prettier::PrettyPrint for EnumType {
713 fn render(&self) -> crate::prettier::Document {
714 use crate::prettier::*;
715
716 let doc = self
717 .docs
718 .as_ref()
719 .map(|docstring| docstring.render())
720 .unwrap_or(Document::Empty);
721
722 let variants = self
723 .variants
724 .iter()
725 .map(|v| v.render())
726 .reduce(|acc, v| acc + const_text(",") + nl() + v)
727 .unwrap_or(Document::Empty);
728
729 doc + const_text("enum")
730 + const_text(" ")
731 + display(&self.name)
732 + const_text(" : ")
733 + self.ty.render()
734 + const_text(" {")
735 + nl()
736 + variants
737 + const_text("}")
738 }
739}
740
741#[derive(Debug, Clone)]
748pub struct Variant {
749 pub span: SourceSpan,
750 pub docs: Option<DocString>,
752 pub name: Ident,
754 pub discriminant: ConstantExpr,
756}
757
758impl Variant {
759 pub fn new(name: Ident, discriminant: ConstantExpr) -> Self {
761 Self {
762 span: name.span(),
763 docs: None,
764 name,
765 discriminant,
766 }
767 }
768
769 pub fn with_span(mut self, span: SourceSpan) -> Self {
771 self.span = span;
772 self
773 }
774
775 pub fn with_docs(mut self, docs: Option<Span<String>>) -> Self {
777 self.docs = docs.map(DocString::new);
778 self
779 }
780
781 pub fn assert_instance_of(&self, ty: &Type) -> Result<(), crate::SemanticAnalysisError> {
789 use crate::{FIELD_MODULUS, SemanticAnalysisError};
790
791 let value = match &self.discriminant {
792 ConstantExpr::Int(value) => value.as_int(),
793 _ => {
794 return Err(SemanticAnalysisError::InvalidEnumDiscriminant {
795 span: self.discriminant.span(),
796 repr: ty.clone(),
797 });
798 },
799 };
800
801 match ty {
802 Type::I1 if value > 1 => Err(SemanticAnalysisError::InvalidEnumDiscriminant {
803 span: self.discriminant.span(),
804 repr: ty.clone(),
805 }),
806 Type::I1 => Ok(()),
807 Type::I8 | Type::U8 if value > u8::MAX as u64 => {
808 Err(SemanticAnalysisError::InvalidEnumDiscriminant {
809 span: self.discriminant.span(),
810 repr: ty.clone(),
811 })
812 },
813 Type::I8 | Type::U8 => Ok(()),
814 Type::I16 | Type::U16 if value > u16::MAX as u64 => {
815 Err(SemanticAnalysisError::InvalidEnumDiscriminant {
816 span: self.discriminant.span(),
817 repr: ty.clone(),
818 })
819 },
820 Type::I16 | Type::U16 => Ok(()),
821 Type::I32 | Type::U32 if value > u32::MAX as u64 => {
822 Err(SemanticAnalysisError::InvalidEnumDiscriminant {
823 span: self.discriminant.span(),
824 repr: ty.clone(),
825 })
826 },
827 Type::I32 | Type::U32 => Ok(()),
828 Type::I64 | Type::U64 if value >= FIELD_MODULUS => {
829 Err(SemanticAnalysisError::InvalidEnumDiscriminant {
830 span: self.discriminant.span(),
831 repr: ty.clone(),
832 })
833 },
834 _ => Err(SemanticAnalysisError::InvalidEnumRepr { span: self.span }),
835 }
836 }
837}
838
839impl Spanned for Variant {
840 fn span(&self) -> SourceSpan {
841 self.span
842 }
843}
844
845impl Eq for Variant {}
846
847impl PartialEq for Variant {
848 fn eq(&self, other: &Self) -> bool {
849 self.name == other.name
850 && self.discriminant == other.discriminant
851 && self.docs == other.docs
852 }
853}
854
855impl core::hash::Hash for Variant {
856 fn hash<H: core::hash::Hasher>(&self, state: &mut H) {
857 let Self { span: _, docs, name, discriminant } = self;
858 docs.hash(state);
859 name.hash(state);
860 discriminant.hash(state);
861 }
862}
863
864impl crate::prettier::PrettyPrint for Variant {
865 fn render(&self) -> crate::prettier::Document {
866 use crate::prettier::*;
867
868 let doc = self
869 .docs
870 .as_ref()
871 .map(|docstring| docstring.render())
872 .unwrap_or(Document::Empty);
873
874 doc + display(&self.name) + const_text(" = ") + self.discriminant.render()
875 }
876}