Skip to main content

pliron_llvm/
types.rs

1// SPDX-License-Identifier: Apache-2.0
2// Copyright (c) The pliron contributors
3
4//! [Type]s defined in the LLVM dialect.
5
6use 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/// Layout of an LLVM struct type.
30#[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/// Represents a c-like struct type.
55/// Limitations and warnings on its usage are similar to that in MLIR.
56/// `<https://mlir.llvm.org/docs/Dialects/LLVM/#structure-types>`
57///   1. Anonymous (aka unnamed) structs cannot be recursive.
58///   2. Named structs are uniqued *only* by name, and may be recursive.
59///   3. LLVM calls anonymous structs as literal structs and
60///      named structs as identified structs.
61///   4. Named structs may be opaque, i.e., no body specificed.
62///      Recursive types may be created by first creating an opaque struct
63///      and later setting its body.
64#[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    /// Get or create a named StructType.
73    /// If `body` is `None`, it indicates an opaque struct.
74    /// A body can be added to opaque structs by calling this again later.
75    /// Returns an error if all of the below conditions are true:
76    ///   a. The name is already registered
77    ///   b. The body is already set (i.e, the struct is not oqaue)
78    ///   c. The body provided here don't match with the existing body.
79    /// Since named structs only rely on the name for uniqueness,
80    /// It is not an error to provide `body` as `None` even when
81    /// the named struct already exists and has its body set.
82    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                // Uniquing happens only on the name, so this doesn't matter.
91                body: None,
92            },
93            ctx,
94        );
95        // Verify that we created a new or equivalent existing type.
96        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            // We've been provided body to be set.
101            if let Some(existing_body) = &self_ref.body {
102                // Body was already set before, ensure it's same as the given one.
103                if existing_body != &body {
104                    input_err_noloc!(StructErr::ExistingMismatch(name.into()))?
105                }
106            } else {
107                // Set the body now.
108                self_ref.body = Some(body);
109            }
110        }
111        Ok(self_handle)
112    }
113
114    /// Get or create a new unnamed (anonymous) struct.
115    /// These are finalized upon creation, and uniqued based on the fields and layout.
116    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    /// A struct without a body set is opaque
127    pub fn is_opaque(&self) -> bool {
128        self.body.is_none()
129    }
130
131    /// Is this a named struct?
132    pub fn is_named(&self) -> bool {
133        self.name.is_some()
134    }
135
136    /// Get this struct's name, if it has one.
137    pub fn name(&self) -> Option<Identifier> {
138        self.name.clone()
139    }
140
141    /// Get this struct's layout.
142    ///
143    /// **Panics** if the struct is opaque.
144    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    /// Get type of the idx'th field.
152    ///
153    /// **Panics** if the struct is opaque or the index is invalid.
154    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    /// Get the number of fields this struct has
162    ///
163    /// **Panics** if the struct is opaque.
164    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    /// Get an iterator over the fields of this struct.
173    ///
174    /// **Panics** if the struct is opaque.
175    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
204/// Record that we're now printing `name`, returning `true` if it's already
205/// being printed higher up the stack (i.e., we've hit a recursive struct).
206fn 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        // We use a vec instead of a set hoping that this isn't
211        // going to be large, in which case vec would be faster.
212        .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
223// We're done printing `name`, so remove it from the list of "under printing" struct types.
224fn 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            // This is the first time we're seeing this struct in this session.
249            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        // Done processing this struct. Remove it from the stack.
266        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            // Parse multiple type annotated fields separated by ',', all of it delimited by braces.
310            delimited_list_parser('{', '}', ',', type_parser())
311                // followed by the layout
312                .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        // A struct type is named or anonymous.
322        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/// A pointer, corresponding to LLVM's pointer type. It is opaque (carries no
346/// pointee type) but carries an address space. The address space is always
347/// printed, e.g. `llvm.ptr (0)` or `llvm.ptr (1)`.
348#[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    /// The address space of this pointer.
361    pub fn address_space(&self) -> u32 {
362        self.address_space
363    }
364}
365
366/// Array type, corresponding to LLVM's array type.
367#[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    /// Get array element type.
381    pub fn elem_type(&self) -> TypeHandle {
382        self.elem
383    }
384
385    /// Get array size.
386    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    /// Result type
416    pub fn result_type(&self) -> TypeHandle {
417        self.res
418    }
419
420    /// Is this a variadic function type?
421    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/// Kind of vector type: fixed or scalable.
437/// See LLVM language reference for semantic details.
438#[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    /// Get the element type.
460    pub fn elem_type(&self) -> TypeHandle {
461        self.elem_ty
462    }
463
464    /// Get the number of elements.
465    pub fn num_elements(&self) -> u32 {
466        self.num_elems
467    }
468
469    /// Is this a scalable vector type?
470    pub fn is_scalable(&self) -> bool {
471        self.kind == VectorTypeKind::Scalable
472    }
473
474    /// Get the scalable/fixed kind of this vector type.
475    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        // Create an opaque struct since we want a recursive type.
508        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        // Set the struct body now.
520        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        // It's okay to get it again without providing the body.
534
535        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        // Re-setting the same body on an already-set named struct is fine.
544        StructType::get_named(
545            &ctx,
546            linked_list_id.clone(),
547            Some((vec![int64, list_struct_ptr], StructLayout::Unpacked)),
548        )?;
549
550        // But changing just the layout, with fields unchanged, is a mismatch.
551        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        // Or changing just the fields, with layout unchanged, is a mismatch.
561        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        // Anonymous structs with the same fields but different layout are
578        // distinct types.
579        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    /// A pointer type that knows the type it points to.
590    /// This used to be in LLVM earlier, but the latest version
591    /// is now type-erased (https://llvm.org/docs/OpaquePointers.html)
592    #[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        /// Get the pointee type.
601        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        // The address space is always printed, so addrspace 0 round-trips as
683        // `llvm.ptr (0)`.
684        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        // A non-zero address space round-trips as `llvm.ptr (N)`.
695        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        // Test parsing an unpacked named struct
711        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        // Test parsing an opaque struct.
716        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        // Test parsing an unpacked unnamed struct.
726        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        // Test parsing a packed unnamed struct.
737        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        // Test parsing a packed named struct.
748        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}