Skip to main content

pliron/
type.rs

1// SPDX-License-Identifier: Apache-2.0
2// Copyright (c) The pliron contributors
3
4//! Every SSA value, such as operation results or block arguments
5//! has a type defined by the type system.
6//!
7//! The type system is open, with no fixed list of types,
8//! and there are no restrictions on the abstractions they represent.
9//!
10//! See [MLIR Types](https://mlir.llvm.org/docs/DefiningDialects/TypesAndTypes/#types)
11//!
12//! The [pliron_type](pliron::derive::pliron_type) proc macro from [pliron-derive]
13//! can be used to implement [Type] for a rust type.
14//!
15//! Common semantics, API and behaviour of [Type]s are
16//! abstracted into interfaces. Interfaces in pliron capture MLIR
17//! functionality of both [Traits](https://mlir.llvm.org/docs/Traits/)
18//! and [Interfaces](https://mlir.llvm.org/docs/Interfaces/).
19//! Interfaces must all implement an associated function named `verify` with
20//! the type [TypeInterfaceVerifier].
21//!
22//! Interfaces are rust Trait definitions annotated with the attribute macro
23//! [type_interface](pliron::derive::type_interface). The attribute ensures that any
24//! verifiers of super-interfaces are run prior to the verifier of this interface.
25//! Note: Super-interface verifiers *may* run multiple times for the same type.
26//!
27//! [Type]s that implement an interface must annotate the implementation with
28//! [type_interface_impl](pliron::derive::type_interface_impl) macro to ensure that
29//! the interface verifier is automatically called during verification
30//! and that a `&dyn Type` object can be [cast](type_cast) into an interface object,
31//! (or that it can be checked if the interface is [implemented](type_impls))
32//! with ease.
33//!
34//! Use [verify_type] to verify a [Type] object.
35//! This function verifies all interfaces implemented by the type, and then the type itself.
36//! The type's verifier must explicitly invoke verifiers on any sub-objects it contains.
37//!
38//! [TypeHandle]s can be [TypeHandle::deref]'d and downcasted to their concrete types using
39//! [downcast_rs](https://docs.rs/downcast-rs/latest/downcast_rs/#example-without-generics).
40
41use crate::{
42    arg_err_noloc,
43    combine::{Parser, parser},
44    common_traits::Verify,
45    context::{Context, collect_deduped_interface_verifiers},
46    dialect::{Dialect, DialectName},
47    identifier::Identifier,
48    impl_printable_for_display, input_err,
49    irfmt::parsers::spaced,
50    location::Located,
51    parsable::{Parsable, ParseResult, StateStream},
52    printable::{self, Printable},
53    result::{Error, Result},
54    std_deps::{hash::FxHashMap, sync::LazyLock},
55    storage_uniquer::TypeValueHash,
56    utils::trait_cast::impls_trait_static,
57};
58
59use alloc::{
60    boxed::Box,
61    string::{String, ToString},
62    vec::Vec,
63};
64use core::{
65    cell::{Ref, RefCell, RefMut},
66    fmt::{Debug, Display},
67    hash::{Hash, Hasher},
68    marker::PhantomData,
69    ops::Deref,
70};
71use downcast_rs::{Downcast, impl_downcast};
72use pliron_derive::format;
73use thiserror::Error;
74
75/// Basic functionality that every type in the IR must implement.
76/// Type objects (instances of a Type) are (mostly) immutable once created,
77/// and are uniqued globally. Uniquing is based on the type name (i.e.,
78/// the rust type being defined) and its contents.
79///
80/// So, for example, if we have
81/// ```rust
82///     # use pliron::derive::pliron_type;
83///     #[pliron_type(
84///         name = "test.intty",
85///         format,
86///         verifier = "succ"
87///     )]
88///     #[derive(Debug, PartialEq, Eq, Hash)]
89///     struct IntType {
90///         width: u64
91///     }
92/// ```
93/// the uniquing will include
94///   - [`core::any::TypeId::of::<IntType>()`](core::any::TypeId)
95///   - `width`
96///
97/// Types *can* have mutable contents that can be modified *after*
98/// the type is created. This enables creation of recursive types.
99/// In such a case, it is up to the type definition to ensure that
100///   1. It manually implements Hash, ignoring these mutable fields.
101///   2. A proper distinguisher content (such as a string), that is part
102///      of the hash, is used so that uniquing still works.
103pub trait Type: Printable + Verify + Downcast + Sync + Send + Debug {
104    /// Compute and get the hash for this instance of Self.
105    /// Hash collisions can be a possibility.
106    fn hash_type(&self) -> TypeValueHash;
107    /// Is self equal to an other Type?
108    fn eq_type(&self, other: &dyn Type) -> bool;
109
110    /// Get a copyable handle to this type.
111    // Unlike in [ArenaObj]s, we do not store a self handle inside the object itself
112    // because that can upset taking automatic hashes of the object.
113    fn get_self_handle(&self, ctx: &Context) -> TypeHandle {
114        let is = |other: &TypeObj| self.eq_type(&**other.0.borrow());
115        let idx = ctx
116            .type_store
117            .get(self.hash_type(), &is)
118            .expect("Unregistered type object in existence");
119        TypeHandle(idx)
120    }
121
122    /// Instantiate a type in the provided [Context], returning a [TypeHandle] to self.
123    fn instantiate(t: Self, ctx: &Context) -> TypedHandle<Self>
124    where
125        Self: Sized,
126    {
127        let hash = t.hash_type();
128        let idx = ctx.type_store.get_or_create_unique(
129            TypeObj(RefCell::new(Box::new(t))),
130            hash,
131            &TypeObj::eq,
132        );
133        TypedHandle(TypeHandle(idx), PhantomData::<Self>)
134    }
135
136    /// Get a Type's static name. This is *not* per instantiation of the type.
137    /// It is mostly useful for printing and parsing the type.
138    /// Uniquing does *not* use this, but instead uses [core::any::TypeId].
139    fn get_type_id(&self) -> TypeId;
140
141    /// Same as [get_type_id](Self::get_type_id), but without the self reference.
142    fn get_type_id_static() -> TypeId
143    where
144        Self: Sized;
145
146    #[doc(hidden)]
147    /// Verify all interfaces implemented by this Type.
148    fn verify_interfaces(&self, ctx: &Context) -> Result<()>;
149
150    /// Register this Type's [TypeId] in the dialect it belongs to.
151    fn register(ctx: &mut Context)
152    where
153        Self: Sized + Parsable<Arg = (), Parsed = TypedHandle<Self>>,
154    {
155        let ptr_parser: TypeParserFn = |parsable_state, &()| {
156            Self::parse(parsable_state, ())
157                .map(|(typtr, r)| -> (TypeHandle, _) { (typtr.to_handle(), r) })
158        };
159        let typeid = Self::get_type_id_static();
160        Dialect::register(ctx, &typeid.dialect.clone()).add_type(typeid, ptr_parser);
161    }
162}
163impl_downcast!(Type);
164
165/// A storable function pointer to parse a specific [Type].
166/// The [Type]'s [Dialect] maps a [TypeId] to such a parser.
167pub(crate) type TypeParserFn =
168    for<'a> fn(&mut StateStream<'a>, &'a ()) -> ParseResult<'a, TypeHandle>;
169
170/// Trait for IR entities that have a direct type.
171pub trait Typed {
172    /// Get the [Type] of the current entity.
173    fn get_type(&self, ctx: &Context) -> TypeHandle;
174}
175
176impl Typed for TypeHandle {
177    fn get_type(&self, _ctx: &Context) -> TypeHandle {
178        *self
179    }
180}
181
182impl Typed for dyn Type {
183    fn get_type(&self, ctx: &Context) -> TypeHandle {
184        self.get_self_handle(ctx)
185    }
186}
187
188impl<T: Typed + ?Sized> Typed for &T {
189    fn get_type(&self, ctx: &Context) -> TypeHandle {
190        (*self).get_type(ctx)
191    }
192}
193
194impl<T: Typed + ?Sized> Typed for &mut T {
195    fn get_type(&self, ctx: &Context) -> TypeHandle {
196        (**self).get_type(ctx)
197    }
198}
199
200impl<T: Typed + ?Sized> Typed for Box<T> {
201    fn get_type(&self, ctx: &Context) -> TypeHandle {
202        (**self).get_type(ctx)
203    }
204}
205
206#[derive(Clone, Hash, PartialEq, Eq)]
207/// A Type's name (not including it's dialect).
208pub struct TypeName(Identifier);
209
210impl TypeName {
211    /// Create a new TypeName.
212    pub fn try_new(name: &str) -> Result<TypeName> {
213        Identifier::try_from(name).map(TypeName)
214    }
215}
216
217impl From<Identifier> for TypeName {
218    fn from(name: Identifier) -> Self {
219        TypeName(name)
220    }
221}
222
223impl From<TypeName> for Identifier {
224    fn from(name: TypeName) -> Self {
225        name.0
226    }
227}
228
229impl TryFrom<&str> for TypeName {
230    type Error = Error;
231
232    fn try_from(value: &str) -> Result<Self> {
233        Identifier::try_from(value).map(TypeName)
234    }
235}
236
237impl TryFrom<String> for TypeName {
238    type Error = Error;
239
240    fn try_from(value: String) -> Result<Self> {
241        Identifier::try_from(value).map(TypeName)
242    }
243}
244
245impl Deref for TypeName {
246    type Target = Identifier;
247
248    fn deref(&self) -> &Self::Target {
249        &self.0
250    }
251}
252
253impl_printable_for_display!(TypeName);
254
255impl Display for TypeName {
256    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
257        write!(f, "{}", self.0)
258    }
259}
260
261impl Parsable for TypeName {
262    type Arg = ();
263    type Parsed = TypeName;
264
265    fn parse<'a>(
266        state_stream: &mut crate::parsable::StateStream<'a>,
267        _arg: Self::Arg,
268    ) -> ParseResult<'a, Self::Parsed>
269    where
270        Self: Sized,
271    {
272        Identifier::parser(())
273            .map(TypeName)
274            .parse_stream(state_stream)
275            .into()
276    }
277}
278
279/// A combination of a Type's name and its dialect.
280#[derive(Clone, Hash, PartialEq, Eq)]
281pub struct TypeId {
282    pub dialect: DialectName,
283    pub name: TypeName,
284}
285
286impl Parsable for TypeId {
287    type Arg = ();
288    type Parsed = TypeId;
289
290    // Parses (but does not validate) a TypeId.
291    fn parse<'a>(
292        state_stream: &mut StateStream<'a>,
293        _arg: Self::Arg,
294    ) -> ParseResult<'a, Self::Parsed>
295    where
296        Self: Sized,
297    {
298        let mut parser = DialectName::parser(())
299            .skip(parser::char::char('.'))
300            .and(TypeName::parser(()))
301            .map(|(dialect, name)| TypeId { dialect, name });
302        parser.parse_stream(state_stream).into()
303    }
304}
305
306impl_printable_for_display!(TypeId);
307
308impl Display for TypeId {
309    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
310        write!(f, "{}.{}", self.dialect, self.name)
311    }
312}
313
314/// An instance of a [Type] stored in the [Context]'s type store.
315pub(crate) struct TypeObj(RefCell<Box<dyn Type>>);
316
317impl PartialEq for TypeObj {
318    fn eq(&self, other: &Self) -> bool {
319        self.0.borrow().eq_type(&**other.0.borrow())
320    }
321}
322
323impl Eq for TypeObj {}
324
325impl Hash for TypeObj {
326    fn hash<H: Hasher>(&self, state: &mut H) {
327        state.write(&u64::from(self.0.borrow().hash_type()).to_ne_bytes())
328    }
329}
330
331/// A handle to the uniqued [Type] objects stored in the [Context].
332#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
333pub struct TypeHandle(usize);
334
335impl TypeHandle {
336    /// Get a reference to the underlying [Type] object.
337    pub fn deref<'a>(&self, ctx: &'a Context) -> Ref<'a, dyn Type> {
338        Ref::map(
339            ctx.type_store.unique_store.get(self.0).unwrap().0.borrow(),
340            |t| &**t,
341        )
342    }
343
344    /// Get a mutable reference to the underlying [Type] object.
345    /// This is useful when building recursive types, and the caller must ensure
346    /// that the mutation does not change the hash-relevant contents of the type.
347    pub fn deref_mut<'a>(&self, ctx: &'a Context) -> RefMut<'a, dyn Type> {
348        RefMut::map(
349            ctx.type_store
350                .unique_store
351                .get(self.0)
352                .unwrap()
353                .0
354                .borrow_mut(),
355            |t| &mut **t,
356        )
357    }
358}
359
360impl Printable for TypeHandle {
361    fn fmt(
362        &self,
363        ctx: &Context,
364        state: &printable::State,
365        f: &mut core::fmt::Formatter<'_>,
366    ) -> core::fmt::Result {
367        write!(f, "{} ", self.deref(ctx).get_type_id())?;
368        Printable::fmt(&*self.deref(ctx), ctx, state, f)
369    }
370}
371
372impl Verify for TypeHandle {
373    fn verify(&self, ctx: &Context) -> Result<()> {
374        verify_type(&*self.deref(ctx), ctx)
375    }
376}
377
378impl Parsable for TypeHandle {
379    type Arg = ();
380    type Parsed = Self;
381
382    fn parse<'a>(
383        state_stream: &mut StateStream<'a>,
384        _arg: Self::Arg,
385    ) -> ParseResult<'a, Self::Parsed> {
386        let loc = state_stream.loc();
387        let type_id_parser = spaced(TypeId::parser(()));
388
389        let mut type_parser = type_id_parser.then(move |type_id: TypeId| {
390            // This clone is to satify the borrow checker.
391            let loc = loc.clone();
392            combine::parser(move |parsable_state: &mut StateStream<'a>| {
393                let state = &parsable_state.state;
394                let dialect = state
395                    .ctx
396                    .dialects
397                    .get(&type_id.dialect)
398                    .expect("Dialect name parsed but dialect isn't registered");
399                let Some(type_parser) = dialect.types.get(&type_id) else {
400                    input_err!(loc.clone(), "Unregistered type {}", type_id.disp(state.ctx))?
401                };
402                type_parser(parsable_state, &())
403            })
404        });
405
406        type_parser.parse_stream(state_stream).into_result()
407    }
408}
409
410/// Verify a [Type] object:
411/// 1. All interfaces it implements are verified
412/// 2. The type itself is verified.
413pub fn verify_type(ty: &dyn Type, ctx: &Context) -> Result<()> {
414    // Verify all interfaces implemented by this Type.
415    ty.verify_interfaces(ctx)?;
416
417    // Verify the type itself.
418    Verify::verify(ty, ctx)
419}
420
421impl Verify for TypeObj {
422    fn verify(&self, ctx: &Context) -> Result<()> {
423        verify_type(self.0.borrow().as_ref(), ctx)
424    }
425}
426
427/// A wrapper around [TypeHandle] with the underlying [Type] statically marked.
428#[derive(Debug)]
429pub struct TypedHandle<T: Type>(TypeHandle, PhantomData<T>);
430
431#[derive(Error, Debug)]
432#[error("TypedHandle mismatch: Constructing {expected} but provided {provided}")]
433pub struct TypedHandleErr {
434    pub expected: String,
435    pub provided: String,
436}
437
438impl<T: Type> TypedHandle<T> {
439    /// Return a [Ref] to the [Type]
440    /// This borrows from a RefCell and the borrow is live
441    /// as long as the returned [Ref] lives.
442    pub fn deref<'a>(&self, ctx: &'a Context) -> Ref<'a, T> {
443        Ref::map(self.0.deref(ctx), |t| {
444            t.downcast_ref::<T>()
445                .expect("Type mistmatch, inconsistent TypedHandle")
446        })
447    }
448
449    /// Create a new [TypedHandle] from a [TypeHandle].
450    pub fn from_handle(handle: TypeHandle, ctx: &Context) -> Result<TypedHandle<T>> {
451        if handle.deref(ctx).is::<T>() {
452            Ok(TypedHandle(handle, PhantomData::<T>))
453        } else {
454            arg_err_noloc!(TypedHandleErr {
455                expected: T::get_type_id_static().disp(ctx).to_string(),
456                provided: handle.disp(ctx).to_string()
457            })
458        }
459    }
460
461    /// Erase the static Rust type and return the underlying [TypeHandle].
462    pub fn to_handle(&self) -> TypeHandle {
463        self.0
464    }
465}
466
467impl<T: Type> From<TypedHandle<T>> for TypeHandle {
468    fn from(value: TypedHandle<T>) -> Self {
469        value.to_handle()
470    }
471}
472
473impl<T: Type> Clone for TypedHandle<T> {
474    fn clone(&self) -> TypedHandle<T> {
475        *self
476    }
477}
478
479impl<T: Type> Copy for TypedHandle<T> {}
480
481impl<T: Type> PartialEq for TypedHandle<T> {
482    fn eq(&self, other: &Self) -> bool {
483        self.0 == other.0
484    }
485}
486
487impl<T: Type> Eq for TypedHandle<T> {}
488
489impl<T: Type> Hash for TypedHandle<T> {
490    fn hash<H: core::hash::Hasher>(&self, state: &mut H) {
491        self.0.hash(state);
492    }
493}
494
495impl<T: Type> Printable for TypedHandle<T> {
496    fn fmt(
497        &self,
498        ctx: &Context,
499        state: &printable::State,
500        f: &mut core::fmt::Formatter<'_>,
501    ) -> core::fmt::Result {
502        Printable::fmt(&self.0, ctx, state, f)
503    }
504}
505
506impl<T: Type + Parsable<Arg = (), Parsed = TypedHandle<T>>> Parsable for TypedHandle<T> {
507    type Arg = ();
508    type Parsed = Self;
509
510    fn parse<'a>(
511        state_stream: &mut StateStream<'a>,
512        arg: Self::Arg,
513    ) -> ParseResult<'a, Self::Parsed> {
514        let loc = state_stream.loc();
515        spaced(TypeId::parser(()))
516            .then(move |type_id| {
517                let loc = loc.clone();
518                combine::parser(move |parsable_state: &mut StateStream<'a>| {
519                    if type_id != T::get_type_id_static() {
520                        input_err!(
521                            loc.clone(),
522                            "Expected type {}, but found {}",
523                            T::get_type_id_static().disp(parsable_state.state.ctx),
524                            type_id.disp(parsable_state.state.ctx)
525                        )?
526                    }
527                    T::parser(arg).parse_stream(parsable_state).into()
528                })
529            })
530            .parse_stream(state_stream)
531            .into_result()
532    }
533}
534
535impl<T: Type> Verify for TypedHandle<T> {
536    fn verify(&self, ctx: &Context) -> Result<()> {
537        self.0.deref(ctx).verify(ctx)
538    }
539}
540
541/// Marker trait for type interface trait objects.
542///
543/// This is auto-implemented by the `#[type_interface]` macro for `dyn Interface`
544/// objects and is used to restrict [type_cast] and [type_impls] to interface casts.
545#[diagnostic::on_unimplemented(
546    message = "`{Self}` not a type interface.",
547    label = "If `{Self}` is a trait, annotate it with #[type_interface] to be able to cast to it from a `&dyn Type`",
548    note = "If you want to cast to a concrete `Type`, use `downcast_ref` instead."
549)]
550pub trait TypeInterfaceMarker {}
551
552/// Cast reference to a [Type] object to an interface reference.
553///
554/// Right usage: cast to an interface trait object.
555/// ```
556/// use pliron::builtin::type_interfaces::FunctionTypeInterface;
557/// use pliron::r#type::{Type, type_cast};
558///
559/// fn right_cast(ty: &dyn Type) {
560///     let _ = type_cast::<dyn FunctionTypeInterface>(ty);
561/// }
562/// ```
563///
564/// Casting to concrete [Type] types are intentionally rejected.
565/// ```compile_fail
566/// use pliron::builtin::types::IntegerType;
567/// use pliron::r#type::{Type, type_cast};
568///
569/// fn wrong_cast(ty: &dyn Type) {
570///     let _ = type_cast::<IntegerType>(ty);
571/// }
572/// ```
573/// Use [downcast_rs](https://docs.rs/downcast-rs/latest/downcast_rs/#example-without-generics)
574/// to cast to concrete [Type] types.
575pub fn type_cast<T: ?Sized + TypeInterfaceMarker + 'static>(ty: &dyn Type) -> Option<&T> {
576    crate::utils::trait_cast::any_to_trait::<T>(ty.as_any())
577}
578
579/// Does this [Type] object implement interface `T`?
580///
581/// Right usage: query using an interface trait object.
582/// ```
583/// use pliron::builtin::type_interfaces::FunctionTypeInterface;
584/// use pliron::r#type::{Type, type_impls};
585///
586/// fn right_query(ty: &dyn Type) {
587///     let _ = type_impls::<dyn FunctionTypeInterface>(ty);
588/// }
589/// ```
590///
591/// Querying with a concrete [Type] type is intentionally rejected.
592/// ```compile_fail
593/// use pliron::builtin::types::IntegerType;
594/// use pliron::r#type::{Type, type_impls};
595///
596/// fn wrong_query(ty: &dyn Type) {
597///     let _ = type_impls::<IntegerType>(ty);
598/// }
599/// ```
600pub fn type_impls<T: ?Sized + TypeInterfaceMarker + 'static>(ty: &dyn Type) -> bool {
601    type_cast::<T>(ty).is_some()
602}
603
604/// Does [Type] `T` implement interface `I`?
605/// See also: [`type_impls`]
606///
607/// Example:
608/// ```
609/// use pliron::builtin::type_interfaces::{FunctionTypeInterface, FloatTypeInterface};
610/// use pliron::builtin::types::FunctionType;
611/// use pliron::r#type::{Type, type_impls_static};
612/// assert!(type_impls_static::<FunctionType, dyn FunctionTypeInterface>());
613/// assert!(!type_impls_static::<FunctionType, dyn FloatTypeInterface>());
614/// ```
615pub fn type_impls_static<T: Type, I: ?Sized + TypeInterfaceMarker + 'static>() -> bool {
616    impls_trait_static::<T, I>()
617}
618
619/// Every type interface must have a function named `verify` with this type.
620pub type TypeInterfaceVerifier = fn(&dyn Type, &Context) -> Result<()>;
621/// Function returns the list of super verifiers, followed by a self verifier, for an interface.
622pub type TypeInterfaceAllVerifiers = fn() -> Vec<TypeInterfaceVerifier>;
623
624#[doc(hidden)]
625/// A [Type] paired with an interface it implements
626/// (specifically the verifiers (including super verifiers) for that interface).
627type TypeInterfaceVerifierInfo = (core::any::TypeId, TypeInterfaceAllVerifiers);
628
629#[doc(hidden)]
630#[cfg(not(target_family = "wasm"))]
631pub mod statics {
632    use super::*;
633
634    #[::pliron::linkme::distributed_slice]
635    pub static TYPE_INTERFACE_VERIFIERS: [TypeInterfaceVerifierInfo] = [..];
636
637    pub(super) fn get_type_interface_verifiers()
638    -> impl Iterator<Item = &'static TypeInterfaceVerifierInfo> {
639        TYPE_INTERFACE_VERIFIERS.iter()
640    }
641}
642#[doc(hidden)]
643#[cfg(not(target_family = "wasm"))]
644pub use statics::TYPE_INTERFACE_VERIFIERS;
645
646#[doc(hidden)]
647#[cfg(target_family = "wasm")]
648pub mod statics {
649    use super::*;
650    use crate::InventoryWrapper;
651
652    ::pliron::inventory::collect!(InventoryWrapper<TypeInterfaceVerifierInfo>);
653
654    pub(super) fn get_type_interface_verifiers()
655    -> impl Iterator<Item = &'static TypeInterfaceVerifierInfo> {
656        ::pliron::inventory::iter::<InventoryWrapper<TypeInterfaceVerifierInfo>>().map(|llw| llw.0)
657    }
658}
659
660#[doc(hidden)]
661/// A map from every [Type] to its ordered (as per interface deps) list of interface verifiers.
662/// An interface's super-interfaces are to be verified before it itself is.
663pub static TYPE_INTERFACE_VERIFIERS_MAP: LazyLock<
664    FxHashMap<core::any::TypeId, Vec<TypeInterfaceVerifier>>,
665> = LazyLock::new(|| collect_deduped_interface_verifiers(statics::get_type_interface_verifiers()));
666
667/// A convenient struct to hold a type signature.
668
669#[derive(Debug, Clone, PartialEq, Eq, Hash)]
670#[format("`(` vec($arguments, CharSpace(`,`)) `)` ` -> ` `(`vec($results, CharSpace(`,`)) `)`")]
671pub struct TypeSig {
672    pub arguments: Vec<TypeHandle>,
673    pub results: Vec<TypeHandle>,
674}