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 parsable::{IntoParseResult, Parsable, ParseResult, StateStream},
22 printable::{self, ListSeparator, Printable},
23 result::Result,
24 r#type::{Type, TypeHandle, TypedHandle},
25 verify_err_noloc,
26};
27use thiserror::Error;
28
29#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Hash)]
31#[format]
32pub enum StructLayout {
33 #[default]
34 Unpacked,
35 Packed,
36}
37
38impl From<bool> for StructLayout {
39 fn from(is_packed: bool) -> Self {
40 if is_packed {
41 Self::Packed
42 } else {
43 Self::Unpacked
44 }
45 }
46}
47
48impl From<StructLayout> for bool {
49 fn from(layout: StructLayout) -> Self {
50 layout == StructLayout::Packed
51 }
52}
53
54#[pliron_type(name = "llvm.struct")]
65#[derive(Debug)]
66pub struct StructType {
67 name: Option<Identifier>,
68 body: Option<(Vec<TypeHandle>, StructLayout)>,
69}
70
71impl StructType {
72 pub fn get_named(
83 ctx: &Context,
84 name: Identifier,
85 body: Option<(Vec<TypeHandle>, StructLayout)>,
86 ) -> Result<TypedHandle<Self>> {
87 let self_handle = Type::instantiate(
88 StructType {
89 name: Some(name.clone()),
90 body: None,
92 },
93 ctx,
94 );
95 let mut self_ref = self_handle.to_handle().deref_mut(ctx);
97 let self_ref = self_ref.downcast_mut::<StructType>().unwrap();
98 assert!(self_ref.name.as_ref().unwrap() == &name);
99 if let Some(body) = body {
100 if let Some(existing_body) = &self_ref.body {
102 if existing_body != &body {
104 input_err_noloc!(StructErr::ExistingMismatch(name.into()))?
105 }
106 } else {
107 self_ref.body = Some(body);
109 }
110 }
111 Ok(self_handle)
112 }
113
114 pub fn get_unnamed(ctx: &Context, body: (Vec<TypeHandle>, StructLayout)) -> TypedHandle<Self> {
117 Type::instantiate(
118 StructType {
119 name: None,
120 body: Some(body),
121 },
122 ctx,
123 )
124 }
125
126 pub fn is_opaque(&self) -> bool {
128 self.body.is_none()
129 }
130
131 pub fn is_named(&self) -> bool {
133 self.name.is_some()
134 }
135
136 pub fn name(&self) -> Option<Identifier> {
138 self.name.clone()
139 }
140
141 pub fn layout(&self) -> StructLayout {
145 self.body
146 .as_ref()
147 .expect("layout shouldn't be called on opaque types")
148 .1
149 }
150
151 pub fn field_type(&self, field_idx: usize) -> TypeHandle {
155 self.body
156 .as_ref()
157 .expect("field_type shouldn't be called on opaque types")
158 .0[field_idx]
159 }
160
161 pub fn num_fields(&self) -> usize {
165 self.body
166 .as_ref()
167 .expect("num_fields shouldn't be called on opaque types")
168 .0
169 .len()
170 }
171
172 pub fn fields(&self) -> impl Iterator<Item = TypeHandle> + '_ {
176 self.body
177 .as_ref()
178 .expect("fields shouldn't be called on opaque types")
179 .0
180 .iter()
181 .cloned()
182 }
183}
184
185#[derive(Debug, Error)]
186pub enum StructErr {
187 #[error("struct cannot be both opaque and anonymous")]
188 OpaqueAndAnonymousErr,
189 #[error("struct {0} already exists and is different")]
190 ExistingMismatch(String),
191}
192
193impl Verify for StructType {
194 fn verify(&self, _ctx: &Context) -> Result<()> {
195 if self.name.is_none() && self.body.is_none() {
196 verify_err_noloc!(StructErr::OpaqueAndAnonymousErr)?
197 }
198 Ok(())
199 }
200}
201
202dict_key!(STRUCT_TYPE_IN_PRINTING, "llvm_struct_type_in_printing");
203
204fn struct_type_start_printing(state: &printable::State, name: &Identifier) -> bool {
207 let mut aux_data = state.aux_data_mut();
208 let in_printing = aux_data
209 .entry(STRUCT_TYPE_IN_PRINTING.clone())
210 .or_insert_with(|| Box::new(Vec::<Identifier>::new()))
213 .downcast_mut::<Vec<Identifier>>()
214 .expect("failed to downcast struct-type-in-printing state");
215 if in_printing.contains(name) {
216 true
217 } else {
218 in_printing.push(name.clone());
219 false
220 }
221}
222
223fn struct_type_done_printing(state: &printable::State, name: &Identifier) {
225 let mut aux_data = state.aux_data_mut();
226 let in_printing = aux_data
227 .get_mut(&STRUCT_TYPE_IN_PRINTING)
228 .expect("struct-type-in-printing state must have been created by now")
229 .downcast_mut::<Vec<Identifier>>()
230 .expect("failed to downcast struct-type-in-printing state");
231 assert!(in_printing.last().unwrap() == name);
232 in_printing.pop();
233}
234
235impl Printable for StructType {
236 fn fmt(
237 &self,
238 ctx: &Context,
239 state: &printable::State,
240 f: &mut core::fmt::Formatter<'_>,
241 ) -> core::fmt::Result {
242 write!(f, "<")?;
243
244 if let Some(name) = &self.name {
245 if struct_type_start_printing(state, name) {
246 return write!(f, "{}>", name.clone());
247 }
248 write!(f, "{name}")?;
250 if !self.is_opaque() {
251 write!(f, " ")?;
252 }
253 }
254
255 if let Some((fields, layout)) = &self.body {
256 enclosed(
257 "{ ",
258 " }",
259 list_with_sep(fields, ListSeparator::CharSpace(',')),
260 )
261 .fmt(ctx, state, f)?;
262 write!(f, " : {}", layout.print(ctx, state))?;
263 }
264
265 if let Some(name) = &self.name {
267 struct_type_done_printing(state, name);
268 }
269 write!(f, ">")
270 }
271}
272
273impl Hash for StructType {
274 fn hash<H: core::hash::Hasher>(&self, state: &mut H) {
275 match &self.name {
276 Some(name) => name.hash(state),
277 None => {
278 self.body
279 .as_ref()
280 .expect("Anonymous struct must have its body set")
281 .hash(state);
282 }
283 }
284 }
285}
286
287impl PartialEq for StructType {
288 fn eq(&self, other: &Self) -> bool {
289 match (&self.name, &other.name) {
290 (Some(name), Some(other_name)) => name == other_name,
291 (None, None) => self.body == other.body,
292 _ => false,
293 }
294 }
295}
296
297impl Parsable for StructType {
298 type Arg = ();
299 type Parsed = TypedHandle<Self>;
300
301 fn parse<'a>(
302 state_stream: &mut StateStream<'a>,
303 _arg: Self::Arg,
304 ) -> ParseResult<'a, Self::Parsed>
305 where
306 Self: Sized,
307 {
308 let body_parser = || {
309 delimited_list_parser('{', '}', ',', type_parser())
311 .and(spaced(token(':')).with(spaced(StructLayout::parser(()))))
313 };
314
315 let named = spaced((location(), Identifier::parser(())))
316 .and(spaced(optional(body_parser())))
317 .map(|((loc, name), body_opt)| (loc, Some(name), body_opt));
318 let anonymous = spaced((location(), body_parser()))
319 .map(|(loc, body)| (loc, None::<Identifier>, Some(body)));
320
321 let mut struct_parser = between(token('<'), token('>'), named.or(anonymous));
323
324 let (loc, name_opt, body_opt) = struct_parser.parse_stream(state_stream).into_result()?.0;
325 let ctx = &mut state_stream.state.ctx;
326 if let Some(name) = name_opt {
327 StructType::get_named(ctx, name, body_opt)
328 .map_err(|mut err| {
329 err.set_loc(loc);
330 err
331 })
332 .into_parse_result()
333 } else {
334 Ok(StructType::get_unnamed(
335 ctx,
336 body_opt.expect("Without a name, a struct type must have a body."),
337 ))
338 .into_parse_result()
339 }
340 }
341}
342
343impl Eq for StructType {}
344
345#[pliron_type(
349 name = "llvm.ptr",
350 generate_get = true,
351 format = "`(` $address_space `)`",
352 verifier = "succ"
353)]
354#[derive(Hash, PartialEq, Eq, Debug)]
355pub struct PointerType {
356 address_space: u32,
357}
358
359impl PointerType {
360 pub fn address_space(&self) -> u32 {
362 self.address_space
363 }
364}
365
366#[pliron_type(
368 name = "llvm.array",
369 generate_get = true,
370 format = "`[` $size ` x ` $elem `]`",
371 verifier = "succ"
372)]
373#[derive(Hash, PartialEq, Eq, Debug)]
374pub struct ArrayType {
375 elem: TypeHandle,
376 size: u64,
377}
378
379impl ArrayType {
380 pub fn elem_type(&self) -> TypeHandle {
382 self.elem
383 }
384
385 pub fn size(&self) -> u64 {
387 self.size
388 }
389}
390
391#[pliron_type(name = "llvm.void", generate_get = true, format, verifier = "succ")]
392#[derive(Hash, PartialEq, Eq, Debug)]
393pub struct VoidType;
394
395#[pliron_type(
396 name = "llvm.func",
397 generate_get = true,
398 format = "`<` $res `(` vec($args, CharSpace(`,`)) `) variadic = ` $is_var_arg `>`",
399 verifier = "succ"
400)]
401#[derive(Hash, PartialEq, Eq, Debug)]
402pub struct FuncType {
403 res: TypeHandle,
404 args: Vec<TypeHandle>,
405 is_var_arg: bool,
406}
407
408#[derive(Debug, Error)]
409pub enum FuncTypeErr {
410 #[error("Expected at most one result")]
411 TooManyResults,
412}
413
414impl FuncType {
415 pub fn result_type(&self) -> TypeHandle {
417 self.res
418 }
419
420 pub fn is_var_arg(&self) -> bool {
422 self.is_var_arg
423 }
424}
425
426#[type_interface_impl]
427impl FunctionTypeInterface for FuncType {
428 fn arg_types(&self) -> Vec<TypeHandle> {
429 self.args.clone()
430 }
431 fn res_types(&self) -> Vec<TypeHandle> {
432 vec![self.res]
433 }
434}
435
436#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
439#[format]
440pub enum VectorTypeKind {
441 Fixed,
442 Scalable,
443}
444
445#[pliron_type(
446 name = "llvm.vector",
447 generate_get = true,
448 format = "`<` $kind ` x ` $num_elems ` x ` $elem_ty `>`",
449 verifier = "succ"
450)]
451#[derive(Hash, PartialEq, Eq, Debug)]
452pub struct VectorType {
453 elem_ty: TypeHandle,
454 num_elems: u32,
455 kind: VectorTypeKind,
456}
457
458impl VectorType {
459 pub fn elem_type(&self) -> TypeHandle {
461 self.elem_ty
462 }
463
464 pub fn num_elements(&self) -> u32 {
466 self.num_elems
467 }
468
469 pub fn is_scalable(&self) -> bool {
471 self.kind == VectorTypeKind::Scalable
472 }
473
474 pub fn kind(&self) -> VectorTypeKind {
476 self.kind
477 }
478}
479
480#[cfg(test)]
481mod tests {
482
483 use alloc::{format, string::ToString, vec};
484
485 use crate::types::{FuncType, PointerType, StructLayout, StructType, VoidType};
486 use expect_test::expect;
487 use pliron::{
488 builtin::types::{IntegerType, Signedness},
489 combine::{self, Parser, eof, token},
490 context::Context,
491 derive::{pliron_type, verify_succ},
492 ident,
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 = ident!("LinkedList");
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_struct_type_parsing() {
708 let mut ctx = Context::new();
709
710 let unpacked_named = "llvm.struct <LinkedList { builtin.integer i64, llvm.typed_ptr <llvm.struct <LinkedList>> } : Unpacked>";
712 let res = parse_from_str(type_parser(), &mut ctx, unpacked_named).expect_ok(&ctx);
713 assert_eq!(&res.disp(&ctx).to_string(), unpacked_named);
714
715 let test_string = "llvm.struct <ExternStruct>";
717 let res = parse_from_str(type_parser(), &mut ctx, test_string).expect_ok(&ctx);
718 assert_eq!(&res.disp(&ctx).to_string(), test_string);
719 {
720 let res = res.deref(&ctx);
721 let res = res.downcast_ref::<StructType>().unwrap();
722 assert!(res.is_opaque() && res.is_named());
723 }
724
725 let test_string = "llvm.struct <{ builtin.integer i8 } : Unpacked>";
727 let res = parse_from_str(type_parser(), &mut ctx, test_string).expect_ok(&ctx);
728 assert_eq!(&res.disp(&ctx).to_string(), test_string);
729 {
730 let res = res.deref(&ctx);
731 let res = res.downcast_ref::<StructType>().unwrap();
732 assert!(!res.is_opaque() && !res.is_named());
733 assert_eq!(res.layout(), StructLayout::Unpacked);
734 }
735
736 let test_string = "llvm.struct <{ builtin.integer i8 } : Packed>";
738 let res = parse_from_str(type_parser(), &mut ctx, test_string).expect_ok(&ctx);
739 assert_eq!(&res.disp(&ctx).to_string(), test_string);
740 {
741 let res = res.deref(&ctx);
742 let res = res.downcast_ref::<StructType>().unwrap();
743 assert!(!res.is_opaque() && !res.is_named());
744 assert_eq!(res.layout(), StructLayout::Packed);
745 }
746
747 let test_string = "llvm.struct <PackedS { builtin.integer i8 } : Packed>";
749 let res = parse_from_str(type_parser(), &mut ctx, test_string).expect_ok(&ctx);
750 assert_eq!(&res.disp(&ctx).to_string(), test_string);
751 {
752 let res = res.deref(&ctx);
753 let res = res.downcast_ref::<StructType>().unwrap();
754 assert!(!res.is_opaque() && res.is_named());
755 assert_eq!(res.layout(), StructLayout::Packed);
756 }
757 }
758
759 #[test]
760 fn test_struct_type_errs() {
761 let mut ctx = Context::new();
762
763 let _ = parse_from_str(
764 type_parser(),
765 &mut ctx,
766 "llvm.struct < My1 { builtin.integer i8 } : Unpacked >",
767 )
768 .expect_ok(&ctx);
769
770 let err_msg = format!(
771 "{}",
772 parse_from_str(
773 type_parser(),
774 &mut ctx,
775 "llvm.struct < My1 { builtin.integer i16 } : Unpacked>",
776 )
777 .unwrap_err()
778 );
779
780 let expected_err_msg = expect![[r#"
781 Compilation error: invalid input program.
782 Parse error at line: 1, column: 15
783 struct My1 already exists and is different
784 "#]];
785 expected_err_msg.assert_eq(&err_msg);
786 }
787
788 #[test]
789 fn test_functype_parsing() {
790 let mut ctx = Context::new();
791
792 let si32 = IntegerType::get(&ctx, 32, Signedness::Signed);
793
794 let input = "llvm.func <llvm.void (builtin.integer si32) variadic = false>";
795 let res = parse_from_str(type_parser().and(eof()), &mut ctx, input)
796 .expect_ok(&ctx)
797 .0;
798
799 let void_ty = VoidType::get(&ctx);
800 assert!(res == FuncType::get(&ctx, void_ty.to_handle(), vec![si32.into()], false).into());
801 assert_eq!(input, &res.disp(&ctx).to_string());
802 }
803}