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