quil_rs/instruction/
extern_call.rs

1/// This module provides support for the `CALL` instruction and the reserved `PRAGMA EXTERN` instruction.
2///
3/// For additional detail on its design and specification see:
4///
5/// * [Quil specification "Other"](https://github.com/quil-lang/quil/blob/7f532c7cdde9f51eae6abe7408cc868fba9f91f6/specgen/spec/sec-other.s_)
6/// * [Quil EXTERN / CALL RFC](https://github.com/quil-lang/quil/blob/master/rfcs/extern-call.md)
7/// * [quil#69](https://github.com/quil-lang/quil/pull/69)
8use std::{collections::HashSet, str::FromStr};
9
10use indexmap::IndexMap;
11use nom_locate::LocatedSpan;
12use num_complex::Complex64;
13
14#[cfg(feature = "stubs")]
15use pyo3_stub_gen::derive::{gen_stub_pyclass, gen_stub_pyclass_complex_enum};
16
17use crate::{
18    expression::format_complex,
19    floating_point_eq,
20    parser::lex,
21    pickleable_new,
22    program::{disallow_leftover, MemoryAccesses, MemoryRegion, SyntaxError},
23    quil::Quil,
24    validation::identifier::{validate_user_identifier, IdentifierValidationError},
25};
26
27use super::{
28    Instruction, MemoryReference, Pragma, PragmaArgument, ScalarType, Vector,
29    RESERVED_PRAGMA_EXTERN,
30};
31
32/// A parameter type within an extern signature.
33#[derive(Clone, Debug, PartialEq, Hash, Eq)]
34#[cfg_attr(feature = "stubs", gen_stub_pyclass_complex_enum)]
35#[cfg_attr(
36    feature = "python",
37    pyo3::pyclass(module = "quil.instructions", eq, frozen, hash)
38)]
39pub enum ExternParameterType {
40    /// A scalar parameter, which may accept a memory reference or immediate value.
41    ///
42    /// For instance `PRAGMA EXTERN foo "(bar : INTEGER)"`.
43    Scalar(ScalarType),
44    /// A fixed-length vector, which must accept a memory region name of the appropriate
45    /// length and data type.
46    ///
47    /// For instance `PRAGMA EXTERN foo "(bar : INTEGER[2])"`.
48    FixedLengthVector(Vector),
49    /// A variable-length vector, which must accept a memory region name of the appropriate
50    /// data type.
51    ///
52    /// For instance `PRAGMA EXTERN foo "(bar : INTEGER[])"`.
53    VariableLengthVector(ScalarType),
54}
55
56impl Quil for ExternParameterType {
57    fn write(
58        &self,
59        f: &mut impl std::fmt::Write,
60        fall_back_to_debug: bool,
61    ) -> crate::quil::ToQuilResult<()> {
62        match self {
63            ExternParameterType::Scalar(value) => value.write(f, fall_back_to_debug),
64            ExternParameterType::FixedLengthVector(value) => value.write(f, fall_back_to_debug),
65            ExternParameterType::VariableLengthVector(value) => {
66                value.write(f, fall_back_to_debug)?;
67                Ok(write!(f, "[]")?)
68            }
69        }
70    }
71}
72
73/// An extern parameter with a name, mutability, and data type.
74#[derive(Clone, Debug, PartialEq, Eq, Hash)]
75#[cfg_attr(feature = "stubs", gen_stub_pyclass)]
76#[cfg_attr(
77    feature = "python",
78    pyo3::pyclass(module = "quil.instructions", eq, frozen, hash, get_all, subclass)
79)]
80pub struct ExternParameter {
81    /// The name of the parameter. This must be a valid user identifier.
82    pub(crate) name: String,
83    /// Whether the parameter is mutable.
84    pub(crate) mutable: bool,
85    /// The data type of the parameter.
86    pub(crate) data_type: ExternParameterType,
87}
88
89pickleable_new! {
90    impl ExternParameter {
91        /// Create a new extern parameter. This will fail if the parameter name
92        /// is not a valid user identifier.
93        pub fn try_new(
94            name: String,
95            mutable: bool,
96            data_type: ExternParameterType,
97        ) -> Result<ExternParameter, ExternError> {
98            validate_user_identifier(name.as_str()).map_err(ExternError::from_boxed)?;
99            Ok(ExternParameter {
100                name,
101                mutable,
102                data_type,
103            })
104        }
105    }
106}
107
108impl ExternParameter {
109    pub fn name(&self) -> &str {
110        self.name.as_str()
111    }
112
113    pub fn mutable(&self) -> bool {
114        self.mutable
115    }
116
117    pub fn data_type(&self) -> &ExternParameterType {
118        &self.data_type
119    }
120}
121
122impl Quil for ExternParameter {
123    fn write(
124        &self,
125        writer: &mut impl std::fmt::Write,
126        fall_back_to_debug: bool,
127    ) -> Result<(), crate::quil::ToQuilError> {
128        write!(writer, "{} : ", self.name)?;
129        if self.mutable {
130            write!(writer, "mut ")?;
131        }
132        self.data_type.write(writer, fall_back_to_debug)
133    }
134}
135
136/// An extern signature with a return type and parameters.
137///
138/// The signature of a ``PRAGMA EXTERN`` instruction.
139/// This signature is defined by a list of ``ExternParameter``s and an optional return type.
140/// See the [Quil Specification](https://github.com/quil-lang/quil/blob/7f532c7cdde9f51eae6abe7408cc868fba9f91f6/specgen/spec/sec-other.s)
141/// for details on how these signatures are formed.
142#[derive(Clone, Debug, PartialEq, Eq, Hash)]
143#[cfg_attr(feature = "stubs", gen_stub_pyclass)]
144#[cfg_attr(
145    feature = "python",
146    pyo3::pyclass(module = "quil.instructions", eq, frozen, hash, get_all, subclass)
147)]
148pub struct ExternSignature {
149    /// The return type of the extern signature, if any.
150    pub(crate) return_type: Option<ScalarType>,
151    /// The parameters of the extern signature.
152    pub(crate) parameters: Vec<ExternParameter>,
153}
154
155pickleable_new! {
156    impl ExternSignature {
157        /// Create a new extern signature.
158        pub fn new(return_type: Option<ScalarType>, parameters: Vec<ExternParameter>);
159    }
160}
161
162impl ExternSignature {
163    pub fn return_type(&self) -> Option<&ScalarType> {
164        self.return_type.as_ref()
165    }
166
167    pub fn parameters(&self) -> &[ExternParameter] {
168        self.parameters.as_slice()
169    }
170}
171
172const EXPECTED_PRAGMA_EXTERN_STRUCTURE: &str = "PRAGMA EXTERN {name} \"{scalar type}? (\\(({parameter name} : mut? {parameter type}) (, {parameter name} : mut? {parameter type})*\\))?\"";
173
174/// An error that can occur when parsing an extern signature.
175#[derive(Debug, thiserror::Error, PartialEq, Clone)]
176pub enum ExternError {
177    /// An error occurred while parsing the contents of the extern signature.
178    #[error(
179        "invalid extern signature syntax: {0:?} (expected `{EXPECTED_PRAGMA_EXTERN_STRUCTURE}`)"
180    )]
181    Syntax(Box<SyntaxError<ExternSignature>>),
182    /// An error occurred while lexing the extern signature.
183    #[error(
184        "failed to lex extern signature: {0:?} (expected `{EXPECTED_PRAGMA_EXTERN_STRUCTURE}`)"
185    )]
186    Lex(Box<crate::parser::LexError>),
187    /// Pragma arguments are invalid.
188    #[error("`PRAGMA EXTERN` must have a single argument representing the extern name")]
189    InvalidPragmaArguments,
190    /// No signature found.
191    #[error("`PRAGMA EXTERN` instruction has no signature")]
192    NoSignature,
193    /// No extern name found.
194    #[error("`PRAGMA EXTERN` instruction has no name")]
195    NoName,
196    /// Pragma is not EXTERN.
197    #[error("ExternPragmaMap contained a pragma that was not EXTERN")]
198    PragmaIsNotExtern,
199    /// The extern definition has a signature but it has neither a return nor parameters.
200    #[error("extern definition has a signature but it has neither a return nor parameters")]
201    NoReturnOrParameters,
202    /// Either the name of the extern or one of its parameters is invalid.
203    #[error("invalid identifier: {0:?}")]
204    Name(#[from] Box<IdentifierValidationError>),
205}
206
207impl ExternError {
208    fn from_boxed<T>(value: T) -> Self
209    where
210        ExternError: From<Box<T>>,
211    {
212        ExternError::from(Box::new(value))
213    }
214}
215
216impl FromStr for ExternSignature {
217    type Err = ExternError;
218
219    fn from_str(s: &str) -> Result<Self, Self::Err> {
220        let signature_input = LocatedSpan::new(s);
221        let signature_tokens = lex(signature_input)
222            .map_err(Box::new)
223            .map_err(ExternError::Lex)?;
224        let signature = disallow_leftover(
225            crate::parser::pragma_extern::parse_extern_signature(signature_tokens.as_slice())
226                .map_err(crate::parser::ParseError::from_nom_internal_err),
227        )
228        .map_err(Box::new)
229        .map_err(ExternError::Syntax)?;
230        if signature.return_type.is_none() && signature.parameters.is_empty() {
231            return Err(ExternError::NoReturnOrParameters);
232        }
233        for parameter in &signature.parameters {
234            validate_user_identifier(parameter.name.as_str()).map_err(ExternError::from_boxed)?;
235        }
236        Ok(signature)
237    }
238}
239
240impl Quil for ExternSignature {
241    fn write(
242        &self,
243        writer: &mut impl std::fmt::Write,
244        fall_back_to_debug: bool,
245    ) -> Result<(), crate::quil::ToQuilError> {
246        if let Some(return_type) = &self.return_type {
247            return_type.write(writer, fall_back_to_debug)?;
248            if !self.parameters.is_empty() {
249                write!(writer, " ")?;
250            }
251        }
252        if self.parameters.is_empty() {
253            return Ok(());
254        }
255        write!(writer, "(")?;
256        for (i, parameter) in self.parameters.iter().enumerate() {
257            if i > 0 {
258                write!(writer, ", ")?;
259            }
260            parameter.write(writer, fall_back_to_debug)?;
261        }
262        write!(writer, ")").map_err(Into::into)
263    }
264}
265
266impl TryFrom<Pragma> for ExternSignature {
267    type Error = ExternError;
268
269    fn try_from(value: Pragma) -> Result<Self, ExternError> {
270        if value.name != RESERVED_PRAGMA_EXTERN {
271            return Err(ExternError::PragmaIsNotExtern);
272        }
273        if value.arguments.is_empty()
274            || !matches!(value.arguments[0], PragmaArgument::Identifier(_))
275        {
276            return Err(ExternError::NoName);
277        }
278        if value.arguments.len() > 1 {
279            return Err(ExternError::InvalidPragmaArguments);
280        }
281
282        match value.data {
283            Some(data) => ExternSignature::from_str(data.as_str()),
284            None => Err(ExternError::NoSignature),
285        }
286    }
287}
288
289/// A map of all program `PRAGMA EXTERN` instructions from their name (if any) to
290/// the corresponding [`Pragma`] instruction. Note, keys are [`Option`]s, but a
291/// `None` key will be considered invalid when converting to an [`ExternSignatureMap`].
292#[derive(Clone, Debug, PartialEq, Default)]
293#[cfg_attr(feature = "python", derive(pyo3::IntoPyObject))]
294pub struct ExternPragmaMap(IndexMap<Option<String>, Pragma>);
295
296impl ExternPragmaMap {
297    pub(crate) fn len(&self) -> usize {
298        self.0.len()
299    }
300
301    pub(crate) fn into_instructions(self) -> Vec<Instruction> {
302        self.0.into_values().map(Instruction::Pragma).collect()
303    }
304
305    /// Expose the [`ExternPragmaMap`] as a list of [`Instruction`]s.
306    pub fn to_instructions(&self) -> Vec<Instruction> {
307        self.0.values().cloned().map(Instruction::Pragma).collect()
308    }
309
310    /// Insert a `PRAGMA EXTERN` instruction into the underlying [`IndexMap`].
311    ///
312    /// If the first argument to the [`Pragma`] is not a [`PragmaArgument::Identifier`], or
313    /// does not exist, then the [`Pragma`] will be inserted with a `None` key.
314    ///
315    /// If the key already exists, the previous [`Pragma`] will be returned, similar to
316    /// the behavior of [`IndexMap::insert`].
317    pub(crate) fn insert(&mut self, pragma: Pragma) -> Option<Pragma> {
318        self.0.insert(
319            match pragma.arguments.first() {
320                Some(PragmaArgument::Identifier(name)) => Some(name.clone()),
321                _ => None,
322            },
323            pragma,
324        )
325    }
326
327    /// Extend this [`ExternPragmaMap`] with another.
328    ///
329    /// The behavior is similar to [`IndexMap::extend`] here. Of note,
330    /// for keys that already existed in [`self`], their value is updated
331    /// but it keeps the existing order.
332    pub fn extend(&mut self, other: Self) {
333        self.0.extend(other.0);
334    }
335
336    pub(crate) fn retain<F>(&mut self, f: F)
337    where
338        F: FnMut(&Option<String>, &mut Pragma) -> bool,
339    {
340        self.0.retain(f)
341    }
342}
343
344impl std::iter::IntoIterator for ExternPragmaMap {
345    type Item = (Option<String>, Pragma);
346    type IntoIter = indexmap::map::IntoIter<Option<String>, Pragma>;
347
348    fn into_iter(self) -> Self::IntoIter {
349        self.0.into_iter()
350    }
351}
352
353/// A map of all program `PRAGMA EXTERN` instructions from their name to the corresponding
354/// parsed and validated [`ExternSignature`].
355#[derive(Clone, Debug, PartialEq, Default)]
356pub struct ExternSignatureMap(IndexMap<String, ExternSignature>);
357
358impl TryFrom<ExternPragmaMap> for ExternSignatureMap {
359    /// The error type for converting an [`ExternPragmaMap`] to an [`ExternSignatureMap`] includes
360    /// the offending [`Pragma`] instruction and the error that occurred.
361    type Error = (Pragma, ExternError);
362
363    fn try_from(value: ExternPragmaMap) -> Result<Self, Self::Error> {
364        Ok(ExternSignatureMap(
365            value
366                .0
367                .into_iter()
368                .map(|(key, value)| -> Result<_, Self::Error> {
369                    match key {
370                        Some(name) => {
371                            validate_user_identifier(name.as_str())
372                                .map_err(ExternError::from_boxed)
373                                .map_err(|error| (value.clone(), error))?;
374                            let signature = ExternSignature::try_from(value.clone())
375                                .map_err(|error| (value, error))?;
376                            Ok((name, signature))
377                        }
378                        _ => Err((value, ExternError::NoName)),
379                    }
380                })
381                .collect::<Result<_, Self::Error>>()?,
382        ))
383    }
384}
385
386impl ExternSignatureMap {
387    #[inline]
388    pub fn len(&self) -> usize {
389        self.0.len()
390    }
391
392    #[inline]
393    pub fn is_empty(&self) -> bool {
394        self.0.is_empty()
395    }
396
397    #[inline]
398    pub fn iter(&self) -> impl Iterator<Item = (&String, &ExternSignature)> {
399        self.0.iter()
400    }
401}
402
403/// An error that can occur when resolving a call instruction.
404#[derive(Clone, Debug, thiserror::Error, PartialEq)]
405pub enum CallArgumentResolutionError {
406    /// An undeclared memory reference was encountered.
407    #[error("undeclared memory reference {0}")]
408    UndeclaredMemoryReference(String),
409    /// A mismatched vector was encountered.
410    #[error("mismatched vector: expected {expected:?}, found {found:?}")]
411    MismatchedVector { expected: Vector, found: Vector },
412    /// A mismatched scalar was encountered.
413    #[error("mismatched scalar: expected {expected:?}, found {found:?}")]
414    MismatchedScalar {
415        expected: ScalarType,
416        found: ScalarType,
417    },
418    /// The argument for a vector parameter was invalid.
419    #[error("vector parameters must be passed as an identifier, found {0:?}")]
420    InvalidVectorArgument(UnresolvedCallArgument),
421    /// The argument for a return parameter was invalid.
422    #[error("return argument must be a memory reference or identifier, found {found:?}")]
423    ReturnArgument { found: UnresolvedCallArgument },
424    /// Immediate arguments cannot be specified for mutable parameters.
425    #[error("immediate arguments cannot be specified for mutable parameter {0}")]
426    ImmediateArgumentForMutable(String),
427}
428
429/// A parsed, but unresolved call argument. This may be resolved into a [`ResolvedCallArgument`]
430/// with the appropriate [`ExternSignature`]. Resolution is required for building the
431/// [`crate::Program`] memory graph.
432#[derive(Clone, Debug)]
433#[cfg_attr(feature = "stubs", gen_stub_pyclass_complex_enum)]
434#[cfg_attr(
435    feature = "python",
436    pyo3::pyclass(name = "CallArgument", module = "quil.instructions", eq, frozen, hash)
437)]
438pub enum UnresolvedCallArgument {
439    /// A reference to a declared memory location. Note, this may be resolved to either
440    /// a scalar or vector. In the former case, the assumed index is 0.
441    Identifier(String),
442    /// A reference to a memory location. This may be resolved to a scalar.
443    MemoryReference(MemoryReference),
444    /// An immediate value. This may be resolved to a non-mutable scalar.
445    Immediate(Complex64),
446}
447
448impl PartialEq for UnresolvedCallArgument {
449    fn eq(&self, other: &Self) -> bool {
450        match (self, other) {
451            (Self::Identifier(this), Self::Identifier(that)) => this == that,
452            (Self::MemoryReference(this), Self::MemoryReference(that)) => this == that,
453            (Self::Immediate(this), Self::Immediate(that)) => {
454                floating_point_eq::complex64::eq(*this, *that)
455            }
456            // This explicit or-pattern ensures that we'll get a compilation error if
457            // `UnresolvedCallArgument` grows another constructor.
458            (Self::Identifier(_) | Self::MemoryReference(_) | Self::Immediate(_), _) => false,
459        }
460    }
461}
462
463impl Eq for UnresolvedCallArgument {}
464
465impl std::hash::Hash for UnresolvedCallArgument {
466    fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
467        match self {
468            UnresolvedCallArgument::Identifier(value) => {
469                "Identifier".hash(state);
470                value.hash(state);
471            }
472            UnresolvedCallArgument::MemoryReference(value) => {
473                "MemoryReference".hash(state);
474                value.hash(state);
475            }
476            UnresolvedCallArgument::Immediate(value) => {
477                "Immediate".hash(state);
478                floating_point_eq::complex64::hash(*value, state);
479            }
480        }
481    }
482}
483
484impl UnresolvedCallArgument {
485    /// Check if the argument is compatible with the given [`ExternParameter`]. If so, return
486    /// the appropriate [`ResolvedCallArgument`]. If not, return an error.
487    fn resolve(
488        &self,
489        memory_regions: &IndexMap<String, MemoryRegion>,
490        extern_parameter: &ExternParameter,
491    ) -> Result<ResolvedCallArgument, CallArgumentResolutionError> {
492        match self {
493            UnresolvedCallArgument::Identifier(value) => {
494                let expected_vector = match &extern_parameter.data_type {
495                    ExternParameterType::Scalar(_) => {
496                        return UnresolvedCallArgument::MemoryReference(MemoryReference::new(
497                            value.clone(),
498                            0,
499                        ))
500                        .resolve(memory_regions, extern_parameter);
501                    }
502                    ExternParameterType::FixedLengthVector(expected_vector) => {
503                        let memory_region =
504                            memory_regions.get(value.as_str()).ok_or_else(|| {
505                                CallArgumentResolutionError::UndeclaredMemoryReference(
506                                    value.clone(),
507                                )
508                            })?;
509                        if &memory_region.size != expected_vector {
510                            return Err(CallArgumentResolutionError::MismatchedVector {
511                                expected: expected_vector.clone(),
512                                found: memory_region.size.clone(),
513                            });
514                        }
515
516                        Ok(expected_vector.clone())
517                    }
518                    ExternParameterType::VariableLengthVector(scalar_type) => {
519                        let memory_region =
520                            memory_regions.get(value.as_str()).ok_or_else(|| {
521                                CallArgumentResolutionError::UndeclaredMemoryReference(
522                                    value.clone(),
523                                )
524                            })?;
525                        if &memory_region.size.data_type != scalar_type {
526                            return Err(CallArgumentResolutionError::MismatchedScalar {
527                                expected: *scalar_type,
528                                found: memory_region.size.data_type,
529                            });
530                        }
531                        Ok(memory_region.size.clone())
532                    }
533                }?;
534                Ok(ResolvedCallArgument::Vector {
535                    memory_region_name: value.clone(),
536                    vector: expected_vector,
537                    mutable: extern_parameter.mutable,
538                })
539            }
540            UnresolvedCallArgument::MemoryReference(value) => {
541                let expected_scalar = match extern_parameter.data_type {
542                    ExternParameterType::Scalar(ref scalar) => Ok(scalar),
543                    ExternParameterType::FixedLengthVector(_)
544                    | ExternParameterType::VariableLengthVector(_) => {
545                        Err(CallArgumentResolutionError::InvalidVectorArgument(
546                            Self::MemoryReference(value.clone()),
547                        ))
548                    }
549                }?;
550                let memory_region = memory_regions.get(value.name.as_str()).ok_or_else(|| {
551                    CallArgumentResolutionError::UndeclaredMemoryReference(value.name.clone())
552                })?;
553                if memory_region.size.data_type != *expected_scalar {
554                    return Err(CallArgumentResolutionError::MismatchedScalar {
555                        expected: *expected_scalar,
556                        found: memory_region.size.data_type,
557                    });
558                }
559                Ok(ResolvedCallArgument::MemoryReference {
560                    memory_reference: value.clone(),
561                    scalar_type: *expected_scalar,
562                    mutable: extern_parameter.mutable,
563                })
564            }
565            UnresolvedCallArgument::Immediate(value) => {
566                if extern_parameter.mutable {
567                    return Err(CallArgumentResolutionError::ImmediateArgumentForMutable(
568                        extern_parameter.name.clone(),
569                    ));
570                }
571                let expected_scalar = match extern_parameter.data_type {
572                    ExternParameterType::Scalar(ref scalar) => Ok(scalar),
573                    ExternParameterType::FixedLengthVector(_)
574                    | ExternParameterType::VariableLengthVector(_) => Err(
575                        CallArgumentResolutionError::InvalidVectorArgument(self.clone()),
576                    ),
577                }?;
578                Ok(ResolvedCallArgument::Immediate {
579                    value: *value,
580                    scalar_type: *expected_scalar,
581                })
582            }
583        }
584    }
585
586    /// Check if the argument is compatible with the return type of the [`ExternSignature`]. If so,
587    /// return the appropriate [`ResolvedCallArgument`]. If not, return an error.
588    fn resolve_return(
589        &self,
590        memory_regions: &IndexMap<String, MemoryRegion>,
591        return_type: ScalarType,
592    ) -> Result<ResolvedCallArgument, CallArgumentResolutionError> {
593        let memory_reference = match self {
594            UnresolvedCallArgument::MemoryReference(memory_reference) => {
595                Ok(memory_reference.clone())
596            }
597            UnresolvedCallArgument::Identifier(identifier) => {
598                Ok(MemoryReference::new(identifier.clone(), 0))
599            }
600            _ => Err(CallArgumentResolutionError::ReturnArgument {
601                found: self.clone(),
602            }),
603        }?;
604        let memory_region = memory_regions
605            .get(memory_reference.name.as_str())
606            .ok_or_else(|| {
607                CallArgumentResolutionError::UndeclaredMemoryReference(
608                    memory_reference.name.clone(),
609                )
610            })?;
611        if memory_region.size.data_type != return_type {
612            return Err(CallArgumentResolutionError::MismatchedScalar {
613                expected: return_type,
614                found: memory_region.size.data_type,
615            });
616        }
617        Ok(ResolvedCallArgument::MemoryReference {
618            memory_reference: memory_reference.clone(),
619            scalar_type: return_type,
620            mutable: true,
621        })
622    }
623}
624
625impl Quil for UnresolvedCallArgument {
626    fn write(
627        &self,
628        f: &mut impl std::fmt::Write,
629        fall_back_to_debug: bool,
630    ) -> crate::quil::ToQuilResult<()> {
631        match &self {
632            UnresolvedCallArgument::Identifier(value) => write!(f, "{value}",).map_err(Into::into),
633            UnresolvedCallArgument::MemoryReference(value) => value.write(f, fall_back_to_debug),
634            UnresolvedCallArgument::Immediate(value) => {
635                write!(f, "{}", format_complex(value)).map_err(Into::into)
636            }
637        }
638    }
639}
640
641/// A resolved call argument. This is the result of resolving an [`UnresolvedCallArgument`] with
642/// the appropriate [`ExternParameter`]. It annotates the argument both with a type (and possibly
643/// a length in the case of a vector) and mutability.
644#[derive(Clone, Debug)]
645pub enum ResolvedCallArgument {
646    /// A resolved vector argument, including its scalar type, length, and mutability.
647    Vector {
648        memory_region_name: String,
649        vector: Vector,
650        mutable: bool,
651    },
652    /// A resolved memory reference, including its scalar type and mutability.
653    MemoryReference {
654        memory_reference: MemoryReference,
655        scalar_type: ScalarType,
656        mutable: bool,
657    },
658    /// A resolved immediate value, including its scalar type.
659    Immediate {
660        value: Complex64,
661        scalar_type: ScalarType,
662    },
663}
664
665impl PartialEq for ResolvedCallArgument {
666    fn eq(&self, other: &Self) -> bool {
667        match (self, other) {
668            (
669                Self::Vector {
670                    memory_region_name: this_memory_region_name,
671                    vector: this_vector,
672                    mutable: this_mutable,
673                },
674                Self::Vector {
675                    memory_region_name: that_memory_region_name,
676                    vector: that_vector,
677                    mutable: that_mutable,
678                },
679            ) => {
680                this_memory_region_name == that_memory_region_name
681                    && this_vector == that_vector
682                    && this_mutable == that_mutable
683            }
684
685            (
686                Self::MemoryReference {
687                    memory_reference: this_memory_reference,
688                    scalar_type: this_scalar_type,
689                    mutable: this_mutable,
690                },
691                Self::MemoryReference {
692                    memory_reference: that_memory_reference,
693                    scalar_type: that_scalar_type,
694                    mutable: that_mutable,
695                },
696            ) => {
697                this_memory_reference == that_memory_reference
698                    && this_scalar_type == that_scalar_type
699                    && this_mutable == that_mutable
700            }
701
702            (
703                Self::Immediate {
704                    value: this_value,
705                    scalar_type: this_scalar_type,
706                },
707                Self::Immediate {
708                    value: that_value,
709                    scalar_type: that_scalar_type,
710                },
711            ) => {
712                floating_point_eq::complex64::eq(*this_value, *that_value)
713                    && this_scalar_type == that_scalar_type
714            }
715
716            // This explicit or-pattern ensures that we'll get a compilation error if
717            // `ResolvedCallArgument` grows another constructor.
718            (Self::Vector { .. } | Self::MemoryReference { .. } | Self::Immediate { .. }, _) => {
719                false
720            }
721        }
722    }
723}
724
725impl From<ResolvedCallArgument> for UnresolvedCallArgument {
726    fn from(value: ResolvedCallArgument) -> Self {
727        match value {
728            ResolvedCallArgument::Vector {
729                memory_region_name, ..
730            } => UnresolvedCallArgument::Identifier(memory_region_name),
731            ResolvedCallArgument::MemoryReference {
732                memory_reference, ..
733            } => UnresolvedCallArgument::MemoryReference(memory_reference),
734            ResolvedCallArgument::Immediate { value, .. } => {
735                UnresolvedCallArgument::Immediate(value)
736            }
737        }
738    }
739}
740
741impl Eq for ResolvedCallArgument {}
742
743impl std::hash::Hash for ResolvedCallArgument {
744    fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
745        match self {
746            ResolvedCallArgument::Vector {
747                memory_region_name,
748                vector,
749                mutable,
750            } => {
751                "Vector".hash(state);
752                memory_region_name.hash(state);
753                vector.hash(state);
754                mutable.hash(state);
755            }
756            ResolvedCallArgument::MemoryReference {
757                memory_reference,
758                scalar_type,
759                mutable,
760            } => {
761                "MemoryReference".hash(state);
762                memory_reference.hash(state);
763                scalar_type.hash(state);
764                mutable.hash(state);
765            }
766            ResolvedCallArgument::Immediate { value, scalar_type } => {
767                "Immediate".hash(state);
768                floating_point_eq::complex64::hash(*value, state);
769                scalar_type.hash(state);
770            }
771        }
772    }
773}
774
775/// An error that can occur when validating a call instruction.
776#[derive(Clone, Debug, PartialEq, thiserror::Error, Eq)]
777pub enum CallError {
778    /// The specified name is not a valid user identifier.
779    #[error(transparent)]
780    Name(#[from] IdentifierValidationError),
781}
782
783/// A call instruction with a name and arguments.
784///
785/// An instruction that calls an external function declared with a `PRAGMA EXTERN` instruction.
786/// These calls are generally specific to a particular hardware or virtual machine backend.
787///
788/// For further detail, see:
789///
790/// * [Other instructions and Directives](https://github.com/quil-lang/quil/blob/master/rfcs/extern-call.md) in the Quil specification.
791/// * [EXTERN / CALL RFC](https://github.com/quil-lang/quil/blob/master/rfcs/extern-call.md)
792/// * [quil#87](https://github.com/quil-lang/quil/issues/87)
793///
794/// Also see [`ExternSignature`].
795#[derive(Clone, Debug, PartialEq, Hash, Eq)]
796#[cfg_attr(feature = "stubs", gen_stub_pyclass)]
797#[cfg_attr(
798    feature = "python",
799    pyo3::pyclass(module = "quil.instructions", eq, frozen, hash, subclass, get_all)
800)]
801pub struct Call {
802    /// The name of the call instruction. This must be a valid user identifier.
803    pub name: String,
804    /// The arguments of the call instruction.
805    pub arguments: Vec<UnresolvedCallArgument>,
806}
807
808pickleable_new! {
809    impl Call {
810        /// Create a new call instruction with resolved arguments.
811        /// This will validate the name as a user identifier.
812        pub fn try_new(
813            name: String,
814            arguments: Vec<UnresolvedCallArgument>,
815        ) -> Result<Call, CallError> {
816            validate_user_identifier(name.as_str()).map_err(CallError::Name)?;
817
818            Ok(Self { name, arguments })
819        }
820    }
821}
822
823impl Call {
824    pub fn arguments(&self) -> &[UnresolvedCallArgument] {
825        self.arguments.as_slice()
826    }
827}
828
829/// An error that can occur when resolving a call instruction argument.
830#[derive(Clone, Debug, thiserror::Error, PartialEq)]
831pub enum CallArgumentError {
832    /// The return argument could not be resolved.
833    #[error("error resolving return argument: {0:?}")]
834    Return(CallArgumentResolutionError),
835    /// An argument could not be resolved.
836    #[error("error resolving argument {index}: {error:?}")]
837    Argument {
838        index: usize,
839        error: CallArgumentResolutionError,
840    },
841}
842
843/// An error that can occur when resolving a call instruction to a specific
844/// [`ExternSignature`].
845#[derive(Debug, thiserror::Error, PartialEq, Clone)]
846pub enum CallSignatureError {
847    #[error("expected {expected} arguments, found {found}")]
848    ParameterCount { expected: usize, found: usize },
849    #[error("error resolving arguments: {0:?}")]
850    Arguments(Vec<CallArgumentError>),
851}
852
853/// An error that can occur when resolving a call instruction, given a complete
854/// [`ExternPragmaMap`] for the [`crate::program::Program`].
855#[derive(Debug, thiserror::Error, PartialEq, Clone)]
856pub enum CallResolutionError {
857    /// A matching extern instruction was found, but signature validation failed.
858    #[error("call found matching extern instruction for {name}, but signature validation failed: {error:?}")]
859    Signature {
860        name: String,
861        error: CallSignatureError,
862    },
863    /// No matching extern instruction was found.
864    #[error("no extern instruction found with name {0}")]
865    NoMatchingExternInstruction(String),
866    /// Failed to convernt the [`ExternPragmaMap`] to an [`ExternSignatureMap`].
867    #[error(transparent)]
868    ExternSignature(#[from] ExternError),
869}
870
871#[allow(clippy::manual_try_fold)]
872fn convert_unresolved_to_resolved_call_arguments(
873    arguments: &[UnresolvedCallArgument],
874    signature: &ExternSignature,
875    memory_regions: &IndexMap<String, MemoryRegion>,
876) -> Result<Vec<ResolvedCallArgument>, CallSignatureError> {
877    arguments
878        .iter()
879        .enumerate()
880        .map(|(i, argument)| {
881            if i == 0 {
882                if let Some(return_type) = signature.return_type {
883                    return argument
884                        .resolve_return(memory_regions, return_type)
885                        .map_err(CallArgumentError::Return);
886                }
887            }
888            let parameter_index = if signature.return_type.is_some() {
889                i - 1
890            } else {
891                i
892            };
893            let parameter = &signature.parameters[parameter_index];
894            argument
895                .resolve(memory_regions, parameter)
896                .map_err(|error| CallArgumentError::Argument {
897                    index: parameter_index,
898                    error,
899                })
900        })
901        .fold(
902            Ok(Vec::new()),
903            |acc: Result<Vec<ResolvedCallArgument>, Vec<CallArgumentError>>,
904             result: Result<ResolvedCallArgument, CallArgumentError>| {
905                match (acc, result) {
906                    (Ok(mut acc), Ok(resolved)) => {
907                        acc.push(resolved);
908                        Ok(acc)
909                    }
910                    (Ok(_), Err(error)) => Err(vec![error]),
911                    (Err(errors), Ok(_)) => Err(errors),
912                    (Err(mut errors), Err(error)) => {
913                        errors.push(error);
914                        Err(errors)
915                    }
916                }
917            },
918        )
919        .map_err(CallSignatureError::Arguments)
920}
921
922impl Call {
923    /// Resolve the [`Call`] instruction to the given [`ExternSignature`].
924    fn resolve_to_signature(
925        &self,
926        signature: &ExternSignature,
927        memory_regions: &IndexMap<String, MemoryRegion>,
928    ) -> Result<Vec<ResolvedCallArgument>, CallSignatureError> {
929        let mut expected_parameter_count = signature.parameters.len();
930        if signature.return_type.is_some() {
931            expected_parameter_count += 1;
932        }
933
934        if self.arguments.len() != expected_parameter_count {
935            return Err(CallSignatureError::ParameterCount {
936                expected: expected_parameter_count,
937                found: self.arguments.len(),
938            });
939        }
940
941        let resolved_call_arguments = convert_unresolved_to_resolved_call_arguments(
942            &self.arguments,
943            signature,
944            memory_regions,
945        )?;
946
947        Ok(resolved_call_arguments)
948    }
949
950    /// Resolve the [`Call`] instruction to any of the given [`ExternSignature`]s and memory regions.
951    /// If no matching extern instruction is found, return an error.
952    pub fn resolve_arguments(
953        &self,
954        memory_regions: &IndexMap<String, MemoryRegion>,
955        extern_signature_map: &ExternSignatureMap,
956    ) -> Result<Vec<ResolvedCallArgument>, CallResolutionError> {
957        let extern_signature = extern_signature_map
958            .0
959            .get(self.name.as_str())
960            .ok_or_else(|| CallResolutionError::NoMatchingExternInstruction(self.name.clone()))?;
961
962        self.resolve_to_signature(extern_signature, memory_regions)
963            .map_err(|error| CallResolutionError::Signature {
964                name: self.name.clone(),
965                error,
966            })
967    }
968
969    /// Return the [`MemoryAccesses`] for the [`Call`] instruction given the [`ExternSignatureMap`].
970    /// This assumes ALL parameters are read, including mutable parameters.
971    pub(crate) fn get_memory_accesses(
972        &self,
973        extern_signatures: &ExternSignatureMap,
974    ) -> Result<MemoryAccesses, CallResolutionError> {
975        let extern_signature = extern_signatures
976            .0
977            .get(self.name.as_str())
978            .ok_or_else(|| CallResolutionError::NoMatchingExternInstruction(self.name.clone()))?;
979
980        let mut reads = HashSet::new();
981        let mut writes = HashSet::new();
982        let mut arguments = self.arguments.iter();
983        if extern_signature.return_type.is_some() {
984            if let Some(argument) = self.arguments.first() {
985                arguments.next();
986                match argument {
987                    UnresolvedCallArgument::MemoryReference(memory_reference) => {
988                        reads.insert(memory_reference.name.clone());
989                        writes.insert(memory_reference.name.clone());
990                    }
991                    UnresolvedCallArgument::Identifier(identifier) => {
992                        reads.insert(identifier.clone());
993                        writes.insert(identifier.clone());
994                    }
995                    _ => {}
996                }
997            }
998        }
999        for (argument, parameter) in std::iter::zip(arguments, extern_signature.parameters.iter()) {
1000            match argument {
1001                UnresolvedCallArgument::MemoryReference(memory_reference) => {
1002                    reads.insert(memory_reference.name.clone());
1003                    if parameter.mutable {
1004                        writes.insert(memory_reference.name.clone());
1005                    }
1006                }
1007                UnresolvedCallArgument::Identifier(identifier) => {
1008                    reads.insert(identifier.clone());
1009                    if parameter.mutable {
1010                        writes.insert(identifier.clone());
1011                    }
1012                }
1013                _ => {}
1014            }
1015        }
1016        Ok(MemoryAccesses {
1017            reads,
1018            writes,
1019            captures: HashSet::new(),
1020        })
1021    }
1022}
1023
1024impl Quil for Call {
1025    fn write(
1026        &self,
1027        f: &mut impl std::fmt::Write,
1028        fall_back_to_debug: bool,
1029    ) -> crate::quil::ToQuilResult<()> {
1030        write!(f, "CALL {}", self.name)?;
1031        for argument in self.arguments.as_slice() {
1032            write!(f, " ")?;
1033            argument.write(f, fall_back_to_debug)?;
1034        }
1035        Ok(())
1036    }
1037}
1038
1039#[cfg(test)]
1040mod tests {
1041    use super::*;
1042    use crate::instruction::PragmaArgument;
1043    use rstest::*;
1044
1045    /// Test cases for the [`ExternSignature`] Quil representation.
1046    struct ExternSignatureQuilTestCase {
1047        /// The extern signature to test.
1048        signature: ExternSignature,
1049        /// The expected Quil representation.
1050        expected: &'static str,
1051    }
1052
1053    impl ExternSignatureQuilTestCase {
1054        /// Signature with return and parameters
1055        fn case_01() -> Self {
1056            Self {
1057                signature: ExternSignature {
1058                    return_type: Some(ScalarType::Integer),
1059                    parameters: vec![
1060                        ExternParameter {
1061                            name: "bar".to_string(),
1062                            mutable: false,
1063                            data_type: ExternParameterType::Scalar(ScalarType::Integer),
1064                        },
1065                        ExternParameter {
1066                            name: "baz".to_string(),
1067                            mutable: true,
1068                            data_type: ExternParameterType::FixedLengthVector(Vector {
1069                                data_type: ScalarType::Bit,
1070                                length: 2,
1071                            }),
1072                        },
1073                    ],
1074                },
1075                expected: "INTEGER (bar : INTEGER, baz : mut BIT[2])",
1076            }
1077        }
1078
1079        /// Signature with only parameters
1080        fn case_02() -> Self {
1081            let signature = ExternSignature {
1082                return_type: None,
1083                parameters: vec![
1084                    ExternParameter {
1085                        name: "bar".to_string(),
1086                        mutable: false,
1087                        data_type: ExternParameterType::Scalar(ScalarType::Integer),
1088                    },
1089                    ExternParameter {
1090                        name: "baz".to_string(),
1091                        mutable: true,
1092                        data_type: ExternParameterType::FixedLengthVector(Vector {
1093                            data_type: ScalarType::Bit,
1094                            length: 2,
1095                        }),
1096                    },
1097                ],
1098            };
1099            Self {
1100                signature,
1101                expected: "(bar : INTEGER, baz : mut BIT[2])",
1102            }
1103        }
1104
1105        /// Signature with return only
1106        fn case_03() -> Self {
1107            let signature = ExternSignature {
1108                return_type: Some(ScalarType::Integer),
1109                parameters: vec![],
1110            };
1111            Self {
1112                signature,
1113                expected: "INTEGER",
1114            }
1115        }
1116
1117        /// Signature with no return nor parameters
1118        fn case_04() -> Self {
1119            let signature = ExternSignature {
1120                return_type: None,
1121                parameters: vec![],
1122            };
1123            Self {
1124                signature,
1125                expected: "",
1126            }
1127        }
1128
1129        /// Variable length vector
1130        fn case_05() -> Self {
1131            let signature = ExternSignature {
1132                return_type: None,
1133                parameters: vec![ExternParameter {
1134                    name: "bar".to_string(),
1135                    mutable: false,
1136                    data_type: ExternParameterType::VariableLengthVector(ScalarType::Integer),
1137                }],
1138            };
1139            Self {
1140                signature,
1141                expected: "(bar : INTEGER[])",
1142            }
1143        }
1144    }
1145
1146    /// Test that the Quil representation of an [`ExternSignature`] is as expected.
1147    #[rstest]
1148    #[case(ExternSignatureQuilTestCase::case_01())]
1149    #[case(ExternSignatureQuilTestCase::case_02())]
1150    #[case(ExternSignatureQuilTestCase::case_03())]
1151    #[case(ExternSignatureQuilTestCase::case_04())]
1152    #[case(ExternSignatureQuilTestCase::case_05())]
1153    #[case(ExternSignatureQuilTestCase::case_05())]
1154    fn test_extern_signature_quil(#[case] test_case: ExternSignatureQuilTestCase) {
1155        assert_eq!(
1156            test_case
1157                .signature
1158                .to_quil()
1159                .expect("must be able to call to quil"),
1160            test_case.expected.to_string()
1161        );
1162    }
1163
1164    /// Test cases for the [`Call`] Quil representation.
1165    struct CallQuilTestCase {
1166        /// The call instruction to test.
1167        call: Call,
1168        /// The expected Quil representation.
1169        expected: &'static str,
1170    }
1171
1172    impl CallQuilTestCase {
1173        fn case_01() -> Self {
1174            let call = Call {
1175                name: "foo".to_string(),
1176                arguments: vec![
1177                    UnresolvedCallArgument::MemoryReference(MemoryReference {
1178                        name: "bar".to_string(),
1179                        index: 0,
1180                    }),
1181                    UnresolvedCallArgument::Immediate(Complex64::new(2.0, 0.0)),
1182                    UnresolvedCallArgument::Identifier("baz".to_string()),
1183                ],
1184            };
1185            Self {
1186                call,
1187                expected: "CALL foo bar[0] 2 baz",
1188            }
1189        }
1190
1191        fn case_02() -> Self {
1192            let call = Call {
1193                name: "foo".to_string(),
1194                arguments: vec![
1195                    UnresolvedCallArgument::MemoryReference(MemoryReference {
1196                        name: "bar".to_string(),
1197                        index: 0,
1198                    }),
1199                    UnresolvedCallArgument::Identifier("baz".to_string()),
1200                ],
1201            };
1202            Self {
1203                call,
1204                expected: "CALL foo bar[0] baz",
1205            }
1206        }
1207
1208        fn case_03() -> Self {
1209            let call = Call {
1210                name: "foo".to_string(),
1211                arguments: vec![UnresolvedCallArgument::MemoryReference(MemoryReference {
1212                    name: "bar".to_string(),
1213                    index: 0,
1214                })],
1215            };
1216            Self {
1217                call,
1218                expected: "CALL foo bar[0]",
1219            }
1220        }
1221
1222        /// No arguments.
1223        fn case_04() -> Self {
1224            let call = Call {
1225                name: "foo".to_string(),
1226                arguments: vec![],
1227            };
1228
1229            Self {
1230                call,
1231                expected: "CALL foo",
1232            }
1233        }
1234    }
1235
1236    /// Test that the Quil representation of a [`Call`] instruction is as expected.
1237    #[rstest]
1238    #[case(CallQuilTestCase::case_01())]
1239    #[case(CallQuilTestCase::case_02())]
1240    #[case(CallQuilTestCase::case_03())]
1241    #[case(CallQuilTestCase::case_04())]
1242    fn test_call_quil(#[case] test_case: CallQuilTestCase) {
1243        assert_eq!(
1244            test_case
1245                .call
1246                .to_quil()
1247                .expect("must be able to call to quil"),
1248            test_case.expected.to_string()
1249        );
1250    }
1251
1252    /// Build a set of memory regions for testing.
1253    fn build_declarations() -> IndexMap<String, MemoryRegion> {
1254        [
1255            ("integer", Vector::new(ScalarType::Integer, 3)),
1256            ("real", Vector::new(ScalarType::Real, 3)),
1257            ("bit", Vector::new(ScalarType::Bit, 3)),
1258            ("octet", Vector::new(ScalarType::Octet, 3)),
1259        ]
1260        .into_iter()
1261        .map(|(name, vector)| (name.to_string(), MemoryRegion::new(vector, None)))
1262        .collect()
1263    }
1264
1265    /// Test cases for resolving call arguments.
1266    struct ArgumentResolutionTestCase {
1267        call_argument: UnresolvedCallArgument,
1268        extern_parameter: ExternParameter,
1269        expected: Result<ResolvedCallArgument, CallArgumentResolutionError>,
1270    }
1271
1272    impl ArgumentResolutionTestCase {
1273        /// Memory reference as scalar
1274        fn case_01() -> Self {
1275            ArgumentResolutionTestCase {
1276                call_argument: UnresolvedCallArgument::MemoryReference(MemoryReference {
1277                    name: "integer".to_string(),
1278                    index: 0,
1279                }),
1280                extern_parameter: ExternParameter {
1281                    name: "bar".to_string(),
1282                    mutable: false,
1283                    data_type: ExternParameterType::Scalar(ScalarType::Integer),
1284                },
1285                expected: Ok(ResolvedCallArgument::MemoryReference {
1286                    memory_reference: MemoryReference {
1287                        name: "integer".to_string(),
1288                        index: 0,
1289                    },
1290                    scalar_type: ScalarType::Integer,
1291                    mutable: false,
1292                }),
1293            }
1294        }
1295
1296        /// Identifier as vector
1297        fn case_02() -> Self {
1298            ArgumentResolutionTestCase {
1299                call_argument: UnresolvedCallArgument::Identifier("real".to_string()),
1300                extern_parameter: ExternParameter {
1301                    name: "bar".to_string(),
1302                    mutable: false,
1303                    data_type: ExternParameterType::FixedLengthVector(Vector {
1304                        data_type: ScalarType::Real,
1305                        length: 3,
1306                    }),
1307                },
1308                expected: Ok(ResolvedCallArgument::Vector {
1309                    memory_region_name: "real".to_string(),
1310                    vector: Vector {
1311                        data_type: ScalarType::Real,
1312                        length: 3,
1313                    },
1314                    mutable: false,
1315                }),
1316            }
1317        }
1318
1319        /// Immediate value as scalar
1320        fn case_03() -> Self {
1321            ArgumentResolutionTestCase {
1322                call_argument: UnresolvedCallArgument::Immediate(Complex64::new(2.0, 0.0)),
1323                extern_parameter: ExternParameter {
1324                    name: "bar".to_string(),
1325                    mutable: false,
1326                    data_type: ExternParameterType::Scalar(ScalarType::Integer),
1327                },
1328                expected: Ok(ResolvedCallArgument::Immediate {
1329                    value: Complex64::new(2.0, 0.0),
1330                    scalar_type: ScalarType::Integer,
1331                }),
1332            }
1333        }
1334
1335        /// Undeclared identifier
1336        fn case_04() -> Self {
1337            ArgumentResolutionTestCase {
1338                call_argument: UnresolvedCallArgument::Identifier("undeclared".to_string()),
1339                extern_parameter: ExternParameter {
1340                    name: "bar".to_string(),
1341                    mutable: false,
1342                    data_type: ExternParameterType::FixedLengthVector(Vector {
1343                        data_type: ScalarType::Real,
1344                        length: 3,
1345                    }),
1346                },
1347                expected: Err(CallArgumentResolutionError::UndeclaredMemoryReference(
1348                    "undeclared".to_string(),
1349                )),
1350            }
1351        }
1352
1353        /// Undeclared memory reference
1354        fn case_05() -> Self {
1355            ArgumentResolutionTestCase {
1356                call_argument: UnresolvedCallArgument::MemoryReference(MemoryReference {
1357                    name: "undeclared".to_string(),
1358                    index: 0,
1359                }),
1360                extern_parameter: ExternParameter {
1361                    name: "bar".to_string(),
1362                    mutable: false,
1363                    data_type: ExternParameterType::Scalar(ScalarType::Integer),
1364                },
1365                expected: Err(CallArgumentResolutionError::UndeclaredMemoryReference(
1366                    "undeclared".to_string(),
1367                )),
1368            }
1369        }
1370
1371        /// Vector data type mismatch
1372        fn case_06() -> Self {
1373            ArgumentResolutionTestCase {
1374                call_argument: UnresolvedCallArgument::Identifier("integer".to_string()),
1375                extern_parameter: ExternParameter {
1376                    name: "bar".to_string(),
1377                    mutable: false,
1378                    data_type: ExternParameterType::FixedLengthVector(Vector {
1379                        data_type: ScalarType::Real,
1380                        length: 3,
1381                    }),
1382                },
1383                expected: Err(CallArgumentResolutionError::MismatchedVector {
1384                    expected: Vector {
1385                        data_type: ScalarType::Real,
1386                        length: 3,
1387                    },
1388                    found: Vector {
1389                        data_type: ScalarType::Integer,
1390                        length: 3,
1391                    },
1392                }),
1393            }
1394        }
1395
1396        /// Vector length mismatch
1397        fn case_07() -> Self {
1398            ArgumentResolutionTestCase {
1399                call_argument: UnresolvedCallArgument::Identifier("integer".to_string()),
1400                extern_parameter: ExternParameter {
1401                    name: "bar".to_string(),
1402                    mutable: false,
1403                    data_type: ExternParameterType::FixedLengthVector(Vector {
1404                        data_type: ScalarType::Integer,
1405                        length: 4,
1406                    }),
1407                },
1408                expected: Err(CallArgumentResolutionError::MismatchedVector {
1409                    expected: Vector {
1410                        data_type: ScalarType::Integer,
1411                        length: 4,
1412                    },
1413                    found: Vector {
1414                        data_type: ScalarType::Integer,
1415                        length: 3,
1416                    },
1417                }),
1418            }
1419        }
1420
1421        /// Scalar data type mismatch
1422        fn case_08() -> Self {
1423            ArgumentResolutionTestCase {
1424                call_argument: UnresolvedCallArgument::MemoryReference(MemoryReference {
1425                    name: "octet".to_string(),
1426                    index: 0,
1427                }),
1428                extern_parameter: ExternParameter {
1429                    name: "bar".to_string(),
1430                    mutable: false,
1431                    data_type: ExternParameterType::Scalar(ScalarType::Integer),
1432                },
1433                expected: Err(CallArgumentResolutionError::MismatchedScalar {
1434                    expected: ScalarType::Integer,
1435                    found: ScalarType::Octet,
1436                }),
1437            }
1438        }
1439
1440        /// Scalar arguments may be passed as identifiers, in which case `0` index is
1441        /// inferred.
1442        fn case_09() -> Self {
1443            let call_argument = UnresolvedCallArgument::Identifier("integer".to_string());
1444            ArgumentResolutionTestCase {
1445                call_argument: call_argument.clone(),
1446                extern_parameter: ExternParameter {
1447                    name: "bar".to_string(),
1448                    mutable: false,
1449                    data_type: ExternParameterType::Scalar(ScalarType::Integer),
1450                },
1451                expected: Ok(ResolvedCallArgument::MemoryReference {
1452                    memory_reference: MemoryReference::new("integer".to_string(), 0),
1453                    scalar_type: ScalarType::Integer,
1454                    mutable: false,
1455                }),
1456            }
1457        }
1458
1459        /// Vector arguments must be passed as identifiers, not memory references.
1460        fn case_10() -> Self {
1461            let call_argument = UnresolvedCallArgument::MemoryReference(MemoryReference {
1462                name: "integer".to_string(),
1463                index: 0,
1464            });
1465            ArgumentResolutionTestCase {
1466                call_argument: call_argument.clone(),
1467                extern_parameter: ExternParameter {
1468                    name: "bar".to_string(),
1469                    mutable: false,
1470                    data_type: ExternParameterType::FixedLengthVector(Vector {
1471                        data_type: ScalarType::Integer,
1472                        length: 3,
1473                    }),
1474                },
1475                expected: Err(CallArgumentResolutionError::InvalidVectorArgument(
1476                    call_argument,
1477                )),
1478            }
1479        }
1480
1481        /// Vector arguments must be passed as identifiers, not immediate values.
1482        fn case_11() -> Self {
1483            let call_argument = UnresolvedCallArgument::Immediate(Complex64::new(2.0, 0.0));
1484            ArgumentResolutionTestCase {
1485                call_argument: call_argument.clone(),
1486                extern_parameter: ExternParameter {
1487                    name: "bar".to_string(),
1488                    mutable: false,
1489                    data_type: ExternParameterType::FixedLengthVector(Vector {
1490                        data_type: ScalarType::Integer,
1491                        length: 3,
1492                    }),
1493                },
1494                expected: Err(CallArgumentResolutionError::InvalidVectorArgument(
1495                    call_argument,
1496                )),
1497            }
1498        }
1499
1500        /// Variable vector arguments are resolved to a specific vector length based on the
1501        /// declaration (see [`build_declarations`]).
1502        fn case_12() -> Self {
1503            let call_argument = UnresolvedCallArgument::Identifier("integer".to_string());
1504            ArgumentResolutionTestCase {
1505                call_argument: call_argument.clone(),
1506                extern_parameter: ExternParameter {
1507                    name: "bar".to_string(),
1508                    mutable: false,
1509                    data_type: ExternParameterType::VariableLengthVector(ScalarType::Integer),
1510                },
1511                expected: Ok(ResolvedCallArgument::Vector {
1512                    memory_region_name: "integer".to_string(),
1513                    mutable: false,
1514                    vector: Vector {
1515                        data_type: ScalarType::Integer,
1516                        length: 3,
1517                    },
1518                }),
1519            }
1520        }
1521
1522        /// Immediate arguments cannot be passed for mutable parameters.
1523        fn case_13() -> Self {
1524            let call_argument = UnresolvedCallArgument::Immediate(Complex64::new(2.0, 0.0));
1525            ArgumentResolutionTestCase {
1526                call_argument: call_argument.clone(),
1527                extern_parameter: ExternParameter {
1528                    name: "bar".to_string(),
1529                    mutable: true,
1530                    data_type: ExternParameterType::Scalar(ScalarType::Integer),
1531                },
1532                expected: Err(CallArgumentResolutionError::ImmediateArgumentForMutable(
1533                    "bar".to_string(),
1534                )),
1535            }
1536        }
1537    }
1538
1539    /// Test resolution of call arguments.
1540    #[rstest]
1541    #[case(ArgumentResolutionTestCase::case_01())]
1542    #[case(ArgumentResolutionTestCase::case_02())]
1543    #[case(ArgumentResolutionTestCase::case_03())]
1544    #[case(ArgumentResolutionTestCase::case_04())]
1545    #[case(ArgumentResolutionTestCase::case_05())]
1546    #[case(ArgumentResolutionTestCase::case_06())]
1547    #[case(ArgumentResolutionTestCase::case_07())]
1548    #[case(ArgumentResolutionTestCase::case_08())]
1549    #[case(ArgumentResolutionTestCase::case_09())]
1550    #[case(ArgumentResolutionTestCase::case_10())]
1551    #[case(ArgumentResolutionTestCase::case_11())]
1552    #[case(ArgumentResolutionTestCase::case_12())]
1553    #[case(ArgumentResolutionTestCase::case_13())]
1554    fn test_argument_resolution(#[case] test_case: ArgumentResolutionTestCase) {
1555        let memory_regions = build_declarations();
1556        let found = test_case
1557            .call_argument
1558            .resolve(&memory_regions, &test_case.extern_parameter);
1559        match (test_case.expected, found) {
1560            (Ok(expected), Ok(found)) => assert_eq!(expected, found),
1561            (Ok(expected), Err(found)) => {
1562                panic!("expected resolution {expected:?}, found err {found:?}")
1563            }
1564            (Err(expected), Ok(found)) => {
1565                panic!("expected err {expected:?}, found resolution {found:?}")
1566            }
1567            (Err(expected), Err(found)) => assert_eq!(expected, found),
1568        }
1569    }
1570
1571    /// Test cases for resolving return arguments.
1572    struct ReturnArgumentResolutionTestCase {
1573        /// The call argument to resolve.
1574        call_argument: UnresolvedCallArgument,
1575        /// The return type of the function.
1576        return_type: ScalarType,
1577        /// The expected result of the resolution.
1578        expected: Result<ResolvedCallArgument, CallArgumentResolutionError>,
1579    }
1580
1581    impl ReturnArgumentResolutionTestCase {
1582        /// Memory reference is ok.
1583        fn case_01() -> Self {
1584            let call_argument = UnresolvedCallArgument::MemoryReference(MemoryReference {
1585                name: "integer".to_string(),
1586                index: 0,
1587            });
1588            let expected = Ok(ResolvedCallArgument::MemoryReference {
1589                memory_reference: MemoryReference {
1590                    name: "integer".to_string(),
1591                    index: 0,
1592                },
1593                scalar_type: ScalarType::Integer,
1594                mutable: true,
1595            });
1596            Self {
1597                call_argument,
1598                return_type: ScalarType::Integer,
1599                expected,
1600            }
1601        }
1602
1603        /// Immediate value is not ok.
1604        fn case_02() -> Self {
1605            let call_argument = UnresolvedCallArgument::Immediate(Complex64::new(2.0, 0.0));
1606            let expected = Err(CallArgumentResolutionError::ReturnArgument {
1607                found: call_argument.clone(),
1608            });
1609            Self {
1610                call_argument,
1611                return_type: ScalarType::Integer,
1612                expected,
1613            }
1614        }
1615
1616        /// Allow plain identifiers to be upcast to memory references.
1617        fn case_03() -> Self {
1618            let call_argument = UnresolvedCallArgument::Identifier("integer".to_string());
1619            let expected = Ok(ResolvedCallArgument::MemoryReference {
1620                memory_reference: MemoryReference::new("integer".to_string(), 0),
1621                scalar_type: ScalarType::Integer,
1622                mutable: true,
1623            });
1624            Self {
1625                call_argument,
1626                return_type: ScalarType::Integer,
1627                expected,
1628            }
1629        }
1630    }
1631
1632    /// Test resolution of return arguments.
1633    #[rstest]
1634    #[case(ReturnArgumentResolutionTestCase::case_01())]
1635    #[case(ReturnArgumentResolutionTestCase::case_02())]
1636    #[case(ReturnArgumentResolutionTestCase::case_03())]
1637    fn test_return_argument_resolution(#[case] test_case: ReturnArgumentResolutionTestCase) {
1638        let memory_regions = build_declarations();
1639
1640        let found = test_case
1641            .call_argument
1642            .resolve_return(&memory_regions, test_case.return_type);
1643        match (test_case.expected, found) {
1644            (Ok(expected), Ok(found)) => assert_eq!(expected, found),
1645            (Ok(expected), Err(found)) => {
1646                panic!("expected resolution {expected:?}, found err {found:?}")
1647            }
1648            (Err(expected), Ok(found)) => {
1649                panic!("expected err {expected:?}, found resolution {found:?}")
1650            }
1651            (Err(expected), Err(found)) => assert_eq!(expected, found),
1652        }
1653    }
1654
1655    /// Test cases for resolving call arguments to a specific signature.
1656    struct ResolveToSignatureTestCase {
1657        /// The call instruction to resolve.
1658        call: Call,
1659        /// The signature to resolve to.
1660        signature: ExternSignature,
1661        /// The expected result of the resolution.
1662        expected: Result<Vec<ResolvedCallArgument>, CallSignatureError>,
1663    }
1664
1665    impl ResolveToSignatureTestCase {
1666        /// Valid match with return and parameters
1667        fn case_01() -> Self {
1668            let call = Call {
1669                name: "foo".to_string(),
1670                arguments: vec![
1671                    UnresolvedCallArgument::MemoryReference(MemoryReference {
1672                        name: "integer".to_string(),
1673                        index: 0,
1674                    }),
1675                    UnresolvedCallArgument::Immediate(Complex64::new(2.0, 0.0)),
1676                    UnresolvedCallArgument::Identifier("bit".to_string()),
1677                ],
1678            };
1679            let signature = ExternSignature {
1680                return_type: Some(ScalarType::Integer),
1681                parameters: vec![
1682                    ExternParameter {
1683                        name: "bar".to_string(),
1684                        mutable: false,
1685                        data_type: ExternParameterType::Scalar(ScalarType::Integer),
1686                    },
1687                    ExternParameter {
1688                        name: "baz".to_string(),
1689                        mutable: true,
1690                        data_type: ExternParameterType::FixedLengthVector(Vector {
1691                            data_type: ScalarType::Bit,
1692                            length: 3,
1693                        }),
1694                    },
1695                ],
1696            };
1697            let resolved = vec![
1698                ResolvedCallArgument::MemoryReference {
1699                    memory_reference: MemoryReference {
1700                        name: "integer".to_string(),
1701                        index: 0,
1702                    },
1703                    scalar_type: ScalarType::Integer,
1704                    mutable: true,
1705                },
1706                ResolvedCallArgument::Immediate {
1707                    value: Complex64::new(2.0, 0.0),
1708                    scalar_type: ScalarType::Integer,
1709                },
1710                ResolvedCallArgument::Vector {
1711                    memory_region_name: "bit".to_string(),
1712                    vector: Vector {
1713                        data_type: ScalarType::Bit,
1714                        length: 3,
1715                    },
1716                    mutable: true,
1717                },
1718            ];
1719            Self {
1720                call,
1721                signature,
1722                expected: Ok(resolved),
1723            }
1724        }
1725
1726        /// Valid match with parameteters only
1727        fn case_02() -> Self {
1728            let call = Call {
1729                name: "foo".to_string(),
1730                arguments: vec![
1731                    UnresolvedCallArgument::Immediate(Complex64::new(2.0, 0.0)),
1732                    UnresolvedCallArgument::Identifier("bit".to_string()),
1733                ],
1734            };
1735            let signature = ExternSignature {
1736                return_type: None,
1737                parameters: vec![
1738                    ExternParameter {
1739                        name: "bar".to_string(),
1740                        mutable: false,
1741                        data_type: ExternParameterType::Scalar(ScalarType::Integer),
1742                    },
1743                    ExternParameter {
1744                        name: "baz".to_string(),
1745                        mutable: true,
1746                        data_type: ExternParameterType::FixedLengthVector(Vector {
1747                            data_type: ScalarType::Bit,
1748                            length: 3,
1749                        }),
1750                    },
1751                ],
1752            };
1753            let resolved = vec![
1754                ResolvedCallArgument::Immediate {
1755                    value: Complex64::new(2.0, 0.0),
1756                    scalar_type: ScalarType::Integer,
1757                },
1758                ResolvedCallArgument::Vector {
1759                    memory_region_name: "bit".to_string(),
1760                    vector: Vector {
1761                        data_type: ScalarType::Bit,
1762                        length: 3,
1763                    },
1764                    mutable: true,
1765                },
1766            ];
1767            Self {
1768                call,
1769                signature,
1770                expected: Ok(resolved),
1771            }
1772        }
1773
1774        /// Valid match with return only
1775        fn case_03() -> Self {
1776            let call = Call {
1777                name: "foo".to_string(),
1778                arguments: vec![UnresolvedCallArgument::MemoryReference(MemoryReference {
1779                    name: "integer".to_string(),
1780                    index: 0,
1781                })],
1782            };
1783            let signature = ExternSignature {
1784                return_type: Some(ScalarType::Integer),
1785                parameters: vec![],
1786            };
1787            let resolved = vec![ResolvedCallArgument::MemoryReference {
1788                memory_reference: MemoryReference {
1789                    name: "integer".to_string(),
1790                    index: 0,
1791                },
1792                scalar_type: ScalarType::Integer,
1793                mutable: true,
1794            }];
1795            Self {
1796                call,
1797                signature,
1798                expected: Ok(resolved),
1799            }
1800        }
1801
1802        /// Parameter count mismatch with return and parameters
1803        fn case_04() -> Self {
1804            let call = Call {
1805                name: "foo".to_string(),
1806                arguments: vec![
1807                    UnresolvedCallArgument::MemoryReference(MemoryReference {
1808                        name: "integer".to_string(),
1809                        index: 0,
1810                    }),
1811                    UnresolvedCallArgument::Immediate(Complex64::new(2.0, 0.0)),
1812                    UnresolvedCallArgument::Identifier("bit".to_string()),
1813                ],
1814            };
1815            let signature = ExternSignature {
1816                return_type: Some(ScalarType::Integer),
1817                parameters: vec![ExternParameter {
1818                    name: "bar".to_string(),
1819                    mutable: false,
1820                    data_type: ExternParameterType::Scalar(ScalarType::Integer),
1821                }],
1822            };
1823
1824            Self {
1825                call,
1826                signature,
1827                expected: Err(CallSignatureError::ParameterCount {
1828                    expected: 2,
1829                    found: 3,
1830                }),
1831            }
1832        }
1833
1834        /// Parameter count mismatch return only
1835        fn case_05() -> Self {
1836            let call = Call {
1837                name: "foo".to_string(),
1838                arguments: vec![
1839                    UnresolvedCallArgument::MemoryReference(MemoryReference {
1840                        name: "integer".to_string(),
1841                        index: 0,
1842                    }),
1843                    UnresolvedCallArgument::Immediate(Complex64::new(2.0, 0.0)),
1844                ],
1845            };
1846            let signature = ExternSignature {
1847                return_type: Some(ScalarType::Integer),
1848                parameters: vec![],
1849            };
1850
1851            Self {
1852                call,
1853                signature,
1854                expected: Err(CallSignatureError::ParameterCount {
1855                    expected: 1,
1856                    found: 2,
1857                }),
1858            }
1859        }
1860
1861        /// Parameter count mismatch parameters only
1862        fn case_06() -> Self {
1863            let call = Call {
1864                name: "foo".to_string(),
1865                arguments: vec![
1866                    UnresolvedCallArgument::MemoryReference(MemoryReference {
1867                        name: "integer".to_string(),
1868                        index: 0,
1869                    }),
1870                    UnresolvedCallArgument::Immediate(Complex64::new(2.0, 0.0)),
1871                    UnresolvedCallArgument::Identifier("bit".to_string()),
1872                ],
1873            };
1874            let signature = ExternSignature {
1875                return_type: None,
1876                parameters: vec![ExternParameter {
1877                    name: "bar".to_string(),
1878                    mutable: false,
1879                    data_type: ExternParameterType::Scalar(ScalarType::Integer),
1880                }],
1881            };
1882
1883            Self {
1884                call,
1885                signature,
1886                expected: Err(CallSignatureError::ParameterCount {
1887                    expected: 1,
1888                    found: 3,
1889                }),
1890            }
1891        }
1892
1893        /// Argument mismatch
1894        fn case_07() -> Self {
1895            let call = Call {
1896                name: "foo".to_string(),
1897                arguments: vec![
1898                    UnresolvedCallArgument::Immediate(Complex64::new(2.0, 0.0)),
1899                    UnresolvedCallArgument::Identifier("bit".to_string()),
1900                ],
1901            };
1902            let signature = ExternSignature {
1903                return_type: Some(ScalarType::Integer),
1904                parameters: vec![ExternParameter {
1905                    name: "bar".to_string(),
1906                    mutable: false,
1907                    data_type: ExternParameterType::Scalar(ScalarType::Real),
1908                }],
1909            };
1910
1911            Self {
1912                call,
1913                signature,
1914                expected: Err(CallSignatureError::Arguments(vec![
1915                    CallArgumentError::Return(CallArgumentResolutionError::ReturnArgument {
1916                        found: UnresolvedCallArgument::Immediate(Complex64::new(2.0, 0.0)),
1917                    }),
1918                    CallArgumentError::Argument {
1919                        index: 0,
1920                        error: CallArgumentResolutionError::MismatchedScalar {
1921                            expected: ScalarType::Real,
1922                            found: ScalarType::Bit,
1923                        },
1924                    },
1925                ])),
1926            }
1927        }
1928    }
1929
1930    /// Test resolution of `Call` instructions to a specific signature.
1931    #[rstest]
1932    #[case(ResolveToSignatureTestCase::case_01())]
1933    #[case(ResolveToSignatureTestCase::case_02())]
1934    #[case(ResolveToSignatureTestCase::case_03())]
1935    #[case(ResolveToSignatureTestCase::case_04())]
1936    #[case(ResolveToSignatureTestCase::case_05())]
1937    #[case(ResolveToSignatureTestCase::case_06())]
1938    #[case(ResolveToSignatureTestCase::case_07())]
1939    fn test_assert_matching_signature(#[case] test_case: ResolveToSignatureTestCase) {
1940        let memory_regions = build_declarations();
1941        let found = test_case
1942            .call
1943            .resolve_to_signature(&test_case.signature, &memory_regions);
1944        match (test_case.expected, found) {
1945            (Ok(_), Ok(_)) => {}
1946            (Ok(expected), Err(found)) => {
1947                panic!("expected resolution {expected:?}, found err {found:?}")
1948            }
1949            (Err(expected), Ok(found)) => {
1950                panic!("expected err {expected:?}, found resolution {found:?}")
1951            }
1952            (Err(expected), Err(found)) => assert_eq!(expected, found),
1953        }
1954    }
1955
1956    /// Test cases for call resolution against an [`ExternSignatureMap`].
1957    struct CallResolutionTestCase {
1958        /// The call instruction to resolve.
1959        call: Call,
1960        /// The set of extern definitions to resolve against.
1961        extern_signature_map: ExternSignatureMap,
1962        /// The expected result of the resolution.
1963        expected: Result<Vec<ResolvedCallArgument>, CallResolutionError>,
1964    }
1965
1966    impl CallResolutionTestCase {
1967        /// Valid resolution
1968        fn case_01() -> Self {
1969            let call = Call {
1970                name: "foo".to_string(),
1971                arguments: vec![UnresolvedCallArgument::MemoryReference(MemoryReference {
1972                    name: "integer".to_string(),
1973                    index: 0,
1974                })],
1975            };
1976            let signature = ExternSignature {
1977                return_type: Some(ScalarType::Integer),
1978                parameters: vec![],
1979            };
1980            let resolved = vec![ResolvedCallArgument::MemoryReference {
1981                memory_reference: MemoryReference {
1982                    name: "integer".to_string(),
1983                    index: 0,
1984                },
1985                scalar_type: ScalarType::Integer,
1986                mutable: true,
1987            }];
1988            Self {
1989                call,
1990                extern_signature_map: ExternSignatureMap(
1991                    [("foo".to_string(), signature)].iter().cloned().collect(),
1992                ),
1993                expected: Ok(resolved),
1994            }
1995        }
1996
1997        /// Signature does not match
1998        fn case_02() -> Self {
1999            let call = Call {
2000                name: "foo".to_string(),
2001                arguments: vec![UnresolvedCallArgument::MemoryReference(MemoryReference {
2002                    name: "integer".to_string(),
2003                    index: 0,
2004                })],
2005            };
2006            let signature = ExternSignature {
2007                return_type: Some(ScalarType::Real),
2008                parameters: vec![],
2009            };
2010            Self {
2011                call,
2012                extern_signature_map: ExternSignatureMap(
2013                    [("foo".to_string(), signature)].iter().cloned().collect(),
2014                ),
2015                expected: Err(CallResolutionError::Signature {
2016                    name: "foo".to_string(),
2017                    error: CallSignatureError::Arguments(vec![CallArgumentError::Return(
2018                        CallArgumentResolutionError::MismatchedScalar {
2019                            expected: ScalarType::Real,
2020                            found: ScalarType::Integer,
2021                        },
2022                    )]),
2023                }),
2024            }
2025        }
2026
2027        /// No corresponding extern definition
2028        fn case_03() -> Self {
2029            let call = Call {
2030                name: "undeclared".to_string(),
2031                arguments: vec![UnresolvedCallArgument::MemoryReference(MemoryReference {
2032                    name: "integer".to_string(),
2033                    index: 0,
2034                })],
2035            };
2036            let signature = ExternSignature {
2037                return_type: Some(ScalarType::Real),
2038                parameters: vec![],
2039            };
2040            Self {
2041                call,
2042                extern_signature_map: ExternSignatureMap(
2043                    [("foo".to_string(), signature)].iter().cloned().collect(),
2044                ),
2045                expected: Err(CallResolutionError::NoMatchingExternInstruction(
2046                    "undeclared".to_string(),
2047                )),
2048            }
2049        }
2050    }
2051
2052    /// Test resolution of [`Call`] instructions against a set of extern definitions.
2053    #[rstest]
2054    #[case(CallResolutionTestCase::case_01())]
2055    #[case(CallResolutionTestCase::case_02())]
2056    #[case(CallResolutionTestCase::case_03())]
2057    fn test_call_resolution(#[case] test_case: CallResolutionTestCase) {
2058        let memory_regions = build_declarations();
2059        let found = test_case
2060            .call
2061            .resolve_arguments(&memory_regions, &test_case.extern_signature_map);
2062        match (test_case.expected, found) {
2063            (Ok(expected), Ok(found)) => {
2064                assert_eq!(expected, found);
2065            }
2066            (Ok(expected), Err(found)) => {
2067                panic!("expected resolution {expected:?}, found err {found:?}")
2068            }
2069            (Err(expected), Ok(_)) => {
2070                panic!(
2071                    "expected err {:?}, found resolution {:?}",
2072                    expected, test_case.call.arguments
2073                )
2074            }
2075            (Err(expected), Err(found)) => assert_eq!(expected, found),
2076        }
2077    }
2078
2079    /// Test cases for converting [`ExternPragmaMap`] to [`ExternSignatureMap`].
2080    struct ExternPragmaMapConverstionTestCase {
2081        /// The set of extern definitions to validate.
2082        extern_pragma_map: ExternPragmaMap,
2083        /// The expected result of the validation.
2084        expected: Result<ExternSignatureMap, ExternError>,
2085    }
2086
2087    impl ExternPragmaMapConverstionTestCase {
2088        /// Valid [`ExternPragmaMap`]s.
2089        fn case_01() -> Self {
2090            let pragma1 = Pragma {
2091                name: RESERVED_PRAGMA_EXTERN.to_string(),
2092                arguments: vec![PragmaArgument::Identifier("foo".to_string())],
2093                data: Some("(bar : INTEGER)".to_string()),
2094            };
2095            let signature1 = ExternSignature {
2096                return_type: None,
2097                parameters: vec![ExternParameter {
2098                    name: "bar".to_string(),
2099                    mutable: false,
2100                    data_type: ExternParameterType::Scalar(ScalarType::Integer),
2101                }],
2102            };
2103            let pragma2 = Pragma {
2104                name: RESERVED_PRAGMA_EXTERN.to_string(),
2105                arguments: vec![PragmaArgument::Identifier("baz".to_string())],
2106                data: Some("REAL (biz : REAL)".to_string()),
2107            };
2108            let signature2 = ExternSignature {
2109                return_type: Some(ScalarType::Real),
2110                parameters: vec![ExternParameter {
2111                    name: "biz".to_string(),
2112                    mutable: false,
2113                    data_type: ExternParameterType::Scalar(ScalarType::Real),
2114                }],
2115            };
2116            let pragma3 = Pragma {
2117                name: RESERVED_PRAGMA_EXTERN.to_string(),
2118                arguments: vec![PragmaArgument::Identifier("buzz".to_string())],
2119                data: Some("OCTET".to_string()),
2120            };
2121            let signature3 = ExternSignature {
2122                return_type: Some(ScalarType::Octet),
2123                parameters: vec![],
2124            };
2125            Self {
2126                extern_pragma_map: ExternPragmaMap(
2127                    [("foo", pragma1), ("baz", pragma2), ("buzz", pragma3)]
2128                        .into_iter()
2129                        .map(|(name, pragma)| (Some(name.to_string()), pragma))
2130                        .collect(),
2131                ),
2132                expected: Ok(ExternSignatureMap(
2133                    [
2134                        ("foo", signature1),
2135                        ("baz", signature2),
2136                        ("buzz", signature3),
2137                    ]
2138                    .into_iter()
2139                    .map(|(name, signature)| (name.to_string(), signature))
2140                    .collect(),
2141                )),
2142            }
2143        }
2144
2145        /// No Signature
2146        fn case_02() -> Self {
2147            let pragma = Pragma {
2148                name: RESERVED_PRAGMA_EXTERN.to_string(),
2149                arguments: vec![PragmaArgument::Identifier("foo".to_string())],
2150                data: None,
2151            };
2152            let expected = Err(ExternError::NoSignature);
2153            Self {
2154                extern_pragma_map: ExternPragmaMap(
2155                    [(Some("foo".to_string()), pragma)].into_iter().collect(),
2156                ),
2157                expected,
2158            }
2159        }
2160
2161        /// No return nor parameters
2162        fn case_03() -> Self {
2163            let pragma = Pragma {
2164                name: RESERVED_PRAGMA_EXTERN.to_string(),
2165                arguments: vec![PragmaArgument::Identifier("foo".to_string())],
2166                data: Some("()".to_string()),
2167            };
2168            let expected = Err(ExternError::NoReturnOrParameters);
2169            Self {
2170                extern_pragma_map: ExternPragmaMap(
2171                    [(Some("foo".to_string()), pragma)].into_iter().collect(),
2172                ),
2173                expected,
2174            }
2175        }
2176
2177        /// No name
2178        fn case_04() -> Self {
2179            let pragma = Pragma {
2180                name: RESERVED_PRAGMA_EXTERN.to_string(),
2181                arguments: vec![],
2182                data: Some("(bar : REAL)".to_string()),
2183            };
2184            let expected = Err(ExternError::NoName);
2185            Self {
2186                extern_pragma_map: ExternPragmaMap([(None, pragma)].into_iter().collect()),
2187                expected,
2188            }
2189        }
2190
2191        /// Not extern
2192        fn case_05() -> Self {
2193            let pragma = Pragma {
2194                name: "NOTEXTERN".to_string(),
2195                arguments: vec![PragmaArgument::Identifier("foo".to_string())],
2196                data: Some("(bar : REAL)".to_string()),
2197            };
2198            let expected = Err(ExternError::NoName);
2199            Self {
2200                extern_pragma_map: ExternPragmaMap([(None, pragma)].into_iter().collect()),
2201                expected,
2202            }
2203        }
2204
2205        /// Extraneous arguments
2206        fn case_06() -> Self {
2207            let pragma = Pragma {
2208                name: RESERVED_PRAGMA_EXTERN.to_string(),
2209                arguments: vec![
2210                    PragmaArgument::Identifier("foo".to_string()),
2211                    PragmaArgument::Identifier("bar".to_string()),
2212                ],
2213                data: Some("OCTET".to_string()),
2214            };
2215            let expected = Err(ExternError::NoName);
2216            Self {
2217                extern_pragma_map: ExternPragmaMap([(None, pragma)].into_iter().collect()),
2218                expected,
2219            }
2220        }
2221
2222        /// Integer is not a name
2223        fn case_07() -> Self {
2224            let pragma = Pragma {
2225                name: RESERVED_PRAGMA_EXTERN.to_string(),
2226                arguments: vec![PragmaArgument::Integer(0)],
2227                data: Some("OCTET".to_string()),
2228            };
2229            let expected = Err(ExternError::NoName);
2230            Self {
2231                extern_pragma_map: ExternPragmaMap([(None, pragma)].into_iter().collect()),
2232                expected,
2233            }
2234        }
2235
2236        /// Lex error
2237        fn case_08() -> Self {
2238            let pragma = Pragma {
2239                name: RESERVED_PRAGMA_EXTERN.to_string(),
2240                arguments: vec![PragmaArgument::Identifier("foo".to_string())],
2241                data: Some("OCTET (ㆆ _ ㆆ)".to_string()),
2242            };
2243            let expected = Err(ExternSignature::from_str("OCTET (ㆆ _ ㆆ)").unwrap_err());
2244            Self {
2245                extern_pragma_map: ExternPragmaMap(
2246                    [(Some("foo".to_string()), pragma)].into_iter().collect(),
2247                ),
2248                expected,
2249            }
2250        }
2251
2252        /// Syntax error - missing parenthesis
2253        fn case_09() -> Self {
2254            let pragma = Pragma {
2255                name: RESERVED_PRAGMA_EXTERN.to_string(),
2256                arguments: vec![PragmaArgument::Identifier("foo".to_string())],
2257                data: Some("OCTET (bar : INTEGER".to_string()),
2258            };
2259            let expected = Err(ExternSignature::from_str("OCTET (bar : INTEGER").unwrap_err());
2260            Self {
2261                extern_pragma_map: ExternPragmaMap(
2262                    [(Some("foo".to_string()), pragma)].into_iter().collect(),
2263                ),
2264                expected,
2265            }
2266        }
2267    }
2268
2269    /// Test conversion of [`ExternPragmaMap`] to [`ExternSignatureMap`].
2270    #[rstest]
2271    #[case(ExternPragmaMapConverstionTestCase::case_01())]
2272    #[case(ExternPragmaMapConverstionTestCase::case_02())]
2273    #[case(ExternPragmaMapConverstionTestCase::case_03())]
2274    #[case(ExternPragmaMapConverstionTestCase::case_04())]
2275    #[case(ExternPragmaMapConverstionTestCase::case_05())]
2276    #[case(ExternPragmaMapConverstionTestCase::case_06())]
2277    #[case(ExternPragmaMapConverstionTestCase::case_07())]
2278    #[case(ExternPragmaMapConverstionTestCase::case_08())]
2279    #[case(ExternPragmaMapConverstionTestCase::case_09())]
2280    fn test_extern_signature_map_validation(#[case] test_case: ExternPragmaMapConverstionTestCase) {
2281        let found = ExternSignatureMap::try_from(test_case.extern_pragma_map);
2282        match (test_case.expected, found) {
2283            (Ok(expected), Ok(found)) => {
2284                assert_eq!(expected, found);
2285            }
2286            (Ok(_), Err(found)) => {
2287                panic!("expected valid, found err {found:?}")
2288            }
2289            (Err(expected), Ok(_)) => {
2290                panic!("expected err {expected:?}, found valid")
2291            }
2292            (Err(expected), Err((_, found))) => assert_eq!(expected, found),
2293        }
2294    }
2295
2296    /// Test cases for parsing [`ExternSignature`]s.
2297    struct ExternSignatureFromStrTestCase {
2298        /// This string to parse.
2299        input: &'static str,
2300        /// The parsing result.
2301        expected: Result<ExternSignature, ExternError>,
2302    }
2303
2304    impl ExternSignatureFromStrTestCase {
2305        /// Empty signature
2306        fn case_01() -> Self {
2307            Self {
2308                input: "",
2309                expected: Err(ExternError::NoReturnOrParameters),
2310            }
2311        }
2312
2313        /// Empty signature with parentheses
2314        fn case_02() -> Self {
2315            Self {
2316                input: "()",
2317                expected: Err(ExternError::NoReturnOrParameters),
2318            }
2319        }
2320
2321        /// Return without parameters
2322        fn case_03() -> Self {
2323            Self {
2324                input: "INTEGER",
2325                expected: Ok(crate::instruction::ExternSignature {
2326                    return_type: Some(ScalarType::Integer),
2327                    parameters: vec![],
2328                }),
2329            }
2330        }
2331
2332        /// Return with empty parentheses
2333        fn case_04() -> Self {
2334            Self {
2335                input: "INTEGER ()",
2336                expected: Ok(crate::instruction::ExternSignature {
2337                    return_type: Some(ScalarType::Integer),
2338                    parameters: vec![],
2339                }),
2340            }
2341        }
2342
2343        /// Return with parameters
2344        fn case_05() -> Self {
2345            Self {
2346                input: "INTEGER (bar: REAL, baz: BIT[10], biz: mut OCTET)",
2347                expected: Ok(crate::instruction::ExternSignature {
2348                    return_type: Some(ScalarType::Integer),
2349                    parameters: vec![
2350                        ExternParameter {
2351                            name: "bar".to_string(),
2352                            mutable: false,
2353                            data_type: ExternParameterType::Scalar(ScalarType::Real),
2354                        },
2355                        ExternParameter {
2356                            name: "baz".to_string(),
2357                            mutable: false,
2358                            data_type: ExternParameterType::FixedLengthVector(Vector {
2359                                data_type: ScalarType::Bit,
2360                                length: 10,
2361                            }),
2362                        },
2363                        ExternParameter {
2364                            name: "biz".to_string(),
2365                            mutable: true,
2366                            data_type: ExternParameterType::Scalar(ScalarType::Octet),
2367                        },
2368                    ],
2369                }),
2370            }
2371        }
2372
2373        /// Parameters without return
2374        fn case_06() -> Self {
2375            Self {
2376                input: "(bar: REAL, baz: BIT[10], biz : mut OCTET)",
2377                expected: Ok(crate::instruction::ExternSignature {
2378                    return_type: None,
2379                    parameters: vec![
2380                        ExternParameter {
2381                            name: "bar".to_string(),
2382                            mutable: false,
2383                            data_type: ExternParameterType::Scalar(ScalarType::Real),
2384                        },
2385                        ExternParameter {
2386                            name: "baz".to_string(),
2387                            mutable: false,
2388                            data_type: ExternParameterType::FixedLengthVector(Vector {
2389                                data_type: ScalarType::Bit,
2390                                length: 10,
2391                            }),
2392                        },
2393                        ExternParameter {
2394                            name: "biz".to_string(),
2395                            mutable: true,
2396                            data_type: ExternParameterType::Scalar(ScalarType::Octet),
2397                        },
2398                    ],
2399                }),
2400            }
2401        }
2402
2403        /// Variable length vector.
2404        fn case_07() -> Self {
2405            Self {
2406                input: "(bar : mut REAL[])",
2407                expected: Ok(crate::instruction::ExternSignature {
2408                    return_type: None,
2409                    parameters: vec![ExternParameter {
2410                        name: "bar".to_string(),
2411                        mutable: true,
2412                        data_type: ExternParameterType::VariableLengthVector(ScalarType::Real),
2413                    }],
2414                }),
2415            }
2416        }
2417    }
2418
2419    /// Test parsing of `PRAGMA EXTERN` instructions.
2420    #[rstest]
2421    #[case(ExternSignatureFromStrTestCase::case_01())]
2422    #[case(ExternSignatureFromStrTestCase::case_02())]
2423    #[case(ExternSignatureFromStrTestCase::case_03())]
2424    #[case(ExternSignatureFromStrTestCase::case_04())]
2425    #[case(ExternSignatureFromStrTestCase::case_05())]
2426    #[case(ExternSignatureFromStrTestCase::case_06())]
2427    #[case(ExternSignatureFromStrTestCase::case_07())]
2428    fn test_parse_reserved_pragma_extern(#[case] test_case: ExternSignatureFromStrTestCase) {
2429        match (
2430            test_case.expected,
2431            ExternSignature::from_str(test_case.input),
2432        ) {
2433            (Ok(expected), Ok(parsed)) => {
2434                assert_eq!(expected, parsed);
2435            }
2436            (Ok(expected), Err(e)) => {
2437                panic!("Expected {expected:?}, got error: {e:?}");
2438            }
2439            (Err(expected), Ok(parsed)) => {
2440                panic!("Expected error: {expected:?}, got {parsed:?}");
2441            }
2442            (Err(expected), Err(found)) => {
2443                let expected = format!("{expected:?}");
2444                let found = format!("{found:?}");
2445                assert!(found.contains(&expected), "`{expected}` not in `{found}`");
2446            }
2447        }
2448    }
2449}