Skip to main content

pliron_llvm/
op_interfaces.rs

1// SPDX-License-Identifier: Apache-2.0
2// Copyright (c) The pliron contributors
3
4//! [Op] Interfaces defined in the LLVM dialect.
5
6use alloc::{
7    string::{String, ToString},
8    vec,
9};
10
11use pliron::{
12    builtin::{
13        attributes::BoolAttr,
14        op_interfaces::{
15            NOpdsInterface, OneOpdInterface, ResultNOfType, SymbolOpInterface,
16            verify_get_operand_n, verify_get_result_n,
17        },
18        type_interfaces::FloatTypeInterface,
19    },
20    derive::op_interface,
21    dict_key,
22    printable::Printable,
23    r#type::{Type, TypeInterfaceHandle, TypeInterfaceMarker, TypedHandle, type_impls},
24    utils::const_bound_n::I,
25};
26use thiserror::Error;
27
28use pliron::{
29    builtin::{
30        op_interfaces::{OneResultInterface, SameOperandsAndResultType},
31        types::{IntegerType, Signedness},
32    },
33    context::Context,
34    location::Location,
35    op::{Op, op_cast},
36    operation::Operation,
37    result::Result,
38    r#type::{TypeHandle, Typed},
39    value::Value,
40    verify_err,
41};
42
43use crate::{
44    attributes::{AlignmentAttr, FastmathFlagsAttr, SyncScopeAttr},
45    types::{VectorType, VectorTypeKind},
46};
47
48use super::{attributes::IntegerOverflowFlagsAttr, types::PointerType};
49
50/// Binary arithmetic [Op].
51#[op_interface]
52pub trait BinArithOp: SameOperandsAndResultType + OneResultInterface + NOpdsInterface<2> {
53    /// Create a new binary arithmetic operation given the operands.
54    fn new(ctx: &mut Context, lhs: Value, rhs: Value) -> Self
55    where
56        Self: Sized,
57    {
58        Operation::new_op::<Self>(ctx, vec![lhs.get_type(ctx)], vec![lhs, rhs], vec![], 0)
59    }
60
61    fn verify(_op: &dyn Op, _ctx: &Context) -> Result<()>
62    where
63        Self: Sized,
64    {
65        Ok(())
66    }
67
68    /// Get the left-hand side operand of this binary arithmetic [Op].
69    fn lhs(&self, ctx: &Context) -> Value
70    where
71        Self: Sized,
72    {
73        self.get_operand_i(ctx, I::<0>.into())
74    }
75
76    /// Get the right-hand side operand of this binary arithmetic [Op].
77    fn rhs(&self, ctx: &Context) -> Value
78    where
79        Self: Sized,
80    {
81        self.get_operand_i(ctx, I::<1>.into())
82    }
83}
84
85#[derive(Error, Debug)]
86#[error("Integer binary arithmetic Op can only have signless integer result/operand type")]
87pub struct IntBinArithOpErr;
88
89/// Integer binary arithmetic [Op]
90#[op_interface]
91pub trait IntBinArithOp: BinArithOp + ScalarOrVectorOpd<IntegerType, 0> {
92    fn verify(op: &dyn Op, ctx: &Context) -> Result<()>
93    where
94        Self: Sized,
95    {
96        let int_ty = op_cast::<dyn ScalarOrVectorOpd<IntegerType, 0>>(op)
97            .expect("Op must impl ScalarOrVectorOpd<IntegerType, 0>")
98            .scalar_or_vector_elem_ty(ctx);
99        let int_ty = int_ty.deref(ctx);
100        if int_ty.signedness() != Signedness::Signless {
101            return verify_err!(op.loc(ctx), IntBinArithOpErr);
102        }
103
104        Ok(())
105    }
106}
107
108dict_key!(
109    /// Attribute key for integer overflow flags.
110    ATTR_KEY_INTEGER_OVERFLOW_FLAGS,
111    "llvm_integer_overflow_flags"
112);
113
114#[derive(Error, Debug)]
115#[error("IntegerOverflowFlag missing on Op")]
116pub struct IntBinArithOpWithOverflowFlagErr;
117
118/// Integer binary arithmetic [Op] with [IntegerOverflowFlagsAttr]
119#[op_interface]
120pub trait IntBinArithOpWithOverflowFlag: IntBinArithOp {
121    /// Create a new integer binary op with overflow flags set.
122    fn new_with_overflow_flag(
123        ctx: &mut Context,
124        lhs: Value,
125        rhs: Value,
126        flag: IntegerOverflowFlagsAttr,
127    ) -> Self
128    where
129        Self: Sized,
130    {
131        let op = Self::new(ctx, lhs, rhs);
132        op.set_integer_overflow_flag(ctx, flag);
133        op
134    }
135
136    /// Get the integer overflow flag on this [Op].
137    fn integer_overflow_flag(&self, ctx: &Context) -> IntegerOverflowFlagsAttr
138    where
139        Self: Sized,
140    {
141        self.get_operation()
142            .deref(ctx)
143            .attributes
144            .get::<IntegerOverflowFlagsAttr>(&ATTR_KEY_INTEGER_OVERFLOW_FLAGS)
145            .expect("Integer overflow flag missing or is of incorrect type")
146            .clone()
147    }
148
149    /// Set the integer overflow flag for this [Op].
150    fn set_integer_overflow_flag(&self, ctx: &Context, flag: IntegerOverflowFlagsAttr)
151    where
152        Self: Sized,
153    {
154        self.get_operation()
155            .deref_mut(ctx)
156            .attributes
157            .set(ATTR_KEY_INTEGER_OVERFLOW_FLAGS.clone(), flag);
158    }
159
160    fn verify(op: &dyn Op, ctx: &Context) -> Result<()>
161    where
162        Self: Sized,
163    {
164        let op = op.get_operation().deref(ctx);
165        if op
166            .attributes
167            .get::<IntegerOverflowFlagsAttr>(&ATTR_KEY_INTEGER_OVERFLOW_FLAGS)
168            .is_none()
169        {
170            return verify_err!(op.loc(), IntBinArithOpWithOverflowFlagErr);
171        }
172
173        Ok(())
174    }
175}
176
177/// Floating point binary arithmetic [Op]
178#[op_interface]
179pub trait FloatBinArithOp: BinArithOp + ScalarOrVectorOpdImpls<dyn FloatTypeInterface, 0> {
180    fn verify(_op: &dyn Op, _ctx: &Context) -> Result<()>
181    where
182        Self: Sized,
183    {
184        Ok(())
185    }
186}
187
188dict_key!(
189    /// Attribute key for fastmath flags.
190    ATTR_KEY_FAST_MATH_FLAGS,
191    "llvm_fast_math_flags"
192);
193
194#[derive(Error, Debug)]
195#[error("Fastmath flag missing on Op")]
196pub struct FastMathFlagMissingErr;
197
198/// Ops that have fast math flags.
199#[op_interface]
200pub trait FastMathFlags {
201    /// Get the fast math flags on this [Op].
202    fn fast_math_flags(&self, ctx: &Context) -> FastmathFlagsAttr
203    where
204        Self: Sized,
205    {
206        *self
207            .get_operation()
208            .deref(ctx)
209            .attributes
210            .get::<FastmathFlagsAttr>(&ATTR_KEY_FAST_MATH_FLAGS)
211            .expect("Fast math flags missing or is of incorrect type")
212    }
213
214    /// Set the fast math flags for this [Op].
215    fn set_fast_math_flags(&self, ctx: &Context, flag: FastmathFlagsAttr)
216    where
217        Self: Sized,
218    {
219        self.get_operation()
220            .deref_mut(ctx)
221            .attributes
222            .set(ATTR_KEY_FAST_MATH_FLAGS.clone(), flag);
223    }
224
225    fn verify(op: &dyn Op, ctx: &Context) -> Result<()>
226    where
227        Self: Sized,
228    {
229        let op = op.get_operation().deref(ctx);
230        if op
231            .attributes
232            .get::<FastmathFlagsAttr>(&ATTR_KEY_FAST_MATH_FLAGS)
233            .is_none()
234        {
235            return verify_err!(op.loc(), FastmathFlagMissingErr);
236        }
237
238        Ok(())
239    }
240}
241
242dict_key!(
243    /// Attribute key for the synchronization scope of an atomic [Op].
244    ATTR_KEY_SYNC_SCOPE,
245    "llvm_syncscope"
246);
247
248#[derive(Error, Debug)]
249#[error("Synchronization scope missing on Op")]
250pub struct SyncScopeMissingErr;
251
252/// Atomic [Op]s that have a synchronization scope.
253#[op_interface]
254pub trait SyncScopeInterface {
255    /// Get the synchronization scope of this [Op].
256    fn syncscope(&self, ctx: &Context) -> SyncScopeAttr
257    where
258        Self: Sized,
259    {
260        self.get_operation()
261            .deref(ctx)
262            .attributes
263            .get::<SyncScopeAttr>(&ATTR_KEY_SYNC_SCOPE)
264            .expect("Synchronization scope missing or is of incorrect type")
265            .clone()
266    }
267
268    /// Set the synchronization scope of this [Op].
269    fn set_syncscope(&self, ctx: &Context, syncscope: SyncScopeAttr)
270    where
271        Self: Sized,
272    {
273        self.get_operation()
274            .deref_mut(ctx)
275            .attributes
276            .set(ATTR_KEY_SYNC_SCOPE.clone(), syncscope);
277    }
278
279    fn verify(op: &dyn Op, ctx: &Context) -> Result<()>
280    where
281        Self: Sized,
282    {
283        let op = op.get_operation().deref(ctx);
284        if op
285            .attributes
286            .get::<SyncScopeAttr>(&ATTR_KEY_SYNC_SCOPE)
287            .is_none()
288        {
289            return verify_err!(op.loc(), SyncScopeMissingErr);
290        }
291
292        Ok(())
293    }
294}
295
296/// Floating point binary arithmetic [Op] with [FastmathFlagsAttr]
297#[op_interface]
298pub trait FloatBinArithOpWithFastMathFlags: FloatBinArithOp + FastMathFlags {
299    /// Create a new floating point binary op with fast math flags set.
300    fn new_with_fast_math_flags(
301        ctx: &mut Context,
302        lhs: Value,
303        rhs: Value,
304        flag: FastmathFlagsAttr,
305    ) -> Self
306    where
307        Self: Sized,
308    {
309        let op = Self::new(ctx, lhs, rhs);
310        op.set_fast_math_flags(ctx, flag);
311        op
312    }
313
314    fn verify(_op: &dyn Op, _ctx: &Context) -> Result<()>
315    where
316        Self: Sized,
317    {
318        Ok(())
319    }
320}
321
322#[derive(Error, Debug)]
323#[error("Fastmath flag missing on Op")]
324pub struct FastmathFlagMissingErr;
325
326dict_key!(
327    /// Attribute key for nneg flag.
328    ATTR_KEY_NNEG_FLAG,
329    "llvm_nneg_flag"
330);
331
332#[op_interface]
333pub trait NNegFlag {
334    // Get the current NNEG flag value.
335    fn nneg(&self, ctx: &Context) -> bool {
336        self.get_operation()
337            .deref(ctx)
338            .attributes
339            .get::<BoolAttr>(&ATTR_KEY_NNEG_FLAG)
340            .expect("NNEG flag missing or is of incorrect type")
341            .clone()
342            .into()
343    }
344    // Set the current NNEG flag value.
345    fn set_nneg(&self, ctx: &Context, flag: bool) {
346        self.get_operation()
347            .deref_mut(ctx)
348            .attributes
349            .set(ATTR_KEY_NNEG_FLAG.clone(), BoolAttr::new(flag));
350    }
351    fn verify(op: &dyn Op, ctx: &Context) -> Result<()>
352    where
353        Self: Sized,
354    {
355        let op = op.get_operation().deref(ctx);
356        if op.attributes.get::<BoolAttr>(&ATTR_KEY_NNEG_FLAG).is_none() {
357            return verify_err!(op.loc(), NNegFlagMissingErr);
358        }
359
360        Ok(())
361    }
362}
363
364#[derive(Error, Debug)]
365#[error("NNEG flag missing on Op")]
366pub struct NNegFlagMissingErr;
367
368#[derive(Error, Debug)]
369#[error("Result must be a pointer type, but is not")]
370pub struct PointerTypeResultVerifyErr;
371
372/// An [Op] with a single result whose type is [PointerType]
373#[op_interface]
374pub trait PointerTypeResult: OneResultInterface + ResultNOfType<0, PointerType> {
375    /// Get the pointee type of the result pointer.
376    fn result_pointee_type(&self, ctx: &Context) -> TypeHandle;
377
378    fn verify(op: &dyn Op, ctx: &Context) -> Result<()>
379    where
380        Self: Sized,
381    {
382        if !op_cast::<dyn OneResultInterface>(op)
383            .expect("An Op here must impl OneResultInterface")
384            .result_type(ctx)
385            .deref(ctx)
386            .is::<PointerType>()
387        {
388            return verify_err!(op.loc(ctx), PointerTypeResultVerifyErr);
389        }
390
391        Ok(())
392    }
393}
394
395/// A Cast [Op] has one argument and one result.
396#[op_interface]
397pub trait CastOpInterface: OneResultInterface + OneOpdInterface {
398    /// Create a new cast operation given the operand.
399    fn new(ctx: &mut Context, operand: Value, res_type: TypeHandle) -> Self
400    where
401        Self: Sized,
402    {
403        Operation::new_op::<Self>(ctx, vec![res_type], vec![operand], vec![], 0)
404    }
405
406    fn verify(_op: &dyn Op, _ctx: &Context) -> Result<()>
407    where
408        Self: Sized,
409    {
410        Ok(())
411    }
412}
413
414/// A Cast [Op] with NNEG flag.
415#[op_interface]
416pub trait CastOpWithNNegInterface: CastOpInterface + NNegFlag {
417    /// Create a new cast operation with nneg flag
418    fn new_with_nneg(ctx: &mut Context, operand: Value, res_type: TypeHandle, nneg: bool) -> Self
419    where
420        Self: Sized,
421    {
422        let op = Self::new(ctx, operand, res_type);
423        op.set_nneg(ctx, nneg);
424        op
425    }
426
427    fn verify(_op: &dyn Op, _ctx: &Context) -> Result<()>
428    where
429        Self: Sized,
430    {
431        Ok(())
432    }
433}
434
435/// Is a global value (variable or function) declaration.
436#[op_interface]
437pub trait IsDeclaration {
438    /// Check if this global value (variable or function) is a declaration.
439    fn is_declaration(&self, ctx: &Context) -> bool
440    where
441        Self: Sized;
442
443    fn verify(_op: &dyn Op, _ctx: &Context) -> Result<()>
444    where
445        Self: Sized,
446    {
447        Ok(())
448    }
449}
450
451dict_key!(
452    /// Attribute key for LLVM symbol name.
453    ATTR_KEY_LLVM_SYMBOL_NAME,
454    "llvm_symbol_name"
455);
456
457/// Since LLVM symbols can have characters that are illegal in pliron,
458/// this interface provides a way to get the original LLVM symbol name.
459#[op_interface]
460pub trait LlvmSymbolName: SymbolOpInterface {
461    /// Get the original LLVM symbol name, if it's different from the pliron symbol name.
462    fn llvm_symbol_name(&self, ctx: &Context) -> Option<String> {
463        self.get_operation()
464            .deref(ctx)
465            .attributes
466            .get::<pliron::builtin::attributes::StringAttr>(&ATTR_KEY_LLVM_SYMBOL_NAME)
467            .map(|attr| attr.clone().into())
468    }
469
470    /// Set the original LLVM symbol name.
471    fn set_llvm_symbol_name(&self, ctx: &Context, name: String) {
472        self.get_operation().deref_mut(ctx).attributes.set(
473            ATTR_KEY_LLVM_SYMBOL_NAME.clone(),
474            pliron::builtin::attributes::StringAttr::new(name),
475        );
476    }
477
478    fn verify(_op: &dyn Op, _ctx: &Context) -> Result<()>
479    where
480        Self: Sized,
481    {
482        Ok(())
483    }
484}
485
486dict_key!(
487    /// Attribute key for alignment.
488    ATTR_KEY_LLVM_ALIGNMENT,
489    "llvm_alignment"
490);
491
492/// Ops that can have an alignment set.
493#[op_interface]
494pub trait AlignableOpInterface {
495    /// Get the alignment of this [Op], if set.
496    fn alignment(&self, ctx: &Context) -> Option<u32>
497    where
498        Self: Sized,
499    {
500        self.get_operation()
501            .deref(ctx)
502            .attributes
503            .get::<AlignmentAttr>(&ATTR_KEY_LLVM_ALIGNMENT)
504            .map(|attr| attr.0)
505    }
506
507    /// Set the alignment of this [Op].
508    fn set_alignment(&self, ctx: &Context, alignment: u32)
509    where
510        Self: Sized,
511    {
512        self.get_operation()
513            .deref_mut(ctx)
514            .attributes
515            .set(ATTR_KEY_LLVM_ALIGNMENT.clone(), AlignmentAttr(alignment));
516    }
517
518    fn verify(_op: &dyn Op, _ctx: &Context) -> Result<()>
519    where
520        Self: Sized,
521    {
522        Ok(())
523    }
524}
525
526dict_key!(
527    /// Attribute key for volatile memory operations.
528    ATTR_KEY_LLVM_VOLATILE,
529    "llvm_volatile"
530);
531
532/// Ops that can be marked volatile.
533#[op_interface]
534pub trait VolatilityOpInterface {
535    /// Return whether this [Op] is volatile.
536    fn is_volatile(&self, ctx: &Context) -> bool
537    where
538        Self: Sized,
539    {
540        self.get_operation()
541            .deref(ctx)
542            .attributes
543            .get::<BoolAttr>(&ATTR_KEY_LLVM_VOLATILE)
544            .map(|attr| attr.clone().into())
545            .unwrap_or(false)
546    }
547
548    /// Set whether this [Op] is volatile.
549    fn set_volatile(&self, ctx: &Context, is_volatile: bool)
550    where
551        Self: Sized,
552    {
553        self.get_operation()
554            .deref_mut(ctx)
555            .attributes
556            .set(ATTR_KEY_LLVM_VOLATILE.clone(), BoolAttr::new(is_volatile));
557    }
558
559    fn verify(_op: &dyn Op, _ctx: &Context) -> Result<()>
560    where
561        Self: Sized,
562    {
563        Ok(())
564    }
565}
566
567#[derive(Debug, Error)]
568pub enum ScalarOrVectorErr {
569    #[error("{0} is not {1} or a vector of it")]
570    NotTOrVectorOfT(String, String),
571    #[error("{0} or its vector element type does not implement interface")]
572    TyOrElemNotImplsI(String),
573}
574
575/// Get the vector shape of `ty`, or `None` if it is not a [VectorType].
576fn vector_shape_of(ty: TypeHandle, ctx: &Context) -> Option<(u32, VectorTypeKind)> {
577    ty.deref(ctx)
578        .downcast_ref::<VectorType>()
579        .map(|vec_ty| (vec_ty.num_elements(), vec_ty.kind()))
580}
581
582/// Get `ty`, or its element type if it is a [VectorType], typed as `T`.
583///
584/// Only valid to call once `verify_t_or_vec_of_t::<T>` has passed for `ty`.
585fn elem_ty_of<T: Type>(ty: TypeHandle, ctx: &Context) -> TypedHandle<T> {
586    if let Ok(typed_handle) = TypedHandle::from_handle(ty, ctx) {
587        return typed_handle;
588    }
589    let ty_ref = &*ty.deref(ctx);
590    let elem_ty = ty_ref
591        .downcast_ref::<VectorType>()
592        .expect("verify() guarantees type is T or a vector of T")
593        .elem_type();
594    TypedHandle::from_handle(elem_ty, ctx).expect("verify() guarantees element type is T")
595}
596
597/// Verify that `ty` is `T`, or a [VectorType] of `T`.
598fn verify_t_or_vec_of_t<T: Type>(loc: Location, ty: &dyn Type, ctx: &Context) -> Result<()> {
599    if ty.is::<T>() {
600        // ty is equal to `T`, we're good.
601        return Ok(());
602    }
603    // See if `ty` is a vector of `T`.
604    let Some(vec_ty) = ty.downcast_ref::<VectorType>() else {
605        return verify_err!(
606            loc,
607            ScalarOrVectorErr::NotTOrVectorOfT(
608                ty.get_type_id().disp(ctx).to_string(),
609                T::get_type_id_static().disp(ctx).to_string()
610            )
611        );
612    };
613    let elem_ty = &*vec_ty.elem_type().deref(ctx);
614    if !elem_ty.is::<T>() {
615        return verify_err!(
616            loc,
617            ScalarOrVectorErr::NotTOrVectorOfT(
618                ty.get_type_id().disp(ctx).to_string(),
619                T::get_type_id_static().disp(ctx).to_string()
620            )
621        );
622    }
623    Ok(())
624}
625
626/// Get `ty`, or its element type if it is a [VectorType], whichever implements `I`.
627///
628/// Only valid to call once `verify_impls_i_or_vec_of_impls_i::<I>` has passed for `ty`.
629fn elem_ty_of_impls<I: ?Sized + TypeInterfaceMarker + 'static>(
630    ty: TypeHandle,
631    ctx: &Context,
632) -> TypeInterfaceHandle<I> {
633    if let Ok(interface_handle) = TypeInterfaceHandle::from_handle(ty, ctx) {
634        return interface_handle;
635    }
636    let ty_ref = &*ty.deref(ctx);
637    let elem_ty = ty_ref
638        .downcast_ref::<VectorType>()
639        .expect("verify() guarantees type impls I or is a vector whose elem impls I")
640        .elem_type();
641    TypeInterfaceHandle::from_handle(elem_ty, ctx)
642        .expect("verify() guarantees element type impls I")
643}
644
645/// Verify that `ty` implements `I`, or is a [VectorType] whose element type implements `I`.
646fn verify_impls_i_or_vec_of_impls_i<I: ?Sized + TypeInterfaceMarker + 'static>(
647    loc: Location,
648    ty: &dyn Type,
649    ctx: &Context,
650) -> Result<()> {
651    if type_impls::<I>(ty) {
652        // ty implements `I`, we're good.
653        return Ok(());
654    }
655    // See if `ty` is a vector whose element type implements `I`.
656    let Some(vec_ty) = ty.downcast_ref::<VectorType>() else {
657        return verify_err!(
658            loc,
659            ScalarOrVectorErr::TyOrElemNotImplsI(ty.get_type_id().disp(ctx).to_string())
660        );
661    };
662    let elem_ty = &*vec_ty.elem_type().deref(ctx);
663    if !type_impls::<I>(elem_ty) {
664        return verify_err!(
665            loc,
666            ScalarOrVectorErr::TyOrElemNotImplsI(ty.get_type_id().disp(ctx).to_string())
667        );
668    }
669    Ok(())
670}
671
672/// An operand whose type is either `T` or [`VectorType<T>`](VectorType).
673#[op_interface]
674pub trait ScalarOrVectorOpd<T: Type, const N: usize> {
675    /// Get the type of operand N, or its element type if its [VectorType].
676    fn scalar_or_vector_elem_ty(&self, ctx: &Context) -> TypedHandle<T> {
677        let op = &*self.get_operation().deref(ctx);
678        elem_ty_of(op.get_operand(N).get_type(ctx), ctx)
679    }
680
681    /// Get the vector shape of operand N, or `None` if it is not a [VectorType].
682    fn vector_shape(&self, ctx: &Context) -> Option<(u32, VectorTypeKind)> {
683        let op = &*self.get_operation().deref(ctx);
684        vector_shape_of(op.get_operand(N).get_type(ctx), ctx)
685    }
686
687    fn verify(op: &dyn Op, ctx: &Context) -> Result<()>
688    where
689        Self: Sized,
690    {
691        let opd = verify_get_operand_n::<N>(op.get_operation(), ctx)?;
692        verify_t_or_vec_of_t::<T>(op.loc(ctx), &*opd.get_type(ctx).deref(ctx), ctx)
693    }
694}
695
696/// An operand whose type either impls `I` or is [`VectorType<T: I>`](VectorType).
697#[op_interface]
698pub trait ScalarOrVectorOpdImpls<I: ?Sized + TypeInterfaceMarker + 'static, const N: usize> {
699    /// Get the type of operand N, or its element type if its [VectorType].
700    fn scalar_or_vector_elem_ty(&self, ctx: &Context) -> TypeInterfaceHandle<I> {
701        let op = &*self.get_operation().deref(ctx);
702        elem_ty_of_impls::<I>(op.get_operand(N).get_type(ctx), ctx)
703    }
704
705    /// Get the vector shape of operand N, or `None` if it is not a [VectorType].
706    fn vector_shape(&self, ctx: &Context) -> Option<(u32, VectorTypeKind)> {
707        let op = &*self.get_operation().deref(ctx);
708        vector_shape_of(op.get_operand(N).get_type(ctx), ctx)
709    }
710
711    fn verify(op: &dyn Op, ctx: &Context) -> Result<()>
712    where
713        Self: Sized,
714    {
715        let opd = verify_get_operand_n::<N>(op.get_operation(), ctx)?;
716        verify_impls_i_or_vec_of_impls_i::<I>(op.loc(ctx), &*opd.get_type(ctx).deref(ctx), ctx)
717    }
718}
719
720/// A result whose type is either `T` or [`VectorType<T>`](VectorType).
721#[op_interface]
722pub trait ScalarOrVectorRes<T: Type, const N: usize> {
723    /// Get the type of result N, or its element type if its [VectorType].
724    fn scalar_or_vector_elem_ty(&self, ctx: &Context) -> TypedHandle<T> {
725        let op = &*self.get_operation().deref(ctx);
726        elem_ty_of(op.get_result(N).get_type(ctx), ctx)
727    }
728
729    /// Get the vector shape of result N, or `None` if it is not a [VectorType].
730    fn vector_shape(&self, ctx: &Context) -> Option<(u32, VectorTypeKind)> {
731        let op = &*self.get_operation().deref(ctx);
732        vector_shape_of(op.get_result(N).get_type(ctx), ctx)
733    }
734
735    fn verify(op: &dyn Op, ctx: &Context) -> Result<()>
736    where
737        Self: Sized,
738    {
739        let res = verify_get_result_n::<N>(op.get_operation(), ctx)?;
740        verify_t_or_vec_of_t::<T>(op.loc(ctx), &*res.get_type(ctx).deref(ctx), ctx)
741    }
742}
743
744/// A result whose type either impls `I` or is [`VectorType<T: I>`](VectorType).
745#[op_interface]
746pub trait ScalarOrVectorResImpls<I: ?Sized + TypeInterfaceMarker + 'static, const N: usize> {
747    /// Get the type of result N, or its element type if its [VectorType].
748    fn scalar_or_vector_elem_ty(&self, ctx: &Context) -> TypeInterfaceHandle<I> {
749        let op = &*self.get_operation().deref(ctx);
750        elem_ty_of_impls::<I>(op.get_result(N).get_type(ctx), ctx)
751    }
752
753    /// Get the vector shape of result N, or `None` if it is not a [VectorType].
754    fn vector_shape(&self, ctx: &Context) -> Option<(u32, VectorTypeKind)> {
755        let op = &*self.get_operation().deref(ctx);
756        vector_shape_of(op.get_result(N).get_type(ctx), ctx)
757    }
758
759    fn verify(op: &dyn Op, ctx: &Context) -> Result<()>
760    where
761        Self: Sized,
762    {
763        let res = verify_get_result_n::<N>(op.get_operation(), ctx)?;
764        verify_impls_i_or_vec_of_impls_i::<I>(op.loc(ctx), &*res.get_type(ctx).deref(ctx), ctx)
765    }
766}