1use alloc::{boxed::Box, string::String, sync::Arc, vec::Vec};
2
3use miden_debug_types::{SourceManager, SourceSpan, Span, Spanned};
4use midenc_hir_type::{AddressSpace, Type, TypeRepr, TypeTemplate};
5
6use super::{
7 ConstantExpr, DocString, GlobalItemIndex, Ident, ItemIndex, Path, SymbolResolution,
8 SymbolResolutionError, Visibility, types,
9};
10
11const MAX_TYPE_EXPR_NESTING: usize = 256;
16
17pub trait TypeResolver<E> {
31 fn source_manager(&self) -> Arc<dyn SourceManager>;
32 fn resolve_local_failed(&self, err: SymbolResolutionError) -> E;
35 fn get_type(
42 &mut self,
43 context: SourceSpan,
44 gid: GlobalItemIndex,
45 ) -> Result<Option<TypeTemplate>, E>;
46 fn get_local_type(
48 &mut self,
49 context: SourceSpan,
50 id: ItemIndex,
51 ) -> Result<Option<TypeTemplate>, E>;
52 fn resolve_type_ref(&mut self, ty: Span<&Path>) -> Result<SymbolResolution, E>;
54 fn finalize(&mut self, context: SourceSpan, template: TypeTemplate) -> Result<Type, E>;
56 fn resolve(&mut self, ty: &TypeExpr) -> Result<Option<Type>, E> {
58 match ty.resolve_template(self)? {
59 Some(template) => self.finalize(ty.span(), template).map(Some),
60 None => Ok(None),
61 }
62 }
63}
64
65#[derive(Debug, Clone, PartialEq, Eq)]
70pub enum TypeDecl {
71 Alias(TypeAlias),
73 Enum(EnumType),
75}
76
77impl TypeDecl {
78 pub fn with_docs(self, docs: Option<Span<String>>) -> Self {
80 match self {
81 Self::Alias(ty) => Self::Alias(ty.with_docs(docs)),
82 Self::Enum(ty) => Self::Enum(ty.with_docs(docs)),
83 }
84 }
85
86 pub fn name(&self) -> &Ident {
88 match self {
89 Self::Alias(ty) => &ty.name,
90 Self::Enum(ty) => &ty.name,
91 }
92 }
93
94 pub const fn visibility(&self) -> Visibility {
96 match self {
97 Self::Alias(ty) => ty.visibility,
98 Self::Enum(ty) => ty.visibility,
99 }
100 }
101
102 pub fn docs(&self) -> Option<Span<&str>> {
104 match self {
105 Self::Alias(ty) => ty.docs(),
106 Self::Enum(ty) => ty.docs(),
107 }
108 }
109
110 pub fn ty(&self) -> TypeExpr {
112 match self {
113 Self::Alias(ty) => ty.ty.clone(),
114 Self::Enum(ty) => TypeExpr::Primitive(Span::new(ty.span, ty.ty.clone())),
115 }
116 }
117}
118
119impl Spanned for TypeDecl {
120 fn span(&self) -> SourceSpan {
121 match self {
122 Self::Alias(spanned) => spanned.span,
123 Self::Enum(spanned) => spanned.span,
124 }
125 }
126}
127
128impl From<TypeAlias> for TypeDecl {
129 fn from(value: TypeAlias) -> Self {
130 Self::Alias(value)
131 }
132}
133
134impl From<EnumType> for TypeDecl {
135 fn from(value: EnumType) -> Self {
136 Self::Enum(value)
137 }
138}
139
140impl crate::prettier::PrettyPrint for TypeDecl {
141 fn render(&self) -> crate::prettier::Document {
142 match self {
143 Self::Alias(ty) => ty.render(),
144 Self::Enum(ty) => ty.render(),
145 }
146 }
147}
148
149#[derive(Debug, Clone)]
154pub struct FunctionType {
155 pub span: SourceSpan,
156 pub cc: types::CallConv,
157 pub args: Vec<TypeExpr>,
158 pub results: Vec<TypeExpr>,
159}
160
161impl Eq for FunctionType {}
162
163impl PartialEq for FunctionType {
164 fn eq(&self, other: &Self) -> bool {
165 self.cc == other.cc && self.args == other.args && self.results == other.results
166 }
167}
168
169impl core::hash::Hash for FunctionType {
170 fn hash<H: core::hash::Hasher>(&self, state: &mut H) {
171 self.cc.hash(state);
172 self.args.hash(state);
173 self.results.hash(state);
174 }
175}
176
177impl Spanned for FunctionType {
178 fn span(&self) -> SourceSpan {
179 self.span
180 }
181}
182
183impl FunctionType {
184 pub fn new(cc: types::CallConv, args: Vec<TypeExpr>, results: Vec<TypeExpr>) -> Self {
185 Self {
186 span: SourceSpan::UNKNOWN,
187 cc,
188 args,
189 results,
190 }
191 }
192
193 #[inline]
195 pub fn with_span(mut self, span: SourceSpan) -> Self {
196 self.span = span;
197 self
198 }
199}
200
201impl crate::prettier::PrettyPrint for FunctionType {
202 fn render(&self) -> crate::prettier::Document {
203 use crate::prettier::*;
204
205 let singleline_args = self
206 .args
207 .iter()
208 .map(PrettyPrint::render)
209 .reduce(|acc, arg| acc + const_text(", ") + arg)
210 .unwrap_or(Document::Empty);
211 let multiline_args = indent(
212 4,
213 nl() + self
214 .args
215 .iter()
216 .map(PrettyPrint::render)
217 .reduce(|acc, arg| acc + const_text(",") + nl() + arg)
218 .unwrap_or(Document::Empty),
219 ) + nl();
220 let args = singleline_args | multiline_args;
221 let args = const_text("(") + args + const_text(")");
222
223 match self.results.len() {
224 0 => args,
225 1 => args + const_text(" -> ") + self.results[0].render(),
226 _ => {
227 let results = self
228 .results
229 .iter()
230 .map(PrettyPrint::render)
231 .reduce(|acc, r| acc + const_text(", ") + r)
232 .unwrap_or(Document::Empty);
233 args + const_text(" -> ") + const_text("(") + results + const_text(")")
234 },
235 }
236 }
237}
238
239#[derive(Debug, Clone, Eq, PartialEq, Hash)]
244pub enum TypeExpr {
245 Primitive(Span<Type>),
247 Ptr(PointerType),
249 Array(ArrayType),
251 Struct(StructType),
253 Ref(Span<Arc<Path>>),
255}
256
257impl TypeExpr {
258 pub fn set_name(&mut self, name: Ident) {
263 match self {
264 Self::Struct(struct_ty) => {
265 struct_ty.name = Some(name);
266 },
267 Self::Primitive(_) | Self::Ptr(_) | Self::Array(_) | Self::Ref(_) => (),
268 }
269 }
270
271 pub fn references(&self) -> Vec<Span<Arc<Path>>> {
273 use alloc::collections::BTreeSet;
274
275 let mut worklist = smallvec::SmallVec::<[_; 4]>::from_slice(&[self]);
276 let mut references = BTreeSet::new();
277
278 while let Some(ty) = worklist.pop() {
279 match ty {
280 Self::Primitive(_) => {},
281 Self::Ptr(ty) => {
282 worklist.push(&ty.pointee);
283 },
284 Self::Array(ty) => {
285 worklist.push(&ty.elem);
286 },
287 Self::Struct(ty) => {
288 for field in ty.fields.iter() {
289 worklist.push(&field.ty);
290 }
291 },
292 Self::Ref(ty) => {
293 references.insert(ty.clone());
294 },
295 }
296 }
297
298 references.into_iter().collect()
299 }
300
301 pub fn resolve_template<E, R>(&self, resolver: &mut R) -> Result<Option<TypeTemplate>, E>
305 where
306 R: ?Sized + TypeResolver<E>,
307 {
308 self.resolve_template_with_depth(resolver, 0)
309 }
310
311 fn resolve_template_with_depth<E, R>(
312 &self,
313 resolver: &mut R,
314 depth: usize,
315 ) -> Result<Option<TypeTemplate>, E>
316 where
317 R: ?Sized + TypeResolver<E>,
318 {
319 if depth > MAX_TYPE_EXPR_NESTING {
320 let source_manager = resolver.source_manager();
321 return Err(resolver.resolve_local_failed(
322 SymbolResolutionError::type_expression_depth_exceeded(
323 self.span(),
324 MAX_TYPE_EXPR_NESTING,
325 source_manager.as_ref(),
326 ),
327 ));
328 }
329
330 match self {
331 TypeExpr::Ref(path) => {
332 let mut current_path = path.clone();
333 loop {
334 match resolver.resolve_type_ref(current_path.as_deref())? {
335 SymbolResolution::Local(item) => {
336 return resolver.get_local_type(current_path.span(), item.into_inner());
337 },
338 SymbolResolution::External(path) => {
339 if path == current_path {
341 break Ok(None);
342 }
343 current_path = path;
344 },
345 SymbolResolution::Exact { gid, .. } => {
346 return resolver.get_type(current_path.span(), gid);
347 },
348 SymbolResolution::Module { path: module_path, .. } => {
349 break Err(resolver.resolve_local_failed(
350 SymbolResolutionError::invalid_symbol_type(
351 path.span(),
352 "type",
353 module_path.span(),
354 &resolver.source_manager(),
355 ),
356 ));
357 },
358 SymbolResolution::MastRoot(item) => {
359 break Err(resolver.resolve_local_failed(
360 SymbolResolutionError::invalid_symbol_type(
361 path.span(),
362 "type",
363 item.span(),
364 &resolver.source_manager(),
365 ),
366 ));
367 },
368 }
369 }
370 },
371 TypeExpr::Primitive(t) => Ok(Some(TypeTemplate::Type(t.inner().clone()))),
372 TypeExpr::Array(t) => Ok(t
373 .elem
374 .resolve_template_with_depth(resolver, depth + 1)?
375 .map(|elem| TypeTemplate::array(elem, t.arity))),
376 TypeExpr::Ptr(ty) => Ok(ty
377 .pointee
378 .resolve_template_with_depth(resolver, depth + 1)?
379 .map(TypeTemplate::ptr)),
380 TypeExpr::Struct(t) => {
381 let mut fields = Vec::with_capacity(t.fields.len());
382 for field in t.fields.iter() {
383 let field_ty = field.ty.resolve_template_with_depth(resolver, depth + 1)?;
384 if let Some(field_ty) = field_ty {
385 fields.push(types::FieldTemplate {
386 name: Some(field.name.clone().into_inner()),
387 ty: field_ty,
388 });
389 } else {
390 return Ok(None);
391 }
392 }
393 Ok(Some(TypeTemplate::Struct(Box::new(types::StructTemplate {
394 name: t.name.clone().map(Ident::into_inner),
395 repr: t.repr.into_inner(),
396 fields,
397 }))))
398 },
399 }
400 }
401}
402
403impl From<Type> for TypeExpr {
404 fn from(ty: Type) -> Self {
405 let mut expanding = Vec::new();
406 type_expr_from(ty, &mut expanding)
407 }
408}
409
410fn type_expr_from(ty: Type, expanding: &mut Vec<types::RecTypeRef>) -> TypeExpr {
417 match ty {
418 Type::Array(t) => TypeExpr::Array(ArrayType::new(
419 type_expr_from(t.element_type().clone(), expanding),
420 t.len(),
421 )),
422 Type::Struct(t) => {
423 let name = t.name().and_then(|name| Ident::new(name.as_ref()).ok());
424
425 if let Some(rec) = t.as_recursive() {
428 if expanding.contains(rec) {
429 let name = name.unwrap_or_else(|| {
430 panic!(
431 "unrepresentable type value: a recursive struct without a name cannot \
432 be referred to as a type expression"
433 )
434 });
435 return TypeExpr::Ref(Span::unknown(
436 Path::from_ident(&name).into_owned().into(),
437 ));
438 }
439 expanding.push(rec.clone());
440 }
441
442 let is_recursive = t.is_recursive();
443 let body = t.get();
444 let fields = body
445 .fields()
446 .iter()
447 .enumerate()
448 .map(|(i, ft)| {
449 let name = ft
450 .name
451 .as_deref()
452 .map(Ident::new)
453 .and_then(Result::ok)
454 .unwrap_or_else(|| Ident::new(format!("field{i}")).unwrap());
455 StructField {
456 span: SourceSpan::UNKNOWN,
457 name,
458 ty: type_expr_from(ft.ty.clone(), expanding),
459 }
460 })
461 .collect::<Vec<_>>();
462 let converted = TypeExpr::Struct(
463 StructType::new(name, fields)
464 .with_repr(Span::unknown(body.repr()))
465 .with_span(SourceSpan::UNKNOWN),
466 );
467
468 if is_recursive {
469 expanding.pop();
470 }
471 converted
472 },
473 Type::Ptr(t) => TypeExpr::Ptr(
474 PointerType::new(type_expr_from(t.pointee().clone(), expanding))
475 .with_address_space(t.addrspace()),
476 ),
477 Type::Function(_) => {
478 TypeExpr::Ptr(PointerType::new(TypeExpr::Primitive(Span::unknown(Type::Felt))))
479 },
480 Type::List(t) => TypeExpr::Ptr(
481 PointerType::new(type_expr_from((*t).clone(), expanding))
482 .with_address_space(AddressSpace::Byte),
483 ),
484 Type::Unknown | Type::Never | Type::F64 => {
485 panic!("unrepresentable type value: {ty}")
486 },
487 ty => TypeExpr::Primitive(Span::unknown(ty)),
488 }
489}
490
491impl Spanned for TypeExpr {
492 fn span(&self) -> SourceSpan {
493 match self {
494 Self::Primitive(spanned) => spanned.span(),
495 Self::Ptr(spanned) => spanned.span(),
496 Self::Array(spanned) => spanned.span(),
497 Self::Struct(spanned) => spanned.span(),
498 Self::Ref(spanned) => spanned.span(),
499 }
500 }
501}
502
503impl crate::prettier::PrettyPrint for TypeExpr {
504 fn render(&self) -> crate::prettier::Document {
505 use crate::prettier::*;
506
507 match self {
508 Self::Primitive(ty) => display(ty),
509 Self::Ptr(ty) => ty.render(),
510 Self::Array(ty) => ty.render(),
511 Self::Struct(ty) => ty.render(),
512 Self::Ref(ty) => display(ty),
513 }
514 }
515}
516
517#[derive(Debug, Clone)]
521pub struct PointerType {
522 pub span: SourceSpan,
523 pub pointee: Box<TypeExpr>,
524 addrspace: Option<AddressSpace>,
525}
526
527impl From<types::PointerType> for PointerType {
528 fn from(ty: types::PointerType) -> Self {
529 let types::PointerType { addrspace, pointee } = ty;
530 let pointee = Box::new(TypeExpr::from(pointee));
531 Self {
532 span: SourceSpan::UNKNOWN,
533 pointee,
534 addrspace: Some(addrspace),
535 }
536 }
537}
538
539impl Eq for PointerType {}
540
541impl PartialEq for PointerType {
542 fn eq(&self, other: &Self) -> bool {
543 self.address_space() == other.address_space() && self.pointee == other.pointee
544 }
545}
546
547impl core::hash::Hash for PointerType {
548 fn hash<H: core::hash::Hasher>(&self, state: &mut H) {
549 self.pointee.hash(state);
550 self.address_space().hash(state);
551 }
552}
553
554impl Spanned for PointerType {
555 fn span(&self) -> SourceSpan {
556 self.span
557 }
558}
559
560impl PointerType {
561 pub fn new(pointee: TypeExpr) -> Self {
562 Self {
563 span: SourceSpan::UNKNOWN,
564 pointee: Box::new(pointee),
565 addrspace: None,
566 }
567 }
568
569 #[inline]
571 pub fn with_span(mut self, span: SourceSpan) -> Self {
572 self.span = span;
573 self
574 }
575
576 #[inline]
578 pub fn with_address_space(mut self, addrspace: AddressSpace) -> Self {
579 self.addrspace = Some(addrspace);
580 self
581 }
582
583 #[inline]
585 pub fn address_space(&self) -> AddressSpace {
586 self.addrspace.unwrap_or(AddressSpace::Element)
587 }
588}
589
590impl crate::prettier::PrettyPrint for PointerType {
591 fn render(&self) -> crate::prettier::Document {
592 use crate::prettier::*;
593
594 let doc = const_text("ptr<") + self.pointee.render();
595 if let Some(addrspace) = self.addrspace.as_ref() {
596 doc + const_text(", ") + text(format!("addrspace({addrspace})")) + const_text(">")
597 } else {
598 doc + const_text(">")
599 }
600 }
601}
602
603#[derive(Debug, Clone)]
607pub struct ArrayType {
608 pub span: SourceSpan,
609 pub elem: Box<TypeExpr>,
610 pub arity: usize,
611}
612
613impl Eq for ArrayType {}
614
615impl PartialEq for ArrayType {
616 fn eq(&self, other: &Self) -> bool {
617 self.arity == other.arity && self.elem == other.elem
618 }
619}
620
621impl core::hash::Hash for ArrayType {
622 fn hash<H: core::hash::Hasher>(&self, state: &mut H) {
623 self.elem.hash(state);
624 self.arity.hash(state);
625 }
626}
627
628impl Spanned for ArrayType {
629 fn span(&self) -> SourceSpan {
630 self.span
631 }
632}
633
634impl ArrayType {
635 pub fn new(elem: TypeExpr, arity: usize) -> Self {
636 Self {
637 span: SourceSpan::UNKNOWN,
638 elem: Box::new(elem),
639 arity,
640 }
641 }
642
643 #[inline]
645 pub fn with_span(mut self, span: SourceSpan) -> Self {
646 self.span = span;
647 self
648 }
649}
650
651impl crate::prettier::PrettyPrint for ArrayType {
652 fn render(&self) -> crate::prettier::Document {
653 use crate::prettier::*;
654
655 const_text("[")
656 + self.elem.render()
657 + const_text("; ")
658 + display(self.arity)
659 + const_text("]")
660 }
661}
662
663#[derive(Debug, Clone)]
667pub struct StructType {
668 pub span: SourceSpan,
669 pub name: Option<Ident>,
670 pub repr: Span<TypeRepr>,
671 pub fields: Vec<StructField>,
672}
673
674impl Eq for StructType {}
675
676impl PartialEq for StructType {
677 fn eq(&self, other: &Self) -> bool {
678 self.name == other.name && self.repr == other.repr && self.fields == other.fields
679 }
680}
681
682impl core::hash::Hash for StructType {
683 fn hash<H: core::hash::Hasher>(&self, state: &mut H) {
684 self.name.hash(state);
685 self.repr.hash(state);
686 self.fields.hash(state);
687 }
688}
689
690impl Spanned for StructType {
691 fn span(&self) -> SourceSpan {
692 self.span
693 }
694}
695
696impl StructType {
697 pub fn new(name: Option<Ident>, fields: impl IntoIterator<Item = StructField>) -> Self {
698 Self {
699 span: SourceSpan::UNKNOWN,
700 name,
701 repr: Span::unknown(TypeRepr::Default),
702 fields: fields.into_iter().collect(),
703 }
704 }
705
706 #[inline]
708 pub fn with_repr(mut self, repr: Span<TypeRepr>) -> Self {
709 self.repr = repr;
710 self
711 }
712
713 #[inline]
715 pub fn with_span(mut self, span: SourceSpan) -> Self {
716 self.span = span;
717 self
718 }
719}
720
721impl crate::prettier::PrettyPrint for StructType {
722 fn render(&self) -> crate::prettier::Document {
723 use crate::prettier::*;
724
725 let repr = match &*self.repr {
726 TypeRepr::Default => Document::Empty,
727 repr @ (TypeRepr::Align(_) | TypeRepr::Packed(_) | TypeRepr::Transparent) => {
728 text(format!(" @{repr}"))
729 },
730 };
731
732 let singleline_body = self
733 .fields
734 .iter()
735 .map(PrettyPrint::render)
736 .reduce(|acc, field| acc + const_text(", ") + field)
737 .unwrap_or(Document::Empty);
738 let multiline_body = indent(
739 4,
740 nl() + self
741 .fields
742 .iter()
743 .map(PrettyPrint::render)
744 .reduce(|acc, field| acc + const_text(",") + nl() + field)
745 .unwrap_or(Document::Empty),
746 ) + nl();
747 let body = singleline_body | multiline_body;
748
749 const_text("struct") + repr + const_text(" { ") + body + const_text(" }")
750 }
751}
752
753#[derive(Debug, Clone)]
757pub struct StructField {
758 pub span: SourceSpan,
759 pub name: Ident,
760 pub ty: TypeExpr,
761}
762
763impl Eq for StructField {}
764
765impl PartialEq for StructField {
766 fn eq(&self, other: &Self) -> bool {
767 self.name == other.name && self.ty == other.ty
768 }
769}
770
771impl core::hash::Hash for StructField {
772 fn hash<H: core::hash::Hasher>(&self, state: &mut H) {
773 self.name.hash(state);
774 self.ty.hash(state);
775 }
776}
777
778impl Spanned for StructField {
779 fn span(&self) -> SourceSpan {
780 self.span
781 }
782}
783
784impl crate::prettier::PrettyPrint for StructField {
785 fn render(&self) -> crate::prettier::Document {
786 use crate::prettier::*;
787
788 display(&self.name) + const_text(": ") + self.ty.render()
789 }
790}
791
792#[derive(Debug, Clone)]
801pub struct TypeAlias {
802 span: SourceSpan,
803 docs: Option<DocString>,
805 pub visibility: Visibility,
807 pub name: Ident,
809 pub ty: TypeExpr,
811}
812
813impl TypeAlias {
814 pub fn new(visibility: Visibility, name: Ident, ty: TypeExpr) -> Self {
816 Self {
817 span: name.span(),
818 docs: None,
819 visibility,
820 name,
821 ty,
822 }
823 }
824
825 pub fn with_docs(mut self, docs: Option<Span<String>>) -> Self {
827 self.docs = docs.map(DocString::new);
828 self
829 }
830
831 #[inline]
833 pub fn with_span(mut self, span: SourceSpan) -> Self {
834 self.span = span;
835 self
836 }
837
838 #[inline]
840 pub fn set_span(&mut self, span: SourceSpan) {
841 self.span = span;
842 }
843
844 pub fn docs(&self) -> Option<Span<&str>> {
846 self.docs.as_ref().map(|docstring| docstring.as_spanned_str())
847 }
848
849 pub fn name(&self) -> &Ident {
851 &self.name
852 }
853
854 #[inline]
856 pub const fn visibility(&self) -> Visibility {
857 self.visibility
858 }
859}
860
861impl Eq for TypeAlias {}
862
863impl PartialEq for TypeAlias {
864 fn eq(&self, other: &Self) -> bool {
865 self.visibility == other.visibility
866 && self.name == other.name
867 && self.docs == other.docs
868 && self.ty == other.ty
869 }
870}
871
872impl core::hash::Hash for TypeAlias {
873 fn hash<H: core::hash::Hasher>(&self, state: &mut H) {
874 let Self { span: _, docs, visibility, name, ty } = self;
875 docs.hash(state);
876 visibility.hash(state);
877 name.hash(state);
878 ty.hash(state);
879 }
880}
881
882impl Spanned for TypeAlias {
883 fn span(&self) -> SourceSpan {
884 self.span
885 }
886}
887
888impl crate::prettier::PrettyPrint for TypeAlias {
889 fn render(&self) -> crate::prettier::Document {
890 use crate::prettier::*;
891
892 let mut doc = self.docs.as_ref().map(PrettyPrint::render).unwrap_or(Document::Empty);
893
894 if self.visibility.is_public() {
895 doc += display(self.visibility) + const_text(" ");
896 }
897
898 doc + const_text("type")
899 + const_text(" ")
900 + display(&self.name)
901 + const_text(" = ")
902 + self.ty.render()
903 }
904}
905
906#[derive(Debug, Clone)]
920pub struct EnumType {
921 span: SourceSpan,
922 docs: Option<DocString>,
924 visibility: Visibility,
926 name: Ident,
928 ty: Type,
932 variants: Vec<Variant>,
934}
935
936impl EnumType {
937 pub fn new(
942 visibility: Visibility,
943 name: Ident,
944 ty: Type,
945 variants: impl IntoIterator<Item = Variant>,
946 ) -> Self {
947 assert!(ty.is_integer(), "only integer types are allowed in enum type definitions");
948 Self {
949 span: name.span(),
950 docs: None,
951 visibility,
952 name,
953 ty,
954 variants: Vec::from_iter(variants),
955 }
956 }
957
958 pub fn with_docs(mut self, docs: Option<Span<String>>) -> Self {
960 self.docs = docs.map(DocString::new);
961 self
962 }
963
964 pub fn with_span(mut self, span: SourceSpan) -> Self {
966 self.span = span;
967 self
968 }
969
970 pub fn is_c_like(&self) -> bool {
972 !self.variants.is_empty() && self.variants.iter().all(|v| v.value_ty.is_none())
973 }
974
975 pub fn set_span(&mut self, span: SourceSpan) {
977 self.span = span;
978 }
979
980 pub fn name(&self) -> &Ident {
982 &self.name
983 }
984
985 pub const fn visibility(&self) -> Visibility {
987 self.visibility
988 }
989
990 pub fn docs(&self) -> Option<Span<&str>> {
992 self.docs.as_ref().map(|docstring| docstring.as_spanned_str())
993 }
994
995 pub fn ty(&self) -> &Type {
997 &self.ty
998 }
999
1000 pub fn variants(&self) -> &[Variant] {
1002 &self.variants
1003 }
1004
1005 pub fn variants_mut(&mut self) -> &mut Vec<Variant> {
1007 &mut self.variants
1008 }
1009
1010 pub fn into_parts(self) -> (TypeAlias, Vec<Variant>) {
1012 let Self {
1013 span,
1014 docs,
1015 visibility,
1016 name,
1017 ty,
1018 variants,
1019 } = self;
1020 let alias = TypeAlias {
1021 span,
1022 docs,
1023 visibility,
1024 name,
1025 ty: TypeExpr::Primitive(Span::new(span, ty)),
1026 };
1027 (alias, variants)
1028 }
1029}
1030
1031impl Spanned for EnumType {
1032 fn span(&self) -> SourceSpan {
1033 self.span
1034 }
1035}
1036
1037impl Eq for EnumType {}
1038
1039impl PartialEq for EnumType {
1040 fn eq(&self, other: &Self) -> bool {
1041 self.visibility == other.visibility
1042 && self.name == other.name
1043 && self.docs == other.docs
1044 && self.ty == other.ty
1045 && self.variants == other.variants
1046 }
1047}
1048
1049impl core::hash::Hash for EnumType {
1050 fn hash<H: core::hash::Hasher>(&self, state: &mut H) {
1051 let Self {
1052 span: _,
1053 docs,
1054 visibility,
1055 name,
1056 ty,
1057 variants,
1058 } = self;
1059 docs.hash(state);
1060 visibility.hash(state);
1061 name.hash(state);
1062 ty.hash(state);
1063 variants.hash(state);
1064 }
1065}
1066
1067impl crate::prettier::PrettyPrint for EnumType {
1068 fn render(&self) -> crate::prettier::Document {
1069 use crate::prettier::*;
1070
1071 let mut doc = self.docs.as_ref().map(PrettyPrint::render).unwrap_or(Document::Empty);
1072
1073 let variants = self
1074 .variants
1075 .iter()
1076 .map(PrettyPrint::render)
1077 .reduce(|acc, v| acc + const_text(",") + nl() + v)
1078 .unwrap_or(Document::Empty);
1079
1080 if self.visibility.is_public() {
1081 doc += display(self.visibility) + const_text(" ");
1082 }
1083
1084 doc + const_text("enum")
1085 + const_text(" ")
1086 + display(&self.name)
1087 + const_text(" : ")
1088 + self.ty.render()
1089 + const_text(" {")
1090 + nl()
1091 + variants
1092 + const_text("}")
1093 }
1094}
1095
1096#[derive(Debug, Clone)]
1103pub struct Variant {
1104 pub span: SourceSpan,
1105 pub docs: Option<DocString>,
1107 pub name: Ident,
1109 pub value_ty: Option<TypeExpr>,
1114 pub discriminant: ConstantExpr,
1116}
1117
1118impl Variant {
1119 pub fn new(name: Ident, discriminant: ConstantExpr, payload: Option<TypeExpr>) -> Self {
1121 Self {
1122 span: name.span(),
1123 docs: None,
1124 name,
1125 value_ty: payload,
1126 discriminant,
1127 }
1128 }
1129
1130 pub fn with_span(mut self, span: SourceSpan) -> Self {
1132 self.span = span;
1133 self
1134 }
1135
1136 pub fn with_docs(mut self, docs: Option<Span<String>>) -> Self {
1138 self.docs = docs.map(DocString::new);
1139 self
1140 }
1141
1142 pub fn assert_instance_of(&self, ty: &Type) -> Result<(), crate::SemanticAnalysisError> {
1150 use crate::{FIELD_MODULUS, SemanticAnalysisError};
1151
1152 let value = match &self.discriminant {
1153 ConstantExpr::Int(value) => value.as_int(),
1154 _ => {
1155 return Err(SemanticAnalysisError::InvalidEnumDiscriminant {
1156 span: self.discriminant.span(),
1157 repr: ty.clone(),
1158 });
1159 },
1160 };
1161
1162 match ty {
1163 Type::Felt if value >= FIELD_MODULUS => {
1164 Err(SemanticAnalysisError::InvalidEnumDiscriminant {
1165 span: self.discriminant.span(),
1166 repr: ty.clone(),
1167 })
1168 },
1169 Type::Felt => Ok(()),
1172 Type::I1 if value > 1 => Err(SemanticAnalysisError::InvalidEnumDiscriminant {
1173 span: self.discriminant.span(),
1174 repr: ty.clone(),
1175 }),
1176 Type::I1 => Ok(()),
1177 Type::I8 | Type::U8 if value > u8::MAX as u64 => {
1178 Err(SemanticAnalysisError::InvalidEnumDiscriminant {
1179 span: self.discriminant.span(),
1180 repr: ty.clone(),
1181 })
1182 },
1183 Type::I8 | Type::U8 => Ok(()),
1184 Type::I16 | Type::U16 if value > u16::MAX as u64 => {
1185 Err(SemanticAnalysisError::InvalidEnumDiscriminant {
1186 span: self.discriminant.span(),
1187 repr: ty.clone(),
1188 })
1189 },
1190 Type::I16 | Type::U16 => Ok(()),
1191 Type::I32 | Type::U32 if value > u32::MAX as u64 => {
1192 Err(SemanticAnalysisError::InvalidEnumDiscriminant {
1193 span: self.discriminant.span(),
1194 repr: ty.clone(),
1195 })
1196 },
1197 Type::I32 | Type::U32 => Ok(()),
1198 Type::I64 | Type::U64 if value >= FIELD_MODULUS => {
1199 Err(SemanticAnalysisError::InvalidEnumDiscriminant {
1200 span: self.discriminant.span(),
1201 repr: ty.clone(),
1202 })
1203 },
1204 _ => Err(SemanticAnalysisError::InvalidEnumRepr { span: self.span }),
1205 }
1206 }
1207}
1208
1209impl Spanned for Variant {
1210 fn span(&self) -> SourceSpan {
1211 self.span
1212 }
1213}
1214
1215impl Eq for Variant {}
1216
1217impl PartialEq for Variant {
1218 fn eq(&self, other: &Self) -> bool {
1219 self.name == other.name
1220 && self.value_ty == other.value_ty
1221 && self.discriminant == other.discriminant
1222 && self.docs == other.docs
1223 }
1224}
1225
1226impl core::hash::Hash for Variant {
1227 fn hash<H: core::hash::Hasher>(&self, state: &mut H) {
1228 let Self {
1229 span: _,
1230 docs,
1231 name,
1232 value_ty,
1233 discriminant,
1234 } = self;
1235 docs.hash(state);
1236 name.hash(state);
1237 value_ty.hash(state);
1238 discriminant.hash(state);
1239 }
1240}
1241
1242impl crate::prettier::PrettyPrint for Variant {
1243 fn render(&self) -> crate::prettier::Document {
1244 use crate::prettier::*;
1245
1246 let doc = self.docs.as_ref().map(PrettyPrint::render).unwrap_or(Document::Empty);
1247
1248 let name = display(&self.name);
1249 let name_and_payload = if let Some(value_ty) = self.value_ty.as_ref() {
1250 name + const_text("(") + value_ty.render() + const_text(")")
1251 } else {
1252 name
1253 };
1254 doc + name_and_payload + const_text(" = ") + self.discriminant.render()
1255 }
1256}
1257
1258#[cfg(test)]
1259mod tests {
1260 use alloc::{string::ToString, sync::Arc};
1261 use core::str::FromStr;
1262
1263 use miden_debug_types::{DefaultSourceManager, SourceFile, SourceId, SourceLanguage, Uri};
1264
1265 use super::*;
1266 use crate::{ast::Form, prettier::PrettyPrint};
1267
1268 struct DummyResolver {
1269 source_manager: Arc<dyn SourceManager>,
1270 }
1271
1272 impl DummyResolver {
1273 fn new() -> Self {
1274 Self {
1275 source_manager: Arc::new(DefaultSourceManager::default()),
1276 }
1277 }
1278 }
1279
1280 impl TypeResolver<SymbolResolutionError> for DummyResolver {
1281 fn source_manager(&self) -> Arc<dyn SourceManager> {
1282 self.source_manager.clone()
1283 }
1284
1285 fn resolve_local_failed(&self, err: SymbolResolutionError) -> SymbolResolutionError {
1286 err
1287 }
1288
1289 fn get_type(
1290 &mut self,
1291 context: SourceSpan,
1292 _gid: GlobalItemIndex,
1293 ) -> Result<Option<TypeTemplate>, SymbolResolutionError> {
1294 Err(SymbolResolutionError::undefined(context, self.source_manager.as_ref()))
1295 }
1296
1297 fn get_local_type(
1298 &mut self,
1299 _context: SourceSpan,
1300 _id: ItemIndex,
1301 ) -> Result<Option<TypeTemplate>, SymbolResolutionError> {
1302 Ok(None)
1303 }
1304
1305 fn resolve_type_ref(
1306 &mut self,
1307 ty: Span<&Path>,
1308 ) -> Result<SymbolResolution, SymbolResolutionError> {
1309 Err(SymbolResolutionError::undefined(ty.span(), self.source_manager.as_ref()))
1310 }
1311
1312 fn finalize(
1313 &mut self,
1314 context: SourceSpan,
1315 template: TypeTemplate,
1316 ) -> Result<Type, SymbolResolutionError> {
1317 midenc_hir_type::close_template(&template, |_| None).map_err(|_| {
1319 SymbolResolutionError::undefined(context, self.source_manager.as_ref())
1320 })
1321 }
1322 }
1323
1324 fn nested_type_expr(depth: usize) -> TypeExpr {
1325 let mut expr = TypeExpr::Primitive(Span::unknown(Type::Felt));
1326 for i in 0..depth {
1327 expr = match i % 3 {
1328 0 => TypeExpr::Ptr(PointerType::new(expr)),
1329 1 => TypeExpr::Array(ArrayType::new(expr, 1)),
1330 _ => {
1331 let field = StructField {
1332 span: SourceSpan::UNKNOWN,
1333 name: Ident::from_str("field").expect("valid ident"),
1334 ty: expr,
1335 };
1336 TypeExpr::Struct(StructType::new(None, [field]))
1337 },
1338 };
1339 }
1340 expr
1341 }
1342
1343 fn test_source_file(source: &str) -> Arc<SourceFile> {
1344 Arc::new(SourceFile::new(
1345 SourceId::default(),
1346 SourceLanguage::Masm,
1347 Uri::new("memory:///type-expr-test.masm"),
1348 source.to_string().into_boxed_str(),
1349 ))
1350 }
1351
1352 fn parse_type_alias_expr(source: &str) -> TypeExpr {
1353 let mut forms =
1354 crate::parser::parse_forms(test_source_file(source)).expect("type alias should parse");
1355 assert_eq!(forms.len(), 1, "expected exactly one parsed form");
1356 match forms.pop().expect("expected parsed form") {
1357 Form::Type(alias) => alias.ty,
1358 form => panic!("expected type alias form, got {form:?}"),
1359 }
1360 }
1361
1362 fn repr_round_trip_struct(repr: TypeRepr) -> TypeExpr {
1363 TypeExpr::Struct(
1364 StructType::new(
1365 None,
1366 [
1367 StructField {
1368 span: SourceSpan::UNKNOWN,
1369 name: Ident::from_str("prefix").expect("valid ident"),
1370 ty: TypeExpr::Primitive(Span::unknown(Type::Felt)),
1371 },
1372 StructField {
1373 span: SourceSpan::UNKNOWN,
1374 name: Ident::from_str("suffix").expect("valid ident"),
1375 ty: TypeExpr::Primitive(Span::unknown(Type::U32)),
1376 },
1377 ],
1378 )
1379 .with_repr(Span::unknown(repr)),
1380 )
1381 }
1382
1383 #[test]
1384 fn type_expr_depth_boundary() {
1385 let mut resolver = DummyResolver::new();
1386
1387 let ok_expr = nested_type_expr(MAX_TYPE_EXPR_NESTING);
1388 assert!(ok_expr.resolve_template(&mut resolver).is_ok());
1389
1390 let err_expr = nested_type_expr(MAX_TYPE_EXPR_NESTING + 1);
1391 let err = err_expr
1392 .resolve_template(&mut resolver)
1393 .expect_err("expected depth-exceeded error");
1394 assert!(
1395 matches!(err, SymbolResolutionError::TypeExpressionDepthExceeded { max_depth, .. }
1396 if max_depth == MAX_TYPE_EXPR_NESTING)
1397 );
1398 }
1399
1400 #[test]
1401 fn struct_type_expr_render_round_trips_non_default_reprs() {
1402 for repr in [
1403 TypeRepr::align(16),
1404 TypeRepr::packed(1),
1405 TypeRepr::packed(2),
1406 TypeRepr::Transparent,
1407 ] {
1408 let rendered = repr_round_trip_struct(repr).to_pretty_string();
1409 assert!(
1410 rendered.starts_with("struct @"),
1411 "non-default struct repr should render after `struct`: {rendered}"
1412 );
1413
1414 let parsed = parse_type_alias_expr(&format!("type RoundTrip = {rendered}\n"));
1415 let TypeExpr::Struct(parsed) = parsed else {
1416 panic!("expected rendered type to parse back as a struct");
1417 };
1418 assert_eq!(*parsed.repr, repr);
1419 assert_eq!(parsed.fields[0].name.as_str(), "prefix");
1420 assert_eq!(parsed.fields[1].name.as_str(), "suffix");
1421 }
1422 }
1423
1424 #[test]
1425 fn type_expr_from_type_preserves_wide_integer_primitives() {
1426 for ty in [Type::I64, Type::U64, Type::I128, Type::U128] {
1427 let expr = TypeExpr::from(ty.clone());
1428 let TypeExpr::Primitive(actual) = expr else {
1429 panic!("expected primitive type expression for {ty}, got {expr:?}");
1430 };
1431 assert_eq!(actual.into_inner(), ty);
1432 }
1433 }
1434
1435 #[test]
1436 fn type_expr_from_type_preserves_struct_metadata() {
1437 let ty = Type::from(Arc::new(types::StructType::from_parts(
1438 Some(Arc::from("miden:base/core-types@1.0.0/account-id")),
1439 TypeRepr::align(16),
1440 [
1441 (Arc::<str>::from("prefix"), Type::Felt),
1442 (Arc::<str>::from("suffix"), Type::Felt),
1443 ],
1444 )));
1445
1446 let TypeExpr::Struct(actual) = TypeExpr::from(ty) else {
1447 panic!("expected struct type expression");
1448 };
1449 assert_eq!(
1450 actual.name.as_ref().map(Ident::as_str),
1451 Some("miden:base/core-types@1.0.0/account-id"),
1452 );
1453 assert_eq!(*actual.repr, TypeRepr::align(16));
1454 assert_eq!(actual.fields[0].name.as_str(), "prefix");
1455 assert_eq!(actual.fields[1].name.as_str(), "suffix");
1456 }
1457
1458 #[test]
1459 fn type_expr_conversion_of_a_recursive_struct_terminates() {
1460 use midenc_hir_type::{RecursiveTypeBuilder, StructTemplate, TypeRepr, TypeTemplate};
1461
1462 let mut builder = RecursiveTypeBuilder::new();
1463 builder.define_struct(
1464 "Node",
1465 StructTemplate::named(
1466 "Node",
1467 TypeRepr::Default,
1468 [("next", TypeTemplate::ptr(TypeTemplate::rec("Node")))],
1469 ),
1470 );
1471 let node = builder.build().unwrap().remove("Node").unwrap();
1472
1473 let TypeExpr::Struct(converted) = TypeExpr::from(node) else {
1476 panic!("expected a struct type expression");
1477 };
1478 let TypeExpr::Ptr(pointer) = &converted.fields[0].ty else {
1479 panic!("expected a pointer");
1480 };
1481 let TypeExpr::Ref(target) = pointer.pointee.as_ref() else {
1482 panic!("expected the pointee to be a reference, got {:?}", pointer.pointee);
1483 };
1484 assert_eq!(target.inner().to_string(), "Node");
1485 }
1486
1487 #[test]
1488 fn parsed_struct_type_preserves_field_names_through_resolution() {
1489 let expr = parse_type_alias_expr(
1490 "type AccountId = struct @align(16) { prefix: felt, suffix: felt }\n",
1491 );
1492
1493 let mut resolver = DummyResolver::new();
1494 let resolved = TypeResolver::resolve(&mut resolver, &expr)
1495 .expect("struct type should resolve")
1496 .expect("struct type should be concrete");
1497 let Type::Struct(resolved_struct) = &resolved else {
1498 panic!("expected resolved struct type, got {resolved:?}");
1499 };
1500 assert_eq!(resolved_struct.repr(), TypeRepr::align(16));
1501 let resolved_fields = resolved_struct.get();
1502 assert_eq!(resolved_fields.fields()[0].name.as_deref(), Some("prefix"));
1503 assert_eq!(resolved_fields.fields()[1].name.as_deref(), Some("suffix"));
1504
1505 let TypeExpr::Struct(converted) = TypeExpr::from(resolved) else {
1506 panic!("expected concrete struct to convert back to struct type expression");
1507 };
1508 assert_eq!(*converted.repr, TypeRepr::align(16));
1509 assert_eq!(converted.fields[0].name.as_str(), "prefix");
1510 assert_eq!(converted.fields[1].name.as_str(), "suffix");
1511 }
1512}