1use alloc::{boxed::Box, string::String, vec, vec::Vec};
7use core::hash::Hash;
8use pliron::{
9 builtin::type_interfaces::FunctionTypeInterface,
10 combine::{Parser, between, optional, token},
11 common_traits::Verify,
12 context::Context,
13 derive::{format, pliron_type, type_interface_impl},
14 dict_key,
15 identifier::Identifier,
16 input_err_noloc,
17 irfmt::{
18 parsers::{delimited_list_parser, location, spaced, type_parser},
19 printers::{enclosed, list_with_sep},
20 },
21 location::Located,
22 parsable::{IntoParseResult, Parsable, ParseResult, StateStream},
23 printable::{self, ListSeparator, Printable},
24 result::Result,
25 r#type::{Type, TypeHandle, TypedHandle},
26 verify_err_noloc,
27};
28use thiserror::Error;
29
30#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Hash)]
32#[format]
33pub enum StructLayout {
34 #[default]
35 Unpacked,
36 Packed,
37}
38
39impl From<bool> for StructLayout {
40 fn from(is_packed: bool) -> Self {
41 if is_packed {
42 Self::Packed
43 } else {
44 Self::Unpacked
45 }
46 }
47}
48
49impl From<StructLayout> for bool {
50 fn from(layout: StructLayout) -> Self {
51 layout == StructLayout::Packed
52 }
53}
54
55#[pliron_type(name = "llvm.struct")]
66#[derive(Debug)]
67pub struct StructType {
68 name: Option<Identifier>,
69 body: Option<(Vec<TypeHandle>, StructLayout)>,
70}
71
72impl StructType {
73 pub fn get_named(
84 ctx: &Context,
85 name: Identifier,
86 body: Option<(Vec<TypeHandle>, StructLayout)>,
87 ) -> Result<TypedHandle<Self>> {
88 let self_handle = Type::instantiate(
89 StructType {
90 name: Some(name.clone()),
91 body: None,
93 },
94 ctx,
95 );
96 let mut self_ref = self_handle.to_handle().deref_mut(ctx);
98 let self_ref = self_ref.downcast_mut::<StructType>().unwrap();
99 assert!(self_ref.name.as_ref().unwrap() == &name);
100 if let Some(body) = body {
101 if let Some(existing_body) = &self_ref.body {
103 if existing_body != &body {
105 input_err_noloc!(StructErr::ExistingMismatch(name.into()))?
106 }
107 } else {
108 self_ref.body = Some(body);
110 }
111 }
112 Ok(self_handle)
113 }
114
115 pub fn get_unnamed(ctx: &Context, body: (Vec<TypeHandle>, StructLayout)) -> TypedHandle<Self> {
118 Type::instantiate(
119 StructType {
120 name: None,
121 body: Some(body),
122 },
123 ctx,
124 )
125 }
126
127 pub fn is_opaque(&self) -> bool {
129 self.body.is_none()
130 }
131
132 pub fn is_named(&self) -> bool {
134 self.name.is_some()
135 }
136
137 pub fn name(&self) -> Option<Identifier> {
139 self.name.clone()
140 }
141
142 pub fn layout(&self) -> StructLayout {
146 self.body
147 .as_ref()
148 .expect("layout shouldn't be called on opaque types")
149 .1
150 }
151
152 pub fn field_type(&self, field_idx: usize) -> TypeHandle {
156 self.body
157 .as_ref()
158 .expect("field_type shouldn't be called on opaque types")
159 .0[field_idx]
160 }
161
162 pub fn num_fields(&self) -> usize {
166 self.body
167 .as_ref()
168 .expect("num_fields shouldn't be called on opaque types")
169 .0
170 .len()
171 }
172
173 pub fn fields(&self) -> impl Iterator<Item = TypeHandle> + '_ {
177 self.body
178 .as_ref()
179 .expect("fields shouldn't be called on opaque types")
180 .0
181 .iter()
182 .cloned()
183 }
184}
185
186#[derive(Debug, Error)]
187pub enum StructErr {
188 #[error("struct cannot be both opaque and anonymous")]
189 OpaqueAndAnonymousErr,
190 #[error("struct {0} already exists and is different")]
191 ExistingMismatch(String),
192}
193
194impl Verify for StructType {
195 fn verify(&self, _ctx: &Context) -> Result<()> {
196 if self.name.is_none() && self.body.is_none() {
197 verify_err_noloc!(StructErr::OpaqueAndAnonymousErr)?
198 }
199 Ok(())
200 }
201}
202
203dict_key!(STRUCT_TYPE_IN_PRINTING, "llvm_struct_type_in_printing");
204
205fn struct_type_start_printing(state: &printable::State, name: &Identifier) -> bool {
208 let mut aux_data = state.aux_data_mut();
209 let in_printing = aux_data
210 .entry(STRUCT_TYPE_IN_PRINTING.clone())
211 .or_insert_with(|| Box::new(Vec::<Identifier>::new()))
214 .downcast_mut::<Vec<Identifier>>()
215 .expect("failed to downcast struct-type-in-printing state");
216 if in_printing.contains(name) {
217 true
218 } else {
219 in_printing.push(name.clone());
220 false
221 }
222}
223
224fn struct_type_done_printing(state: &printable::State, name: &Identifier) {
226 let mut aux_data = state.aux_data_mut();
227 let in_printing = aux_data
228 .get_mut(&*STRUCT_TYPE_IN_PRINTING)
229 .expect("struct-type-in-printing state must have been created by now")
230 .downcast_mut::<Vec<Identifier>>()
231 .expect("failed to downcast struct-type-in-printing state");
232 assert!(in_printing.last().unwrap() == name);
233 in_printing.pop();
234}
235
236impl Printable for StructType {
237 fn fmt(
238 &self,
239 ctx: &Context,
240 state: &printable::State,
241 f: &mut core::fmt::Formatter<'_>,
242 ) -> core::fmt::Result {
243 write!(f, "<")?;
244
245 if let Some(name) = &self.name {
246 if struct_type_start_printing(state, name) {
247 return write!(f, "{}>", name.clone());
248 }
249 write!(f, "{name}")?;
251 if !self.is_opaque() {
252 write!(f, " ")?;
253 }
254 }
255
256 if let Some((fields, layout)) = &self.body {
257 enclosed(
258 "{ ",
259 " }",
260 list_with_sep(fields, ListSeparator::CharSpace(',')),
261 )
262 .fmt(ctx, state, f)?;
263 write!(f, " : {}", layout.disp(ctx))?;
264 }
265
266 if let Some(name) = &self.name {
268 struct_type_done_printing(state, name);
269 }
270 write!(f, ">")
271 }
272}
273
274impl Hash for StructType {
275 fn hash<H: core::hash::Hasher>(&self, state: &mut H) {
276 match &self.name {
277 Some(name) => name.hash(state),
278 None => {
279 self.body
280 .as_ref()
281 .expect("Anonymous struct must have its body set")
282 .hash(state);
283 }
284 }
285 }
286}
287
288impl PartialEq for StructType {
289 fn eq(&self, other: &Self) -> bool {
290 match (&self.name, &other.name) {
291 (Some(name), Some(other_name)) => name == other_name,
292 (None, None) => self.body == other.body,
293 _ => false,
294 }
295 }
296}
297
298impl Parsable for StructType {
299 type Arg = ();
300 type Parsed = TypedHandle<Self>;
301
302 fn parse<'a>(
303 state_stream: &mut StateStream<'a>,
304 _arg: Self::Arg,
305 ) -> ParseResult<'a, Self::Parsed>
306 where
307 Self: Sized,
308 {
309 let body_parser = || {
310 delimited_list_parser('{', '}', ',', type_parser())
312 .and(spaced(token(':')).with(spaced(StructLayout::parser(()))))
314 };
315
316 let named = spaced((location(), Identifier::parser(())))
317 .and(spaced(optional(body_parser())))
318 .map(|((loc, name), body_opt)| (loc, Some(name), body_opt));
319 let anonymous = spaced((location(), body_parser()))
320 .map(|(loc, body)| (loc, None::<Identifier>, Some(body)));
321
322 let mut struct_parser = between(token('<'), token('>'), named.or(anonymous));
324
325 let (loc, name_opt, body_opt) = struct_parser.parse_stream(state_stream).into_result()?.0;
326 let ctx = &mut state_stream.state.ctx;
327 if let Some(name) = name_opt {
328 StructType::get_named(ctx, name, body_opt)
329 .map_err(|mut err| {
330 err.set_loc(loc);
331 err
332 })
333 .into_parse_result()
334 } else {
335 Ok(StructType::get_unnamed(
336 ctx,
337 body_opt.expect("Without a name, a struct type must have a body."),
338 ))
339 .into_parse_result()
340 }
341 }
342}
343
344impl Eq for StructType {}
345
346#[pliron_type(
350 name = "llvm.ptr",
351 generate_get = true,
352 format = "`(` $address_space `)`",
353 verifier = "succ"
354)]
355#[derive(Hash, PartialEq, Eq, Debug)]
356pub struct PointerType {
357 address_space: u32,
358}
359
360impl PointerType {
361 pub fn address_space(&self) -> u32 {
363 self.address_space
364 }
365}
366
367#[pliron_type(
369 name = "llvm.array",
370 generate_get = true,
371 format = "`[` $size ` x ` $elem `]`",
372 verifier = "succ"
373)]
374#[derive(Hash, PartialEq, Eq, Debug)]
375pub struct ArrayType {
376 elem: TypeHandle,
377 size: u64,
378}
379
380impl ArrayType {
381 pub fn elem_type(&self) -> TypeHandle {
383 self.elem
384 }
385
386 pub fn size(&self) -> u64 {
388 self.size
389 }
390}
391
392#[pliron_type(name = "llvm.void", generate_get = true, format, verifier = "succ")]
393#[derive(Hash, PartialEq, Eq, Debug)]
394pub struct VoidType;
395
396#[pliron_type(
397 name = "llvm.func",
398 generate_get = true,
399 format = "`<` $res `(` vec($args, CharSpace(`,`)) `) variadic = ` $is_var_arg `>`",
400 verifier = "succ"
401)]
402#[derive(Hash, PartialEq, Eq, Debug)]
403pub struct FuncType {
404 res: TypeHandle,
405 args: Vec<TypeHandle>,
406 is_var_arg: bool,
407}
408
409#[derive(Debug, Error)]
410pub enum FuncTypeErr {
411 #[error("Expected at most one result")]
412 TooManyResults,
413}
414
415impl FuncType {
416 pub fn result_type(&self) -> TypeHandle {
418 self.res
419 }
420
421 pub fn is_var_arg(&self) -> bool {
423 self.is_var_arg
424 }
425}
426
427#[type_interface_impl]
428impl FunctionTypeInterface for FuncType {
429 fn arg_types(&self) -> Vec<TypeHandle> {
430 self.args.clone()
431 }
432 fn res_types(&self) -> Vec<TypeHandle> {
433 vec![self.res]
434 }
435}
436
437#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
440#[format]
441pub enum VectorTypeKind {
442 Fixed,
443 Scalable,
444}
445
446#[pliron_type(
447 name = "llvm.vector",
448 generate_get = true,
449 format = "`<` $kind ` x ` $num_elems ` x ` $elem_ty `>`",
450 verifier = "succ"
451)]
452#[derive(Hash, PartialEq, Eq, Debug)]
453pub struct VectorType {
454 elem_ty: TypeHandle,
455 num_elems: u32,
456 kind: VectorTypeKind,
457}
458
459impl VectorType {
460 pub fn elem_type(&self) -> TypeHandle {
462 self.elem_ty
463 }
464
465 pub fn num_elements(&self) -> u32 {
467 self.num_elems
468 }
469
470 pub fn is_scalable(&self) -> bool {
472 self.kind == VectorTypeKind::Scalable
473 }
474
475 pub fn kind(&self) -> VectorTypeKind {
477 self.kind
478 }
479}
480
481#[cfg(test)]
482mod tests {
483
484 use alloc::{format, string::ToString, vec};
485
486 use crate::types::{FuncType, PointerType, StructLayout, StructType, VoidType};
487 use expect_test::expect;
488 use pliron::{
489 builtin::types::{IntegerType, Signedness},
490 combine::{self, Parser, eof, token},
491 context::Context,
492 derive::{pliron_type, verify_succ},
493 identifier::Identifier,
494 irfmt::parsers::{spaced, type_parser},
495 parsable::{Parsable, ParseResult, StateStream, parse_from_str},
496 printable::{self, Printable},
497 result::{ExpectOk, Result},
498 r#type::{TypeHandle, TypedHandle},
499 };
500
501 #[test]
502 fn test_struct() -> Result<()> {
503 let ctx = Context::new();
504 let int64 = IntegerType::get(&ctx, 64, Signedness::Signless).into();
505 let linked_list_id: Identifier = "LinkedList".try_into().unwrap();
506
507 let list_struct: TypeHandle =
509 StructType::get_named(&ctx, linked_list_id.clone(), None)?.into();
510 assert!(
511 list_struct
512 .deref(&ctx)
513 .downcast_ref::<StructType>()
514 .unwrap()
515 .is_opaque()
516 );
517 let list_struct_ptr = TypedPointerType::get(&ctx, list_struct).into();
518 let fields = vec![int64, list_struct_ptr];
519 StructType::get_named(
521 &ctx,
522 linked_list_id.clone(),
523 Some((fields, StructLayout::Unpacked)),
524 )?;
525 assert!(
526 !list_struct
527 .deref(&ctx)
528 .downcast_ref::<StructType>()
529 .unwrap()
530 .is_opaque()
531 );
532
533 let list_struct_2 = StructType::get_named(&ctx, linked_list_id.clone(), None)?.into();
536 assert!(list_struct == list_struct_2);
537
538 assert_eq!(
539 list_struct.disp(&ctx).to_string(),
540 "llvm.struct <LinkedList { builtin.integer i64, llvm.typed_ptr <llvm.struct <LinkedList>> } : Unpacked>"
541 );
542
543 StructType::get_named(
545 &ctx,
546 linked_list_id.clone(),
547 Some((vec![int64, list_struct_ptr], StructLayout::Unpacked)),
548 )?;
549
550 assert!(
552 StructType::get_named(
553 &ctx,
554 linked_list_id.clone(),
555 Some((vec![int64, list_struct_ptr], StructLayout::Packed)),
556 )
557 .is_err()
558 );
559
560 assert!(
562 StructType::get_named(
563 &ctx,
564 linked_list_id,
565 Some((vec![int64, int64], StructLayout::Unpacked)),
566 )
567 .is_err()
568 );
569
570 let head_fields = vec![int64, list_struct_ptr];
571 let head_struct =
572 StructType::get_unnamed(&ctx, (head_fields.clone(), StructLayout::Unpacked));
573 let head_struct2 =
574 StructType::get_unnamed(&ctx, (head_fields.clone(), StructLayout::Unpacked));
575 assert!(head_struct == head_struct2);
576
577 let head_struct_packed = StructType::get_unnamed(&ctx, (head_fields, StructLayout::Packed));
580 assert!(head_struct != head_struct_packed);
581 assert_eq!(
582 head_struct_packed.deref(&ctx).layout(),
583 StructLayout::Packed
584 );
585
586 Ok(())
587 }
588
589 #[verify_succ]
593 #[pliron_type(name = "llvm.typed_ptr", generate_get = true)]
594 #[derive(Hash, PartialEq, Eq, Debug)]
595 pub struct TypedPointerType {
596 to: TypeHandle,
597 }
598
599 impl TypedPointerType {
600 pub fn get_pointee_type(&self) -> TypeHandle {
602 self.to
603 }
604 }
605
606 impl Printable for TypedPointerType {
607 fn fmt(
608 &self,
609 ctx: &Context,
610 state: &printable::State,
611 f: &mut core::fmt::Formatter<'_>,
612 ) -> core::fmt::Result {
613 write!(f, "<{}>", self.to.print(ctx, state))
614 }
615 }
616
617 impl Parsable for TypedPointerType {
618 type Arg = ();
619 type Parsed = TypedHandle<Self>;
620
621 fn parse<'a>(
622 state_stream: &mut StateStream<'a>,
623 _arg: Self::Arg,
624 ) -> ParseResult<'a, Self::Parsed>
625 where
626 Self: Sized,
627 {
628 combine::between(token('<'), token('>'), spaced(type_parser()))
629 .parse_stream(state_stream)
630 .map(|pointee_ty| TypedPointerType::get(state_stream.state.ctx, pointee_ty))
631 .into()
632 }
633 }
634
635 #[test]
636 fn test_pointer_types() {
637 let ctx = Context::new();
638 let int32_1 = IntegerType::get(&ctx, 32, Signedness::Signed);
639 let int64 = IntegerType::get(&ctx, 64, Signedness::Signed).into();
640
641 let int64pointer = TypedPointerType::get(&ctx, int64);
642 assert_eq!(
643 int64pointer.disp(&ctx).to_string(),
644 "llvm.typed_ptr <builtin.integer si64>"
645 );
646 assert!(int64pointer == TypedPointerType::get(&ctx, int64));
647
648 assert!(
649 int64
650 .deref(&ctx)
651 .downcast_ref::<IntegerType>()
652 .unwrap()
653 .width()
654 == 64
655 );
656
657 assert!(IntegerType::get(&ctx, 32, Signedness::Signed) == int32_1);
658 assert!(TypedPointerType::get(&ctx, int64) == int64pointer);
659 assert!(int64pointer.deref(&ctx).get_pointee_type() == int64);
660 }
661
662 #[test]
663 fn test_pointer_type_parsing() {
664 let mut ctx = Context::new();
665
666 let res = parse_from_str(
667 type_parser(),
668 &mut ctx,
669 "llvm.typed_ptr <builtin.integer si64>",
670 )
671 .expect_ok(&ctx);
672 assert_eq!(
673 &res.disp(&ctx).to_string(),
674 "llvm.typed_ptr <builtin.integer si64>"
675 );
676 }
677
678 #[test]
679 fn test_opaque_pointer_addrspace() {
680 let mut ctx = Context::new();
681
682 let res = parse_from_str(type_parser(), &mut ctx, "llvm.ptr (0)").expect_ok(&ctx);
685 assert_eq!(res.disp(&ctx).to_string().trim(), "llvm.ptr (0)");
686 assert_eq!(
687 res.deref(&ctx)
688 .downcast_ref::<PointerType>()
689 .unwrap()
690 .address_space(),
691 0
692 );
693
694 let res = parse_from_str(type_parser(), &mut ctx, "llvm.ptr (3)").expect_ok(&ctx);
696 assert_eq!(res.disp(&ctx).to_string().trim(), "llvm.ptr (3)");
697 assert_eq!(
698 res.deref(&ctx)
699 .downcast_ref::<PointerType>()
700 .unwrap()
701 .address_space(),
702 3
703 );
704 }
705
706 #[test]
707 fn test_fp16_type_roundtrip() {
708 let mut ctx = Context::new();
709 let res = parse_from_str(type_parser(), &mut ctx, "builtin.fp16").expect_ok(&ctx);
710 assert_eq!(res.disp(&ctx).to_string().trim(), "builtin.fp16");
711 assert!(
712 res.deref(&ctx)
713 .downcast_ref::<pliron::builtin::types::FP16Type>()
714 .is_some()
715 );
716 }
717
718 #[test]
719 fn test_struct_type_parsing() {
720 let mut ctx = Context::new();
721
722 let unpacked_named = "llvm.struct <LinkedList { builtin.integer i64, llvm.typed_ptr <llvm.struct <LinkedList>> } : Unpacked>";
724 let res = parse_from_str(type_parser(), &mut ctx, unpacked_named).expect_ok(&ctx);
725 assert_eq!(&res.disp(&ctx).to_string(), unpacked_named);
726
727 let test_string = "llvm.struct <ExternStruct>";
729 let res = parse_from_str(type_parser(), &mut ctx, test_string).expect_ok(&ctx);
730 assert_eq!(&res.disp(&ctx).to_string(), test_string);
731 {
732 let res = res.deref(&ctx);
733 let res = res.downcast_ref::<StructType>().unwrap();
734 assert!(res.is_opaque() && res.is_named());
735 }
736
737 let test_string = "llvm.struct <{ builtin.integer i8 } : Unpacked>";
739 let res = parse_from_str(type_parser(), &mut ctx, test_string).expect_ok(&ctx);
740 assert_eq!(&res.disp(&ctx).to_string(), test_string);
741 {
742 let res = res.deref(&ctx);
743 let res = res.downcast_ref::<StructType>().unwrap();
744 assert!(!res.is_opaque() && !res.is_named());
745 assert_eq!(res.layout(), StructLayout::Unpacked);
746 }
747
748 let test_string = "llvm.struct <{ builtin.integer i8 } : Packed>";
750 let res = parse_from_str(type_parser(), &mut ctx, test_string).expect_ok(&ctx);
751 assert_eq!(&res.disp(&ctx).to_string(), test_string);
752 {
753 let res = res.deref(&ctx);
754 let res = res.downcast_ref::<StructType>().unwrap();
755 assert!(!res.is_opaque() && !res.is_named());
756 assert_eq!(res.layout(), StructLayout::Packed);
757 }
758
759 let test_string = "llvm.struct <PackedS { builtin.integer i8 } : Packed>";
761 let res = parse_from_str(type_parser(), &mut ctx, test_string).expect_ok(&ctx);
762 assert_eq!(&res.disp(&ctx).to_string(), test_string);
763 {
764 let res = res.deref(&ctx);
765 let res = res.downcast_ref::<StructType>().unwrap();
766 assert!(!res.is_opaque() && res.is_named());
767 assert_eq!(res.layout(), StructLayout::Packed);
768 }
769 }
770
771 #[test]
772 fn test_struct_type_errs() {
773 let mut ctx = Context::new();
774
775 let _ = parse_from_str(
776 type_parser(),
777 &mut ctx,
778 "llvm.struct < My1 { builtin.integer i8 } : Unpacked >",
779 )
780 .expect_ok(&ctx);
781
782 let err_msg = format!(
783 "{}",
784 parse_from_str(
785 type_parser(),
786 &mut ctx,
787 "llvm.struct < My1 { builtin.integer i16 } : Unpacked>",
788 )
789 .unwrap_err()
790 );
791
792 let expected_err_msg = expect![[r#"
793 Compilation error: invalid input program.
794 Parse error at line: 1, column: 15
795 struct My1 already exists and is different
796 "#]];
797 expected_err_msg.assert_eq(&err_msg);
798 }
799
800 #[test]
801 fn test_functype_parsing() {
802 let mut ctx = Context::new();
803
804 let si32 = IntegerType::get(&ctx, 32, Signedness::Signed);
805
806 let input = "llvm.func <llvm.void (builtin.integer si32) variadic = false>";
807 let res = parse_from_str(type_parser().and(eof()), &mut ctx, input)
808 .expect_ok(&ctx)
809 .0;
810
811 let void_ty = VoidType::get(&ctx);
812 assert!(res == FuncType::get(&ctx, void_ty.to_handle(), vec![si32.into()], false).into());
813 assert_eq!(input, &res.disp(&ctx).to_string());
814 }
815}