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    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/// Layout of an LLVM struct type.
31#[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/// Represents a c-like struct type.
56/// Limitations and warnings on its usage are similar to that in MLIR.
57/// `<https://mlir.llvm.org/docs/Dialects/LLVM/#structure-types>`
58///   1. Anonymous (aka unnamed) structs cannot be recursive.
59///   2. Named structs are uniqued *only* by name, and may be recursive.
60///   3. LLVM calls anonymous structs as literal structs and
61///      named structs as identified structs.
62///   4. Named structs may be opaque, i.e., no body specificed.
63///      Recursive types may be created by first creating an opaque struct
64///      and later setting its body.
65#[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    /// Get or create a named StructType.
74    /// If `body` is `None`, it indicates an opaque struct.
75    /// A body can be added to opaque structs by calling this again later.
76    /// Returns an error if all of the below conditions are true:
77    ///   a. The name is already registered
78    ///   b. The body is already set (i.e, the struct is not oqaue)
79    ///   c. The body provided here don't match with the existing body.
80    /// Since named structs only rely on the name for uniqueness,
81    /// It is not an error to provide `body` as `None` even when
82    /// the named struct already exists and has its body set.
83    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                // Uniquing happens only on the name, so this doesn't matter.
92                body: None,
93            },
94            ctx,
95        );
96        // Verify that we created a new or equivalent existing type.
97        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            // We've been provided body to be set.
102            if let Some(existing_body) = &self_ref.body {
103                // Body was already set before, ensure it's same as the given one.
104                if existing_body != &body {
105                    input_err_noloc!(StructErr::ExistingMismatch(name.into()))?
106                }
107            } else {
108                // Set the body now.
109                self_ref.body = Some(body);
110            }
111        }
112        Ok(self_handle)
113    }
114
115    /// Get or create a new unnamed (anonymous) struct.
116    /// These are finalized upon creation, and uniqued based on the fields and layout.
117    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    /// A struct without a body set is opaque
128    pub fn is_opaque(&self) -> bool {
129        self.body.is_none()
130    }
131
132    /// Is this a named struct?
133    pub fn is_named(&self) -> bool {
134        self.name.is_some()
135    }
136
137    /// Get this struct's name, if it has one.
138    pub fn name(&self) -> Option<Identifier> {
139        self.name.clone()
140    }
141
142    /// Get this struct's layout.
143    ///
144    /// **Panics** if the struct is opaque.
145    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    /// Get type of the idx'th field.
153    ///
154    /// **Panics** if the struct is opaque or the index is invalid.
155    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    /// Get the number of fields this struct has
163    ///
164    /// **Panics** if the struct is opaque.
165    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    /// Get an iterator over the fields of this struct.
174    ///
175    /// **Panics** if the struct is opaque.
176    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
205/// Record that we're now printing `name`, returning `true` if it's already
206/// being printed higher up the stack (i.e., we've hit a recursive struct).
207fn 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        // We use a vec instead of a set hoping that this isn't
212        // going to be large, in which case vec would be faster.
213        .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
224// We're done printing `name`, so remove it from the list of "under printing" struct types.
225fn 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            // This is the first time we're seeing this struct in this session.
250            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        // Done processing this struct. Remove it from the stack.
267        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            // Parse multiple type annotated fields separated by ',', all of it delimited by braces.
311            delimited_list_parser('{', '}', ',', type_parser())
312                // followed by the layout
313                .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        // A struct type is named or anonymous.
323        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/// A pointer, corresponding to LLVM's pointer type. It is opaque (carries no
347/// pointee type) but carries an address space. The address space is always
348/// printed, e.g. `llvm.ptr (0)` or `llvm.ptr (1)`.
349#[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    /// The address space of this pointer.
362    pub fn address_space(&self) -> u32 {
363        self.address_space
364    }
365}
366
367/// Array type, corresponding to LLVM's array type.
368#[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    /// Get array element type.
382    pub fn elem_type(&self) -> TypeHandle {
383        self.elem
384    }
385
386    /// Get array size.
387    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    /// Result type
417    pub fn result_type(&self) -> TypeHandle {
418        self.res
419    }
420
421    /// Is this a variadic function type?
422    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/// Kind of vector type: fixed or scalable.
438/// See LLVM language reference for semantic details.
439#[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    /// Get the element type.
461    pub fn elem_type(&self) -> TypeHandle {
462        self.elem_ty
463    }
464
465    /// Get the number of elements.
466    pub fn num_elements(&self) -> u32 {
467        self.num_elems
468    }
469
470    /// Is this a scalable vector type?
471    pub fn is_scalable(&self) -> bool {
472        self.kind == VectorTypeKind::Scalable
473    }
474
475    /// Get the scalable/fixed kind of this vector type.
476    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        // 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_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        // Test parsing an unpacked named struct
723        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        // Test parsing an opaque struct.
728        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        // Test parsing an unpacked unnamed struct.
738        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        // Test parsing a packed unnamed struct.
749        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        // Test parsing a packed named struct.
760        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}