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