From bcf0f51274d91e20733caeaf373e0d6708259b0c Mon Sep 17 00:00:00 2001 From: orpuente-MS <156957451+orpuente-MS@users.noreply.github.com> Date: Sun, 2 Feb 2025 15:07:06 -0800 Subject: [PATCH 001/108] Implement OpenQASM 3 lexer (#2129) This PR adds our own implementation of an OpenQASM3 parser to the `compiler/qsc_qasm3` directory. --- Cargo.lock | 2 +- compiler/qsc_qasm3/Cargo.toml | 2 +- compiler/qsc_qasm3/src/keyword.rs | 104 ++ compiler/qsc_qasm3/src/lex.rs | 51 + compiler/qsc_qasm3/src/lex/cooked.rs | 696 ++++++++++++ compiler/qsc_qasm3/src/lex/cooked/tests.rs | 941 +++++++++++++++++ compiler/qsc_qasm3/src/lex/raw.rs | 601 +++++++++++ compiler/qsc_qasm3/src/lex/raw/tests.rs | 1112 ++++++++++++++++++++ compiler/qsc_qasm3/src/lib.rs | 2 + 9 files changed, 3509 insertions(+), 2 deletions(-) create mode 100644 compiler/qsc_qasm3/src/keyword.rs create mode 100644 compiler/qsc_qasm3/src/lex.rs create mode 100644 compiler/qsc_qasm3/src/lex/cooked.rs create mode 100644 compiler/qsc_qasm3/src/lex/cooked/tests.rs create mode 100644 compiler/qsc_qasm3/src/lex/raw.rs create mode 100644 compiler/qsc_qasm3/src/lex/raw/tests.rs diff --git a/Cargo.lock b/Cargo.lock index 340efdc04c..46bef4aeb9 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1478,11 +1478,11 @@ version = "0.0.0" dependencies = [ "bitflags 2.6.0", "difference", + "enum-iterator", "expect-test", "indoc", "miette", "num-bigint", - "oq3_lexer", "oq3_parser", "oq3_semantics", "oq3_source_file", diff --git a/compiler/qsc_qasm3/Cargo.toml b/compiler/qsc_qasm3/Cargo.toml index b7a5d9f294..4be5b3092d 100644 --- a/compiler/qsc_qasm3/Cargo.toml +++ b/compiler/qsc_qasm3/Cargo.toml @@ -9,6 +9,7 @@ version.workspace = true [dependencies] bitflags = { workspace = true } +enum-iterator = { workspace = true } num-bigint = { workspace = true } miette = { workspace = true } qsc_ast = { path = "../qsc_ast" } @@ -20,7 +21,6 @@ thiserror = { workspace = true } oq3_source_file = { workspace = true } oq3_syntax = { workspace = true } oq3_parser = { workspace = true } -oq3_lexer = { workspace = true } oq3_semantics = { workspace = true } [dev-dependencies] diff --git a/compiler/qsc_qasm3/src/keyword.rs b/compiler/qsc_qasm3/src/keyword.rs new file mode 100644 index 0000000000..dd30c8bd02 --- /dev/null +++ b/compiler/qsc_qasm3/src/keyword.rs @@ -0,0 +1,104 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +use enum_iterator::Sequence; +use std::{ + fmt::{self, Display, Formatter}, + str::FromStr, +}; + +#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq, Sequence)] +pub enum Keyword { + Box, + Break, + Cal, + Case, + Continue, + Def, + Default, + Defcalgrammar, + Else, + End, + Extern, + For, + Gate, + If, + In, + Include, + Let, + OpenQASM, + Pragma, + Return, + Switch, + While, +} + +impl Keyword { + pub(super) fn as_str(self) -> &'static str { + match self { + Keyword::Box => "box", + Keyword::Break => "break", + Keyword::Cal => "cal", + Keyword::Case => "case", + Keyword::Continue => "continue", + Keyword::Def => "def", + Keyword::Default => "default", + Keyword::Defcalgrammar => "defcalgrammar", + Keyword::Else => "else", + Keyword::End => "end", + Keyword::Extern => "extern", + Keyword::For => "for", + Keyword::Gate => "gate", + Keyword::If => "if", + Keyword::In => "in", + Keyword::Include => "include", + Keyword::Let => "let", + Keyword::OpenQASM => "openqasm", + Keyword::Pragma => "pragma", + Keyword::Return => "return", + Keyword::Switch => "switch", + Keyword::While => "while", + } + } +} + +impl Display for Keyword { + fn fmt(&self, f: &mut Formatter) -> fmt::Result { + f.write_str(self.as_str()) + } +} + +impl FromStr for Keyword { + type Err = (); + + // This is a hot function. Use a match expression so that the Rust compiler + // can optimize the string comparisons better, and order the cases by + // frequency in Q# so that fewer comparisons are needed on average. + fn from_str(s: &str) -> Result { + match s { + "box" => Ok(Self::Box), + "break" => Ok(Self::Break), + "cal" => Ok(Self::Cal), + "case" => Ok(Self::Case), + "continue" => Ok(Self::Continue), + "def" => Ok(Self::Def), + "default" => Ok(Self::Default), + "defcalgrammar" => Ok(Self::Defcalgrammar), + "else" => Ok(Self::Else), + "end" => Ok(Self::End), + "extern" => Ok(Self::Extern), + "for" => Ok(Self::For), + "gate" => Ok(Self::Gate), + "if" => Ok(Self::If), + "in" => Ok(Self::In), + "include" => Ok(Self::Include), + "let" => Ok(Self::Let), + "openqasm" => Ok(Self::OpenQASM), + "pragma" => Ok(Self::Pragma), + "return" => Ok(Self::Return), + "switch" => Ok(Self::Switch), + "while" => Ok(Self::While), + _ => Err(()), + } + } +} diff --git a/compiler/qsc_qasm3/src/lex.rs b/compiler/qsc_qasm3/src/lex.rs new file mode 100644 index 0000000000..34683c5259 --- /dev/null +++ b/compiler/qsc_qasm3/src/lex.rs @@ -0,0 +1,51 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. +#![allow(unused)] + +pub mod cooked; +pub mod raw; +use enum_iterator::Sequence; + +pub(super) use cooked::{Error, Lexer, Token, TokenKind}; + +/// A delimiter token. +#[derive(Clone, Copy, Debug, Eq, PartialEq, Sequence)] +pub enum Delim { + /// `{` or `}` + Brace, + /// `[` or `]` + Bracket, + /// `(` or `)` + Paren, +} + +#[derive(Clone, Copy, Debug, Eq, PartialEq, Sequence)] +pub enum Radix { + Binary, + Octal, + Decimal, + Hexadecimal, +} + +impl From for u32 { + fn from(value: Radix) -> Self { + match value { + Radix::Binary => 2, + Radix::Octal => 8, + Radix::Decimal => 10, + Radix::Hexadecimal => 16, + } + } +} + +#[derive(Clone, Copy, Debug, Eq, PartialEq, Sequence)] +pub enum InterpolatedStart { + DollarQuote, + RBrace, +} + +#[derive(Clone, Copy, Debug, Eq, PartialEq, Sequence)] +pub enum InterpolatedEnding { + Quote, + LBrace, +} diff --git a/compiler/qsc_qasm3/src/lex/cooked.rs b/compiler/qsc_qasm3/src/lex/cooked.rs new file mode 100644 index 0000000000..faebc1bb28 --- /dev/null +++ b/compiler/qsc_qasm3/src/lex/cooked.rs @@ -0,0 +1,696 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +//! The second lexing phase "cooks" a raw token stream, transforming them into tokens that directly +//! correspond to components in the `OpenQASM` grammar. Keywords are treated as identifiers, except `and` +//! and `or`, which are cooked into [`BinaryOperator`] so that `and=` and `or=` are lexed correctly. +//! +//! Whitespace and comment tokens are discarded; this means that cooked tokens are not necessarily +//! contiguous, so they include both a starting and ending byte offset. +//! +//! Tokens never contain substrings from the original input, but are simply labels that refer back +//! to regions in the input. Lexing never fails, but may produce error tokens. + +#[cfg(test)] +mod tests; + +use super::{ + raw::{self, Number, Single}, + Delim, Radix, +}; +use crate::keyword::Keyword; +use enum_iterator::Sequence; +use miette::Diagnostic; +use qsc_data_structures::span::Span; +use std::{ + fmt::{self, Display, Formatter}, + iter::Peekable, + str::FromStr, +}; +use thiserror::Error; + +#[derive(Clone, Copy, Debug, Eq, PartialEq)] +pub(crate) struct Token { + pub(crate) kind: TokenKind, + pub(crate) span: Span, +} + +#[derive(Clone, Copy, Debug, Diagnostic, Eq, Error, PartialEq)] +pub enum Error { + #[error("expected {0} to complete {1}, found {2}")] + #[diagnostic(code("Qasm3.Lex.Incomplete"))] + Incomplete(raw::TokenKind, TokenKind, raw::TokenKind, #[label] Span), + + #[error("expected {0} to complete {1}, found EOF")] + #[diagnostic(code("Qasm3.Lex.IncompleteEof"))] + IncompleteEof(raw::TokenKind, TokenKind, #[label] Span), + + #[error("unterminated string literal")] + #[diagnostic(code("Qasm3.Lex.UnterminatedString"))] + UnterminatedString(#[label] Span), + + #[error("unrecognized character `{0}`")] + #[diagnostic(code("Qasm3.Lex.UnknownChar"))] + Unknown(char, #[label] Span), +} + +impl Error { + pub(crate) fn with_offset(self, offset: u32) -> Self { + match self { + Self::Incomplete(expected, token, actual, span) => { + Self::Incomplete(expected, token, actual, span + offset) + } + Self::IncompleteEof(expected, token, span) => { + Self::IncompleteEof(expected, token, span + offset) + } + Self::UnterminatedString(span) => Self::UnterminatedString(span + offset), + Self::Unknown(c, span) => Self::Unknown(c, span + offset), + } + } + + pub(crate) fn span(self) -> Span { + match self { + Error::Incomplete(_, _, _, s) + | Error::IncompleteEof(_, _, s) + | Error::UnterminatedString(s) + | Error::Unknown(_, s) => s, + } + } +} + +/// A token kind. +#[derive(Clone, Copy, Debug, Eq, PartialEq, Sequence)] +pub enum TokenKind { + Annotation, + Keyword(Keyword), + Type(Type), + + // Builtin identifiers and operations + GPhase, + Inv, + Pow, + Ctrl, + NegCtrl, + Dim, + DurationOf, + Delay, + Reset, + Measure, + Barrier, + + Literal(Literal), + + // Symbols + /// `{[(` + Open(Delim), + /// `}])` + Close(Delim), + + // Punctuation + /// `:` + Colon, + /// `;` + Semicolon, + /// `.` + Dot, + /// `,` + Comma, + /// `++` + PlusPlus, + /// `->` + Arrow, + + // Operators, + ClosedBinOp(ClosedBinOp), + BinOpEq(ClosedBinOp), + ComparisonOp(ComparisonOp), + /// `=` + Eq, + /// `!` + Bang, + /// `~` + Tilde, + + Identifier, + HardwareQubit, +} + +impl Display for TokenKind { + fn fmt(&self, f: &mut Formatter) -> fmt::Result { + match self { + TokenKind::Annotation => write!(f, "annotation"), + TokenKind::Keyword(keyword) => write!(f, "keyword `{keyword}`"), + TokenKind::Type(type_) => write!(f, "keyword `{type_}`"), + TokenKind::GPhase => write!(f, "gphase"), + TokenKind::Inv => write!(f, "inv"), + TokenKind::Pow => write!(f, "pow"), + TokenKind::Ctrl => write!(f, "ctrl"), + TokenKind::NegCtrl => write!(f, "negctrl"), + TokenKind::Dim => write!(f, "dim"), + TokenKind::DurationOf => write!(f, "durationof"), + TokenKind::Delay => write!(f, "delay"), + TokenKind::Reset => write!(f, "reset"), + TokenKind::Measure => write!(f, "measure"), + TokenKind::Barrier => write!(f, "barrier"), + TokenKind::Literal(literal) => write!(f, "literal `{literal}`"), + TokenKind::Open(Delim::Brace) => write!(f, "`{{`"), + TokenKind::Open(Delim::Bracket) => write!(f, "`[`"), + TokenKind::Open(Delim::Paren) => write!(f, "`(`"), + TokenKind::Close(Delim::Brace) => write!(f, "`}}`"), + TokenKind::Close(Delim::Bracket) => write!(f, "`]`"), + TokenKind::Close(Delim::Paren) => write!(f, "`)`"), + TokenKind::Colon => write!(f, "`:`"), + TokenKind::Semicolon => write!(f, "`;`"), + TokenKind::Dot => write!(f, "`.`"), + TokenKind::Comma => write!(f, "`,`"), + TokenKind::PlusPlus => write!(f, "`++`"), + TokenKind::Arrow => write!(f, "`->`"), + TokenKind::ClosedBinOp(op) => write!(f, "`{op}`"), + TokenKind::BinOpEq(op) => write!(f, "`{op}=`"), + TokenKind::ComparisonOp(op) => write!(f, "`{op}`"), + TokenKind::Eq => write!(f, "`=`"), + TokenKind::Bang => write!(f, "`!`"), + TokenKind::Tilde => write!(f, "`~`"), + TokenKind::Identifier => write!(f, "identifier"), + TokenKind::HardwareQubit => write!(f, "hardware bit"), + } + } +} + +impl From for TokenKind { + fn from(value: Number) -> Self { + match value { + Number::Float => Self::Literal(Literal::Float), + Number::Int(radix) => Self::Literal(Literal::Integer(radix)), + } + } +} + +#[derive(Clone, Copy, Debug, Eq, PartialEq, Sequence)] +pub enum Type { + Input, + Output, + Const, + Readonly, + Mutable, + + QReg, + Qubit, + + CReg, + Bool, + Bit, + Int, + UInt, + Float, + Angle, + Complex, + Array, + Void, + + Duration, + Stretch, +} + +impl Display for Type { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + f.write_str(match self { + Type::Input => "input", + Type::Output => "output", + Type::Const => "const", + Type::Readonly => "readonly", + Type::Mutable => "mutable", + Type::QReg => "qreg", + Type::Qubit => "qubit", + Type::CReg => "creg", + Type::Bool => "bool", + Type::Bit => "bit", + Type::Int => "int", + Type::UInt => "uint", + Type::Float => "float", + Type::Angle => "angle", + Type::Complex => "complex", + Type::Array => "array", + Type::Void => "void", + Type::Duration => "duration", + Type::Stretch => "stretch", + }) + } +} + +impl FromStr for Type { + type Err = (); + + fn from_str(s: &str) -> Result { + match s { + "input" => Ok(Type::Input), + "output" => Ok(Type::Output), + "const" => Ok(Type::Const), + "readonly" => Ok(Type::Readonly), + "mutable" => Ok(Type::Mutable), + "qreg" => Ok(Type::QReg), + "qubit" => Ok(Type::Qubit), + "creg" => Ok(Type::CReg), + "bool" => Ok(Type::Bool), + "bit" => Ok(Type::Bit), + "int" => Ok(Type::Int), + "uint" => Ok(Type::UInt), + "float" => Ok(Type::Float), + "angle" => Ok(Type::Angle), + "complex" => Ok(Type::Complex), + "array" => Ok(Type::Array), + "void" => Ok(Type::Void), + "duration" => Ok(Type::Duration), + "stretch" => Ok(Type::Stretch), + _ => Err(()), + } + } +} + +#[derive(Clone, Copy, Debug, Eq, PartialEq, Sequence)] +pub enum Literal { + Bitstring, + Boolean, + Float, + Imaginary, + Integer(Radix), + String, + Timing(TimingLiteralKind), +} + +impl Display for Literal { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + f.write_str(match self { + Literal::Bitstring => "bitstring", + Literal::Boolean => "boolean", + Literal::Float => "float", + Literal::Imaginary => "imaginary", + Literal::Integer(_) => "integer", + Literal::String => "string", + Literal::Timing(_) => "timing", + }) + } +} + +#[derive(Clone, Copy, Debug, Eq, PartialEq, Sequence)] +pub enum TimingLiteralKind { + /// Timing literal: Backend-dependent unit. + /// Equivalent to the duration of one waveform sample on the backend. + Dt, + /// Timing literal: Nanoseconds. + Ns, + /// Timing literal: Microseconds. + Us, + /// Timing literal: Milliseconds. + Ms, + /// Timing literal: Seconds. + S, +} + +/// A binary operator that returns the same type as the type of its first operand; in other words, +/// the domain of the first operand is closed under this operation. These are candidates for +/// compound assignment operators, like `+=`. +#[derive(Clone, Copy, Debug, Eq, PartialEq, Sequence)] +pub enum ClosedBinOp { + /// `&` + Amp, + /// `&&` + AmpAmp, + /// `|` + Bar, + /// `||` + BarBar, + /// `^` + Caret, + /// `>>` + GtGt, + /// `<<` + LtLt, + /// `-` + Minus, + /// `%` + Percent, + /// `+` + Plus, + /// `/` + Slash, + /// `*` + Star, + /// `**` + StarStar, + // Note: Missing Tilde according to qasm3Lexer.g4 to be able to express ~= + // But this is this a bug in the official qasm lexer? +} + +impl Display for ClosedBinOp { + fn fmt(&self, f: &mut Formatter) -> fmt::Result { + f.write_str(match self { + ClosedBinOp::Amp => "&", + ClosedBinOp::AmpAmp => "&&", + ClosedBinOp::Bar => "|", + ClosedBinOp::BarBar => "||", + ClosedBinOp::Caret => "^", + ClosedBinOp::GtGt => ">>", + ClosedBinOp::LtLt => "<<", + ClosedBinOp::Minus => "-", + ClosedBinOp::Percent => "%", + ClosedBinOp::Plus => "+", + ClosedBinOp::Slash => "/", + ClosedBinOp::Star => "*", + ClosedBinOp::StarStar => "**", + }) + } +} + +#[derive(Clone, Copy, Debug, Eq, PartialEq, Sequence)] +pub enum ComparisonOp { + /// `!=` + BangEq, + /// `==` + EqEq, + /// `>` + Gt, + /// `>=` + GtEq, + /// `<` + Lt, + /// `<=` + LtEq, +} + +impl Display for ComparisonOp { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + f.write_str(match self { + ComparisonOp::BangEq => "!=", + ComparisonOp::EqEq => "==", + ComparisonOp::Gt => ">", + ComparisonOp::GtEq => ">=", + ComparisonOp::Lt => "<", + ComparisonOp::LtEq => "<=", + }) + } +} + +pub(crate) struct Lexer<'a> { + input: &'a str, + len: u32, + + // This uses a `Peekable` iterator over the raw lexer, which allows for one token lookahead. + tokens: Peekable>, +} + +impl<'a> Lexer<'a> { + pub(crate) fn new(input: &'a str) -> Self { + Self { + input, + len: input + .len() + .try_into() + .expect("input length should fit into u32"), + tokens: raw::Lexer::new(input).peekable(), + } + } + + fn offset(&mut self) -> u32 { + self.tokens.peek().map_or_else(|| self.len, |t| t.offset) + } + + fn next_if_eq_single(&mut self, single: Single) -> bool { + self.next_if_eq(raw::TokenKind::Single(single)) + } + + fn next_if_eq(&mut self, tok: raw::TokenKind) -> bool { + self.tokens.next_if(|t| t.kind == tok).is_some() + } + + fn expect_single(&mut self, single: Single, complete: TokenKind) -> Result<(), Error> { + self.expect(raw::TokenKind::Single(single), complete) + } + + fn expect(&mut self, tok: raw::TokenKind, complete: TokenKind) -> Result<(), Error> { + if self.next_if_eq(tok) { + Ok(()) + } else if let Some(&raw::Token { kind, offset }) = self.tokens.peek() { + let mut tokens = self.tokens.clone(); + let hi = tokens.nth(1).map_or_else(|| self.len, |t| t.offset); + let span = Span { lo: offset, hi }; + Err(Error::Incomplete(tok, complete, kind, span)) + } else { + let lo = self.len; + let span = Span { lo, hi: lo }; + Err(Error::IncompleteEof(tok, complete, span)) + } + } + + /// Returns the first token ahead of the cursor without consuming it. This operation is fast, + /// but if you know you want to consume the token if it matches, use [`next_if_eq`] instead. + fn first(&mut self) -> Option { + self.tokens.peek().map(|i| i.kind) + } + + /// Returns the second token ahead of the cursor without consuming it. This is slower + /// than [`first`] and should be avoided when possible. + fn second(&self) -> Option { + let mut tokens = self.tokens.clone(); + tokens.next(); + tokens.next().map(|i| i.kind) + } + + /// Consumes a list of tokens zero or more times. + fn kleen_star(&mut self, tokens: &[raw::TokenKind], complete: TokenKind) -> Result<(), Error> { + let mut iter = tokens.iter(); + while self.next_if_eq(*(iter.next().expect("tokens should have at least one token"))) { + for token in iter { + self.expect(*token, complete)?; + } + iter = tokens.iter(); + } + Ok(()) + } + + fn cook(&mut self, token: &raw::Token) -> Result, Error> { + let kind = match token.kind { + raw::TokenKind::Bitstring { terminated: true } => { + Ok(Some(TokenKind::Literal(Literal::Bitstring))) + } + raw::TokenKind::Bitstring { terminated: false } => { + Err(Error::UnterminatedString(Span { + lo: token.offset, + hi: token.offset, + })) + } + raw::TokenKind::Comment(_) | raw::TokenKind::Newline | raw::TokenKind::Whitespace => { + Ok(None) + } + raw::TokenKind::Ident => { + let ident = &self.input[(token.offset as usize)..(self.offset() as usize)]; + Ok(Some(Self::ident(ident))) + } + raw::TokenKind::HardwareQubit => Ok(Some(TokenKind::HardwareQubit)), + raw::TokenKind::LiteralFragment(_) => { + // if a literal fragment does not appear after a decimal + // or a float, treat it as an identifier. + Ok(Some(TokenKind::Identifier)) + } + raw::TokenKind::Number(number) => { + // after reading a decimal number or a float there could be a whitespace + // followed by a fragment, which will change the type of the literal. + match (self.first(), self.second()) { + (Some(raw::TokenKind::LiteralFragment(fragment)), _) + | ( + Some(raw::TokenKind::Whitespace), + Some(raw::TokenKind::LiteralFragment(fragment)), + ) => { + use self::Literal::{Imaginary, Timing}; + use TokenKind::Literal; + + // if first() was a whitespace, we need to consume an extra token + if self.first() == Some(raw::TokenKind::Whitespace) { + self.next(); + } + self.next(); + + Ok(Some(match fragment { + raw::LiteralFragmentKind::Imag => Literal(Imaginary), + raw::LiteralFragmentKind::Dt => Literal(Timing(TimingLiteralKind::Dt)), + raw::LiteralFragmentKind::Ns => Literal(Timing(TimingLiteralKind::Ns)), + raw::LiteralFragmentKind::Us => Literal(Timing(TimingLiteralKind::Us)), + raw::LiteralFragmentKind::Ms => Literal(Timing(TimingLiteralKind::Ms)), + raw::LiteralFragmentKind::S => Literal(Timing(TimingLiteralKind::S)), + })) + } + _ => Ok(Some(number.into())), + } + } + raw::TokenKind::Single(single) => self.single(single).map(Some), + raw::TokenKind::String { terminated: true } => { + Ok(Some(TokenKind::Literal(Literal::String))) + } + raw::TokenKind::String { terminated: false } => Err(Error::UnterminatedString(Span { + lo: token.offset, + hi: token.offset, + })), + raw::TokenKind::Unknown => { + let c = self.input[(token.offset as usize)..] + .chars() + .next() + .expect("token offset should be the start of a character"); + let span = Span { + lo: token.offset, + hi: self.offset(), + }; + Err(Error::Unknown(c, span)) + } + }?; + + Ok(kind.map(|kind| { + let span = Span { + lo: token.offset, + hi: self.offset(), + }; + Token { kind, span } + })) + } + + #[allow(clippy::too_many_lines)] + fn single(&mut self, single: Single) -> Result { + match single { + Single::Amp => { + if self.next_if_eq_single(Single::Amp) { + Ok(TokenKind::ClosedBinOp(ClosedBinOp::AmpAmp)) + } else { + Ok(self.closed_bin_op(ClosedBinOp::Amp)) + } + } + Single::At => { + let complete = TokenKind::Annotation; + self.expect(raw::TokenKind::Ident, complete)?; + self.kleen_star( + &[raw::TokenKind::Single(Single::Dot), raw::TokenKind::Ident], + complete, + )?; + Ok(complete) + } + Single::Bang => { + if self.next_if_eq_single(Single::Eq) { + Ok(TokenKind::ComparisonOp(ComparisonOp::BangEq)) + } else { + Ok(TokenKind::Bang) + } + } + Single::Bar => { + if self.next_if_eq_single(Single::Bar) { + Ok(TokenKind::ClosedBinOp(ClosedBinOp::BarBar)) + } else { + Ok(self.closed_bin_op(ClosedBinOp::Bar)) + } + } + Single::Caret => Ok(self.closed_bin_op(ClosedBinOp::Caret)), + Single::Close(delim) => Ok(TokenKind::Close(delim)), + Single::Colon => Ok(TokenKind::Colon), + Single::Comma => Ok(TokenKind::Comma), + Single::Dot => Ok(TokenKind::Dot), + Single::Eq => { + if self.next_if_eq_single(Single::Eq) { + Ok(TokenKind::ComparisonOp(ComparisonOp::EqEq)) + } else { + Ok(TokenKind::Eq) + } + } + Single::Gt => { + if self.next_if_eq_single(Single::Eq) { + Ok(TokenKind::ComparisonOp(ComparisonOp::GtEq)) + } else if self.next_if_eq_single(Single::Gt) { + Ok(self.closed_bin_op(ClosedBinOp::GtGt)) + } else { + Ok(TokenKind::ComparisonOp(ComparisonOp::Gt)) + } + } + Single::Lt => { + if self.next_if_eq_single(Single::Eq) { + Ok(TokenKind::ComparisonOp(ComparisonOp::LtEq)) + } else if self.next_if_eq_single(Single::Lt) { + Ok(self.closed_bin_op(ClosedBinOp::LtLt)) + } else { + Ok(TokenKind::ComparisonOp(ComparisonOp::Lt)) + } + } + Single::Minus => { + if self.next_if_eq_single(Single::Gt) { + Ok(TokenKind::Arrow) + } else { + Ok(self.closed_bin_op(ClosedBinOp::Minus)) + } + } + Single::Open(delim) => Ok(TokenKind::Open(delim)), + Single::Percent => Ok(self.closed_bin_op(ClosedBinOp::Percent)), + Single::Plus => { + if self.next_if_eq_single(Single::Plus) { + Ok(TokenKind::PlusPlus) + } else { + Ok(self.closed_bin_op(ClosedBinOp::Plus)) + } + } + Single::Semi => Ok(TokenKind::Semicolon), + Single::Slash => Ok(self.closed_bin_op(ClosedBinOp::Slash)), + Single::Star => { + if self.next_if_eq_single(Single::Star) { + Ok(self.closed_bin_op(ClosedBinOp::StarStar)) + } else { + Ok(self.closed_bin_op(ClosedBinOp::Star)) + } + } + Single::Tilde => Ok(TokenKind::Tilde), + } + } + + fn closed_bin_op(&mut self, op: ClosedBinOp) -> TokenKind { + if self.next_if_eq_single(Single::Eq) { + TokenKind::BinOpEq(op) + } else { + TokenKind::ClosedBinOp(op) + } + } + + fn ident(ident: &str) -> TokenKind { + match ident { + "gphase" => TokenKind::GPhase, + "inv" => TokenKind::Inv, + "pow" => TokenKind::Pow, + "ctrl" => TokenKind::Ctrl, + "negctrl" => TokenKind::NegCtrl, + "dim" => TokenKind::Dim, + "durationof" => TokenKind::DurationOf, + "delay" => TokenKind::Delay, + "reset" => TokenKind::Reset, + "measure" => TokenKind::Measure, + "barrier" => TokenKind::Barrier, + "false" | "true" => TokenKind::Literal(Literal::Boolean), + ident => { + if let Ok(keyword) = ident.parse::() { + TokenKind::Keyword(keyword) + } else if let Ok(type_) = ident.parse::() { + TokenKind::Type(type_) + } else { + TokenKind::Identifier + } + } + } + } +} + +impl Iterator for Lexer<'_> { + type Item = Result; + + fn next(&mut self) -> Option { + while let Some(token) = self.tokens.next() { + match self.cook(&token) { + Ok(None) => {} + Ok(Some(token)) => return Some(Ok(token)), + Err(err) => return Some(Err(err)), + } + } + + None + } +} diff --git a/compiler/qsc_qasm3/src/lex/cooked/tests.rs b/compiler/qsc_qasm3/src/lex/cooked/tests.rs new file mode 100644 index 0000000000..7002c957b7 --- /dev/null +++ b/compiler/qsc_qasm3/src/lex/cooked/tests.rs @@ -0,0 +1,941 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +use super::{Lexer, Token, TokenKind}; +use crate::lex::Delim; +use expect_test::{expect, Expect}; +use qsc_data_structures::span::Span; + +fn check(input: &str, expect: &Expect) { + let actual: Vec<_> = Lexer::new(input).collect(); + expect.assert_debug_eq(&actual); +} + +fn op_string(kind: TokenKind) -> Option { + match kind { + TokenKind::Close(Delim::Brace) => Some("}".to_string()), + TokenKind::Close(Delim::Bracket) => Some("]".to_string()), + TokenKind::Close(Delim::Paren) => Some(")".to_string()), + TokenKind::Colon => Some(":".to_string()), + TokenKind::Comma => Some(",".to_string()), + TokenKind::Dot => Some(".".to_string()), + TokenKind::Eq => Some("=".to_string()), + TokenKind::Bang => Some("!".to_string()), + TokenKind::Tilde => Some("~".to_string()), + TokenKind::Open(Delim::Brace) => Some("{".to_string()), + TokenKind::Open(Delim::Bracket) => Some("[".to_string()), + TokenKind::Open(Delim::Paren) => Some("(".to_string()), + TokenKind::PlusPlus => Some("++".to_string()), + TokenKind::Keyword(keyword) => Some(keyword.to_string()), + TokenKind::Type(type_) => Some(type_.to_string()), + TokenKind::GPhase => Some("gphase".to_string()), + TokenKind::Inv => Some("inv".to_string()), + TokenKind::Pow => Some("pow".to_string()), + TokenKind::Ctrl => Some("ctrl".to_string()), + TokenKind::NegCtrl => Some("negctrl".to_string()), + TokenKind::Dim => Some("dim".to_string()), + TokenKind::DurationOf => Some("durationof".to_string()), + TokenKind::Delay => Some("delay".to_string()), + TokenKind::Reset => Some("reset".to_string()), + TokenKind::Measure => Some("measure".to_string()), + TokenKind::Barrier => Some("barrier".to_string()), + TokenKind::Semicolon => Some(";".to_string()), + TokenKind::Arrow => Some("->".to_string()), + TokenKind::ClosedBinOp(op) => Some(op.to_string()), + TokenKind::BinOpEq(super::ClosedBinOp::AmpAmp | super::ClosedBinOp::BarBar) + | TokenKind::Literal(_) + | TokenKind::Annotation => None, + TokenKind::BinOpEq(op) => Some(format!("{op}=")), + TokenKind::ComparisonOp(op) => Some(op.to_string()), + TokenKind::Identifier => Some("foo".to_string()), + TokenKind::HardwareQubit => Some("$1".to_string()), + } +} + +#[test] +fn basic_ops() { + for kind in enum_iterator::all() { + let Some(input) = op_string(kind) else { + continue; + }; + let actual: Vec<_> = Lexer::new(&input).collect(); + let len = input + .len() + .try_into() + .expect("input length should fit into u32"); + assert_eq!( + actual, + vec![Ok(Token { + kind, + span: Span { lo: 0, hi: len } + }),] + ); + } +} + +#[test] +fn empty() { + check( + "", + &expect![[r#" + [] + "#]], + ); +} + +#[test] +fn amp() { + check( + "&", + &expect![[r#" + [ + Ok( + Token { + kind: ClosedBinOp( + Amp, + ), + span: Span { + lo: 0, + hi: 1, + }, + }, + ), + ] + "#]], + ); +} + +#[test] +fn amp_amp() { + check( + "&&", + &expect![[r#" + [ + Ok( + Token { + kind: ClosedBinOp( + AmpAmp, + ), + span: Span { + lo: 0, + hi: 2, + }, + }, + ), + ] + "#]], + ); +} + +#[test] +fn amp_plus() { + check( + "&+", + &expect![[r#" + [ + Ok( + Token { + kind: ClosedBinOp( + Amp, + ), + span: Span { + lo: 0, + hi: 1, + }, + }, + ), + Ok( + Token { + kind: ClosedBinOp( + Plus, + ), + span: Span { + lo: 1, + hi: 2, + }, + }, + ), + ] + "#]], + ); +} + +#[test] +fn amp_multibyte() { + check( + "&🦀", + &expect![[r#" + [ + Ok( + Token { + kind: ClosedBinOp( + Amp, + ), + span: Span { + lo: 0, + hi: 1, + }, + }, + ), + Err( + Unknown( + '🦀', + Span { + lo: 1, + hi: 5, + }, + ), + ), + ] + "#]], + ); +} + +#[test] +fn amp_amp_amp_amp() { + check( + "&&&&", + &expect![[r#" + [ + Ok( + Token { + kind: ClosedBinOp( + AmpAmp, + ), + span: Span { + lo: 0, + hi: 2, + }, + }, + ), + Ok( + Token { + kind: ClosedBinOp( + AmpAmp, + ), + span: Span { + lo: 2, + hi: 4, + }, + }, + ), + ] + "#]], + ); +} + +#[test] +fn int() { + check( + "123", + &expect![[r#" + [ + Ok( + Token { + kind: Literal( + Integer( + Decimal, + ), + ), + span: Span { + lo: 0, + hi: 3, + }, + }, + ), + ] + "#]], + ); +} + +#[test] +fn negative_int() { + check( + "-123", + &expect![[r#" + [ + Ok( + Token { + kind: ClosedBinOp( + Minus, + ), + span: Span { + lo: 0, + hi: 1, + }, + }, + ), + Ok( + Token { + kind: Literal( + Integer( + Decimal, + ), + ), + span: Span { + lo: 1, + hi: 4, + }, + }, + ), + ] + "#]], + ); +} + +#[test] +fn positive_int() { + check( + "+123", + &expect![[r#" + [ + Ok( + Token { + kind: ClosedBinOp( + Plus, + ), + span: Span { + lo: 0, + hi: 1, + }, + }, + ), + Ok( + Token { + kind: Literal( + Integer( + Decimal, + ), + ), + span: Span { + lo: 1, + hi: 4, + }, + }, + ), + ] + "#]], + ); +} + +#[test] +fn imag() { + check( + "123im", + &expect![[r#" + [ + Ok( + Token { + kind: Literal( + Imaginary, + ), + span: Span { + lo: 0, + hi: 5, + }, + }, + ), + ] + "#]], + ); +} + +#[test] +fn imag_with_whitespace() { + check( + "123 im", + &expect![[r#" + [ + Ok( + Token { + kind: Literal( + Imaginary, + ), + span: Span { + lo: 0, + hi: 6, + }, + }, + ), + ] + "#]], + ); +} + +#[test] +fn negative_imag() { + check( + "-123im", + &expect![[r#" + [ + Ok( + Token { + kind: ClosedBinOp( + Minus, + ), + span: Span { + lo: 0, + hi: 1, + }, + }, + ), + Ok( + Token { + kind: Literal( + Imaginary, + ), + span: Span { + lo: 1, + hi: 6, + }, + }, + ), + ] + "#]], + ); +} + +#[test] +fn positive_imag() { + check( + "+123im", + &expect![[r#" + [ + Ok( + Token { + kind: ClosedBinOp( + Plus, + ), + span: Span { + lo: 0, + hi: 1, + }, + }, + ), + Ok( + Token { + kind: Literal( + Imaginary, + ), + span: Span { + lo: 1, + hi: 6, + }, + }, + ), + ] + "#]], + ); +} + +#[test] +fn float() { + check( + "1.23", + &expect![[r#" + [ + Ok( + Token { + kind: Literal( + Float, + ), + span: Span { + lo: 0, + hi: 4, + }, + }, + ), + ] + "#]], + ); +} + +#[test] +fn negative_float() { + check( + "-1.23", + &expect![[r#" + [ + Ok( + Token { + kind: ClosedBinOp( + Minus, + ), + span: Span { + lo: 0, + hi: 1, + }, + }, + ), + Ok( + Token { + kind: Literal( + Float, + ), + span: Span { + lo: 1, + hi: 5, + }, + }, + ), + ] + "#]], + ); +} + +#[test] +fn positive_float() { + check( + "+1.23", + &expect![[r#" + [ + Ok( + Token { + kind: ClosedBinOp( + Plus, + ), + span: Span { + lo: 0, + hi: 1, + }, + }, + ), + Ok( + Token { + kind: Literal( + Float, + ), + span: Span { + lo: 1, + hi: 5, + }, + }, + ), + ] + "#]], + ); +} + +#[test] +fn leading_point() { + check( + ".1", + &expect![[r#" + [ + Ok( + Token { + kind: Literal( + Float, + ), + span: Span { + lo: 0, + hi: 2, + }, + }, + ), + ] + "#]], + ); +} + +#[test] +fn trailing_point() { + check( + "1.", + &expect![[r#" + [ + Ok( + Token { + kind: Literal( + Float, + ), + span: Span { + lo: 0, + hi: 2, + }, + }, + ), + ] + "#]], + ); +} + +#[test] +fn leading_zero_float() { + check( + "0.42", + &expect![[r#" + [ + Ok( + Token { + kind: Literal( + Float, + ), + span: Span { + lo: 0, + hi: 4, + }, + }, + ), + ] + "#]], + ); +} + +#[test] +fn dot_float() { + check( + "..1", + &expect![[r#" + [ + Ok( + Token { + kind: Dot, + span: Span { + lo: 0, + hi: 1, + }, + }, + ), + Ok( + Token { + kind: Literal( + Float, + ), + span: Span { + lo: 1, + hi: 3, + }, + }, + ), + ] + "#]], + ); +} + +#[test] +fn float_dot() { + check( + "1..", + &expect![[r#" + [ + Ok( + Token { + kind: Literal( + Float, + ), + span: Span { + lo: 0, + hi: 2, + }, + }, + ), + Ok( + Token { + kind: Dot, + span: Span { + lo: 2, + hi: 3, + }, + }, + ), + ] + "#]], + ); +} + +#[test] +fn dot_dot_int_dot_dot() { + check( + "..1..", + &expect![[r#" + [ + Ok( + Token { + kind: Dot, + span: Span { + lo: 0, + hi: 1, + }, + }, + ), + Ok( + Token { + kind: Literal( + Float, + ), + span: Span { + lo: 1, + hi: 3, + }, + }, + ), + Ok( + Token { + kind: Dot, + span: Span { + lo: 3, + hi: 4, + }, + }, + ), + Ok( + Token { + kind: Dot, + span: Span { + lo: 4, + hi: 5, + }, + }, + ), + ] + "#]], + ); +} + +#[test] +fn two_points_with_leading() { + check( + ".1.2", + &expect![[r#" + [ + Ok( + Token { + kind: Literal( + Float, + ), + span: Span { + lo: 0, + hi: 2, + }, + }, + ), + Ok( + Token { + kind: Literal( + Float, + ), + span: Span { + lo: 2, + hi: 4, + }, + }, + ), + ] + "#]], + ); +} + +#[test] +fn leading_point_exp() { + check( + ".1e2", + &expect![[r#" + [ + Ok( + Token { + kind: Literal( + Float, + ), + span: Span { + lo: 0, + hi: 4, + }, + }, + ), + ] + "#]], + ); +} + +#[test] +fn ident() { + check( + "foo", + &expect![[r#" + [ + Ok( + Token { + kind: Identifier, + span: Span { + lo: 0, + hi: 3, + }, + }, + ), + ] + "#]], + ); +} + +#[test] +fn string() { + check( + r#""string""#, + &expect![[r#" + [ + Ok( + Token { + kind: Literal( + String, + ), + span: Span { + lo: 0, + hi: 8, + }, + }, + ), + ] + "#]], + ); +} + +#[test] +fn string_empty() { + check( + r#""""#, + &expect![[r#" + [ + Ok( + Token { + kind: Literal( + String, + ), + span: Span { + lo: 0, + hi: 2, + }, + }, + ), + ] + "#]], + ); +} + +#[test] +fn string_missing_ending() { + check( + r#""Uh oh..."#, + &expect![[r#" + [ + Err( + UnterminatedString( + Span { + lo: 0, + hi: 0, + }, + ), + ), + ] + "#]], + ); +} + +#[test] +fn hardware_qubit() { + check( + r"$12", + &expect![[r#" + [ + Ok( + Token { + kind: HardwareQubit, + span: Span { + lo: 0, + hi: 3, + }, + }, + ), + ] + "#]], + ); +} + +#[test] +fn unknown() { + check( + "##", + &expect![[r#" + [ + Err( + Unknown( + '#', + Span { + lo: 0, + hi: 1, + }, + ), + ), + Err( + Unknown( + '#', + Span { + lo: 1, + hi: 2, + }, + ), + ), + ] + "#]], + ); +} + +#[test] +fn comment() { + check( + "//comment\nx", + &expect![[r#" + [ + Ok( + Token { + kind: Identifier, + span: Span { + lo: 10, + hi: 11, + }, + }, + ), + ] + "#]], + ); +} + +#[test] +fn block_comment() { + check( + "/*comment*/x", + &expect![[r#" + [ + Ok( + Token { + kind: Identifier, + span: Span { + lo: 11, + hi: 12, + }, + }, + ), + ] + "#]], + ); +} + +#[test] +fn comment_four_slashes() { + check( + "////comment\nx", + &expect![[r#" + [ + Ok( + Token { + kind: Identifier, + span: Span { + lo: 12, + hi: 13, + }, + }, + ), + ] + "#]], + ); +} diff --git a/compiler/qsc_qasm3/src/lex/raw.rs b/compiler/qsc_qasm3/src/lex/raw.rs new file mode 100644 index 0000000000..65bf3d7fea --- /dev/null +++ b/compiler/qsc_qasm3/src/lex/raw.rs @@ -0,0 +1,601 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +//! The first lexing phase transforms an input string into literals, single-character operators, +//! whitespace, and comments. Keywords are treated as identifiers. The raw token stream is +//! contiguous: there are no gaps between tokens. +//! +//! These are "raw" tokens because single-character operators don't always correspond to `OpenQASM` +//! operators, and whitespace and comments will later be discarded. Raw tokens are the ingredients +//! that are "cooked" into compound tokens before they can be consumed by the parser. +//! +//! Tokens never contain substrings from the original input, but are simply labels that refer back +//! to offsets in the input. Lexing never fails, but may produce unknown tokens. + +#[cfg(test)] +mod tests; + +use super::{Delim, Radix}; +use enum_iterator::Sequence; +use std::{ + fmt::{self, Display, Formatter, Write}, + iter::Peekable, + str::CharIndices, +}; + +/// An enum used internally by the raw lexer to signal whether +/// a token was partially parsed or if it wasn't parsed at all. +enum LexError { + /// An incomplete token was parsed, e.g., a string missing + /// the closing quote or a number ending in an underscore. + Incomplete(T), + /// The token wasn't parsed and no characters were consumed + /// when trying to parse the token. + None, +} + +/// A raw token. +#[derive(Clone, Debug, Eq, PartialEq)] +pub struct Token { + /// The token kind. + pub kind: TokenKind, + /// The byte offset of the token starting character. + pub offset: u32, +} + +#[derive(Clone, Copy, Debug, Eq, PartialEq, Sequence)] +pub enum TokenKind { + Bitstring { terminated: bool }, + Comment(CommentKind), + HardwareQubit, + Ident, + LiteralFragment(LiteralFragmentKind), + Newline, + Number(Number), + Single(Single), + String { terminated: bool }, + Unknown, + Whitespace, +} + +impl Display for TokenKind { + fn fmt(&self, f: &mut Formatter) -> fmt::Result { + match self { + TokenKind::Bitstring { .. } => f.write_str("bitstring"), + TokenKind::Comment(CommentKind::Block) => f.write_str("block comment"), + TokenKind::Comment(CommentKind::Normal) => f.write_str("comment"), + TokenKind::HardwareQubit => f.write_str("hardware qubit"), + TokenKind::Ident => f.write_str("identifier"), + TokenKind::LiteralFragment(_) => f.write_str("literal fragment"), + TokenKind::Newline => f.write_str("newline"), + TokenKind::Number(Number::Float) => f.write_str("float"), + TokenKind::Number(Number::Int(_)) => f.write_str("integer"), + TokenKind::Single(single) => write!(f, "`{single}`"), + TokenKind::String { .. } => f.write_str("string"), + TokenKind::Unknown => f.write_str("unknown"), + TokenKind::Whitespace => f.write_str("whitespace"), + } + } +} + +/// A single-character operator token. +#[derive(Clone, Copy, Debug, Eq, PartialEq, Sequence)] +pub enum Single { + /// `&` + Amp, + /// `@` + At, + /// `!` + Bang, + /// `|` + Bar, + /// `^` + Caret, + /// A closing delimiter. + Close(Delim), + /// `:` + Colon, + /// `,` + Comma, + /// `.` + Dot, + /// `=` + Eq, + /// `>` + Gt, + /// `<` + Lt, + /// `-` + Minus, + /// An opening delimiter. + Open(Delim), + /// `%` + Percent, + /// `+` + Plus, + /// `;` + Semi, + /// `/` + Slash, + /// `*` + Star, + /// `~` + Tilde, +} + +impl Display for Single { + fn fmt(&self, f: &mut Formatter) -> fmt::Result { + f.write_char(match self { + Single::Amp => '&', + Single::At => '@', + Single::Bang => '!', + Single::Bar => '|', + Single::Caret => '^', + Single::Close(Delim::Brace) => '}', + Single::Close(Delim::Bracket) => ']', + Single::Close(Delim::Paren) => ')', + Single::Colon => ':', + Single::Comma => ',', + Single::Dot => '.', + Single::Eq => '=', + Single::Gt => '>', + Single::Lt => '<', + Single::Minus => '-', + Single::Open(Delim::Brace) => '{', + Single::Open(Delim::Bracket) => '[', + Single::Open(Delim::Paren) => '(', + Single::Percent => '%', + Single::Plus => '+', + Single::Semi => ';', + Single::Slash => '/', + Single::Star => '*', + Single::Tilde => '~', + }) + } +} + +#[derive(Clone, Copy, Debug, Eq, PartialEq, Sequence)] +pub enum Number { + Float, + Int(Radix), +} + +#[derive(Clone, Copy, Debug, Eq, PartialEq, Sequence)] +pub struct StringToken { + pub terminated: bool, +} + +#[derive(Clone, Copy, Debug, Eq, PartialEq, Sequence)] +pub enum CommentKind { + Block, + Normal, +} + +#[derive(Clone, Copy, Debug, Eq, PartialEq, Sequence)] +pub enum LiteralFragmentKind { + /// Imaginary literal fragment. + Imag, + /// Timing literal: Backend-dependent unit. + /// Equivalent to the duration of one waveform sample on the backend. + Dt, + /// Timing literal: Nanoseconds. + Ns, + /// Timing literal: Microseconds. + Us, + /// Timing literal: Milliseconds. + Ms, + /// Timing literal: Seconds. + S, +} + +#[derive(Clone)] +pub struct Lexer<'a> { + chars: Peekable>, + starting_offset: u32, +} + +impl<'a> Lexer<'a> { + #[must_use] + pub fn new(input: &'a str) -> Self { + Self { + chars: input.char_indices().peekable(), + starting_offset: 0, + } + } + + #[must_use] + pub fn new_with_starting_offset(input: &'a str, starting_offset: u32) -> Self { + Self { + chars: input.char_indices().peekable(), + starting_offset, + } + } + + fn next_if(&mut self, f: impl FnOnce(char) -> bool) -> bool { + self.chars.next_if(|i| f(i.1)).is_some() + } + + fn next_if_eq(&mut self, c: char) -> bool { + self.chars.next_if(|i| i.1 == c).is_some() + } + + /// Consumes the characters while they satisfy `f`. Returns the last character eaten, if any. + fn eat_while(&mut self, mut f: impl FnMut(char) -> bool) -> Option { + let mut last_eaten = None; + loop { + let c = self.chars.next_if(|i| f(i.1)); + if c.is_none() { + return last_eaten.map(|(_, c)| c); + } + last_eaten = c; + } + } + + /// Returns the first character ahead of the cursor without consuming it. This operation is fast, + /// but if you know you want to consume the character if it matches, use [`next_if_eq`] instead. + fn first(&mut self) -> Option { + self.chars.peek().map(|i| i.1) + } + + /// Returns the second character ahead of the cursor without consuming it. This is slower + /// than [`first`] and should be avoided when possible. + fn second(&self) -> Option { + let mut chars = self.chars.clone(); + chars.next(); + chars.next().map(|i| i.1) + } + + fn newline(&mut self, c: char) -> bool { + if is_newline(c) { + self.eat_while(is_newline); + true + } else { + false + } + } + + fn whitespace(&mut self, c: char) -> bool { + if is_whitespace(c) { + self.eat_while(is_whitespace); + true + } else { + false + } + } + + fn comment(&mut self, c: char) -> Option { + if c == '/' && self.next_if_eq('/') { + self.eat_while(|c| !is_newline(c)); + Some(CommentKind::Normal) + } else if c == '/' && self.next_if_eq('*') { + loop { + let (_, c) = self.chars.next()?; + if c == '*' && self.next_if_eq('/') { + return Some(CommentKind::Block); + } + } + } else { + None + } + } + + fn ident(&mut self, c: char) -> Option { + // Check for some special literal fragments. + // We need to check that the character following the fragment isn't an + // underscore or an alphanumeric character, else it is an identifier. + let first = self.first(); + if c == 's' + && (first.is_none() || first.is_some_and(|c1| c1 != '_' && !c1.is_alphanumeric())) + { + return Some(TokenKind::LiteralFragment(LiteralFragmentKind::S)); + } + + let second = self.second(); + if let Some(c1) = first { + if second.is_none() || second.is_some_and(|c1| c1 != '_' && !c1.is_alphanumeric()) { + let fragment = match (c, c1) { + ('i', 'm') => Some(TokenKind::LiteralFragment(LiteralFragmentKind::Imag)), + ('d', 't') => Some(TokenKind::LiteralFragment(LiteralFragmentKind::Dt)), + ('n', 's') => Some(TokenKind::LiteralFragment(LiteralFragmentKind::Ns)), + ('u' | 'µ', 's') => Some(TokenKind::LiteralFragment(LiteralFragmentKind::Us)), + ('m', 's') => Some(TokenKind::LiteralFragment(LiteralFragmentKind::Ms)), + _ => None, + }; + + if fragment.is_some() { + // Consume `first` before returning. + self.chars.next(); + return fragment; + } + } + } + + if c == '_' || c.is_alphabetic() { + self.eat_while(|c| c == '_' || c.is_alphanumeric()); + Some(TokenKind::Ident) + } else { + None + } + } + + fn number(&mut self, c: char) -> Result> { + self.leading_zero(c) + .or_else(|_| self.leading_dot(c)) + .or_else(|_| self.decimal_or_float(c)) + } + + /// This rule allows us to differentiate a leading dot from a mid dot. + /// A float starting with a leading dot must contain at least one digit + /// after the dot. + fn leading_dot(&mut self, c: char) -> Result> { + let first = self.first(); + if c == '.' && first.is_some_and(|c| c == '_' || c.is_ascii_digit()) { + self.chars.next(); + let c1 = first.expect("first.is_some_and() succeeded"); + self.decimal(c1)?; + match self.exp() { + Ok(()) | Err(LexError::None) => Ok(Number::Float), + Err(_) => Err(LexError::Incomplete(Number::Float)), + } + } else { + Err(LexError::None) + } + } + + /// A float with a middle dot could optionally contain numbers after the dot. + /// This rule is necessary to differentiate from the floats with a leading dot, + /// which must have digits after the dot. + fn mid_dot(&mut self, c: char) -> Result> { + if c == '.' { + match self.first() { + Some(c1) if c1 == '_' || c1.is_ascii_digit() => { + self.chars.next(); + match self.decimal(c1) { + Err(LexError::Incomplete(_)) => Err(LexError::Incomplete(Number::Float)), + Ok(_) | Err(LexError::None) => match self.exp() { + Ok(()) | Err(LexError::None) => Ok(Number::Float), + Err(_) => Err(LexError::Incomplete(Number::Float)), + }, + } + } + None | Some(_) => Ok(Number::Float), + } + } else { + Err(LexError::None) + } + } + + /// This rule parses binary, octal, hexadecimal numbers, or decimal/floats + /// if the next character isn't a radix specifier. + /// Numbers in Qasm aren't allowed to end in an underscore. + fn leading_zero(&mut self, c: char) -> Result> { + if c != '0' { + return Err(LexError::None); + } + + let radix = if self.next_if_eq('b') || self.next_if_eq('B') { + Radix::Binary + } else if self.next_if_eq('o') || self.next_if_eq('O') { + Radix::Octal + } else if self.next_if_eq('x') || self.next_if_eq('X') { + Radix::Hexadecimal + } else { + Radix::Decimal + }; + + let last_eaten = self.eat_while(|c| c == '_' || c.is_digit(radix.into())); + + match radix { + Radix::Binary | Radix::Octal | Radix::Hexadecimal => match last_eaten { + None | Some('_') => Err(LexError::Incomplete(Number::Int(radix))), + _ => Ok(Number::Int(radix)), + }, + Radix::Decimal => match self.first() { + Some(c1 @ '.') => { + self.chars.next(); + self.mid_dot(c1) + } + None | Some(_) => Ok(Number::Int(Radix::Decimal)), + }, + } + } + + /// This rule parses a decimal integer. + /// Numbers in QASM aren't allowed to end in an underscore. + /// The rule in the .g4 file is + /// `DecimalIntegerLiteral: ([0-9] '_'?)* [0-9];` + fn decimal(&mut self, c: char) -> Result> { + if !c.is_ascii_digit() { + return Err(LexError::None); + } + + let last_eaten = self.eat_while(|c| c == '_' || c.is_ascii_digit()); + + match last_eaten { + None if c == '_' => Err(LexError::None), + Some('_') => Err(LexError::Incomplete(Number::Int(Radix::Decimal))), + _ => Ok(Number::Int(Radix::Decimal)), + } + } + + /// This rule disambiguates between a decimal integer and a float with a + /// mid dot, like `12.3`. + fn decimal_or_float(&mut self, c: char) -> Result> { + self.decimal(c)?; + match self.first() { + None => Ok(Number::Int(Radix::Decimal)), + Some(first @ '.') => { + self.chars.next(); + self.mid_dot(first) + } + _ => match self.exp() { + Ok(()) => Ok(Number::Float), + Err(LexError::None) => Ok(Number::Int(Radix::Decimal)), + Err(_) => Err(LexError::Incomplete(Number::Float)), + }, + } + } + + /// Parses an exponent. Errors if the exponent was missing or incomplete. + /// The rule `decimal_or_float` uses the `LexError::None` variant of the error + /// to classify the token as an integer. + /// The `leading_dot` and `mid_dot` rules use the `LexError::None` variant to + /// classify the token as a float. + fn exp(&mut self) -> Result<(), LexError> { + if self.next_if(|c| c == 'e' || c == 'E') { + // Optionally there could be a + or - sign. + self.chars.next_if(|i| i.1 == '+' || i.1 == '-'); + + // If the next character isn't a digit or an + // underscore we issue an error without consuming it. + let first = self.first().ok_or(LexError::Incomplete(Number::Float))?; + if first != '_' && !first.is_ascii_digit() { + Err(LexError::Incomplete(Number::Float)) + } else { + self.chars.next(); + match self.decimal(first) { + Ok(_) => Ok(()), + Err(_) => Err(LexError::Incomplete(Number::Float)), + } + } + } else { + Err(LexError::None) + } + } + + /// Tries to parse a string or a bitstring. QASM strings can be enclosed + /// by double quotes or single quotes. Bitstrings can only be enclosed by + /// double quotes and contain 0s and 1s. + fn string(&mut self, string_start: char) -> Option { + if string_start != '"' && string_start != '\'' { + return None; + } + + if let Some(bitstring) = self.bitstring() { + // Try consuming the closing '"'. + self.chars.next(); + return Some(bitstring); + } + + while self.first().is_some_and(|c| c != string_start) { + self.eat_while(|c| c != '\\' && c != string_start); + if self.next_if_eq('\\') { + self.chars.next(); + } + } + + Some(TokenKind::String { + terminated: self.next_if_eq(string_start), + }) + } + + /// Parses the body of a bitstring. Bitstrings can only contain 0s and 1s. + /// Returns `None` if it finds an invalid character. + fn bitstring(&mut self) -> Option { + const STRING_START: char = '"'; + + // A bitstring must have at least one character. + if matches!(self.first(), None | Some(STRING_START)) { + return None; + } + + // A bitstring must end in a 0 or a 1. + if let Some('_') = self.eat_while(is_bitstring_char) { + return None; + } + + // Check the next character to determine if the bitstring is valid and closed, + // valid and open because we reached the EOF, or invalid, in which case we + // will treat it as a regular string. + match self.first() { + Some(STRING_START) => Some(TokenKind::Bitstring { terminated: true }), + None => Some(TokenKind::Bitstring { terminated: false }), + _ => None, + } + } + + /// Tries parsing a hardware qubit literal, consisting of a `$` sign followed by + /// ASCII digits. + fn hardware_qubit(&mut self, c: char) -> bool { + if c == '$' { + self.eat_while(|c| c.is_ascii_digit()).is_some() + } else { + false + } + } +} + +impl Iterator for Lexer<'_> { + type Item = Token; + + fn next(&mut self) -> Option { + let (offset, c) = self.chars.next()?; + let kind = if let Some(kind) = self.comment(c) { + TokenKind::Comment(kind) + } else if self.whitespace(c) { + TokenKind::Whitespace + } else if self.newline(c) { + TokenKind::Newline + } else if let Some(ident) = self.ident(c) { + ident + } else if self.hardware_qubit(c) { + TokenKind::HardwareQubit + } else { + match self.number(c) { + Ok(number) => TokenKind::Number(number), + Err(LexError::Incomplete(_)) => TokenKind::Unknown, + Err(LexError::None) => self + .string(c) + .or_else(|| single(c).map(TokenKind::Single)) + .unwrap_or(TokenKind::Unknown), + } + }; + let offset: u32 = offset.try_into().expect("offset should fit into u32"); + Some(Token { + kind, + offset: offset + self.starting_offset, + }) + } +} + +fn single(c: char) -> Option { + match c { + '-' => Some(Single::Minus), + ',' => Some(Single::Comma), + ';' => Some(Single::Semi), + ':' => Some(Single::Colon), + '!' => Some(Single::Bang), + '.' => Some(Single::Dot), + '(' => Some(Single::Open(Delim::Paren)), + ')' => Some(Single::Close(Delim::Paren)), + '[' => Some(Single::Open(Delim::Bracket)), + ']' => Some(Single::Close(Delim::Bracket)), + '{' => Some(Single::Open(Delim::Brace)), + '}' => Some(Single::Close(Delim::Brace)), + '@' => Some(Single::At), + '*' => Some(Single::Star), + '/' => Some(Single::Slash), + '&' => Some(Single::Amp), + '%' => Some(Single::Percent), + '^' => Some(Single::Caret), + '+' => Some(Single::Plus), + '<' => Some(Single::Lt), + '=' => Some(Single::Eq), + '>' => Some(Single::Gt), + '|' => Some(Single::Bar), + '~' => Some(Single::Tilde), + _ => None, + } +} + +fn is_bitstring_char(c: char) -> bool { + c == '0' || c == '1' || c == '_' +} + +fn is_newline(c: char) -> bool { + c == '\n' || c == '\r' +} + +fn is_whitespace(c: char) -> bool { + !is_newline(c) && c.is_whitespace() +} diff --git a/compiler/qsc_qasm3/src/lex/raw/tests.rs b/compiler/qsc_qasm3/src/lex/raw/tests.rs new file mode 100644 index 0000000000..96ff8383ec --- /dev/null +++ b/compiler/qsc_qasm3/src/lex/raw/tests.rs @@ -0,0 +1,1112 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +use super::Lexer; +use crate::lex::raw::{Single, Token, TokenKind}; +use expect_test::{expect, Expect}; + +fn check(input: &str, expect: &Expect) { + let actual: Vec<_> = Lexer::new(input).collect(); + expect.assert_debug_eq(&actual); +} + +#[test] +fn singles() { + for single in enum_iterator::all::() { + let actual: Vec<_> = Lexer::new(&single.to_string()).collect(); + let kind = TokenKind::Single(single); + assert_eq!(actual, vec![Token { kind, offset: 0 }]); + } +} + +#[test] +fn braces() { + check( + "{}", + &expect![[r#" + [ + Token { + kind: Single( + Open( + Brace, + ), + ), + offset: 0, + }, + Token { + kind: Single( + Close( + Brace, + ), + ), + offset: 1, + }, + ] + "#]], + ); +} + +#[test] +fn negate() { + check( + "-x", + &expect![[r#" + [ + Token { + kind: Single( + Minus, + ), + offset: 0, + }, + Token { + kind: Ident, + offset: 1, + }, + ] + "#]], + ); +} + +#[test] +fn whitespace() { + check( + "- x", + &expect![[r#" + [ + Token { + kind: Single( + Minus, + ), + offset: 0, + }, + Token { + kind: Whitespace, + offset: 1, + }, + Token { + kind: Ident, + offset: 4, + }, + ] + "#]], + ); +} + +#[test] +fn comment() { + check( + "//comment\nx", + &expect![[r#" + [ + Token { + kind: Comment( + Normal, + ), + offset: 0, + }, + Token { + kind: Newline, + offset: 9, + }, + Token { + kind: Ident, + offset: 10, + }, + ] + "#]], + ); +} + +#[test] +fn block_comment() { + check( + "/* comment\n x */", + &expect![[r#" + [ + Token { + kind: Comment( + Block, + ), + offset: 0, + }, + ] + "#]], + ); +} + +#[test] +fn comment_four_slashes() { + check( + "////comment\nx", + &expect![[r#" + [ + Token { + kind: Comment( + Normal, + ), + offset: 0, + }, + Token { + kind: Newline, + offset: 11, + }, + Token { + kind: Ident, + offset: 12, + }, + ] + "#]], + ); +} + +#[test] +fn string() { + check( + r#""string""#, + &expect![[r#" + [ + Token { + kind: String { + terminated: true, + }, + offset: 0, + }, + ] + "#]], + ); +} + +#[test] +fn string_escape_quote() { + check( + r#""str\"ing""#, + &expect![[r#" + [ + Token { + kind: String { + terminated: true, + }, + offset: 0, + }, + ] + "#]], + ); +} + +#[test] +fn string_missing_ending() { + check( + r#""string"#, + &expect![[r#" + [ + Token { + kind: String { + terminated: false, + }, + offset: 0, + }, + ] + "#]], + ); +} + +#[test] +fn binary() { + check( + "0b10110", + &expect![[r#" + [ + Token { + kind: Number( + Int( + Binary, + ), + ), + offset: 0, + }, + ] + "#]], + ); + check( + "0B10110", + &expect![[r#" + [ + Token { + kind: Number( + Int( + Binary, + ), + ), + offset: 0, + }, + ] + "#]], + ); +} + +#[test] +fn octal() { + check( + "0o70351", + &expect![[r#" + [ + Token { + kind: Number( + Int( + Octal, + ), + ), + offset: 0, + }, + ] + "#]], + ); + check( + "0O70351", + &expect![[r#" + [ + Token { + kind: Number( + Int( + Octal, + ), + ), + offset: 0, + }, + ] + "#]], + ); +} + +#[test] +fn decimal() { + check( + "123", + &expect![[r#" + [ + Token { + kind: Number( + Int( + Decimal, + ), + ), + offset: 0, + }, + ] + "#]], + ); +} + +#[test] +fn number_seps() { + check( + "123_456", + &expect![[r#" + [ + Token { + kind: Number( + Int( + Decimal, + ), + ), + offset: 0, + }, + ] + "#]], + ); +} + +#[test] +fn float_dot() { + check( + "0..", + &expect![[r#" + [ + Token { + kind: Number( + Float, + ), + offset: 0, + }, + Token { + kind: Single( + Dot, + ), + offset: 2, + }, + ] + "#]], + ); +} + +#[test] +fn float_dot2() { + check( + ".0.", + &expect![[r#" + [ + Token { + kind: Number( + Float, + ), + offset: 0, + }, + Token { + kind: Single( + Dot, + ), + offset: 2, + }, + ] + "#]], + ); +} + +#[test] +fn leading_dot_float() { + check( + ".0", + &expect![[r#" + [ + Token { + kind: Number( + Float, + ), + offset: 0, + }, + ] + "#]], + ); +} + +#[test] +fn dot_float() { + check( + "..0", + &expect![[r#" + [ + Token { + kind: Single( + Dot, + ), + offset: 0, + }, + Token { + kind: Number( + Float, + ), + offset: 1, + }, + ] + "#]], + ); +} + +#[test] +fn dot_dot_float() { + check( + "...0", + &expect![[r#" + [ + Token { + kind: Single( + Dot, + ), + offset: 0, + }, + Token { + kind: Single( + Dot, + ), + offset: 1, + }, + Token { + kind: Number( + Float, + ), + offset: 2, + }, + ] + "#]], + ); +} + +#[test] +fn hexadecimal() { + check( + "0x123abc", + &expect![[r#" + [ + Token { + kind: Number( + Int( + Hexadecimal, + ), + ), + offset: 0, + }, + ] + "#]], + ); + check( + "0X123abc", + &expect![[r#" + [ + Token { + kind: Number( + Int( + Hexadecimal, + ), + ), + offset: 0, + }, + ] + "#]], + ); +} + +#[test] +fn negative() { + check( + "-4", + &expect![[r#" + [ + Token { + kind: Single( + Minus, + ), + offset: 0, + }, + Token { + kind: Number( + Int( + Decimal, + ), + ), + offset: 1, + }, + ] + "#]], + ); +} + +#[test] +fn positive() { + check( + "+4", + &expect![[r#" + [ + Token { + kind: Single( + Plus, + ), + offset: 0, + }, + Token { + kind: Number( + Int( + Decimal, + ), + ), + offset: 1, + }, + ] + "#]], + ); +} + +#[test] +fn float() { + check( + "1.23", + &expect![[r#" + [ + Token { + kind: Number( + Float, + ), + offset: 0, + }, + ] + "#]], + ); +} + +#[test] +fn leading_zero() { + check( + "0123", + &expect![[r#" + [ + Token { + kind: Number( + Int( + Decimal, + ), + ), + offset: 0, + }, + ] + "#]], + ); +} + +#[test] +fn leading_point() { + check( + ".123", + &expect![[r#" + [ + Token { + kind: Number( + Float, + ), + offset: 0, + }, + ] + "#]], + ); +} + +#[test] +fn trailing_point() { + check( + "123.", + &expect![[r#" + [ + Token { + kind: Number( + Float, + ), + offset: 0, + }, + ] + "#]], + ); +} + +#[test] +fn exp() { + check( + "1e23", + &expect![[r#" + [ + Token { + kind: Number( + Float, + ), + offset: 0, + }, + ] + "#]], + ); + check( + "1E23", + &expect![[r#" + [ + Token { + kind: Number( + Float, + ), + offset: 0, + }, + ] + "#]], + ); +} + +#[test] +fn exp_plus() { + check( + "1e+23", + &expect![[r#" + [ + Token { + kind: Number( + Float, + ), + offset: 0, + }, + ] + "#]], + ); +} + +#[test] +fn exp_minus() { + check( + "1e-23", + &expect![[r#" + [ + Token { + kind: Number( + Float, + ), + offset: 0, + }, + ] + "#]], + ); +} + +#[test] +fn leading_point_exp() { + check( + ".25e2", + &expect![[r#" + [ + Token { + kind: Number( + Float, + ), + offset: 0, + }, + ] + "#]], + ); +} + +#[test] +fn leading_zero_point() { + check( + "0.25", + &expect![[r#" + [ + Token { + kind: Number( + Float, + ), + offset: 0, + }, + ] + "#]], + ); +} + +#[test] +fn leading_zero_zero_point() { + check( + "00.25", + &expect![[r#" + [ + Token { + kind: Number( + Float, + ), + offset: 0, + }, + ] + "#]], + ); +} + +#[test] +fn leading_zero_exp() { + check( + "0.25e2", + &expect![[r#" + [ + Token { + kind: Number( + Float, + ), + offset: 0, + }, + ] + "#]], + ); +} + +#[test] +fn unknown() { + check( + "##", + &expect![[r#" + [ + Token { + kind: Unknown, + offset: 0, + }, + Token { + kind: Unknown, + offset: 1, + }, + ] + "#]], + ); +} + +#[test] +fn float_hexadecimal() { + check( + "0x123.45", + &expect![[r#" + [ + Token { + kind: Number( + Int( + Hexadecimal, + ), + ), + offset: 0, + }, + Token { + kind: Number( + Float, + ), + offset: 5, + }, + ] + "#]], + ); +} + +#[test] +fn fragments() { + check( + "im dt ns us µs ms s", + &expect![[r#" + [ + Token { + kind: LiteralFragment( + Imag, + ), + offset: 0, + }, + Token { + kind: Whitespace, + offset: 2, + }, + Token { + kind: LiteralFragment( + Dt, + ), + offset: 3, + }, + Token { + kind: Whitespace, + offset: 5, + }, + Token { + kind: LiteralFragment( + Ns, + ), + offset: 6, + }, + Token { + kind: Whitespace, + offset: 8, + }, + Token { + kind: LiteralFragment( + Us, + ), + offset: 9, + }, + Token { + kind: Whitespace, + offset: 11, + }, + Token { + kind: LiteralFragment( + Us, + ), + offset: 12, + }, + Token { + kind: Whitespace, + offset: 15, + }, + Token { + kind: LiteralFragment( + Ms, + ), + offset: 16, + }, + Token { + kind: Whitespace, + offset: 18, + }, + Token { + kind: LiteralFragment( + S, + ), + offset: 19, + }, + ] + "#]], + ); +} + +#[test] +fn identifiers_with_fragment_prefixes() { + check( + "imx dtx nsx usx µsx msx sx", + &expect![[r#" + [ + Token { + kind: Ident, + offset: 0, + }, + Token { + kind: Whitespace, + offset: 3, + }, + Token { + kind: Ident, + offset: 4, + }, + Token { + kind: Whitespace, + offset: 7, + }, + Token { + kind: Ident, + offset: 8, + }, + Token { + kind: Whitespace, + offset: 11, + }, + Token { + kind: Ident, + offset: 12, + }, + Token { + kind: Whitespace, + offset: 15, + }, + Token { + kind: Ident, + offset: 16, + }, + Token { + kind: Whitespace, + offset: 20, + }, + Token { + kind: Ident, + offset: 21, + }, + Token { + kind: Whitespace, + offset: 24, + }, + Token { + kind: Ident, + offset: 25, + }, + ] + "#]], + ); +} + +#[test] +fn leading_underscores_digit() { + check( + "___3", + &expect![[r#" + [ + Token { + kind: Ident, + offset: 0, + }, + ] + "#]], + ); +} + +#[test] +fn leading_underscores_ident_dot() { + check( + "___3.", + &expect![[r#" + [ + Token { + kind: Ident, + offset: 0, + }, + Token { + kind: Single( + Dot, + ), + offset: 4, + }, + ] + "#]], + ); +} + +#[test] +fn leading_underscores_binary() { + check( + "___0b11", + &expect![[r#" + [ + Token { + kind: Ident, + offset: 0, + }, + ] + "#]], + ); +} + +#[test] +fn leading_underscores_binary_extended() { + check( + "___0b11abc", + &expect![[r#" + [ + Token { + kind: Ident, + offset: 0, + }, + ] + "#]], + ); +} + +#[test] +fn leading_underscores_identifier() { + check( + "___abc", + &expect![[r#" + [ + Token { + kind: Ident, + offset: 0, + }, + ] + "#]], + ); +} + +#[test] +fn hardware_qubit() { + check( + "$12", + &expect![[r#" + [ + Token { + kind: HardwareQubit, + offset: 0, + }, + ] + "#]], + ); +} + +#[test] +fn hardware_qubit_dot() { + check( + "$2.", + &expect![[r#" + [ + Token { + kind: HardwareQubit, + offset: 0, + }, + Token { + kind: Single( + Dot, + ), + offset: 2, + }, + ] + "#]], + ); +} + +#[test] +fn incomplete_hardware_qubit() { + check( + "$", + &expect![[r#" + [ + Token { + kind: Unknown, + offset: 0, + }, + ] + "#]], + ); +} + +#[test] +fn incomplete_hardware_qubit_identifier() { + check( + "$a", + &expect![[r#" + [ + Token { + kind: Unknown, + offset: 0, + }, + Token { + kind: Ident, + offset: 1, + }, + ] + "#]], + ); +} + +#[test] +fn incomplete_hardware_qubit_float() { + check( + "$.2", + &expect![[r#" + [ + Token { + kind: Unknown, + offset: 0, + }, + Token { + kind: Number( + Float, + ), + offset: 1, + }, + ] + "#]], + ); +} + +#[test] +fn hardware_qubit_with_underscore_at_end() { + check( + "$12_", + &expect![[r#" + [ + Token { + kind: HardwareQubit, + offset: 0, + }, + Token { + kind: Ident, + offset: 3, + }, + ] + "#]], + ); +} + +#[test] +fn hardware_qubit_with_underscore_in_the_middle() { + check( + "$12_3", + &expect![[r#" + [ + Token { + kind: HardwareQubit, + offset: 0, + }, + Token { + kind: Ident, + offset: 3, + }, + ] + "#]], + ); +} diff --git a/compiler/qsc_qasm3/src/lib.rs b/compiler/qsc_qasm3/src/lib.rs index b94e18abb4..5415533de3 100644 --- a/compiler/qsc_qasm3/src/lib.rs +++ b/compiler/qsc_qasm3/src/lib.rs @@ -6,6 +6,8 @@ mod ast_builder; mod compile; pub use compile::qasm_to_program; pub mod io; +mod keyword; +mod lex; mod oqasm_helpers; mod oqasm_types; pub mod parse; From 0408e2a3280f8ec1ae0a7f4570e0279577934250 Mon Sep 17 00:00:00 2001 From: Ian Davis Date: Mon, 3 Feb 2025 18:02:15 -0800 Subject: [PATCH 002/108] Add initial AST and parser infrastructure (#2148) --- Cargo.lock | 2 + compiler/qsc_qasm3/Cargo.toml | 2 + compiler/qsc_qasm3/src/ast.rs | 2018 +++++++++++++++++ compiler/qsc_qasm3/src/keyword.rs | 76 +- compiler/qsc_qasm3/src/lex.rs | 2 +- compiler/qsc_qasm3/src/lex/cooked.rs | 18 +- compiler/qsc_qasm3/src/lex/cooked/tests.rs | 4 +- compiler/qsc_qasm3/src/lib.rs | 2 + compiler/qsc_qasm3/src/parser.rs | 265 +++ compiler/qsc_qasm3/src/parser/completion.rs | 39 + .../src/parser/completion/collector.rs | 186 ++ .../qsc_qasm3/src/parser/completion/tests.rs | 55 + .../src/parser/completion/word_kinds.rs | 179 ++ compiler/qsc_qasm3/src/parser/error.rs | 137 ++ compiler/qsc_qasm3/src/parser/expr.rs | 292 +++ compiler/qsc_qasm3/src/parser/prgm.rs | 76 + compiler/qsc_qasm3/src/parser/prim.rs | 326 +++ compiler/qsc_qasm3/src/parser/prim/tests.rs | 265 +++ compiler/qsc_qasm3/src/parser/scan.rs | 248 ++ compiler/qsc_qasm3/src/parser/stmt.rs | 132 ++ compiler/qsc_qasm3/src/parser/tests.rs | 172 ++ 21 files changed, 4477 insertions(+), 19 deletions(-) create mode 100644 compiler/qsc_qasm3/src/ast.rs create mode 100644 compiler/qsc_qasm3/src/parser.rs create mode 100644 compiler/qsc_qasm3/src/parser/completion.rs create mode 100644 compiler/qsc_qasm3/src/parser/completion/collector.rs create mode 100644 compiler/qsc_qasm3/src/parser/completion/tests.rs create mode 100644 compiler/qsc_qasm3/src/parser/completion/word_kinds.rs create mode 100644 compiler/qsc_qasm3/src/parser/error.rs create mode 100644 compiler/qsc_qasm3/src/parser/expr.rs create mode 100644 compiler/qsc_qasm3/src/parser/prgm.rs create mode 100644 compiler/qsc_qasm3/src/parser/prim.rs create mode 100644 compiler/qsc_qasm3/src/parser/prim/tests.rs create mode 100644 compiler/qsc_qasm3/src/parser/scan.rs create mode 100644 compiler/qsc_qasm3/src/parser/stmt.rs create mode 100644 compiler/qsc_qasm3/src/parser/tests.rs diff --git a/Cargo.lock b/Cargo.lock index 46bef4aeb9..e4520e48a1 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1480,9 +1480,11 @@ dependencies = [ "difference", "enum-iterator", "expect-test", + "indenter", "indoc", "miette", "num-bigint", + "num-traits", "oq3_parser", "oq3_semantics", "oq3_source_file", diff --git a/compiler/qsc_qasm3/Cargo.toml b/compiler/qsc_qasm3/Cargo.toml index 4be5b3092d..0884792b61 100644 --- a/compiler/qsc_qasm3/Cargo.toml +++ b/compiler/qsc_qasm3/Cargo.toml @@ -10,7 +10,9 @@ version.workspace = true [dependencies] bitflags = { workspace = true } enum-iterator = { workspace = true } +indenter = { workspace = true } num-bigint = { workspace = true } +num-traits = { workspace = true } miette = { workspace = true } qsc_ast = { path = "../qsc_ast" } qsc_data_structures = { path = "../qsc_data_structures" } diff --git a/compiler/qsc_qasm3/src/ast.rs b/compiler/qsc_qasm3/src/ast.rs new file mode 100644 index 0000000000..b63abb55d3 --- /dev/null +++ b/compiler/qsc_qasm3/src/ast.rs @@ -0,0 +1,2018 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +// while we work through the conversion, allow dead code to avoid warnings +#![allow(dead_code)] + +use indenter::{indented, Indented}; +use qsc_data_structures::span::{Span, WithSpan}; +use std::{ + fmt::{self, Display, Formatter, Write}, + hash::Hash, + rc::Rc, +}; + +fn set_indentation<'a, 'b>( + indent: Indented<'a, Formatter<'b>>, + level: usize, +) -> Indented<'a, Formatter<'b>> { + match level { + 0 => indent.with_str(""), + 1 => indent.with_str(" "), + 2 => indent.with_str(" "), + _ => unimplemented!("indentation level not supported"), + } +} + +// TODO: profile this with iai-callgrind in a large OpenQASM3 +// sample to verify that is actually faster than using Vec. +/// An alternative to `Vec` that uses less stack space. +type List = Box<[Box]>; + +#[derive(Clone, Debug)] +pub struct Program { + pub span: Span, + pub statements: List, + pub version: Option, +} + +impl Display for Program { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + let mut indent = set_indentation(indented(f), 0); + write!(indent, "Program {}:", self.span)?; + indent = set_indentation(indent, 1); + if let Some(version) = &self.version { + write!(indent, "\nVersion {version}")?; + } + for stmt in &self.statements { + write!(indent, "\n{stmt}")?; + } + Ok(()) + } +} + +#[derive(Clone, Debug)] +pub struct Stmt { + pub span: Span, + pub annotations: List, + pub kind: Box, +} + +impl Display for Stmt { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + let mut indent = set_indentation(indented(f), 0); + for annotation in &self.annotations { + write!(indent, "\n{annotation}")?; + } + write!(indent, "Stmt {}", self.span)?; + write!(indent, "\n{}", self.kind)?; + Ok(()) + } +} + +#[derive(Clone, Debug)] +pub struct Annotation { + pub span: Span, + pub name: Box, + pub value: Option>, +} +impl Display for Annotation { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + if let Some(value) = &self.value { + write!(f, "Annotation {}: {}, {}", self.span, self.name, value) + } else { + write!(f, "Annotation {}: {}", self.span, self.name) + } + } +} + +/// A path that may or may not have been successfully parsed. +#[derive(Clone, Debug, Hash, PartialEq, Eq)] +pub enum PathKind { + /// A successfully parsed path. + Ok(Box), + /// An invalid path. + Err(Option>), +} + +impl Default for PathKind { + fn default() -> Self { + PathKind::Err(None) + } +} + +impl Display for PathKind { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + match self { + PathKind::Ok(path) => write!(f, "{path}")?, + PathKind::Err(Some(incomplete_path)) => { + let mut indent = set_indentation(indented(f), 0); + write!(indent, "Err IncompletePath {}:", incomplete_path.span)?; + indent = set_indentation(indent, 1); + for part in &incomplete_path.segments { + write!(indent, "\n{part}")?; + } + } + PathKind::Err(None) => write!(f, "Err",)?, + } + Ok(()) + } +} + +/// A path that was successfully parsed up to a certain `.`, +/// but is missing its final identifier. +#[derive(Clone, Debug, PartialEq, Eq, Hash)] +pub struct IncompletePath { + /// The whole span of the incomplete path, + /// including the final `.` and any whitespace or keyword + /// that follows it. + pub span: Span, + /// Any segments that were successfully parsed before the final `.`. + pub segments: Box<[Ident]>, + /// Whether a keyword exists after the final `.`. + /// This keyword can be presumed to be a partially typed identifier. + pub keyword: bool, +} + +/// A path to a declaration or a field access expression, +/// to be disambiguated during name resolution. +#[derive(Clone, Debug, Hash, PartialEq, Eq)] +pub struct Path { + /// The span. + pub span: Span, + /// The segments that make up the front of the path before the final `.`. + pub segments: Option>, + /// The declaration or field name. + pub name: Box, +} + +impl Display for Path { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + if self.segments.is_none() { + write!(f, "Path {} ({})", self.span, self.name)?; + } else { + let mut indent = set_indentation(indented(f), 0); + write!(indent, "Path {}:", self.span)?; + indent = set_indentation(indent, 1); + if let Some(parts) = &self.segments { + for part in parts { + write!(indent, "\n{part}")?; + } + } + write!(indent, "\n{}", self.name)?; + } + Ok(()) + } +} + +impl WithSpan for Path { + fn with_span(self, span: Span) -> Self { + Self { span, ..self } + } +} + +#[derive(Clone, Debug)] +pub enum AssignmentExpr { + Expr(Expr), + Measurement(MeasureExpr), +} + +impl Display for AssignmentExpr { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + match self { + AssignmentExpr::Expr(expr) => write!(f, "AssignmentExpr {expr}"), + AssignmentExpr::Measurement(measure) => write!(f, "AssignmentExpr {measure}"), + } + } +} + +#[derive(Clone, Debug)] +pub struct MeasureExpr { + pub span: Span, + pub operand: GateOperand, +} + +impl Display for MeasureExpr { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + write!(f, "MeasureExpr {}: {}", self.span, self.operand) + } +} +/// A binary operator. +#[derive(Clone, Debug)] +pub enum BinOp { + /// Addition: `+`. + Add, + /// Bitwise AND: `&`. + AndB, + /// Logical AND: `&&`. + AndL, + /// Division: `/`. + Div, + /// Equality: `==`. + Eq, + /// Exponentiation: `**`. + Exp, + /// Greater than: `>`. + Gt, + /// Greater than or equal: `>=`. + Gte, + /// Less than: `<`. + Lt, + /// Less than or equal: `<=`. + Lte, + /// Modulus: `%`. + Mod, + /// Multiplication: `*`. + Mul, + /// Inequality: `!=`. + Neq, + /// Bitwise OR: `|`. + OrB, + /// Logical OR: `||`. + OrL, + /// Shift left: `<<`. + Shl, + /// Shift right: `>>`. + Shr, + /// Subtraction: `-`. + Sub, + /// Bitwise XOR: `^`. + XorB, +} + +impl Display for BinOp { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + match self { + BinOp::Add => write!(f, "Add"), + BinOp::AndB => write!(f, "AndB"), + BinOp::AndL => write!(f, "AndL"), + BinOp::Div => write!(f, "Div"), + BinOp::Eq => write!(f, "Eq"), + BinOp::Exp => write!(f, "Exp"), + BinOp::Gt => write!(f, "Gt"), + BinOp::Gte => write!(f, "Gte"), + BinOp::Lt => write!(f, "Lt"), + BinOp::Lte => write!(f, "Lte"), + BinOp::Mod => write!(f, "Mod"), + BinOp::Mul => write!(f, "Mul"), + BinOp::Neq => write!(f, "Neq"), + BinOp::OrB => write!(f, "OrB"), + BinOp::OrL => write!(f, "OrL"), + BinOp::Shl => write!(f, "Shl"), + BinOp::Shr => write!(f, "Shr"), + BinOp::Sub => write!(f, "Sub"), + BinOp::XorB => write!(f, "XorB"), + } + } +} + +/// A unary operator. +#[derive(Clone, Debug)] +pub enum UnOp { + /// Negation: `-`. + Neg, + /// Bitwise NOT: `~`. + NotB, + /// Logical NOT: `!`. + NotL, +} + +impl Display for UnOp { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + match self { + UnOp::Neg => write!(f, "Neg"), + UnOp::NotB => write!(f, "NotB"), + UnOp::NotL => write!(f, "NotL"), + } + } +} + +#[derive(Clone, Debug)] +pub enum GateOperand { + Ident(Box), + HardwareQubit(Box), +} + +impl Display for GateOperand { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + match self { + GateOperand::Ident(ident) => write!(f, "GateOperand {ident}"), + GateOperand::HardwareQubit(qubit) => write!(f, "GateOperand {qubit}"), + } + } +} + +#[derive(Clone, Debug)] +pub struct HardwareQubit { + pub span: Span, + pub name: Rc, +} + +impl Display for HardwareQubit { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + write!(f, "HardwareQubit {}: {}", self.span, self.name) + } +} + +#[derive(Clone, Debug)] +pub struct Alias { + pub ident: Box, + pub expr: Box>, + pub span: Span, +} + +impl Display for Alias { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + let mut indent = set_indentation(indented(f), 0); + write!(indent, "Alias {}: {}", self.span, self.ident)?; + indent = set_indentation(indent, 1); + for expr in &*self.expr { + write!(indent, "\n{expr}")?; + } + Ok(()) + } +} + +#[derive(Clone, Debug)] +pub struct Assign { + pub ident: Box, + pub expr: Box, + pub span: Span, +} + +impl Display for Assign { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + write!(f, "Assign {}: {}, {}", self.span, self.ident, self.expr) + } +} + +#[derive(Clone, Debug)] +pub struct AssignOp { + pub op: BinOp, + pub ident: Box, + pub expr: Box, + pub span: Span, +} + +impl Display for AssignOp { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + write!( + f, + "AssignOp {}: {}, {}, {}", + self.span, self.op, self.ident, self.expr + ) + } +} + +/// A statement kind. +#[derive(Clone, Debug, Default)] +pub enum StmtKind { + Alias(Alias), + Assign(Assign), + AssignOp(AssignOp), + Barrier(BarrierStmt), + Box(BoxStmt), + Break(BreakStmt), + Block(Box), + Cal(CalibrationStmt), + CalibrationGrammar(CalibrationGrammarStmt), + ClassicalDecl(ClassicalDeclarationStmt), + ConstDecl(ConstantDeclaration), + Continue(ContinueStmt), + Def(DefStmt), + DefCal(DefCalStmt), + DelayStmt(DelayStmt), + /// An empty statement. + Empty, + ExprStmt(ExprStmt), + ExternDecl(ExternDecl), + For(ForStmt), + If(IfStmt), + Include(IncludeStmt), + IODeclaration(IODeclaration), + Measure(MeasureStmt), + Pragma(Pragma), + QuantumGateDefinition(QuantumGateDefinition), + QuantumDecl(QubitDeclaration), + Quantum(QuantumStmt), + Reset(ResetStmt), + Return(ReturnStmt), + Switch(SwitchStmt), + WhileLoop(WhileLoop), + /// An invalid statement. + #[default] + Err, +} + +impl Display for StmtKind { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + write!(f, "StmtKind: ")?; + match self { + StmtKind::Alias(alias) => write!(f, "{alias}"), + StmtKind::Assign(assign) => write!(f, "{assign}"), + StmtKind::AssignOp(assign_op) => write!(f, "{assign_op}"), + StmtKind::Barrier(barrier) => write!(f, "{barrier}"), + StmtKind::Box(box_stmt) => write!(f, "{box_stmt}"), + StmtKind::Break(break_stmt) => write!(f, "{break_stmt}"), + StmtKind::Block(block) => write!(f, "{block}"), + StmtKind::Cal(cal) => write!(f, "{cal}"), + StmtKind::CalibrationGrammar(grammar) => write!(f, "{grammar}"), + StmtKind::ClassicalDecl(decl) => write!(f, "{decl}"), + StmtKind::ConstDecl(decl) => write!(f, "{decl}"), + StmtKind::Continue(continue_stmt) => write!(f, "{continue_stmt}"), + StmtKind::Def(def) => write!(f, "{def}"), + StmtKind::DefCal(defcal) => write!(f, "{defcal}"), + StmtKind::DelayStmt(delay) => write!(f, "{delay}"), + StmtKind::Empty => write!(f, "Empty"), + StmtKind::ExprStmt(expr) => write!(f, "{expr}"), + StmtKind::ExternDecl(decl) => write!(f, "{decl}"), + StmtKind::For(for_stmt) => write!(f, "{for_stmt}"), + StmtKind::If(if_stmt) => write!(f, "{if_stmt}"), + StmtKind::Include(include) => write!(f, "{include}"), + StmtKind::IODeclaration(io) => write!(f, "{io}"), + StmtKind::Measure(measure) => write!(f, "{measure}"), + StmtKind::Pragma(pragma) => write!(f, "{pragma}"), + StmtKind::QuantumGateDefinition(gate) => write!(f, "{gate}"), + StmtKind::QuantumDecl(decl) => write!(f, "{decl}"), + StmtKind::Quantum(quantum_stmt) => write!(f, "{quantum_stmt}"), + StmtKind::Reset(reset_stmt) => write!(f, "{reset_stmt}"), + StmtKind::Return(return_stmt) => write!(f, "{return_stmt}"), + StmtKind::Switch(switch_stmt) => write!(f, "{switch_stmt}"), + StmtKind::WhileLoop(while_loop) => write!(f, "{while_loop}"), + StmtKind::Err => write!(f, "Err"), + } + } +} + +#[derive(Clone, Debug)] +pub struct CalibrationGrammarStmt { + pub span: Span, + pub name: String, +} + +impl Display for CalibrationGrammarStmt { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + write!(f, "CalibrationGrammarStmt {}: {}", self.span, self.name) + } +} + +#[derive(Clone, Debug)] +pub struct DefCalStmt {} + +impl Display for DefCalStmt { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + write!(f, "DefCalStmt") + } +} + +#[derive(Clone, Debug)] +pub struct IfStmt { + pub span: Span, + pub condition: ExprStmt, + pub if_block: List, + pub else_block: Option>, +} + +impl Display for IfStmt { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + let mut indent = set_indentation(indented(f), 0); + write!(indent, "IfStmt {}: {}", self.span, self.condition)?; + for stmt in &self.if_block { + write!(indent, "\n{stmt}")?; + } + if let Some(else_block) = &self.else_block { + write!(indent, "\nElse:")?; + for stmt in else_block { + write!(indent, "\n{stmt}")?; + } + } + Ok(()) + } +} + +#[derive(Clone, Debug)] +pub struct DelayStmt { + pub span: Span, + pub duration: ExprStmt, +} + +impl Display for DelayStmt { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + write!(f, "DelayStmt {}: {}", self.span, self.duration) + } +} + +#[derive(Clone, Debug)] +pub struct BarrierStmt { + pub span: Span, + pub qubits: List, +} + +impl Display for BarrierStmt { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + let mut indent = set_indentation(indented(f), 0); + write!(indent, "Barrier {}: [", self.span)?; + for qubit in &self.qubits { + write!(indent, "\n{qubit}")?; + } + write!(indent, "]") + } +} + +#[derive(Clone, Debug)] +pub struct ResetStmt { + pub span: Span, + pub operand: Box, +} + +impl Display for ResetStmt { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + write!(f, "ResetStmt {}: {}", self.span, self.operand) + } +} + +/// A sequenced block of statements. +#[derive(Clone, Debug, Default)] +pub struct Block { + /// The span. + pub span: Span, + /// The statements in the block. + pub stmts: List, +} + +impl Display for Block { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + if self.stmts.is_empty() { + write!(f, "Block {}: ", self.span)?; + } else { + let mut indent = set_indentation(indented(f), 0); + write!(indent, "Block {}:", self.span)?; + indent = set_indentation(indent, 1); + for s in &self.stmts { + write!(indent, "\n{s}")?; + } + } + Ok(()) + } +} + +#[derive(Clone, Debug)] +pub enum Identifier { + Ident(Box), + IndexedIdent(Box), +} + +impl Display for Identifier { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + match self { + Identifier::Ident(ident) => write!(f, "{ident}"), + Identifier::IndexedIdent(ident) => write!(f, "{ident}"), + } + } +} + +#[derive(Clone, Debug, Hash, PartialEq, Eq)] +pub struct Ident { + pub span: Span, + pub name: Rc, +} + +impl Default for Ident { + fn default() -> Self { + Ident { + span: Span::default(), + name: "".into(), + } + } +} + +impl Display for Ident { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + write!(f, "Ident {} \"{}\"", self.span, self.name) + } +} + +impl WithSpan for Ident { + fn with_span(self, span: Span) -> Self { + Self { span, ..self } + } +} + +#[derive(Clone, Debug)] +pub struct IndexedIdent { + pub span: Span, + pub name: Ident, + pub indices: List, +} + +impl Display for IndexedIdent { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + write!(f, "IndexedIdent {}: {}[", self.span, self.name)?; + + for index in &self.indices { + write!(f, "\n{index}")?; + } + write!(f, "]") + } +} + +#[derive(Clone, Debug)] +pub struct AliasStmt { + pub span: Span, + pub kind: Box, +} + +impl Display for AliasStmt { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + write!(f, "AliasStmt {}: {}", self.span, self.kind) + } +} + +#[derive(Clone, Debug)] +pub struct ExprStmt { + pub span: Span, + pub expr: Box, +} + +impl Display for ExprStmt { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + write!(f, "ExprStmt {}: {}", self.span, self.expr) + } +} + +#[derive(Clone, Debug, Default)] +pub struct Expr { + pub span: Span, + pub kind: Box, +} + +impl WithSpan for Expr { + fn with_span(self, span: Span) -> Self { + Self { span, ..self } + } +} + +impl Display for Expr { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + write!(f, "Expr {}: {}", self.span, self.kind) + } +} + +#[derive(Clone, Debug)] +pub struct DiscreteSet { + pub span: Span, + pub values: List, +} + +impl Display for DiscreteSet { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + let mut indent = set_indentation(indented(f), 0); + write!(indent, "DiscreteSet {}:", self.span)?; + indent = set_indentation(indent, 1); + for value in &self.values { + write!(indent, "\n{value}")?; + } + Ok(()) + } +} + +#[derive(Clone, Debug)] +pub struct RangeDefinition { + pub span: Span, + pub start: Option, + pub end: Option, + pub step: Option, +} + +#[derive(Clone, Debug)] +pub struct QuantumGateModifier { + pub span: Span, + pub qubit: Box, +} + +impl Display for QuantumGateModifier { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + write!(f, "QuantumGateModifier {}: {}", self.span, self.qubit) + } +} + +#[derive(Clone, Debug)] +pub struct QuantumMeasurement { + pub span: Span, + pub qubit: Box, +} + +impl Display for QuantumMeasurement { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + write!(f, "QuantumMeasurement {}: {}", self.span, self.qubit) + } +} + +#[derive(Clone, Debug)] +pub struct ClassicalArgument { + pub span: Span, + pub r#type: ClassicalType, + pub name: Identifier, + pub access: Option, +} + +impl Display for ClassicalArgument { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + if let Some(access) = &self.access { + write!( + f, + "ClassicalArgument {}: {}, {}, {}", + self.span, self.r#type, self.name, access + ) + } else { + write!( + f, + "ClassicalArgument {}: {}, {}", + self.span, self.r#type, self.name + ) + } + } +} + +#[derive(Clone, Debug)] +pub struct ExternArgument { + pub span: Span, + pub r#type: ClassicalType, + pub access: Option, +} + +impl Display for ExternArgument { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + if let Some(access) = &self.access { + write!( + f, + "ExternArgument {}: {}, {}", + self.span, self.r#type, access + ) + } else { + write!(f, "ExternArgument {}: {}", self.span, self.r#type) + } + } +} + +#[derive(Clone, Debug)] +pub struct ClassicalType { + pub span: Span, + pub kind: ClassicalTypeKind, +} + +impl Display for ClassicalType { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + write!(f, "ClassicalType {}: {}", self.span, self.kind) + } +} + +#[derive(Clone, Debug)] +pub enum ClassicalTypeKind { + Int(IntType), + UInt(UIntType), + Float(FloatType), + Complex(ComplexType), + Angle(AngleType), + Bit(BitType), + BoolType, + Array(ArrayType), + ArrayReference(ArrayReferenceType), + Duration, + Stretch, +} + +impl Display for ClassicalTypeKind { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + match self { + ClassicalTypeKind::Int(int) => write!(f, "ClassicalTypeKind {int}"), + ClassicalTypeKind::UInt(uint) => write!(f, "ClassicalTypeKind {uint}"), + ClassicalTypeKind::Float(float) => write!(f, "ClassicalTypeKind {float}"), + ClassicalTypeKind::Complex(complex) => write!(f, "ClassicalTypeKind {complex}"), + ClassicalTypeKind::Angle(angle) => write!(f, "ClassicalTypeKind {angle}"), + ClassicalTypeKind::Bit(bit) => write!(f, "ClassicalTypeKind {bit}"), + ClassicalTypeKind::BoolType => write!(f, "ClassicalTypeKind BoolType"), + ClassicalTypeKind::Array(array) => write!(f, "ClassicalTypeKind {array}"), + ClassicalTypeKind::ArrayReference(array) => write!(f, "ClassicalTypeKind {array}"), + ClassicalTypeKind::Duration => write!(f, "ClassicalTypeKind Duration"), + ClassicalTypeKind::Stretch => write!(f, "ClassicalTypeKind Stretch"), + } + } +} + +#[derive(Clone, Debug)] +pub enum ArrayBaseTypeKind { + Int(IntType), + UInt(UIntType), + Float(FloatType), + Complex(ComplexType), + Angle(AngleType), + Bit(BitType), + BoolType, +} + +impl Display for ArrayBaseTypeKind { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + match self { + ArrayBaseTypeKind::Int(int) => write!(f, "ArrayBaseTypeKind {int}"), + ArrayBaseTypeKind::UInt(uint) => write!(f, "ArrayBaseTypeKind {uint}"), + ArrayBaseTypeKind::Float(float) => write!(f, "ArrayBaseTypeKind {float}"), + ArrayBaseTypeKind::Complex(complex) => write!(f, "ArrayBaseTypeKind {complex}"), + ArrayBaseTypeKind::Angle(angle) => write!(f, "ArrayBaseTypeKind {angle}"), + ArrayBaseTypeKind::Bit(bit) => write!(f, "ArrayBaseTypeKind {bit}"), + ArrayBaseTypeKind::BoolType => write!(f, "ArrayBaseTypeKind BoolType"), + } + } +} + +#[derive(Clone, Debug)] +pub struct IntType { + pub size: Option, + pub span: Span, +} + +impl Display for IntType { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + if let Some(size) = &self.size { + write!(f, "IntType {}: {}", self.span, size) + } else { + write!(f, "IntType") + } + } +} + +#[derive(Clone, Debug)] +pub struct UIntType { + pub size: Option, + pub span: Span, +} + +impl Display for UIntType { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + if let Some(size) = &self.size { + write!(f, "UIntType {}: {}", self.span, size) + } else { + write!(f, "UIntType") + } + } +} + +#[derive(Clone, Debug)] +pub struct FloatType { + pub size: Option, + pub span: Span, +} + +impl Display for FloatType { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + if let Some(size) = &self.size { + write!(f, "FloatType {}: {}", self.span, size) + } else { + write!(f, "FloatType") + } + } +} + +#[derive(Clone, Debug)] +pub struct ComplexType { + pub base_size: Option, + pub span: Span, +} + +impl Display for ComplexType { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + if let Some(size) = &self.base_size { + write!(f, "ComplexType {}: {}", self.span, size) + } else { + write!(f, "ComplexType") + } + } +} + +#[derive(Clone, Debug)] +pub struct AngleType { + pub size: Option, + pub span: Span, +} + +impl Display for AngleType { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + if let Some(size) = &self.size { + write!(f, "AngleType {}: {}", self.span, size) + } else { + write!(f, "AngleType") + } + } +} + +#[derive(Clone, Debug)] +pub struct BitType { + pub size: Option, + pub span: Span, +} + +impl Display for BitType { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + if let Some(size) = &self.size { + write!(f, "BitType {}: {}", self.span, size) + } else { + write!(f, "BitType") + } + } +} + +#[derive(Clone, Debug)] +pub struct ArrayType { + pub span: Span, + pub base_type: ArrayBaseTypeKind, + pub dimensions: List, +} + +impl Display for ArrayType { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + let mut indent = set_indentation(indented(f), 0); + write!(indent, "ArrayType {}: {}", self.span, self.base_type)?; + for dimension in &self.dimensions { + write!(indent, "\n{dimension}")?; + } + Ok(()) + } +} + +#[derive(Clone, Debug)] +pub struct ArrayReferenceType { + pub span: Span, + pub base_type: ArrayBaseTypeKind, + pub dimensions: List, +} + +impl Display for ArrayReferenceType { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + let mut indent = set_indentation(indented(f), 0); + write!( + indent, + "ArrayReferenceType {}: {}", + self.span, self.base_type + )?; + for dimension in &self.dimensions { + write!(indent, "\n{dimension}")?; + } + Ok(()) + } +} + +#[derive(Clone, Debug)] +pub enum AccessControl { + ReadOnly, + Mutable, +} + +impl Display for AccessControl { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + match self { + AccessControl::ReadOnly => write!(f, "ReadOnly"), + AccessControl::Mutable => write!(f, "Mutable"), + } + } +} + +#[derive(Clone, Debug)] +pub struct QuantumArgument { + pub span: Span, + pub expr: Option, +} + +impl Display for QuantumArgument { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + write!(f, "QuantumArgument {}: {:?}", self.span, self.expr) + } +} + +#[derive(Clone, Debug)] +pub struct Pragma { + pub span: Span, + pub name: Box, + pub value: Option>, +} + +impl Display for Pragma { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + if let Some(value) = &self.value { + write!(f, "Pragma {}: {}, {}", self.span, self.name, value) + } else { + write!(f, "Pragma {}: {}", self.span, self.name) + } + } +} + +#[derive(Clone, Debug)] +pub struct CompoundStmt { + pub span: Span, + pub statements: List, +} + +impl Display for CompoundStmt { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + let mut indent = set_indentation(indented(f), 0); + write!(indent, "CompoundStmt {}:", self.span)?; + for stmt in &self.statements { + write!(indent, "\n{stmt}")?; + } + Ok(()) + } +} + +#[derive(Clone, Debug)] +pub struct IncludeStmt { + pub span: Span, + pub filename: String, +} + +impl Display for IncludeStmt { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + write!(f, "IncludeStmt {}: {}", self.span, self.filename) + } +} + +#[derive(Clone, Debug)] +pub struct QubitDeclaration { + pub span: Span, + pub qubit: Identifier, + pub size: Option, +} + +impl Display for QubitDeclaration { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + if let Some(size) = &self.size { + write!( + f, + "QubitDeclaration {}: {}, {}", + self.span, self.qubit, size + ) + } else { + write!(f, "QubitDeclaration {}: {}", self.span, self.qubit) + } + } +} + +#[derive(Clone, Debug)] +pub struct QuantumGateDefinition { + pub span: Span, + pub name: Identifier, + pub arguments: Vec, + pub qubits: Vec, + pub body: Vec, +} + +impl Display for QuantumGateDefinition { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + let mut indent = set_indentation(indented(f), 0); + write!(indent, "QuantumGateDefinition {}: {}", self.span, self.name)?; + for arg in &self.arguments { + write!(indent, "\n{arg}")?; + } + for qubit in &self.qubits { + write!(indent, "\n{qubit}")?; + } + for stmt in &self.body { + write!(indent, "\n{stmt}")?; + } + Ok(()) + } +} + +#[derive(Clone, Debug)] +pub struct ExternDecl { + pub span: Span, + pub name: Identifier, + pub arguments: List, + pub return_type: Option, +} + +impl Display for ExternDecl { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + let mut indent = set_indentation(indented(f), 0); + write!(indent, "ExternDecl {}: {}", self.span, self.name)?; + for arg in &self.arguments { + write!(indent, "\n{arg}")?; + } + if let Some(return_type) = &self.return_type { + write!(indent, "\n{return_type}")?; + } + Ok(()) + } +} + +#[derive(Clone, Debug)] +pub struct QuantumStmt { + pub span: Span, + pub kind: QuantumStmtKind, +} + +impl Display for QuantumStmt { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + write!(f, "QuantumStmt {}: {}", self.span, self.kind) + } +} + +#[derive(Clone, Debug)] +pub enum QuantumStmtKind { + Gate(QuantumGate), + Phase(QuantumPhase), + Barrier(List), + Reset(List>), + DelayInstruction(DelayInstruction), + Box(BoxStmt), +} + +impl Display for QuantumStmtKind { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + match self { + QuantumStmtKind::Gate(gate) => write!(f, "QuantumStmtKind {gate}"), + QuantumStmtKind::Phase(phase) => write!(f, "QuantumStmtKind {phase}"), + QuantumStmtKind::Barrier(barrier) => write!(f, "QuantumStmtKind {barrier:?}"), + QuantumStmtKind::Reset(reset) => write!(f, "QuantumStmtKind {reset:?}"), + QuantumStmtKind::DelayInstruction(delay) => write!(f, "QuantumStmtKind {delay:?}"), + QuantumStmtKind::Box(box_stmt) => write!(f, "QuantumStmtKind {box_stmt:?}"), + } + } +} + +#[derive(Clone, Debug)] +pub struct QuantumGate { + pub span: Span, + pub modifiers: List, + pub name: Identifier, + pub args: List, + pub qubits: List>, + pub duration: Option, +} + +impl Display for QuantumGate { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + let mut indent = set_indentation(indented(f), 0); + write!(indent, "QuantumGate {}: {}", self.span, self.name)?; + for arg in &self.args { + write!(indent, "\n{arg}")?; + } + for qubit in &self.qubits { + write!(indent, "\n{qubit}")?; + } + if let Some(duration) = &self.duration { + write!(indent, "\n{duration}")?; + } + Ok(()) + } +} + +#[derive(Clone, Debug)] +pub struct QuantumPhase { + pub span: Span, + pub modifiers: List, + pub arg: ExprStmt, + pub qubits: List>, +} + +impl Display for QuantumPhase { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + let mut indent = set_indentation(indented(f), 0); + write!(indent, "QuantumPhase {}: {}", self.span, self.arg)?; + for qubit in &self.qubits { + write!(indent, "\n{qubit}")?; + } + Ok(()) + } +} + +#[derive(Clone, Debug)] +pub struct DelayInstruction { + span: Span, + duration: ExprStmt, + qubits: List>, +} + +impl Display for DelayInstruction { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + let mut indent = set_indentation(indented(f), 0); + write!(indent, "DelayInstruction {}: {}", self.span, self.duration)?; + for qubit in &self.qubits { + write!(indent, "\n{qubit}")?; + } + Ok(()) + } +} + +#[derive(Clone, Debug)] +pub struct BoxStmt { + pub span: Span, + pub duration: Option, + pub body: List, +} + +impl Display for BoxStmt { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + let mut indent = set_indentation(indented(f), 0); + if let Some(duration) = &self.duration { + write!(indent, "BoxStmt {}: {}", self.span, duration)?; + } else { + write!(indent, "BoxStmt {}: ", self.span)?; + } + for stmt in &self.body { + write!(indent, "\n{stmt}")?; + } + Ok(()) + } +} + +#[derive(Clone, Debug)] +pub struct MeasureStmt { + pub span: Span, + pub measure: QuantumMeasurement, + pub target: Option>, +} + +impl Display for MeasureStmt { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + if let Some(target) = &self.target { + write!(f, "MeasureStmt {}: {}, {}", self.span, self.measure, target) + } else { + write!(f, "MeasureStmt {}: {}", self.span, self.measure) + } + } +} + +#[derive(Clone, Debug)] +pub struct ClassicalDeclarationStmt { + pub span: Span, + pub r#type: ClassicalType, + pub identifier: Identifier, + pub init_expr: Option>, +} + +impl Display for ClassicalDeclarationStmt { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + if let Some(init_expr) = &self.init_expr { + write!( + f, + "ClassicalDeclarationStmt {}: {}, {}, {}", + self.span, self.r#type, self.identifier, init_expr + ) + } else { + write!( + f, + "ClassicalDeclarationStmt {}: {}, {}", + self.span, self.r#type, self.identifier + ) + } + } +} + +#[derive(Clone, Debug)] +pub enum ValueExpression { + Expr(ExprStmt), + Measurement(QuantumMeasurement), +} + +impl Display for ValueExpression { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + match self { + ValueExpression::Expr(expr) => write!(f, "ValueExpression {expr}"), + ValueExpression::Measurement(measure) => write!(f, "ValueExpression {measure}"), + } + } +} + +#[derive(Clone, Debug)] +pub struct IODeclaration { + pub span: Span, + pub io_identifier: IOKeyword, + pub r#type: ClassicalType, + pub identifier: Identifier, +} + +impl Display for IODeclaration { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + write!( + f, + "IODeclaration {}: {}, {}, {}", + self.span, self.io_identifier, self.r#type, self.identifier + ) + } +} + +#[derive(Clone, Debug)] +pub struct ConstantDeclaration { + span: Span, + r#type: ClassicalType, + identifier: Identifier, + init_expr: ExprStmt, +} + +impl Display for ConstantDeclaration { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + write!( + f, + "ConstantDeclaration {}: {}, {}, {}", + self.span, self.r#type, self.identifier, self.init_expr + ) + } +} + +#[derive(Clone, Debug)] +pub struct CalibrationGrammarDeclaration { + span: Span, + name: String, +} + +impl Display for CalibrationGrammarDeclaration { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + write!( + f, + "CalibrationGrammarDeclaration {}: {}", + self.span, self.name + ) + } +} + +#[derive(Clone, Debug)] +pub struct CalibrationStmt { + span: Span, + body: String, +} + +impl Display for CalibrationStmt { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + write!(f, "CalibrationStmt {}: {}", self.span, self.body) + } +} + +#[derive(Clone, Debug)] +pub struct CalibrationDefinition { + span: Span, + name: Identifier, + args: List, + qubits: List, + return_type: Option, + body: String, +} + +impl Display for CalibrationDefinition { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + let mut indent = set_indentation(indented(f), 0); + write!(indent, "CalibrationDefinition {}: {}", self.span, self.name)?; + for arg in &self.args { + write!(indent, "\n{arg}")?; + } + for qubit in &self.qubits { + write!(indent, "\n{qubit}")?; + } + if let Some(return_type) = &self.return_type { + write!(indent, "\n{return_type}")?; + } + write!(indent, "\n{}", self.body) + } +} + +#[derive(Clone, Debug)] +pub enum CalibrationArgument { + Classical(ClassicalArgument), + Expr(ExprStmt), +} + +impl Display for CalibrationArgument { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + match self { + CalibrationArgument::Classical(arg) => write!(f, "CalibrationArgument {arg}"), + CalibrationArgument::Expr(expr) => write!(f, "CalibrationArgument {expr}"), + } + } +} + +#[derive(Clone, Debug)] +pub struct DefStmt { + span: Span, + name: Identifier, + args: List>, + body: List>, + return_type: Option, +} + +impl Display for DefStmt { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + let mut indent = set_indentation(indented(f), 0); + write!(indent, "DefStmt {}: {}", self.span, self.name)?; + for arg in &self.args { + write!(indent, "\n{arg}")?; + } + for stmt in &self.body { + write!(indent, "\n{stmt}")?; + } + if let Some(return_type) = &self.return_type { + write!(indent, "\n{return_type}")?; + } + Ok(()) + } +} + +#[derive(Clone, Debug)] +pub enum Operand { + Classical(ClassicalArgument), + Quantum(QuantumArgument), +} + +impl Display for Operand { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + match self { + Operand::Classical(arg) => write!(f, "Operand {arg}"), + Operand::Quantum(arg) => write!(f, "Operand {arg}"), + } + } +} + +#[derive(Clone, Debug)] +pub struct ReturnStmt { + span: Span, + expr: Option>, +} + +impl Display for ReturnStmt { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + if let Some(expr) = &self.expr { + write!(f, "ReturnStmt {}: {}", self.span, expr) + } else { + write!(f, "ReturnStmt {}: ", self.span) + } + } +} + +#[derive(Clone, Debug)] +pub struct BranchingStmt { + span: Span, + condition: ExprStmt, + if_block: List, + else_block: List, +} + +impl Display for BranchingStmt { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + let mut indent = set_indentation(indented(f), 0); + write!(indent, "BranchingStmt {}: {}", self.span, self.condition)?; + for stmt in &self.if_block { + write!(indent, "\n{stmt}")?; + } + for stmt in &self.else_block { + write!(indent, "\n{stmt}")?; + } + Ok(()) + } +} + +#[derive(Clone, Debug)] +pub struct WhileLoop { + span: Span, + while_condition: ExprStmt, + block: List, +} + +impl Display for WhileLoop { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + let mut indent = set_indentation(indented(f), 0); + write!(indent, "WhileLoop {}: {}", self.span, self.while_condition)?; + for stmt in &self.block { + write!(indent, "\n{stmt}")?; + } + Ok(()) + } +} + +#[derive(Clone, Debug)] +pub struct ForStmt { + span: Span, + r#type: ClassicalType, + identifier: Identifier, + set_declaration: Box, + block: List, +} + +impl Display for ForStmt { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + let mut indent = set_indentation(indented(f), 0); + write!( + indent, + "ForStmt {}: {}, {}, {}", + self.span, self.r#type, self.identifier, self.set_declaration + )?; + for stmt in &self.block { + write!(indent, "\n{stmt}")?; + } + Ok(()) + } +} + +#[derive(Clone, Debug)] +pub enum EnumerableSet { + DiscreteSet(DiscreteSet), + RangeDefinition(RangeDefinition), + Expr(ExprStmt), +} + +impl Display for EnumerableSet { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + match self { + EnumerableSet::DiscreteSet(set) => write!(f, "{set}"), + EnumerableSet::RangeDefinition(range) => { + let indent = set_indentation(indented(f), 0); + display_range(indent, range) + } + EnumerableSet::Expr(expr) => write!(f, "{expr}"), + } + } +} + +#[derive(Clone, Debug)] +pub struct SwitchStmt { + pub span: Span, + pub target: ExprStmt, + pub cases: List<(List, CompoundStmt)>, + /// Note that `None` is quite different to `[]` in this case; the latter is + /// an explicitly empty body, whereas the absence of a default might mean + /// that the switch is inexhaustive, and a linter might want to complain. + pub default: Option, +} + +impl Display for SwitchStmt { + fn fmt(&self, _f: &mut Formatter<'_>) -> fmt::Result { + todo!("SwitchStmt display"); + } +} + +#[derive(Clone, Debug)] +pub struct ClassicalAssignment { + pub span: Span, + pub lvalue: Identifier, + pub op: AssignmentOp, +} + +impl Display for ClassicalAssignment { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + write!( + f, + "ClassicalAssignment {}: {}, {}", + self.span, self.lvalue, self.op + ) + } +} + +#[derive(Clone, Debug, Default)] +pub enum ExprKind { + /// An expression with invalid syntax that can't be parsed. + #[default] + Err, + Ident(Ident), + UnaryExpr(UnaryExpr), + BinaryExpr(BinaryExpr), + Literal(Lit), + FunctionCall(FunctionCall), + Cast(Cast), + Concatenation(Concatenation), + IndexExpr(IndexExpr), +} + +impl Display for ExprKind { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + let indent = set_indentation(indented(f), 0); + match self { + ExprKind::Err => write!(f, "Err"), + ExprKind::Ident(id) => write!(f, "Ident {id}"), + ExprKind::UnaryExpr(expr) => write!(f, "UnaryExpr {expr}"), + ExprKind::BinaryExpr(expr) => display_bin_op(indent, expr), + ExprKind::Literal(lit) => write!(f, "Literal {lit}"), + ExprKind::FunctionCall(call) => write!(f, "FunctionCall {call}"), + ExprKind::Cast(cast) => write!(f, "Cast {cast}"), + ExprKind::Concatenation(concat) => write!(f, "Concatenation {concat}"), + ExprKind::IndexExpr(index) => write!(f, "IndexExpr {index}"), + } + } +} + +#[derive(Clone, Debug)] +pub struct UnaryExpr { + pub span: Span, + pub op: UnaryOp, + pub expr: Box, +} + +impl Display for UnaryExpr { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + let indent = set_indentation(indented(f), 0); + display_un_op(indent, self.op, &self.expr) + } +} + +#[derive(Clone, Debug)] +pub struct BinaryExpr { + pub span: Span, + pub op: BinaryOp, + pub lhs: ExprStmt, + pub rhs: ExprStmt, +} + +impl Display for BinaryExpr { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + let indent = set_indentation(indented(f), 0); + display_bin_op(indent, self) + } +} + +#[derive(Clone, Debug)] +pub struct FunctionCall { + pub span: Span, + pub name: Identifier, + pub args: List, +} + +impl Display for FunctionCall { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + let mut indent = set_indentation(indented(f), 0); + write!(indent, "FunctionCall {}: {}", self.span, self.name)?; + indent = set_indentation(indent, 1); + for arg in &self.args { + write!(indent, "\n{arg}")?; + } + Ok(()) + } +} + +#[derive(Clone, Debug)] +pub struct Cast { + pub span: Span, + pub r#type: ClassicalType, + pub arg: ExprStmt, +} + +impl Display for Cast { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + write!(f, "Cast {}: {}, {}", self.span, self.r#type, self.arg) + } +} + +#[derive(Clone, Debug)] +pub struct IndexExpr { + pub span: Span, + pub collection: ExprStmt, + pub index: IndexElement, +} + +impl Display for IndexExpr { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + write!( + f, + "IndexExpr {}: {}, {}", + self.span, self.collection, self.index + ) + } +} + +#[derive(Clone, Copy, Debug)] +pub enum UnaryOp { + NegB, + NegL, + NegN, +} + +impl Display for UnaryOp { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + match self { + UnaryOp::NegB => write!(f, "NegB"), + UnaryOp::NegL => write!(f, "NegL"), + UnaryOp::NegN => write!(f, "NegN"), + } + } +} + +#[derive(Clone, Copy, Debug)] +pub enum BinaryOp { + /// `>` + Gt, + /// `<` + Lt, + /// `>=` + Gte, + /// `<=` + Lte, + /// `==` + Eq, + /// `!=` + Neq, + /// `&&` + AndL, + /// `||` + OrL, + /// `|` + OrB, + /// `^` + XorB, + /// `&` + AndB, + /// `<<` + ShL, + /// `>>` + ShR, + /// `+` + Add, + /// `-` + Sub, + /// `*` + Mul, + /// `/` + Div, + /// `%` + Mod, + /// `**` + Exp, +} + +#[derive(Clone, Debug)] +pub struct Lit { + pub span: Span, + pub kind: LiteralKind, +} + +impl Display for Lit { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + write!(f, "Lit {}: {}", self.span, self.kind) + } +} + +#[derive(Clone, Debug)] +pub enum LiteralKind { + Array(List), + Bitstring { value: usize, width: u32 }, + Boolean(bool), + Duration { value: f64, unit: TimeUnit }, + Float(f64), + Imaginary(f64), + Integer(i64), + String(Rc), +} + +impl Display for LiteralKind { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + match self { + LiteralKind::Array(exprs) => { + let mut indent = set_indentation(indented(f), 0); + write!(indent, "Array:")?; + indent = set_indentation(indent, 1); + for expr in exprs { + write!(indent, "\n{expr}")?; + } + Ok(()) + } + LiteralKind::Bitstring { value, width } => { + write!(f, "Bitstring: {value} (width {width})") + } + LiteralKind::Boolean(b) => write!(f, "Boolean: {b}"), + LiteralKind::Duration { value, unit } => { + write!(f, "Duration: {value} {unit}") + } + LiteralKind::Float(value) => write!(f, "Float: {value}"), + LiteralKind::Imaginary(value) => write!(f, "Imaginary: {value} im"), + LiteralKind::Integer(i) => write!(f, "Integer: {i}"), + LiteralKind::String(s) => write!(f, "String: {s}"), + } + } +} + +#[derive(Debug, Clone, Copy, PartialEq)] +pub struct Version { + pub major: u32, + pub minor: Option, + pub span: Span, +} + +impl fmt::Display for Version { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self.minor { + Some(minor) => write!(f, "{}.{}", self.major, minor), + None => write!(f, "{}", self.major), + } + } +} + +#[derive(Clone, Debug)] +pub struct Concatenation { + lhs: ExprStmt, + rhs: ExprStmt, +} + +impl Display for Concatenation { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + let mut indent = set_indentation(indented(f), 0); + write!(indent, "Concatenation:")?; + indent = set_indentation(indent, 1); + write!(indent, "\n{}", self.lhs)?; + write!(indent, "\n{}", self.rhs)?; + Ok(()) + } +} + +#[derive(Clone, Debug)] +pub enum IndexElement { + DiscreteSet(DiscreteSet), + IndexSet(List), +} + +impl Display for IndexElement { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + match self { + IndexElement::DiscreteSet(set) => write!(f, "IndexElement {set}"), + IndexElement::IndexSet(items) => { + let mut indent = set_indentation(indented(f), 0); + write!(indent, "IndexElement:")?; + indent = set_indentation(indent, 1); + for item in items { + write!(indent, "\n{item}")?; + } + Ok(()) + } + } + } +} + +#[derive(Clone, Debug)] +pub enum IndexSetItem { + RangeDefinition(RangeDefinition), + Expr(ExprStmt), +} + +impl Display for IndexSetItem { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + let indent = set_indentation(indented(f), 0); + match self { + IndexSetItem::RangeDefinition(range) => display_range(indent, range), + IndexSetItem::Expr(expr) => write!(f, "IndexSetItem {expr}"), + } + } +} + +#[derive(Clone, Debug)] +pub enum AssignmentOp { + BinaryOp(BinaryOp), + /// `OpenQASM3` has the `~=` assignment operator. + /// This enum variant is meant to capture that. + UnaryOp(UnaryOp), + Assign, +} + +impl Display for AssignmentOp { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + match self { + AssignmentOp::BinaryOp(op) => write!(f, "AssignmentOp ({op:?})"), + AssignmentOp::UnaryOp(op) => write!(f, "AssignmentOp ({op:?})"), + AssignmentOp::Assign => write!(f, "AssignmentOp (Assign)"), + } + } +} + +#[derive(Clone, Debug)] +pub enum GateModifierName { + Inv, + Pow, + Ctrl, + NegCtrl, +} + +impl Display for GateModifierName { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + match self { + GateModifierName::Inv => write!(f, "inv"), + GateModifierName::Pow => write!(f, "pow"), + GateModifierName::Ctrl => write!(f, "ctrl"), + GateModifierName::NegCtrl => write!(f, "negctrl"), + } + } +} + +#[derive(Clone, Debug)] +pub enum IOKeyword { + Input, + Output, +} + +impl Display for IOKeyword { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + match self { + IOKeyword::Input => write!(f, "input"), + IOKeyword::Output => write!(f, "output"), + } + } +} + +#[derive(Clone, Debug)] +pub enum TimeUnit { + Dt, + /// Nanoseconds. + Ns, + /// Microseconds. + Us, + /// Milliseconds. + Ms, + /// Seconds. + S, +} + +impl Display for TimeUnit { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + match self { + TimeUnit::Dt => write!(f, "dt"), + TimeUnit::Ns => write!(f, "ns"), + TimeUnit::Us => write!(f, "us"), + TimeUnit::Ms => write!(f, "ms"), + TimeUnit::S => write!(f, "s"), + } + } +} + +#[derive(Clone, Debug)] +pub struct BreakStmt { + pub span: Span, +} + +impl Display for BreakStmt { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + write!(f, "Break {}", self.span) + } +} + +#[derive(Clone, Debug)] +pub struct ContinueStmt { + pub span: Span, +} + +impl Display for ContinueStmt { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + write!(f, "Continue {}", self.span) + } +} + +fn display_assign(mut indent: Indented, lhs: &Expr, rhs: &Expr) -> fmt::Result { + write!(indent, "Assign:")?; + indent = set_indentation(indent, 1); + write!(indent, "\n{lhs}")?; + write!(indent, "\n{rhs}")?; + Ok(()) +} + +fn display_assign_op( + mut indent: Indented, + op: BinaryOp, + lhs: &Expr, + rhs: &Expr, +) -> fmt::Result { + write!(indent, "AssignOp ({op:?}):")?; + indent = set_indentation(indent, 1); + write!(indent, "\n{lhs}")?; + write!(indent, "\n{rhs}")?; + Ok(()) +} + +fn display_bin_op(mut indent: Indented, expr: &BinaryExpr) -> fmt::Result { + write!(indent, "BinOp ({:?}):", expr.op)?; + indent = set_indentation(indent, 1); + write!(indent, "\n{}", expr.lhs)?; + write!(indent, "\n{}", expr.rhs)?; + Ok(()) +} + +fn display_un_op(mut indent: Indented, op: UnaryOp, expr: &Expr) -> fmt::Result { + write!(indent, "UnOp ({op}):")?; + indent = set_indentation(indent, 1); + write!(indent, "\n{expr}")?; + Ok(()) +} + +fn display_while(mut indent: Indented, cond: &Expr, block: &Block) -> fmt::Result { + write!(indent, "While:")?; + indent = set_indentation(indent, 1); + write!(indent, "\n{cond}")?; + write!(indent, "\n{block}")?; + Ok(()) +} + +fn display_range(mut indent: Indented, range: &RangeDefinition) -> fmt::Result { + write!(indent, "Range: {}", range.span)?; + indent = set_indentation(indent, 1); + match &range.start { + Some(e) => write!(indent, "\n{e}")?, + None => write!(indent, "\n")?, + } + match &range.step { + Some(e) => write!(indent, "\n{e}")?, + None => write!(indent, "\n")?, + } + match &range.end { + Some(e) => write!(indent, "\n{e}")?, + None => write!(indent, "\n")?, + } + Ok(()) +} diff --git a/compiler/qsc_qasm3/src/keyword.rs b/compiler/qsc_qasm3/src/keyword.rs index dd30c8bd02..86f520b5c3 100644 --- a/compiler/qsc_qasm3/src/keyword.rs +++ b/compiler/qsc_qasm3/src/keyword.rs @@ -9,54 +9,98 @@ use std::{ #[derive(Clone, Copy, Debug, Eq, Hash, PartialEq, Sequence)] pub enum Keyword { + Array, + Barrier, Box, Break, Cal, Case, + Const, Continue, + CReg, + Ctrl, Def, + DefCal, + DefCalGrammar, Default, - Defcalgrammar, + Delay, Else, End, Extern, + False, For, Gate, + GPhase, If, In, Include, + Input, + Inv, Let, + Measure, + Mutable, + NegCtrl, OpenQASM, + Output, + Pow, Pragma, + QReg, + Qubit, + Reset, + True, + ReadOnly, Return, Switch, + Void, While, } impl Keyword { pub(super) fn as_str(self) -> &'static str { match self { + Keyword::Array => "array", + Keyword::Barrier => "barrier", Keyword::Box => "box", Keyword::Break => "break", Keyword::Cal => "cal", Keyword::Case => "case", + Keyword::Const => "const", Keyword::Continue => "continue", + Keyword::CReg => "creg", + Keyword::Ctrl => "ctrl", Keyword::Def => "def", + Keyword::DefCal => "defcal", + Keyword::DefCalGrammar => "defcalgrammar", Keyword::Default => "default", - Keyword::Defcalgrammar => "defcalgrammar", + Keyword::Delay => "delay", Keyword::Else => "else", Keyword::End => "end", Keyword::Extern => "extern", + Keyword::False => "false", Keyword::For => "for", Keyword::Gate => "gate", + Keyword::GPhase => "gphase", Keyword::If => "if", Keyword::In => "in", Keyword::Include => "include", + Keyword::Input => "input", + Keyword::Inv => "inv", Keyword::Let => "let", - Keyword::OpenQASM => "openqasm", + Keyword::Measure => "measure", + Keyword::Mutable => "mutable", + Keyword::NegCtrl => "negctrl", + Keyword::OpenQASM => "OPENQASM", + Keyword::Output => "output", + Keyword::Pow => "pow", Keyword::Pragma => "pragma", + Keyword::QReg => "qreg", + Keyword::Qubit => "qubit", + Keyword::Reset => "reset", + Keyword::True => "true", + Keyword::ReadOnly => "readonly", Keyword::Return => "return", Keyword::Switch => "switch", + Keyword::Void => "void", Keyword::While => "while", } } @@ -76,27 +120,49 @@ impl FromStr for Keyword { // frequency in Q# so that fewer comparisons are needed on average. fn from_str(s: &str) -> Result { match s { + "array" => Ok(Self::Array), + "barrier" => Ok(Self::Barrier), "box" => Ok(Self::Box), "break" => Ok(Self::Break), "cal" => Ok(Self::Cal), "case" => Ok(Self::Case), + "const" => Ok(Self::Const), "continue" => Ok(Self::Continue), + "creg" => Ok(Self::CReg), + "ctrl" => Ok(Self::Ctrl), "def" => Ok(Self::Def), + "defcal" => Ok(Self::DefCal), + "defcalgrammar" => Ok(Self::DefCalGrammar), "default" => Ok(Self::Default), - "defcalgrammar" => Ok(Self::Defcalgrammar), + "delay" => Ok(Self::Delay), "else" => Ok(Self::Else), "end" => Ok(Self::End), "extern" => Ok(Self::Extern), + "false" => Ok(Self::False), "for" => Ok(Self::For), "gate" => Ok(Self::Gate), + "gphase" => Ok(Self::GPhase), "if" => Ok(Self::If), "in" => Ok(Self::In), "include" => Ok(Self::Include), + "input" => Ok(Self::Input), + "inv" => Ok(Self::Inv), "let" => Ok(Self::Let), - "openqasm" => Ok(Self::OpenQASM), + "measure" => Ok(Self::Measure), + "mutable" => Ok(Self::Mutable), + "negctrl" => Ok(Self::NegCtrl), + "OPENQASM" => Ok(Self::OpenQASM), + "output" => Ok(Self::Output), + "pow" => Ok(Self::Pow), "pragma" => Ok(Self::Pragma), + "qreg" => Ok(Self::QReg), + "qubit" => Ok(Self::Qubit), + "reset" => Ok(Self::Reset), + "true" => Ok(Self::True), + "readonly" => Ok(Self::ReadOnly), "return" => Ok(Self::Return), "switch" => Ok(Self::Switch), + "void" => Ok(Self::Void), "while" => Ok(Self::While), _ => Err(()), } diff --git a/compiler/qsc_qasm3/src/lex.rs b/compiler/qsc_qasm3/src/lex.rs index 34683c5259..16257a1ea0 100644 --- a/compiler/qsc_qasm3/src/lex.rs +++ b/compiler/qsc_qasm3/src/lex.rs @@ -6,7 +6,7 @@ pub mod cooked; pub mod raw; use enum_iterator::Sequence; -pub(super) use cooked::{Error, Lexer, Token, TokenKind}; +pub(super) use cooked::{ClosedBinOp, Error, Lexer, Token, TokenKind}; /// A delimiter token. #[derive(Clone, Copy, Debug, Eq, PartialEq, Sequence)] diff --git a/compiler/qsc_qasm3/src/lex/cooked.rs b/compiler/qsc_qasm3/src/lex/cooked.rs index faebc1bb28..afc1bbb62d 100644 --- a/compiler/qsc_qasm3/src/lex/cooked.rs +++ b/compiler/qsc_qasm3/src/lex/cooked.rs @@ -96,7 +96,6 @@ pub enum TokenKind { Delay, Reset, Measure, - Barrier, Literal(Literal), @@ -119,6 +118,7 @@ pub enum TokenKind { PlusPlus, /// `->` Arrow, + At, // Operators, ClosedBinOp(ClosedBinOp), @@ -133,6 +133,8 @@ pub enum TokenKind { Identifier, HardwareQubit, + /// End of file. + Eof, } impl Display for TokenKind { @@ -151,7 +153,6 @@ impl Display for TokenKind { TokenKind::Delay => write!(f, "delay"), TokenKind::Reset => write!(f, "reset"), TokenKind::Measure => write!(f, "measure"), - TokenKind::Barrier => write!(f, "barrier"), TokenKind::Literal(literal) => write!(f, "literal `{literal}`"), TokenKind::Open(Delim::Brace) => write!(f, "`{{`"), TokenKind::Open(Delim::Bracket) => write!(f, "`[`"), @@ -165,6 +166,7 @@ impl Display for TokenKind { TokenKind::Comma => write!(f, "`,`"), TokenKind::PlusPlus => write!(f, "`++`"), TokenKind::Arrow => write!(f, "`->`"), + TokenKind::At => write!(f, "`@`"), TokenKind::ClosedBinOp(op) => write!(f, "`{op}`"), TokenKind::BinOpEq(op) => write!(f, "`{op}=`"), TokenKind::ComparisonOp(op) => write!(f, "`{op}`"), @@ -173,6 +175,7 @@ impl Display for TokenKind { TokenKind::Tilde => write!(f, "`~`"), TokenKind::Identifier => write!(f, "identifier"), TokenKind::HardwareQubit => write!(f, "hardware bit"), + TokenKind::Eof => f.write_str("EOF"), } } } @@ -562,15 +565,7 @@ impl<'a> Lexer<'a> { Ok(self.closed_bin_op(ClosedBinOp::Amp)) } } - Single::At => { - let complete = TokenKind::Annotation; - self.expect(raw::TokenKind::Ident, complete)?; - self.kleen_star( - &[raw::TokenKind::Single(Single::Dot), raw::TokenKind::Ident], - complete, - )?; - Ok(complete) - } + Single::At => Ok(TokenKind::At), Single::Bang => { if self.next_if_eq_single(Single::Eq) { Ok(TokenKind::ComparisonOp(ComparisonOp::BangEq)) @@ -664,7 +659,6 @@ impl<'a> Lexer<'a> { "delay" => TokenKind::Delay, "reset" => TokenKind::Reset, "measure" => TokenKind::Measure, - "barrier" => TokenKind::Barrier, "false" | "true" => TokenKind::Literal(Literal::Boolean), ident => { if let Ok(keyword) = ident.parse::() { diff --git a/compiler/qsc_qasm3/src/lex/cooked/tests.rs b/compiler/qsc_qasm3/src/lex/cooked/tests.rs index 7002c957b7..66d0f0c9c1 100644 --- a/compiler/qsc_qasm3/src/lex/cooked/tests.rs +++ b/compiler/qsc_qasm3/src/lex/cooked/tests.rs @@ -38,7 +38,6 @@ fn op_string(kind: TokenKind) -> Option { TokenKind::Delay => Some("delay".to_string()), TokenKind::Reset => Some("reset".to_string()), TokenKind::Measure => Some("measure".to_string()), - TokenKind::Barrier => Some("barrier".to_string()), TokenKind::Semicolon => Some(";".to_string()), TokenKind::Arrow => Some("->".to_string()), TokenKind::ClosedBinOp(op) => Some(op.to_string()), @@ -49,10 +48,13 @@ fn op_string(kind: TokenKind) -> Option { TokenKind::ComparisonOp(op) => Some(op.to_string()), TokenKind::Identifier => Some("foo".to_string()), TokenKind::HardwareQubit => Some("$1".to_string()), + TokenKind::At => Some("@".to_string()), + TokenKind::Eof => Some("EOF".to_string()), } } #[test] +#[ignore = "Need to talk through how to handle this"] fn basic_ops() { for kind in enum_iterator::all() { let Some(input) = op_string(kind) else { diff --git a/compiler/qsc_qasm3/src/lib.rs b/compiler/qsc_qasm3/src/lib.rs index 5415533de3..362a969a76 100644 --- a/compiler/qsc_qasm3/src/lib.rs +++ b/compiler/qsc_qasm3/src/lib.rs @@ -2,6 +2,7 @@ // Licensed under the MIT License. mod angle; +mod ast; mod ast_builder; mod compile; pub use compile::qasm_to_program; @@ -11,6 +12,7 @@ mod lex; mod oqasm_helpers; mod oqasm_types; pub mod parse; +pub mod parser; mod runtime; mod symbols; mod types; diff --git a/compiler/qsc_qasm3/src/parser.rs b/compiler/qsc_qasm3/src/parser.rs new file mode 100644 index 0000000000..9c74e68e40 --- /dev/null +++ b/compiler/qsc_qasm3/src/parser.rs @@ -0,0 +1,265 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +use crate::ast::{Program, StmtKind}; +use crate::io::SourceResolver; +use qsc_frontend::compile::SourceMap; +use qsc_frontend::error::WithSource; +use scan::ParserContext; +use std::path::{Path, PathBuf}; +use std::sync::Arc; + +#[cfg(test)] +pub(crate) mod tests; + +mod completion; +mod error; +mod expr; +mod prgm; +mod prim; +mod scan; +mod stmt; + +pub struct QasmParseResult { + pub source: QasmSource, + pub source_map: SourceMap, +} + +impl QasmParseResult { + #[must_use] + pub fn new(source: QasmSource) -> QasmParseResult { + let source_map = create_source_map(&source); + QasmParseResult { source, source_map } + } + + #[must_use] + pub fn has_errors(&self) -> bool { + self.source.has_errors() + } + + pub fn all_errors(&self) -> Vec> { + let mut self_errors = self.errors(); + let include_errors = self + .source + .includes() + .iter() + .flat_map(QasmSource::all_errors) + .map(|e| self.map_error(e)); + + self_errors.extend(include_errors); + self_errors + } + + #[must_use] + pub fn errors(&self) -> Vec> { + self.source + .errors() + .iter() + .map(|e| self.map_error(e.clone())) + .collect() + } + + fn map_error( + &self, + error: crate::parser::error::Error, + ) -> WithSource { + let path = self.source.path().display().to_string(); + let source = self.source_map.find_by_name(&path); + let offset = source.map_or(0, |source| source.offset); + + let offset_error = error.with_offset(offset); + + WithSource::from_map(&self.source_map, offset_error) + } +} + +/// Parse a QASM file and return the parse result. +/// This function will resolve includes using the provided resolver. +/// If an include file cannot be resolved, an error will be returned. +/// If a file is included recursively, a stack overflow occurs. +pub fn parse_source(source: S, path: P, resolver: &R) -> miette::Result +where + S: AsRef, + P: AsRef, + R: SourceResolver, +{ + let res = parse_qasm_source(source, path, resolver)?; + Ok(QasmParseResult::new(res)) +} + +/// Creates a Q# source map from a QASM parse output. The `QasmSource` +/// has all of the recursive includes resolved with their own source +/// and parse results. +fn create_source_map(source: &QasmSource) -> SourceMap { + let mut files: Vec<(Arc, Arc)> = Vec::new(); + files.push(( + Arc::from(source.path().to_string_lossy().to_string()), + Arc::from(source.source()), + )); + // Collect all source files from the includes, this + // begins the recursive process of collecting all source files. + for include in source.includes() { + collect_source_files(include, &mut files); + } + // Map the main source file to the entry point expression + // This may be incorrect, but it's the best we can do for now. + SourceMap::new(files, Some(Arc::from(source.source()))) +} + +/// Recursively collect all source files from the includes +fn collect_source_files(source: &QasmSource, files: &mut Vec<(Arc, Arc)>) { + files.push(( + Arc::from(source.path().to_string_lossy().to_string()), + Arc::from(source.source()), + )); + for include in source.includes() { + collect_source_files(include, files); + } +} + +/// Represents a QASM source file that has been parsed. +#[derive(Clone, Debug)] +pub struct QasmSource { + /// The path to the source file. This is used for error reporting. + /// This path is just a name, it does not have to exist on disk. + path: PathBuf, + /// The source code of the file. + source: Arc, + /// The parsed AST of the source file or any parse errors. + program: Program, + /// Any parse errors that occurred. + errors: Vec, + /// Any included files that were resolved. + /// Note that this is a recursive structure. + included: Vec, +} + +impl QasmSource { + pub fn new, P: AsRef>( + source: T, + file_path: P, + program: Program, + errors: Vec, + included: Vec, + ) -> QasmSource { + QasmSource { + path: file_path.as_ref().to_owned(), + source: source.as_ref().into(), + program, + errors, + included, + } + } + + #[must_use] + pub fn has_errors(&self) -> bool { + if !self.errors().is_empty() { + return true; + } + self.includes().iter().any(QasmSource::has_errors) + } + + #[must_use] + pub fn all_errors(&self) -> Vec { + let mut self_errors = self.errors(); + let include_errors = self.includes().iter().flat_map(QasmSource::all_errors); + self_errors.extend(include_errors); + self_errors + } + + #[must_use] + pub fn includes(&self) -> &Vec { + self.included.as_ref() + } + + #[must_use] + pub fn program(&self) -> &Program { + &self.program + } + + #[must_use] + pub fn path(&self) -> PathBuf { + self.path.clone() + } + + #[must_use] + pub fn errors(&self) -> Vec { + self.errors.clone() + } + + #[must_use] + pub fn source(&self) -> &str { + self.source.as_ref() + } +} + +/// Parse a QASM file and return the parse result using the provided resolver. +/// Returns `Err` if the resolver cannot resolve the file. +/// Returns `Ok` otherwise. Any parse errors will be included in the result. +/// +/// This function is the start of a recursive process that will resolve all +/// includes in the QASM file. Any includes are parsed as if their contents +/// were defined where the include statement is. +fn parse_qasm_file(path: P, resolver: &R) -> miette::Result +where + P: AsRef, + R: SourceResolver, +{ + let (path, source) = resolver.resolve(&path)?; + parse_qasm_source(source, path, resolver) +} + +fn parse_qasm_source(source: S, path: P, resolver: &R) -> miette::Result +where + S: AsRef, + P: AsRef, + R: SourceResolver, +{ + let (program, errors, includes) = parse_source_and_includes(source.as_ref(), resolver)?; + Ok(QasmSource::new(source, path, program, errors, includes)) +} + +fn parse_source_and_includes, R>( + source: P, + resolver: &R, +) -> miette::Result<(Program, Vec, Vec)> +where + R: SourceResolver, +{ + let (program, errors) = parse(source.as_ref())?; + let included = parse_includes(&program, resolver)?; + Ok((program, errors, included)) +} + +fn parse_includes(program: &crate::ast::Program, resolver: &R) -> miette::Result> +where + R: SourceResolver, +{ + let mut includes = vec![]; + for stmt in &program.statements { + if let StmtKind::Include(include) = stmt.kind.as_ref() { + let file_path = &include.filename; + // Skip the standard gates include file. + // Handling of this file is done by the compiler. + if file_path.to_lowercase() == "stdgates.inc" { + continue; + } + let source = parse_qasm_file(file_path, resolver)?; + includes.push(source); + } + } + + Ok(includes) +} + +pub(crate) type Result = std::result::Result; + +pub(crate) trait Parser: FnMut(&mut ParserContext) -> Result {} + +impl Result> Parser for F {} + +pub fn parse(input: &str) -> Result<(Program, Vec)> { + let mut scanner = ParserContext::new(input); + let program = prgm::parse(&mut scanner)?; + Ok((program, scanner.into_errors())) +} diff --git a/compiler/qsc_qasm3/src/parser/completion.rs b/compiler/qsc_qasm3/src/parser/completion.rs new file mode 100644 index 0000000000..17b37d8507 --- /dev/null +++ b/compiler/qsc_qasm3/src/parser/completion.rs @@ -0,0 +1,39 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +// while we work through the conversion, allow dead code to avoid warnings +#![allow(dead_code)] + +pub(crate) mod collector; +#[cfg(test)] +mod tests; +pub(crate) mod word_kinds; + +pub(crate) use collector::ValidWordCollector; +pub(crate) use word_kinds::WordKinds; + +use super::{prgm, ParserContext}; + +/// Returns the words that would be valid syntax at a particular offset +/// in the given source file (using the source file parser). +/// +/// This is useful for providing completions in an editor. +#[must_use] +pub fn possible_words_at_offset_in_source(input: &str, at_offset: u32) -> WordKinds { + let mut collector = ValidWordCollector::new(at_offset); + let mut scanner = ParserContext::with_word_collector(input, &mut collector); + let _ = prgm::parse(&mut scanner); + collector.into_words() +} + +/// Returns the words that would be valid syntax at a particular offset +/// in the given notebook cell (using the fragments parser). +/// +/// This is useful for providing completions in an editor. +#[must_use] +pub fn possible_words_at_offset_in_fragments(input: &str, at_offset: u32) -> WordKinds { + let mut collector = ValidWordCollector::new(at_offset); + let mut scanner = ParserContext::with_word_collector(input, &mut collector); + let _ = prgm::parse(&mut scanner); + collector.into_words() +} diff --git a/compiler/qsc_qasm3/src/parser/completion/collector.rs b/compiler/qsc_qasm3/src/parser/completion/collector.rs new file mode 100644 index 0000000000..5339058d2f --- /dev/null +++ b/compiler/qsc_qasm3/src/parser/completion/collector.rs @@ -0,0 +1,186 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +//! The [`ValidWordCollector`] provides a mechanism to hook into the parser +//! to collect the possible valid words at a specific cursor location in the +//! code. It's meant to be used by the code completion feature in the +//! language service. +//! +//! Any time the parser is about to try parsing a word token, it records the +//! expected word(s) through a call to [`ValidWordCollector::expect()`]. +//! These are considered to be valid words for that location. +//! +//! If the parser is not at the cursor position yet, this call is ignored. +//! +//! Once the parser has reached the cursor position, the expected word(s) +//! are recorded into a list. +//! +//! At this point, the [`ValidWordCollector`] tricks the parser by +//! intercepting the lexer and returning an EOF token to the parser instead +//! of the real next token from the source. +//! +//! Since EOF will never match a token that the parser is looking for, this +//! causes the parser to keep trying all possible tokens at at this location, +//! recording the expected words in the process. Finally, it gives up. +//! +//! As soon as the parser reports a parse error at the cursor location, +//! the [`ValidWordCollector`] stops recording expected words. This +//! is to prevent the word list from getting polluted with words that are +//! expected after recovery occurs. +//! +//! For example, consider the code sample below, where `|` denotes the +//! cursor location: +//! +//! ```qsharp +//! operation Main() : Unit { let x: | +//! ``` +//! +//! When the parser gets to the cursor location, it looks for the words that are +//! applicable at a type position (paths, type parameters, etc). But it +//! keeps finding the EOF that was inserted by the [`ValidWordCollector`].As the +//! parser goes through each possible word, the word is recorded by the collector. +//! Finally, the parser gives up and reports a parse error. The parser then recovers, +//! and and starts looking for words that can start statements instead (`let`, etc). +//! These words are *not* recorded by the collector since they occur +//! after the parser has already reported an error. +//! +//! Note that returning EOF at the cursor means that the "manipulated" +//! parser will never run further than the cursor location, meaning the two +//! below code inputs are equivalent: +//! +//! ```qsharp +//! operation Foo() : | Unit {} +//! ``` +//! +//! ```qsharp +//! operation Foo() : | +//! ``` + +use super::WordKinds; +use crate::lex::{ClosedBinOp, Token, TokenKind}; +use qsc_data_structures::span::Span; + +pub(crate) struct ValidWordCollector { + cursor_offset: u32, + state: State, + collected: WordKinds, +} + +#[derive(Debug, PartialEq, Eq)] +enum State { + /// The parser has not reached the cursor location yet. + BeforeCursor, + /// The parser is at the cursor, i.e. the cursor touches the next + /// token the parser is about to consume. + /// + /// This is when we start collecting expected valid words from the parser. + AtCursor, + /// The parser has encountered an error at the cursor location. + /// Stop collecting expected valid words. + End, +} + +impl ValidWordCollector { + pub fn new(cursor_offset: u32) -> Self { + Self { + cursor_offset, + state: State::BeforeCursor, + collected: WordKinds::empty(), + } + } + + /// The parser expects the given word(s) at the next token. + pub fn expect(&mut self, expected: WordKinds) { + match self.state { + State::AtCursor => self.collected.extend(expected), + State::BeforeCursor | State::End => {} + } + } + + /// The parser has advanced. Update state. + pub fn did_advance(&mut self, next_token: &mut Token, scanner_offset: u32) { + match self.state { + State::BeforeCursor => { + if cursor_at_token(self.cursor_offset, *next_token, scanner_offset) { + self.state = State::AtCursor; + // Set the next token to be EOF. This will trick the parser into + // attempting to parse the token over and over again, + // collecting `WordKinds` in the process. + *next_token = eof(next_token.span.hi); + } + } + State::End | State::AtCursor => {} + } + } + + /// The parser reported an error. Update state. + pub fn did_error(&mut self) { + match self.state { + State::AtCursor => self.state = State::End, + State::BeforeCursor | State::End => {} + } + } + + /// Returns the collected valid words. + pub fn into_words(self) -> WordKinds { + self.collected + } +} + +/// Returns true if the cursor is at the given token. +/// +/// Cursor is considered to be at a token if it's just before +/// the token or in the middle of it. The only exception is when +/// the cursor is touching a word on the right side. In this +/// case, we want to count the cursor as being at that word. +/// +/// Touching the left side of a word: +/// def Foo(|int[64] x, int[64] y) : {} +/// - at `int` +/// +/// Touching the right side of a word: +/// `def Foo(int|[64] x, int[64] y) : {}` +/// - at `int` +/// +/// In the middle of a word: +/// `def Foo(in|t[64] x , int[64] y) : {}` +/// - at `int` +/// +/// Touching the right side of a non-word: +/// `def Foo(int[64]| x , int[64] y) : {}` +/// - at `x` +/// +/// Between a word and a non-word: +/// `def Foo(|int|[64] x , int[64] y) : {}` +/// - at `int` +/// +/// EOF: +/// `def Foo(|int[64] x , int[64] y) : {}|` +/// - at `EOF` +/// +fn cursor_at_token(cursor_offset: u32, next_token: Token, scanner_offset: u32) -> bool { + match next_token.kind { + // Order matters here as the cases overlap. + TokenKind::Identifier + | TokenKind::Keyword(_) + | TokenKind::ClosedBinOp(ClosedBinOp::AmpAmp | ClosedBinOp::BarBar) + | TokenKind::Eof => { + // next token is a word or eof, so count if cursor touches either side of the token + scanner_offset <= cursor_offset && cursor_offset <= next_token.span.hi + } + _ => { + // next token is not a word, so only count if cursor touches left side of token + scanner_offset <= cursor_offset && cursor_offset < next_token.span.hi + } + } +} + +fn eof(offset: u32) -> Token { + Token { + kind: TokenKind::Eof, + span: Span { + lo: offset, + hi: offset, + }, + } +} diff --git a/compiler/qsc_qasm3/src/parser/completion/tests.rs b/compiler/qsc_qasm3/src/parser/completion/tests.rs new file mode 100644 index 0000000000..396380748a --- /dev/null +++ b/compiler/qsc_qasm3/src/parser/completion/tests.rs @@ -0,0 +1,55 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +use crate::parser::completion::possible_words_at_offset_in_source; +use expect_test::expect; + +fn get_source_and_cursor(input: &str) -> (String, u32) { + let mut cursor = -1; + let mut source = String::new(); + for c in input.chars() { + if c == '|' { + cursor = i32::try_from(source.len()).expect("input length should fit into u32"); + } else { + source.push(c); + } + } + let cursor = u32::try_from(cursor).expect("missing cursor marker in input"); + (source, cursor) +} + +fn check_valid_words(input: &str, expect: &expect_test::Expect) { + let (input, cursor) = get_source_and_cursor(input); + let w = possible_words_at_offset_in_source(&input, cursor); + expect.assert_debug_eq(&w); +} + +fn check_valid_words_no_source_name(input: &str, expect: &expect_test::Expect) { + let (input, cursor) = get_source_and_cursor(input); + let w = possible_words_at_offset_in_source(&input, cursor); + expect.assert_debug_eq(&w); +} + +#[test] +fn begin_document() { + check_valid_words( + "|OPENQASM 3;", + &expect![[r" + WordKinds( + Annotation | Include | OpenQASM | Pragma, + ) + "]], + ); +} + +#[test] +fn end_of_version() { + check_valid_words( + "OPENQASM 3;|", + &expect![[r" + WordKinds( + Annotation | Include | Pragma, + ) + "]], + ); +} diff --git a/compiler/qsc_qasm3/src/parser/completion/word_kinds.rs b/compiler/qsc_qasm3/src/parser/completion/word_kinds.rs new file mode 100644 index 0000000000..44b441338a --- /dev/null +++ b/compiler/qsc_qasm3/src/parser/completion/word_kinds.rs @@ -0,0 +1,179 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +use crate::keyword::Keyword; +use bitflags::bitflags; +use enum_iterator::all; + +bitflags! { + /// + /// Words can be of these kinds: + /// - Names + /// - Hardcoded words: + /// - Keywords + /// - Hardcoded identifiers + /// + /// Names are identifiers or paths that can be resolved to a definition + /// in the code, e.g. callable names, type names, namespaces. + /// + /// Keywords are known words that are not allowed as identifiers, e.g. `function`, `if`. + /// + /// Hardcoded identifiers are treated as identifiers by the parser, but the + /// possible names are hardcoded into the language, e.g. "EntryPoint", "Qubit". + /// + /// IF UPDATING: If new values are added before the keyword range, + /// [`KEYWORDS_START`] *must* be updated. + /// + #[repr(transparent)] + #[derive(Default, PartialEq, Debug, Clone, Copy)] + pub struct WordKinds: u128 { + + // + // Begin names. + // + + /// A path in an expression. Callables, UDT constructors, local variables. + const PathExpr = 1 << 0; + + /// A path segment that follows a `.` + /// A more specific name kind can be inferred from a recovered AST. + const PathSegment = 1 << 1; + /// A primitive class. + const PrimitiveClass = 1 << 2; + + + // + // End names. + // + + // + // Begin hardcoded identifiers. + // + + /// An annotation, without the leading `@`. + const Annotation = 1 << 3; + + + // + // End hardcoded identifiers. + // + + // + // Begin keywords. + // + + const Array = keyword_bit(Keyword::Array); + const Barrier = keyword_bit(Keyword::Barrier); + const Box = keyword_bit(Keyword::Box); + const Break = keyword_bit(Keyword::Break); + const Cal = keyword_bit(Keyword::Cal); + const Case = keyword_bit(Keyword::Case); + const Const = keyword_bit(Keyword::Const); + const Continue = keyword_bit(Keyword::Continue); + const CReg = keyword_bit(Keyword::CReg); + const Ctrl = keyword_bit(Keyword::Ctrl); + const Def = keyword_bit(Keyword::Def); + const DefCal = keyword_bit(Keyword::DefCal); + const DefCalGrammar = keyword_bit(Keyword::DefCalGrammar); + const Default = keyword_bit(Keyword::Default); + const Delay = keyword_bit(Keyword::Delay); + const Else = keyword_bit(Keyword::Else); + const End = keyword_bit(Keyword::End); + const Extern = keyword_bit(Keyword::Extern); + const False = keyword_bit(Keyword::False); + const For = keyword_bit(Keyword::For); + const Gate = keyword_bit(Keyword::Gate); + const GPhase = keyword_bit(Keyword::GPhase); + const If = keyword_bit(Keyword::If); + const In = keyword_bit(Keyword::In); + const Include = keyword_bit(Keyword::Include); + const Input = keyword_bit(Keyword::Input); + const Inv = keyword_bit(Keyword::Inv); + const Let = keyword_bit(Keyword::Let); + const Measure = keyword_bit(Keyword::Measure); + const Mutable = keyword_bit(Keyword::Mutable); + const NegCtrl = keyword_bit(Keyword::NegCtrl); + const OpenQASM = keyword_bit(Keyword::OpenQASM); + const Output = keyword_bit(Keyword::Output); + const Pow = keyword_bit(Keyword::Pow); + const Pragma = keyword_bit(Keyword::Pragma); + const QReg = keyword_bit(Keyword::QReg); + const Qubit = keyword_bit(Keyword::Qubit); + const Reset = keyword_bit(Keyword::Reset); + const True = keyword_bit(Keyword::True); + const ReadOnly = keyword_bit(Keyword::ReadOnly); + const Return = keyword_bit(Keyword::Return); + const Switch = keyword_bit(Keyword::Switch); + const Void = keyword_bit(Keyword::Void); + const While = keyword_bit(Keyword::While); + } +} + +const KEYWORDS_START: u8 = 4; +const fn keyword_bit(k: Keyword) -> u128 { + 1 << (k as u8 + KEYWORDS_START) +} + +impl From for WordKinds { + fn from(k: Keyword) -> Self { + Self::from_bits_truncate(keyword_bit(k)) + } +} + +impl WordKinds { + /// Returns only the name kinds that this prediction set contains. + pub fn iter_name_kinds(&self) -> impl Iterator + '_ { + self.iter().filter_map(|p| match p { + WordKinds::PathExpr => Some(NameKind::Path(PathKind::Expr)), + WordKinds::PathSegment => Some(NameKind::PathSegment), + WordKinds::PrimitiveClass => Some(NameKind::PrimitiveClass), + _ => None, + }) + } + + /// Returns only the hardcoded identifier kinds that this prediction set contains. + pub fn iter_hardcoded_ident_kinds(&self) -> impl Iterator + '_ { + self.iter().filter_map(|p| match p { + WordKinds::Annotation => Some(HardcodedIdentKind::Annotation), + _ => None, + }) + } + + /// Returns only the keywords that this prediction set contains. + pub fn iter_keywords(&self) -> impl Iterator + '_ { + all::().filter(|k| self.contains((*k).into())) + } +} + +/// A hardcoded identifier. +/// +/// Maps to a subset of values in [`Predictions`], but an enum +/// for friendly consumption. +pub enum HardcodedIdentKind { + /// An attribute, without the leading `@`. + Annotation, +} + +/// A name (see: [`Predictions`]) +/// +/// Maps to a subset of values in [`Predictions`], but an enum +/// for friendly consumption. +pub enum NameKind { + /// A path. + Path(PathKind), + /// A path segment that follows a `.` + /// A more specific name kind can only be inferred from a recovered AST. + PathSegment, + /// A primitive class, like Eq, Exp, or Add. + PrimitiveClass, +} + +/// A path (see: [`Predictions`]) +/// +/// Maps to a subset of values in [`Predictions`], but an enum +/// for friendly consumption. +#[derive(Debug, Clone, Copy)] +pub enum PathKind { + /// A path in an expression. Callables, UDT constructors, local variables. + Expr, +} diff --git a/compiler/qsc_qasm3/src/parser/error.rs b/compiler/qsc_qasm3/src/parser/error.rs new file mode 100644 index 0000000000..34ca512e21 --- /dev/null +++ b/compiler/qsc_qasm3/src/parser/error.rs @@ -0,0 +1,137 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +use miette::Diagnostic; +use qsc_data_structures::span::Span; +use thiserror::Error; + +use crate::lex::{self, TokenKind}; + +#[derive(Clone, Eq, Error, PartialEq)] +pub struct Error(pub ErrorKind, pub Option); + +impl std::fmt::Display for Error { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + ErrorKind::fmt(&self.0, f) + } +} + +impl std::fmt::Debug for Error { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + let mut formatter = f.debug_tuple("Error"); + if self.1.is_some() { + formatter.field(&self.0).field(&self.1) + } else { + formatter.field(&self.0) + } + .finish() + } +} + +impl Diagnostic for Error { + fn code<'a>(&'a self) -> Option> { + self.0.code() + } + + fn severity(&self) -> Option { + self.0.severity() + } + + fn help<'a>(&'a self) -> Option> { + self.1 + .clone() + .map(|help| Box::new(help) as Box) + } + + fn url<'a>(&'a self) -> Option> { + self.0.url() + } + + fn source_code(&self) -> Option<&dyn miette::SourceCode> { + self.0.source_code() + } + + fn labels(&self) -> Option + '_>> { + self.0.labels() + } + + fn related<'a>(&'a self) -> Option + 'a>> { + self.0.related() + } + + fn diagnostic_source(&self) -> Option<&dyn Diagnostic> { + self.0.diagnostic_source() + } +} + +impl Error { + #[must_use] + pub fn with_offset(self, offset: u32) -> Self { + Self(self.0.with_offset(offset), self.1) + } + + #[must_use] + pub(crate) fn new(kind: ErrorKind) -> Self { + Self(kind, None) + } + + #[must_use] + pub fn with_help(self, help_text: impl Into) -> Self { + Self(self.0, Some(help_text.into())) + } +} + +#[derive(Clone, Debug, Diagnostic, Eq, Error, PartialEq)] +pub enum ErrorKind { + #[error(transparent)] + #[diagnostic(transparent)] + Lex(lex::Error), + #[error("invalid {0} literal")] + #[diagnostic(code("Qasm3.Parse.Literal"))] + Lit(&'static str, #[label] Span), + #[error("unknown escape sequence: `{0}`")] + #[diagnostic(code("Qasm3.Parse.Escape"))] + Escape(char, #[label] Span), + #[error("expected {0}, found {1}")] + #[diagnostic(code("Qasm3.Parse.Token"))] + Token(TokenKind, TokenKind, #[label] Span), + #[error("expected statement after annotation")] + #[diagnostic(code("Qasm3.Parse.FloatingAnnotation"))] + FloatingAnnotation(#[label] Span), + #[error("expected {0}, found {1}")] + #[diagnostic(code("Qasm3.Parse.Rule"))] + Rule(&'static str, TokenKind, #[label] Span), + #[error("expected {0}, found {1}")] + #[diagnostic(code("Qasm3.Parse.Convert"))] + Convert(&'static str, &'static str, #[label] Span), + #[error("expected statement to end with a semicolon")] + #[diagnostic(code("Qasm3.Parse.MissingSemi"))] + MissingSemi(#[label] Span), + #[error("expected inputs to be parenthesized")] + #[diagnostic(code("Qasm3.Parse.MissingParens"))] + MissingParens(#[label] Span), + #[error("missing entry in sequence")] + #[diagnostic(code("Qasm3.Parse.MissingSeqEntry"))] + MissingSeqEntry(#[label] Span), + #[error("expected an item or closing brace, found {0}")] + #[diagnostic(code("Qasm3.Parse.ExpectedItem"))] + ExpectedItem(TokenKind, #[label] Span), +} + +impl ErrorKind { + fn with_offset(self, offset: u32) -> Self { + match self { + Self::Lex(error) => Self::Lex(error.with_offset(offset)), + Self::Lit(name, span) => Self::Lit(name, span + offset), + Self::Escape(ch, span) => Self::Escape(ch, span + offset), + Self::Token(expected, actual, span) => Self::Token(expected, actual, span + offset), + Self::Rule(name, token, span) => Self::Rule(name, token, span + offset), + Self::Convert(expected, actual, span) => Self::Convert(expected, actual, span + offset), + Self::MissingSemi(span) => Self::MissingSemi(span + offset), + Self::MissingParens(span) => Self::MissingParens(span + offset), + Self::FloatingAnnotation(span) => Self::FloatingAnnotation(span + offset), + Self::MissingSeqEntry(span) => Self::MissingSeqEntry(span + offset), + Self::ExpectedItem(token, span) => Self::ExpectedItem(token, span + offset), + } + } +} diff --git a/compiler/qsc_qasm3/src/parser/expr.rs b/compiler/qsc_qasm3/src/parser/expr.rs new file mode 100644 index 0000000000..e1b7cfd57c --- /dev/null +++ b/compiler/qsc_qasm3/src/parser/expr.rs @@ -0,0 +1,292 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +// while we work through the conversion, allow dead code to avoid warnings +#![allow(dead_code)] + +//! Expression parsing makes use of Pratt parsing (or “top-down operator-precedence parsing”) to handle +//! relative precedence of operators. + +use crate::{ + ast::{BinOp, Expr, Lit, LiteralKind, StmtKind, UnOp, Version}, + keyword::Keyword, + lex::{cooked::Literal, ClosedBinOp, Radix, Token, TokenKind}, + parser::{ + completion::WordKinds, + prim::{shorten, token}, + scan::ParserContext, + }, +}; + +use crate::parser::Result; + +use super::error::{Error, ErrorKind}; + +struct PrefixOp { + kind: UnOp, + precedence: u8, +} + +struct MixfixOp { + kind: OpKind, + precedence: u8, +} + +enum OpKind { + Postfix(UnOp), + Binary(BinOp, Assoc), + Assign, + AssignUpdate, + AssignBinary(BinOp), +} + +#[derive(Clone, Copy)] +enum OpName { + Token(TokenKind), + Keyword(Keyword), +} + +#[derive(Clone, Copy)] +enum OpContext { + Precedence(u8), + Stmt, +} + +#[derive(Clone, Copy)] +enum Assoc { + Left, + Right, +} + +const RANGE_PRECEDENCE: u8 = 1; + +pub(super) fn expr(s: &mut ParserContext) -> Result> { + Err(Error::new(ErrorKind::Rule( + "expression", + s.peek().kind, + s.peek().span, + ))) +} + +pub(super) fn expr_eof(s: &mut ParserContext) -> Result> { + let expr = expr(s)?; + token(s, TokenKind::Eof)?; + Ok(expr) +} + +/// Returns true if the expression kind is statement-final. When a statement-final expression occurs +/// at the top level of an expression statement, it indicates the end of the statement, and any +/// operators following it will not be parsed as part of the expression. Statement-final expressions +/// in a top level position also do not require a semicolon when they are followed by another +/// statement. +pub(super) fn is_stmt_final(kind: &StmtKind) -> bool { + matches!( + kind, + StmtKind::Block(_) + | StmtKind::Box(_) + | StmtKind::Cal(_) + | StmtKind::DefCal(_) + | StmtKind::Def(_) + | StmtKind::If(_) + | StmtKind::For(_) + | StmtKind::Switch(_) + | StmtKind::WhileLoop(_) + ) +} + +pub(super) fn lit(s: &mut ParserContext) -> Result> { + let lexeme = s.read(); + + s.expect(WordKinds::True | WordKinds::False); + + let token = s.peek(); + match lit_token(lexeme, token) { + Ok(Some(lit)) => { + s.advance(); + Ok(Some(lit)) + } + Ok(None) => Ok(None), + Err(err) => { + s.advance(); + Err(err) + } + } +} + +pub(super) fn version(s: &mut ParserContext) -> Result> { + let lexeme = s.read(); + let token = s.peek(); + match version_token(lexeme, token) { + Ok(Some(lit)) => { + s.advance(); + Ok(Some(lit)) + } + Ok(None) => Ok(None), + Err(err) => { + s.advance(); + Err(err) + } + } +} + +#[allow(clippy::inline_always)] +#[inline(always)] +fn lit_token(lexeme: &str, token: Token) -> Result> { + match token.kind { + TokenKind::Literal(literal) => match literal { + Literal::Integer(radix) => { + let offset = if radix == Radix::Decimal { 0 } else { 2 }; + let value = lit_int(&lexeme[offset..], radix.into()) + .ok_or(Error::new(ErrorKind::Lit("integer", token.span)))?; + Ok(Some(Lit { + kind: LiteralKind::Integer(value), + span: token.span, + })) + } + Literal::Float => { + let lexeme = lexeme.replace('_', ""); + let value = lexeme + .parse() + .map_err(|_| Error::new(ErrorKind::Lit("floating-point", token.span)))?; + Ok(Some(Lit { + kind: LiteralKind::Float(value), + span: token.span, + })) + } + + Literal::String => { + let lit = shorten(1, 1, lexeme); + Ok(Some(Lit { + kind: LiteralKind::String(lit.into()), + span: token.span, + })) + } + Literal::Bitstring => todo!("bitstring literal"), + Literal::Boolean => todo!("boolean literal"), + Literal::Imaginary => todo!("imaginary literal"), + Literal::Timing(_timing_literal_kind) => todo!("timing literal"), + }, + TokenKind::Keyword(Keyword::True) => Ok(Some(Lit { + kind: LiteralKind::Boolean(true), + span: token.span, + })), + TokenKind::Keyword(Keyword::False) => Ok(Some(Lit { + kind: LiteralKind::Boolean(false), + span: token.span, + })), + _ => Ok(None), + } +} + +pub(super) fn version_token(lexeme: &str, token: Token) -> Result> { + match token.kind { + TokenKind::Literal(literal) => { + if let Literal::Float = literal { + // validate the version number is in the form of `x.y` + let (major, minor) = split_and_parse_numbers(lexeme, token)?; + Ok(Some(Version { + major, + minor: Some(minor), + span: token.span, + })) + } else if let Literal::Integer(radix) = literal { + if radix != Radix::Decimal { + return Err(Error::new(ErrorKind::Lit("version", token.span))); + } + let major = lexeme + .parse::() + .map_err(|_| Error::new(ErrorKind::Lit("version", token.span)))?; + + Ok(Some(Version { + major, + minor: None, + span: token.span, + })) + } else { + Ok(None) + } + } + _ => Ok(None), + } +} + +fn split_and_parse_numbers(lexeme: &str, token: Token) -> Result<(u32, u32)> { + let parts: Vec<&str> = lexeme.split('.').collect(); + if parts.len() != 2 { + return Err(Error::new(ErrorKind::Lit("version", token.span))); + } + + let left = parts[0] + .parse::() + .map_err(|_| Error::new(ErrorKind::Lit("version major", token.span)))?; + let right = parts[1] + .parse::() + .map_err(|_| Error::new(ErrorKind::Lit("version minor", token.span)))?; + + Ok((left, right)) +} + +fn lit_int(lexeme: &str, radix: u32) -> Option { + let multiplier = i64::from(radix); + lexeme + .chars() + .filter(|&c| c != '_') + .try_rfold((0i64, 1i64, false), |(value, place, mut overflow), c| { + let (increment, over) = i64::from(c.to_digit(radix)?).overflowing_mul(place); + overflow |= over; + + let (new_value, over) = value.overflowing_add(increment); + overflow |= over; + + // Only treat as overflow if the value is not i64::MIN, since we need to allow once special + // case of overflow to allow for minimum value literals. + if overflow && new_value != i64::MIN { + return None; + } + + let (new_place, over) = place.overflowing_mul(multiplier); + overflow |= over; + + // If the place overflows, we can still accept the value as long as it's the last digit. + // Pass the overflow forward so that it fails if there are more digits. + Some((new_value, new_place, overflow)) + }) + .map(|(value, _, _)| value) +} + +fn prefix_op(name: OpName) -> Option { + match name { + OpName::Token(TokenKind::Bang) => Some(PrefixOp { + kind: UnOp::NotL, + precedence: 11, + }), + OpName::Token(TokenKind::Tilde) => Some(PrefixOp { + kind: UnOp::NotB, + precedence: 11, + }), + OpName::Token(TokenKind::ClosedBinOp(ClosedBinOp::Minus)) => Some(PrefixOp { + kind: UnOp::Neg, + precedence: 11, + }), + + _ => None, + } +} + +fn closed_bin_op(op: ClosedBinOp) -> BinOp { + match op { + ClosedBinOp::Amp => BinOp::AndB, + ClosedBinOp::AmpAmp => BinOp::AndL, + ClosedBinOp::Bar => BinOp::OrB, + ClosedBinOp::StarStar => BinOp::Exp, + ClosedBinOp::Caret => BinOp::XorB, + ClosedBinOp::GtGt => BinOp::Shr, + ClosedBinOp::LtLt => BinOp::Shl, + ClosedBinOp::Minus => BinOp::Sub, + ClosedBinOp::BarBar => BinOp::OrL, + ClosedBinOp::Percent => BinOp::Mod, + ClosedBinOp::Plus => BinOp::Add, + ClosedBinOp::Slash => BinOp::Div, + ClosedBinOp::Star => BinOp::Mul, + } +} diff --git a/compiler/qsc_qasm3/src/parser/prgm.rs b/compiler/qsc_qasm3/src/parser/prgm.rs new file mode 100644 index 0000000000..5de4907612 --- /dev/null +++ b/compiler/qsc_qasm3/src/parser/prgm.rs @@ -0,0 +1,76 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +use super::{ + prim::{many, opt, recovering, recovering_semi, recovering_token, token}, + stmt, Result, +}; +use crate::{ + ast::{Program, Stmt, StmtKind, Version}, + lex::{Delim, TokenKind}, + parser::{completion::WordKinds, expr}, +}; + +use super::ParserContext; + +pub(super) fn parse(s: &mut ParserContext) -> Result { + let lo = s.peek().span.lo; + let version = opt(s, parse_version)?; + let stmts = parse_top_level_nodes(s)?; + + Ok(Program { + span: s.span(lo), + version, + statements: stmts + .into_iter() + .map(Box::new) + .collect::>() + .into_boxed_slice(), + }) +} + +fn parse_version(s: &mut ParserContext<'_>) -> Result { + s.expect(WordKinds::OpenQASM); + token(s, TokenKind::Keyword(crate::keyword::Keyword::OpenQASM))?; + let next = s.peek(); + if let Some(version) = expr::version(s)? { + recovering_semi(s); + Ok(version) + } else { + Err(crate::parser::error::Error::new( + crate::parser::error::ErrorKind::Lit("version", next.span), + )) + } +} + +pub(super) fn parse_top_level_nodes(s: &mut ParserContext) -> Result> { + const RECOVERY_TOKENS: &[TokenKind] = &[TokenKind::Semicolon, TokenKind::Close(Delim::Brace)]; + let nodes = { + many(s, |s| { + recovering( + s, + |span| Stmt { + span, + annotations: Vec::new().into_boxed_slice(), + kind: Box::new(StmtKind::Err), + }, + RECOVERY_TOKENS, + parse_top_level_node, + ) + }) + }?; + recovering_token(s, TokenKind::Eof); + Ok(nodes) +} + +fn parse_top_level_node(s: &mut ParserContext) -> Result { + if let Some(block) = opt(s, stmt::parse_block)? { + Ok(Stmt { + span: block.span, + annotations: Vec::new().into_boxed_slice(), + kind: Box::new(StmtKind::Block(block)), + }) + } else { + Ok(*stmt::parse(s)?) + } +} diff --git a/compiler/qsc_qasm3/src/parser/prim.rs b/compiler/qsc_qasm3/src/parser/prim.rs new file mode 100644 index 0000000000..6ae8353a17 --- /dev/null +++ b/compiler/qsc_qasm3/src/parser/prim.rs @@ -0,0 +1,326 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +// while we work through the conversion, allow dead code to avoid warnings +#![allow(dead_code)] + +#[cfg(test)] +pub(crate) mod tests; + +use super::{ + error::{Error, ErrorKind}, + scan::ParserContext, + Parser, Result, +}; +use crate::{ + ast::{Ident, IncompletePath, Path, PathKind}, + lex::TokenKind, + parser::completion::WordKinds, +}; + +use qsc_data_structures::span::{Span, WithSpan}; + +#[derive(Clone, Copy, Debug, Eq, PartialEq)] +pub(super) enum FinalSep { + Present, + Missing, +} + +impl FinalSep { + pub(super) fn reify( + self, + mut xs: Vec, + mut as_paren: impl FnMut(T) -> U, + mut as_seq: impl FnMut(Box<[T]>) -> U, + ) -> U { + if self == Self::Missing && xs.len() == 1 { + as_paren(xs.pop().expect("vector should have exactly one item")) + } else { + as_seq(xs.into_boxed_slice()) + } + } +} + +pub(super) fn token(s: &mut ParserContext, t: TokenKind) -> Result<()> { + if let TokenKind::Keyword(k) = t { + s.expect(k.into()); + } + + if s.peek().kind == t { + s.advance(); + Ok(()) + } else { + Err(Error::new(ErrorKind::Token( + t, + s.peek().kind, + s.peek().span, + ))) + } +} + +pub(super) fn ident(s: &mut ParserContext) -> Result> { + let peek = s.peek(); + if peek.kind == TokenKind::Identifier { + let name = s.read().into(); + s.advance(); + Ok(Box::new(Ident { + span: peek.span, + name, + })) + } else { + Err(Error::new(ErrorKind::Rule( + "identifier", + peek.kind, + peek.span, + ))) + } +} + +/// A `path` is a dot-separated list of idents like "Foo.Bar.Baz" +/// this can be a namespace name (in an open statement or namespace declaration), +/// a reference to an item, like `Microsoft.Quantum.Diagnostics.DumpMachine`, +/// or a field access. +/// +/// Path parser. If parsing fails, also returns any valid segments +/// that were parsed up to the final `.` token. +pub(super) fn path( + s: &mut ParserContext, + kind: WordKinds, +) -> std::result::Result, (Error, Option>)> { + s.expect(kind); + + let lo = s.peek().span.lo; + let i = ident(s).map_err(|e| (e, None))?; + + let mut parts = vec![*i]; + while token(s, TokenKind::Dot).is_ok() { + s.expect(WordKinds::PathSegment); + match ident(s) { + Ok(ident) => parts.push(*ident), + Err(error) => { + let trivia_span = s.skip_trivia(); + let keyword = trivia_span.hi == trivia_span.lo + && matches!(s.peek().kind, TokenKind::Keyword(_)); + if keyword { + // Consume any keyword that comes immediately after the final + // dot, assuming it was intended to be part of the path. + s.advance(); + } + + return Err(( + error, + Some(Box::new(IncompletePath { + span: s.span(lo), + segments: parts.into(), + keyword, + })), + )); + } + } + } + + let name = parts.pop().expect("path should have at least one part"); + let namespace = if parts.is_empty() { + None + } else { + Some(parts.into()) + }; + + Ok(Box::new(Path { + span: s.span(lo), + segments: namespace, + name: name.into(), + })) +} + +/// Recovering [`Path`] parser. Parsing only fails if no segments +/// were successfully parsed. If any segments were successfully parsed, +/// returns a [`PathKind::Err`] containing the segments that were +/// successfully parsed up to the final `.` token. +pub(super) fn recovering_path(s: &mut ParserContext, kind: WordKinds) -> Result { + match path(s, kind) { + Ok(path) => Ok(PathKind::Ok(path)), + Err((error, Some(incomplete_path))) => { + s.push_error(error); + Ok(PathKind::Err(Some(incomplete_path))) + } + Err((error, None)) => Err(error), + } +} + +/// Optionally parse with the given parser. +/// Returns Ok(Some(value)) if the parser succeeded, +/// Ok(None) if the parser failed on the first token, +/// Err(error) if the parser failed after consuming some tokens. +pub(super) fn opt(s: &mut ParserContext, mut p: impl Parser) -> Result> { + let offset = s.peek().span.lo; + match p(s) { + Ok(x) => Ok(Some(x)), + Err(error) if advanced(s, offset) => Err(error), + Err(_) => Ok(None), + } +} + +pub(super) fn many(s: &mut ParserContext, mut p: impl Parser) -> Result> { + let mut xs = Vec::new(); + while let Some(x) = opt(s, &mut p)? { + xs.push(x); + } + Ok(xs) +} + +/// Parses a sequence of items separated by commas. +/// Supports recovering on missing items. +pub(super) fn seq(s: &mut ParserContext, mut p: impl Parser) -> Result<(Vec, FinalSep)> +where + T: Default + WithSpan, +{ + let mut xs = Vec::new(); + let mut final_sep = FinalSep::Missing; + while s.peek().kind == TokenKind::Comma { + let mut span = s.peek().span; + span.hi = span.lo; + s.push_error(Error::new(ErrorKind::MissingSeqEntry(span))); + xs.push(T::default().with_span(span)); + s.advance(); + } + while let Some(x) = opt(s, &mut p)? { + xs.push(x); + if token(s, TokenKind::Comma).is_ok() { + while s.peek().kind == TokenKind::Comma { + let mut span = s.peek().span; + span.hi = span.lo; + s.push_error(Error::new(ErrorKind::MissingSeqEntry(span))); + xs.push(T::default().with_span(span)); + s.advance(); + } + final_sep = FinalSep::Present; + } else { + final_sep = FinalSep::Missing; + break; + } + } + Ok((xs, final_sep)) +} + +/// Try to parse with the given parser. +/// +/// If the parser fails on the first token, returns the default value. +/// +/// If the parser fails after consuming some tokens, propagates the error. +pub(super) fn parse_or_else( + s: &mut ParserContext, + default: impl FnOnce(Span) -> T, + mut p: impl Parser, +) -> Result { + let lo = s.peek().span.lo; + match p(s) { + Ok(value) => Ok(value), + Err(error) if advanced(s, lo) => Err(error), + Err(error) => { + s.push_error(error); + // The whitespace will become part of the error span + s.skip_trivia(); + Ok(default(s.span(lo))) + } + } +} + +/// Try to parse with the given parser. +/// +/// If the parser fails on the first token, propagates the error. +/// +/// If the parser fails after consuming some tokens, performs +/// recovery by advancing until the next token in `tokens` is found. +/// The recovery token is consumed. +pub(super) fn recovering( + s: &mut ParserContext, + default: impl FnOnce(Span) -> T, + tokens: &[TokenKind], + mut p: impl Parser, +) -> Result { + let offset = s.peek().span.lo; + match p(s) { + Ok(value) => Ok(value), + Err(error) if advanced(s, offset) => { + s.push_error(error); + s.recover(tokens); + Ok(default(s.span(offset))) + } + Err(error) => Err(error), + } +} + +/// Try to parse with the given parser. +/// +/// If the parser fails on the first token, returns the default value. +/// +/// If the parser fails after consuming some tokens, performs +/// recovery by advancing until the next token in `tokens` is found. +/// The recovery token is consumed. +/// +/// This behavior is a combination of [`recovering`] and [`parse_or_else`], +/// and provides the most aggressive error recovery. +pub(super) fn recovering_parse_or_else( + s: &mut ParserContext, + default: impl FnOnce(Span) -> T, + tokens: &[TokenKind], + mut p: impl Parser, +) -> T { + let lo = s.peek().span.lo; + match p(s) { + Ok(value) => value, + Err(error) => { + s.push_error(error); + + if advanced(s, lo) { + s.recover(tokens); + } else { + // The whitespace will become part of the error node span + s.skip_trivia(); + } + default(s.span(lo)) + } + } +} + +pub(super) fn recovering_semi(s: &mut ParserContext) { + if let Err(error) = token(s, TokenKind::Semicolon) { + // no recovery, just move on to the next token + s.push_error(error); + } +} + +pub(super) fn recovering_token(s: &mut ParserContext, t: TokenKind) { + if let Err(error) = token(s, t) { + s.push_error(error); + s.recover(&[t]); + } +} + +pub(super) fn barrier<'a, T>( + s: &mut ParserContext<'a>, + tokens: &'a [TokenKind], + mut p: impl Parser, +) -> Result { + s.push_barrier(tokens); + let result = p(s); + s.pop_barrier().expect("barrier should be popped"); + result +} + +pub(super) fn shorten(from_start: usize, from_end: usize, s: &str) -> &str { + &s[from_start..s.len() - from_end] +} + +fn advanced(s: &ParserContext, from: u32) -> bool { + s.peek().span.lo > from +} + +fn map_rule_name(name: &'static str, error: Error) -> Error { + Error::new(match error.0 { + ErrorKind::Rule(_, found, span) => ErrorKind::Rule(name, found, span), + ErrorKind::Convert(_, found, span) => ErrorKind::Convert(name, found, span), + kind => kind, + }) +} diff --git a/compiler/qsc_qasm3/src/parser/prim/tests.rs b/compiler/qsc_qasm3/src/parser/prim/tests.rs new file mode 100644 index 0000000000..cacf228ebb --- /dev/null +++ b/compiler/qsc_qasm3/src/parser/prim/tests.rs @@ -0,0 +1,265 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +use super::{ident, opt, seq}; +use crate::{ + ast::PathKind, + keyword::Keyword, + lex::TokenKind, + parser::{ + completion::WordKinds, + error::{Error, ErrorKind}, + scan::ParserContext, + tests::{check, check_opt, check_seq}, + }, +}; +use expect_test::expect; + +use qsc_data_structures::span::Span; + +fn path(s: &mut ParserContext) -> Result { + super::recovering_path(s, WordKinds::empty()) +} + +#[test] +fn ident_basic() { + check(ident, "foo", &expect![[r#"Ident [0-3] "foo""#]]); +} + +#[test] +fn ident_num_suffix() { + check(ident, "foo2", &expect![[r#"Ident [0-4] "foo2""#]]); +} + +#[test] +fn ident_underscore_prefix() { + check(ident, "_foo", &expect![[r#"Ident [0-4] "_foo""#]]); +} + +#[test] +fn ident_num_prefix() { + check( + ident, + "2foo", + &expect![[r#" + Error( + Rule( + "identifier", + Literal( + Integer( + Decimal, + ), + ), + Span { + lo: 0, + hi: 1, + }, + ), + ) + "#]], + ); +} + +#[test] +#[ignore = "Need to talk through how to handle this"] +fn ident_keyword() { + for keyword in enum_iterator::all::() { + let mut scanner = ParserContext::new(keyword.as_str()); + let actual = ident(&mut scanner); + let span = Span { + lo: 0, + hi: keyword + .as_str() + .len() + .try_into() + .expect("keyword length should fit into u32"), + }; + + let expected = Error::new(ErrorKind::Rule( + "identifier", + TokenKind::Keyword(keyword), + span, + )); + + assert_eq!(actual, Err(expected), "{keyword}"); + } +} + +#[test] +fn path_single() { + check(path, "Foo", &expect![[r#"Path [0-3] (Ident [0-3] "Foo")"#]]); +} + +#[test] +fn path_double() { + check( + path, + "Foo.Bar", + &expect![[r#" + Path [0-7]: + Ident [0-3] "Foo" + Ident [4-7] "Bar""#]], + ); +} + +#[test] +fn path_triple() { + check( + path, + "Foo.Bar.Baz", + &expect![[r#" + Path [0-11]: + Ident [0-3] "Foo" + Ident [4-7] "Bar" + Ident [8-11] "Baz""#]], + ); +} + +#[test] +fn path_trailing_dot() { + check( + path, + "Foo.Bar.", + &expect![[r#" + Err IncompletePath [0-8]: + Ident [0-3] "Foo" + Ident [4-7] "Bar" + + [ + Error( + Rule( + "identifier", + Eof, + Span { + lo: 8, + hi: 8, + }, + ), + ), + ]"#]], + ); +} + +#[test] +fn path_followed_by_keyword() { + check( + path, + "Foo.Bar.in", + &expect![[r#" + Err IncompletePath [0-10]: + Ident [0-3] "Foo" + Ident [4-7] "Bar" + + [ + Error( + Rule( + "identifier", + Keyword( + In, + ), + Span { + lo: 8, + hi: 10, + }, + ), + ), + ]"#]], + ); +} + +#[test] +fn opt_succeed() { + check_opt( + |s| opt(s, path), + "Foo.Bar", + &expect![[r#" + Path [0-7]: + Ident [0-3] "Foo" + Ident [4-7] "Bar""#]], + ); +} + +#[test] +fn opt_fail_no_consume() { + check_opt(|s| opt(s, path), "123", &expect!["None"]); +} + +#[test] +fn opt_fail_consume() { + check_opt( + |s| opt(s, path), + "Foo.#", + &expect![[r#" + Err IncompletePath [0-5]: + Ident [0-3] "Foo" + + [ + Error( + Lex( + Unknown( + '#', + Span { + lo: 4, + hi: 5, + }, + ), + ), + ), + Error( + Rule( + "identifier", + Eof, + Span { + lo: 5, + hi: 5, + }, + ), + ), + ]"#]], + ); +} + +#[test] +fn seq_empty() { + check_seq(|s| seq(s, ident), "", &expect!["(, Missing)"]); +} + +#[test] +fn seq_single() { + check_seq( + |s| seq(s, ident), + "foo", + &expect![[r#"(Ident [0-3] "foo", Missing)"#]], + ); +} + +#[test] +fn seq_double() { + check_seq( + |s| seq(s, ident), + "foo, bar", + &expect![[r#" + (Ident [0-3] "foo", + Ident [5-8] "bar", Missing)"#]], + ); +} + +#[test] +fn seq_trailing() { + check_seq( + |s| seq(s, ident), + "foo, bar,", + &expect![[r#" + (Ident [0-3] "foo", + Ident [5-8] "bar", Present)"#]], + ); +} + +#[test] +fn seq_fail_no_consume() { + check_seq( + |s| seq(s, ident), + "foo, 2", + &expect![[r#"(Ident [0-3] "foo", Present)"#]], + ); +} diff --git a/compiler/qsc_qasm3/src/parser/scan.rs b/compiler/qsc_qasm3/src/parser/scan.rs new file mode 100644 index 0000000000..775c20373f --- /dev/null +++ b/compiler/qsc_qasm3/src/parser/scan.rs @@ -0,0 +1,248 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +use crate::{ + lex::{Lexer, Token, TokenKind}, + parser::completion::{collector::ValidWordCollector, WordKinds}, +}; +use qsc_data_structures::span::Span; + +use super::error::Error; +use super::error::ErrorKind; + +#[derive(Debug)] +pub(super) struct NoBarrierError; + +pub(crate) struct ParserContext<'a> { + scanner: Scanner<'a>, + word_collector: Option<&'a mut ValidWordCollector>, +} + +/// Scans over the token stream. Notably enforces LL(1) parser behavior via +/// its lack of a [Clone] implementation and limited peek functionality. +/// This struct should never be clonable, and it should never be able to +/// peek more than one token ahead, to maintain LL(1) enforcement. +pub(super) struct Scanner<'a> { + input: &'a str, + tokens: Lexer<'a>, + barriers: Vec<&'a [TokenKind]>, + errors: Vec, + recovered_eof: bool, + peek: Token, + offset: u32, +} + +impl<'a> ParserContext<'a> { + pub fn new(input: &'a str) -> Self { + Self { + scanner: Scanner::new(input), + word_collector: None, + } + } + + // while we work through the conversion, allow dead code to avoid warnings + #[allow(dead_code)] + pub fn with_word_collector(input: &'a str, word_collector: &'a mut ValidWordCollector) -> Self { + let mut scanner = Scanner::new(input); + + word_collector.did_advance(&mut scanner.peek, scanner.offset); + + Self { + scanner, + word_collector: Some(word_collector), + } + } + + pub(super) fn peek(&self) -> Token { + self.scanner.peek() + } + + pub(super) fn read(&self) -> &'a str { + self.scanner.read() + } + + pub(super) fn span(&self, from: u32) -> Span { + self.scanner.span(from) + } + + /// Advances the scanner to start of the the next valid token. + pub(super) fn advance(&mut self) { + self.scanner.advance(); + + if let Some(e) = &mut self.word_collector { + e.did_advance(&mut self.scanner.peek, self.scanner.offset); + } + } + + /// Moves the scanner to the start of the current token, + /// returning the span of the skipped trivia. + pub(super) fn skip_trivia(&mut self) -> Span { + self.scanner.skip_trivia() + } + + /// Pushes a recovery barrier. While the barrier is active, recovery will never advance past any + /// of the barrier tokens, unless it is explicitly listed as a recovery token. + pub(super) fn push_barrier(&mut self, tokens: &'a [TokenKind]) { + self.scanner.push_barrier(tokens); + } + + /// Pops the most recently pushed active barrier. + pub(super) fn pop_barrier(&mut self) -> Result<(), NoBarrierError> { + self.scanner.pop_barrier() + } + + /// Tries to recover from a parse error by advancing tokens until any of the given recovery + /// tokens, or a barrier token, is found. If a recovery token is found, it is consumed. If a + /// barrier token is found first, it is not consumed. + pub(super) fn recover(&mut self, tokens: &[TokenKind]) { + self.scanner.recover(tokens); + } + + pub(super) fn push_error(&mut self, error: Error) { + self.scanner.push_error(error); + + if let Some(e) = &mut self.word_collector { + e.did_error(); + } + } + + pub(super) fn into_errors(self) -> Vec { + self.scanner.into_errors() + } + + pub fn expect(&mut self, expected: WordKinds) { + if let Some(e) = &mut self.word_collector { + e.expect(expected); + } + } +} + +impl<'a> Scanner<'a> { + pub(super) fn new(input: &'a str) -> Self { + let mut tokens = Lexer::new(input); + let (peek, errors) = next_ok(&mut tokens); + Self { + input, + tokens, + barriers: Vec::new(), + peek: peek.unwrap_or_else(|| eof(input.len())), + errors: errors + .into_iter() + .map(|e| Error::new(ErrorKind::Lex(e))) + .collect(), + offset: 0, + recovered_eof: false, + } + } + + pub(super) fn peek(&self) -> Token { + self.peek + } + + pub(super) fn read(&self) -> &'a str { + &self.input[self.peek.span] + } + + pub(super) fn span(&self, from: u32) -> Span { + Span { + lo: from, + hi: self.offset, + } + } + + /// Moves the scanner to the start of the current token, + /// returning the span of the skipped trivia. + pub(super) fn skip_trivia(&mut self) -> Span { + let lo = self.offset; + self.offset = self.peek.span.lo; + let hi = self.offset; + Span { lo, hi } + } + + pub(super) fn advance(&mut self) { + if self.peek.kind != TokenKind::Eof { + self.offset = self.peek.span.hi; + let (peek, errors) = next_ok(&mut self.tokens); + self.errors + .extend(errors.into_iter().map(|e| Error::new(ErrorKind::Lex(e)))); + self.peek = peek.unwrap_or_else(|| eof(self.input.len())); + } + } + + /// Pushes a recovery barrier. While the barrier is active, recovery will never advance past any + /// of the barrier tokens, unless it is explicitly listed as a recovery token. + pub(super) fn push_barrier(&mut self, tokens: &'a [TokenKind]) { + self.barriers.push(tokens); + } + + /// Pops the most recently pushed active barrier. + pub(super) fn pop_barrier(&mut self) -> Result<(), NoBarrierError> { + match self.barriers.pop() { + Some(_) => Ok(()), + None => Err(NoBarrierError), + } + } + + /// Tries to recover from a parse error by advancing tokens until any of the given recovery + /// tokens, or a barrier token, is found. If a recovery token is found, it is consumed. If a + /// barrier token is found first, it is not consumed. + pub(super) fn recover(&mut self, tokens: &[TokenKind]) { + loop { + let peek = self.peek.kind; + if contains(peek, tokens) { + self.advance(); + break; + } + if peek == TokenKind::Eof || self.barriers.iter().any(|&b| contains(peek, b)) { + break; + } + + self.advance(); + } + } + + pub(super) fn push_error(&mut self, error: Error) { + let is_eof_err = matches!( + error.0, + ErrorKind::Token(_, TokenKind::Eof, _) | ErrorKind::Rule(_, TokenKind::Eof, _) + ); + if !is_eof_err || !self.recovered_eof { + self.errors.push(error); + self.recovered_eof = self.recovered_eof || is_eof_err; + } + } + + pub(super) fn into_errors(self) -> Vec { + self.errors + } +} + +fn eof(offset: usize) -> Token { + let offset = offset.try_into().expect("eof offset should fit into u32"); + Token { + kind: TokenKind::Eof, + span: Span { + lo: offset, + hi: offset, + }, + } +} + +/// Advances the iterator by skipping [`Err`] values until the first [`Ok`] value is found. Returns +/// the found value or [`None`] if the iterator is exhausted. All skipped errors are also +/// accumulated into a vector and returned. +fn next_ok(iter: impl Iterator>) -> (Option, Vec) { + let mut errors = Vec::new(); + for result in iter { + match result { + Ok(v) => return (Some(v), errors), + Err(e) => errors.push(e), + } + } + + (None, errors) +} + +fn contains<'a>(token: TokenKind, tokens: impl IntoIterator) -> bool { + tokens.into_iter().any(|&t| t == token) +} diff --git a/compiler/qsc_qasm3/src/parser/stmt.rs b/compiler/qsc_qasm3/src/parser/stmt.rs new file mode 100644 index 0000000000..d5011eeefb --- /dev/null +++ b/compiler/qsc_qasm3/src/parser/stmt.rs @@ -0,0 +1,132 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +use qsc_data_structures::span::Span; + +use super::{ + completion::WordKinds, + error::{Error, ErrorKind}, + expr::{self}, + prim::{barrier, many, opt, recovering, recovering_semi, recovering_token}, + Result, +}; +use crate::{ + ast::{Annotation, Block, IncludeStmt, LiteralKind, PathKind, Pragma, Stmt, StmtKind}, + lex::{cooked::Literal, Delim, TokenKind}, +}; + +use super::{prim::token, ParserContext}; + +pub(super) fn parse(s: &mut ParserContext) -> Result> { + let lo = s.peek().span.lo; + if let Some(pragma) = opt(s, parse_pragma)? { + return Ok(Box::new(Stmt { + span: s.span(lo), + annotations: [].into(), + kind: Box::new(StmtKind::Pragma(pragma)), + })); + } + let attrs = many(s, parse_annotation)?; + let kind = if token(s, TokenKind::Semicolon).is_ok() { + if attrs.is_empty() { + Box::new(StmtKind::Empty) + } else { + let err_item = default(s.span(lo)); + s.push_error(Error::new(ErrorKind::FloatingAnnotation(err_item.span))); + return Ok(err_item); + } + } else if let Some(v) = opt(s, parse_include)? { + Box::new(v) + } else { + return Err(Error::new(ErrorKind::Rule( + "statement", + s.peek().kind, + s.peek().span, + ))); + }; + + Ok(Box::new(Stmt { + span: s.span(lo), + annotations: attrs.into_boxed_slice(), + kind, + })) +} + +#[allow(clippy::vec_box)] +pub(super) fn parse_many(s: &mut ParserContext) -> Result>> { + many(s, |s| { + recovering(s, default, &[TokenKind::Semicolon], parse) + }) +} + +pub(super) fn parse_block(s: &mut ParserContext) -> Result> { + let lo = s.peek().span.lo; + token(s, TokenKind::Open(Delim::Brace))?; + let stmts = barrier(s, &[TokenKind::Close(Delim::Brace)], parse_many)?; + recovering_token(s, TokenKind::Close(Delim::Brace)); + Ok(Box::new(Block { + span: s.span(lo), + stmts: stmts.into_boxed_slice(), + })) +} + +#[allow(clippy::unnecessary_box_returns)] +fn default(span: Span) -> Box { + Box::new(Stmt { + span, + annotations: Vec::new().into_boxed_slice(), + kind: Box::new(StmtKind::Err), + }) +} + +fn parse_annotation(s: &mut ParserContext) -> Result> { + let lo = s.peek().span.lo; + s.expect(WordKinds::Annotation); + token(s, TokenKind::At)?; + // parse name + // parse value + recovering_semi(s); + Ok(Box::new(Annotation { + span: s.span(lo), + name: Box::new(PathKind::default()), + value: None, + })) +} + +fn parse_include(s: &mut ParserContext) -> Result { + let lo = s.peek().span.lo; + s.expect(WordKinds::Include); + token(s, TokenKind::Keyword(crate::keyword::Keyword::Include))?; + let next = s.peek(); + + let v = expr::lit(s)?; + if let Some(v) = v { + if let LiteralKind::String(v) = v.kind { + let r = IncludeStmt { + span: s.span(lo), + filename: v.to_string(), + }; + token(s, TokenKind::Semicolon)?; + return Ok(StmtKind::Include(r)); + } + }; + Err(Error::new(ErrorKind::Rule( + "include statement", + TokenKind::Literal(Literal::String), + next.span, + ))) +} + +fn parse_pragma(s: &mut ParserContext) -> Result { + let lo = s.peek().span.lo; + s.expect(WordKinds::Pragma); + token(s, TokenKind::Keyword(crate::keyword::Keyword::Pragma))?; + // parse name + // parse value + + Ok(Pragma { + span: s.span(lo), + name: Box::new(PathKind::default()), + value: None, + }) +} diff --git a/compiler/qsc_qasm3/src/parser/tests.rs b/compiler/qsc_qasm3/src/parser/tests.rs new file mode 100644 index 0000000000..1f36a9bff9 --- /dev/null +++ b/compiler/qsc_qasm3/src/parser/tests.rs @@ -0,0 +1,172 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +use std::path::Path; +use std::sync::Arc; + +use crate::io::InMemorySourceResolver; +use crate::io::SourceResolver; + +use super::parse_source; +use super::QasmParseResult; +use miette::Report; + +use super::prim::FinalSep; +use super::{scan::ParserContext, Parser}; +use expect_test::Expect; +use std::fmt::Display; + +pub(crate) fn parse_all

( + path: P, + sources: impl IntoIterator, Arc)>, +) -> miette::Result> +where + P: AsRef, +{ + let resolver = InMemorySourceResolver::from_iter(sources); + let source = resolver.resolve(path.as_ref()).map_err(|e| vec![e])?.1; + let res = crate::parser::parse_source(source, path, &resolver).map_err(|e| vec![e])?; + if res.source.has_errors() { + let errors = res + .errors() + .into_iter() + .map(|e| Report::new(e.clone())) + .collect(); + Err(errors) + } else { + Ok(res) + } +} + +pub(crate) fn parse(source: S) -> miette::Result> +where + S: AsRef, +{ + let resolver = InMemorySourceResolver::from_iter([("test".into(), source.as_ref().into())]); + let res = parse_source(source, "test", &resolver).map_err(|e| vec![e])?; + if res.source.has_errors() { + let errors = res + .errors() + .into_iter() + .map(|e| Report::new(e.clone())) + .collect(); + return Err(errors); + } + Ok(res) +} + +pub(super) fn check(parser: impl Parser, input: &str, expect: &Expect) { + check_map(parser, input, expect, ToString::to_string); +} + +pub(super) fn check_opt(parser: impl Parser>, input: &str, expect: &Expect) { + check_map(parser, input, expect, |value| match value { + Some(value) => value.to_string(), + None => "None".to_string(), + }); +} + +#[allow(dead_code)] +pub(super) fn check_vec(parser: impl Parser>, input: &str, expect: &Expect) { + check_map(parser, input, expect, |values| { + values + .iter() + .map(ToString::to_string) + .collect::>() + .join(",\n") + }); +} + +pub(super) fn check_seq( + parser: impl Parser<(Vec, FinalSep)>, + input: &str, + expect: &Expect, +) { + check_map(parser, input, expect, |(values, sep)| { + format!( + "({}, {sep:?})", + values + .iter() + .map(ToString::to_string) + .collect::>() + .join(",\n") + ) + }); +} + +fn check_map( + mut parser: impl Parser, + input: &str, + expect: &Expect, + f: impl FnOnce(&T) -> String, +) { + let mut scanner = ParserContext::new(input); + let result = parser(&mut scanner); + let errors = scanner.into_errors(); + match result { + Ok(value) if errors.is_empty() => expect.assert_eq(&f(&value)), + Ok(value) => expect.assert_eq(&format!("{}\n\n{errors:#?}", f(&value))), + Err(error) if errors.is_empty() => expect.assert_debug_eq(&error), + Err(error) => expect.assert_eq(&format!("{error:#?}\n\n{errors:#?}")), + } +} + +#[test] +fn int_version_can_be_parsed() -> miette::Result<(), Vec> { + let source = r#"OPENQASM 3;"#; + let res = parse(source)?; + assert_eq!( + Some(format!("{}", res.source.program.version.expect("version"))), + Some("3".to_string()) + ); + Ok(()) +} + +#[test] +fn dotted_version_can_be_parsed() -> miette::Result<(), Vec> { + let source = r#"OPENQASM 3.0;"#; + let res = parse(source)?; + assert_eq!( + Some(format!("{}", res.source.program.version.expect("version"))), + Some("3.0".to_string()) + ); + Ok(()) +} + +#[test] +fn programs_with_includes_can_be_parsed() -> miette::Result<(), Vec> { + let source0 = r#"OPENQASM 3.0; + include "stdgates.inc"; + include "source1.qasm";"#; + let source1 = ""; + let all_sources = [ + ("source0.qasm".into(), source0.into()), + ("source1.qasm".into(), source1.into()), + ]; + + let res = parse_all("source0.qasm", all_sources)?; + assert!(res.source.includes().len() == 1); + println!("{}", res.source.program); + Ok(()) +} + +#[test] +fn programs_with_includes_with_includes_can_be_parsed() -> miette::Result<(), Vec> { + let source0 = r#"OPENQASM 3.0; + include "stdgates.inc"; + include "source1.qasm"; + "#; + let source1 = r#"include "source2.qasm"; + "#; + let source2 = ""; + let all_sources = [ + ("source0.qasm".into(), source0.into()), + ("source1.qasm".into(), source1.into()), + ("source2.qasm".into(), source2.into()), + ]; + + let res = parse_all("source0.qasm", all_sources)?; + assert!(res.source.includes().len() == 1); + assert!(res.source.includes()[0].includes().len() == 1); + Ok(()) +} From 1819c72a39f1619ab6ea40aa6bf84b77dee993db Mon Sep 17 00:00:00 2001 From: Ian Davis Date: Wed, 5 Feb 2025 09:59:10 -0800 Subject: [PATCH 003/108] Add ability to parse quantum decls and lit exprs (#2160) Co-authored-by: Oscar Puente <156957451+orpuente-MS@users.noreply.github.com> --- compiler/qsc_qasm3/src/ast.rs | 47 +- compiler/qsc_qasm3/src/lex/cooked.rs | 68 +- compiler/qsc_qasm3/src/lex/cooked/tests.rs | 264 +++++++- compiler/qsc_qasm3/src/lex/raw.rs | 126 ++-- compiler/qsc_qasm3/src/lex/raw/tests.rs | 148 ++++- .../qsc_qasm3/src/parser/completion/tests.rs | 4 +- compiler/qsc_qasm3/src/parser/expr.rs | 181 +++-- compiler/qsc_qasm3/src/parser/expr/tests.rs | 617 ++++++++++++++++++ compiler/qsc_qasm3/src/parser/prim/tests.rs | 4 +- compiler/qsc_qasm3/src/parser/stmt.rs | 31 +- compiler/qsc_qasm3/src/parser/stmt/tests.rs | 67 ++ 11 files changed, 1417 insertions(+), 140 deletions(-) create mode 100644 compiler/qsc_qasm3/src/parser/expr/tests.rs create mode 100644 compiler/qsc_qasm3/src/parser/stmt/tests.rs diff --git a/compiler/qsc_qasm3/src/ast.rs b/compiler/qsc_qasm3/src/ast.rs index b63abb55d3..18fb0d2c91 100644 --- a/compiler/qsc_qasm3/src/ast.rs +++ b/compiler/qsc_qasm3/src/ast.rs @@ -5,6 +5,7 @@ #![allow(dead_code)] use indenter::{indented, Indented}; +use num_bigint::BigInt; use qsc_data_structures::span::{Span, WithSpan}; use std::{ fmt::{self, Display, Formatter, Write}, @@ -1037,7 +1038,7 @@ impl Display for IncludeStmt { #[derive(Clone, Debug)] pub struct QubitDeclaration { pub span: Span, - pub qubit: Identifier, + pub qubit: Ident, pub size: Option, } @@ -1571,7 +1572,7 @@ pub enum ExprKind { Ident(Ident), UnaryExpr(UnaryExpr), BinaryExpr(BinaryExpr), - Literal(Lit), + Lit(Lit), FunctionCall(FunctionCall), Cast(Cast), Concatenation(Concatenation), @@ -1583,14 +1584,14 @@ impl Display for ExprKind { let indent = set_indentation(indented(f), 0); match self { ExprKind::Err => write!(f, "Err"), - ExprKind::Ident(id) => write!(f, "Ident {id}"), - ExprKind::UnaryExpr(expr) => write!(f, "UnaryExpr {expr}"), + ExprKind::Ident(id) => write!(f, "{id}"), + ExprKind::UnaryExpr(expr) => write!(f, "{expr}"), ExprKind::BinaryExpr(expr) => display_bin_op(indent, expr), - ExprKind::Literal(lit) => write!(f, "Literal {lit}"), - ExprKind::FunctionCall(call) => write!(f, "FunctionCall {call}"), - ExprKind::Cast(cast) => write!(f, "Cast {cast}"), - ExprKind::Concatenation(concat) => write!(f, "Concatenation {concat}"), - ExprKind::IndexExpr(index) => write!(f, "IndexExpr {index}"), + ExprKind::Lit(lit) => write!(f, "{lit}"), + ExprKind::FunctionCall(call) => write!(f, "{call}"), + ExprKind::Cast(cast) => write!(f, "{cast}"), + ExprKind::Concatenation(concat) => write!(f, "{concat}"), + ExprKind::IndexExpr(index) => write!(f, "{index}"), } } } @@ -1740,19 +1741,20 @@ pub struct Lit { impl Display for Lit { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { - write!(f, "Lit {}: {}", self.span, self.kind) + write!(f, "Lit: {}", self.kind) } } #[derive(Clone, Debug)] pub enum LiteralKind { Array(List), - Bitstring { value: usize, width: u32 }, - Boolean(bool), + Bitstring(BigInt, usize), + Bool(bool), Duration { value: f64, unit: TimeUnit }, Float(f64), Imaginary(f64), - Integer(i64), + Int(i64), + BigInt(BigInt), String(Rc), } @@ -1764,21 +1766,22 @@ impl Display for LiteralKind { write!(indent, "Array:")?; indent = set_indentation(indent, 1); for expr in exprs { - write!(indent, "\n{expr}")?; + write!(indent, "\n{expr:?}")?; } Ok(()) } - LiteralKind::Bitstring { value, width } => { - write!(f, "Bitstring: {value} (width {width})") + LiteralKind::Bitstring(value, width) => { + write!(f, "Bitstring(\"{:0>width$}\")", value.to_str_radix(2)) } - LiteralKind::Boolean(b) => write!(f, "Boolean: {b}"), + LiteralKind::Bool(b) => write!(f, "Bool({b:?})"), LiteralKind::Duration { value, unit } => { - write!(f, "Duration: {value} {unit}") + write!(f, "Duration({value:?}, {unit:?})") } - LiteralKind::Float(value) => write!(f, "Float: {value}"), - LiteralKind::Imaginary(value) => write!(f, "Imaginary: {value} im"), - LiteralKind::Integer(i) => write!(f, "Integer: {i}"), - LiteralKind::String(s) => write!(f, "String: {s}"), + LiteralKind::Float(value) => write!(f, "Float({value:?})"), + LiteralKind::Imaginary(value) => write!(f, "Imaginary({value:?})"), + LiteralKind::Int(i) => write!(f, "Int({i:?})"), + LiteralKind::BigInt(i) => write!(f, "BigInt({i:?})"), + LiteralKind::String(s) => write!(f, "String({s:?})"), } } } diff --git a/compiler/qsc_qasm3/src/lex/cooked.rs b/compiler/qsc_qasm3/src/lex/cooked.rs index afc1bbb62d..c4bfe12c28 100644 --- a/compiler/qsc_qasm3/src/lex/cooked.rs +++ b/compiler/qsc_qasm3/src/lex/cooked.rs @@ -49,6 +49,10 @@ pub enum Error { #[diagnostic(code("Qasm3.Lex.UnterminatedString"))] UnterminatedString(#[label] Span), + #[error("string literal with an invalid escape sequence")] + #[diagnostic(code("Qasm3.Lex.InvalidEscapeSequence"))] + InvalidEscapeSequence(#[label] Span), + #[error("unrecognized character `{0}`")] #[diagnostic(code("Qasm3.Lex.UnknownChar"))] Unknown(char, #[label] Span), @@ -64,6 +68,7 @@ impl Error { Self::IncompleteEof(expected, token, span + offset) } Self::UnterminatedString(span) => Self::UnterminatedString(span + offset), + Self::InvalidEscapeSequence(span) => Self::InvalidEscapeSequence(span + offset), Self::Unknown(c, span) => Self::Unknown(c, span + offset), } } @@ -73,6 +78,7 @@ impl Error { Error::Incomplete(_, _, _, s) | Error::IncompleteEof(_, _, s) | Error::UnterminatedString(s) + | Error::InvalidEscapeSequence(s) | Error::Unknown(_, s) => s, } } @@ -82,6 +88,7 @@ impl Error { #[derive(Clone, Copy, Debug, Eq, PartialEq, Sequence)] pub enum TokenKind { Annotation, + Pragma, Keyword(Keyword), Type(Type), @@ -118,7 +125,6 @@ pub enum TokenKind { PlusPlus, /// `->` Arrow, - At, // Operators, ClosedBinOp(ClosedBinOp), @@ -141,6 +147,7 @@ impl Display for TokenKind { fn fmt(&self, f: &mut Formatter) -> fmt::Result { match self { TokenKind::Annotation => write!(f, "annotation"), + TokenKind::Pragma => write!(f, "pragma"), TokenKind::Keyword(keyword) => write!(f, "keyword `{keyword}`"), TokenKind::Type(type_) => write!(f, "keyword `{type_}`"), TokenKind::GPhase => write!(f, "gphase"), @@ -166,7 +173,6 @@ impl Display for TokenKind { TokenKind::Comma => write!(f, "`,`"), TokenKind::PlusPlus => write!(f, "`++`"), TokenKind::Arrow => write!(f, "`->`"), - TokenKind::At => write!(f, "`@`"), TokenKind::ClosedBinOp(op) => write!(f, "`{op}`"), TokenKind::BinOpEq(op) => write!(f, "`{op}=`"), TokenKind::ComparisonOp(op) => write!(f, "`{op}`"), @@ -273,7 +279,6 @@ impl FromStr for Type { #[derive(Clone, Copy, Debug, Eq, PartialEq, Sequence)] pub enum Literal { Bitstring, - Boolean, Float, Imaginary, Integer(Radix), @@ -285,7 +290,6 @@ impl Display for Literal { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { f.write_str(match self { Literal::Bitstring => "bitstring", - Literal::Boolean => "boolean", Literal::Float => "float", Literal::Imaginary => "imaginary", Literal::Integer(_) => "integer", @@ -459,6 +463,23 @@ impl<'a> Lexer<'a> { tokens.next().map(|i| i.kind) } + /// Consumes the characters while they satisfy `f`. Returns the last character eaten, if any. + fn eat_while(&mut self, mut f: impl FnMut(raw::TokenKind) -> bool) -> Option { + let mut last_eaten: Option = None; + loop { + let t = self.tokens.next_if(|t| f(t.kind)); + if t.is_none() { + return last_eaten.map(|t| t.kind); + } + last_eaten = t; + } + } + + fn eat_to_end_of_line(&mut self) { + self.eat_while(|t| t != raw::TokenKind::Newline); + self.next_if_eq(raw::TokenKind::Newline); + } + /// Consumes a list of tokens zero or more times. fn kleen_star(&mut self, tokens: &[raw::TokenKind], complete: TokenKind) -> Result<(), Error> { let mut iter = tokens.iter(); @@ -471,6 +492,7 @@ impl<'a> Lexer<'a> { Ok(()) } + #[allow(clippy::too_many_lines)] fn cook(&mut self, token: &raw::Token) -> Result, Error> { let kind = match token.kind { raw::TokenKind::Bitstring { terminated: true } => { @@ -487,7 +509,13 @@ impl<'a> Lexer<'a> { } raw::TokenKind::Ident => { let ident = &self.input[(token.offset as usize)..(self.offset() as usize)]; - Ok(Some(Self::ident(ident))) + let cooked_ident = Self::ident(ident); + if matches!(cooked_ident, TokenKind::Keyword(Keyword::Pragma)) { + self.eat_to_end_of_line(); + Ok(Some(TokenKind::Pragma)) + } else { + Ok(Some(cooked_ident)) + } } raw::TokenKind::HardwareQubit => Ok(Some(TokenKind::HardwareQubit)), raw::TokenKind::LiteralFragment(_) => { @@ -525,6 +553,26 @@ impl<'a> Lexer<'a> { _ => Ok(Some(number.into())), } } + raw::TokenKind::Single(Single::Sharp) => { + let complete = TokenKind::Pragma; + self.expect(raw::TokenKind::Ident, complete)?; + let ident = &self.input[(token.offset as usize + 1)..(self.offset() as usize)]; + if matches!(Self::ident(ident), TokenKind::Keyword(Keyword::Pragma)) { + self.eat_to_end_of_line(); + Ok(Some(complete)) + } else { + let span = Span { + lo: token.offset, + hi: self.offset(), + }; + Err(Error::Incomplete( + raw::TokenKind::Ident, + complete, + raw::TokenKind::Ident, + span, + )) + } + } raw::TokenKind::Single(single) => self.single(single).map(Some), raw::TokenKind::String { terminated: true } => { Ok(Some(TokenKind::Literal(Literal::String))) @@ -565,7 +613,13 @@ impl<'a> Lexer<'a> { Ok(self.closed_bin_op(ClosedBinOp::Amp)) } } - Single::At => Ok(TokenKind::At), + Single::At => { + // AnnotationKeyword: '@' Identifier ('.' Identifier)* -> pushMode(EAT_TO_LINE_END); + let complete = TokenKind::Annotation; + self.expect(raw::TokenKind::Ident, complete); + self.eat_to_end_of_line(); + Ok(complete) + } Single::Bang => { if self.next_if_eq_single(Single::Eq) { Ok(TokenKind::ComparisonOp(ComparisonOp::BangEq)) @@ -627,6 +681,7 @@ impl<'a> Lexer<'a> { } } Single::Semi => Ok(TokenKind::Semicolon), + Single::Sharp => unreachable!(), Single::Slash => Ok(self.closed_bin_op(ClosedBinOp::Slash)), Single::Star => { if self.next_if_eq_single(Single::Star) { @@ -659,7 +714,6 @@ impl<'a> Lexer<'a> { "delay" => TokenKind::Delay, "reset" => TokenKind::Reset, "measure" => TokenKind::Measure, - "false" | "true" => TokenKind::Literal(Literal::Boolean), ident => { if let Ok(keyword) = ident.parse::() { TokenKind::Keyword(keyword) diff --git a/compiler/qsc_qasm3/src/lex/cooked/tests.rs b/compiler/qsc_qasm3/src/lex/cooked/tests.rs index 66d0f0c9c1..5b6df0c593 100644 --- a/compiler/qsc_qasm3/src/lex/cooked/tests.rs +++ b/compiler/qsc_qasm3/src/lex/cooked/tests.rs @@ -43,12 +43,12 @@ fn op_string(kind: TokenKind) -> Option { TokenKind::ClosedBinOp(op) => Some(op.to_string()), TokenKind::BinOpEq(super::ClosedBinOp::AmpAmp | super::ClosedBinOp::BarBar) | TokenKind::Literal(_) - | TokenKind::Annotation => None, + | TokenKind::Annotation + | TokenKind::Pragma => None, TokenKind::BinOpEq(op) => Some(format!("{op}=")), TokenKind::ComparisonOp(op) => Some(op.to_string()), TokenKind::Identifier => Some("foo".to_string()), TokenKind::HardwareQubit => Some("$1".to_string()), - TokenKind::At => Some("@".to_string()), TokenKind::Eof => Some("EOF".to_string()), } } @@ -833,6 +833,147 @@ fn string_missing_ending() { ); } +#[test] +fn string_escape_quote() { + check( + r#""\"""#, + &expect![[r#" + [ + Ok( + Token { + kind: Literal( + String, + ), + span: Span { + lo: 0, + hi: 4, + }, + }, + ), + ] + "#]], + ); +} + +#[test] +fn string_escape_single_quote() { + check( + r#""\'""#, + &expect![[r#" + [ + Ok( + Token { + kind: Literal( + String, + ), + span: Span { + lo: 0, + hi: 4, + }, + }, + ), + ] + "#]], + ); +} + +#[test] +fn string_escape_newline() { + check( + r#""\n""#, + &expect![[r#" + [ + Ok( + Token { + kind: Literal( + String, + ), + span: Span { + lo: 0, + hi: 4, + }, + }, + ), + ] + "#]], + ); +} + +#[test] +fn string_escape_return() { + check( + r#""\"""#, + &expect![[r#" + [ + Ok( + Token { + kind: Literal( + String, + ), + span: Span { + lo: 0, + hi: 4, + }, + }, + ), + ] + "#]], + ); +} + +#[test] +fn string_escape_tab() { + check( + r#""\t""#, + &expect![[r#" + [ + Ok( + Token { + kind: Literal( + String, + ), + span: Span { + lo: 0, + hi: 4, + }, + }, + ), + ] + "#]], + ); +} + +#[test] +fn string_invalid_escape() { + check( + r#""foo\abar" a"#, + &expect![[r#" + [ + Ok( + Token { + kind: Literal( + String, + ), + span: Span { + lo: 0, + hi: 10, + }, + }, + ), + Ok( + Token { + kind: Identifier, + span: Span { + lo: 11, + hi: 12, + }, + }, + ), + ] + "#]], + ); +} + #[test] fn hardware_qubit() { check( @@ -860,19 +1001,24 @@ fn unknown() { &expect![[r#" [ Err( - Unknown( - '#', + Incomplete( + Ident, + Pragma, + Single( + Sharp, + ), Span { - lo: 0, - hi: 1, + lo: 1, + hi: 2, }, ), ), Err( - Unknown( - '#', + IncompleteEof( + Ident, + Pragma, Span { - lo: 1, + lo: 2, hi: 2, }, ), @@ -941,3 +1087,103 @@ fn comment_four_slashes() { "#]], ); } + +#[test] +fn annotation() { + check( + "@foo.bar 1 2 3;", + &expect![[r#" + [ + Ok( + Token { + kind: Annotation, + span: Span { + lo: 0, + hi: 15, + }, + }, + ), + ] + "#]], + ); +} + +#[test] +fn pragma() { + check( + "pragma", + &expect![[r#" + [ + Ok( + Token { + kind: Pragma, + span: Span { + lo: 0, + hi: 6, + }, + }, + ), + ] + "#]], + ); +} + +#[test] +fn pragma_ident() { + check( + "pragma foo", + &expect![[r#" + [ + Ok( + Token { + kind: Pragma, + span: Span { + lo: 0, + hi: 10, + }, + }, + ), + ] + "#]], + ); +} + +#[test] +fn sharp_pragma() { + check( + "#pragma", + &expect![[r#" + [ + Ok( + Token { + kind: Pragma, + span: Span { + lo: 0, + hi: 7, + }, + }, + ), + ] + "#]], + ); +} + +#[test] +fn sharp_pragma_ident() { + check( + "#pragma foo", + &expect![[r#" + [ + Ok( + Token { + kind: Pragma, + span: Span { + lo: 0, + hi: 11, + }, + }, + ), + ] + "#]], + ); +} diff --git a/compiler/qsc_qasm3/src/lex/raw.rs b/compiler/qsc_qasm3/src/lex/raw.rs index 65bf3d7fea..7b36bad167 100644 --- a/compiler/qsc_qasm3/src/lex/raw.rs +++ b/compiler/qsc_qasm3/src/lex/raw.rs @@ -25,10 +25,11 @@ use std::{ /// An enum used internally by the raw lexer to signal whether /// a token was partially parsed or if it wasn't parsed at all. -enum LexError { - /// An incomplete token was parsed, e.g., a string missing - /// the closing quote or a number ending in an underscore. - Incomplete(T), +enum NumberLexError { + /// A number ending in an underscore. + EndsInUnderscore, + /// An incomplete binary, octal, or hex numer. + Incomplete, /// The token wasn't parsed and no characters were consumed /// when trying to parse the token. None, @@ -115,6 +116,8 @@ pub enum Single { Plus, /// `;` Semi, + /// `#` Used for pragmas. + Sharp, /// `/` Slash, /// `*` @@ -147,6 +150,7 @@ impl Display for Single { Single::Percent => '%', Single::Plus => '+', Single::Semi => ';', + Single::Sharp => '#', Single::Slash => '/', Single::Star => '*', Single::Tilde => '~', @@ -318,59 +322,74 @@ impl<'a> Lexer<'a> { } } - fn number(&mut self, c: char) -> Result> { - self.leading_zero(c) - .or_else(|_| self.leading_dot(c)) - .or_else(|_| self.decimal_or_float(c)) + fn number(&mut self, c: char) -> Result { + match self.leading_zero(c) { + Ok(number) => return Ok(number), + Err(NumberLexError::None) => (), + Err(err) => return Err(err), + } + + match self.leading_dot(c) { + Ok(number) => return Ok(number), + Err(NumberLexError::None) => (), + Err(err) => return Err(err), + } + + self.decimal_or_float(c) } /// This rule allows us to differentiate a leading dot from a mid dot. /// A float starting with a leading dot must contain at least one digit /// after the dot. - fn leading_dot(&mut self, c: char) -> Result> { - let first = self.first(); - if c == '.' && first.is_some_and(|c| c == '_' || c.is_ascii_digit()) { - self.chars.next(); - let c1 = first.expect("first.is_some_and() succeeded"); + fn leading_dot(&mut self, c: char) -> Result { + if c == '.' && self.first().is_some_and(|c| c.is_ascii_digit()) { + let (_, c1) = self.chars.next().expect("first.is_some_and() succeeded"); self.decimal(c1)?; match self.exp() { - Ok(()) | Err(LexError::None) => Ok(Number::Float), - Err(_) => Err(LexError::Incomplete(Number::Float)), + Ok(()) | Err(NumberLexError::None) => Ok(Number::Float), + Err(err) => Err(err), } } else { - Err(LexError::None) + Err(NumberLexError::None) } } /// A float with a middle dot could optionally contain numbers after the dot. /// This rule is necessary to differentiate from the floats with a leading dot, /// which must have digits after the dot. - fn mid_dot(&mut self, c: char) -> Result> { + fn mid_dot(&mut self, c: char) -> Result { if c == '.' { match self.first() { - Some(c1) if c1 == '_' || c1.is_ascii_digit() => { + Some(c1) if c1.is_ascii_digit() => { self.chars.next(); match self.decimal(c1) { - Err(LexError::Incomplete(_)) => Err(LexError::Incomplete(Number::Float)), - Ok(_) | Err(LexError::None) => match self.exp() { - Ok(()) | Err(LexError::None) => Ok(Number::Float), - Err(_) => Err(LexError::Incomplete(Number::Float)), + Err(NumberLexError::EndsInUnderscore) => { + Err(NumberLexError::EndsInUnderscore) + } + Ok(_) | Err(NumberLexError::None) => match self.exp() { + Ok(()) | Err(NumberLexError::None) => Ok(Number::Float), + Err(_) => Err(NumberLexError::EndsInUnderscore), }, + Err(NumberLexError::Incomplete) => unreachable!(), } } + Some('e') => match self.exp() { + Ok(()) => Ok(Number::Float), + Err(_) => todo!(), + }, None | Some(_) => Ok(Number::Float), } } else { - Err(LexError::None) + Err(NumberLexError::None) } } /// This rule parses binary, octal, hexadecimal numbers, or decimal/floats /// if the next character isn't a radix specifier. /// Numbers in Qasm aren't allowed to end in an underscore. - fn leading_zero(&mut self, c: char) -> Result> { + fn leading_zero(&mut self, c: char) -> Result { if c != '0' { - return Err(LexError::None); + return Err(NumberLexError::None); } let radix = if self.next_if_eq('b') || self.next_if_eq('B') { @@ -387,7 +406,8 @@ impl<'a> Lexer<'a> { match radix { Radix::Binary | Radix::Octal | Radix::Hexadecimal => match last_eaten { - None | Some('_') => Err(LexError::Incomplete(Number::Int(radix))), + None => Err(NumberLexError::Incomplete), + Some('_') => Err(NumberLexError::EndsInUnderscore), _ => Ok(Number::Int(radix)), }, Radix::Decimal => match self.first() { @@ -395,6 +415,11 @@ impl<'a> Lexer<'a> { self.chars.next(); self.mid_dot(c1) } + Some('e') => match self.exp() { + Ok(()) => Ok(Number::Float), + Err(NumberLexError::None) => unreachable!(), + Err(_) => Err(NumberLexError::EndsInUnderscore), + }, None | Some(_) => Ok(Number::Int(Radix::Decimal)), }, } @@ -404,23 +429,23 @@ impl<'a> Lexer<'a> { /// Numbers in QASM aren't allowed to end in an underscore. /// The rule in the .g4 file is /// `DecimalIntegerLiteral: ([0-9] '_'?)* [0-9];` - fn decimal(&mut self, c: char) -> Result> { + fn decimal(&mut self, c: char) -> Result { if !c.is_ascii_digit() { - return Err(LexError::None); + return Err(NumberLexError::None); } let last_eaten = self.eat_while(|c| c == '_' || c.is_ascii_digit()); match last_eaten { - None if c == '_' => Err(LexError::None), - Some('_') => Err(LexError::Incomplete(Number::Int(Radix::Decimal))), + None if c == '_' => Err(NumberLexError::None), + Some('_') => Err(NumberLexError::EndsInUnderscore), _ => Ok(Number::Int(Radix::Decimal)), } } /// This rule disambiguates between a decimal integer and a float with a /// mid dot, like `12.3`. - fn decimal_or_float(&mut self, c: char) -> Result> { + fn decimal_or_float(&mut self, c: char) -> Result { self.decimal(c)?; match self.first() { None => Ok(Number::Int(Radix::Decimal)), @@ -430,36 +455,42 @@ impl<'a> Lexer<'a> { } _ => match self.exp() { Ok(()) => Ok(Number::Float), - Err(LexError::None) => Ok(Number::Int(Radix::Decimal)), - Err(_) => Err(LexError::Incomplete(Number::Float)), + Err(NumberLexError::None) => Ok(Number::Int(Radix::Decimal)), + Err(NumberLexError::EndsInUnderscore) => Err(NumberLexError::EndsInUnderscore), + Err(NumberLexError::Incomplete) => unreachable!(), }, } } - /// Parses an exponent. Errors if the exponent was missing or incomplete. + /// Parses an exponent. Errors if the exponent is an invalid decimal. /// The rule `decimal_or_float` uses the `LexError::None` variant of the error /// to classify the token as an integer. /// The `leading_dot` and `mid_dot` rules use the `LexError::None` variant to /// classify the token as a float. - fn exp(&mut self) -> Result<(), LexError> { + fn exp(&mut self) -> Result<(), NumberLexError> { if self.next_if(|c| c == 'e' || c == 'E') { // Optionally there could be a + or - sign. self.chars.next_if(|i| i.1 == '+' || i.1 == '-'); - // If the next character isn't a digit or an - // underscore we issue an error without consuming it. - let first = self.first().ok_or(LexError::Incomplete(Number::Float))?; - if first != '_' && !first.is_ascii_digit() { - Err(LexError::Incomplete(Number::Float)) - } else { + // If we reached the end of file, we return a valid float. + let Some(first) = self.first() else { + return Ok(()); + }; + + // If the next character isn't a digit + // we issue an error without consuming it. + if first.is_ascii_digit() { self.chars.next(); match self.decimal(first) { Ok(_) => Ok(()), - Err(_) => Err(LexError::Incomplete(Number::Float)), + Err(NumberLexError::EndsInUnderscore) => Err(NumberLexError::EndsInUnderscore), + Err(NumberLexError::None | NumberLexError::Incomplete) => unreachable!(), } + } else { + Ok(()) } } else { - Err(LexError::None) + Err(NumberLexError::None) } } @@ -477,6 +508,8 @@ impl<'a> Lexer<'a> { return Some(bitstring); } + let mut invalid_escape = false; + while self.first().is_some_and(|c| c != string_start) { self.eat_while(|c| c != '\\' && c != string_start); if self.next_if_eq('\\') { @@ -543,8 +576,10 @@ impl Iterator for Lexer<'_> { } else { match self.number(c) { Ok(number) => TokenKind::Number(number), - Err(LexError::Incomplete(_)) => TokenKind::Unknown, - Err(LexError::None) => self + Err(NumberLexError::EndsInUnderscore | NumberLexError::Incomplete) => { + TokenKind::Unknown + } + Err(NumberLexError::None) => self .string(c) .or_else(|| single(c).map(TokenKind::Single)) .unwrap_or(TokenKind::Unknown), @@ -584,6 +619,7 @@ fn single(c: char) -> Option { '>' => Some(Single::Gt), '|' => Some(Single::Bar), '~' => Some(Single::Tilde), + '#' => Some(Single::Sharp), _ => None, } } diff --git a/compiler/qsc_qasm3/src/lex/raw/tests.rs b/compiler/qsc_qasm3/src/lex/raw/tests.rs index 96ff8383ec..3850b06485 100644 --- a/compiler/qsc_qasm3/src/lex/raw/tests.rs +++ b/compiler/qsc_qasm3/src/lex/raw/tests.rs @@ -176,10 +176,27 @@ fn string() { ); } +#[test] +fn string_missing_ending() { + check( + r#""string"#, + &expect![[r#" + [ + Token { + kind: String { + terminated: false, + }, + offset: 0, + }, + ] + "#]], + ); +} + #[test] fn string_escape_quote() { check( - r#""str\"ing""#, + r#""\"""#, &expect![[r#" [ Token { @@ -194,14 +211,82 @@ fn string_escape_quote() { } #[test] -fn string_missing_ending() { +fn string_escape_single_quote() { check( - r#""string"#, + r#""\'""#, &expect![[r#" [ Token { kind: String { - terminated: false, + terminated: true, + }, + offset: 0, + }, + ] + "#]], + ); +} + +#[test] +fn string_escape_newline() { + check( + r#""\n""#, + &expect![[r#" + [ + Token { + kind: String { + terminated: true, + }, + offset: 0, + }, + ] + "#]], + ); +} + +#[test] +fn string_escape_return() { + check( + r#""\r""#, + &expect![[r#" + [ + Token { + kind: String { + terminated: true, + }, + offset: 0, + }, + ] + "#]], + ); +} + +#[test] +fn string_escape_tab() { + check( + r#""\t""#, + &expect![[r#" + [ + Token { + kind: String { + terminated: true, + }, + offset: 0, + }, + ] + "#]], + ); +} + +#[test] +fn string_invalid_escape() { + check( + r#""\s""#, + &expect![[r#" + [ + Token { + kind: String { + terminated: true, }, offset: 0, }, @@ -532,6 +617,23 @@ fn float() { ); } +#[test] +fn incomplete_exponent_lexed_as_float() { + check( + "1.e", + &expect![[r#" + [ + Token { + kind: Number( + Float, + ), + offset: 0, + }, + ] + "#]], + ); +} + #[test] fn leading_zero() { check( @@ -666,6 +768,36 @@ fn leading_point_exp() { ); } +#[test] +fn incomplete_exp() { + check( + "0e", + &expect![[r#" + [ + Token { + kind: Number( + Float, + ), + offset: 0, + }, + ] + "#]], + ); + check( + "1e", + &expect![[r#" + [ + Token { + kind: Number( + Float, + ), + offset: 0, + }, + ] + "#]], + ); +} + #[test] fn leading_zero_point() { check( @@ -724,11 +856,15 @@ fn unknown() { &expect![[r#" [ Token { - kind: Unknown, + kind: Single( + Sharp, + ), offset: 0, }, Token { - kind: Unknown, + kind: Single( + Sharp, + ), offset: 1, }, ] diff --git a/compiler/qsc_qasm3/src/parser/completion/tests.rs b/compiler/qsc_qasm3/src/parser/completion/tests.rs index 396380748a..dddafe1a79 100644 --- a/compiler/qsc_qasm3/src/parser/completion/tests.rs +++ b/compiler/qsc_qasm3/src/parser/completion/tests.rs @@ -36,7 +36,7 @@ fn begin_document() { "|OPENQASM 3;", &expect![[r" WordKinds( - Annotation | Include | OpenQASM | Pragma, + Annotation | Include | OpenQASM | Pragma | Qubit, ) "]], ); @@ -48,7 +48,7 @@ fn end_of_version() { "OPENQASM 3;|", &expect![[r" WordKinds( - Annotation | Include | Pragma, + Annotation | Include | Pragma | Qubit, ) "]], ); diff --git a/compiler/qsc_qasm3/src/parser/expr.rs b/compiler/qsc_qasm3/src/parser/expr.rs index e1b7cfd57c..489211fecd 100644 --- a/compiler/qsc_qasm3/src/parser/expr.rs +++ b/compiler/qsc_qasm3/src/parser/expr.rs @@ -7,10 +7,17 @@ //! Expression parsing makes use of Pratt parsing (or “top-down operator-precedence parsing”) to handle //! relative precedence of operators. +#[cfg(test)] +pub(crate) mod tests; + +use num_bigint::BigInt; +use num_traits::Num; +use qsc_data_structures::span::Span; + use crate::{ - ast::{BinOp, Expr, Lit, LiteralKind, StmtKind, UnOp, Version}, + ast::{BinOp, Expr, ExprKind, ExprStmt, Lit, LiteralKind, UnOp, Version}, keyword::Keyword, - lex::{cooked::Literal, ClosedBinOp, Radix, Token, TokenKind}, + lex::{cooked::Literal, ClosedBinOp, Delim, Radix, Token, TokenKind}, parser::{ completion::WordKinds, prim::{shorten, token}, @@ -61,37 +68,34 @@ enum Assoc { const RANGE_PRECEDENCE: u8 = 1; pub(super) fn expr(s: &mut ParserContext) -> Result> { - Err(Error::new(ErrorKind::Rule( - "expression", - s.peek().kind, - s.peek().span, - ))) + expr_op(s, OpContext::Precedence(0)) } -pub(super) fn expr_eof(s: &mut ParserContext) -> Result> { - let expr = expr(s)?; - token(s, TokenKind::Eof)?; - Ok(expr) +pub(super) fn expr_stmt(s: &mut ParserContext) -> Result> { + expr_op(s, OpContext::Stmt) +} + +fn expr_op(s: &mut ParserContext, _context: OpContext) -> Result> { + let lhs = expr_base(s)?; + Ok(lhs) } -/// Returns true if the expression kind is statement-final. When a statement-final expression occurs -/// at the top level of an expression statement, it indicates the end of the statement, and any -/// operators following it will not be parsed as part of the expression. Statement-final expressions -/// in a top level position also do not require a semicolon when they are followed by another -/// statement. -pub(super) fn is_stmt_final(kind: &StmtKind) -> bool { - matches!( +fn expr_base(s: &mut ParserContext) -> Result> { + let lo = s.peek().span.lo; + let kind = if let Some(l) = lit(s)? { + Ok(Box::new(ExprKind::Lit(l))) + } else { + Err(Error::new(ErrorKind::Rule( + "expression", + s.peek().kind, + s.peek().span, + ))) + }?; + + Ok(Box::new(Expr { + span: s.span(lo), kind, - StmtKind::Block(_) - | StmtKind::Box(_) - | StmtKind::Cal(_) - | StmtKind::DefCal(_) - | StmtKind::Def(_) - | StmtKind::If(_) - | StmtKind::For(_) - | StmtKind::Switch(_) - | StmtKind::WhileLoop(_) - ) + })) } pub(super) fn lit(s: &mut ParserContext) -> Result> { @@ -136,12 +140,20 @@ fn lit_token(lexeme: &str, token: Token) -> Result> { TokenKind::Literal(literal) => match literal { Literal::Integer(radix) => { let offset = if radix == Radix::Decimal { 0 } else { 2 }; - let value = lit_int(&lexeme[offset..], radix.into()) - .ok_or(Error::new(ErrorKind::Lit("integer", token.span)))?; - Ok(Some(Lit { - kind: LiteralKind::Integer(value), - span: token.span, - })) + let value = lit_int(&lexeme[offset..], radix.into()); + if let Some(value) = value { + Ok(Some(Lit { + kind: LiteralKind::Int(value), + span: token.span, + })) + } else if let Some(value) = lit_bigint(&lexeme[offset..], radix.into()) { + Ok(Some(Lit { + kind: LiteralKind::BigInt(value), + span: token.span, + })) + } else { + Err(Error::new(ErrorKind::Lit("integer", token.span))) + } } Literal::Float => { let lexeme = lexeme.replace('_', ""); @@ -153,25 +165,65 @@ fn lit_token(lexeme: &str, token: Token) -> Result> { span: token.span, })) } - Literal::String => { - let lit = shorten(1, 1, lexeme); + let lexeme = shorten(1, 1, lexeme); + let string = unescape(lexeme).map_err(|index| { + let ch = lexeme[index + 1..] + .chars() + .next() + .expect("character should be found at index"); + let index: u32 = index.try_into().expect("index should fit into u32"); + let lo = token.span.lo + index + 2; + let span = Span { lo, hi: lo + 1 }; + Error::new(ErrorKind::Escape(ch, span)) + })?; + Ok(Some(Lit { + kind: LiteralKind::String(string.into()), + span: token.span, + })) + } + Literal::Bitstring => { + let lexeme = shorten(1, 1, lexeme); + let width = lexeme + .to_string() + .chars() + .filter(|c| *c == '0' || *c == '1') + .count(); + // parse it to validate the bitstring + let value = BigInt::from_str_radix(lexeme, 2) + .map_err(|_| Error::new(ErrorKind::Lit("bitstring", token.span)))?; + Ok(Some(Lit { - kind: LiteralKind::String(lit.into()), span: token.span, + kind: LiteralKind::Bitstring(value, width), })) } - Literal::Bitstring => todo!("bitstring literal"), - Literal::Boolean => todo!("boolean literal"), - Literal::Imaginary => todo!("imaginary literal"), - Literal::Timing(_timing_literal_kind) => todo!("timing literal"), + Literal::Imaginary => { + let lexeme = lexeme + .chars() + .filter(|x| *x != '_') + .take_while(|x| x.is_numeric() || *x == '.') + .collect::(); + + let value = lexeme + .parse() + .map_err(|_| Error::new(ErrorKind::Lit("imaginary", token.span)))?; + Ok(Some(Lit { + kind: LiteralKind::Imaginary(value), + span: token.span, + })) + } + Literal::Timing(_timing_literal_kind) => Err(Error::new(ErrorKind::Lit( + "unimplemented: timing literal", + token.span, + ))), }, TokenKind::Keyword(Keyword::True) => Ok(Some(Lit { - kind: LiteralKind::Boolean(true), + kind: LiteralKind::Bool(true), span: token.span, })), TokenKind::Keyword(Keyword::False) => Ok(Some(Lit { - kind: LiteralKind::Boolean(false), + kind: LiteralKind::Bool(false), span: token.span, })), _ => Ok(None), @@ -254,6 +306,15 @@ fn lit_int(lexeme: &str, radix: u32) -> Option { .map(|(value, _, _)| value) } +fn lit_bigint(lexeme: &str, radix: u32) -> Option { + // from_str_radix does removes underscores as long as the lexeme + // doesn't start with an underscore. + match BigInt::from_str_radix(lexeme, radix) { + Ok(value) => Some(value), + Err(_) => None, + } +} + fn prefix_op(name: OpName) -> Option { match name { OpName::Token(TokenKind::Bang) => Some(PrefixOp { @@ -290,3 +351,37 @@ fn closed_bin_op(op: ClosedBinOp) -> BinOp { ClosedBinOp::Star => BinOp::Mul, } } + +fn unescape(s: &str) -> std::result::Result { + let mut chars = s.char_indices(); + let mut buf = String::with_capacity(s.len()); + while let Some((index, ch)) = chars.next() { + buf.push(if ch == '\\' { + let escape = chars.next().expect("escape should not be empty").1; + match escape { + '\\' => '\\', + '\'' => '\'', + '"' => '"', + 'n' => '\n', + 'r' => '\r', + 't' => '\t', + _ => return Err(index), + } + } else { + ch + }); + } + + Ok(buf) +} + +pub(super) fn designator(s: &mut ParserContext) -> Result { + let lo = s.peek().span.lo; + token(s, TokenKind::Open(Delim::Bracket))?; + let expr = expr(s)?; + token(s, TokenKind::Close(Delim::Bracket))?; + Ok(ExprStmt { + span: s.span(lo), + expr, + }) +} diff --git a/compiler/qsc_qasm3/src/parser/expr/tests.rs b/compiler/qsc_qasm3/src/parser/expr/tests.rs new file mode 100644 index 0000000000..3fe0d6d034 --- /dev/null +++ b/compiler/qsc_qasm3/src/parser/expr/tests.rs @@ -0,0 +1,617 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +use crate::parser::tests::check; + +use super::expr; + +use expect_test::expect; + +#[test] +fn lit_int() { + check(expr, "123", &expect!["Expr [0-3]: Lit: Int(123)"]); +} + +#[test] +fn lit_int_underscore() { + check(expr, "123_456", &expect!["Expr [0-7]: Lit: Int(123456)"]); +} + +#[test] +fn lit_int_leading_zero() { + check(expr, "0123", &expect!["Expr [0-4]: Lit: Int(123)"]); +} + +#[test] +fn lit_int_max() { + check( + expr, + "9_223_372_036_854_775_807", + &expect!["Expr [0-25]: Lit: Int(9223372036854775807)"], + ); +} + +// NOTE: Since we need to support literals of value i64::MIN while also parsing the negative sign +// as a unary operator, we need to allow one special case of overflow that is the absolute value +// of i64::MIN. This will wrap to a negative value. See the `lit_int_min` test below. +// To check for other issues with handling i64::MIN, hexadecimal and binary literals +// of i64::MIN also need to be tested. +#[test] +fn lit_int_overflow_min() { + check( + expr, + "9_223_372_036_854_775_808", + &expect!["Expr [0-25]: Lit: Int(-9223372036854775808)"], + ); +} + +#[test] +fn lit_int_overflow_min_hexadecimal() { + check( + expr, + "0x8000000000000000", + &expect!["Expr [0-18]: Lit: Int(-9223372036854775808)"], + ); +} + +#[test] +fn lit_int_overflow_min_binary() { + check( + expr, + "0b1000000000000000000000000000000000000000000000000000000000000000", + &expect!["Expr [0-66]: Lit: Int(-9223372036854775808)"], + ); +} + +#[test] +fn lit_int_too_big_for_i64() { + check( + expr, + "9_223_372_036_854_775_809", + &expect!["Expr [0-25]: Lit: BigInt(9223372036854775809)"], + ); +} + +#[test] +fn lit_int_too_big_hexadecimal_promotes_to_bigint() { + check( + expr, + "0x8000000000000001", + &expect!["Expr [0-18]: Lit: BigInt(9223372036854775809)"], + ); +} + +#[test] +fn lit_int_too_big_binary_promotes_to_bigint() { + check( + expr, + "0b1000000000000000000000000000000000000000000000000000000000000001", + &expect!["Expr [0-66]: Lit: BigInt(9223372036854775809)"], + ); +} + +// NOTE: Since we need to support literals of value i64::MIN while also parsing the negative sign +// as a unary operator, we need to allow one special case of overflow that is the absolute value +// of i64::MIN. This will wrap to a negative value, and then negate of i64::MIN is i64::MIN, so +// the correct value is achieved at runtime. +#[test] +#[ignore = "Re-enable when we support unary ops"] +fn lit_int_min() { + check( + expr, + "-9_223_372_036_854_775_808", + &expect![[r#" + Expr [0-26]: UnOp (Neg): + Expr [1-26]: Lit: Int(-9223372036854775808)"#]], + ); +} + +#[test] +fn lit_int_hexadecimal() { + check(expr, "0x1a2b3c", &expect!["Expr [0-8]: Lit: Int(1715004)"]); +} + +#[test] +fn lit_int_octal() { + check(expr, "0o1234567", &expect!["Expr [0-9]: Lit: Int(342391)"]); +} + +#[test] +fn lit_int_binary() { + check(expr, "0b10110", &expect!["Expr [0-7]: Lit: Int(22)"]); +} + +#[test] +fn lit_bigint_hexadecimal() { + check( + expr, + "0x1a2b3c1a2b3c1a2b3c1a", + &expect!["Expr [0-22]: Lit: BigInt(123579069371309093501978)"], + ); +} + +#[test] +fn lit_bigint_hexadecimal_capital_x() { + check( + expr, + "0X1a2b3c1a2b3c1a2b3c1a", + &expect!["Expr [0-22]: Lit: BigInt(123579069371309093501978)"], + ); +} + +#[test] +fn lit_bigint_octal() { + check( + expr, + "0o1234567123456712345671234", + &expect!["Expr [0-27]: Lit: BigInt(6167970861177743307420)"], + ); +} + +#[test] +fn lit_bigint_octal_capital_o() { + check( + expr, + "0O1234567123456712345671234", + &expect!["Expr [0-27]: Lit: BigInt(6167970861177743307420)"], + ); +} + +#[test] +fn lit_bigint_binary() { + check( + expr, + "0b1011010110101101011010110101101011010110101101011010110101101011", + &expect!["Expr [0-66]: Lit: BigInt(13091237729729359211)"], + ); +} + +#[test] +fn lit_bigint_binary_capital_b() { + check( + expr, + "0B1011010110101101011010110101101011010110101101011010110101101011", + &expect!["Expr [0-66]: Lit: BigInt(13091237729729359211)"], + ); +} + +#[test] +fn lit_float() { + check(expr, "1.23", &expect!["Expr [0-4]: Lit: Float(1.23)"]); +} + +#[test] +fn lit_float_leading_dot() { + check(expr, ".23", &expect!["Expr [0-3]: Lit: Float(0.23)"]); +} + +#[test] +fn lit_float_trailing_dot() { + check(expr, "1.", &expect!["Expr [0-2]: Lit: Float(1.0)"]); +} + +#[test] +fn lit_float_underscore() { + check( + expr, + "123_456.78", + &expect!["Expr [0-10]: Lit: Float(123456.78)"], + ); +} + +#[test] +fn lit_float_leading_zero() { + check(expr, "0.23", &expect!["Expr [0-4]: Lit: Float(0.23)"]); +} + +#[test] +fn lit_float_trailing_exp_0() { + check( + expr, + "0e", + &expect![[r#" + Error( + Lit( + "floating-point", + Span { + lo: 0, + hi: 2, + }, + ), + ) + "#]], + ); +} + +#[test] +fn lit_float_trailing_exp_1() { + check( + expr, + "1e", + &expect![[r#" + Error( + Lit( + "floating-point", + Span { + lo: 0, + hi: 2, + }, + ), + ) + "#]], + ); +} + +#[test] +fn lit_float_trailing_dot_trailing_exp() { + check( + expr, + "1.e", + &expect![[r#" + Error( + Lit( + "floating-point", + Span { + lo: 0, + hi: 3, + }, + ), + ) + "#]], + ); +} + +#[test] +fn lit_float_dot_trailing_exp() { + check( + expr, + "1.2e", + &expect![[r#" + Error( + Lit( + "floating-point", + Span { + lo: 0, + hi: 4, + }, + ), + ) + "#]], + ); +} + +#[test] +fn lit_float_trailing_exp_dot() { + check( + expr, + "1e.", + &expect![[r#" + Error( + Lit( + "floating-point", + Span { + lo: 0, + hi: 2, + }, + ), + ) + "#]], + ); +} + +#[test] +#[ignore = "Re-enable when we support more than literals"] +fn lit_int_hexadecimal_dot() { + check( + expr, + "0x123.45", + &expect![[r#" + Expr [0-6]: Field: + Expr [0-5]: Lit: Int(291) + Err + + [ + Error( + Rule( + "identifier", + Int( + Decimal, + ), + Span { + lo: 6, + hi: 8, + }, + ), + ), + ]"#]], + ); +} + +#[test] +fn lit_string() { + check( + expr, + r#""foo""#, + &expect![[r#"Expr [0-5]: Lit: String("foo")"#]], + ); +} + +#[test] +fn lit_string_single_quote() { + check( + expr, + r#"'foo'"#, + &expect![[r#"Expr [0-5]: Lit: String("foo")"#]], + ); +} + +#[test] +fn lit_string_escape_quote() { + check( + expr, + r#""foo\"bar""#, + &expect![[r#"Expr [0-10]: Lit: String("foo\"bar")"#]], + ); +} + +#[test] +fn lit_string_single_quote_escape_double_quote() { + check( + expr, + r#"'foo\"bar'"#, + &expect![[r#"Expr [0-10]: Lit: String("foo\"bar")"#]], + ); +} + +#[test] +fn lit_string_escape_backslash() { + check( + expr, + r#""\\""#, + &expect![[r#"Expr [0-4]: Lit: String("\\")"#]], + ); +} + +#[test] +fn lit_string_single_quote_escape_backslash() { + check( + expr, + r#"'\\'"#, + &expect![[r#"Expr [0-4]: Lit: String("\\")"#]], + ); +} + +#[test] +fn lit_string_escape_newline() { + check( + expr, + r#""\n""#, + &expect![[r#"Expr [0-4]: Lit: String("\n")"#]], + ); +} + +#[test] +fn lit_string_single_quote_escape_newline() { + check( + expr, + r#"'\n'"#, + &expect![[r#"Expr [0-4]: Lit: String("\n")"#]], + ); +} + +#[test] +fn lit_string_escape_carriage_return() { + check( + expr, + r#""\r""#, + &expect![[r#"Expr [0-4]: Lit: String("\r")"#]], + ); +} + +#[test] +fn lit_string_single_quote_escape_carriage_return() { + check( + expr, + r#"'\r'"#, + &expect![[r#"Expr [0-4]: Lit: String("\r")"#]], + ); +} + +#[test] +fn lit_string_escape_tab() { + check( + expr, + r#""\t""#, + &expect![[r#"Expr [0-4]: Lit: String("\t")"#]], + ); +} + +#[test] +fn lit_string_single_quote_escape_tab() { + check( + expr, + r#"'\t'"#, + &expect![[r#"Expr [0-4]: Lit: String("\t")"#]], + ); +} + +#[test] +fn lit_string_unknown_escape() { + check( + expr, + r#""\x""#, + &expect![[r#" + Error( + Escape( + 'x', + Span { + lo: 2, + hi: 3, + }, + ), + ) + "#]], + ); +} + +#[test] +fn lit_string_unmatched_quote() { + check( + expr, + r#""Uh oh.."#, + &expect![[r#" + Error( + Rule( + "expression", + Eof, + Span { + lo: 8, + hi: 8, + }, + ), + ) + + [ + Error( + Lex( + UnterminatedString( + Span { + lo: 0, + hi: 0, + }, + ), + ), + ), + ]"#]], + ); +} + +#[test] +fn lit_string_empty() { + check(expr, r#""""#, &expect![[r#"Expr [0-2]: Lit: String("")"#]]); +} + +#[test] +fn lit_false() { + check(expr, "false", &expect!["Expr [0-5]: Lit: Bool(false)"]); +} + +#[test] +fn lit_true() { + check(expr, "true", &expect!["Expr [0-4]: Lit: Bool(true)"]); +} + +#[test] +fn lit_bitstring() { + check( + expr, + r#""101010101""#, + &expect![[r#"Expr [0-11]: Lit: Bitstring("101010101")"#]], + ); +} + +#[test] +fn lit_bitstring_preserves_leading_zeroes() { + check( + expr, + r#""00011000""#, + &expect![[r#"Expr [0-10]: Lit: Bitstring("00011000")"#]], + ); +} + +#[test] +fn lit_bitstring_separators() { + check( + expr, + r#""10_10_10_101""#, + &expect![[r#"Expr [0-14]: Lit: Bitstring("101010101")"#]], + ); +} + +#[test] +fn lit_bitstring_unmatched_quote() { + check( + expr, + r#""101010101"#, + &expect![[r#" + Error( + Rule( + "expression", + Eof, + Span { + lo: 10, + hi: 10, + }, + ), + ) + + [ + Error( + Lex( + UnterminatedString( + Span { + lo: 0, + hi: 0, + }, + ), + ), + ), + ]"#]], + ); +} + +#[test] +fn lit_float_imag() { + check( + expr, + r#"10.3im"#, + &expect!["Expr [0-6]: Lit: Imaginary(10.3)"], + ); +} + +#[test] +fn lit_float_imag_with_spacing() { + check( + expr, + r#"10.3 im"#, + &expect!["Expr [0-8]: Lit: Imaginary(10.3)"], + ); +} + +#[test] +fn lit_int_imag() { + check(expr, r#"10"#, &expect!["Expr [0-2]: Lit: Int(10)"]); +} + +#[test] +fn lit_int_imag_with_spacing() { + check( + expr, + r#"10 im"#, + &expect!["Expr [0-6]: Lit: Imaginary(10.0)"], + ); +} + +#[test] +fn lit_float_imag_leading_dot() { + check(expr, ".23im", &expect!["Expr [0-5]: Lit: Imaginary(0.23)"]); +} + +#[test] +fn lit_float_imag_trailing_dot() { + check(expr, "1.im", &expect!["Expr [0-4]: Lit: Imaginary(1.0)"]); +} + +#[test] +fn lit_float_imag_underscore() { + check( + expr, + "123_456.78im", + &expect!["Expr [0-12]: Lit: Imaginary(123456.78)"], + ); +} + +#[test] +fn lit_float_imag_leading_zero() { + check(expr, "0.23im", &expect!["Expr [0-6]: Lit: Imaginary(0.23)"]); +} diff --git a/compiler/qsc_qasm3/src/parser/prim/tests.rs b/compiler/qsc_qasm3/src/parser/prim/tests.rs index cacf228ebb..d21861f1cd 100644 --- a/compiler/qsc_qasm3/src/parser/prim/tests.rs +++ b/compiler/qsc_qasm3/src/parser/prim/tests.rs @@ -188,7 +188,7 @@ fn opt_fail_no_consume() { fn opt_fail_consume() { check_opt( |s| opt(s, path), - "Foo.#", + "Foo.$", &expect![[r#" Err IncompletePath [0-5]: Ident [0-3] "Foo" @@ -197,7 +197,7 @@ fn opt_fail_consume() { Error( Lex( Unknown( - '#', + '$', Span { lo: 4, hi: 5, diff --git a/compiler/qsc_qasm3/src/parser/stmt.rs b/compiler/qsc_qasm3/src/parser/stmt.rs index d5011eeefb..13620e342b 100644 --- a/compiler/qsc_qasm3/src/parser/stmt.rs +++ b/compiler/qsc_qasm3/src/parser/stmt.rs @@ -1,17 +1,23 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. +#[cfg(test)] +pub(crate) mod tests; + use qsc_data_structures::span::Span; use super::{ completion::WordKinds, error::{Error, ErrorKind}, - expr::{self}, - prim::{barrier, many, opt, recovering, recovering_semi, recovering_token}, + expr::{self, designator}, + prim::{self, barrier, many, opt, recovering, recovering_semi, recovering_token}, Result, }; use crate::{ - ast::{Annotation, Block, IncludeStmt, LiteralKind, PathKind, Pragma, Stmt, StmtKind}, + ast::{ + Annotation, Block, IncludeStmt, LiteralKind, PathKind, Pragma, QubitDeclaration, Stmt, + StmtKind, + }, lex::{cooked::Literal, Delim, TokenKind}, }; @@ -37,6 +43,8 @@ pub(super) fn parse(s: &mut ParserContext) -> Result> { } } else if let Some(v) = opt(s, parse_include)? { Box::new(v) + } else if let Some(decl) = opt(s, parse_quantum_decl)? { + Box::new(decl) } else { return Err(Error::new(ErrorKind::Rule( "statement", @@ -82,7 +90,7 @@ fn default(span: Span) -> Box { fn parse_annotation(s: &mut ParserContext) -> Result> { let lo = s.peek().span.lo; s.expect(WordKinds::Annotation); - token(s, TokenKind::At)?; + token(s, TokenKind::Annotation)?; // parse name // parse value recovering_semi(s); @@ -130,3 +138,18 @@ fn parse_pragma(s: &mut ParserContext) -> Result { value: None, }) } + +fn parse_quantum_decl(s: &mut ParserContext) -> Result { + let lo = s.peek().span.lo; + s.expect(WordKinds::Qubit); + token(s, TokenKind::Keyword(crate::keyword::Keyword::Qubit))?; + let size = opt(s, designator)?; + let name = prim::ident(s)?; + + recovering_semi(s); + Ok(StmtKind::QuantumDecl(QubitDeclaration { + span: s.span(lo), + qubit: *name, + size, + })) +} diff --git a/compiler/qsc_qasm3/src/parser/stmt/tests.rs b/compiler/qsc_qasm3/src/parser/stmt/tests.rs new file mode 100644 index 0000000000..a3fc5aa008 --- /dev/null +++ b/compiler/qsc_qasm3/src/parser/stmt/tests.rs @@ -0,0 +1,67 @@ +use expect_test::expect; + +use crate::parser::tests::check; + +use super::parse; + +#[test] +fn quantum_decl() { + check( + parse, + "qubit q;", + &expect![[r#" + Stmt [0-8] + StmtKind: QubitDeclaration [0-8]: Ident [6-7] "q""#]], + ); +} + +#[test] +fn quantum_decl_missing_name() { + check( + parse, + "qubit;", + &expect![[r#" + Error( + Rule( + "identifier", + Semicolon, + Span { + lo: 5, + hi: 6, + }, + ), + ) + "#]], + ); +} + +#[test] +fn quantum_decl_with_designator() { + check( + parse, + "qubit[5] qubits;", + &expect![[r#" + Stmt [0-16] + StmtKind: QubitDeclaration [0-16]: Ident [9-15] "qubits", ExprStmt [5-8]: Expr [6-7]: Lit: Int(5)"#]], + ); +} + +#[test] +fn quantum_decl_with_designator_missing_name() { + check( + parse, + "qubit[5]", + &expect![[r#" + Error( + Rule( + "identifier", + Eof, + Span { + lo: 8, + hi: 8, + }, + ), + ) + "#]], + ); +} From 0d049cdd2ddbb2e5e6a4f119a1f0aaf6db147a5e Mon Sep 17 00:00:00 2001 From: Ian Davis Date: Fri, 7 Feb 2025 12:52:29 -0800 Subject: [PATCH 004/108] Adding classical & io decls to the parser, stubbing out array decls (#2165) --- compiler/qsc_qasm3/src/ast.rs | 124 ++-- compiler/qsc_qasm3/src/lex/cooked.rs | 1 - .../qsc_qasm3/src/parser/completion/tests.rs | 12 +- compiler/qsc_qasm3/src/parser/expr.rs | 17 +- compiler/qsc_qasm3/src/parser/stmt.rs | 483 ++++++++++++- compiler/qsc_qasm3/src/parser/stmt/tests.rs | 75 +- .../src/parser/stmt/tests/annotation.rs | 49 ++ .../src/parser/stmt/tests/classical_decl.rs | 670 ++++++++++++++++++ .../src/parser/stmt/tests/io_decl.rs | 338 +++++++++ .../qsc_qasm3/src/parser/stmt/tests/pragma.rs | 114 +++ .../src/parser/stmt/tests/quantum_decl.rs | 102 +++ 11 files changed, 1840 insertions(+), 145 deletions(-) create mode 100644 compiler/qsc_qasm3/src/parser/stmt/tests/annotation.rs create mode 100644 compiler/qsc_qasm3/src/parser/stmt/tests/classical_decl.rs create mode 100644 compiler/qsc_qasm3/src/parser/stmt/tests/io_decl.rs create mode 100644 compiler/qsc_qasm3/src/parser/stmt/tests/pragma.rs create mode 100644 compiler/qsc_qasm3/src/parser/stmt/tests/quantum_decl.rs diff --git a/compiler/qsc_qasm3/src/ast.rs b/compiler/qsc_qasm3/src/ast.rs index 18fb0d2c91..7e2314e4e7 100644 --- a/compiler/qsc_qasm3/src/ast.rs +++ b/compiler/qsc_qasm3/src/ast.rs @@ -62,10 +62,11 @@ pub struct Stmt { impl Display for Stmt { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { let mut indent = set_indentation(indented(f), 0); + write!(indent, "Stmt {}", self.span)?; + indent = set_indentation(indent, 1); for annotation in &self.annotations { write!(indent, "\n{annotation}")?; } - write!(indent, "Stmt {}", self.span)?; write!(indent, "\n{}", self.kind)?; Ok(()) } @@ -74,15 +75,19 @@ impl Display for Stmt { #[derive(Clone, Debug)] pub struct Annotation { pub span: Span, - pub name: Box, + pub identifier: Rc, pub value: Option>, } impl Display for Annotation { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { if let Some(value) = &self.value { - write!(f, "Annotation {}: {}, {}", self.span, self.name, value) + write!( + f, + "Annotation {}: ({}, {})", + self.span, self.identifier, value + ) } else { - write!(f, "Annotation {}: {}", self.span, self.name) + write!(f, "Annotation {}: ({})", self.span, self.identifier) } } } @@ -712,7 +717,7 @@ impl Display for QuantumMeasurement { #[derive(Clone, Debug)] pub struct ClassicalArgument { pub span: Span, - pub r#type: ClassicalType, + pub r#type: ScalarType, pub name: Identifier, pub access: Option, } @@ -738,7 +743,7 @@ impl Display for ClassicalArgument { #[derive(Clone, Debug)] pub struct ExternArgument { pub span: Span, - pub r#type: ClassicalType, + pub r#type: ScalarType, pub access: Option, } @@ -757,46 +762,42 @@ impl Display for ExternArgument { } #[derive(Clone, Debug)] -pub struct ClassicalType { +pub struct ScalarType { pub span: Span, - pub kind: ClassicalTypeKind, + pub kind: ScalarTypeKind, } -impl Display for ClassicalType { +impl Display for ScalarType { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { write!(f, "ClassicalType {}: {}", self.span, self.kind) } } #[derive(Clone, Debug)] -pub enum ClassicalTypeKind { +pub enum ScalarTypeKind { + Bit(BitType), Int(IntType), UInt(UIntType), Float(FloatType), Complex(ComplexType), Angle(AngleType), - Bit(BitType), BoolType, - Array(ArrayType), - ArrayReference(ArrayReferenceType), Duration, Stretch, } -impl Display for ClassicalTypeKind { +impl Display for ScalarTypeKind { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { match self { - ClassicalTypeKind::Int(int) => write!(f, "ClassicalTypeKind {int}"), - ClassicalTypeKind::UInt(uint) => write!(f, "ClassicalTypeKind {uint}"), - ClassicalTypeKind::Float(float) => write!(f, "ClassicalTypeKind {float}"), - ClassicalTypeKind::Complex(complex) => write!(f, "ClassicalTypeKind {complex}"), - ClassicalTypeKind::Angle(angle) => write!(f, "ClassicalTypeKind {angle}"), - ClassicalTypeKind::Bit(bit) => write!(f, "ClassicalTypeKind {bit}"), - ClassicalTypeKind::BoolType => write!(f, "ClassicalTypeKind BoolType"), - ClassicalTypeKind::Array(array) => write!(f, "ClassicalTypeKind {array}"), - ClassicalTypeKind::ArrayReference(array) => write!(f, "ClassicalTypeKind {array}"), - ClassicalTypeKind::Duration => write!(f, "ClassicalTypeKind Duration"), - ClassicalTypeKind::Stretch => write!(f, "ClassicalTypeKind Stretch"), + ScalarTypeKind::Int(int) => write!(f, "{int}"), + ScalarTypeKind::UInt(uint) => write!(f, "{uint}"), + ScalarTypeKind::Float(float) => write!(f, "{float}"), + ScalarTypeKind::Complex(complex) => write!(f, "{complex}"), + ScalarTypeKind::Angle(angle) => write!(f, "{angle}"), + ScalarTypeKind::Bit(bit) => write!(f, "{bit}"), + ScalarTypeKind::BoolType => write!(f, "BoolType"), + ScalarTypeKind::Duration => write!(f, "Duration"), + ScalarTypeKind::Stretch => write!(f, "Stretch"), } } } @@ -808,8 +809,8 @@ pub enum ArrayBaseTypeKind { Float(FloatType), Complex(ComplexType), Angle(AngleType), - Bit(BitType), BoolType, + Duration, } impl Display for ArrayBaseTypeKind { @@ -820,7 +821,7 @@ impl Display for ArrayBaseTypeKind { ArrayBaseTypeKind::Float(float) => write!(f, "ArrayBaseTypeKind {float}"), ArrayBaseTypeKind::Complex(complex) => write!(f, "ArrayBaseTypeKind {complex}"), ArrayBaseTypeKind::Angle(angle) => write!(f, "ArrayBaseTypeKind {angle}"), - ArrayBaseTypeKind::Bit(bit) => write!(f, "ArrayBaseTypeKind {bit}"), + ArrayBaseTypeKind::Duration => write!(f, "ArrayBaseTypeKind DurationType"), ArrayBaseTypeKind::BoolType => write!(f, "ArrayBaseTypeKind BoolType"), } } @@ -835,9 +836,9 @@ pub struct IntType { impl Display for IntType { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { if let Some(size) = &self.size { - write!(f, "IntType {}: {}", self.span, size) + write!(f, "IntType[{}]: {}", size, self.span) } else { - write!(f, "IntType") + write!(f, "IntType {}", self.span) } } } @@ -851,9 +852,9 @@ pub struct UIntType { impl Display for UIntType { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { if let Some(size) = &self.size { - write!(f, "UIntType {}: {}", self.span, size) + write!(f, "UIntType[{}]: {}", size, self.span) } else { - write!(f, "UIntType") + write!(f, "UIntType {}", self.span) } } } @@ -867,9 +868,9 @@ pub struct FloatType { impl Display for FloatType { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { if let Some(size) = &self.size { - write!(f, "FloatType {}: {}", self.span, size) + write!(f, "FloatType[{}]: {}", size, self.span) } else { - write!(f, "FloatType") + write!(f, "FloatType {}", self.span) } } } @@ -883,9 +884,9 @@ pub struct ComplexType { impl Display for ComplexType { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { if let Some(size) = &self.base_size { - write!(f, "ComplexType {}: {}", self.span, size) + write!(f, "ComplexType[float[{}]]: {}", size, self.span) } else { - write!(f, "ComplexType") + write!(f, "ComplexType {}", self.span) } } } @@ -901,7 +902,7 @@ impl Display for AngleType { if let Some(size) = &self.size { write!(f, "AngleType {}: {}", self.span, size) } else { - write!(f, "AngleType") + write!(f, "AngleType {}", self.span) } } } @@ -922,11 +923,28 @@ impl Display for BitType { } } +#[derive(Clone, Debug)] +pub enum TypeDef { + Scalar(ScalarType), + Array(ArrayType), + ArrayReference(ArrayReferenceType), +} + +impl Display for TypeDef { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + match self { + TypeDef::Scalar(scalar) => write!(f, "{scalar}"), + TypeDef::Array(array) => write!(f, "{array}"), + TypeDef::ArrayReference(array) => write!(f, "{array}"), + } + } +} + #[derive(Clone, Debug)] pub struct ArrayType { pub span: Span, pub base_type: ArrayBaseTypeKind, - pub dimensions: List, + pub dimensions: List, } impl Display for ArrayType { @@ -992,16 +1010,16 @@ impl Display for QuantumArgument { #[derive(Clone, Debug)] pub struct Pragma { pub span: Span, - pub name: Box, + pub identifier: Rc, pub value: Option>, } impl Display for Pragma { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { if let Some(value) = &self.value { - write!(f, "Pragma {}: {}, {}", self.span, self.name, value) + write!(f, "Pragma {}: ({}, {})", self.span, self.identifier, value) } else { - write!(f, "Pragma {}: {}", self.span, self.name) + write!(f, "Pragma {}: ({})", self.span, self.identifier) } } } @@ -1087,7 +1105,7 @@ pub struct ExternDecl { pub span: Span, pub name: Identifier, pub arguments: List, - pub return_type: Option, + pub return_type: Option, } impl Display for ExternDecl { @@ -1245,8 +1263,8 @@ impl Display for MeasureStmt { #[derive(Clone, Debug)] pub struct ClassicalDeclarationStmt { pub span: Span, - pub r#type: ClassicalType, - pub identifier: Identifier, + pub r#type: TypeDef, + pub identifier: Box, pub init_expr: Option>, } @@ -1287,8 +1305,8 @@ impl Display for ValueExpression { pub struct IODeclaration { pub span: Span, pub io_identifier: IOKeyword, - pub r#type: ClassicalType, - pub identifier: Identifier, + pub r#type: TypeDef, + pub identifier: Box, } impl Display for IODeclaration { @@ -1303,10 +1321,10 @@ impl Display for IODeclaration { #[derive(Clone, Debug)] pub struct ConstantDeclaration { - span: Span, - r#type: ClassicalType, - identifier: Identifier, - init_expr: ExprStmt, + pub span: Span, + pub r#type: TypeDef, + pub identifier: Box, + pub init_expr: Box, } impl Display for ConstantDeclaration { @@ -1353,7 +1371,7 @@ pub struct CalibrationDefinition { name: Identifier, args: List, qubits: List, - return_type: Option, + return_type: Option, body: String, } @@ -1395,7 +1413,7 @@ pub struct DefStmt { name: Identifier, args: List>, body: List>, - return_type: Option, + return_type: Option, } impl Display for DefStmt { @@ -1489,7 +1507,7 @@ impl Display for WhileLoop { #[derive(Clone, Debug)] pub struct ForStmt { span: Span, - r#type: ClassicalType, + r#type: ScalarType, identifier: Identifier, set_declaration: Box, block: List, @@ -1647,7 +1665,7 @@ impl Display for FunctionCall { #[derive(Clone, Debug)] pub struct Cast { pub span: Span, - pub r#type: ClassicalType, + pub r#type: ScalarType, pub arg: ExprStmt, } diff --git a/compiler/qsc_qasm3/src/lex/cooked.rs b/compiler/qsc_qasm3/src/lex/cooked.rs index c4bfe12c28..d4f83c2c94 100644 --- a/compiler/qsc_qasm3/src/lex/cooked.rs +++ b/compiler/qsc_qasm3/src/lex/cooked.rs @@ -477,7 +477,6 @@ impl<'a> Lexer<'a> { fn eat_to_end_of_line(&mut self) { self.eat_while(|t| t != raw::TokenKind::Newline); - self.next_if_eq(raw::TokenKind::Newline); } /// Consumes a list of tokens zero or more times. diff --git a/compiler/qsc_qasm3/src/parser/completion/tests.rs b/compiler/qsc_qasm3/src/parser/completion/tests.rs index dddafe1a79..127c972cd1 100644 --- a/compiler/qsc_qasm3/src/parser/completion/tests.rs +++ b/compiler/qsc_qasm3/src/parser/completion/tests.rs @@ -34,11 +34,11 @@ fn check_valid_words_no_source_name(input: &str, expect: &expect_test::Expect) { fn begin_document() { check_valid_words( "|OPENQASM 3;", - &expect![[r" + &expect![[r#" WordKinds( - Annotation | Include | OpenQASM | Pragma | Qubit, + Annotation | Include | Input | OpenQASM | Output | Pragma | Qubit, ) - "]], + "#]], ); } @@ -46,10 +46,10 @@ fn begin_document() { fn end_of_version() { check_valid_words( "OPENQASM 3;|", - &expect![[r" + &expect![[r#" WordKinds( - Annotation | Include | Pragma | Qubit, + Annotation | Include | Input | Output | Pragma | Qubit, ) - "]], + "#]], ); } diff --git a/compiler/qsc_qasm3/src/parser/expr.rs b/compiler/qsc_qasm3/src/parser/expr.rs index 489211fecd..7bc73665a6 100644 --- a/compiler/qsc_qasm3/src/parser/expr.rs +++ b/compiler/qsc_qasm3/src/parser/expr.rs @@ -15,7 +15,7 @@ use num_traits::Num; use qsc_data_structures::span::Span; use crate::{ - ast::{BinOp, Expr, ExprKind, ExprStmt, Lit, LiteralKind, UnOp, Version}, + ast::{BinOp, Expr, ExprKind, ExprStmt, Lit, LiteralKind, UnOp, ValueExpression, Version}, keyword::Keyword, lex::{cooked::Literal, ClosedBinOp, Delim, Radix, Token, TokenKind}, parser::{ @@ -385,3 +385,18 @@ pub(super) fn designator(s: &mut ParserContext) -> Result { expr, }) } + +pub(super) fn value_expr(s: &mut ParserContext) -> Result> { + let lo = s.peek().span.lo; + let expr = expr_stmt(s)?; + let stmt = ExprStmt { + span: s.span(lo), + expr, + }; + // todo: measurement + Ok(Box::new(ValueExpression::Expr(stmt))) +} + +pub(crate) fn expr_list(_s: &mut ParserContext<'_>) -> Result> { + todo!("expr_list") +} diff --git a/compiler/qsc_qasm3/src/parser/stmt.rs b/compiler/qsc_qasm3/src/parser/stmt.rs index 13620e342b..1de4ac2533 100644 --- a/compiler/qsc_qasm3/src/parser/stmt.rs +++ b/compiler/qsc_qasm3/src/parser/stmt.rs @@ -4,21 +4,28 @@ #[cfg(test)] pub(crate) mod tests; +use std::rc::Rc; + use qsc_data_structures::span::Span; use super::{ completion::WordKinds, error::{Error, ErrorKind}, expr::{self, designator}, - prim::{self, barrier, many, opt, recovering, recovering_semi, recovering_token}, + prim::{self, barrier, many, opt, recovering, recovering_semi, recovering_token, shorten}, Result, }; use crate::{ ast::{ - Annotation, Block, IncludeStmt, LiteralKind, PathKind, Pragma, QubitDeclaration, Stmt, - StmtKind, + AngleType, Annotation, ArrayBaseTypeKind, ArrayType, BitType, Block, + ClassicalDeclarationStmt, ComplexType, ConstantDeclaration, ExprStmt, FloatType, + IODeclaration, IOKeyword, IncludeStmt, IntType, LiteralKind, Pragma, QubitDeclaration, + ScalarType, ScalarTypeKind, Stmt, StmtKind, TypeDef, UIntType, + }, + lex::{ + cooked::{Literal, Type}, + Delim, TokenKind, }, - lex::{cooked::Literal, Delim, TokenKind}, }; use super::{prim::token, ParserContext}; @@ -43,7 +50,7 @@ pub(super) fn parse(s: &mut ParserContext) -> Result> { } } else if let Some(v) = opt(s, parse_include)? { Box::new(v) - } else if let Some(decl) = opt(s, parse_quantum_decl)? { + } else if let Some(decl) = opt(s, parse_local)? { Box::new(decl) } else { return Err(Error::new(ErrorKind::Rule( @@ -87,17 +94,54 @@ fn default(span: Span) -> Box { }) } -fn parse_annotation(s: &mut ParserContext) -> Result> { +pub fn parse_annotation(s: &mut ParserContext) -> Result> { let lo = s.peek().span.lo; s.expect(WordKinds::Annotation); - token(s, TokenKind::Annotation)?; - // parse name - // parse value - recovering_semi(s); + + let token = s.peek(); + let pat = &['\t', ' ']; + let parts: Vec<&str> = if token.kind == TokenKind::Annotation { + let lexeme = s.read(); + s.advance(); + // remove @ + // split lexeme at first space/tab collecting each side + shorten(1, 0, lexeme).splitn(2, pat).collect() + } else { + return Err(Error::new(ErrorKind::Rule( + "annotation", + token.kind, + token.span, + ))); + }; + + let identifier = parts.first().map_or_else( + || { + Err(Error::new(ErrorKind::Rule( + "annotation", + token.kind, + token.span, + ))) + }, + |s| Ok(Into::>::into(*s)), + )?; + + if identifier.is_empty() { + s.push_error(Error::new(ErrorKind::Rule( + "annotation missing identifier", + token.kind, + token.span, + ))); + } + + // remove any leading whitespace from the value side + let value = parts + .get(1) + .map(|s| Into::>::into(s.trim_start_matches(pat))); + Ok(Box::new(Annotation { span: s.span(lo), - name: Box::new(PathKind::default()), - value: None, + identifier, + value, })) } @@ -128,17 +172,66 @@ fn parse_include(s: &mut ParserContext) -> Result { fn parse_pragma(s: &mut ParserContext) -> Result { let lo = s.peek().span.lo; s.expect(WordKinds::Pragma); - token(s, TokenKind::Keyword(crate::keyword::Keyword::Pragma))?; - // parse name - // parse value + + let token = s.peek(); + + let parts: Vec<&str> = if token.kind == TokenKind::Pragma { + let lexeme = s.read(); + s.advance(); + // remove pragma keyword and any leading whitespace + // split lexeme at first space/tab collecting each side + let pat = &['\t', ' ']; + shorten(6, 0, lexeme) + .trim_start_matches(pat) + .splitn(2, pat) + .collect() + } else { + return Err(Error::new(ErrorKind::Rule( + "pragma", token.kind, token.span, + ))); + }; + + let identifier = parts.first().map_or_else( + || { + Err(Error::new(ErrorKind::Rule( + "pragma", token.kind, token.span, + ))) + }, + |s| Ok(Into::>::into(*s)), + )?; + + if identifier.is_empty() { + s.push_error(Error::new(ErrorKind::Rule( + "pragma missing identifier", + token.kind, + token.span, + ))); + } + let value = parts.get(1).map(|s| Into::>::into(*s)); Ok(Pragma { span: s.span(lo), - name: Box::new(PathKind::default()), - value: None, + identifier, + value, }) } +fn parse_local(s: &mut ParserContext) -> Result { + if let Some(decl) = opt(s, parse_classical_decl)? { + Ok(decl) + } else if let Some(decl) = opt(s, parse_quantum_decl)? { + Ok(decl) + } else if let Some(decl) = opt(s, parse_io_decl)? { + Ok(decl) + } else { + Err(Error::new(ErrorKind::Rule( + "local declaration", + s.peek().kind, + s.peek().span, + ))) + } +} + fn parse_quantum_decl(s: &mut ParserContext) -> Result { let lo = s.peek().span.lo; s.expect(WordKinds::Qubit); @@ -153,3 +246,359 @@ fn parse_quantum_decl(s: &mut ParserContext) -> Result { size, })) } + +fn parse_io_decl(s: &mut ParserContext) -> Result { + let lo = s.peek().span.lo; + + let kind = if token(s, TokenKind::Keyword(crate::keyword::Keyword::Input)).is_ok() { + IOKeyword::Input + } else if token(s, TokenKind::Keyword(crate::keyword::Keyword::Output)).is_ok() { + IOKeyword::Output + } else { + let token = s.peek(); + return Err(Error::new(ErrorKind::Rule( + "io declaration", + token.kind, + token.span, + ))); + }; + + let ty = scalar_or_array_type(s)?; + + let identifier = prim::ident(s)?; + recovering_semi(s); + let decl = IODeclaration { + span: s.span(lo), + io_identifier: kind, + r#type: ty, + identifier, + }; + Ok(StmtKind::IODeclaration(decl)) +} + +fn scalar_or_array_type(s: &mut ParserContext) -> Result { + if let Ok(v) = scalar_type(s) { + return Ok(TypeDef::Scalar(v)); + } + if let Ok(v) = array_type(s) { + return Ok(TypeDef::Array(v)); + } + Err(Error::new(ErrorKind::Rule( + "scalar or array type", + s.peek().kind, + s.peek().span, + ))) +} + +fn parse_classical_decl(s: &mut ParserContext) -> Result { + let lo = s.peek().span.lo; + let is_const = if s.peek().kind == TokenKind::Keyword(crate::keyword::Keyword::Const) { + s.advance(); + true + } else { + false + }; + let ty = scalar_or_array_type(s)?; + + let identifier = prim::ident(s)?; + + let stmt = if is_const { + token(s, TokenKind::Eq)?; + let init_expr = expr::expr(s)?; + recovering_semi(s); + let decl = ConstantDeclaration { + span: s.span(lo), + r#type: ty, + identifier, + init_expr: Box::new(ExprStmt { + span: init_expr.span, + expr: init_expr, + }), + }; + StmtKind::ConstDecl(decl) + } else { + let init_expr = if s.peek().kind == TokenKind::Eq { + s.advance(); + Some(expr::value_expr(s)?) + } else { + None + }; + recovering_semi(s); + let decl = ClassicalDeclarationStmt { + span: s.span(lo), + r#type: ty, + identifier, + init_expr, + }; + StmtKind::ClassicalDecl(decl) + }; + + Ok(stmt) +} + +pub(super) fn array_type(s: &mut ParserContext) -> Result { + let lo = s.peek().span.lo; + token(s, TokenKind::Type(Type::Array))?; + token(s, TokenKind::Open(Delim::Bracket))?; + let kind = array_base_type(s)?; + token(s, TokenKind::Comma)?; + let expr_list = expr::expr_list(s)?; + token(s, TokenKind::Close(Delim::Bracket))?; + + Ok(ArrayType { + base_type: kind, + span: s.span(lo), + dimensions: expr_list + .into_iter() + .map(Box::new) + .collect::>() + .into_boxed_slice(), + }) +} + +pub(super) fn array_base_type(s: &mut ParserContext) -> Result { + if let Ok(v) = array_angle_type(s) { + return Ok(v); + } + if let Ok(v) = array_bool_type(s) { + return Ok(v); + } + if let Ok(v) = array_int_type(s) { + return Ok(v); + } + if let Ok(v) = array_uint_type(s) { + return Ok(v); + } + if let Ok(v) = array_float_type(s) { + return Ok(v); + } + if let Ok(v) = array_complex_type(s) { + return Ok(v); + } + if let Ok(v) = array_duration_type(s) { + return Ok(v); + } + + Err(Error::new(ErrorKind::Rule( + "array type", + s.peek().kind, + s.peek().span, + ))) +} + +pub(super) fn scalar_type(s: &mut ParserContext) -> Result { + if let Ok(v) = scalar_bit_type(s) { + return Ok(v); + } + if let Ok(v) = scalar_angle_type(s) { + return Ok(v); + } + if let Ok(v) = scalar_bool_type(s) { + return Ok(v); + } + if let Ok(v) = scalar_int_type(s) { + return Ok(v); + } + if let Ok(v) = scalar_uint_type(s) { + return Ok(v); + } + if let Ok(v) = scalar_float_type(s) { + return Ok(v); + } + if let Ok(v) = scalar_complex_type(s) { + return Ok(v); + } + if let Ok(v) = scalar_duration_type(s) { + return Ok(v); + } + if let Ok(v) = scalar_stretch_type(s) { + return Ok(v); + } + Err(Error::new(ErrorKind::Rule( + "scalar type", + s.peek().kind, + s.peek().span, + ))) +} + +fn scalar_bit_type(s: &mut ParserContext) -> Result { + let lo = s.peek().span.lo; + token(s, TokenKind::Type(Type::Bit))?; + let size = opt(s, designator)?; + Ok(ScalarType { + span: s.span(lo), + kind: ScalarTypeKind::Bit(BitType { + size, + span: s.span(lo), + }), + }) +} + +fn scalar_int_type(s: &mut ParserContext) -> Result { + let lo = s.peek().span.lo; + let ty = int_type(s)?; + Ok(ScalarType { + span: s.span(lo), + kind: ScalarTypeKind::Int(ty), + }) +} + +fn array_int_type(s: &mut ParserContext) -> Result { + token(s, TokenKind::Type(Type::Int))?; + let ty = int_type(s)?; + Ok(ArrayBaseTypeKind::Int(ty)) +} + +fn int_type(s: &mut ParserContext) -> Result { + let lo = s.peek().span.lo; + token(s, TokenKind::Type(Type::Int))?; + let size = opt(s, designator)?; + Ok(IntType { + size, + span: s.span(lo), + }) +} +fn scalar_uint_type(s: &mut ParserContext) -> Result { + let lo = s.peek().span.lo; + let ty = uint_type(s)?; + Ok(ScalarType { + span: s.span(lo), + kind: ScalarTypeKind::UInt(ty), + }) +} + +fn array_uint_type(s: &mut ParserContext) -> Result { + token(s, TokenKind::Type(Type::UInt))?; + let ty = uint_type(s)?; + Ok(ArrayBaseTypeKind::UInt(ty)) +} + +fn uint_type(s: &mut ParserContext) -> Result { + let lo = s.peek().span.lo; + token(s, TokenKind::Type(Type::UInt))?; + let size = opt(s, designator)?; + Ok(UIntType { + size, + span: s.span(lo), + }) +} + +fn scalar_float_type(s: &mut ParserContext) -> Result { + let lo = s.peek().span.lo; + let ty = float_type(s)?; + Ok(ScalarType { + span: s.span(lo), + kind: ScalarTypeKind::Float(ty), + }) +} + +fn array_float_type(s: &mut ParserContext) -> Result { + token(s, TokenKind::Type(Type::Float))?; + let ty = float_type(s)?; + Ok(ArrayBaseTypeKind::Float(ty)) +} + +fn float_type(s: &mut ParserContext) -> Result { + let lo = s.peek().span.lo; + token(s, TokenKind::Type(Type::Float))?; + let size = opt(s, designator)?; + Ok(FloatType { + span: s.span(lo), + size, + }) +} + +fn scalar_angle_type(s: &mut ParserContext) -> Result { + let lo = s.peek().span.lo; + let ty = angle_type(s)?; + Ok(ScalarType { + span: s.span(lo), + kind: ScalarTypeKind::Angle(ty), + }) +} + +fn array_angle_type(s: &mut ParserContext) -> Result { + token(s, TokenKind::Type(Type::Angle))?; + let ty = angle_type(s)?; + Ok(ArrayBaseTypeKind::Angle(ty)) +} + +fn angle_type(s: &mut ParserContext) -> Result { + let lo = s.peek().span.lo; + token(s, TokenKind::Type(Type::Angle))?; + let size = opt(s, designator)?; + Ok(AngleType { + size, + span: s.span(lo), + }) +} + +fn scalar_bool_type(s: &mut ParserContext) -> Result { + let lo = s.peek().span.lo; + token(s, TokenKind::Type(Type::Bool))?; + Ok(ScalarType { + span: s.span(lo), + kind: ScalarTypeKind::BoolType, + }) +} + +fn array_bool_type(s: &mut ParserContext) -> Result { + token(s, TokenKind::Type(Type::Bool))?; + Ok(ArrayBaseTypeKind::BoolType) +} + +fn scalar_duration_type(s: &mut ParserContext) -> Result { + let lo = s.peek().span.lo; + token(s, TokenKind::Type(Type::Duration))?; + Ok(ScalarType { + span: s.span(lo), + kind: ScalarTypeKind::Duration, + }) +} + +fn array_duration_type(s: &mut ParserContext) -> Result { + token(s, TokenKind::Type(Type::Duration))?; + Ok(ArrayBaseTypeKind::Duration) +} + +fn scalar_stretch_type(s: &mut ParserContext) -> Result { + let lo = s.peek().span.lo; + token(s, TokenKind::Type(Type::Stretch))?; + Ok(ScalarType { + span: s.span(lo), + kind: ScalarTypeKind::Stretch, + }) +} + +pub(super) fn scalar_complex_type(s: &mut ParserContext) -> Result { + let lo = s.peek().span.lo; + + let ty = complex_type(s)?; + Ok(ScalarType { + span: s.span(lo), + kind: ScalarTypeKind::Complex(ty), + }) +} + +pub(super) fn array_complex_type(s: &mut ParserContext) -> Result { + let ty = complex_type(s)?; + Ok(ArrayBaseTypeKind::Complex(ty)) +} + +pub(super) fn complex_type(s: &mut ParserContext) -> Result { + let lo = s.peek().span.lo; + token(s, TokenKind::Type(Type::Complex))?; + + let subty = opt(s, complex_subtype)?; + Ok(ComplexType { + base_size: subty, + span: s.span(lo), + }) +} + +pub(super) fn complex_subtype(s: &mut ParserContext) -> Result { + token(s, TokenKind::Open(Delim::Bracket))?; + let ty = float_type(s)?; + token(s, TokenKind::Close(Delim::Bracket))?; + Ok(ty) +} diff --git a/compiler/qsc_qasm3/src/parser/stmt/tests.rs b/compiler/qsc_qasm3/src/parser/stmt/tests.rs index a3fc5aa008..fed929c480 100644 --- a/compiler/qsc_qasm3/src/parser/stmt/tests.rs +++ b/compiler/qsc_qasm3/src/parser/stmt/tests.rs @@ -1,67 +1,8 @@ -use expect_test::expect; - -use crate::parser::tests::check; - -use super::parse; - -#[test] -fn quantum_decl() { - check( - parse, - "qubit q;", - &expect![[r#" - Stmt [0-8] - StmtKind: QubitDeclaration [0-8]: Ident [6-7] "q""#]], - ); -} - -#[test] -fn quantum_decl_missing_name() { - check( - parse, - "qubit;", - &expect![[r#" - Error( - Rule( - "identifier", - Semicolon, - Span { - lo: 5, - hi: 6, - }, - ), - ) - "#]], - ); -} - -#[test] -fn quantum_decl_with_designator() { - check( - parse, - "qubit[5] qubits;", - &expect![[r#" - Stmt [0-16] - StmtKind: QubitDeclaration [0-16]: Ident [9-15] "qubits", ExprStmt [5-8]: Expr [6-7]: Lit: Int(5)"#]], - ); -} - -#[test] -fn quantum_decl_with_designator_missing_name() { - check( - parse, - "qubit[5]", - &expect![[r#" - Error( - Rule( - "identifier", - Eof, - Span { - lo: 8, - hi: 8, - }, - ), - ) - "#]], - ); -} +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +mod annotation; +mod classical_decl; +mod io_decl; +mod pragma; +mod quantum_decl; diff --git a/compiler/qsc_qasm3/src/parser/stmt/tests/annotation.rs b/compiler/qsc_qasm3/src/parser/stmt/tests/annotation.rs new file mode 100644 index 0000000000..d2f5eb4942 --- /dev/null +++ b/compiler/qsc_qasm3/src/parser/stmt/tests/annotation.rs @@ -0,0 +1,49 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +use expect_test::expect; + +use crate::parser::tests::check; + +use crate::parser::stmt::parse_annotation; + +#[test] +fn annotation() { + check( + parse_annotation, + "@a.b.d 23", + &expect!["Annotation [0-9]: (a.b.d, 23)"], + ); +} + +#[test] +fn annotation_ident_only() { + check( + parse_annotation, + "@a.b.d", + &expect!["Annotation [0-6]: (a.b.d)"], + ); +} + +#[test] +fn annotation_missing_ident() { + check( + parse_annotation, + "@", + &expect![[r#" + Annotation [0-1]: () + + [ + Error( + Rule( + "annotation missing identifier", + Annotation, + Span { + lo: 0, + hi: 1, + }, + ), + ), + ]"#]], + ); +} diff --git a/compiler/qsc_qasm3/src/parser/stmt/tests/classical_decl.rs b/compiler/qsc_qasm3/src/parser/stmt/tests/classical_decl.rs new file mode 100644 index 0000000000..9fb8775554 --- /dev/null +++ b/compiler/qsc_qasm3/src/parser/stmt/tests/classical_decl.rs @@ -0,0 +1,670 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +use expect_test::expect; + +use crate::parser::tests::check; + +use crate::parser::stmt::parse; + +#[test] +fn bit_decl() { + check( + parse, + "bit b;", + &expect![[r#" + Stmt [0-6] + StmtKind: ClassicalDeclarationStmt [0-6]: ClassicalType [0-3]: BitType, Ident [4-5] "b""#]], + ); +} + +#[test] +fn bit_decl_bit_lit() { + check( + parse, + "bit b = 1;", + &expect![[r#" + Stmt [0-10] + StmtKind: ClassicalDeclarationStmt [0-10]: ClassicalType [0-3]: BitType, Ident [4-5] "b", ValueExpression ExprStmt [8-9]: Expr [8-9]: Lit: Int(1)"#]], + ); +} + +#[test] +fn const_bit_decl_bit_lit() { + check( + parse, + "const bit b = 1;", + &expect![[r#" + Stmt [0-16] + StmtKind: ConstantDeclaration [0-16]: ClassicalType [6-9]: BitType, Ident [10-11] "b", ExprStmt [14-15]: Expr [14-15]: Lit: Int(1)"#]], + ); +} + +#[test] +fn bit_array_decl() { + check( + parse, + "bit[2] b;", + &expect![[r#" + Stmt [0-9] + StmtKind: ClassicalDeclarationStmt [0-9]: ClassicalType [0-6]: BitType [0-6]: ExprStmt [3-6]: Expr [4-5]: Lit: Int(2), Ident [7-8] "b""#]], + ); +} + +#[test] +fn bit_array_decl_bit_lit() { + check( + parse, + "bit[2] b = \"11\";", + &expect![[r#" + Stmt [0-16] + StmtKind: ClassicalDeclarationStmt [0-16]: ClassicalType [0-6]: BitType [0-6]: ExprStmt [3-6]: Expr [4-5]: Lit: Int(2), Ident [7-8] "b", ValueExpression ExprStmt [11-15]: Expr [11-15]: Lit: Bitstring("11")"#]], + ); +} + +#[test] +fn const_bit_array_decl_bit_lit() { + check( + parse, + "const bit[2] b = \"11\";", + &expect![[r#" + Stmt [0-22] + StmtKind: ConstantDeclaration [0-22]: ClassicalType [6-12]: BitType [6-12]: ExprStmt [9-12]: Expr [10-11]: Lit: Int(2), Ident [13-14] "b", ExprStmt [17-21]: Expr [17-21]: Lit: Bitstring("11")"#]], + ); +} + +#[test] +fn bool_decl() { + check( + parse, + "bool b;", + &expect![[r#" + Stmt [0-7] + StmtKind: ClassicalDeclarationStmt [0-7]: ClassicalType [0-4]: BoolType, Ident [5-6] "b""#]], + ); +} + +#[test] +fn bool_decl_int_lit() { + check( + parse, + "bool b = 1;", + &expect![[r#" + Stmt [0-11] + StmtKind: ClassicalDeclarationStmt [0-11]: ClassicalType [0-4]: BoolType, Ident [5-6] "b", ValueExpression ExprStmt [9-10]: Expr [9-10]: Lit: Int(1)"#]], + ); +} + +#[test] +fn const_bool_decl_bool_lit() { + check( + parse, + "const bool b = true;", + &expect![[r#" + Stmt [0-20] + StmtKind: ConstantDeclaration [0-20]: ClassicalType [6-10]: BoolType, Ident [11-12] "b", ExprStmt [15-19]: Expr [15-19]: Lit: Bool(true)"#]], + ); +} + +#[test] +fn complex_decl() { + check( + parse, + "complex c;", + &expect![[r#" + Stmt [0-10] + StmtKind: ClassicalDeclarationStmt [0-10]: ClassicalType [0-7]: ComplexType [0-7], Ident [8-9] "c""#]], + ); +} + +#[test] +fn complex_decl_complex_lit() { + check( + parse, + "complex c = 1im;", + &expect![[r#" + Stmt [0-16] + StmtKind: ClassicalDeclarationStmt [0-16]: ClassicalType [0-7]: ComplexType [0-7], Ident [8-9] "c", ValueExpression ExprStmt [12-15]: Expr [12-15]: Lit: Imaginary(1.0)"#]], + ); +} + +#[test] +fn const_complex_decl_complex_lit() { + check( + parse, + "const complex c = 1im;", + &expect![[r#" + Stmt [0-22] + StmtKind: ConstantDeclaration [0-22]: ClassicalType [6-13]: ComplexType [6-13], Ident [14-15] "c", ExprStmt [18-21]: Expr [18-21]: Lit: Imaginary(1.0)"#]], + ); +} + +#[test] +#[ignore = "need binary operator support for const complex number exprs"] +fn const_complex_decl_complex_binary_lit() { + check( + parse, + "const complex c = 23.5 + 1.7im;", + &expect![[r#" + Stmt [0-22] + StmtKind: ConstantDeclaration [0-22]: ClassicalType [6-13]: ComplexType [6-13], Ident [14-15] "c", ExprStmt [18-22]: Expr [18-22]: Lit: Float(23.5) + + [ + Error( + Token( + Semicolon, + ClosedBinOp( + Plus, + ), + Span { + lo: 23, + hi: 24, + }, + ), + ), + ]"#]], + ); +} + +#[test] +fn complex_sized_decl() { + check( + parse, + "complex[float[32]] c;", + &expect![[r#" + Stmt [0-21] + StmtKind: ClassicalDeclarationStmt [0-21]: ClassicalType [0-18]: ComplexType[float[FloatType[ExprStmt [13-17]: Expr [14-16]: Lit: Int(32)]: [8-17]]]: [0-18], Ident [19-20] "c""#]], + ); +} + +#[test] +fn complex_sized_non_float_subty_decl() { + check( + parse, + "complex[int[32]] c;", + &expect![[r#" + Error( + Rule( + "scalar or array type", + Type( + Int, + ), + Span { + lo: 8, + hi: 11, + }, + ), + ) + "#]], + ); +} + +#[test] +fn complex_sized_decl_complex_lit() { + check( + parse, + "complex[float[32]] c = 1im;", + &expect![[r#" + Stmt [0-27] + StmtKind: ClassicalDeclarationStmt [0-27]: ClassicalType [0-18]: ComplexType[float[FloatType[ExprStmt [13-17]: Expr [14-16]: Lit: Int(32)]: [8-17]]]: [0-18], Ident [19-20] "c", ValueExpression ExprStmt [23-26]: Expr [23-26]: Lit: Imaginary(1.0)"#]], + ); +} + +#[test] +fn const_complex_sized_decl_complex_lit() { + check( + parse, + "const complex[float[32]] c = 1im;", + &expect![[r#" + Stmt [0-33] + StmtKind: ConstantDeclaration [0-33]: ClassicalType [6-24]: ComplexType[float[FloatType[ExprStmt [19-23]: Expr [20-22]: Lit: Int(32)]: [14-23]]]: [6-24], Ident [25-26] "c", ExprStmt [29-32]: Expr [29-32]: Lit: Imaginary(1.0)"#]], + ); +} + +#[test] +fn int_decl() { + check( + parse, + "int i;", + &expect![[r#" + Stmt [0-6] + StmtKind: ClassicalDeclarationStmt [0-6]: ClassicalType [0-3]: IntType [0-3], Ident [4-5] "i""#]], + ); +} + +#[test] +fn int_decl_int_lit() { + check( + parse, + "int i = 1;", + &expect![[r#" + Stmt [0-10] + StmtKind: ClassicalDeclarationStmt [0-10]: ClassicalType [0-3]: IntType [0-3], Ident [4-5] "i", ValueExpression ExprStmt [8-9]: Expr [8-9]: Lit: Int(1)"#]], + ); +} + +#[test] +fn const_int_decl_int_lit() { + check( + parse, + "const int i = 1;", + &expect![[r#" + Stmt [0-16] + StmtKind: ConstantDeclaration [0-16]: ClassicalType [6-9]: IntType [6-9], Ident [10-11] "i", ExprStmt [14-15]: Expr [14-15]: Lit: Int(1)"#]], + ); +} + +#[test] +fn int_sized_decl() { + check( + parse, + "int[32] i;", + &expect![[r#" + Stmt [0-10] + StmtKind: ClassicalDeclarationStmt [0-10]: ClassicalType [0-7]: IntType[ExprStmt [3-7]: Expr [4-6]: Lit: Int(32)]: [0-7], Ident [8-9] "i""#]], + ); +} + +#[test] +fn int_sized_decl_int_lit() { + check( + parse, + "int[32] i = 1;", + &expect![[r#" + Stmt [0-14] + StmtKind: ClassicalDeclarationStmt [0-14]: ClassicalType [0-7]: IntType[ExprStmt [3-7]: Expr [4-6]: Lit: Int(32)]: [0-7], Ident [8-9] "i", ValueExpression ExprStmt [12-13]: Expr [12-13]: Lit: Int(1)"#]], + ); +} + +#[test] +fn const_int_sized_decl_int_lit() { + check( + parse, + "const int[32] i = 1;", + &expect![[r#" + Stmt [0-20] + StmtKind: ConstantDeclaration [0-20]: ClassicalType [6-13]: IntType[ExprStmt [9-13]: Expr [10-12]: Lit: Int(32)]: [6-13], Ident [14-15] "i", ExprStmt [18-19]: Expr [18-19]: Lit: Int(1)"#]], + ); +} + +#[test] +fn uint_decl() { + check( + parse, + "uint i;", + &expect![[r#" + Stmt [0-7] + StmtKind: ClassicalDeclarationStmt [0-7]: ClassicalType [0-4]: UIntType [0-4], Ident [5-6] "i""#]], + ); +} + +#[test] +fn uint_decl_uint_lit() { + check( + parse, + "uint i = 1;", + &expect![[r#" + Stmt [0-11] + StmtKind: ClassicalDeclarationStmt [0-11]: ClassicalType [0-4]: UIntType [0-4], Ident [5-6] "i", ValueExpression ExprStmt [9-10]: Expr [9-10]: Lit: Int(1)"#]], + ); +} + +#[test] +fn const_uint_decl_uint_lit() { + check( + parse, + "const uint i = 1;", + &expect![[r#" + Stmt [0-17] + StmtKind: ConstantDeclaration [0-17]: ClassicalType [6-10]: UIntType [6-10], Ident [11-12] "i", ExprStmt [15-16]: Expr [15-16]: Lit: Int(1)"#]], + ); +} + +#[test] +fn uint_sized_decl() { + check( + parse, + "uint[32] i;", + &expect![[r#" + Stmt [0-11] + StmtKind: ClassicalDeclarationStmt [0-11]: ClassicalType [0-8]: UIntType[ExprStmt [4-8]: Expr [5-7]: Lit: Int(32)]: [0-8], Ident [9-10] "i""#]], + ); +} + +#[test] +fn uint_sized_decl_uint_lit() { + check( + parse, + "uint[32] i = 1;", + &expect![[r#" + Stmt [0-15] + StmtKind: ClassicalDeclarationStmt [0-15]: ClassicalType [0-8]: UIntType[ExprStmt [4-8]: Expr [5-7]: Lit: Int(32)]: [0-8], Ident [9-10] "i", ValueExpression ExprStmt [13-14]: Expr [13-14]: Lit: Int(1)"#]], + ); +} + +#[test] +fn const_uint_sized_decl_uint_lit() { + check( + parse, + "const uint[32] i = 1;", + &expect![[r#" + Stmt [0-21] + StmtKind: ConstantDeclaration [0-21]: ClassicalType [6-14]: UIntType[ExprStmt [10-14]: Expr [11-13]: Lit: Int(32)]: [6-14], Ident [15-16] "i", ExprStmt [19-20]: Expr [19-20]: Lit: Int(1)"#]], + ); +} + +#[test] +fn float_decl() { + check( + parse, + "float f;", + &expect![[r#" + Stmt [0-8] + StmtKind: ClassicalDeclarationStmt [0-8]: ClassicalType [0-5]: FloatType [0-5], Ident [6-7] "f""#]], + ); +} + +#[test] +fn float_decl_float_lit() { + check( + parse, + "float f = 1;", + &expect![[r#" + Stmt [0-12] + StmtKind: ClassicalDeclarationStmt [0-12]: ClassicalType [0-5]: FloatType [0-5], Ident [6-7] "f", ValueExpression ExprStmt [10-11]: Expr [10-11]: Lit: Int(1)"#]], + ); +} + +#[test] +fn const_float_decl_float_lit() { + check( + parse, + "const float f = 1.0;", + &expect![[r#" + Stmt [0-20] + StmtKind: ConstantDeclaration [0-20]: ClassicalType [6-11]: FloatType [6-11], Ident [12-13] "f", ExprStmt [16-19]: Expr [16-19]: Lit: Float(1.0)"#]], + ); +} + +#[test] +fn float_sized_decl() { + check( + parse, + "float[32] f;", + &expect![[r#" + Stmt [0-12] + StmtKind: ClassicalDeclarationStmt [0-12]: ClassicalType [0-9]: FloatType[ExprStmt [5-9]: Expr [6-8]: Lit: Int(32)]: [0-9], Ident [10-11] "f""#]], + ); +} + +#[test] +fn float_sized_decl_float_lit() { + check( + parse, + "float[32] f = 1.0;", + &expect![[r#" + Stmt [0-18] + StmtKind: ClassicalDeclarationStmt [0-18]: ClassicalType [0-9]: FloatType[ExprStmt [5-9]: Expr [6-8]: Lit: Int(32)]: [0-9], Ident [10-11] "f", ValueExpression ExprStmt [14-17]: Expr [14-17]: Lit: Float(1.0)"#]], + ); +} + +#[test] +fn const_float_sized_decl_float_lit() { + check( + parse, + "const float[32] f = 1;", + &expect![[r#" + Stmt [0-22] + StmtKind: ConstantDeclaration [0-22]: ClassicalType [6-15]: FloatType[ExprStmt [11-15]: Expr [12-14]: Lit: Int(32)]: [6-15], Ident [16-17] "f", ExprStmt [20-21]: Expr [20-21]: Lit: Int(1)"#]], + ); +} + +#[test] +fn angle_decl() { + check( + parse, + "angle a;", + &expect![[r#" + Stmt [0-8] + StmtKind: ClassicalDeclarationStmt [0-8]: ClassicalType [0-5]: AngleType [0-5], Ident [6-7] "a""#]], + ); +} + +#[test] +fn angle_decl_angle_lit() { + check( + parse, + "angle a = 1.0;", + &expect![[r#" + Stmt [0-14] + StmtKind: ClassicalDeclarationStmt [0-14]: ClassicalType [0-5]: AngleType [0-5], Ident [6-7] "a", ValueExpression ExprStmt [10-13]: Expr [10-13]: Lit: Float(1.0)"#]], + ); +} + +#[test] +fn const_angle_decl_no_init() { + check( + parse, + "const angle a;", + &expect![[r#" + Error( + Token( + Eq, + Semicolon, + Span { + lo: 13, + hi: 14, + }, + ), + ) + "#]], + ); +} + +#[test] +fn const_angle_decl_angle_lit() { + check( + parse, + "const angle a = 1.0;", + &expect![[r#" + Stmt [0-20] + StmtKind: ConstantDeclaration [0-20]: ClassicalType [6-11]: AngleType [6-11], Ident [12-13] "a", ExprStmt [16-19]: Expr [16-19]: Lit: Float(1.0)"#]], + ); +} + +#[test] +fn angle_sized_decl() { + check( + parse, + "angle[32] a;", + &expect![[r#" + Stmt [0-12] + StmtKind: ClassicalDeclarationStmt [0-12]: ClassicalType [0-9]: AngleType [0-9]: ExprStmt [5-9]: Expr [6-8]: Lit: Int(32), Ident [10-11] "a""#]], + ); +} + +#[test] +fn angle_sized_decl_angle_lit() { + check( + parse, + "angle[32] a = 1.0;", + &expect![[r#" + Stmt [0-18] + StmtKind: ClassicalDeclarationStmt [0-18]: ClassicalType [0-9]: AngleType [0-9]: ExprStmt [5-9]: Expr [6-8]: Lit: Int(32), Ident [10-11] "a", ValueExpression ExprStmt [14-17]: Expr [14-17]: Lit: Float(1.0)"#]], + ); +} + +#[test] +fn const_angle_sized_decl_angle_lit() { + check( + parse, + "const angle[32] a = 1.0;", + &expect![[r#" + Stmt [0-24] + StmtKind: ConstantDeclaration [0-24]: ClassicalType [6-15]: AngleType [6-15]: ExprStmt [11-15]: Expr [12-14]: Lit: Int(32), Ident [16-17] "a", ExprStmt [20-23]: Expr [20-23]: Lit: Float(1.0)"#]], + ); +} + +#[test] +fn duration_decl() { + check( + parse, + "duration d;", + &expect![[r#" + Stmt [0-11] + StmtKind: ClassicalDeclarationStmt [0-11]: ClassicalType [0-8]: Duration, Ident [9-10] "d""#]], + ); +} + +#[test] +#[ignore = "unimplemented: timing literal"] +fn duration_decl_ns_lit() { + check( + parse, + "duration d = 1000ns;", + &expect![[r#" + Error( + Lit( + "unimplemented: timing literal", + Span { + lo: 13, + hi: 19, + }, + ), + ) + "#]], + ); +} + +#[test] +#[ignore = "unimplemented: timing literal"] +fn duration_decl_us_lit() { + check( + parse, + "duration d = 1000us;", + &expect![[r#" + Error( + Lit( + "unimplemented: timing literal", + Span { + lo: 13, + hi: 19, + }, + ), + ) + "#]], + ); +} + +#[test] +#[ignore = "unimplemented: timing literal"] +fn duration_decl_uus_lit() { + // uus is for µ, disabling the lint must be done at the + // crate level, so using uus here in the test name. + check( + parse, + "duration d = 1000µs;", + &expect![[r#" + Error( + Lit( + "unimplemented: timing literal", + Span { + lo: 13, + hi: 20, + }, + ), + ) + "#]], + ); +} + +#[test] +#[ignore = "unimplemented: timing literal"] +fn duration_decl_ms_lit() { + check( + parse, + "duration d = 1000ms;", + &expect![[r#" + Error( + Lit( + "unimplemented: timing literal", + Span { + lo: 13, + hi: 19, + }, + ), + ) + "#]], + ); +} + +#[test] +#[ignore = "unimplemented: timing literal"] +fn duration_decl_s_lit() { + check( + parse, + "duration d = 1000s;", + &expect![[r#" + Error( + Lit( + "unimplemented: timing literal", + Span { + lo: 13, + hi: 18, + }, + ), + ) + "#]], + ); +} + +#[test] +#[ignore = "unimplemented: timing literal"] +fn duration_decl_dt_lit() { + check( + parse, + "duration d = 1000dt;", + &expect![[r#" + Error( + Lit( + "unimplemented: timing literal", + Span { + lo: 13, + hi: 19, + }, + ), + ) + "#]], + ); +} + +#[test] +#[ignore = "unimplemented: timing literal"] +fn const_duration_decl_dt_lit() { + check( + parse, + "const duration d = 10dt;", + &expect![[r#" + Error( + Lit( + "unimplemented: timing literal", + Span { + lo: 19, + hi: 23, + }, + ), + ) + "#]], + ); +} + +#[test] +fn stretch_decl() { + check( + parse, + "stretch s;", + &expect![[r#" + Stmt [0-10] + StmtKind: ClassicalDeclarationStmt [0-10]: ClassicalType [0-7]: Stretch, Ident [8-9] "s""#]], + ); +} diff --git a/compiler/qsc_qasm3/src/parser/stmt/tests/io_decl.rs b/compiler/qsc_qasm3/src/parser/stmt/tests/io_decl.rs new file mode 100644 index 0000000000..b291ee1c6e --- /dev/null +++ b/compiler/qsc_qasm3/src/parser/stmt/tests/io_decl.rs @@ -0,0 +1,338 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +use expect_test::expect; + +use crate::parser::tests::check; + +use crate::parser::stmt::parse; + +#[test] +fn input_bit_decl() { + check( + parse, + "input bit b;", + &expect![[r#" + Stmt [0-12] + StmtKind: IODeclaration [0-12]: input, ClassicalType [6-9]: BitType, Ident [10-11] "b""#]], + ); +} + +#[test] +fn output_bit_decl() { + check( + parse, + "output bit b;", + &expect![[r#" + Stmt [0-13] + StmtKind: IODeclaration [0-13]: output, ClassicalType [7-10]: BitType, Ident [11-12] "b""#]], + ); +} + +#[test] +fn input_bit_array_decl() { + check( + parse, + "input bit[2] b;", + &expect![[r#" + Stmt [0-15] + StmtKind: IODeclaration [0-15]: input, ClassicalType [6-12]: BitType [6-12]: ExprStmt [9-12]: Expr [10-11]: Lit: Int(2), Ident [13-14] "b""#]], + ); +} + +#[test] +fn output_bit_array_decl() { + check( + parse, + "output bit[2] b;", + &expect![[r#" + Stmt [0-16] + StmtKind: IODeclaration [0-16]: output, ClassicalType [7-13]: BitType [7-13]: ExprStmt [10-13]: Expr [11-12]: Lit: Int(2), Ident [14-15] "b""#]], + ); +} + +#[test] +fn intput_bool_decl() { + check( + parse, + "input bool b;", + &expect![[r#" + Stmt [0-13] + StmtKind: IODeclaration [0-13]: input, ClassicalType [6-10]: BoolType, Ident [11-12] "b""#]], + ); +} + +#[test] +fn output_bool_decl() { + check( + parse, + "output bool b;", + &expect![[r#" + Stmt [0-14] + StmtKind: IODeclaration [0-14]: output, ClassicalType [7-11]: BoolType, Ident [12-13] "b""#]], + ); +} + +#[test] +fn input_complex_decl() { + check( + parse, + "input complex c;", + &expect![[r#" + Stmt [0-16] + StmtKind: IODeclaration [0-16]: input, ClassicalType [6-13]: ComplexType [6-13], Ident [14-15] "c""#]], + ); +} + +#[test] +fn output_complex_decl() { + check( + parse, + "output complex c;", + &expect![[r#" + Stmt [0-17] + StmtKind: IODeclaration [0-17]: output, ClassicalType [7-14]: ComplexType [7-14], Ident [15-16] "c""#]], + ); +} + +#[test] +fn input_complex_sized_decl() { + check( + parse, + "input complex[float[32]] c;", + &expect![[r#" + Stmt [0-27] + StmtKind: IODeclaration [0-27]: input, ClassicalType [6-24]: ComplexType[float[FloatType[ExprStmt [19-23]: Expr [20-22]: Lit: Int(32)]: [14-23]]]: [6-24], Ident [25-26] "c""#]], + ); +} + +#[test] +fn output_complex_sized_decl() { + check( + parse, + "output complex[float[32]] c;", + &expect![[r#" + Stmt [0-28] + StmtKind: IODeclaration [0-28]: output, ClassicalType [7-25]: ComplexType[float[FloatType[ExprStmt [20-24]: Expr [21-23]: Lit: Int(32)]: [15-24]]]: [7-25], Ident [26-27] "c""#]], + ); +} + +#[test] +fn input_int_decl() { + check( + parse, + "input int i;", + &expect![[r#" + Stmt [0-12] + StmtKind: IODeclaration [0-12]: input, ClassicalType [6-9]: IntType [6-9], Ident [10-11] "i""#]], + ); +} + +#[test] +fn output_int_decl() { + check( + parse, + "output int i;", + &expect![[r#" + Stmt [0-13] + StmtKind: IODeclaration [0-13]: output, ClassicalType [7-10]: IntType [7-10], Ident [11-12] "i""#]], + ); +} + +#[test] +fn input_int_sized_decl() { + check( + parse, + "input int[32] i;", + &expect![[r#" + Stmt [0-16] + StmtKind: IODeclaration [0-16]: input, ClassicalType [6-13]: IntType[ExprStmt [9-13]: Expr [10-12]: Lit: Int(32)]: [6-13], Ident [14-15] "i""#]], + ); +} + +#[test] +fn output_int_sized_decl() { + check( + parse, + "output int[32] i;", + &expect![[r#" + Stmt [0-17] + StmtKind: IODeclaration [0-17]: output, ClassicalType [7-14]: IntType[ExprStmt [10-14]: Expr [11-13]: Lit: Int(32)]: [7-14], Ident [15-16] "i""#]], + ); +} + +#[test] +fn input_uint_decl() { + check( + parse, + "input uint i;", + &expect![[r#" + Stmt [0-13] + StmtKind: IODeclaration [0-13]: input, ClassicalType [6-10]: UIntType [6-10], Ident [11-12] "i""#]], + ); +} + +#[test] +fn output_uint_decl() { + check( + parse, + "output uint i;", + &expect![[r#" + Stmt [0-14] + StmtKind: IODeclaration [0-14]: output, ClassicalType [7-11]: UIntType [7-11], Ident [12-13] "i""#]], + ); +} + +#[test] +fn input_uint_sized_decl() { + check( + parse, + "input uint[32] i;", + &expect![[r#" + Stmt [0-17] + StmtKind: IODeclaration [0-17]: input, ClassicalType [6-14]: UIntType[ExprStmt [10-14]: Expr [11-13]: Lit: Int(32)]: [6-14], Ident [15-16] "i""#]], + ); +} + +#[test] +fn output_uint_sized_decl() { + check( + parse, + "output uint[32] i;", + &expect![[r#" + Stmt [0-18] + StmtKind: IODeclaration [0-18]: output, ClassicalType [7-15]: UIntType[ExprStmt [11-15]: Expr [12-14]: Lit: Int(32)]: [7-15], Ident [16-17] "i""#]], + ); +} + +#[test] +fn input_float_decl() { + check( + parse, + "input float f;", + &expect![[r#" + Stmt [0-14] + StmtKind: IODeclaration [0-14]: input, ClassicalType [6-11]: FloatType [6-11], Ident [12-13] "f""#]], + ); +} + +#[test] +fn output_float_decl() { + check( + parse, + "output float f;", + &expect![[r#" + Stmt [0-15] + StmtKind: IODeclaration [0-15]: output, ClassicalType [7-12]: FloatType [7-12], Ident [13-14] "f""#]], + ); +} + +#[test] +fn input_float_sized_decl() { + check( + parse, + "input float[32] f;", + &expect![[r#" + Stmt [0-18] + StmtKind: IODeclaration [0-18]: input, ClassicalType [6-15]: FloatType[ExprStmt [11-15]: Expr [12-14]: Lit: Int(32)]: [6-15], Ident [16-17] "f""#]], + ); +} + +#[test] +fn output_float_sized_decl() { + check( + parse, + "output float[32] f;", + &expect![[r#" + Stmt [0-19] + StmtKind: IODeclaration [0-19]: output, ClassicalType [7-16]: FloatType[ExprStmt [12-16]: Expr [13-15]: Lit: Int(32)]: [7-16], Ident [17-18] "f""#]], + ); +} + +#[test] +fn input_angle_decl() { + check( + parse, + "input angle a;", + &expect![[r#" + Stmt [0-14] + StmtKind: IODeclaration [0-14]: input, ClassicalType [6-11]: AngleType [6-11], Ident [12-13] "a""#]], + ); +} + +#[test] +fn output_angle_decl() { + check( + parse, + "output angle a;", + &expect![[r#" + Stmt [0-15] + StmtKind: IODeclaration [0-15]: output, ClassicalType [7-12]: AngleType [7-12], Ident [13-14] "a""#]], + ); +} + +#[test] +fn input_angle_sized_decl() { + check( + parse, + "input angle[32] a;", + &expect![[r#" + Stmt [0-18] + StmtKind: IODeclaration [0-18]: input, ClassicalType [6-15]: AngleType [6-15]: ExprStmt [11-15]: Expr [12-14]: Lit: Int(32), Ident [16-17] "a""#]], + ); +} + +#[test] +fn output_angle_sized_decl() { + check( + parse, + "output angle[32] a;", + &expect![[r#" + Stmt [0-19] + StmtKind: IODeclaration [0-19]: output, ClassicalType [7-16]: AngleType [7-16]: ExprStmt [12-16]: Expr [13-15]: Lit: Int(32), Ident [17-18] "a""#]], + ); +} + +#[test] +fn input_duration_decl() { + check( + parse, + "input duration d;", + &expect![[r#" + Stmt [0-17] + StmtKind: IODeclaration [0-17]: input, ClassicalType [6-14]: Duration, Ident [15-16] "d""#]], + ); +} + +#[test] +fn output_duration_decl() { + check( + parse, + "output duration d;", + &expect![[r#" + Stmt [0-18] + StmtKind: IODeclaration [0-18]: output, ClassicalType [7-15]: Duration, Ident [16-17] "d""#]], + ); +} + +#[test] +fn input_stretch_decl() { + check( + parse, + "input stretch s;", + &expect![[r#" + Stmt [0-16] + StmtKind: IODeclaration [0-16]: input, ClassicalType [6-13]: Stretch, Ident [14-15] "s""#]], + ); +} + +#[test] +fn output_stretch_decl() { + check( + parse, + "output stretch s;", + &expect![[r#" + Stmt [0-17] + StmtKind: IODeclaration [0-17]: output, ClassicalType [7-14]: Stretch, Ident [15-16] "s""#]], + ); +} diff --git a/compiler/qsc_qasm3/src/parser/stmt/tests/pragma.rs b/compiler/qsc_qasm3/src/parser/stmt/tests/pragma.rs new file mode 100644 index 0000000000..54c822619d --- /dev/null +++ b/compiler/qsc_qasm3/src/parser/stmt/tests/pragma.rs @@ -0,0 +1,114 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +use expect_test::expect; + +use crate::parser::tests::check; + +use crate::parser::stmt::parse; + +#[test] +fn pragma_decl() { + check( + parse, + "pragma a.b.d 23", + &expect![[r#" + Stmt [0-15] + StmtKind: Pragma [0-15]: (a.b.d, 23)"#]], + ); +} + +#[test] +fn pragma_decl_ident_only() { + check( + parse, + "pragma a.b.d", + &expect![[r#" + Stmt [0-12] + StmtKind: Pragma [0-12]: (a.b.d)"#]], + ); +} + +#[test] +fn pragma_decl_missing_ident() { + check( + parse, + "pragma ", + &expect![[r#" + Stmt [0-7] + StmtKind: Pragma [0-7]: () + + [ + Error( + Rule( + "pragma missing identifier", + Pragma, + Span { + lo: 0, + hi: 7, + }, + ), + ), + ]"#]], + ); +} + +#[test] +fn legacy_pragma_decl() { + check( + parse, + "#pragma a.b.d 23", + &expect![[r#" + Stmt [0-16] + StmtKind: Pragma [0-16]: (a, a.b.d 23)"#]], + ); +} + +#[test] +fn legacy_pragma_decl_ident_only() { + check( + parse, + "#pragma a.b.d", + &expect![[r#" + Stmt [0-13] + StmtKind: Pragma [0-13]: (a, a.b.d)"#]], + ); +} + +#[test] +fn legacy_pragma_ws_after_hash() { + check( + parse, + "# pragma a.b.d", + &expect![[r#" + Stmt [2-14] + StmtKind: Pragma [2-14]: (a.b.d) + + [ + Error( + Lex( + Incomplete( + Ident, + Pragma, + Whitespace, + Span { + lo: 1, + hi: 2, + }, + ), + ), + ), + ]"#]], + ); +} + +#[test] +fn legacy_pragma_decl_missing_ident() { + check( + parse, + "#pragma ", + &expect![[r#" + Stmt [0-8] + StmtKind: Pragma [0-8]: (a, )"#]], + ); +} diff --git a/compiler/qsc_qasm3/src/parser/stmt/tests/quantum_decl.rs b/compiler/qsc_qasm3/src/parser/stmt/tests/quantum_decl.rs new file mode 100644 index 0000000000..c2d5e4337c --- /dev/null +++ b/compiler/qsc_qasm3/src/parser/stmt/tests/quantum_decl.rs @@ -0,0 +1,102 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +use expect_test::expect; + +use crate::parser::tests::check; + +use crate::parser::stmt::parse; + +#[test] +fn quantum_decl() { + check( + parse, + "qubit q;", + &expect![[r#" + Stmt [0-8] + StmtKind: QubitDeclaration [0-8]: Ident [6-7] "q""#]], + ); +} + +#[test] +fn annotated_quantum_decl() { + check( + parse, + r#" + @a.b.c 123 + qubit q;"#, + &expect![[r#" + Stmt [9-36] + Annotation [9-19]: (a.b.c, 123) + StmtKind: QubitDeclaration [28-36]: Ident [34-35] "q""#]], + ); +} + +#[test] +fn multi_annotated_quantum_decl() { + check( + parse, + r#" + @g.h dolor sit amet, consectetur adipiscing elit + @d.e.f + @a.b.c 123 + qubit q;"#, + &expect![[r#" + Stmt [9-108] + Annotation [9-57]: (g.h, dolor sit amet, consectetur adipiscing elit) + Annotation [66-72]: (d.e.f) + Annotation [81-91]: (a.b.c, 123) + StmtKind: QubitDeclaration [100-108]: Ident [106-107] "q""#]], + ); +} + +#[test] +fn quantum_decl_missing_name() { + check( + parse, + "qubit;", + &expect![[r#" + Error( + Rule( + "identifier", + Semicolon, + Span { + lo: 5, + hi: 6, + }, + ), + ) + "#]], + ); +} + +#[test] +fn quantum_decl_with_designator() { + check( + parse, + "qubit[5] qubits;", + &expect![[r#" + Stmt [0-16] + StmtKind: QubitDeclaration [0-16]: Ident [9-15] "qubits", ExprStmt [5-8]: Expr [6-7]: Lit: Int(5)"#]], + ); +} + +#[test] +fn quantum_decl_with_designator_missing_name() { + check( + parse, + "qubit[5]", + &expect![[r#" + Error( + Rule( + "identifier", + Eof, + Span { + lo: 8, + hi: 8, + }, + ), + ) + "#]], + ); +} From a075818fd07a3dffbc89ef8e3a123cca7a38932a Mon Sep 17 00:00:00 2001 From: orpuente-MS <156957451+orpuente-MS@users.noreply.github.com> Date: Mon, 10 Feb 2025 17:10:43 -0800 Subject: [PATCH 005/108] Fix panic in qasm3 lexer found by the fuzzer (#2170) Fixes #2168 --- compiler/qsc_qasm3/src/lex/raw.rs | 10 ++++++--- compiler/qsc_qasm3/src/lex/raw/tests.rs | 28 +++++++++++++++++++++++++ 2 files changed, 35 insertions(+), 3 deletions(-) diff --git a/compiler/qsc_qasm3/src/lex/raw.rs b/compiler/qsc_qasm3/src/lex/raw.rs index 7b36bad167..4e0be3285d 100644 --- a/compiler/qsc_qasm3/src/lex/raw.rs +++ b/compiler/qsc_qasm3/src/lex/raw.rs @@ -373,9 +373,13 @@ impl<'a> Lexer<'a> { Err(NumberLexError::Incomplete) => unreachable!(), } } - Some('e') => match self.exp() { + Some('e' | 'E') => match self.exp() { Ok(()) => Ok(Number::Float), - Err(_) => todo!(), + Err(NumberLexError::None) => unreachable!("we know there is an `e`"), + Err(NumberLexError::Incomplete) => { + unreachable!("this only applies when lexing binary, octal, or hex") + } + Err(err) => Err(err), }, None | Some(_) => Ok(Number::Float), } @@ -415,7 +419,7 @@ impl<'a> Lexer<'a> { self.chars.next(); self.mid_dot(c1) } - Some('e') => match self.exp() { + Some('e' | 'E') => match self.exp() { Ok(()) => Ok(Number::Float), Err(NumberLexError::None) => unreachable!(), Err(_) => Err(NumberLexError::EndsInUnderscore), diff --git a/compiler/qsc_qasm3/src/lex/raw/tests.rs b/compiler/qsc_qasm3/src/lex/raw/tests.rs index 3850b06485..af9f480aff 100644 --- a/compiler/qsc_qasm3/src/lex/raw/tests.rs +++ b/compiler/qsc_qasm3/src/lex/raw/tests.rs @@ -798,6 +798,34 @@ fn incomplete_exp() { ); } +#[test] +fn incomplete_exp2() { + check( + "0.e3_", + &expect![[r#" + [ + Token { + kind: Unknown, + offset: 0, + }, + ] + "#]], + ); + check( + "1e", + &expect![[r#" + [ + Token { + kind: Number( + Float, + ), + offset: 0, + }, + ] + "#]], + ); +} + #[test] fn leading_zero_point() { check( From 0803258a7f0f95002f2cd60768452b4d3fff1772 Mon Sep 17 00:00:00 2001 From: Ian Davis Date: Mon, 10 Feb 2025 17:32:51 -0800 Subject: [PATCH 006/108] Add fuzzing for qasm3 (#2167) --- .github/workflows/fuzz.yml | 38 ++++++++++--------- fuzz/Cargo.toml | 10 ++++- fuzz/fuzz_targets/qasm3.rs | 27 +++++++++++++ fuzz/fuzz_targets/{compile.rs => qsharp.rs} | 0 fuzz/seed_inputs/compile/list.txt | 1 - fuzz/seed_inputs/qasm3/input.qasm | 8 ++++ fuzz/seed_inputs/qasm3/list.txt | 1 + fuzz/seed_inputs/{compile => qsharp}/input.qs | 0 fuzz/seed_inputs/qsharp/list.txt | 1 + 9 files changed, 66 insertions(+), 20 deletions(-) create mode 100644 fuzz/fuzz_targets/qasm3.rs rename fuzz/fuzz_targets/{compile.rs => qsharp.rs} (100%) delete mode 100644 fuzz/seed_inputs/compile/list.txt create mode 100644 fuzz/seed_inputs/qasm3/input.qasm create mode 100644 fuzz/seed_inputs/qasm3/list.txt rename fuzz/seed_inputs/{compile => qsharp}/input.qs (100%) create mode 100644 fuzz/seed_inputs/qsharp/list.txt diff --git a/.github/workflows/fuzz.yml b/.github/workflows/fuzz.yml index 7130d62d82..26938b9963 100644 --- a/.github/workflows/fuzz.yml +++ b/.github/workflows/fuzz.yml @@ -7,7 +7,6 @@ env: TMIN_LOG_FNAME: fuzz.tmin.log # File name to redirect the fuzzing input minimization log to. GH_ISSUE_TEMPLATE_RFPATH: .github/ISSUE_TEMPLATE/fuzz_bug_report.md # GitHub issue template rel file path. - TARGET_NAME: compile # Fuzzing target name. Fuzzes the `compile` func of the Q# compiler. ARTIFACTS_RDPATH: fuzz/artifacts # Fuzzing artifacts rel dir path. SEEDS_RDPATH: fuzz/seed_inputs # Fuzzing seed inputs rel dir path. SEEDS_FNAME: list.txt # Fuzzing seed inputs list file name. @@ -31,11 +30,15 @@ jobs: fuzz: name: Fuzzing strategy: + fail-fast: false matrix: os: [ubuntu-latest] # Fuzzing is not supported on Win. The macos is temporarily removed # because of low availability. - runs-on: ${{ matrix.os }} + target_name: [qsharp, qasm3] + runs-on: ${{ matrix.os }} + permissions: + issues: write steps: - name: Install and Configure Tools run: | @@ -49,6 +52,7 @@ jobs: submodules: "true" - name: Gather the Seed Inputs + if: matrix.target_name == 'qsharp' run: | cd $OWNER_RDPATH # Enter the dir containing the fuzzing infra. @@ -56,22 +60,22 @@ jobs: REPOS="Quantum Quantum-NC QuantumKatas QuantumLibraries iqsharp qdk-python qsharp-compiler qsharp-runtime" for REPO in $REPOS ; do git clone --depth 1 --single-branch --no-tags --recurse-submodules --shallow-submodules --jobs 4 \ - https://github.com/microsoft/$REPO.git $SEEDS_RDPATH/$TARGET_NAME/$REPO + https://github.com/microsoft/$REPO.git $SEEDS_RDPATH/${{ matrix.target_name }}/$REPO done # Build a comma-separated list of all the .qs files in $SEEDS_FNAME file: - find $SEEDS_RDPATH/$TARGET_NAME -name "*.qs" | tr "\n" "," > \ - $SEEDS_RDPATH/$TARGET_NAME/$SEEDS_FNAME + find $SEEDS_RDPATH/${{ matrix.target_name }} -name "*.qs" | tr "\n" "," > \ + $SEEDS_RDPATH/${{ matrix.target_name }}/$SEEDS_FNAME - name: Build and Run the Fuzz Target run: | cd $OWNER_RDPATH # Enter the dir containing the fuzzing infra. - cargo fuzz build --release --sanitizer=none --features do_fuzz $TARGET_NAME # Build the fuzz target. + cargo fuzz build --release --sanitizer=none --features do_fuzz ${{ matrix.target_name }} # Build the fuzz target. # Run fuzzing for specified number of seconds and redirect the `stderr` to a file # whose name is specified by the STDERR_LOG_FNAME env var: - RUST_BACKTRACE=1 cargo fuzz run --release --sanitizer=none --features do_fuzz $TARGET_NAME -- \ - -seed_inputs=@$SEEDS_RDPATH/$TARGET_NAME/$SEEDS_FNAME \ + RUST_BACKTRACE=1 cargo fuzz run --release --sanitizer=none --features do_fuzz ${{ matrix.target_name }} -- \ + -seed_inputs=@$SEEDS_RDPATH/${{ matrix.target_name }}/$SEEDS_FNAME \ -max_total_time=$DURATION_SEC \ -rss_limit_mb=4096 \ -max_len=20000 \ @@ -116,20 +120,20 @@ jobs: # the subsequent `run:` and `uses:` steps. # Determine the name of a file containing the input of interest (that triggers the panic/crash): - if [ -e $ARTIFACTS_RDPATH/$TARGET_NAME/crash-* ]; then # Panic and Stack Overflow Cases. + if [ -e $ARTIFACTS_RDPATH/${{ matrix.target_name }}/crash-* ]; then # Panic and Stack Overflow Cases. TO_MINIMIZE_FNAME=crash-*; - elif [ -e $ARTIFACTS_RDPATH/$TARGET_NAME/oom-* ]; then # Out-of-Memory Case. + elif [ -e $ARTIFACTS_RDPATH/${{ matrix.target_name }}/oom-* ]; then # Out-of-Memory Case. TO_MINIMIZE_FNAME=oom-*; else - echo -e "File to minimize not found.\nContents of artifacts dir \"$ARTIFACTS_RDPATH/$TARGET_NAME/\":" - ls $ARTIFACTS_RDPATH/$TARGET_NAME/ + echo -e "File to minimize not found.\nContents of artifacts dir \"$ARTIFACTS_RDPATH/${{ matrix.target_name }}/\":" + ls $ARTIFACTS_RDPATH/${{ matrix.target_name }}/ fi if [ "$TO_MINIMIZE_FNAME" != "" ]; then echo "TO_MINIMIZE_FNAME: $TO_MINIMIZE_FNAME" # Minimize the input: - ( cargo fuzz tmin --release --sanitizer=none --features do_fuzz -r 10000 $TARGET_NAME $ARTIFACTS_RDPATH/$TARGET_NAME/$TO_MINIMIZE_FNAME 2>&1 ) > \ + ( cargo fuzz tmin --release --sanitizer=none --features do_fuzz -r 10000 ${{ matrix.target_name }} $ARTIFACTS_RDPATH/${{ matrix.target_name }}/$TO_MINIMIZE_FNAME 2>&1 ) > \ $TMIN_LOG_FNAME || MINIMIZATION_FAILED=1 # Get the minimized input relative faile path: @@ -137,12 +141,12 @@ jobs: # Minimization failed, get the latest successful minimized input relative faile path: MINIMIZED_INPUT_RFPATH=` cat $TMIN_LOG_FNAME | grep "CRASH_MIN: minimizing crash input: " | tail -n 1 | - sed "s|^.*\($ARTIFACTS_RDPATH/$TARGET_NAME/[^\']*\).*|\1|"` + sed "s|^.*\($ARTIFACTS_RDPATH/${{ matrix.target_name }}/[^\']*\).*|\1|"` else # Minimization Succeeded, get the reported minimized input relative faile path:: MINIMIZED_INPUT_RFPATH=` cat $TMIN_LOG_FNAME | grep "failed to minimize beyond" | - sed "s|.*\($ARTIFACTS_RDPATH/$TARGET_NAME/[^ ]*\).*|\1|" ` + sed "s|.*\($ARTIFACTS_RDPATH/${{ matrix.target_name }}/[^ ]*\).*|\1|" ` fi echo "MINIMIZED_INPUT_RFPATH: $MINIMIZED_INPUT_RFPATH" echo "MINIMIZED_INPUT_RFPATH=$MINIMIZED_INPUT_RFPATH" >> "$GITHUB_ENV" @@ -187,8 +191,8 @@ jobs: path: | ${{ env.OWNER_RDPATH }}/${{ env.STDERR_LOG_FNAME }} ${{ env.OWNER_RDPATH }}/${{ env.TMIN_LOG_FNAME }} - ${{ env.OWNER_RDPATH }}/${{ env.ARTIFACTS_RDPATH }}/${{ env.TARGET_NAME }}/* - ${{ env.OWNER_RDPATH }}/${{ env.SEEDS_RDPATH }}/${{ env.TARGET_NAME }}/${{ env.SEEDS_FNAME }} + ${{ env.OWNER_RDPATH }}/${{ env.ARTIFACTS_RDPATH }}/${{ matrix.target_name }}/* + ${{ env.OWNER_RDPATH }}/${{ env.SEEDS_RDPATH }}/${{ matrix.target_name }}/${{ env.SEEDS_FNAME }} if-no-files-found: error - name: "If Fuzzing Failed: Report GutHub Issue" diff --git a/fuzz/Cargo.toml b/fuzz/Cargo.toml index 34b9ba6609..552c9e4d18 100644 --- a/fuzz/Cargo.toml +++ b/fuzz/Cargo.toml @@ -26,7 +26,13 @@ do_fuzz = [ "dep:libfuzzer-sys" ] workspace = true [[bin]] -name = "compile" -path = "fuzz_targets/compile.rs" +name = "qsharp" +path = "fuzz_targets/qsharp.rs" +test = false +doc = false + +[[bin]] +name = "qasm3" +path = "fuzz_targets/qasm3.rs" test = false doc = false diff --git a/fuzz/fuzz_targets/qasm3.rs b/fuzz/fuzz_targets/qasm3.rs new file mode 100644 index 0000000000..ce7b6f2ee1 --- /dev/null +++ b/fuzz/fuzz_targets/qasm3.rs @@ -0,0 +1,27 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +#![no_main] + +allocator::assign_global!(); + +#[cfg(feature = "do_fuzz")] +use libfuzzer_sys::fuzz_target; + +fn compile(data: &[u8]) { + if let Ok(fuzzed_code) = std::str::from_utf8(data) { + let resolver = qsc::qasm3::io::InMemorySourceResolver::from_iter([]); + let _ = qsc::qasm3::parser::parse_source(fuzzed_code, "fuzz.qasm", &resolver); + } +} + +#[cfg(feature = "do_fuzz")] +fuzz_target!(|data: &[u8]| { + compile(data); +}); + +#[cfg(not(feature = "do_fuzz"))] +#[no_mangle] +pub extern "C" fn main() { + compile(&[]); +} diff --git a/fuzz/fuzz_targets/compile.rs b/fuzz/fuzz_targets/qsharp.rs similarity index 100% rename from fuzz/fuzz_targets/compile.rs rename to fuzz/fuzz_targets/qsharp.rs diff --git a/fuzz/seed_inputs/compile/list.txt b/fuzz/seed_inputs/compile/list.txt deleted file mode 100644 index 75e4c71576..0000000000 --- a/fuzz/seed_inputs/compile/list.txt +++ /dev/null @@ -1 +0,0 @@ -fuzz/seed_inputs/compile/input.qs \ No newline at end of file diff --git a/fuzz/seed_inputs/qasm3/input.qasm b/fuzz/seed_inputs/qasm3/input.qasm new file mode 100644 index 0000000000..cddbfe007f --- /dev/null +++ b/fuzz/seed_inputs/qasm3/input.qasm @@ -0,0 +1,8 @@ +OPENQASM 3; +include "stdgates.inc"; +qubit q; +qubit[2] q2; +bit c; +bit[2] c2; +c2 = measure q2; +c = measure q; diff --git a/fuzz/seed_inputs/qasm3/list.txt b/fuzz/seed_inputs/qasm3/list.txt new file mode 100644 index 0000000000..08acb8e3f9 --- /dev/null +++ b/fuzz/seed_inputs/qasm3/list.txt @@ -0,0 +1 @@ +fuzz/seed_inputs/qasm3/input.qasm diff --git a/fuzz/seed_inputs/compile/input.qs b/fuzz/seed_inputs/qsharp/input.qs similarity index 100% rename from fuzz/seed_inputs/compile/input.qs rename to fuzz/seed_inputs/qsharp/input.qs diff --git a/fuzz/seed_inputs/qsharp/list.txt b/fuzz/seed_inputs/qsharp/list.txt new file mode 100644 index 0000000000..c6222243a3 --- /dev/null +++ b/fuzz/seed_inputs/qsharp/list.txt @@ -0,0 +1 @@ +fuzz/seed_inputs/qsharp/input.qs From 64bc5da538335e9ddf8984394bc93ebde3457b13 Mon Sep 17 00:00:00 2001 From: orpuente-MS <156957451+orpuente-MS@users.noreply.github.com> Date: Tue, 11 Feb 2025 13:13:05 -0800 Subject: [PATCH 007/108] Implement Pratt Parsing for Qasm3 parser (#2166) --- compiler/qsc_qasm3/src/ast.rs | 186 +++--- compiler/qsc_qasm3/src/keyword.rs | 3 - .../src/parser/completion/word_kinds.rs | 1 - compiler/qsc_qasm3/src/parser/expr.rs | 338 ++++++++++- compiler/qsc_qasm3/src/parser/expr/tests.rs | 553 ++++++++++++++++++ compiler/qsc_qasm3/src/parser/stmt.rs | 6 +- 6 files changed, 952 insertions(+), 135 deletions(-) diff --git a/compiler/qsc_qasm3/src/ast.rs b/compiler/qsc_qasm3/src/ast.rs index 7e2314e4e7..0fa41bc626 100644 --- a/compiler/qsc_qasm3/src/ast.rs +++ b/compiler/qsc_qasm3/src/ast.rs @@ -204,7 +204,7 @@ impl Display for MeasureExpr { } } /// A binary operator. -#[derive(Clone, Debug)] +#[derive(Clone, Copy, Debug)] pub enum BinOp { /// Addition: `+`. Add, @@ -273,8 +273,8 @@ impl Display for BinOp { } /// A unary operator. -#[derive(Clone, Debug)] -pub enum UnOp { +#[derive(Clone, Copy, Debug)] +pub enum UnaryOp { /// Negation: `-`. Neg, /// Bitwise NOT: `~`. @@ -283,12 +283,12 @@ pub enum UnOp { NotL, } -impl Display for UnOp { +impl Display for UnaryOp { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { match self { - UnOp::Neg => write!(f, "Neg"), - UnOp::NotB => write!(f, "NotB"), - UnOp::NotL => write!(f, "NotL"), + UnaryOp::Neg => write!(f, "Neg"), + UnaryOp::NotB => write!(f, "NotB"), + UnaryOp::NotL => write!(f, "NotL"), } } } @@ -637,7 +637,7 @@ impl Display for AliasStmt { #[derive(Clone, Debug)] pub struct ExprStmt { pub span: Span, - pub expr: Box, + pub expr: Expr, } impl Display for ExprStmt { @@ -667,7 +667,7 @@ impl Display for Expr { #[derive(Clone, Debug)] pub struct DiscreteSet { pub span: Span, - pub values: List, + pub values: Box<[Expr]>, } impl Display for DiscreteSet { @@ -685,9 +685,9 @@ impl Display for DiscreteSet { #[derive(Clone, Debug)] pub struct RangeDefinition { pub span: Span, - pub start: Option, - pub end: Option, - pub step: Option, + pub start: Option, + pub end: Option, + pub step: Option, } #[derive(Clone, Debug)] @@ -1584,17 +1584,20 @@ impl Display for ClassicalAssignment { #[derive(Clone, Debug, Default)] pub enum ExprKind { + Assign(AssignExpr), + AssignOp(AssignOpExpr), /// An expression with invalid syntax that can't be parsed. #[default] Err, Ident(Ident), - UnaryExpr(UnaryExpr), - BinaryExpr(BinaryExpr), + UnaryOp(UnaryOpExpr), + BinaryOp(BinaryOpExpr), Lit(Lit), FunctionCall(FunctionCall), Cast(Cast), Concatenation(Concatenation), IndexExpr(IndexExpr), + Paren(Expr), } impl Display for ExprKind { @@ -1603,25 +1606,54 @@ impl Display for ExprKind { match self { ExprKind::Err => write!(f, "Err"), ExprKind::Ident(id) => write!(f, "{id}"), - ExprKind::UnaryExpr(expr) => write!(f, "{expr}"), - ExprKind::BinaryExpr(expr) => display_bin_op(indent, expr), + ExprKind::UnaryOp(expr) => write!(f, "{expr}"), + ExprKind::BinaryOp(expr) => display_bin_op(indent, expr), ExprKind::Lit(lit) => write!(f, "{lit}"), ExprKind::FunctionCall(call) => write!(f, "{call}"), - ExprKind::Cast(cast) => write!(f, "{cast}"), + ExprKind::Cast(cast) => display_cast(indent, cast), ExprKind::Concatenation(concat) => write!(f, "{concat}"), ExprKind::IndexExpr(index) => write!(f, "{index}"), + ExprKind::Assign(expr) => write!(f, "{expr}"), + ExprKind::AssignOp(expr) => write!(f, "{expr}"), + ExprKind::Paren(expr) => display_paren(indent, expr), } } } #[derive(Clone, Debug)] -pub struct UnaryExpr { - pub span: Span, +pub struct AssignExpr { + pub lhs: Expr, + pub rhs: Expr, +} + +impl Display for AssignExpr { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + let indent = set_indentation(indented(f), 0); + display_assign(indent, &self.lhs, &self.rhs) + } +} + +#[derive(Clone, Debug)] +pub struct AssignOpExpr { + pub op: BinOp, + pub lhs: Expr, + pub rhs: Expr, +} + +impl Display for AssignOpExpr { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + let indent = set_indentation(indented(f), 0); + display_assign_op(indent, self.op, &self.lhs, &self.rhs) + } +} + +#[derive(Clone, Debug)] +pub struct UnaryOpExpr { pub op: UnaryOp, - pub expr: Box, + pub expr: Expr, } -impl Display for UnaryExpr { +impl Display for UnaryOpExpr { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { let indent = set_indentation(indented(f), 0); display_un_op(indent, self.op, &self.expr) @@ -1629,14 +1661,13 @@ impl Display for UnaryExpr { } #[derive(Clone, Debug)] -pub struct BinaryExpr { - pub span: Span, - pub op: BinaryOp, - pub lhs: ExprStmt, - pub rhs: ExprStmt, +pub struct BinaryOpExpr { + pub op: BinOp, + pub lhs: Expr, + pub rhs: Expr, } -impl Display for BinaryExpr { +impl Display for BinaryOpExpr { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { let indent = set_indentation(indented(f), 0); display_bin_op(indent, self) @@ -1647,7 +1678,7 @@ impl Display for BinaryExpr { pub struct FunctionCall { pub span: Span, pub name: Identifier, - pub args: List, + pub args: List, } impl Display for FunctionCall { @@ -1665,8 +1696,8 @@ impl Display for FunctionCall { #[derive(Clone, Debug)] pub struct Cast { pub span: Span, - pub r#type: ScalarType, - pub arg: ExprStmt, + pub r#type: TypeDef, + pub arg: Expr, } impl Display for Cast { @@ -1678,7 +1709,7 @@ impl Display for Cast { #[derive(Clone, Debug)] pub struct IndexExpr { pub span: Span, - pub collection: ExprStmt, + pub collection: Expr, pub index: IndexElement, } @@ -1692,65 +1723,6 @@ impl Display for IndexExpr { } } -#[derive(Clone, Copy, Debug)] -pub enum UnaryOp { - NegB, - NegL, - NegN, -} - -impl Display for UnaryOp { - fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { - match self { - UnaryOp::NegB => write!(f, "NegB"), - UnaryOp::NegL => write!(f, "NegL"), - UnaryOp::NegN => write!(f, "NegN"), - } - } -} - -#[derive(Clone, Copy, Debug)] -pub enum BinaryOp { - /// `>` - Gt, - /// `<` - Lt, - /// `>=` - Gte, - /// `<=` - Lte, - /// `==` - Eq, - /// `!=` - Neq, - /// `&&` - AndL, - /// `||` - OrL, - /// `|` - OrB, - /// `^` - XorB, - /// `&` - AndB, - /// `<<` - ShL, - /// `>>` - ShR, - /// `+` - Add, - /// `-` - Sub, - /// `*` - Mul, - /// `/` - Div, - /// `%` - Mod, - /// `**` - Exp, -} - #[derive(Clone, Debug)] pub struct Lit { pub span: Span, @@ -1860,10 +1832,19 @@ impl Display for IndexElement { } } -#[derive(Clone, Debug)] +#[derive(Clone, Debug, Default)] pub enum IndexSetItem { RangeDefinition(RangeDefinition), - Expr(ExprStmt), + Expr(Expr), + #[default] + Err, +} + +/// This is needed to able to use `IndexSetItem` in the `seq` combinator. +impl WithSpan for IndexSetItem { + fn with_span(self, _span: Span) -> Self { + self + } } impl Display for IndexSetItem { @@ -1872,13 +1853,14 @@ impl Display for IndexSetItem { match self { IndexSetItem::RangeDefinition(range) => display_range(indent, range), IndexSetItem::Expr(expr) => write!(f, "IndexSetItem {expr}"), + IndexSetItem::Err => write!(f, "Err"), } } } #[derive(Clone, Debug)] pub enum AssignmentOp { - BinaryOp(BinaryOp), + BinaryOp(BinOp), /// `OpenQASM3` has the `~=` assignment operator. /// This enum variant is meant to capture that. UnaryOp(UnaryOp), @@ -1986,7 +1968,7 @@ fn display_assign(mut indent: Indented, lhs: &Expr, rhs: &Expr) -> fm fn display_assign_op( mut indent: Indented, - op: BinaryOp, + op: BinOp, lhs: &Expr, rhs: &Expr, ) -> fmt::Result { @@ -1997,7 +1979,7 @@ fn display_assign_op( Ok(()) } -fn display_bin_op(mut indent: Indented, expr: &BinaryExpr) -> fmt::Result { +fn display_bin_op(mut indent: Indented, expr: &BinaryOpExpr) -> fmt::Result { write!(indent, "BinOp ({:?}):", expr.op)?; indent = set_indentation(indent, 1); write!(indent, "\n{}", expr.lhs)?; @@ -2012,6 +1994,20 @@ fn display_un_op(mut indent: Indented, op: UnaryOp, expr: &Expr) -> f Ok(()) } +fn display_paren(mut indent: Indented, expr: &Expr) -> fmt::Result { + write!(indent, "Paren:")?; + indent = set_indentation(indent, 1); + write!(indent, "\n{expr}")?; + Ok(()) +} +fn display_cast(mut indent: Indented, cast: &Cast) -> fmt::Result { + let Cast { span, r#type, arg } = cast; + write!(indent, "Cast {span}:")?; + indent = set_indentation(indent, 1); + write!(indent, "\n{type}\n{arg}")?; + Ok(()) +} + fn display_while(mut indent: Indented, cond: &Expr, block: &Block) -> fmt::Result { write!(indent, "While:")?; indent = set_indentation(indent, 1); diff --git a/compiler/qsc_qasm3/src/keyword.rs b/compiler/qsc_qasm3/src/keyword.rs index 86f520b5c3..2a2a07e8e3 100644 --- a/compiler/qsc_qasm3/src/keyword.rs +++ b/compiler/qsc_qasm3/src/keyword.rs @@ -9,7 +9,6 @@ use std::{ #[derive(Clone, Copy, Debug, Eq, Hash, PartialEq, Sequence)] pub enum Keyword { - Array, Barrier, Box, Break, @@ -58,7 +57,6 @@ pub enum Keyword { impl Keyword { pub(super) fn as_str(self) -> &'static str { match self { - Keyword::Array => "array", Keyword::Barrier => "barrier", Keyword::Box => "box", Keyword::Break => "break", @@ -120,7 +118,6 @@ impl FromStr for Keyword { // frequency in Q# so that fewer comparisons are needed on average. fn from_str(s: &str) -> Result { match s { - "array" => Ok(Self::Array), "barrier" => Ok(Self::Barrier), "box" => Ok(Self::Box), "break" => Ok(Self::Break), diff --git a/compiler/qsc_qasm3/src/parser/completion/word_kinds.rs b/compiler/qsc_qasm3/src/parser/completion/word_kinds.rs index 44b441338a..f300e4d26c 100644 --- a/compiler/qsc_qasm3/src/parser/completion/word_kinds.rs +++ b/compiler/qsc_qasm3/src/parser/completion/word_kinds.rs @@ -62,7 +62,6 @@ bitflags! { // Begin keywords. // - const Array = keyword_bit(Keyword::Array); const Barrier = keyword_bit(Keyword::Barrier); const Box = keyword_bit(Keyword::Box); const Break = keyword_bit(Keyword::Break); diff --git a/compiler/qsc_qasm3/src/parser/expr.rs b/compiler/qsc_qasm3/src/parser/expr.rs index 7bc73665a6..7486d20083 100644 --- a/compiler/qsc_qasm3/src/parser/expr.rs +++ b/compiler/qsc_qasm3/src/parser/expr.rs @@ -15,9 +15,16 @@ use num_traits::Num; use qsc_data_structures::span::Span; use crate::{ - ast::{BinOp, Expr, ExprKind, ExprStmt, Lit, LiteralKind, UnOp, ValueExpression, Version}, + ast::{ + self, AssignExpr, AssignOpExpr, BinOp, BinaryOpExpr, Cast, DiscreteSet, Expr, ExprKind, + ExprStmt, FunctionCall, IndexElement, IndexExpr, IndexSetItem, Lit, LiteralKind, + RangeDefinition, TypeDef, UnaryOp, ValueExpression, Version, + }, keyword::Keyword, - lex::{cooked::Literal, ClosedBinOp, Delim, Radix, Token, TokenKind}, + lex::{ + cooked::{ComparisonOp, Literal}, + ClosedBinOp, Delim, Radix, Token, TokenKind, + }, parser::{ completion::WordKinds, prim::{shorten, token}, @@ -27,26 +34,31 @@ use crate::{ use crate::parser::Result; -use super::error::{Error, ErrorKind}; +use super::{ + error::{Error, ErrorKind}, + prim::{ident, opt, seq, FinalSep}, + stmt::scalar_or_array_type, +}; struct PrefixOp { - kind: UnOp, + kind: UnaryOp, precedence: u8, } -struct MixfixOp { +struct InfixOp { kind: OpKind, precedence: u8, } enum OpKind { - Postfix(UnOp), - Binary(BinOp, Assoc), Assign, - AssignUpdate, AssignBinary(BinOp), + Binary(BinOp, Assoc), + Funcall, + Index, } +// TODO: This seems to be an unnecessary wrapper. Consider removing. #[derive(Clone, Copy)] enum OpName { Token(TokenKind), @@ -67,35 +79,117 @@ enum Assoc { const RANGE_PRECEDENCE: u8 = 1; -pub(super) fn expr(s: &mut ParserContext) -> Result> { +pub(super) fn expr(s: &mut ParserContext) -> Result { expr_op(s, OpContext::Precedence(0)) } -pub(super) fn expr_stmt(s: &mut ParserContext) -> Result> { +pub(super) fn expr_stmt(s: &mut ParserContext) -> Result { expr_op(s, OpContext::Stmt) } -fn expr_op(s: &mut ParserContext, _context: OpContext) -> Result> { - let lhs = expr_base(s)?; +fn expr_op(s: &mut ParserContext, context: OpContext) -> Result { + let lo = s.peek().span.lo; + + let mut lhs = if let Some(op) = prefix_op(op_name(s)) { + s.advance(); + let rhs = expr_op(s, OpContext::Precedence(op.precedence))?; + Expr { + span: s.span(lo), + kind: Box::new(ExprKind::UnaryOp(ast::UnaryOpExpr { + op: op.kind, + expr: rhs, + })), + } + } else { + expr_base(s)? + }; + + let min_precedence = match context { + OpContext::Precedence(p) => p, + OpContext::Stmt => 0, + }; + + while let Some(op) = infix_op(op_name(s)) { + if op.precedence < min_precedence { + break; + } + + s.advance(); + let kind = match op.kind { + OpKind::Assign => { + let rhs = expr_op(s, OpContext::Precedence(op.precedence))?; + Box::new(ExprKind::Assign(AssignExpr { lhs, rhs })) + } + OpKind::AssignBinary(kind) => { + let rhs = expr_op(s, OpContext::Precedence(op.precedence))?; + Box::new(ExprKind::AssignOp(AssignOpExpr { op: kind, lhs, rhs })) + } + OpKind::Binary(kind, assoc) => { + let precedence = next_precedence(op.precedence, assoc); + let rhs = expr_op(s, OpContext::Precedence(precedence))?; + Box::new(ExprKind::BinaryOp(BinaryOpExpr { op: kind, lhs, rhs })) + } + OpKind::Funcall => { + if let ExprKind::Ident(ident) = *lhs.kind { + Box::new(funcall(s, ident)?) + } else { + return Err(Error::new(ErrorKind::Convert("identifier", "", lhs.span))); + } + } + OpKind::Index => Box::new(index_expr(s, lhs)?), + }; + + lhs = Expr { + span: s.span(lo), + kind, + }; + } + Ok(lhs) } -fn expr_base(s: &mut ParserContext) -> Result> { +fn expr_base(s: &mut ParserContext) -> Result { let lo = s.peek().span.lo; - let kind = if let Some(l) = lit(s)? { - Ok(Box::new(ExprKind::Lit(l))) + if let Some(l) = lit(s)? { + Ok(Expr { + span: s.span(lo), + kind: Box::new(ExprKind::Lit(l)), + }) + } else if token(s, TokenKind::Open(Delim::Paren)).is_ok() { + paren_expr(s, lo) } else { - Err(Error::new(ErrorKind::Rule( - "expression", - s.peek().kind, - s.peek().span, - ))) - }?; - - Ok(Box::new(Expr { - span: s.span(lo), - kind, - })) + match opt(s, scalar_or_array_type) { + Err(err) => Err(err), + Ok(Some(r#type)) => { + // If we have a type, we expect to see a + // parenthesized expression next. + token(s, TokenKind::Open(Delim::Paren))?; + let arg = paren_expr(s, lo)?; + Ok(Expr { + span: s.span(lo), + kind: Box::new(ExprKind::Cast(Cast { + span: s.span(lo), + r#type, + arg, + })), + }) + } + Ok(None) => { + if let Ok(id) = ident(s) { + Ok(Expr { + span: s.span(lo), + kind: Box::new(ExprKind::Ident(*id)), + }) + } else { + Err(Error::new(ErrorKind::Rule( + "expression", + s.peek().kind, + s.peek().span, + ))) + } + } + } + } } pub(super) fn lit(s: &mut ParserContext) -> Result> { @@ -315,18 +409,147 @@ fn lit_bigint(lexeme: &str, radix: u32) -> Option { } } +fn paren_expr(s: &mut ParserContext, lo: u32) -> Result { + let (mut exprs, final_sep) = seq(s, expr)?; + token(s, TokenKind::Close(Delim::Paren))?; + + let kind = if final_sep == FinalSep::Missing && exprs.len() == 1 { + ExprKind::Paren(exprs.pop().expect("vector should have exactly one item")) + } else { + return Err(Error::new(ErrorKind::Convert( + "parenthesized expression", + "expression list", + s.span(lo), + ))); + }; + + Ok(Expr { + span: s.span(lo), + kind: Box::new(kind), + }) +} + +fn funcall(s: &mut ParserContext, ident: ast::Ident) -> Result { + let lo = ident.span.lo; + let (args, _) = seq(s, expr)?; + token(s, TokenKind::Close(Delim::Paren))?; + Ok(ExprKind::FunctionCall(FunctionCall { + span: s.span(lo), + name: ast::Identifier::Ident(Box::new(ident)), + args: args.into_iter().map(Box::new).collect(), + })) +} + +fn cast_op(s: &mut ParserContext, r#type: TypeDef) -> Result { + let lo = match &r#type { + TypeDef::Scalar(ident) => ident.span.lo, + TypeDef::Array(array) => array.span.lo, + TypeDef::ArrayReference(array) => array.span.lo, + }; + let arg = paren_expr(s, lo)?; + token(s, TokenKind::Close(Delim::Paren))?; + Ok(ExprKind::Cast(Cast { + span: s.span(lo), + r#type, + arg, + })) +} + +fn index_expr(s: &mut ParserContext, lhs: Expr) -> Result { + let lo = s.span(0).hi - 1; + let index = index_element(s)?; + token(s, TokenKind::Close(Delim::Bracket))?; + Ok(ExprKind::IndexExpr(IndexExpr { + span: s.span(lo), + collection: lhs, + index, + })) +} + +fn index_element(s: &mut ParserContext) -> Result { + let index = match opt(s, set_expr) { + Ok(Some(v)) => IndexElement::DiscreteSet(v), + Err(err) => return Err(err), + Ok(None) => { + let (exprs, _) = seq(s, index_set_item)?; + let exprs = exprs + .into_iter() + .map(Box::new) + .collect::>() + .into_boxed_slice(); + IndexElement::IndexSet(exprs) + } + }; + Ok(index) +} + +fn index_set_item(s: &mut ParserContext) -> Result { + let lo = s.peek().span.lo; + let start = opt(s, expr)?; + + // If no colon, return the expr as a normal index. + if token(s, TokenKind::Colon).is_err() { + let expr = start.ok_or(Error::new(ErrorKind::Rule( + "expression", + s.peek().kind, + s.span(lo), + )))?; + return Ok(IndexSetItem::Expr(expr)); + } + + let end = opt(s, expr)?; + let step = opt(s, |s| { + token(s, TokenKind::Colon)?; + expr(s) + })?; + + Ok(IndexSetItem::RangeDefinition(RangeDefinition { + span: s.span(lo), + start, + end, + step, + })) +} + +fn set_expr(s: &mut ParserContext) -> Result { + let lo = s.peek().span.lo; + token(s, TokenKind::Open(Delim::Brace))?; + let (exprs, _) = seq(s, expr)?; + token(s, TokenKind::Close(Delim::Brace))?; + Ok(DiscreteSet { + span: s.span(lo), + values: exprs.into_boxed_slice(), + }) +} + +fn op_name(s: &ParserContext) -> OpName { + match s.peek().kind { + TokenKind::Keyword(keyword) => OpName::Keyword(keyword), + kind => OpName::Token(kind), + } +} + +fn next_precedence(precedence: u8, assoc: Assoc) -> u8 { + match assoc { + Assoc::Left => precedence + 1, + Assoc::Right => precedence, + } +} + +/// The operation precedence table is at +/// . fn prefix_op(name: OpName) -> Option { match name { OpName::Token(TokenKind::Bang) => Some(PrefixOp { - kind: UnOp::NotL, + kind: UnaryOp::NotL, precedence: 11, }), OpName::Token(TokenKind::Tilde) => Some(PrefixOp { - kind: UnOp::NotB, + kind: UnaryOp::NotB, precedence: 11, }), OpName::Token(TokenKind::ClosedBinOp(ClosedBinOp::Minus)) => Some(PrefixOp { - kind: UnOp::Neg, + kind: UnaryOp::Neg, precedence: 11, }), @@ -334,6 +557,59 @@ fn prefix_op(name: OpName) -> Option { } } +/// The operation precedence table is at +/// . +fn infix_op(name: OpName) -> Option { + fn left_assoc(op: BinOp, precedence: u8) -> Option { + Some(InfixOp { + kind: OpKind::Binary(op, Assoc::Left), + precedence, + }) + } + + let OpName::Token(kind) = name else { + return None; + }; + + match kind { + TokenKind::ClosedBinOp(token) => match token { + ClosedBinOp::StarStar => Some(InfixOp { + kind: OpKind::Binary(BinOp::Exp, Assoc::Right), + precedence: 12, + }), + ClosedBinOp::Star => left_assoc(BinOp::Mul, 10), + ClosedBinOp::Slash => left_assoc(BinOp::Div, 10), + ClosedBinOp::Percent => left_assoc(BinOp::Mod, 10), + ClosedBinOp::Minus => left_assoc(BinOp::Sub, 9), + ClosedBinOp::Plus => left_assoc(BinOp::Add, 9), + ClosedBinOp::LtLt => left_assoc(BinOp::Shl, 8), + ClosedBinOp::GtGt => left_assoc(BinOp::Shr, 8), + ClosedBinOp::Amp => left_assoc(BinOp::AndB, 5), + ClosedBinOp::Bar => left_assoc(BinOp::OrB, 4), + ClosedBinOp::Caret => left_assoc(BinOp::XorB, 3), + ClosedBinOp::AmpAmp => left_assoc(BinOp::AndL, 2), + ClosedBinOp::BarBar => left_assoc(BinOp::OrL, 1), + }, + TokenKind::ComparisonOp(token) => match token { + ComparisonOp::Gt => left_assoc(BinOp::Gt, 7), + ComparisonOp::GtEq => left_assoc(BinOp::Gte, 7), + ComparisonOp::Lt => left_assoc(BinOp::Lt, 7), + ComparisonOp::LtEq => left_assoc(BinOp::Lte, 7), + ComparisonOp::BangEq => left_assoc(BinOp::Neq, 6), + ComparisonOp::EqEq => left_assoc(BinOp::Eq, 6), + }, + TokenKind::Open(Delim::Paren) => Some(InfixOp { + kind: OpKind::Funcall, + precedence: 13, + }), + TokenKind::Open(Delim::Bracket) => Some(InfixOp { + kind: OpKind::Index, + precedence: 13, + }), + _ => None, + } +} + fn closed_bin_op(op: ClosedBinOp) -> BinOp { match op { ClosedBinOp::Amp => BinOp::AndB, @@ -397,6 +673,6 @@ pub(super) fn value_expr(s: &mut ParserContext) -> Result> Ok(Box::new(ValueExpression::Expr(stmt))) } -pub(crate) fn expr_list(_s: &mut ParserContext<'_>) -> Result> { - todo!("expr_list") +pub(crate) fn expr_list(s: &mut ParserContext<'_>) -> Result> { + seq(s, expr).map(|pair| pair.0) } diff --git a/compiler/qsc_qasm3/src/parser/expr/tests.rs b/compiler/qsc_qasm3/src/parser/expr/tests.rs index 3fe0d6d034..0569e32374 100644 --- a/compiler/qsc_qasm3/src/parser/expr/tests.rs +++ b/compiler/qsc_qasm3/src/parser/expr/tests.rs @@ -615,3 +615,556 @@ fn lit_float_imag_underscore() { fn lit_float_imag_leading_zero() { check(expr, "0.23im", &expect!["Expr [0-6]: Lit: Imaginary(0.23)"]); } + +#[test] +fn pratt_parsing_mul_add() { + check( + expr, + "1 + 2 * 3", + &expect![[r#" + Expr [0-9]: BinOp (Add): + Expr [0-1]: Lit: Int(1) + Expr [4-9]: BinOp (Mul): + Expr [4-5]: Lit: Int(2) + Expr [8-9]: Lit: Int(3)"#]], + ); +} + +#[test] +fn pratt_parsing_parens() { + check( + expr, + "(1 + 2) * 3", + &expect![[r#" + Expr [0-11]: BinOp (Mul): + Expr [0-7]: Paren: + Expr [1-6]: BinOp (Add): + Expr [1-2]: Lit: Int(1) + Expr [5-6]: Lit: Int(2) + Expr [10-11]: Lit: Int(3)"#]], + ); +} + +#[test] +fn prat_parsing_mul_unary() { + check( + expr, + "2 * -3", + &expect![[r#" + Expr [0-6]: BinOp (Mul): + Expr [0-1]: Lit: Int(2) + Expr [4-6]: UnOp (Neg): + Expr [5-6]: Lit: Int(3)"#]], + ); +} + +#[test] +fn prat_parsing_unary_mul() { + check( + expr, + "-2 * 3", + &expect![[r#" + Expr [0-6]: BinOp (Mul): + Expr [0-2]: UnOp (Neg): + Expr [1-2]: Lit: Int(2) + Expr [5-6]: Lit: Int(3)"#]], + ); +} + +#[test] +fn prat_parsing_exp_funcall() { + check( + expr, + "2 ** square(3)", + &expect![[r#" + Expr [0-14]: BinOp (Exp): + Expr [0-1]: Lit: Int(2) + Expr [5-14]: FunctionCall [5-14]: Ident [5-11] "square" + Expr [12-13]: Lit: Int(3)"#]], + ); +} + +#[test] +fn prat_parsing_funcall_exp() { + check( + expr, + "square(2) ** 3", + &expect![[r#" + Expr [0-14]: BinOp (Exp): + Expr [0-9]: FunctionCall [0-9]: Ident [0-6] "square" + Expr [7-8]: Lit: Int(2) + Expr [13-14]: Lit: Int(3)"#]], + ); +} + +#[test] +fn prat_parsing_funcall_exp_arg() { + check( + expr, + "square(2 ** 3)", + &expect![[r#" + Expr [0-14]: FunctionCall [0-14]: Ident [0-6] "square" + Expr [7-13]: BinOp (Exp): + Expr [7-8]: Lit: Int(2) + Expr [12-13]: Lit: Int(3)"#]], + ); +} + +#[test] +fn funcall() { + check( + expr, + "square(2)", + &expect![[r#" + Expr [0-9]: FunctionCall [0-9]: Ident [0-6] "square" + Expr [7-8]: Lit: Int(2)"#]], + ); +} + +#[test] +fn funcall_multiple_args() { + check( + expr, + "square(2, 3)", + &expect![[r#" + Expr [0-12]: FunctionCall [0-12]: Ident [0-6] "square" + Expr [7-8]: Lit: Int(2) + Expr [10-11]: Lit: Int(3)"#]], + ); +} + +#[test] +fn funcall_multiple_args_trailing_comma() { + check( + expr, + "square(2, 3,)", + &expect![[r#" + Expr [0-13]: FunctionCall [0-13]: Ident [0-6] "square" + Expr [7-8]: Lit: Int(2) + Expr [10-11]: Lit: Int(3)"#]], + ); +} + +#[test] +fn cast_to_bit() { + check( + expr, + "bit(0)", + &expect![[r#" + Expr [0-6]: Cast [0-6]: + ClassicalType [0-3]: BitType + Expr [0-6]: Paren: + Expr [4-5]: Lit: Int(0)"#]], + ); +} + +#[test] +fn cast_to_bit_with_designator() { + check( + expr, + "bit[4](0)", + &expect![[r#" + Expr [0-9]: Cast [0-9]: + ClassicalType [0-6]: BitType [0-6]: ExprStmt [3-6]: Expr [4-5]: Lit: Int(4) + Expr [0-9]: Paren: + Expr [7-8]: Lit: Int(0)"#]], + ); +} + +#[test] +fn cast_to_int() { + check( + expr, + "int(0)", + &expect![[r#" + Expr [0-6]: Cast [0-6]: + ClassicalType [0-3]: IntType [0-3] + Expr [0-6]: Paren: + Expr [4-5]: Lit: Int(0)"#]], + ); +} + +#[test] +fn cast_to_int_with_designator() { + check( + expr, + "int[64](0)", + &expect![[r#" + Expr [0-10]: Cast [0-10]: + ClassicalType [0-7]: IntType[ExprStmt [3-7]: Expr [4-6]: Lit: Int(64)]: [0-7] + Expr [0-10]: Paren: + Expr [8-9]: Lit: Int(0)"#]], + ); +} + +#[test] +fn cast_to_uint() { + check( + expr, + "uint(0)", + &expect![[r#" + Expr [0-7]: Cast [0-7]: + ClassicalType [0-4]: UIntType [0-4] + Expr [0-7]: Paren: + Expr [5-6]: Lit: Int(0)"#]], + ); +} + +#[test] +fn cast_to_uint_with_designator() { + check( + expr, + "uint[64](0)", + &expect![[r#" + Expr [0-11]: Cast [0-11]: + ClassicalType [0-8]: UIntType[ExprStmt [4-8]: Expr [5-7]: Lit: Int(64)]: [0-8] + Expr [0-11]: Paren: + Expr [9-10]: Lit: Int(0)"#]], + ); +} + +#[test] +fn cast_to_float() { + check( + expr, + "float(0)", + &expect![[r#" + Expr [0-8]: Cast [0-8]: + ClassicalType [0-5]: FloatType [0-5] + Expr [0-8]: Paren: + Expr [6-7]: Lit: Int(0)"#]], + ); +} + +#[test] +fn cast_to_float_with_designator() { + check( + expr, + "float[64](0)", + &expect![[r#" + Expr [0-12]: Cast [0-12]: + ClassicalType [0-9]: FloatType[ExprStmt [5-9]: Expr [6-8]: Lit: Int(64)]: [0-9] + Expr [0-12]: Paren: + Expr [10-11]: Lit: Int(0)"#]], + ); +} + +#[test] +fn cast_to_complex() { + check( + expr, + "complex[float](0)", + &expect![[r#" + Expr [0-17]: Cast [0-17]: + ClassicalType [0-14]: ComplexType[float[FloatType [8-13]]]: [0-14] + Expr [0-17]: Paren: + Expr [15-16]: Lit: Int(0)"#]], + ); +} + +#[test] +fn cast_to_complex_with_designator() { + check( + expr, + "complex[float[64]](0)", + &expect![[r#" + Expr [0-21]: Cast [0-21]: + ClassicalType [0-18]: ComplexType[float[FloatType[ExprStmt [13-17]: Expr [14-16]: Lit: Int(64)]: [8-17]]]: [0-18] + Expr [0-21]: Paren: + Expr [19-20]: Lit: Int(0)"#]], + ); +} + +#[test] +fn cast_to_bool() { + check( + expr, + "bool(0)", + &expect![[r#" + Expr [0-7]: Cast [0-7]: + ClassicalType [0-4]: BoolType + Expr [0-7]: Paren: + Expr [5-6]: Lit: Int(0)"#]], + ); +} + +#[test] +fn cast_to_duration() { + check( + expr, + "duration(0)", + &expect![[r#" + Expr [0-11]: Cast [0-11]: + ClassicalType [0-8]: Duration + Expr [0-11]: Paren: + Expr [9-10]: Lit: Int(0)"#]], + ); +} + +#[test] +fn cast_to_stretch() { + check( + expr, + "stretch(0)", + &expect![[r#" + Expr [0-10]: Cast [0-10]: + ClassicalType [0-7]: Stretch + Expr [0-10]: Paren: + Expr [8-9]: Lit: Int(0)"#]], + ); +} + +#[test] +fn cast_to_int_array() { + check( + expr, + "array[int[64], 4](0)", + &expect![[r#" + Expr [0-20]: Cast [0-20]: + ArrayType [0-17]: ArrayBaseTypeKind IntType[ExprStmt [9-13]: Expr [10-12]: Lit: Int(64)]: [6-13] + Expr [15-16]: Lit: Int(4) + Expr [0-20]: Paren: + Expr [18-19]: Lit: Int(0)"#]], + ); +} + +#[test] +fn cast_to_uint_array() { + check( + expr, + "array[uint[64], 4](0)", + &expect![[r#" + Expr [0-21]: Cast [0-21]: + ArrayType [0-18]: ArrayBaseTypeKind UIntType[ExprStmt [10-14]: Expr [11-13]: Lit: Int(64)]: [6-14] + Expr [16-17]: Lit: Int(4) + Expr [0-21]: Paren: + Expr [19-20]: Lit: Int(0)"#]], + ); +} + +#[test] +fn cast_to_float_array() { + check( + expr, + "array[float[64], 4](0)", + &expect![[r#" + Expr [0-22]: Cast [0-22]: + ArrayType [0-19]: ArrayBaseTypeKind FloatType[ExprStmt [11-15]: Expr [12-14]: Lit: Int(64)]: [6-15] + Expr [17-18]: Lit: Int(4) + Expr [0-22]: Paren: + Expr [20-21]: Lit: Int(0)"#]], + ); +} + +#[test] +fn cast_to_angle_array() { + check( + expr, + "array[angle[64], 4](0)", + &expect![[r#" + Expr [0-22]: Cast [0-22]: + ArrayType [0-19]: ArrayBaseTypeKind AngleType [6-15]: ExprStmt [11-15]: Expr [12-14]: Lit: Int(64) + Expr [17-18]: Lit: Int(4) + Expr [0-22]: Paren: + Expr [20-21]: Lit: Int(0)"#]], + ); +} + +#[test] +fn cast_to_bool_array() { + check( + expr, + "array[bool, 4](0)", + &expect![[r#" + Expr [0-17]: Cast [0-17]: + ArrayType [0-14]: ArrayBaseTypeKind BoolType + Expr [12-13]: Lit: Int(4) + Expr [0-17]: Paren: + Expr [15-16]: Lit: Int(0)"#]], + ); +} + +#[test] +fn cast_to_duration_array() { + check( + expr, + "array[duration, 4](0)", + &expect![[r#" + Expr [0-21]: Cast [0-21]: + ArrayType [0-18]: ArrayBaseTypeKind DurationType + Expr [16-17]: Lit: Int(4) + Expr [0-21]: Paren: + Expr [19-20]: Lit: Int(0)"#]], + ); +} + +#[test] +fn cast_to_complex_array() { + check( + expr, + "array[complex[float[32]], 4](0)", + &expect![[r#" + Expr [0-31]: Cast [0-31]: + ArrayType [0-28]: ArrayBaseTypeKind ComplexType[float[FloatType[ExprStmt [19-23]: Expr [20-22]: Lit: Int(32)]: [14-23]]]: [6-24] + Expr [26-27]: Lit: Int(4) + Expr [0-31]: Paren: + Expr [29-30]: Lit: Int(0)"#]], + ); +} + +#[test] +fn index_expr() { + check( + expr, + "foo[1]", + &expect![[r#" + Expr [0-6]: IndexExpr [3-6]: Expr [0-3]: Ident [0-3] "foo", IndexElement: + IndexSetItem Expr [4-5]: Lit: Int(1)"#]], + ); +} + +#[test] +fn index_set() { + check( + expr, + "foo[{1, 4, 5}]", + &expect![[r#" + Expr [0-14]: IndexExpr [3-14]: Expr [0-3]: Ident [0-3] "foo", IndexElement DiscreteSet [4-13]: + Expr [5-6]: Lit: Int(1) + Expr [8-9]: Lit: Int(4) + Expr [11-12]: Lit: Int(5)"#]], + ); +} + +#[test] +fn index_multiple_ranges() { + check( + expr, + "foo[1:5, 3:7, 4:8]", + &expect![[r#" + Expr [0-18]: IndexExpr [3-18]: Expr [0-3]: Ident [0-3] "foo", IndexElement: + Range: [4-7] + Expr [4-5]: Lit: Int(1) + + Expr [6-7]: Lit: Int(5) + Range: [9-12] + Expr [9-10]: Lit: Int(3) + + Expr [11-12]: Lit: Int(7) + Range: [14-17] + Expr [14-15]: Lit: Int(4) + + Expr [16-17]: Lit: Int(8)"#]], + ); +} + +#[test] +fn index_range() { + check( + expr, + "foo[1:5:2]", + &expect![[r#" + Expr [0-10]: IndexExpr [3-10]: Expr [0-3]: Ident [0-3] "foo", IndexElement: + Range: [4-9] + Expr [4-5]: Lit: Int(1) + Expr [8-9]: Lit: Int(2) + Expr [6-7]: Lit: Int(5)"#]], + ); +} + +#[test] +fn index_full_range() { + check( + expr, + "foo[:]", + &expect![[r#" + Expr [0-6]: IndexExpr [3-6]: Expr [0-3]: Ident [0-3] "foo", IndexElement: + Range: [4-5] + + + "#]], + ); +} + +#[test] +fn index_range_start() { + check( + expr, + "foo[1:]", + &expect![[r#" + Expr [0-7]: IndexExpr [3-7]: Expr [0-3]: Ident [0-3] "foo", IndexElement: + Range: [4-6] + Expr [4-5]: Lit: Int(1) + + "#]], + ); +} + +#[test] +fn index_range_end() { + check( + expr, + "foo[:5]", + &expect![[r#" + Expr [0-7]: IndexExpr [3-7]: Expr [0-3]: Ident [0-3] "foo", IndexElement: + Range: [4-6] + + + Expr [5-6]: Lit: Int(5)"#]], + ); +} + +#[test] +fn index_range_step() { + check( + expr, + "foo[::2]", + &expect![[r#" + Expr [0-8]: IndexExpr [3-8]: Expr [0-3]: Ident [0-3] "foo", IndexElement: + Range: [4-7] + + Expr [6-7]: Lit: Int(2) + "#]], + ); +} + +#[test] +fn set_expr() { + check( + super::set_expr, + "{2, 3, 4}", + &expect![[r#" + DiscreteSet [0-9]: + Expr [1-2]: Lit: Int(2) + Expr [4-5]: Lit: Int(3) + Expr [7-8]: Lit: Int(4)"#]], + ); +} + +#[test] +fn assignment_and_unop() { + check( + crate::parser::stmt::parse, + "bool c = a && !b;", + &expect![[r#" + Stmt [0-17] + StmtKind: ClassicalDeclarationStmt [0-17]: ClassicalType [0-4]: BoolType, Ident [5-6] "c", ValueExpression ExprStmt [9-16]: Expr [9-16]: BinOp (AndL): + Expr [9-10]: Ident [9-10] "a" + Expr [14-16]: UnOp (NotL): + Expr [15-16]: Ident [15-16] "b""#]], + ); +} + +#[test] +fn assignment_unop_and() { + check( + crate::parser::stmt::parse, + "bool d = !a && b;", + &expect![[r#" + Stmt [0-17] + StmtKind: ClassicalDeclarationStmt [0-17]: ClassicalType [0-4]: BoolType, Ident [5-6] "d", ValueExpression ExprStmt [9-16]: Expr [9-16]: BinOp (AndL): + Expr [9-11]: UnOp (NotL): + Expr [10-11]: Ident [10-11] "a" + Expr [15-16]: Ident [15-16] "b""#]], + ); +} diff --git a/compiler/qsc_qasm3/src/parser/stmt.rs b/compiler/qsc_qasm3/src/parser/stmt.rs index 1de4ac2533..54de2376f3 100644 --- a/compiler/qsc_qasm3/src/parser/stmt.rs +++ b/compiler/qsc_qasm3/src/parser/stmt.rs @@ -276,7 +276,7 @@ fn parse_io_decl(s: &mut ParserContext) -> Result { Ok(StmtKind::IODeclaration(decl)) } -fn scalar_or_array_type(s: &mut ParserContext) -> Result { +pub fn scalar_or_array_type(s: &mut ParserContext) -> Result { if let Ok(v) = scalar_type(s) { return Ok(TypeDef::Scalar(v)); } @@ -444,7 +444,6 @@ fn scalar_int_type(s: &mut ParserContext) -> Result { } fn array_int_type(s: &mut ParserContext) -> Result { - token(s, TokenKind::Type(Type::Int))?; let ty = int_type(s)?; Ok(ArrayBaseTypeKind::Int(ty)) } @@ -468,7 +467,6 @@ fn scalar_uint_type(s: &mut ParserContext) -> Result { } fn array_uint_type(s: &mut ParserContext) -> Result { - token(s, TokenKind::Type(Type::UInt))?; let ty = uint_type(s)?; Ok(ArrayBaseTypeKind::UInt(ty)) } @@ -493,7 +491,6 @@ fn scalar_float_type(s: &mut ParserContext) -> Result { } fn array_float_type(s: &mut ParserContext) -> Result { - token(s, TokenKind::Type(Type::Float))?; let ty = float_type(s)?; Ok(ArrayBaseTypeKind::Float(ty)) } @@ -518,7 +515,6 @@ fn scalar_angle_type(s: &mut ParserContext) -> Result { } fn array_angle_type(s: &mut ParserContext) -> Result { - token(s, TokenKind::Type(Type::Angle))?; let ty = angle_type(s)?; Ok(ArrayBaseTypeKind::Angle(ty)) } From ecb1623969777e5ae5819e77454cdfd0036e150d Mon Sep 17 00:00:00 2001 From: orpuente-MS <156957451+orpuente-MS@users.noreply.github.com> Date: Wed, 12 Feb 2025 12:36:25 -0800 Subject: [PATCH 008/108] Parse qasm3 switch statements (#2178) --- compiler/qsc_qasm3/src/ast.rs | 56 +++++- compiler/qsc_qasm3/src/lex/cooked.rs | 49 +++--- compiler/qsc_qasm3/src/lex/cooked/tests.rs | 44 ++++- .../qsc_qasm3/src/parser/completion/tests.rs | 4 +- compiler/qsc_qasm3/src/parser/error.rs | 8 + compiler/qsc_qasm3/src/parser/expr.rs | 2 +- compiler/qsc_qasm3/src/parser/stmt.rs | 65 ++++++- compiler/qsc_qasm3/src/parser/stmt/tests.rs | 1 + .../qsc_qasm3/src/parser/stmt/tests/pragma.rs | 2 +- .../src/parser/stmt/tests/switch_stmt.rs | 165 ++++++++++++++++++ 10 files changed, 359 insertions(+), 37 deletions(-) create mode 100644 compiler/qsc_qasm3/src/parser/stmt/tests/switch_stmt.rs diff --git a/compiler/qsc_qasm3/src/ast.rs b/compiler/qsc_qasm3/src/ast.rs index 0fa41bc626..9a16e08a0e 100644 --- a/compiler/qsc_qasm3/src/ast.rs +++ b/compiler/qsc_qasm3/src/ast.rs @@ -21,6 +21,7 @@ fn set_indentation<'a, 'b>( 0 => indent.with_str(""), 1 => indent.with_str(" "), 2 => indent.with_str(" "), + 3 => indent.with_str(" "), _ => unimplemented!("indentation level not supported"), } } @@ -28,7 +29,11 @@ fn set_indentation<'a, 'b>( // TODO: profile this with iai-callgrind in a large OpenQASM3 // sample to verify that is actually faster than using Vec. /// An alternative to `Vec` that uses less stack space. -type List = Box<[Box]>; +pub(crate) type List = Box<[Box]>; + +pub(crate) fn list_from_iter(vals: impl IntoIterator) -> List { + vals.into_iter().map(Box::new).collect() +} #[derive(Clone, Debug)] pub struct Program { @@ -1551,20 +1556,59 @@ impl Display for EnumerableSet { #[derive(Clone, Debug)] pub struct SwitchStmt { pub span: Span, - pub target: ExprStmt, - pub cases: List<(List, CompoundStmt)>, + pub target: Expr, + pub cases: List<(List, Block)>, /// Note that `None` is quite different to `[]` in this case; the latter is /// an explicitly empty body, whereas the absence of a default might mean /// that the switch is inexhaustive, and a linter might want to complain. - pub default: Option, + pub default: Option, } impl Display for SwitchStmt { - fn fmt(&self, _f: &mut Formatter<'_>) -> fmt::Result { - todo!("SwitchStmt display"); + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + write!(f, "SwitchStmt {}:", self.span)?; + let mut indent = set_indentation(indented(f), 1); + write!(indent, "\nTarget: {}", self.target)?; + if self.cases.is_empty() { + write!(indent, "\n")?; + } else { + write!(indent, "\nCases:")?; + for elt in &self.cases { + let (labels, block) = &**elt; + indent = display_switch_case(indent, labels, block)?; + } + } + if let Some(default) = &self.default { + write!(indent, "\nDefault Case:")?; + indent = set_indentation(indent, 2); + write!(indent, "\n{default}")?; + } else { + write!(indent, "\n")?; + } + Ok(()) } } +fn display_switch_case<'a, 'b>( + mut indent: Indented<'a, Formatter<'b>>, + labels: &List, + block: &Block, +) -> Result>, core::fmt::Error> { + indent = set_indentation(indent, 2); + if labels.is_empty() { + write!(indent, "\n")?; + } else { + write!(indent, "\nLabels:")?; + indent = set_indentation(indent, 3); + for label in labels { + write!(indent, "\n{label}")?; + } + } + indent = set_indentation(indent, 2); + write!(indent, "\n{block}")?; + Ok(indent) +} + #[derive(Clone, Debug)] pub struct ClassicalAssignment { pub span: Span, diff --git a/compiler/qsc_qasm3/src/lex/cooked.rs b/compiler/qsc_qasm3/src/lex/cooked.rs index d4f83c2c94..8838cda2b7 100644 --- a/compiler/qsc_qasm3/src/lex/cooked.rs +++ b/compiler/qsc_qasm3/src/lex/cooked.rs @@ -509,11 +509,15 @@ impl<'a> Lexer<'a> { raw::TokenKind::Ident => { let ident = &self.input[(token.offset as usize)..(self.offset() as usize)]; let cooked_ident = Self::ident(ident); - if matches!(cooked_ident, TokenKind::Keyword(Keyword::Pragma)) { - self.eat_to_end_of_line(); - Ok(Some(TokenKind::Pragma)) - } else { - Ok(Some(cooked_ident)) + match cooked_ident { + // A `dim` token without a `#` in front should be + // treated as an identifier and not as a keyword. + TokenKind::Dim => Ok(Some(TokenKind::Identifier)), + TokenKind::Keyword(Keyword::Pragma) => { + self.eat_to_end_of_line(); + Ok(Some(TokenKind::Pragma)) + } + _ => Ok(Some(cooked_ident)), } } raw::TokenKind::HardwareQubit => Ok(Some(TokenKind::HardwareQubit)), @@ -553,23 +557,26 @@ impl<'a> Lexer<'a> { } } raw::TokenKind::Single(Single::Sharp) => { - let complete = TokenKind::Pragma; - self.expect(raw::TokenKind::Ident, complete)?; + self.expect(raw::TokenKind::Ident, TokenKind::Identifier)?; let ident = &self.input[(token.offset as usize + 1)..(self.offset() as usize)]; - if matches!(Self::ident(ident), TokenKind::Keyword(Keyword::Pragma)) { - self.eat_to_end_of_line(); - Ok(Some(complete)) - } else { - let span = Span { - lo: token.offset, - hi: self.offset(), - }; - Err(Error::Incomplete( - raw::TokenKind::Ident, - complete, - raw::TokenKind::Ident, - span, - )) + match Self::ident(ident) { + TokenKind::Dim => Ok(Some(TokenKind::Dim)), + TokenKind::Keyword(Keyword::Pragma) => { + self.eat_to_end_of_line(); + Ok(Some(TokenKind::Pragma)) + } + _ => { + let span = Span { + lo: token.offset, + hi: self.offset(), + }; + Err(Error::Incomplete( + raw::TokenKind::Ident, + TokenKind::Pragma, + raw::TokenKind::Ident, + span, + )) + } } } raw::TokenKind::Single(single) => self.single(single).map(Some), diff --git a/compiler/qsc_qasm3/src/lex/cooked/tests.rs b/compiler/qsc_qasm3/src/lex/cooked/tests.rs index 5b6df0c593..b025519177 100644 --- a/compiler/qsc_qasm3/src/lex/cooked/tests.rs +++ b/compiler/qsc_qasm3/src/lex/cooked/tests.rs @@ -1003,7 +1003,7 @@ fn unknown() { Err( Incomplete( Ident, - Pragma, + Identifier, Single( Sharp, ), @@ -1016,7 +1016,7 @@ fn unknown() { Err( IncompleteEof( Ident, - Pragma, + Identifier, Span { lo: 2, hi: 2, @@ -1187,3 +1187,43 @@ fn sharp_pragma_ident() { "#]], ); } + +#[test] +fn dim() { + check( + "dim", + &expect![[r#" + [ + Ok( + Token { + kind: Identifier, + span: Span { + lo: 0, + hi: 3, + }, + }, + ), + ] + "#]], + ); +} + +#[test] +fn sharp_dim() { + check( + "#dim", + &expect![[r#" + [ + Ok( + Token { + kind: Dim, + span: Span { + lo: 0, + hi: 4, + }, + }, + ), + ] + "#]], + ); +} diff --git a/compiler/qsc_qasm3/src/parser/completion/tests.rs b/compiler/qsc_qasm3/src/parser/completion/tests.rs index 127c972cd1..f15e41a7f2 100644 --- a/compiler/qsc_qasm3/src/parser/completion/tests.rs +++ b/compiler/qsc_qasm3/src/parser/completion/tests.rs @@ -36,7 +36,7 @@ fn begin_document() { "|OPENQASM 3;", &expect![[r#" WordKinds( - Annotation | Include | Input | OpenQASM | Output | Pragma | Qubit, + Annotation | Include | Input | OpenQASM | Output | Pragma | Qubit | Switch, ) "#]], ); @@ -48,7 +48,7 @@ fn end_of_version() { "OPENQASM 3;|", &expect![[r#" WordKinds( - Annotation | Include | Input | Output | Pragma | Qubit, + Annotation | Include | Input | Output | Pragma | Qubit | Switch, ) "#]], ); diff --git a/compiler/qsc_qasm3/src/parser/error.rs b/compiler/qsc_qasm3/src/parser/error.rs index 34ca512e21..a4853cc3b4 100644 --- a/compiler/qsc_qasm3/src/parser/error.rs +++ b/compiler/qsc_qasm3/src/parser/error.rs @@ -113,6 +113,12 @@ pub enum ErrorKind { #[error("missing entry in sequence")] #[diagnostic(code("Qasm3.Parse.MissingSeqEntry"))] MissingSeqEntry(#[label] Span), + #[error("missing switch statement cases")] + #[diagnostic(code("Qasm3.Parse.MissingSwitchCases"))] + MissingSwitchCases(#[label] Span), + #[error("missing switch statement case labels")] + #[diagnostic(code("Qasm3.Parse.MissingSwitchCaseLabels"))] + MissingSwitchCaseLabels(#[label] Span), #[error("expected an item or closing brace, found {0}")] #[diagnostic(code("Qasm3.Parse.ExpectedItem"))] ExpectedItem(TokenKind, #[label] Span), @@ -131,6 +137,8 @@ impl ErrorKind { Self::MissingParens(span) => Self::MissingParens(span + offset), Self::FloatingAnnotation(span) => Self::FloatingAnnotation(span + offset), Self::MissingSeqEntry(span) => Self::MissingSeqEntry(span + offset), + Self::MissingSwitchCases(span) => Self::MissingSwitchCases(span + offset), + Self::MissingSwitchCaseLabels(span) => Self::MissingSwitchCaseLabels(span + offset), Self::ExpectedItem(token, span) => Self::ExpectedItem(token, span + offset), } } diff --git a/compiler/qsc_qasm3/src/parser/expr.rs b/compiler/qsc_qasm3/src/parser/expr.rs index 7486d20083..7940f11b52 100644 --- a/compiler/qsc_qasm3/src/parser/expr.rs +++ b/compiler/qsc_qasm3/src/parser/expr.rs @@ -409,7 +409,7 @@ fn lit_bigint(lexeme: &str, radix: u32) -> Option { } } -fn paren_expr(s: &mut ParserContext, lo: u32) -> Result { +pub(crate) fn paren_expr(s: &mut ParserContext, lo: u32) -> Result { let (mut exprs, final_sep) = seq(s, expr)?; token(s, TokenKind::Close(Delim::Paren))?; diff --git a/compiler/qsc_qasm3/src/parser/stmt.rs b/compiler/qsc_qasm3/src/parser/stmt.rs index 54de2376f3..34b5bf2a68 100644 --- a/compiler/qsc_qasm3/src/parser/stmt.rs +++ b/compiler/qsc_qasm3/src/parser/stmt.rs @@ -17,11 +17,13 @@ use super::{ }; use crate::{ ast::{ - AngleType, Annotation, ArrayBaseTypeKind, ArrayType, BitType, Block, - ClassicalDeclarationStmt, ComplexType, ConstantDeclaration, ExprStmt, FloatType, - IODeclaration, IOKeyword, IncludeStmt, IntType, LiteralKind, Pragma, QubitDeclaration, - ScalarType, ScalarTypeKind, Stmt, StmtKind, TypeDef, UIntType, + list_from_iter, AngleType, Annotation, ArrayBaseTypeKind, ArrayType, BitType, Block, + ClassicalDeclarationStmt, ComplexType, ConstantDeclaration, Expr, ExprStmt, FloatType, + IODeclaration, IOKeyword, IncludeStmt, IntType, List, LiteralKind, Pragma, + QubitDeclaration, ScalarType, ScalarTypeKind, Stmt, StmtKind, SwitchStmt, TypeDef, + UIntType, }, + keyword::Keyword, lex::{ cooked::{Literal, Type}, Delim, TokenKind, @@ -52,6 +54,8 @@ pub(super) fn parse(s: &mut ParserContext) -> Result> { Box::new(v) } else if let Some(decl) = opt(s, parse_local)? { Box::new(decl) + } else if let Some(switch) = opt(s, parse_switch_stmt)? { + Box::new(StmtKind::Switch(switch)) } else { return Err(Error::new(ErrorKind::Rule( "statement", @@ -598,3 +602,56 @@ pub(super) fn complex_subtype(s: &mut ParserContext) -> Result { token(s, TokenKind::Close(Delim::Bracket))?; Ok(ty) } + +/// The Language Spec and the grammar for switch statements disagree. +/// We followed the Spec when writing the parser +/// . +pub fn parse_switch_stmt(s: &mut ParserContext) -> Result { + let lo = s.peek().span.lo; + token(s, TokenKind::Keyword(Keyword::Switch))?; + + // Controlling expression. + token(s, TokenKind::Open(Delim::Paren))?; + let controlling_expr = expr::paren_expr(s, lo)?; + + // Open cases bracket. + token(s, TokenKind::Open(Delim::Brace))?; + + // Cases. + let lo_cases = s.peek().span.lo; + let cases = list_from_iter(many(s, case_stmt)?); + if cases.is_empty() { + s.push_error(Error::new(ErrorKind::MissingSwitchCases(s.span(lo_cases)))); + } + + // Default case. + let default = opt(s, default_case_stmt)?; + + // Close cases bracket. + recovering_token(s, TokenKind::Close(Delim::Brace)); + + Ok(SwitchStmt { + span: s.span(lo), + target: controlling_expr, + cases, + default, + }) +} + +fn case_stmt(s: &mut ParserContext) -> Result<(List, Block)> { + let lo = s.peek().span.lo; + token(s, TokenKind::Keyword(Keyword::Case))?; + + let controlling_label = expr::expr_list(s)?; + if controlling_label.is_empty() { + s.push_error(Error::new(ErrorKind::MissingSwitchCaseLabels(s.span(lo)))); + } + + let block = parse_block(s).map(|block| *block)?; + Ok((list_from_iter(controlling_label), block)) +} + +fn default_case_stmt(s: &mut ParserContext) -> Result { + token(s, TokenKind::Keyword(Keyword::Default))?; + parse_block(s).map(|block| *block) +} diff --git a/compiler/qsc_qasm3/src/parser/stmt/tests.rs b/compiler/qsc_qasm3/src/parser/stmt/tests.rs index fed929c480..73372ce1d3 100644 --- a/compiler/qsc_qasm3/src/parser/stmt/tests.rs +++ b/compiler/qsc_qasm3/src/parser/stmt/tests.rs @@ -6,3 +6,4 @@ mod classical_decl; mod io_decl; mod pragma; mod quantum_decl; +mod switch_stmt; diff --git a/compiler/qsc_qasm3/src/parser/stmt/tests/pragma.rs b/compiler/qsc_qasm3/src/parser/stmt/tests/pragma.rs index 54c822619d..83319befae 100644 --- a/compiler/qsc_qasm3/src/parser/stmt/tests/pragma.rs +++ b/compiler/qsc_qasm3/src/parser/stmt/tests/pragma.rs @@ -89,7 +89,7 @@ fn legacy_pragma_ws_after_hash() { Lex( Incomplete( Ident, - Pragma, + Identifier, Whitespace, Span { lo: 1, diff --git a/compiler/qsc_qasm3/src/parser/stmt/tests/switch_stmt.rs b/compiler/qsc_qasm3/src/parser/stmt/tests/switch_stmt.rs new file mode 100644 index 0000000000..9968592f55 --- /dev/null +++ b/compiler/qsc_qasm3/src/parser/stmt/tests/switch_stmt.rs @@ -0,0 +1,165 @@ +use expect_test::expect; + +use crate::parser::{stmt::parse_switch_stmt, tests::check}; + +#[test] +fn simple_switch() { + check( + parse_switch_stmt, + " + switch (x) { + case 1 {} + default {} + } + ", + &expect![[r#" + SwitchStmt [9-72]: + Target: Expr [9-19]: Paren: + Expr [17-18]: Ident [17-18] "x" + Cases: + Labels: + Expr [37-38]: Lit: Int(1) + Block [39-41]: + Default Case: + Block [60-62]: "#]], + ); +} + +#[test] +fn no_cases_no_default() { + check( + parse_switch_stmt, + " + switch (x) {} + ", + &expect![[r#" + SwitchStmt [9-22]: + Target: Expr [9-19]: Paren: + Expr [17-18]: Ident [17-18] "x" + + + + [ + Error( + MissingSwitchCases( + Span { + lo: 21, + hi: 21, + }, + ), + ), + ]"#]], + ); +} + +#[test] +fn no_cases() { + check( + parse_switch_stmt, + " + switch (x) { + default {} + } + ", + &expect![[r#" + SwitchStmt [9-52]: + Target: Expr [9-19]: Paren: + Expr [17-18]: Ident [17-18] "x" + + Default Case: + Block [40-42]: + + [ + Error( + MissingSwitchCases( + Span { + lo: 32, + hi: 21, + }, + ), + ), + ]"#]], + ); +} + +#[test] +fn no_default() { + check( + parse_switch_stmt, + " + switch (x) { + case 0, 1 {} + } + ", + &expect![[r#" + SwitchStmt [9-54]: + Target: Expr [9-19]: Paren: + Expr [17-18]: Ident [17-18] "x" + Cases: + Labels: + Expr [37-38]: Lit: Int(0) + Expr [40-41]: Lit: Int(1) + Block [42-44]: + "#]], + ); +} + +#[test] +fn case_with_no_labels() { + check( + parse_switch_stmt, + " + switch (x) { + case {} + } + ", + &expect![[r#" + SwitchStmt [9-49]: + Target: Expr [9-19]: Paren: + Expr [17-18]: Ident [17-18] "x" + Cases: + + Block [37-39]: + + + [ + Error( + MissingSwitchCaseLabels( + Span { + lo: 32, + hi: 36, + }, + ), + ), + ]"#]], + ); +} + +#[test] +fn multiple_cases() { + check( + parse_switch_stmt, + " + switch (x) { + case 0 { int x = 0; } + case 1 { int y = 1; } + } + ", + &expect![[r#" + SwitchStmt [9-95]: + Target: Expr [9-19]: Paren: + Expr [17-18]: Ident [17-18] "x" + Cases: + Labels: + Expr [37-38]: Lit: Int(0) + Block [39-53]: + Stmt [41-51] + StmtKind: ClassicalDeclarationStmt [41-51]: ClassicalType [41-44]: IntType [41-44], Ident [45-46] "x", ValueExpression ExprStmt [49-50]: Expr [49-50]: Lit: Int(0) + Labels: + Expr [69-70]: Lit: Int(1) + Block [71-85]: + Stmt [73-83] + StmtKind: ClassicalDeclarationStmt [73-83]: ClassicalType [73-76]: IntType [73-76], Ident [77-78] "y", ValueExpression ExprStmt [81-82]: Expr [81-82]: Lit: Int(1) + "#]], + ); +} From 8a559565721a6a617581450aed16c02fe43f641a Mon Sep 17 00:00:00 2001 From: Ian Davis Date: Wed, 12 Feb 2025 15:01:24 -0800 Subject: [PATCH 009/108] Add subroutine, return, and gate defs, externs, and old style decls (#2176) --- compiler/qsc_qasm3/src/ast.rs | 205 +++++++++---- .../qsc_qasm3/src/parser/completion/tests.rs | 4 +- compiler/qsc_qasm3/src/parser/expr.rs | 2 +- compiler/qsc_qasm3/src/parser/prim.rs | 10 +- compiler/qsc_qasm3/src/parser/stmt.rs | 278 ++++++++++++++++-- compiler/qsc_qasm3/src/parser/stmt/tests.rs | 4 + .../qsc_qasm3/src/parser/stmt/tests/def.rs | 165 +++++++++++ .../src/parser/stmt/tests/extern_decl.rs | 229 +++++++++++++++ .../src/parser/stmt/tests/gate_def.rs | 144 +++++++++ .../src/parser/stmt/tests/old_style_decl.rs | 52 ++++ 10 files changed, 1009 insertions(+), 84 deletions(-) create mode 100644 compiler/qsc_qasm3/src/parser/stmt/tests/def.rs create mode 100644 compiler/qsc_qasm3/src/parser/stmt/tests/extern_decl.rs create mode 100644 compiler/qsc_qasm3/src/parser/stmt/tests/gate_def.rs create mode 100644 compiler/qsc_qasm3/src/parser/stmt/tests/old_style_decl.rs diff --git a/compiler/qsc_qasm3/src/ast.rs b/compiler/qsc_qasm3/src/ast.rs index 9a16e08a0e..5814afc38f 100644 --- a/compiler/qsc_qasm3/src/ast.rs +++ b/compiler/qsc_qasm3/src/ast.rs @@ -746,27 +746,45 @@ impl Display for ClassicalArgument { } #[derive(Clone, Debug)] -pub struct ExternArgument { - pub span: Span, - pub r#type: ScalarType, - pub access: Option, +pub enum ExternParameter { + Scalar(ScalarType, Span), + Quantum(Option, Span), + ArrayReference(ArrayReferenceType, Span), } -impl Display for ExternArgument { +impl Display for ExternParameter { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { - if let Some(access) = &self.access { - write!( - f, - "ExternArgument {}: {}, {}", - self.span, self.r#type, access - ) - } else { - write!(f, "ExternArgument {}: {}", self.span, self.r#type) + match self { + ExternParameter::Scalar(ty, span) => { + write!(f, "{span}: {ty}") + } + ExternParameter::Quantum(expr, span) => { + write!(f, "{span}: {expr:?}") + } + ExternParameter::ArrayReference(ty, span) => { + write!(f, "{span}: {ty}") + } } } } -#[derive(Clone, Debug)] +impl Default for ExternParameter { + fn default() -> Self { + ExternParameter::Scalar(ScalarType::default(), Span::default()) + } +} + +impl WithSpan for ExternParameter { + fn with_span(self, span: Span) -> Self { + match self { + ExternParameter::Scalar(ty, _) => ExternParameter::Scalar(ty, span), + ExternParameter::Quantum(expr, _) => ExternParameter::Quantum(expr, span), + ExternParameter::ArrayReference(ty, _) => ExternParameter::ArrayReference(ty, span), + } + } +} + +#[derive(Clone, Debug, Default)] pub struct ScalarType { pub span: Span, pub kind: ScalarTypeKind, @@ -778,7 +796,7 @@ impl Display for ScalarType { } } -#[derive(Clone, Debug)] +#[derive(Clone, Debug, Default)] pub enum ScalarTypeKind { Bit(BitType), Int(IntType), @@ -789,6 +807,8 @@ pub enum ScalarTypeKind { BoolType, Duration, Stretch, + #[default] + Err, } impl Display for ScalarTypeKind { @@ -803,6 +823,7 @@ impl Display for ScalarTypeKind { ScalarTypeKind::BoolType => write!(f, "BoolType"), ScalarTypeKind::Duration => write!(f, "Duration"), ScalarTypeKind::Stretch => write!(f, "Stretch"), + ScalarTypeKind::Err => write!(f, "Err"), } } } @@ -966,8 +987,9 @@ impl Display for ArrayType { #[derive(Clone, Debug)] pub struct ArrayReferenceType { pub span: Span, + pub mutability: AccessControl, pub base_type: ArrayBaseTypeKind, - pub dimensions: List, + pub dimensions: List, } impl Display for ArrayReferenceType { @@ -1061,7 +1083,7 @@ impl Display for IncludeStmt { #[derive(Clone, Debug)] pub struct QubitDeclaration { pub span: Span, - pub qubit: Ident, + pub qubit: Box, pub size: Option, } @@ -1082,23 +1104,40 @@ impl Display for QubitDeclaration { #[derive(Clone, Debug)] pub struct QuantumGateDefinition { pub span: Span, - pub name: Identifier, - pub arguments: Vec, - pub qubits: Vec, - pub body: Vec, + pub ident: Box, + pub params: List, + pub qubits: List, + pub body: Box, } impl Display for QuantumGateDefinition { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { let mut indent = set_indentation(indented(f), 0); - write!(indent, "QuantumGateDefinition {}: {}", self.span, self.name)?; - for arg in &self.arguments { - write!(indent, "\n{arg}")?; - } - for qubit in &self.qubits { - write!(indent, "\n{qubit}")?; - } - for stmt in &self.body { + write!(indent, "Gate {}: {}", self.span, self.ident)?; + write!(indent, "(")?; + if self.params.is_empty() { + write!(indent, "")?; + } else { + let param_str = self + .params + .iter() + .map(std::string::ToString::to_string) + .collect::>() + .join(", "); + write!(indent, "{param_str}")?; + } + write!(indent, ") ")?; + + let qubit_str = self + .qubits + .iter() + .map(std::string::ToString::to_string) + .collect::>() + .join(", "); + write!(indent, "{qubit_str}")?; + + writeln!(indent)?; + for stmt in &self.body.stmts { write!(indent, "\n{stmt}")?; } Ok(()) @@ -1108,16 +1147,16 @@ impl Display for QuantumGateDefinition { #[derive(Clone, Debug)] pub struct ExternDecl { pub span: Span, - pub name: Identifier, - pub arguments: List, + pub ident: Box, + pub params: List, pub return_type: Option, } impl Display for ExternDecl { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { let mut indent = set_indentation(indented(f), 0); - write!(indent, "ExternDecl {}: {}", self.span, self.name)?; - for arg in &self.arguments { + write!(indent, "ExternDecl {}: {}", self.span, self.ident)?; + for arg in &self.params { write!(indent, "\n{arg}")?; } if let Some(return_type) = &self.return_type { @@ -1141,7 +1180,7 @@ impl Display for QuantumStmt { #[derive(Clone, Debug)] pub enum QuantumStmtKind { - Gate(QuantumGate), + Gate(GateCall), Phase(QuantumPhase), Barrier(List), Reset(List>), @@ -1152,18 +1191,18 @@ pub enum QuantumStmtKind { impl Display for QuantumStmtKind { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { match self { - QuantumStmtKind::Gate(gate) => write!(f, "QuantumStmtKind {gate}"), - QuantumStmtKind::Phase(phase) => write!(f, "QuantumStmtKind {phase}"), - QuantumStmtKind::Barrier(barrier) => write!(f, "QuantumStmtKind {barrier:?}"), - QuantumStmtKind::Reset(reset) => write!(f, "QuantumStmtKind {reset:?}"), - QuantumStmtKind::DelayInstruction(delay) => write!(f, "QuantumStmtKind {delay:?}"), - QuantumStmtKind::Box(box_stmt) => write!(f, "QuantumStmtKind {box_stmt:?}"), + QuantumStmtKind::Gate(gate) => write!(f, "{gate}"), + QuantumStmtKind::Phase(phase) => write!(f, "{phase}"), + QuantumStmtKind::Barrier(barrier) => write!(f, "{barrier:?}"), + QuantumStmtKind::Reset(reset) => write!(f, "{reset:?}"), + QuantumStmtKind::DelayInstruction(delay) => write!(f, "{delay:?}"), + QuantumStmtKind::Box(box_stmt) => write!(f, "{box_stmt:?}"), } } } #[derive(Clone, Debug)] -pub struct QuantumGate { +pub struct GateCall { pub span: Span, pub modifiers: List, pub name: Identifier, @@ -1172,10 +1211,10 @@ pub struct QuantumGate { pub duration: Option, } -impl Display for QuantumGate { +impl Display for GateCall { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { let mut indent = set_indentation(indented(f), 0); - write!(indent, "QuantumGate {}: {}", self.span, self.name)?; + write!(indent, "GateCall {}: {}", self.span, self.name)?; for arg in &self.args { write!(indent, "\n{arg}")?; } @@ -1311,7 +1350,7 @@ pub struct IODeclaration { pub span: Span, pub io_identifier: IOKeyword, pub r#type: TypeDef, - pub identifier: Box, + pub ident: Box, } impl Display for IODeclaration { @@ -1319,7 +1358,7 @@ impl Display for IODeclaration { write!( f, "IODeclaration {}: {}, {}, {}", - self.span, self.io_identifier, self.r#type, self.identifier + self.span, self.io_identifier, self.r#type, self.ident ) } } @@ -1412,23 +1451,79 @@ impl Display for CalibrationArgument { } } +#[derive(Clone, Debug)] +pub enum TypedParameter { + Scalar(ScalarType, Box, Span), + Quantum(Option, Box, Span), + ArrayReference(ArrayReferenceType, Box, Span), +} + +impl WithSpan for TypedParameter { + fn with_span(self, span: Span) -> Self { + match self { + TypedParameter::Scalar(scalar, ident, _) => TypedParameter::Scalar(scalar, ident, span), + TypedParameter::Quantum(expr, ident, _) => TypedParameter::Quantum(expr, ident, span), + TypedParameter::ArrayReference(array, ident, _) => { + TypedParameter::ArrayReference(array, ident, span) + } + } + } +} + +impl Display for TypedParameter { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + match self { + TypedParameter::Scalar(scalar, ident, span) => { + write!(f, "{span} {ident}: {scalar}") + } + TypedParameter::Quantum(expr, ident, span) => { + if let Some(expr) = expr { + write!(f, "{span} {ident}: qubit[{expr}]") + } else { + write!(f, "{span} {ident}: qubit") + } + } + TypedParameter::ArrayReference(array, ident, span) => { + write!(f, "{span} {ident}: {array}") + } + } + } +} + +impl Default for TypedParameter { + fn default() -> Self { + TypedParameter::Scalar(ScalarType::default(), Box::default(), Span::default()) + } +} + #[derive(Clone, Debug)] pub struct DefStmt { - span: Span, - name: Identifier, - args: List>, - body: List>, - return_type: Option, + pub span: Span, + pub name: Box, + pub params: List, + pub body: Box, + pub return_type: Option, } impl Display for DefStmt { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { let mut indent = set_indentation(indented(f), 0); write!(indent, "DefStmt {}: {}", self.span, self.name)?; - for arg in &self.args { - write!(indent, "\n{arg}")?; + write!(indent, "(")?; + if self.params.is_empty() { + write!(indent, "")?; + } else { + let param_str = self + .params + .iter() + .map(std::string::ToString::to_string) + .collect::>() + .join(", "); + write!(indent, "{param_str}")?; } - for stmt in &self.body { + write!(indent, ") ")?; + + for stmt in &self.body.stmts { write!(indent, "\n{stmt}")?; } if let Some(return_type) = &self.return_type { @@ -1455,8 +1550,8 @@ impl Display for Operand { #[derive(Clone, Debug)] pub struct ReturnStmt { - span: Span, - expr: Option>, + pub span: Span, + pub expr: Option>, } impl Display for ReturnStmt { diff --git a/compiler/qsc_qasm3/src/parser/completion/tests.rs b/compiler/qsc_qasm3/src/parser/completion/tests.rs index f15e41a7f2..0917cf49ff 100644 --- a/compiler/qsc_qasm3/src/parser/completion/tests.rs +++ b/compiler/qsc_qasm3/src/parser/completion/tests.rs @@ -36,7 +36,7 @@ fn begin_document() { "|OPENQASM 3;", &expect![[r#" WordKinds( - Annotation | Include | Input | OpenQASM | Output | Pragma | Qubit | Switch, + Annotation | CReg | Def | Extern | Gate | Include | Input | OpenQASM | Output | Pragma | QReg | Qubit | Return | Switch, ) "#]], ); @@ -48,7 +48,7 @@ fn end_of_version() { "OPENQASM 3;|", &expect![[r#" WordKinds( - Annotation | Include | Input | Output | Pragma | Qubit | Switch, + Annotation | CReg | Def | Extern | Gate | Include | Input | Output | Pragma | QReg | Qubit | Return | Switch, ) "#]], ); diff --git a/compiler/qsc_qasm3/src/parser/expr.rs b/compiler/qsc_qasm3/src/parser/expr.rs index 7940f11b52..b4953d0204 100644 --- a/compiler/qsc_qasm3/src/parser/expr.rs +++ b/compiler/qsc_qasm3/src/parser/expr.rs @@ -178,7 +178,7 @@ fn expr_base(s: &mut ParserContext) -> Result { if let Ok(id) = ident(s) { Ok(Expr { span: s.span(lo), - kind: Box::new(ExprKind::Ident(*id)), + kind: Box::new(ExprKind::Ident(id)), }) } else { Err(Error::new(ErrorKind::Rule( diff --git a/compiler/qsc_qasm3/src/parser/prim.rs b/compiler/qsc_qasm3/src/parser/prim.rs index 6ae8353a17..c3062c9bf4 100644 --- a/compiler/qsc_qasm3/src/parser/prim.rs +++ b/compiler/qsc_qasm3/src/parser/prim.rs @@ -58,15 +58,15 @@ pub(super) fn token(s: &mut ParserContext, t: TokenKind) -> Result<()> { } } -pub(super) fn ident(s: &mut ParserContext) -> Result> { +pub(super) fn ident(s: &mut ParserContext) -> Result { let peek = s.peek(); if peek.kind == TokenKind::Identifier { let name = s.read().into(); s.advance(); - Ok(Box::new(Ident { + Ok(Ident { span: peek.span, name, - })) + }) } else { Err(Error::new(ErrorKind::Rule( "identifier", @@ -92,11 +92,11 @@ pub(super) fn path( let lo = s.peek().span.lo; let i = ident(s).map_err(|e| (e, None))?; - let mut parts = vec![*i]; + let mut parts = vec![i]; while token(s, TokenKind::Dot).is_ok() { s.expect(WordKinds::PathSegment); match ident(s) { - Ok(ident) => parts.push(*ident), + Ok(ident) => parts.push(ident), Err(error) => { let trivia_span = s.skip_trivia(); let keyword = trivia_span.hi == trivia_span.lo diff --git a/compiler/qsc_qasm3/src/parser/stmt.rs b/compiler/qsc_qasm3/src/parser/stmt.rs index 34b5bf2a68..b01735615b 100644 --- a/compiler/qsc_qasm3/src/parser/stmt.rs +++ b/compiler/qsc_qasm3/src/parser/stmt.rs @@ -12,16 +12,17 @@ use super::{ completion::WordKinds, error::{Error, ErrorKind}, expr::{self, designator}, - prim::{self, barrier, many, opt, recovering, recovering_semi, recovering_token, shorten}, + prim::{self, barrier, many, opt, recovering, recovering_semi, recovering_token, seq, shorten}, Result, }; use crate::{ ast::{ - list_from_iter, AngleType, Annotation, ArrayBaseTypeKind, ArrayType, BitType, Block, - ClassicalDeclarationStmt, ComplexType, ConstantDeclaration, Expr, ExprStmt, FloatType, - IODeclaration, IOKeyword, IncludeStmt, IntType, List, LiteralKind, Pragma, - QubitDeclaration, ScalarType, ScalarTypeKind, Stmt, StmtKind, SwitchStmt, TypeDef, - UIntType, + list_from_iter, AccessControl, AngleType, Annotation, ArrayBaseTypeKind, + ArrayReferenceType, ArrayType, BitType, Block, ClassicalDeclarationStmt, ComplexType, + ConstantDeclaration, DefStmt, Expr, ExprStmt, ExternDecl, ExternParameter, FloatType, + IODeclaration, IOKeyword, Ident, IncludeStmt, IntType, List, LiteralKind, Pragma, + QuantumGateDefinition, QubitDeclaration, ReturnStmt, ScalarType, ScalarTypeKind, Stmt, + StmtKind, SwitchStmt, TypeDef, TypedParameter, UIntType, }, keyword::Keyword, lex::{ @@ -50,12 +51,20 @@ pub(super) fn parse(s: &mut ParserContext) -> Result> { s.push_error(Error::new(ErrorKind::FloatingAnnotation(err_item.span))); return Ok(err_item); } - } else if let Some(v) = opt(s, parse_include)? { - Box::new(v) + } else if let Some(decl) = opt(s, parse_gatedef)? { + Box::new(decl) + } else if let Some(decl) = opt(s, parse_def)? { + Box::new(decl) + } else if let Some(include) = opt(s, parse_include)? { + Box::new(include) } else if let Some(decl) = opt(s, parse_local)? { Box::new(decl) + } else if let Some(decl) = opt(s, parse_extern)? { + Box::new(decl) } else if let Some(switch) = opt(s, parse_switch_stmt)? { Box::new(StmtKind::Switch(switch)) + } else if let Some(decl) = opt(s, parse_return)? { + Box::new(decl) } else { return Err(Error::new(ErrorKind::Rule( "statement", @@ -220,6 +229,9 @@ fn parse_pragma(s: &mut ParserContext) -> Result { }) } +// qreg and creg are separate from classical and quantum declarations +// simply for performance reasons. The latter are more common and old +// style declarations should be rare. fn parse_local(s: &mut ParserContext) -> Result { if let Some(decl) = opt(s, parse_classical_decl)? { Ok(decl) @@ -227,6 +239,10 @@ fn parse_local(s: &mut ParserContext) -> Result { Ok(decl) } else if let Some(decl) = opt(s, parse_io_decl)? { Ok(decl) + } else if let Some(decl) = opt(s, qreg_decl)? { + Ok(decl) + } else if let Some(decl) = opt(s, creg_decl)? { + Ok(decl) } else { Err(Error::new(ErrorKind::Rule( "local declaration", @@ -236,21 +252,196 @@ fn parse_local(s: &mut ParserContext) -> Result { } } +fn parse_extern(s: &mut ParserContext) -> Result { + let lo = s.peek().span.lo; + token(s, TokenKind::Keyword(crate::keyword::Keyword::Extern))?; + let ident = Box::new(prim::ident(s)?); + token(s, TokenKind::Open(Delim::Paren))?; + let (params, _) = seq(s, extern_arg_def)?; + token(s, TokenKind::Close(Delim::Paren))?; + let return_type = opt(s, return_sig)?; + recovering_semi(s); + let kind = StmtKind::ExternDecl(ExternDecl { + span: s.span(lo), + ident, + params: list_from_iter(params), + return_type, + }); + Ok(kind) +} + +fn parse_def(s: &mut ParserContext) -> Result { + let lo = s.peek().span.lo; + token(s, TokenKind::Keyword(crate::keyword::Keyword::Def))?; + let name = Box::new(prim::ident(s)?); + token(s, TokenKind::Open(Delim::Paren))?; + let (exprs, _) = seq(s, arg_def)?; + token(s, TokenKind::Close(Delim::Paren))?; + let return_type = opt(s, return_sig)?; + let body = parse_block(s)?; + let kind = StmtKind::Def(DefStmt { + span: s.span(lo), + name, + params: list_from_iter(exprs), + body, + return_type, + }); + Ok(kind) +} + +fn extern_arg_def(s: &mut ParserContext) -> Result { + let lo = s.peek().span.lo; + + let kind = if let Ok(ty) = scalar_type(s) { + ExternParameter::Scalar(ty, s.span(lo)) + } else if let Ok(ty) = array_reference_ty(s) { + ExternParameter::ArrayReference(ty, s.span(lo)) + } else if let Ok(size) = extern_creg_type(s) { + let ty = ScalarType { + span: s.span(lo), + kind: ScalarTypeKind::Bit(BitType { + size, + span: s.span(lo), + }), + }; + ExternParameter::Scalar(ty, s.span(lo)) + } else { + return Err(Error::new(ErrorKind::Rule( + "extern argument definition", + s.peek().kind, + s.peek().span, + ))); + }; + Ok(kind) +} + +fn arg_def(s: &mut ParserContext) -> Result { + let lo = s.peek().span.lo; + + let kind = if let Ok(ty) = scalar_type(s) { + let ident = prim::ident(s)?; + TypedParameter::Scalar(ty, Box::new(ident), s.span(lo)) + } else if let Ok(size) = qubit_type(s) { + let ident = prim::ident(s)?; + TypedParameter::Quantum(size, Box::new(ident), s.span(lo)) + } else if let Ok((ident, size)) = creg_type(s) { + let ty = ScalarType { + span: s.span(lo), + kind: ScalarTypeKind::Bit(BitType { + size, + span: s.span(lo), + }), + }; + TypedParameter::Scalar(ty, ident, s.span(lo)) + } else if let Ok((ident, size)) = qreg_type(s) { + TypedParameter::Quantum(size, ident, s.span(lo)) + } else if let Ok(ty) = array_reference_ty(s) { + let ident = prim::ident(s)?; + TypedParameter::ArrayReference(ty, Box::new(ident), s.span(lo)) + } else { + return Err(Error::new(ErrorKind::Rule( + "argument definition", + s.peek().kind, + s.peek().span, + ))); + }; + Ok(kind) +} + +fn array_reference_ty(s: &mut ParserContext) -> Result { + let lo = s.peek().span.lo; + + let mutability = if token(s, TokenKind::Keyword(crate::keyword::Keyword::ReadOnly)).is_ok() { + AccessControl::ReadOnly + } else if token(s, TokenKind::Keyword(crate::keyword::Keyword::Mutable)).is_ok() { + AccessControl::Mutable + } else { + let token = s.peek(); + return Err(Error::new(ErrorKind::Rule( + "array reference declaration", + token.kind, + token.span, + ))); + }; + token(s, TokenKind::Type(Type::Array))?; + token(s, TokenKind::Open(Delim::Bracket))?; + let base_type = array_base_type(s)?; + token(s, TokenKind::Comma)?; + + let dimensions = if token(s, TokenKind::Dim).is_ok() { + token(s, TokenKind::Eq)?; + vec![expr::expr(s)?] + } else { + expr::expr_list(s)? + }; + + token(s, TokenKind::Close(Delim::Bracket))?; + Ok(ArrayReferenceType { + span: s.span(lo), + mutability, + base_type, + dimensions: list_from_iter(dimensions), + }) +} + +fn return_sig(s: &mut ParserContext) -> Result { + token(s, TokenKind::Arrow)?; + scalar_type(s) +} + +fn parse_gatedef(s: &mut ParserContext) -> Result { + let lo = s.peek().span.lo; + token(s, TokenKind::Keyword(crate::keyword::Keyword::Gate))?; + let ident = Box::new(prim::ident(s)?); + let params = opt(s, gate_params)?.unwrap_or_else(Vec::new); + let (qubits, _) = seq(s, prim::ident)?; + let body = parse_block(s)?; + Ok(StmtKind::QuantumGateDefinition(QuantumGateDefinition { + span: s.span(lo), + ident, + params: list_from_iter(params), + qubits: list_from_iter(qubits), + body, + })) +} + +fn gate_params(s: &mut ParserContext<'_>) -> Result> { + token(s, TokenKind::Open(Delim::Paren))?; + let (params, _) = seq(s, prim::ident)?; + token(s, TokenKind::Close(Delim::Paren))?; + Ok(params) +} + +fn parse_return(s: &mut ParserContext) -> Result { + let lo = s.peek().span.lo; + token(s, TokenKind::Keyword(crate::keyword::Keyword::Return))?; + let expr = opt(s, expr::value_expr)?; + recovering_semi(s); + Ok(StmtKind::Return(ReturnStmt { + span: s.span(lo), + expr, + })) +} + fn parse_quantum_decl(s: &mut ParserContext) -> Result { let lo = s.peek().span.lo; - s.expect(WordKinds::Qubit); - token(s, TokenKind::Keyword(crate::keyword::Keyword::Qubit))?; - let size = opt(s, designator)?; - let name = prim::ident(s)?; + let size = qubit_type(s)?; + let ident = prim::ident(s)?; recovering_semi(s); Ok(StmtKind::QuantumDecl(QubitDeclaration { span: s.span(lo), - qubit: *name, + qubit: Box::new(ident), size, })) } +fn qubit_type(s: &mut ParserContext<'_>) -> Result> { + token(s, TokenKind::Keyword(crate::keyword::Keyword::Qubit))?; + let size = opt(s, designator)?; + Ok(size) +} + fn parse_io_decl(s: &mut ParserContext) -> Result { let lo = s.peek().span.lo; @@ -269,13 +460,13 @@ fn parse_io_decl(s: &mut ParserContext) -> Result { let ty = scalar_or_array_type(s)?; - let identifier = prim::ident(s)?; + let ident = Box::new(prim::ident(s)?); recovering_semi(s); let decl = IODeclaration { span: s.span(lo), io_identifier: kind, r#type: ty, - identifier, + ident, }; Ok(StmtKind::IODeclaration(decl)) } @@ -304,7 +495,7 @@ fn parse_classical_decl(s: &mut ParserContext) -> Result { }; let ty = scalar_or_array_type(s)?; - let identifier = prim::ident(s)?; + let identifier = Box::new(prim::ident(s)?); let stmt = if is_const { token(s, TokenKind::Eq)?; @@ -352,11 +543,7 @@ pub(super) fn array_type(s: &mut ParserContext) -> Result { Ok(ArrayType { base_type: kind, span: s.span(lo), - dimensions: expr_list - .into_iter() - .map(Box::new) - .collect::>() - .into_boxed_slice(), + dimensions: list_from_iter(expr_list), }) } @@ -425,6 +612,55 @@ pub(super) fn scalar_type(s: &mut ParserContext) -> Result { ))) } +fn creg_decl(s: &mut ParserContext) -> Result { + let lo: u32 = s.peek().span.lo; + let (identifier, size) = creg_type(s)?; + recovering_semi(s); + Ok(StmtKind::ClassicalDecl(ClassicalDeclarationStmt { + span: s.span(lo), + r#type: TypeDef::Scalar(ScalarType { + span: s.span(lo), + kind: ScalarTypeKind::Bit(BitType { + size, + span: s.span(lo), + }), + }), + identifier, + init_expr: None, + })) +} + +fn qreg_decl(s: &mut ParserContext) -> Result { + let lo = s.peek().span.lo; + let (identifier, size) = qreg_type(s)?; + recovering_semi(s); + Ok(StmtKind::QuantumDecl(QubitDeclaration { + span: s.span(lo), + qubit: identifier, + size, + })) +} + +fn extern_creg_type(s: &mut ParserContext) -> Result> { + token(s, TokenKind::Keyword(crate::keyword::Keyword::CReg))?; + let size = opt(s, designator)?; + Ok(size) +} + +fn creg_type(s: &mut ParserContext) -> Result<(Box, Option)> { + token(s, TokenKind::Keyword(crate::keyword::Keyword::CReg))?; + let name = Box::new(prim::ident(s)?); + let size = opt(s, designator)?; + Ok((name, size)) +} + +fn qreg_type(s: &mut ParserContext) -> Result<(Box, Option)> { + token(s, TokenKind::Keyword(crate::keyword::Keyword::QReg))?; + let name = Box::new(prim::ident(s)?); + let size = opt(s, designator)?; + Ok((name, size)) +} + fn scalar_bit_type(s: &mut ParserContext) -> Result { let lo = s.peek().span.lo; token(s, TokenKind::Type(Type::Bit))?; diff --git a/compiler/qsc_qasm3/src/parser/stmt/tests.rs b/compiler/qsc_qasm3/src/parser/stmt/tests.rs index 73372ce1d3..f4312061ba 100644 --- a/compiler/qsc_qasm3/src/parser/stmt/tests.rs +++ b/compiler/qsc_qasm3/src/parser/stmt/tests.rs @@ -3,7 +3,11 @@ mod annotation; mod classical_decl; +mod def; +mod extern_decl; +mod gate_def; mod io_decl; +mod old_style_decl; mod pragma; mod quantum_decl; mod switch_stmt; diff --git a/compiler/qsc_qasm3/src/parser/stmt/tests/def.rs b/compiler/qsc_qasm3/src/parser/stmt/tests/def.rs new file mode 100644 index 0000000000..a67bd8b88d --- /dev/null +++ b/compiler/qsc_qasm3/src/parser/stmt/tests/def.rs @@ -0,0 +1,165 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +use expect_test::expect; + +use crate::parser::tests::check; + +use crate::parser::stmt::parse; + +#[test] +fn minimal() { + check( + parse, + "def x() { }", + &expect![[r#" + Stmt [0-11] + StmtKind: DefStmt [0-11]: Ident [4-5] "x"() "#]], + ); +} + +#[test] +fn missing_ty_error() { + check( + parse, + "def x() -> { }", + &expect![[r#" + Error( + Rule( + "scalar type", + Open( + Brace, + ), + Span { + lo: 11, + hi: 12, + }, + ), + ) + "#]], + ); +} + +#[test] +fn missing_args_with_delim_error() { + check( + parse, + "def x(,) { }", + &expect![[r#" + Stmt [0-12] + StmtKind: DefStmt [0-12]: Ident [4-5] "x"([6-6] Ident [0-0] "": ClassicalType [0-0]: Err) + + [ + Error( + MissingSeqEntry( + Span { + lo: 6, + hi: 6, + }, + ), + ), + ]"#]], + ); +} + +#[test] +fn args_with_extra_delim_err_ty() { + check( + parse, + "def x(int a,,int b) { }", + &expect![[r#" + Stmt [0-23] + StmtKind: DefStmt [0-23]: Ident [4-5] "x"([6-11] Ident [10-11] "a": ClassicalType [6-9]: IntType [6-9], [12-12] Ident [0-0] "": ClassicalType [0-0]: Err, [13-18] Ident [17-18] "b": ClassicalType [13-16]: IntType [13-16]) + + [ + Error( + MissingSeqEntry( + Span { + lo: 12, + hi: 12, + }, + ), + ), + ]"#]], + ); +} + +#[test] +fn classical_subroutine() { + check( + parse, + "def square(int[32] x) -> int { return x ** 2; }", + &expect![[r#" + Stmt [0-47] + StmtKind: DefStmt [0-47]: Ident [4-10] "square"([11-20] Ident [19-20] "x": ClassicalType [11-18]: IntType[ExprStmt [14-18]: Expr [15-17]: Lit: Int(32)]: [11-18]) + Stmt [31-45] + StmtKind: ReturnStmt [31-45]: ValueExpression ExprStmt [38-44]: Expr [38-44]: BinOp (Exp): + Expr [38-39]: Ident [38-39] "x" + Expr [43-44]: Lit: Int(2) + ClassicalType [25-28]: IntType [25-28]"#]], + ); +} + +#[test] +fn quantum_args() { + check( + parse, + "def x(qubit q, qubit[n] qubits) { }", + &expect![[r#" + Stmt [0-35] + StmtKind: DefStmt [0-35]: Ident [4-5] "x"([6-13] Ident [12-13] "q": qubit, [15-30] Ident [24-30] "qubits": qubit[ExprStmt [20-23]: Expr [21-22]: Ident [21-22] "n"]) "#]], + ); +} + +#[test] +fn old_style_args() { + check( + parse, + "def test(creg c, qreg q, creg c2[2], qreg q4[4]) -> int { return x ** 2; }", + &expect![[r#" + Stmt [0-74] + StmtKind: DefStmt [0-74]: Ident [4-8] "test"([9-15] Ident [14-15] "c": ClassicalType [9-15]: BitType, [17-23] Ident [22-23] "q": qubit, [25-35] Ident [30-32] "c2": ClassicalType [25-35]: BitType [25-35]: ExprStmt [32-35]: Expr [33-34]: Lit: Int(2), [37-47] Ident [42-44] "q4": qubit[ExprStmt [44-47]: Expr [45-46]: Lit: Int(4)]) + Stmt [58-72] + StmtKind: ReturnStmt [58-72]: ValueExpression ExprStmt [65-71]: Expr [65-71]: BinOp (Exp): + Expr [65-66]: Ident [65-66] "x" + Expr [70-71]: Lit: Int(2) + ClassicalType [52-55]: IntType [52-55]"#]], + ); +} + +#[test] +fn readonly_array_arg_with_int_dims() { + check( + parse, + "def specified_sub(readonly array[int[8], 2, 10] arr_arg) {}", + &expect![[r#" + Stmt [0-59] + StmtKind: DefStmt [0-59]: Ident [4-17] "specified_sub"([18-55] Ident [48-55] "arr_arg": ArrayReferenceType [18-47]: ArrayBaseTypeKind IntType[ExprStmt [36-39]: Expr [37-38]: Lit: Int(8)]: [33-39] + Expr [41-42]: Lit: Int(2) + Expr [44-46]: Lit: Int(10)) "#]], + ); +} + +#[test] +fn readonly_array_arg_with_dim() { + check( + parse, + "def arr_subroutine(readonly array[int[8], #dim = 1] arr_arg) {}", + &expect![[r#" + Stmt [0-63] + StmtKind: DefStmt [0-63]: Ident [4-18] "arr_subroutine"([19-59] Ident [52-59] "arr_arg": ArrayReferenceType [19-51]: ArrayBaseTypeKind IntType[ExprStmt [37-40]: Expr [38-39]: Lit: Int(8)]: [34-40] + Expr [49-50]: Lit: Int(1)) "#]], + ); +} + +#[test] +fn mutable_array_arg() { + check( + parse, + "def mut_subroutine(mutable array[int[8], #dim = 1] arr_arg) {}", + &expect![[r#" + Stmt [0-62] + StmtKind: DefStmt [0-62]: Ident [4-18] "mut_subroutine"([19-58] Ident [51-58] "arr_arg": ArrayReferenceType [19-50]: ArrayBaseTypeKind IntType[ExprStmt [36-39]: Expr [37-38]: Lit: Int(8)]: [33-39] + Expr [48-49]: Lit: Int(1)) "#]], + ); +} diff --git a/compiler/qsc_qasm3/src/parser/stmt/tests/extern_decl.rs b/compiler/qsc_qasm3/src/parser/stmt/tests/extern_decl.rs new file mode 100644 index 0000000000..8f5eddc341 --- /dev/null +++ b/compiler/qsc_qasm3/src/parser/stmt/tests/extern_decl.rs @@ -0,0 +1,229 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +use expect_test::expect; + +use crate::parser::tests::check; + +use crate::parser::stmt::parse; + +#[test] +fn missing_semicolon_err() { + check( + parse, + "extern x()", + &expect![[r#" + Stmt [0-10] + StmtKind: ExternDecl [0-10]: Ident [7-8] "x" + + [ + Error( + Token( + Semicolon, + Eof, + Span { + lo: 10, + hi: 10, + }, + ), + ), + ]"#]], + ); +} + +#[test] +fn bit_param_bit_ret_decl() { + check( + parse, + "extern x(bit) -> bit;", + &expect![[r#" + Stmt [0-21] + StmtKind: ExternDecl [0-21]: Ident [7-8] "x" + [9-12]: ClassicalType [9-12]: BitType + ClassicalType [17-20]: BitType"#]], + ); +} + +#[test] +fn sized_bit_param_bit_ret_decl() { + check( + parse, + "extern x(bit[n]) -> bit;", + &expect![[r#" + Stmt [0-24] + StmtKind: ExternDecl [0-24]: Ident [7-8] "x" + [9-15]: ClassicalType [9-15]: BitType [9-15]: ExprStmt [12-15]: Expr [13-14]: Ident [13-14] "n" + ClassicalType [20-23]: BitType"#]], + ); +} + +#[test] +fn sized_creg_param_bit_ret_decl() { + check( + parse, + "extern x(creg[n]) -> bit;", + &expect![[r#" + Stmt [0-25] + StmtKind: ExternDecl [0-25]: Ident [7-8] "x" + [9-16]: ClassicalType [9-16]: BitType [9-16]: ExprStmt [13-16]: Expr [14-15]: Ident [14-15] "n" + ClassicalType [21-24]: BitType"#]], + ); +} + +#[test] +fn creg_param_bit_ret_decl() { + check( + parse, + "extern x(creg) -> bit;", + &expect![[r#" + Stmt [0-22] + StmtKind: ExternDecl [0-22]: Ident [7-8] "x" + [9-13]: ClassicalType [9-13]: BitType + ClassicalType [18-21]: BitType"#]], + ); +} + +#[test] +fn readonly_array_arg_with_int_dims() { + check( + parse, + "extern x(readonly array[int[8], 2, 10]);", + &expect![[r#" + Stmt [0-40] + StmtKind: ExternDecl [0-40]: Ident [7-8] "x" + [9-38]: ArrayReferenceType [9-38]: ArrayBaseTypeKind IntType[ExprStmt [27-30]: Expr [28-29]: Lit: Int(8)]: [24-30] + Expr [32-33]: Lit: Int(2) + Expr [35-37]: Lit: Int(10)"#]], + ); +} + +#[test] +fn readonly_array_arg_with_dim() { + check( + parse, + "extern x(readonly array[int[8], #dim = 1]);", + &expect![[r#" + Stmt [0-43] + StmtKind: ExternDecl [0-43]: Ident [7-8] "x" + [9-41]: ArrayReferenceType [9-41]: ArrayBaseTypeKind IntType[ExprStmt [27-30]: Expr [28-29]: Lit: Int(8)]: [24-30] + Expr [39-40]: Lit: Int(1)"#]], + ); +} + +#[test] +fn mutable_array_arg() { + check( + parse, + "extern x(mutable array[int[8], #dim = 1]);", + &expect![[r#" + Stmt [0-42] + StmtKind: ExternDecl [0-42]: Ident [7-8] "x" + [9-40]: ArrayReferenceType [9-40]: ArrayBaseTypeKind IntType[ExprStmt [26-29]: Expr [27-28]: Lit: Int(8)]: [23-29] + Expr [38-39]: Lit: Int(1)"#]], + ); +} + +#[test] +fn unexpected_ident_in_params() { + check( + parse, + "extern x(creg c) -> bit;", + &expect![[r#" + Error( + Token( + Close( + Paren, + ), + Identifier, + Span { + lo: 14, + hi: 15, + }, + ), + ) + "#]], + ); +} + +#[test] +fn annotation() { + check( + parse, + r#"@test.annotation + extern x(creg) -> bit;"#, + &expect![[r#" + Stmt [0-47] + Annotation [0-16]: (test.annotation) + StmtKind: ExternDecl [25-47]: Ident [32-33] "x" + [34-38]: ClassicalType [34-38]: BitType + ClassicalType [43-46]: BitType"#]], + ); +} + +#[test] +fn missing_ty_error() { + check( + parse, + "extern x() -> ;", + &expect![[r#" + Error( + Rule( + "scalar type", + Semicolon, + Span { + lo: 14, + hi: 15, + }, + ), + ) + "#]], + ); +} + +#[test] +fn missing_args_with_delim_error() { + check( + parse, + "extern x(,);", + &expect![[r#" + Stmt [0-12] + StmtKind: ExternDecl [0-12]: Ident [7-8] "x" + [9-9]: ClassicalType [0-0]: Err + + [ + Error( + MissingSeqEntry( + Span { + lo: 9, + hi: 9, + }, + ), + ), + ]"#]], + ); +} + +#[test] +fn args_with_extra_delim_err_ty() { + check( + parse, + "extern x(int,,int);", + &expect![[r#" + Stmt [0-19] + StmtKind: ExternDecl [0-19]: Ident [7-8] "x" + [9-12]: ClassicalType [9-12]: IntType [9-12] + [13-13]: ClassicalType [0-0]: Err + [14-17]: ClassicalType [14-17]: IntType [14-17] + + [ + Error( + MissingSeqEntry( + Span { + lo: 13, + hi: 13, + }, + ), + ), + ]"#]], + ); +} diff --git a/compiler/qsc_qasm3/src/parser/stmt/tests/gate_def.rs b/compiler/qsc_qasm3/src/parser/stmt/tests/gate_def.rs new file mode 100644 index 0000000000..24b8fb4ce2 --- /dev/null +++ b/compiler/qsc_qasm3/src/parser/stmt/tests/gate_def.rs @@ -0,0 +1,144 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +use expect_test::expect; + +use crate::parser::tests::check; + +use crate::parser::stmt::parse; + +#[test] +fn no_qubits_no_classical() { + check( + parse, + "gate c0q0 {}", + &expect![[r#" + Stmt [0-12] + StmtKind: Gate [0-12]: Ident [5-9] "c0q0"() + "#]], + ); +} + +#[test] +fn no_qubits_no_classical_with_parens() { + check( + parse, + "gate c0q0() {}", + &expect![[r#" + Stmt [0-14] + StmtKind: Gate [0-14]: Ident [5-9] "c0q0"() + "#]], + ); +} + +#[test] +fn one_qubit_no_classical() { + check( + parse, + "gate c0q1 a {}", + &expect![[r#" + Stmt [0-14] + StmtKind: Gate [0-14]: Ident [5-9] "c0q1"() Ident [10-11] "a" + "#]], + ); +} + +#[test] +fn two_qubits_no_classical() { + check( + parse, + "gate c0q2 a, b {}", + &expect![[r#" + Stmt [0-17] + StmtKind: Gate [0-17]: Ident [5-9] "c0q2"() Ident [10-11] "a", Ident [13-14] "b" + "#]], + ); +} + +#[test] +fn three_qubits_trailing_comma_no_classical() { + check( + parse, + "gate c0q3 a, b, c, {}", + &expect![[r#" + Stmt [0-21] + StmtKind: Gate [0-21]: Ident [5-9] "c0q3"() Ident [10-11] "a", Ident [13-14] "b", Ident [16-17] "c" + "#]], + ); +} + +#[test] +fn no_qubits_one_classical() { + check( + parse, + "gate c1q0(a) {}", + &expect![[r#" + Stmt [0-15] + StmtKind: Gate [0-15]: Ident [5-9] "c1q0"(Ident [10-11] "a") + "#]], + ); +} + +#[test] +fn no_qubits_two_classical() { + check( + parse, + "gate c2q0(a, b) {}", + &expect![[r#" + Stmt [0-18] + StmtKind: Gate [0-18]: Ident [5-9] "c2q0"(Ident [10-11] "a", Ident [13-14] "b") + "#]], + ); +} + +#[test] +fn no_qubits_three_classical() { + check( + parse, + "gate c3q0(a, b, c) {}", + &expect![[r#" + Stmt [0-21] + StmtKind: Gate [0-21]: Ident [5-9] "c3q0"(Ident [10-11] "a", Ident [13-14] "b", Ident [16-17] "c") + "#]], + ); +} + +#[test] +fn one_qubit_one_classical() { + check( + parse, + "gate c1q1(a) b {}", + &expect![[r#" + Stmt [0-17] + StmtKind: Gate [0-17]: Ident [5-9] "c1q1"(Ident [10-11] "a") Ident [13-14] "b" + "#]], + ); +} + +#[test] +fn two_qubits_two_classical() { + check( + parse, + "gate c2q2(a, b) c, d {}", + &expect![[r#" + Stmt [0-23] + StmtKind: Gate [0-23]: Ident [5-9] "c2q2"(Ident [10-11] "a", Ident [13-14] "b") Ident [16-17] "c", Ident [19-20] "d" + "#]], + ); +} + +#[test] +fn two_qubits_two_classical_with_body() { + check( + parse, + "gate c2q2(a, b) c, d { float[32] x = a - b; }", + &expect![[r#" + Stmt [0-45] + StmtKind: Gate [0-45]: Ident [5-9] "c2q2"(Ident [10-11] "a", Ident [13-14] "b") Ident [16-17] "c", Ident [19-20] "d" + + Stmt [23-43] + StmtKind: ClassicalDeclarationStmt [23-43]: ClassicalType [23-32]: FloatType[ExprStmt [28-32]: Expr [29-31]: Lit: Int(32)]: [23-32], Ident [33-34] "x", ValueExpression ExprStmt [37-42]: Expr [37-42]: BinOp (Sub): + Expr [37-38]: Ident [37-38] "a" + Expr [41-42]: Ident [41-42] "b""#]], + ); +} diff --git a/compiler/qsc_qasm3/src/parser/stmt/tests/old_style_decl.rs b/compiler/qsc_qasm3/src/parser/stmt/tests/old_style_decl.rs new file mode 100644 index 0000000000..87db062e4c --- /dev/null +++ b/compiler/qsc_qasm3/src/parser/stmt/tests/old_style_decl.rs @@ -0,0 +1,52 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +use expect_test::expect; + +use crate::parser::tests::check; + +use crate::parser::stmt::parse; + +#[test] +fn creg_decl() { + check( + parse, + "creg c;", + &expect![[r#" + Stmt [0-7] + StmtKind: ClassicalDeclarationStmt [0-7]: ClassicalType [0-7]: BitType, Ident [5-6] "c""#]], + ); +} + +#[test] +fn creg_array_decl() { + check( + parse, + "creg c[n];", + &expect![[r#" + Stmt [0-10] + StmtKind: ClassicalDeclarationStmt [0-10]: ClassicalType [0-10]: BitType [0-10]: ExprStmt [6-9]: Expr [7-8]: Ident [7-8] "n", Ident [5-6] "c""#]], + ); +} + +#[test] +fn qreg_decl() { + check( + parse, + "qreg q;", + &expect![[r#" + Stmt [0-7] + StmtKind: QubitDeclaration [0-7]: Ident [5-6] "q""#]], + ); +} + +#[test] +fn qreg_array_decl() { + check( + parse, + "qreg q[n];", + &expect![[r#" + Stmt [0-10] + StmtKind: QubitDeclaration [0-10]: Ident [5-6] "q", ExprStmt [6-9]: Expr [7-8]: Ident [7-8] "n""#]], + ); +} From b49fc6da477dbb9ae44577b96949960100f09112 Mon Sep 17 00:00:00 2001 From: orpuente-MS <156957451+orpuente-MS@users.noreply.github.com> Date: Thu, 13 Feb 2025 13:19:52 -0800 Subject: [PATCH 010/108] Add array and measurement declaration statements (#2180) --- compiler/qsc_qasm3/src/ast.rs | 8 +- compiler/qsc_qasm3/src/parser/expr.rs | 96 +++++++++++++++++-- compiler/qsc_qasm3/src/parser/expr/tests.rs | 58 +++++++++++ .../src/parser/stmt/tests/classical_decl.rs | 69 +++++++++++++ 4 files changed, 219 insertions(+), 12 deletions(-) diff --git a/compiler/qsc_qasm3/src/ast.rs b/compiler/qsc_qasm3/src/ast.rs index 5814afc38f..e56abed25e 100644 --- a/compiler/qsc_qasm3/src/ast.rs +++ b/compiler/qsc_qasm3/src/ast.rs @@ -300,14 +300,14 @@ impl Display for UnaryOp { #[derive(Clone, Debug)] pub enum GateOperand { - Ident(Box), + IndexedIdent(Box), HardwareQubit(Box), } impl Display for GateOperand { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { match self { - GateOperand::Ident(ident) => write!(f, "GateOperand {ident}"), + GateOperand::IndexedIdent(ident) => write!(f, "GateOperand {ident}"), GateOperand::HardwareQubit(qubit) => write!(f, "GateOperand {qubit}"), } } @@ -1333,7 +1333,7 @@ impl Display for ClassicalDeclarationStmt { #[derive(Clone, Debug)] pub enum ValueExpression { Expr(ExprStmt), - Measurement(QuantumMeasurement), + Measurement(MeasureExpr), } impl Display for ValueExpression { @@ -1876,7 +1876,7 @@ impl Display for Lit { #[derive(Clone, Debug)] pub enum LiteralKind { - Array(List), + Array(List), Bitstring(BigInt, usize), Bool(bool), Duration { value: f64, unit: TimeUnit }, diff --git a/compiler/qsc_qasm3/src/parser/expr.rs b/compiler/qsc_qasm3/src/parser/expr.rs index b4953d0204..fd47f68280 100644 --- a/compiler/qsc_qasm3/src/parser/expr.rs +++ b/compiler/qsc_qasm3/src/parser/expr.rs @@ -16,9 +16,10 @@ use qsc_data_structures::span::Span; use crate::{ ast::{ - self, AssignExpr, AssignOpExpr, BinOp, BinaryOpExpr, Cast, DiscreteSet, Expr, ExprKind, - ExprStmt, FunctionCall, IndexElement, IndexExpr, IndexSetItem, Lit, LiteralKind, - RangeDefinition, TypeDef, UnaryOp, ValueExpression, Version, + self, list_from_iter, AssignExpr, AssignOpExpr, BinOp, BinaryOpExpr, Cast, DiscreteSet, + Expr, ExprKind, ExprStmt, FunctionCall, GateOperand, HardwareQubit, Ident, IndexElement, + IndexExpr, IndexSetItem, IndexedIdent, Lit, LiteralKind, MeasureExpr, RangeDefinition, + TypeDef, UnaryOp, ValueExpression, Version, }, keyword::Keyword, lex::{ @@ -36,7 +37,7 @@ use crate::parser::Result; use super::{ error::{Error, ErrorKind}, - prim::{ident, opt, seq, FinalSep}, + prim::{ident, many, opt, recovering_token, seq, FinalSep}, stmt::scalar_or_array_type, }; @@ -458,7 +459,7 @@ fn cast_op(s: &mut ParserContext, r#type: TypeDef) -> Result { fn index_expr(s: &mut ParserContext, lhs: Expr) -> Result { let lo = s.span(0).hi - 1; let index = index_element(s)?; - token(s, TokenKind::Close(Delim::Bracket))?; + recovering_token(s, TokenKind::Close(Delim::Bracket)); Ok(ExprKind::IndexExpr(IndexExpr { span: s.span(lo), collection: lhs, @@ -662,17 +663,96 @@ pub(super) fn designator(s: &mut ParserContext) -> Result { }) } +/// A literal array is a list of literal array elements. +fn lit_array(s: &mut ParserContext) -> Result { + let lo = s.peek().span.lo; + token(s, TokenKind::Open(Delim::Brace))?; + let elements = seq(s, lit_array_element).map(|pair| pair.0)?; + recovering_token(s, TokenKind::Close(Delim::Brace)); + Ok(Expr { + span: s.span(lo), + kind: Box::new(ExprKind::Lit(Lit { + span: s.span(lo), + kind: LiteralKind::Array(list_from_iter(elements)), + })), + }) +} + +/// A literal array element can be an expression, or a literal array element. +fn lit_array_element(s: &mut ParserContext) -> Result { + if let Some(elt) = opt(s, expr)? { + return Ok(elt); + } + lit_array(s) +} + pub(super) fn value_expr(s: &mut ParserContext) -> Result> { let lo = s.peek().span.lo; - let expr = expr_stmt(s)?; + if let Some(measurement) = opt(s, measure_expr)? { + return Ok(Box::new(ValueExpression::Measurement(measurement))); + } + + let expr = if let Some(expr) = opt(s, expr_stmt)? { + expr + } else { + lit_array(s)? + }; + let stmt = ExprStmt { span: s.span(lo), expr, }; - // todo: measurement + Ok(Box::new(ValueExpression::Expr(stmt))) } -pub(crate) fn expr_list(s: &mut ParserContext<'_>) -> Result> { +pub(crate) fn expr_list(s: &mut ParserContext) -> Result> { seq(s, expr).map(|pair| pair.0) } + +fn measure_expr(s: &mut ParserContext) -> Result { + let lo = s.peek().span.lo; + token(s, TokenKind::Measure)?; + + Ok(MeasureExpr { + span: s.span(lo), + operand: gate_operand(s)?, + }) +} + +fn gate_operand(s: &mut ParserContext) -> Result { + if let Some(indexed_ident) = opt(s, indexed_identifier)? { + return Ok(GateOperand::IndexedIdent(Box::new(indexed_ident))); + } + Ok(GateOperand::HardwareQubit(Box::new(hardware_qubit(s)?))) +} + +fn hardware_qubit(s: &mut ParserContext) -> Result { + let lo = s.peek().span.lo; + let hardware_qubit = s.read(); + token(s, TokenKind::HardwareQubit)?; + + Ok(HardwareQubit { + span: s.span(lo), + name: hardware_qubit[1..].into(), + }) +} + +fn indexed_identifier(s: &mut ParserContext) -> Result { + let lo = s.peek().span.lo; + let name: Ident = ident(s)?; + let indices = list_from_iter(many(s, index_operand)?); + + Ok(IndexedIdent { + span: s.span(lo), + name, + indices, + }) +} + +fn index_operand(s: &mut ParserContext) -> Result { + token(s, TokenKind::Open(Delim::Bracket))?; + let index = index_element(s)?; + recovering_token(s, TokenKind::Close(Delim::Bracket)); + Ok(index) +} diff --git a/compiler/qsc_qasm3/src/parser/expr/tests.rs b/compiler/qsc_qasm3/src/parser/expr/tests.rs index 0569e32374..cf8db7ab75 100644 --- a/compiler/qsc_qasm3/src/parser/expr/tests.rs +++ b/compiler/qsc_qasm3/src/parser/expr/tests.rs @@ -1141,6 +1141,18 @@ fn set_expr() { ); } +#[test] +fn lit_array() { + check( + super::lit_array, + "{{2, {5}}, 1 + z}", + &expect![[r#" + Expr [0-17]: Lit: Array: + Expr { span: Span { lo: 1, hi: 9 }, kind: Lit(Lit { span: Span { lo: 1, hi: 9 }, kind: Array([Expr { span: Span { lo: 2, hi: 3 }, kind: Lit(Lit { span: Span { lo: 2, hi: 3 }, kind: Int(2) }) }, Expr { span: Span { lo: 5, hi: 8 }, kind: Lit(Lit { span: Span { lo: 5, hi: 8 }, kind: Array([Expr { span: Span { lo: 6, hi: 7 }, kind: Lit(Lit { span: Span { lo: 6, hi: 7 }, kind: Int(5) }) }]) }) }]) }) } + Expr { span: Span { lo: 11, hi: 16 }, kind: BinaryOp(BinaryOpExpr { op: Add, lhs: Expr { span: Span { lo: 11, hi: 12 }, kind: Lit(Lit { span: Span { lo: 11, hi: 12 }, kind: Int(1) }) }, rhs: Expr { span: Span { lo: 15, hi: 16 }, kind: Ident(Ident { span: Span { lo: 15, hi: 16 }, name: "z" }) } }) }"#]], + ); +} + #[test] fn assignment_and_unop() { check( @@ -1168,3 +1180,49 @@ fn assignment_unop_and() { Expr [15-16]: Ident [15-16] "b""#]], ); } + +#[test] +fn hardware_qubit() { + check( + super::hardware_qubit, + "$12", + &expect!["HardwareQubit [0-3]: 12"], + ); +} + +#[test] +fn indexed_identifier() { + check( + super::indexed_identifier, + "arr[1][2]", + &expect![[r#" + IndexedIdent [0-9]: Ident [0-3] "arr"[ + IndexElement: + IndexSetItem Expr [4-5]: Lit: Int(1) + IndexElement: + IndexSetItem Expr [7-8]: Lit: Int(2)]"#]], + ); +} + +#[test] +fn measure_hardware_qubit() { + check( + super::measure_expr, + "measure $12", + &expect!["MeasureExpr [0-7]: GateOperand HardwareQubit [8-11]: 12"], + ); +} + +#[test] +fn measure_indexed_identifier() { + check( + super::measure_expr, + "measure qubits[1][2]", + &expect![[r#" + MeasureExpr [0-7]: GateOperand IndexedIdent [8-20]: Ident [8-14] "qubits"[ + IndexElement: + IndexSetItem Expr [15-16]: Lit: Int(1) + IndexElement: + IndexSetItem Expr [18-19]: Lit: Int(2)]"#]], + ); +} diff --git a/compiler/qsc_qasm3/src/parser/stmt/tests/classical_decl.rs b/compiler/qsc_qasm3/src/parser/stmt/tests/classical_decl.rs index 9fb8775554..6159810966 100644 --- a/compiler/qsc_qasm3/src/parser/stmt/tests/classical_decl.rs +++ b/compiler/qsc_qasm3/src/parser/stmt/tests/classical_decl.rs @@ -668,3 +668,72 @@ fn stretch_decl() { StmtKind: ClassicalDeclarationStmt [0-10]: ClassicalType [0-7]: Stretch, Ident [8-9] "s""#]], ); } + +#[test] +fn empty_array_decl() { + check( + parse, + "array[int, 0] arr = {};", + &expect![[r#" + Stmt [0-23] + StmtKind: ClassicalDeclarationStmt [0-23]: ArrayType [0-13]: ArrayBaseTypeKind IntType [6-9] + Expr [11-12]: Lit: Int(0), Ident [14-17] "arr", ValueExpression ExprStmt [20-22]: Expr [20-22]: Lit: Array:"#]], + ); +} + +#[test] +fn simple_array_decl() { + check( + parse, + "array[int[32], 3] arr = {1, 2, 3};", + &expect![[r#" + Stmt [0-34] + StmtKind: ClassicalDeclarationStmt [0-34]: ArrayType [0-17]: ArrayBaseTypeKind IntType[ExprStmt [9-13]: Expr [10-12]: Lit: Int(32)]: [6-13] + Expr [15-16]: Lit: Int(3), Ident [18-21] "arr", ValueExpression ExprStmt [24-33]: Expr [24-33]: Lit: Array: + Expr { span: Span { lo: 25, hi: 26 }, kind: Lit(Lit { span: Span { lo: 25, hi: 26 }, kind: Int(1) }) } + Expr { span: Span { lo: 28, hi: 29 }, kind: Lit(Lit { span: Span { lo: 28, hi: 29 }, kind: Int(2) }) } + Expr { span: Span { lo: 31, hi: 32 }, kind: Lit(Lit { span: Span { lo: 31, hi: 32 }, kind: Int(3) }) }"#]], + ); +} + +#[test] +fn nested_array_decl() { + check( + parse, + "array[int[32], 3, 2] arr = {{1, 2}, {3, 4}, {5, 6}};", + &expect![[r#" + Stmt [0-52] + StmtKind: ClassicalDeclarationStmt [0-52]: ArrayType [0-20]: ArrayBaseTypeKind IntType[ExprStmt [9-13]: Expr [10-12]: Lit: Int(32)]: [6-13] + Expr [15-16]: Lit: Int(3) + Expr [18-19]: Lit: Int(2), Ident [21-24] "arr", ValueExpression ExprStmt [27-51]: Expr [27-51]: Lit: Array: + Expr { span: Span { lo: 28, hi: 34 }, kind: Lit(Lit { span: Span { lo: 28, hi: 34 }, kind: Array([Expr { span: Span { lo: 29, hi: 30 }, kind: Lit(Lit { span: Span { lo: 29, hi: 30 }, kind: Int(1) }) }, Expr { span: Span { lo: 32, hi: 33 }, kind: Lit(Lit { span: Span { lo: 32, hi: 33 }, kind: Int(2) }) }]) }) } + Expr { span: Span { lo: 36, hi: 42 }, kind: Lit(Lit { span: Span { lo: 36, hi: 42 }, kind: Array([Expr { span: Span { lo: 37, hi: 38 }, kind: Lit(Lit { span: Span { lo: 37, hi: 38 }, kind: Int(3) }) }, Expr { span: Span { lo: 40, hi: 41 }, kind: Lit(Lit { span: Span { lo: 40, hi: 41 }, kind: Int(4) }) }]) }) } + Expr { span: Span { lo: 44, hi: 50 }, kind: Lit(Lit { span: Span { lo: 44, hi: 50 }, kind: Array([Expr { span: Span { lo: 45, hi: 46 }, kind: Lit(Lit { span: Span { lo: 45, hi: 46 }, kind: Int(5) }) }, Expr { span: Span { lo: 48, hi: 49 }, kind: Lit(Lit { span: Span { lo: 48, hi: 49 }, kind: Int(6) }) }]) }) }"#]], + ); +} + +#[test] +fn measure_hardware_qubit_decl() { + check( + parse, + "bit res = measure $12;", + &expect![[r#" + Stmt [0-22] + StmtKind: ClassicalDeclarationStmt [0-22]: ClassicalType [0-3]: BitType, Ident [4-7] "res", ValueExpression MeasureExpr [10-17]: GateOperand HardwareQubit [18-21]: 12"#]], + ); +} + +#[test] +fn measure_register_decl() { + check( + parse, + "bit res = measure qubits[2][3];", + &expect![[r#" + Stmt [0-31] + StmtKind: ClassicalDeclarationStmt [0-31]: ClassicalType [0-3]: BitType, Ident [4-7] "res", ValueExpression MeasureExpr [10-17]: GateOperand IndexedIdent [18-30]: Ident [18-24] "qubits"[ + IndexElement: + IndexSetItem Expr [25-26]: Lit: Int(2) + IndexElement: + IndexSetItem Expr [28-29]: Lit: Int(3)]"#]], + ); +} From 0ac5d269c06b297afe122d6aac0448113ae9c6f5 Mon Sep 17 00:00:00 2001 From: Oscar Puente <156957451+orpuente-MS@users.noreply.github.com> Date: Wed, 19 Feb 2025 14:46:26 -0800 Subject: [PATCH 011/108] parse if, for, and while statements --- compiler/qsc_qasm3/src/ast.rs | 42 ++---- compiler/qsc_qasm3/src/parser/expr.rs | 10 +- compiler/qsc_qasm3/src/parser/stmt.rs | 138 +++++++++++++++++- compiler/qsc_qasm3/src/parser/stmt/tests.rs | 3 + .../src/parser/stmt/tests/for_loops.rs | 94 ++++++++++++ .../src/parser/stmt/tests/if_stmt.rs | 96 ++++++++++++ .../src/parser/stmt/tests/switch_stmt.rs | 3 +- .../src/parser/stmt/tests/while_loops.rs | 39 +++++ 8 files changed, 380 insertions(+), 45 deletions(-) create mode 100644 compiler/qsc_qasm3/src/parser/stmt/tests/for_loops.rs create mode 100644 compiler/qsc_qasm3/src/parser/stmt/tests/if_stmt.rs create mode 100644 compiler/qsc_qasm3/src/parser/stmt/tests/while_loops.rs diff --git a/compiler/qsc_qasm3/src/ast.rs b/compiler/qsc_qasm3/src/ast.rs index e56abed25e..2adfea88b5 100644 --- a/compiler/qsc_qasm3/src/ast.rs +++ b/compiler/qsc_qasm3/src/ast.rs @@ -479,7 +479,7 @@ impl Display for DefCalStmt { #[derive(Clone, Debug)] pub struct IfStmt { pub span: Span, - pub condition: ExprStmt, + pub condition: Expr, pub if_block: List, pub else_block: Option>, } @@ -1564,33 +1564,11 @@ impl Display for ReturnStmt { } } -#[derive(Clone, Debug)] -pub struct BranchingStmt { - span: Span, - condition: ExprStmt, - if_block: List, - else_block: List, -} - -impl Display for BranchingStmt { - fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { - let mut indent = set_indentation(indented(f), 0); - write!(indent, "BranchingStmt {}: {}", self.span, self.condition)?; - for stmt in &self.if_block { - write!(indent, "\n{stmt}")?; - } - for stmt in &self.else_block { - write!(indent, "\n{stmt}")?; - } - Ok(()) - } -} - #[derive(Clone, Debug)] pub struct WhileLoop { - span: Span, - while_condition: ExprStmt, - block: List, + pub span: Span, + pub while_condition: Expr, + pub block: List, } impl Display for WhileLoop { @@ -1606,11 +1584,11 @@ impl Display for WhileLoop { #[derive(Clone, Debug)] pub struct ForStmt { - span: Span, - r#type: ScalarType, - identifier: Identifier, - set_declaration: Box, - block: List, + pub span: Span, + pub r#type: ScalarType, + pub identifier: Identifier, + pub set_declaration: Box, + pub block: List, } impl Display for ForStmt { @@ -1632,7 +1610,7 @@ impl Display for ForStmt { pub enum EnumerableSet { DiscreteSet(DiscreteSet), RangeDefinition(RangeDefinition), - Expr(ExprStmt), + Expr(Expr), } impl Display for EnumerableSet { diff --git a/compiler/qsc_qasm3/src/parser/expr.rs b/compiler/qsc_qasm3/src/parser/expr.rs index fd47f68280..9e1c4ba6bd 100644 --- a/compiler/qsc_qasm3/src/parser/expr.rs +++ b/compiler/qsc_qasm3/src/parser/expr.rs @@ -498,8 +498,8 @@ fn index_set_item(s: &mut ParserContext) -> Result { return Ok(IndexSetItem::Expr(expr)); } - let end = opt(s, expr)?; - let step = opt(s, |s| { + let step = opt(s, expr)?; + let end = opt(s, |s| { token(s, TokenKind::Colon)?; expr(s) })?; @@ -512,11 +512,11 @@ fn index_set_item(s: &mut ParserContext) -> Result { })) } -fn set_expr(s: &mut ParserContext) -> Result { +pub(crate) fn set_expr(s: &mut ParserContext) -> Result { let lo = s.peek().span.lo; token(s, TokenKind::Open(Delim::Brace))?; - let (exprs, _) = seq(s, expr)?; - token(s, TokenKind::Close(Delim::Brace))?; + let exprs = expr_list(s)?; + recovering_token(s, TokenKind::Close(Delim::Brace)); Ok(DiscreteSet { span: s.span(lo), values: exprs.into_boxed_slice(), diff --git a/compiler/qsc_qasm3/src/parser/stmt.rs b/compiler/qsc_qasm3/src/parser/stmt.rs index b01735615b..7400fa38de 100644 --- a/compiler/qsc_qasm3/src/parser/stmt.rs +++ b/compiler/qsc_qasm3/src/parser/stmt.rs @@ -19,10 +19,11 @@ use crate::{ ast::{ list_from_iter, AccessControl, AngleType, Annotation, ArrayBaseTypeKind, ArrayReferenceType, ArrayType, BitType, Block, ClassicalDeclarationStmt, ComplexType, - ConstantDeclaration, DefStmt, Expr, ExprStmt, ExternDecl, ExternParameter, FloatType, - IODeclaration, IOKeyword, Ident, IncludeStmt, IntType, List, LiteralKind, Pragma, - QuantumGateDefinition, QubitDeclaration, ReturnStmt, ScalarType, ScalarTypeKind, Stmt, - StmtKind, SwitchStmt, TypeDef, TypedParameter, UIntType, + ConstantDeclaration, DefStmt, EnumerableSet, Expr, ExprStmt, ExternDecl, ExternParameter, + FloatType, ForStmt, IODeclaration, IOKeyword, Ident, Identifier, IfStmt, IncludeStmt, + IntType, List, LiteralKind, Pragma, QuantumGateDefinition, QubitDeclaration, + RangeDefinition, ReturnStmt, ScalarType, ScalarTypeKind, Stmt, StmtKind, SwitchStmt, + TypeDef, TypedParameter, UIntType, WhileLoop, }, keyword::Keyword, lex::{ @@ -63,6 +64,12 @@ pub(super) fn parse(s: &mut ParserContext) -> Result> { Box::new(decl) } else if let Some(switch) = opt(s, parse_switch_stmt)? { Box::new(StmtKind::Switch(switch)) + } else if let Some(stmt) = opt(s, parse_if_stmt)? { + Box::new(StmtKind::If(stmt)) + } else if let Some(stmt) = opt(s, parse_for_loop)? { + Box::new(StmtKind::For(stmt)) + } else if let Some(stmt) = opt(s, parse_while_loop)? { + Box::new(StmtKind::WhileLoop(stmt)) } else if let Some(decl) = opt(s, parse_return)? { Box::new(decl) } else { @@ -840,8 +847,8 @@ pub(super) fn complex_subtype(s: &mut ParserContext) -> Result { } /// The Language Spec and the grammar for switch statements disagree. -/// We followed the Spec when writing the parser -/// . +/// We followed the Spec when writing the parser. +/// Reference: . pub fn parse_switch_stmt(s: &mut ParserContext) -> Result { let lo = s.peek().span.lo; token(s, TokenKind::Keyword(Keyword::Switch))?; @@ -891,3 +898,122 @@ fn default_case_stmt(s: &mut ParserContext) -> Result { token(s, TokenKind::Keyword(Keyword::Default))?; parse_block(s).map(|block| *block) } + +/// Parses a block or a statement. This is a helper function +/// to be used in loops and if stmts, in which their bodies +/// can be a block expr or a single statement. +fn parse_block_or_stmt(s: &mut ParserContext) -> Result> { + if let Some(block) = opt(s, parse_block)? { + Ok(block.stmts) + } else { + Ok(Box::new([parse(s)?])) + } +} + +/// Grammar ` LPAREN expression RPAREN if_body=statementOrScope (ELSE else_body=statementOrScope)?`. +/// Source: . +pub fn parse_if_stmt(s: &mut ParserContext) -> Result { + let lo = s.peek().span.lo; + token(s, TokenKind::Keyword(Keyword::If))?; + let paren_expr_lo = s.peek().span.lo; + token(s, TokenKind::Open(Delim::Paren))?; + let condition = expr::paren_expr(s, paren_expr_lo)?; + let if_block = parse_block_or_stmt(s)?; + let else_block = if opt(s, |s| token(s, TokenKind::Keyword(Keyword::Else)))?.is_some() { + Some(parse_block_or_stmt(s)?) + } else { + None + }; + + Ok(IfStmt { + span: s.span(lo), + condition, + if_block, + else_block, + }) +} + +/// Grammar `LBRACKET start=expression COLON (step=expression COLON)? stop=expression]`. +/// Reference: . +fn for_loop_range_expr(s: &mut ParserContext) -> Result { + let lo = s.peek().span.lo; + token(s, TokenKind::Open(Delim::Bracket))?; + let start = Some(expr::expr(s)?); + + // If no colon, return the expr as a normal index. + token(s, TokenKind::Colon)?; + + // QASM ranges have the pattern [start : (step :)? end] + // We assume the second expr is the `end`. + let mut end = Some(expr::expr(s)?); + let mut step = None; + + // If we find a third expr, then the second expr was the `step`. + // and this third expr is the actual `end`. + if let Some(expr) = opt(s, |s| { + token(s, TokenKind::Colon)?; + expr::expr(s) + })? { + step = end; + end = Some(expr); + } + + recovering_token(s, TokenKind::Close(Delim::Bracket)); + + Ok(RangeDefinition { + span: s.span(lo), + start, + end, + step, + }) +} + +/// Parses the `(setExpression | LBRACKET rangeExpression RBRACKET | expression)` +/// part of a for loop statement. +/// Reference: . +fn for_loop_iterable_expr(s: &mut ParserContext) -> Result { + if let Some(range) = opt(s, for_loop_range_expr)? { + Ok(EnumerableSet::RangeDefinition(range)) + } else if let Some(set) = opt(s, expr::set_expr)? { + Ok(EnumerableSet::DiscreteSet(set)) + } else { + Ok(EnumerableSet::Expr(expr::expr(s)?)) + } +} + +/// Grammar: `FOR scalarType Identifier IN (setExpression | LBRACKET rangeExpression RBRACKET | expression) body=statementOrScope`. +/// Reference: . +pub fn parse_for_loop(s: &mut ParserContext) -> Result { + let lo = s.peek().span.lo; + token(s, TokenKind::Keyword(Keyword::For))?; + let r#type = scalar_type(s)?; + let identifier = Identifier::Ident(Box::new(prim::ident(s)?)); + token(s, TokenKind::Keyword(Keyword::In))?; + let set_declaration = Box::new(for_loop_iterable_expr(s)?); + let block = parse_block_or_stmt(s)?; + + Ok(ForStmt { + span: s.span(lo), + r#type, + identifier, + set_declaration, + block, + }) +} + +/// Grammar: `WHILE LPAREN expression RPAREN body=statementOrScope`. +/// Reference: . +pub fn parse_while_loop(s: &mut ParserContext) -> Result { + let lo = s.peek().span.lo; + token(s, TokenKind::Keyword(Keyword::While))?; + let paren_expr_lo = s.peek().span.lo; + token(s, TokenKind::Open(Delim::Paren))?; + let while_condition = expr::paren_expr(s, paren_expr_lo)?; + let block = parse_block_or_stmt(s)?; + + Ok(WhileLoop { + span: s.span(lo), + while_condition, + block, + }) +} diff --git a/compiler/qsc_qasm3/src/parser/stmt/tests.rs b/compiler/qsc_qasm3/src/parser/stmt/tests.rs index f4312061ba..7fbaeee543 100644 --- a/compiler/qsc_qasm3/src/parser/stmt/tests.rs +++ b/compiler/qsc_qasm3/src/parser/stmt/tests.rs @@ -5,9 +5,12 @@ mod annotation; mod classical_decl; mod def; mod extern_decl; +mod for_loops; mod gate_def; +mod if_stmt; mod io_decl; mod old_style_decl; mod pragma; mod quantum_decl; mod switch_stmt; +mod while_loops; diff --git a/compiler/qsc_qasm3/src/parser/stmt/tests/for_loops.rs b/compiler/qsc_qasm3/src/parser/stmt/tests/for_loops.rs new file mode 100644 index 0000000000..c25866968e --- /dev/null +++ b/compiler/qsc_qasm3/src/parser/stmt/tests/for_loops.rs @@ -0,0 +1,94 @@ +use crate::parser::{stmt::parse, tests::check}; +use expect_test::expect; + +#[test] +fn simple_for_loop() { + check( + parse, + " + for int x in {1, 2, 3} { + int a = 0; + }", + &expect![[r#" + Stmt [5-54] + StmtKind: ForStmt [5-54]: ClassicalType [9-12]: IntType [9-12], Ident [13-14] "x", DiscreteSet [18-27]: + Expr [19-20]: Lit: Int(1) + Expr [22-23]: Lit: Int(2) + Expr [25-26]: Lit: Int(3) + Stmt [38-48] + StmtKind: ClassicalDeclarationStmt [38-48]: ClassicalType [38-41]: IntType [38-41], Ident [42-43] "a", ValueExpression ExprStmt [46-47]: Expr [46-47]: Lit: Int(0)"#]], + ); +} + +#[test] +fn simple_for_loop_stmt_body() { + check( + parse, + " + for int x in {1, 2, 3} + int a = 0; + ", + &expect![[r#" + Stmt [5-46] + StmtKind: ForStmt [5-46]: ClassicalType [9-12]: IntType [9-12], Ident [13-14] "x", DiscreteSet [18-27]: + Expr [19-20]: Lit: Int(1) + Expr [22-23]: Lit: Int(2) + Expr [25-26]: Lit: Int(3) + Stmt [36-46] + StmtKind: ClassicalDeclarationStmt [36-46]: ClassicalType [36-39]: IntType [36-39], Ident [40-41] "a", ValueExpression ExprStmt [44-45]: Expr [44-45]: Lit: Int(0)"#]], + ); +} + +#[test] +fn for_loop_range() { + check( + parse, + " + for int x in [0:2:7] { + int a = 0; + }", + &expect![[r#" + Stmt [5-52] + StmtKind: ForStmt [5-52]: ClassicalType [9-12]: IntType [9-12], Ident [13-14] "x", Range: [18-25] + Expr [19-20]: Lit: Int(0) + Expr [21-22]: Lit: Int(2) + Expr [23-24]: Lit: Int(7) + Stmt [36-46] + StmtKind: ClassicalDeclarationStmt [36-46]: ClassicalType [36-39]: IntType [36-39], Ident [40-41] "a", ValueExpression ExprStmt [44-45]: Expr [44-45]: Lit: Int(0)"#]], + ); +} + +#[test] +fn for_loop_range_no_step() { + check( + parse, + " + for int x in [0:7] { + int a = 0; + }", + &expect![[r#" + Stmt [5-50] + StmtKind: ForStmt [5-50]: ClassicalType [9-12]: IntType [9-12], Ident [13-14] "x", Range: [18-23] + Expr [19-20]: Lit: Int(0) + + Expr [21-22]: Lit: Int(7) + Stmt [34-44] + StmtKind: ClassicalDeclarationStmt [34-44]: ClassicalType [34-37]: IntType [34-37], Ident [38-39] "a", ValueExpression ExprStmt [42-43]: Expr [42-43]: Lit: Int(0)"#]], + ); +} + +#[test] +fn for_loop_expr() { + check( + parse, + " + for int x in xs { + int a = 0; + }", + &expect![[r#" + Stmt [5-47] + StmtKind: ForStmt [5-47]: ClassicalType [9-12]: IntType [9-12], Ident [13-14] "x", Expr [18-20]: Ident [18-20] "xs" + Stmt [31-41] + StmtKind: ClassicalDeclarationStmt [31-41]: ClassicalType [31-34]: IntType [31-34], Ident [35-36] "a", ValueExpression ExprStmt [39-40]: Expr [39-40]: Lit: Int(0)"#]], + ); +} diff --git a/compiler/qsc_qasm3/src/parser/stmt/tests/if_stmt.rs b/compiler/qsc_qasm3/src/parser/stmt/tests/if_stmt.rs new file mode 100644 index 0000000000..1e265ba04b --- /dev/null +++ b/compiler/qsc_qasm3/src/parser/stmt/tests/if_stmt.rs @@ -0,0 +1,96 @@ +use crate::parser::{stmt::parse, tests::check}; +use expect_test::expect; + +#[test] +fn simple_if_stmt() { + check( + parse, + " + if (x == y) { + int a = 0; + } else { + int a = 1; + } + ", + &expect![[r#" + Stmt [5-75] + StmtKind: IfStmt [5-75]: Expr [8-16]: Paren: + Expr [9-15]: BinOp (Eq): + Expr [9-10]: Ident [9-10] "x" + Expr [14-15]: Ident [14-15] "y" + Stmt [27-37] + StmtKind: ClassicalDeclarationStmt [27-37]: ClassicalType [27-30]: IntType [27-30], Ident [31-32] "a", ValueExpression ExprStmt [35-36]: Expr [35-36]: Lit: Int(0) + Else: + Stmt [59-69] + StmtKind: ClassicalDeclarationStmt [59-69]: ClassicalType [59-62]: IntType [59-62], Ident [63-64] "a", ValueExpression ExprStmt [67-68]: Expr [67-68]: Lit: Int(1)"#]], + ); +} + +#[test] +fn if_stmt_missing_else() { + check( + parse, + " + if (x == y) { + int a = 0; + } + ", + &expect![[r#" + Stmt [5-43] + StmtKind: IfStmt [5-43]: Expr [8-16]: Paren: + Expr [9-15]: BinOp (Eq): + Expr [9-10]: Ident [9-10] "x" + Expr [14-15]: Ident [14-15] "y" + Stmt [27-37] + StmtKind: ClassicalDeclarationStmt [27-37]: ClassicalType [27-30]: IntType [27-30], Ident [31-32] "a", ValueExpression ExprStmt [35-36]: Expr [35-36]: Lit: Int(0)"#]], + ); +} + +#[test] +fn nested_if_stmts() { + check( + parse, + " + if (x == y) { + if (x1 == y1) { + int a = 0; + } else { + int a = 1; + } + } else { + if (x2 == y2) { + int a = 2; + } else { + int a = 3; + } + } + ", + &expect![[r#" + Stmt [5-231] + StmtKind: IfStmt [5-231]: Expr [8-16]: Paren: + Expr [9-15]: BinOp (Eq): + Expr [9-10]: Ident [9-10] "x" + Expr [14-15]: Ident [14-15] "y" + Stmt [27-115] + StmtKind: IfStmt [27-115]: Expr [30-40]: Paren: + Expr [31-39]: BinOp (Eq): + Expr [31-33]: Ident [31-33] "x1" + Expr [37-39]: Ident [37-39] "y1" + Stmt [55-65] + StmtKind: ClassicalDeclarationStmt [55-65]: ClassicalType [55-58]: IntType [55-58], Ident [59-60] "a", ValueExpression ExprStmt [63-64]: Expr [63-64]: Lit: Int(0) + Else: + Stmt [95-105] + StmtKind: ClassicalDeclarationStmt [95-105]: ClassicalType [95-98]: IntType [95-98], Ident [99-100] "a", ValueExpression ExprStmt [103-104]: Expr [103-104]: Lit: Int(1) + Else: + Stmt [137-225] + StmtKind: IfStmt [137-225]: Expr [140-150]: Paren: + Expr [141-149]: BinOp (Eq): + Expr [141-143]: Ident [141-143] "x2" + Expr [147-149]: Ident [147-149] "y2" + Stmt [165-175] + StmtKind: ClassicalDeclarationStmt [165-175]: ClassicalType [165-168]: IntType [165-168], Ident [169-170] "a", ValueExpression ExprStmt [173-174]: Expr [173-174]: Lit: Int(2) + Else: + Stmt [205-215] + StmtKind: ClassicalDeclarationStmt [205-215]: ClassicalType [205-208]: IntType [205-208], Ident [209-210] "a", ValueExpression ExprStmt [213-214]: Expr [213-214]: Lit: Int(3)"#]], + ); +} diff --git a/compiler/qsc_qasm3/src/parser/stmt/tests/switch_stmt.rs b/compiler/qsc_qasm3/src/parser/stmt/tests/switch_stmt.rs index 9968592f55..6077fa366d 100644 --- a/compiler/qsc_qasm3/src/parser/stmt/tests/switch_stmt.rs +++ b/compiler/qsc_qasm3/src/parser/stmt/tests/switch_stmt.rs @@ -1,6 +1,5 @@ -use expect_test::expect; - use crate::parser::{stmt::parse_switch_stmt, tests::check}; +use expect_test::expect; #[test] fn simple_switch() { diff --git a/compiler/qsc_qasm3/src/parser/stmt/tests/while_loops.rs b/compiler/qsc_qasm3/src/parser/stmt/tests/while_loops.rs new file mode 100644 index 0000000000..f3bcc6f936 --- /dev/null +++ b/compiler/qsc_qasm3/src/parser/stmt/tests/while_loops.rs @@ -0,0 +1,39 @@ +use crate::parser::{stmt::parse, tests::check}; +use expect_test::expect; + +#[test] +fn simple_while() { + check( + parse, + " + while (x != 2) { + int a = 0; + }", + &expect![[r#" + Stmt [5-46] + StmtKind: WhileLoop [5-46]: Expr [11-19]: Paren: + Expr [12-18]: BinOp (Neq): + Expr [12-13]: Ident [12-13] "x" + Expr [17-18]: Lit: Int(2) + Stmt [30-40] + StmtKind: ClassicalDeclarationStmt [30-40]: ClassicalType [30-33]: IntType [30-33], Ident [34-35] "a", ValueExpression ExprStmt [38-39]: Expr [38-39]: Lit: Int(0)"#]], + ); +} + +#[test] +fn while_stmt_body() { + check( + parse, + " + while (x != 2) + int a = 0;", + &expect![[r#" + Stmt [5-38] + StmtKind: WhileLoop [5-38]: Expr [11-19]: Paren: + Expr [12-18]: BinOp (Neq): + Expr [12-13]: Ident [12-13] "x" + Expr [17-18]: Lit: Int(2) + Stmt [28-38] + StmtKind: ClassicalDeclarationStmt [28-38]: ClassicalType [28-31]: IntType [28-31], Ident [32-33] "a", ValueExpression ExprStmt [36-37]: Expr [36-37]: Lit: Int(0)"#]], + ); +} From 5b13c80cd4cd08ca586426d1b1aec7105fc4f532 Mon Sep 17 00:00:00 2001 From: Oscar Puente <156957451+orpuente-MS@users.noreply.github.com> Date: Wed, 19 Feb 2025 16:15:06 -0800 Subject: [PATCH 012/108] fix range expression --- compiler/qsc_qasm3/src/ast.rs | 6 ++--- .../qsc_qasm3/src/parser/completion/tests.rs | 4 +-- compiler/qsc_qasm3/src/parser/expr.rs | 27 +++++++++++++++---- compiler/qsc_qasm3/src/parser/expr/tests.rs | 26 +++++++++--------- compiler/qsc_qasm3/src/parser/stmt.rs | 3 +-- 5 files changed, 41 insertions(+), 25 deletions(-) diff --git a/compiler/qsc_qasm3/src/ast.rs b/compiler/qsc_qasm3/src/ast.rs index 2adfea88b5..34efd72a13 100644 --- a/compiler/qsc_qasm3/src/ast.rs +++ b/compiler/qsc_qasm3/src/ast.rs @@ -2137,15 +2137,15 @@ fn display_range(mut indent: Indented, range: &RangeDefinition) -> fm write!(indent, "Range: {}", range.span)?; indent = set_indentation(indent, 1); match &range.start { - Some(e) => write!(indent, "\n{e}")?, + Some(e) => write!(indent, "\nstart: {e}")?, None => write!(indent, "\n")?, } match &range.step { - Some(e) => write!(indent, "\n{e}")?, + Some(e) => write!(indent, "\nstep: {e}")?, None => write!(indent, "\n")?, } match &range.end { - Some(e) => write!(indent, "\n{e}")?, + Some(e) => write!(indent, "\nend: {e}")?, None => write!(indent, "\n")?, } Ok(()) diff --git a/compiler/qsc_qasm3/src/parser/completion/tests.rs b/compiler/qsc_qasm3/src/parser/completion/tests.rs index 0917cf49ff..fd22757573 100644 --- a/compiler/qsc_qasm3/src/parser/completion/tests.rs +++ b/compiler/qsc_qasm3/src/parser/completion/tests.rs @@ -36,7 +36,7 @@ fn begin_document() { "|OPENQASM 3;", &expect![[r#" WordKinds( - Annotation | CReg | Def | Extern | Gate | Include | Input | OpenQASM | Output | Pragma | QReg | Qubit | Return | Switch, + Annotation | CReg | Def | Extern | For | Gate | If | Include | Input | OpenQASM | Output | Pragma | QReg | Qubit | Return | Switch | While, ) "#]], ); @@ -48,7 +48,7 @@ fn end_of_version() { "OPENQASM 3;|", &expect![[r#" WordKinds( - Annotation | CReg | Def | Extern | Gate | Include | Input | Output | Pragma | QReg | Qubit | Return | Switch, + Annotation | CReg | Def | Extern | For | Gate | If | Include | Input | Output | Pragma | QReg | Qubit | Return | Switch | While, ) "#]], ); diff --git a/compiler/qsc_qasm3/src/parser/expr.rs b/compiler/qsc_qasm3/src/parser/expr.rs index 9e1c4ba6bd..20320facd5 100644 --- a/compiler/qsc_qasm3/src/parser/expr.rs +++ b/compiler/qsc_qasm3/src/parser/expr.rs @@ -484,6 +484,12 @@ fn index_element(s: &mut ParserContext) -> Result { Ok(index) } +/// QASM3 index set items can either of: +/// 1. An expression: arr[2] +/// 2. A range with start and end: arr[start : end] +/// 3. A range with start, step, and end: arr[start : step : end] +/// 4. Additionally, points 2. and 3. can have missing start, step, or step. +/// here are some examples: arr[:], arr[: step :], arr[: step : end] fn index_set_item(s: &mut ParserContext) -> Result { let lo = s.peek().span.lo; let start = opt(s, expr)?; @@ -498,11 +504,22 @@ fn index_set_item(s: &mut ParserContext) -> Result { return Ok(IndexSetItem::Expr(expr)); } - let step = opt(s, expr)?; - let end = opt(s, |s| { - token(s, TokenKind::Colon)?; - expr(s) - })?; + // We assume the second expr is the `end`. + let end = opt(s, expr)?; + + // If no colon, return a range with start and end: [start : end]. + if token(s, TokenKind::Colon).is_err() { + return Ok(IndexSetItem::RangeDefinition(RangeDefinition { + span: s.span(lo), + start, + end, + step: None, + })); + } + + // If there was a second semicolon, the second expression was the step. + let step = end; + let end = opt(s, expr)?; Ok(IndexSetItem::RangeDefinition(RangeDefinition { span: s.span(lo), diff --git a/compiler/qsc_qasm3/src/parser/expr/tests.rs b/compiler/qsc_qasm3/src/parser/expr/tests.rs index cf8db7ab75..a547ea8284 100644 --- a/compiler/qsc_qasm3/src/parser/expr/tests.rs +++ b/compiler/qsc_qasm3/src/parser/expr/tests.rs @@ -1044,17 +1044,17 @@ fn index_multiple_ranges() { &expect![[r#" Expr [0-18]: IndexExpr [3-18]: Expr [0-3]: Ident [0-3] "foo", IndexElement: Range: [4-7] - Expr [4-5]: Lit: Int(1) + start: Expr [4-5]: Lit: Int(1) - Expr [6-7]: Lit: Int(5) + end: Expr [6-7]: Lit: Int(5) Range: [9-12] - Expr [9-10]: Lit: Int(3) + start: Expr [9-10]: Lit: Int(3) - Expr [11-12]: Lit: Int(7) + end: Expr [11-12]: Lit: Int(7) Range: [14-17] - Expr [14-15]: Lit: Int(4) + start: Expr [14-15]: Lit: Int(4) - Expr [16-17]: Lit: Int(8)"#]], + end: Expr [16-17]: Lit: Int(8)"#]], ); } @@ -1066,9 +1066,9 @@ fn index_range() { &expect![[r#" Expr [0-10]: IndexExpr [3-10]: Expr [0-3]: Ident [0-3] "foo", IndexElement: Range: [4-9] - Expr [4-5]: Lit: Int(1) - Expr [8-9]: Lit: Int(2) - Expr [6-7]: Lit: Int(5)"#]], + start: Expr [4-5]: Lit: Int(1) + step: Expr [6-7]: Lit: Int(5) + end: Expr [8-9]: Lit: Int(2)"#]], ); } @@ -1094,7 +1094,7 @@ fn index_range_start() { &expect![[r#" Expr [0-7]: IndexExpr [3-7]: Expr [0-3]: Ident [0-3] "foo", IndexElement: Range: [4-6] - Expr [4-5]: Lit: Int(1) + start: Expr [4-5]: Lit: Int(1) "#]], ); @@ -1110,7 +1110,7 @@ fn index_range_end() { Range: [4-6] - Expr [5-6]: Lit: Int(5)"#]], + end: Expr [5-6]: Lit: Int(5)"#]], ); } @@ -1118,12 +1118,12 @@ fn index_range_end() { fn index_range_step() { check( expr, - "foo[::2]", + "foo[:2:]", &expect![[r#" Expr [0-8]: IndexExpr [3-8]: Expr [0-3]: Ident [0-3] "foo", IndexElement: Range: [4-7] - Expr [6-7]: Lit: Int(2) + step: Expr [5-6]: Lit: Int(2) "#]], ); } diff --git a/compiler/qsc_qasm3/src/parser/stmt.rs b/compiler/qsc_qasm3/src/parser/stmt.rs index 7400fa38de..098277303b 100644 --- a/compiler/qsc_qasm3/src/parser/stmt.rs +++ b/compiler/qsc_qasm3/src/parser/stmt.rs @@ -933,14 +933,13 @@ pub fn parse_if_stmt(s: &mut ParserContext) -> Result { }) } +/// Ranges in for loops are a bit different. They must have explicit start and end. /// Grammar `LBRACKET start=expression COLON (step=expression COLON)? stop=expression]`. /// Reference: . fn for_loop_range_expr(s: &mut ParserContext) -> Result { let lo = s.peek().span.lo; token(s, TokenKind::Open(Delim::Bracket))?; let start = Some(expr::expr(s)?); - - // If no colon, return the expr as a normal index. token(s, TokenKind::Colon)?; // QASM ranges have the pattern [start : (step :)? end] From 842d10f67ca50ac10090f6eb194df8a3a1e4cb82 Mon Sep 17 00:00:00 2001 From: Oscar Puente <156957451+orpuente-MS@users.noreply.github.com> Date: Wed, 19 Feb 2025 16:21:28 -0800 Subject: [PATCH 013/108] update for loop unit tests --- compiler/qsc_qasm3/src/parser/stmt/tests/for_loops.rs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/compiler/qsc_qasm3/src/parser/stmt/tests/for_loops.rs b/compiler/qsc_qasm3/src/parser/stmt/tests/for_loops.rs index c25866968e..2b1b500cca 100644 --- a/compiler/qsc_qasm3/src/parser/stmt/tests/for_loops.rs +++ b/compiler/qsc_qasm3/src/parser/stmt/tests/for_loops.rs @@ -50,9 +50,9 @@ fn for_loop_range() { &expect![[r#" Stmt [5-52] StmtKind: ForStmt [5-52]: ClassicalType [9-12]: IntType [9-12], Ident [13-14] "x", Range: [18-25] - Expr [19-20]: Lit: Int(0) - Expr [21-22]: Lit: Int(2) - Expr [23-24]: Lit: Int(7) + start: Expr [19-20]: Lit: Int(0) + step: Expr [21-22]: Lit: Int(2) + end: Expr [23-24]: Lit: Int(7) Stmt [36-46] StmtKind: ClassicalDeclarationStmt [36-46]: ClassicalType [36-39]: IntType [36-39], Ident [40-41] "a", ValueExpression ExprStmt [44-45]: Expr [44-45]: Lit: Int(0)"#]], ); @@ -69,9 +69,9 @@ fn for_loop_range_no_step() { &expect![[r#" Stmt [5-50] StmtKind: ForStmt [5-50]: ClassicalType [9-12]: IntType [9-12], Ident [13-14] "x", Range: [18-23] - Expr [19-20]: Lit: Int(0) + start: Expr [19-20]: Lit: Int(0) - Expr [21-22]: Lit: Int(7) + end: Expr [21-22]: Lit: Int(7) Stmt [34-44] StmtKind: ClassicalDeclarationStmt [34-44]: ClassicalType [34-37]: IntType [34-37], Ident [38-39] "a", ValueExpression ExprStmt [42-43]: Expr [42-43]: Lit: Int(0)"#]], ); From 1ad8e335818eb39863d03c52687331a8b9294282 Mon Sep 17 00:00:00 2001 From: Oscar Puente <156957451+orpuente-MS@users.noreply.github.com> Date: Thu, 20 Feb 2025 10:28:03 -0800 Subject: [PATCH 014/108] add continue statements --- compiler/qsc_qasm3/src/parser/stmt.rs | 23 +++++++++++++------ .../src/parser/stmt/tests/for_loops.rs | 22 ++++++++++++++++++ .../src/parser/stmt/tests/while_loops.rs | 22 ++++++++++++++++++ 3 files changed, 60 insertions(+), 7 deletions(-) diff --git a/compiler/qsc_qasm3/src/parser/stmt.rs b/compiler/qsc_qasm3/src/parser/stmt.rs index 098277303b..f005143484 100644 --- a/compiler/qsc_qasm3/src/parser/stmt.rs +++ b/compiler/qsc_qasm3/src/parser/stmt.rs @@ -4,9 +4,8 @@ #[cfg(test)] pub(crate) mod tests; -use std::rc::Rc; - use qsc_data_structures::span::Span; +use std::rc::Rc; use super::{ completion::WordKinds, @@ -19,9 +18,9 @@ use crate::{ ast::{ list_from_iter, AccessControl, AngleType, Annotation, ArrayBaseTypeKind, ArrayReferenceType, ArrayType, BitType, Block, ClassicalDeclarationStmt, ComplexType, - ConstantDeclaration, DefStmt, EnumerableSet, Expr, ExprStmt, ExternDecl, ExternParameter, - FloatType, ForStmt, IODeclaration, IOKeyword, Ident, Identifier, IfStmt, IncludeStmt, - IntType, List, LiteralKind, Pragma, QuantumGateDefinition, QubitDeclaration, + ConstantDeclaration, ContinueStmt, DefStmt, EnumerableSet, Expr, ExprStmt, ExternDecl, + ExternParameter, FloatType, ForStmt, IODeclaration, IOKeyword, Ident, Identifier, IfStmt, + IncludeStmt, IntType, List, LiteralKind, Pragma, QuantumGateDefinition, QubitDeclaration, RangeDefinition, ReturnStmt, ScalarType, ScalarTypeKind, Stmt, StmtKind, SwitchStmt, TypeDef, TypedParameter, UIntType, WhileLoop, }, @@ -70,8 +69,10 @@ pub(super) fn parse(s: &mut ParserContext) -> Result> { Box::new(StmtKind::For(stmt)) } else if let Some(stmt) = opt(s, parse_while_loop)? { Box::new(StmtKind::WhileLoop(stmt)) - } else if let Some(decl) = opt(s, parse_return)? { - Box::new(decl) + } else if let Some(stmt) = opt(s, parse_return)? { + Box::new(stmt) + } else if let Some(stmt) = opt(s, parse_continue_stmt)? { + Box::new(StmtKind::Continue(stmt)) } else { return Err(Error::new(ErrorKind::Rule( "statement", @@ -1016,3 +1017,11 @@ pub fn parse_while_loop(s: &mut ParserContext) -> Result { block, }) } + +/// Grammar: CONTINUE SEMICOLON +fn parse_continue_stmt(s: &mut ParserContext) -> Result { + let lo = s.peek().span.lo; + token(s, TokenKind::Keyword(Keyword::Continue))?; + recovering_token(s, TokenKind::Semicolon); + Ok(ContinueStmt { span: s.span(lo) }) +} diff --git a/compiler/qsc_qasm3/src/parser/stmt/tests/for_loops.rs b/compiler/qsc_qasm3/src/parser/stmt/tests/for_loops.rs index 2b1b500cca..26582b9a7b 100644 --- a/compiler/qsc_qasm3/src/parser/stmt/tests/for_loops.rs +++ b/compiler/qsc_qasm3/src/parser/stmt/tests/for_loops.rs @@ -92,3 +92,25 @@ fn for_loop_expr() { StmtKind: ClassicalDeclarationStmt [31-41]: ClassicalType [31-34]: IntType [31-34], Ident [35-36] "a", ValueExpression ExprStmt [39-40]: Expr [39-40]: Lit: Int(0)"#]], ); } + +#[test] +fn for_loop_with_continue_stmt() { + check( + parse, + " + for int x in {1, 2, 3} { + int a = 0; + continue; + }", + &expect![[r#" + Stmt [5-72] + StmtKind: ForStmt [5-72]: ClassicalType [9-12]: IntType [9-12], Ident [13-14] "x", DiscreteSet [18-27]: + Expr [19-20]: Lit: Int(1) + Expr [22-23]: Lit: Int(2) + Expr [25-26]: Lit: Int(3) + Stmt [38-48] + StmtKind: ClassicalDeclarationStmt [38-48]: ClassicalType [38-41]: IntType [38-41], Ident [42-43] "a", ValueExpression ExprStmt [46-47]: Expr [46-47]: Lit: Int(0) + Stmt [57-66] + StmtKind: Continue [57-66]"#]], + ); +} diff --git a/compiler/qsc_qasm3/src/parser/stmt/tests/while_loops.rs b/compiler/qsc_qasm3/src/parser/stmt/tests/while_loops.rs index f3bcc6f936..19648bb7da 100644 --- a/compiler/qsc_qasm3/src/parser/stmt/tests/while_loops.rs +++ b/compiler/qsc_qasm3/src/parser/stmt/tests/while_loops.rs @@ -37,3 +37,25 @@ fn while_stmt_body() { StmtKind: ClassicalDeclarationStmt [28-38]: ClassicalType [28-31]: IntType [28-31], Ident [32-33] "a", ValueExpression ExprStmt [36-37]: Expr [36-37]: Lit: Int(0)"#]], ); } + +#[test] +fn while_loop_with_continue_stmt() { + check( + parse, + " + while (x != 2) { + int a = 0; + continue; + }", + &expect![[r#" + Stmt [5-64] + StmtKind: WhileLoop [5-64]: Expr [11-19]: Paren: + Expr [12-18]: BinOp (Neq): + Expr [12-13]: Ident [12-13] "x" + Expr [17-18]: Lit: Int(2) + Stmt [30-40] + StmtKind: ClassicalDeclarationStmt [30-40]: ClassicalType [30-33]: IntType [30-33], Ident [34-35] "a", ValueExpression ExprStmt [38-39]: Expr [38-39]: Lit: Int(0) + Stmt [49-58] + StmtKind: Continue [49-58]"#]], + ); +} From 9f247de5b5f54336ddfcb970a2117705f4dc3e5a Mon Sep 17 00:00:00 2001 From: Oscar Puente <156957451+orpuente-MS@users.noreply.github.com> Date: Thu, 20 Feb 2025 10:29:06 -0800 Subject: [PATCH 015/108] update completions unit tests --- compiler/qsc_qasm3/src/parser/completion/tests.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/compiler/qsc_qasm3/src/parser/completion/tests.rs b/compiler/qsc_qasm3/src/parser/completion/tests.rs index fd22757573..b0f9dd0bf1 100644 --- a/compiler/qsc_qasm3/src/parser/completion/tests.rs +++ b/compiler/qsc_qasm3/src/parser/completion/tests.rs @@ -36,7 +36,7 @@ fn begin_document() { "|OPENQASM 3;", &expect![[r#" WordKinds( - Annotation | CReg | Def | Extern | For | Gate | If | Include | Input | OpenQASM | Output | Pragma | QReg | Qubit | Return | Switch | While, + Annotation | Continue | CReg | Def | Extern | For | Gate | If | Include | Input | OpenQASM | Output | Pragma | QReg | Qubit | Return | Switch | While, ) "#]], ); @@ -48,7 +48,7 @@ fn end_of_version() { "OPENQASM 3;|", &expect![[r#" WordKinds( - Annotation | CReg | Def | Extern | For | Gate | If | Include | Input | Output | Pragma | QReg | Qubit | Return | Switch | While, + Annotation | Continue | CReg | Def | Extern | For | Gate | If | Include | Input | Output | Pragma | QReg | Qubit | Return | Switch | While, ) "#]], ); From 9d24fae4826b2bfa96575d72a45295b7913993df Mon Sep 17 00:00:00 2001 From: Oscar Puente <156957451+orpuente-MS@users.noreply.github.com> Date: Thu, 20 Feb 2025 10:36:19 -0800 Subject: [PATCH 016/108] add break statements --- .../qsc_qasm3/src/parser/completion/tests.rs | 4 ++-- compiler/qsc_qasm3/src/parser/stmt.rs | 24 +++++++++++++------ .../src/parser/stmt/tests/for_loops.rs | 22 +++++++++++++++++ .../src/parser/stmt/tests/while_loops.rs | 22 +++++++++++++++++ 4 files changed, 63 insertions(+), 9 deletions(-) diff --git a/compiler/qsc_qasm3/src/parser/completion/tests.rs b/compiler/qsc_qasm3/src/parser/completion/tests.rs index b0f9dd0bf1..bd89cafc63 100644 --- a/compiler/qsc_qasm3/src/parser/completion/tests.rs +++ b/compiler/qsc_qasm3/src/parser/completion/tests.rs @@ -36,7 +36,7 @@ fn begin_document() { "|OPENQASM 3;", &expect![[r#" WordKinds( - Annotation | Continue | CReg | Def | Extern | For | Gate | If | Include | Input | OpenQASM | Output | Pragma | QReg | Qubit | Return | Switch | While, + Annotation | Break | Continue | CReg | Def | Extern | For | Gate | If | Include | Input | OpenQASM | Output | Pragma | QReg | Qubit | Return | Switch | While, ) "#]], ); @@ -48,7 +48,7 @@ fn end_of_version() { "OPENQASM 3;|", &expect![[r#" WordKinds( - Annotation | Continue | CReg | Def | Extern | For | Gate | If | Include | Input | Output | Pragma | QReg | Qubit | Return | Switch | While, + Annotation | Break | Continue | CReg | Def | Extern | For | Gate | If | Include | Input | Output | Pragma | QReg | Qubit | Return | Switch | While, ) "#]], ); diff --git a/compiler/qsc_qasm3/src/parser/stmt.rs b/compiler/qsc_qasm3/src/parser/stmt.rs index f005143484..eaef067ffa 100644 --- a/compiler/qsc_qasm3/src/parser/stmt.rs +++ b/compiler/qsc_qasm3/src/parser/stmt.rs @@ -17,12 +17,12 @@ use super::{ use crate::{ ast::{ list_from_iter, AccessControl, AngleType, Annotation, ArrayBaseTypeKind, - ArrayReferenceType, ArrayType, BitType, Block, ClassicalDeclarationStmt, ComplexType, - ConstantDeclaration, ContinueStmt, DefStmt, EnumerableSet, Expr, ExprStmt, ExternDecl, - ExternParameter, FloatType, ForStmt, IODeclaration, IOKeyword, Ident, Identifier, IfStmt, - IncludeStmt, IntType, List, LiteralKind, Pragma, QuantumGateDefinition, QubitDeclaration, - RangeDefinition, ReturnStmt, ScalarType, ScalarTypeKind, Stmt, StmtKind, SwitchStmt, - TypeDef, TypedParameter, UIntType, WhileLoop, + ArrayReferenceType, ArrayType, BitType, Block, BreakStmt, ClassicalDeclarationStmt, + ComplexType, ConstantDeclaration, ContinueStmt, DefStmt, EnumerableSet, Expr, ExprStmt, + ExternDecl, ExternParameter, FloatType, ForStmt, IODeclaration, IOKeyword, Ident, + Identifier, IfStmt, IncludeStmt, IntType, List, LiteralKind, Pragma, QuantumGateDefinition, + QubitDeclaration, RangeDefinition, ReturnStmt, ScalarType, ScalarTypeKind, Stmt, StmtKind, + SwitchStmt, TypeDef, TypedParameter, UIntType, WhileLoop, }, keyword::Keyword, lex::{ @@ -73,6 +73,8 @@ pub(super) fn parse(s: &mut ParserContext) -> Result> { Box::new(stmt) } else if let Some(stmt) = opt(s, parse_continue_stmt)? { Box::new(StmtKind::Continue(stmt)) + } else if let Some(stmt) = opt(s, parse_break_stmt)? { + Box::new(StmtKind::Break(stmt)) } else { return Err(Error::new(ErrorKind::Rule( "statement", @@ -1018,10 +1020,18 @@ pub fn parse_while_loop(s: &mut ParserContext) -> Result { }) } -/// Grammar: CONTINUE SEMICOLON +/// Grammar: `CONTINUE SEMICOLON`. fn parse_continue_stmt(s: &mut ParserContext) -> Result { let lo = s.peek().span.lo; token(s, TokenKind::Keyword(Keyword::Continue))?; recovering_token(s, TokenKind::Semicolon); Ok(ContinueStmt { span: s.span(lo) }) } + +/// Grammar: `BREAK SEMICOLON`. +fn parse_break_stmt(s: &mut ParserContext) -> Result { + let lo = s.peek().span.lo; + token(s, TokenKind::Keyword(Keyword::Break))?; + recovering_token(s, TokenKind::Semicolon); + Ok(BreakStmt { span: s.span(lo) }) +} diff --git a/compiler/qsc_qasm3/src/parser/stmt/tests/for_loops.rs b/compiler/qsc_qasm3/src/parser/stmt/tests/for_loops.rs index 26582b9a7b..04fde906fd 100644 --- a/compiler/qsc_qasm3/src/parser/stmt/tests/for_loops.rs +++ b/compiler/qsc_qasm3/src/parser/stmt/tests/for_loops.rs @@ -114,3 +114,25 @@ fn for_loop_with_continue_stmt() { StmtKind: Continue [57-66]"#]], ); } + +#[test] +fn for_loop_with_break_stmt() { + check( + parse, + " + for int x in {1, 2, 3} { + int a = 0; + break; + }", + &expect![[r#" + Stmt [5-69] + StmtKind: ForStmt [5-69]: ClassicalType [9-12]: IntType [9-12], Ident [13-14] "x", DiscreteSet [18-27]: + Expr [19-20]: Lit: Int(1) + Expr [22-23]: Lit: Int(2) + Expr [25-26]: Lit: Int(3) + Stmt [38-48] + StmtKind: ClassicalDeclarationStmt [38-48]: ClassicalType [38-41]: IntType [38-41], Ident [42-43] "a", ValueExpression ExprStmt [46-47]: Expr [46-47]: Lit: Int(0) + Stmt [57-63] + StmtKind: Break [57-63]"#]], + ); +} diff --git a/compiler/qsc_qasm3/src/parser/stmt/tests/while_loops.rs b/compiler/qsc_qasm3/src/parser/stmt/tests/while_loops.rs index 19648bb7da..921448ce72 100644 --- a/compiler/qsc_qasm3/src/parser/stmt/tests/while_loops.rs +++ b/compiler/qsc_qasm3/src/parser/stmt/tests/while_loops.rs @@ -59,3 +59,25 @@ fn while_loop_with_continue_stmt() { StmtKind: Continue [49-58]"#]], ); } + +#[test] +fn while_loop_with_break_stmt() { + check( + parse, + " + while (x != 2) { + int a = 0; + break; + }", + &expect![[r#" + Stmt [5-61] + StmtKind: WhileLoop [5-61]: Expr [11-19]: Paren: + Expr [12-18]: BinOp (Neq): + Expr [12-13]: Ident [12-13] "x" + Expr [17-18]: Lit: Int(2) + Stmt [30-40] + StmtKind: ClassicalDeclarationStmt [30-40]: ClassicalType [30-33]: IntType [30-33], Ident [34-35] "a", ValueExpression ExprStmt [38-39]: Expr [38-39]: Lit: Int(0) + Stmt [49-55] + StmtKind: Break [49-55]"#]], + ); +} From dc8eae0e6a870b75a6d94a594049cbff9071a6dd Mon Sep 17 00:00:00 2001 From: Oscar Puente <156957451+orpuente-MS@users.noreply.github.com> Date: Thu, 20 Feb 2025 10:58:25 -0800 Subject: [PATCH 017/108] fix assignment expressions and add expression statements --- .../qsc_qasm3/src/parser/completion/tests.rs | 4 +- compiler/qsc_qasm3/src/parser/expr.rs | 4 + compiler/qsc_qasm3/src/parser/expr/tests.rs | 30 +++--- compiler/qsc_qasm3/src/parser/stmt.rs | 16 +++- .../src/parser/stmt/tests/for_loops.rs | 92 +++++++++++-------- .../src/parser/stmt/tests/while_loops.rs | 56 ++++++----- 6 files changed, 121 insertions(+), 81 deletions(-) diff --git a/compiler/qsc_qasm3/src/parser/completion/tests.rs b/compiler/qsc_qasm3/src/parser/completion/tests.rs index bd89cafc63..eaac90607f 100644 --- a/compiler/qsc_qasm3/src/parser/completion/tests.rs +++ b/compiler/qsc_qasm3/src/parser/completion/tests.rs @@ -36,7 +36,7 @@ fn begin_document() { "|OPENQASM 3;", &expect![[r#" WordKinds( - Annotation | Break | Continue | CReg | Def | Extern | For | Gate | If | Include | Input | OpenQASM | Output | Pragma | QReg | Qubit | Return | Switch | While, + Annotation | Break | Continue | CReg | Def | Extern | False | For | Gate | If | Include | Input | OpenQASM | Output | Pragma | QReg | Qubit | True | Return | Switch | While, ) "#]], ); @@ -48,7 +48,7 @@ fn end_of_version() { "OPENQASM 3;|", &expect![[r#" WordKinds( - Annotation | Break | Continue | CReg | Def | Extern | For | Gate | If | Include | Input | Output | Pragma | QReg | Qubit | Return | Switch | While, + Annotation | Break | Continue | CReg | Def | Extern | False | For | Gate | If | Include | Input | Output | Pragma | QReg | Qubit | True | Return | Switch | While, ) "#]], ); diff --git a/compiler/qsc_qasm3/src/parser/expr.rs b/compiler/qsc_qasm3/src/parser/expr.rs index 20320facd5..be8c1f1fa6 100644 --- a/compiler/qsc_qasm3/src/parser/expr.rs +++ b/compiler/qsc_qasm3/src/parser/expr.rs @@ -624,6 +624,10 @@ fn infix_op(name: OpName) -> Option { kind: OpKind::Index, precedence: 13, }), + TokenKind::Eq => Some(InfixOp { + kind: OpKind::Assign, + precedence: 0, + }), _ => None, } } diff --git a/compiler/qsc_qasm3/src/parser/expr/tests.rs b/compiler/qsc_qasm3/src/parser/expr/tests.rs index a547ea8284..808c3f6781 100644 --- a/compiler/qsc_qasm3/src/parser/expr/tests.rs +++ b/compiler/qsc_qasm3/src/parser/expr/tests.rs @@ -1156,28 +1156,30 @@ fn lit_array() { #[test] fn assignment_and_unop() { check( - crate::parser::stmt::parse, - "bool c = a && !b;", + expr, + "c = a && !b", &expect![[r#" - Stmt [0-17] - StmtKind: ClassicalDeclarationStmt [0-17]: ClassicalType [0-4]: BoolType, Ident [5-6] "c", ValueExpression ExprStmt [9-16]: Expr [9-16]: BinOp (AndL): - Expr [9-10]: Ident [9-10] "a" - Expr [14-16]: UnOp (NotL): - Expr [15-16]: Ident [15-16] "b""#]], + Expr [0-11]: Assign: + Expr [0-1]: Ident [0-1] "c" + Expr [4-11]: BinOp (AndL): + Expr [4-5]: Ident [4-5] "a" + Expr [9-11]: UnOp (NotL): + Expr [10-11]: Ident [10-11] "b""#]], ); } #[test] fn assignment_unop_and() { check( - crate::parser::stmt::parse, - "bool d = !a && b;", + expr, + "d = !a && b", &expect![[r#" - Stmt [0-17] - StmtKind: ClassicalDeclarationStmt [0-17]: ClassicalType [0-4]: BoolType, Ident [5-6] "d", ValueExpression ExprStmt [9-16]: Expr [9-16]: BinOp (AndL): - Expr [9-11]: UnOp (NotL): - Expr [10-11]: Ident [10-11] "a" - Expr [15-16]: Ident [15-16] "b""#]], + Expr [0-11]: Assign: + Expr [0-1]: Ident [0-1] "d" + Expr [4-11]: BinOp (AndL): + Expr [4-6]: UnOp (NotL): + Expr [5-6]: Ident [5-6] "a" + Expr [10-11]: Ident [10-11] "b""#]], ); } diff --git a/compiler/qsc_qasm3/src/parser/stmt.rs b/compiler/qsc_qasm3/src/parser/stmt.rs index eaef067ffa..5e71b5f46f 100644 --- a/compiler/qsc_qasm3/src/parser/stmt.rs +++ b/compiler/qsc_qasm3/src/parser/stmt.rs @@ -75,6 +75,8 @@ pub(super) fn parse(s: &mut ParserContext) -> Result> { Box::new(StmtKind::Continue(stmt)) } else if let Some(stmt) = opt(s, parse_break_stmt)? { Box::new(StmtKind::Break(stmt)) + } else if let Some(stmt) = opt(s, parse_expression_stmt)? { + Box::new(StmtKind::ExprStmt(stmt)) } else { return Err(Error::new(ErrorKind::Rule( "statement", @@ -1024,7 +1026,7 @@ pub fn parse_while_loop(s: &mut ParserContext) -> Result { fn parse_continue_stmt(s: &mut ParserContext) -> Result { let lo = s.peek().span.lo; token(s, TokenKind::Keyword(Keyword::Continue))?; - recovering_token(s, TokenKind::Semicolon); + recovering_semi(s); Ok(ContinueStmt { span: s.span(lo) }) } @@ -1032,6 +1034,16 @@ fn parse_continue_stmt(s: &mut ParserContext) -> Result { fn parse_break_stmt(s: &mut ParserContext) -> Result { let lo = s.peek().span.lo; token(s, TokenKind::Keyword(Keyword::Break))?; - recovering_token(s, TokenKind::Semicolon); + recovering_semi(s); Ok(BreakStmt { span: s.span(lo) }) } + +fn parse_expression_stmt(s: &mut ParserContext) -> Result { + let lo = s.peek().span.lo; + let expr = expr::expr(s)?; + recovering_semi(s); + Ok(ExprStmt { + span: s.span(lo), + expr, + }) +} diff --git a/compiler/qsc_qasm3/src/parser/stmt/tests/for_loops.rs b/compiler/qsc_qasm3/src/parser/stmt/tests/for_loops.rs index 04fde906fd..1d6aa1f1e9 100644 --- a/compiler/qsc_qasm3/src/parser/stmt/tests/for_loops.rs +++ b/compiler/qsc_qasm3/src/parser/stmt/tests/for_loops.rs @@ -7,16 +7,18 @@ fn simple_for_loop() { parse, " for int x in {1, 2, 3} { - int a = 0; + a = 0; }", &expect![[r#" - Stmt [5-54] - StmtKind: ForStmt [5-54]: ClassicalType [9-12]: IntType [9-12], Ident [13-14] "x", DiscreteSet [18-27]: + Stmt [5-50] + StmtKind: ForStmt [5-50]: ClassicalType [9-12]: IntType [9-12], Ident [13-14] "x", DiscreteSet [18-27]: Expr [19-20]: Lit: Int(1) Expr [22-23]: Lit: Int(2) Expr [25-26]: Lit: Int(3) - Stmt [38-48] - StmtKind: ClassicalDeclarationStmt [38-48]: ClassicalType [38-41]: IntType [38-41], Ident [42-43] "a", ValueExpression ExprStmt [46-47]: Expr [46-47]: Lit: Int(0)"#]], + Stmt [38-44] + StmtKind: ExprStmt [38-44]: Expr [38-43]: Assign: + Expr [38-39]: Ident [38-39] "a" + Expr [42-43]: Lit: Int(0)"#]], ); } @@ -26,16 +28,18 @@ fn simple_for_loop_stmt_body() { parse, " for int x in {1, 2, 3} - int a = 0; + a = 0; ", &expect![[r#" - Stmt [5-46] - StmtKind: ForStmt [5-46]: ClassicalType [9-12]: IntType [9-12], Ident [13-14] "x", DiscreteSet [18-27]: + Stmt [5-42] + StmtKind: ForStmt [5-42]: ClassicalType [9-12]: IntType [9-12], Ident [13-14] "x", DiscreteSet [18-27]: Expr [19-20]: Lit: Int(1) Expr [22-23]: Lit: Int(2) Expr [25-26]: Lit: Int(3) - Stmt [36-46] - StmtKind: ClassicalDeclarationStmt [36-46]: ClassicalType [36-39]: IntType [36-39], Ident [40-41] "a", ValueExpression ExprStmt [44-45]: Expr [44-45]: Lit: Int(0)"#]], + Stmt [36-42] + StmtKind: ExprStmt [36-42]: Expr [36-41]: Assign: + Expr [36-37]: Ident [36-37] "a" + Expr [40-41]: Lit: Int(0)"#]], ); } @@ -45,16 +49,18 @@ fn for_loop_range() { parse, " for int x in [0:2:7] { - int a = 0; + a = 0; }", &expect![[r#" - Stmt [5-52] - StmtKind: ForStmt [5-52]: ClassicalType [9-12]: IntType [9-12], Ident [13-14] "x", Range: [18-25] + Stmt [5-48] + StmtKind: ForStmt [5-48]: ClassicalType [9-12]: IntType [9-12], Ident [13-14] "x", Range: [18-25] start: Expr [19-20]: Lit: Int(0) step: Expr [21-22]: Lit: Int(2) end: Expr [23-24]: Lit: Int(7) - Stmt [36-46] - StmtKind: ClassicalDeclarationStmt [36-46]: ClassicalType [36-39]: IntType [36-39], Ident [40-41] "a", ValueExpression ExprStmt [44-45]: Expr [44-45]: Lit: Int(0)"#]], + Stmt [36-42] + StmtKind: ExprStmt [36-42]: Expr [36-41]: Assign: + Expr [36-37]: Ident [36-37] "a" + Expr [40-41]: Lit: Int(0)"#]], ); } @@ -64,16 +70,18 @@ fn for_loop_range_no_step() { parse, " for int x in [0:7] { - int a = 0; + a = 0; }", &expect![[r#" - Stmt [5-50] - StmtKind: ForStmt [5-50]: ClassicalType [9-12]: IntType [9-12], Ident [13-14] "x", Range: [18-23] + Stmt [5-46] + StmtKind: ForStmt [5-46]: ClassicalType [9-12]: IntType [9-12], Ident [13-14] "x", Range: [18-23] start: Expr [19-20]: Lit: Int(0) end: Expr [21-22]: Lit: Int(7) - Stmt [34-44] - StmtKind: ClassicalDeclarationStmt [34-44]: ClassicalType [34-37]: IntType [34-37], Ident [38-39] "a", ValueExpression ExprStmt [42-43]: Expr [42-43]: Lit: Int(0)"#]], + Stmt [34-40] + StmtKind: ExprStmt [34-40]: Expr [34-39]: Assign: + Expr [34-35]: Ident [34-35] "a" + Expr [38-39]: Lit: Int(0)"#]], ); } @@ -83,13 +91,15 @@ fn for_loop_expr() { parse, " for int x in xs { - int a = 0; + a = 0; }", &expect![[r#" - Stmt [5-47] - StmtKind: ForStmt [5-47]: ClassicalType [9-12]: IntType [9-12], Ident [13-14] "x", Expr [18-20]: Ident [18-20] "xs" - Stmt [31-41] - StmtKind: ClassicalDeclarationStmt [31-41]: ClassicalType [31-34]: IntType [31-34], Ident [35-36] "a", ValueExpression ExprStmt [39-40]: Expr [39-40]: Lit: Int(0)"#]], + Stmt [5-43] + StmtKind: ForStmt [5-43]: ClassicalType [9-12]: IntType [9-12], Ident [13-14] "x", Expr [18-20]: Ident [18-20] "xs" + Stmt [31-37] + StmtKind: ExprStmt [31-37]: Expr [31-36]: Assign: + Expr [31-32]: Ident [31-32] "a" + Expr [35-36]: Lit: Int(0)"#]], ); } @@ -99,19 +109,21 @@ fn for_loop_with_continue_stmt() { parse, " for int x in {1, 2, 3} { - int a = 0; + a = 0; continue; }", &expect![[r#" - Stmt [5-72] - StmtKind: ForStmt [5-72]: ClassicalType [9-12]: IntType [9-12], Ident [13-14] "x", DiscreteSet [18-27]: + Stmt [5-68] + StmtKind: ForStmt [5-68]: ClassicalType [9-12]: IntType [9-12], Ident [13-14] "x", DiscreteSet [18-27]: Expr [19-20]: Lit: Int(1) Expr [22-23]: Lit: Int(2) Expr [25-26]: Lit: Int(3) - Stmt [38-48] - StmtKind: ClassicalDeclarationStmt [38-48]: ClassicalType [38-41]: IntType [38-41], Ident [42-43] "a", ValueExpression ExprStmt [46-47]: Expr [46-47]: Lit: Int(0) - Stmt [57-66] - StmtKind: Continue [57-66]"#]], + Stmt [38-44] + StmtKind: ExprStmt [38-44]: Expr [38-43]: Assign: + Expr [38-39]: Ident [38-39] "a" + Expr [42-43]: Lit: Int(0) + Stmt [53-62] + StmtKind: Continue [53-62]"#]], ); } @@ -121,18 +133,20 @@ fn for_loop_with_break_stmt() { parse, " for int x in {1, 2, 3} { - int a = 0; + a = 0; break; }", &expect![[r#" - Stmt [5-69] - StmtKind: ForStmt [5-69]: ClassicalType [9-12]: IntType [9-12], Ident [13-14] "x", DiscreteSet [18-27]: + Stmt [5-65] + StmtKind: ForStmt [5-65]: ClassicalType [9-12]: IntType [9-12], Ident [13-14] "x", DiscreteSet [18-27]: Expr [19-20]: Lit: Int(1) Expr [22-23]: Lit: Int(2) Expr [25-26]: Lit: Int(3) - Stmt [38-48] - StmtKind: ClassicalDeclarationStmt [38-48]: ClassicalType [38-41]: IntType [38-41], Ident [42-43] "a", ValueExpression ExprStmt [46-47]: Expr [46-47]: Lit: Int(0) - Stmt [57-63] - StmtKind: Break [57-63]"#]], + Stmt [38-44] + StmtKind: ExprStmt [38-44]: Expr [38-43]: Assign: + Expr [38-39]: Ident [38-39] "a" + Expr [42-43]: Lit: Int(0) + Stmt [53-59] + StmtKind: Break [53-59]"#]], ); } diff --git a/compiler/qsc_qasm3/src/parser/stmt/tests/while_loops.rs b/compiler/qsc_qasm3/src/parser/stmt/tests/while_loops.rs index 921448ce72..9e5bde86a8 100644 --- a/compiler/qsc_qasm3/src/parser/stmt/tests/while_loops.rs +++ b/compiler/qsc_qasm3/src/parser/stmt/tests/while_loops.rs @@ -7,16 +7,18 @@ fn simple_while() { parse, " while (x != 2) { - int a = 0; + a = 0; }", &expect![[r#" - Stmt [5-46] - StmtKind: WhileLoop [5-46]: Expr [11-19]: Paren: + Stmt [5-42] + StmtKind: WhileLoop [5-42]: Expr [11-19]: Paren: Expr [12-18]: BinOp (Neq): Expr [12-13]: Ident [12-13] "x" Expr [17-18]: Lit: Int(2) - Stmt [30-40] - StmtKind: ClassicalDeclarationStmt [30-40]: ClassicalType [30-33]: IntType [30-33], Ident [34-35] "a", ValueExpression ExprStmt [38-39]: Expr [38-39]: Lit: Int(0)"#]], + Stmt [30-36] + StmtKind: ExprStmt [30-36]: Expr [30-35]: Assign: + Expr [30-31]: Ident [30-31] "a" + Expr [34-35]: Lit: Int(0)"#]], ); } @@ -26,15 +28,17 @@ fn while_stmt_body() { parse, " while (x != 2) - int a = 0;", + a = 0;", &expect![[r#" - Stmt [5-38] - StmtKind: WhileLoop [5-38]: Expr [11-19]: Paren: + Stmt [5-34] + StmtKind: WhileLoop [5-34]: Expr [11-19]: Paren: Expr [12-18]: BinOp (Neq): Expr [12-13]: Ident [12-13] "x" Expr [17-18]: Lit: Int(2) - Stmt [28-38] - StmtKind: ClassicalDeclarationStmt [28-38]: ClassicalType [28-31]: IntType [28-31], Ident [32-33] "a", ValueExpression ExprStmt [36-37]: Expr [36-37]: Lit: Int(0)"#]], + Stmt [28-34] + StmtKind: ExprStmt [28-34]: Expr [28-33]: Assign: + Expr [28-29]: Ident [28-29] "a" + Expr [32-33]: Lit: Int(0)"#]], ); } @@ -44,19 +48,21 @@ fn while_loop_with_continue_stmt() { parse, " while (x != 2) { - int a = 0; + a = 0; continue; }", &expect![[r#" - Stmt [5-64] - StmtKind: WhileLoop [5-64]: Expr [11-19]: Paren: + Stmt [5-60] + StmtKind: WhileLoop [5-60]: Expr [11-19]: Paren: Expr [12-18]: BinOp (Neq): Expr [12-13]: Ident [12-13] "x" Expr [17-18]: Lit: Int(2) - Stmt [30-40] - StmtKind: ClassicalDeclarationStmt [30-40]: ClassicalType [30-33]: IntType [30-33], Ident [34-35] "a", ValueExpression ExprStmt [38-39]: Expr [38-39]: Lit: Int(0) - Stmt [49-58] - StmtKind: Continue [49-58]"#]], + Stmt [30-36] + StmtKind: ExprStmt [30-36]: Expr [30-35]: Assign: + Expr [30-31]: Ident [30-31] "a" + Expr [34-35]: Lit: Int(0) + Stmt [45-54] + StmtKind: Continue [45-54]"#]], ); } @@ -66,18 +72,20 @@ fn while_loop_with_break_stmt() { parse, " while (x != 2) { - int a = 0; + a = 0; break; }", &expect![[r#" - Stmt [5-61] - StmtKind: WhileLoop [5-61]: Expr [11-19]: Paren: + Stmt [5-57] + StmtKind: WhileLoop [5-57]: Expr [11-19]: Paren: Expr [12-18]: BinOp (Neq): Expr [12-13]: Ident [12-13] "x" Expr [17-18]: Lit: Int(2) - Stmt [30-40] - StmtKind: ClassicalDeclarationStmt [30-40]: ClassicalType [30-33]: IntType [30-33], Ident [34-35] "a", ValueExpression ExprStmt [38-39]: Expr [38-39]: Lit: Int(0) - Stmt [49-55] - StmtKind: Break [49-55]"#]], + Stmt [30-36] + StmtKind: ExprStmt [30-36]: Expr [30-35]: Assign: + Expr [30-31]: Ident [30-31] "a" + Expr [34-35]: Lit: Int(0) + Stmt [45-51] + StmtKind: Break [45-51]"#]], ); } From 2162b11b768502ce19ffd10a65a24af60ff67e43 Mon Sep 17 00:00:00 2001 From: Oscar Puente <156957451+orpuente-MS@users.noreply.github.com> Date: Thu, 20 Feb 2025 11:09:57 -0800 Subject: [PATCH 018/108] add end stmt --- compiler/qsc_qasm3/src/ast.rs | 13 +++++++++++++ compiler/qsc_qasm3/src/parser/completion/tests.rs | 4 ++-- compiler/qsc_qasm3/src/parser/stmt.rs | 15 +++++++++++++-- 3 files changed, 28 insertions(+), 4 deletions(-) diff --git a/compiler/qsc_qasm3/src/ast.rs b/compiler/qsc_qasm3/src/ast.rs index 34efd72a13..c27e701a7a 100644 --- a/compiler/qsc_qasm3/src/ast.rs +++ b/compiler/qsc_qasm3/src/ast.rs @@ -395,6 +395,7 @@ pub enum StmtKind { DelayStmt(DelayStmt), /// An empty statement. Empty, + End(EndStmt), ExprStmt(ExprStmt), ExternDecl(ExternDecl), For(ForStmt), @@ -435,6 +436,7 @@ impl Display for StmtKind { StmtKind::DefCal(defcal) => write!(f, "{defcal}"), StmtKind::DelayStmt(delay) => write!(f, "{delay}"), StmtKind::Empty => write!(f, "Empty"), + StmtKind::End(end_stmt) => write!(f, "{end_stmt}"), StmtKind::ExprStmt(expr) => write!(f, "{expr}"), StmtKind::ExternDecl(decl) => write!(f, "{decl}"), StmtKind::For(for_stmt) => write!(f, "{for_stmt}"), @@ -2075,6 +2077,17 @@ impl Display for ContinueStmt { } } +#[derive(Clone, Debug)] +pub struct EndStmt { + pub span: Span, +} + +impl Display for EndStmt { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + write!(f, "End {}", self.span) + } +} + fn display_assign(mut indent: Indented, lhs: &Expr, rhs: &Expr) -> fmt::Result { write!(indent, "Assign:")?; indent = set_indentation(indent, 1); diff --git a/compiler/qsc_qasm3/src/parser/completion/tests.rs b/compiler/qsc_qasm3/src/parser/completion/tests.rs index eaac90607f..515b53eec5 100644 --- a/compiler/qsc_qasm3/src/parser/completion/tests.rs +++ b/compiler/qsc_qasm3/src/parser/completion/tests.rs @@ -36,7 +36,7 @@ fn begin_document() { "|OPENQASM 3;", &expect![[r#" WordKinds( - Annotation | Break | Continue | CReg | Def | Extern | False | For | Gate | If | Include | Input | OpenQASM | Output | Pragma | QReg | Qubit | True | Return | Switch | While, + Annotation | Break | Continue | CReg | Def | End | Extern | False | For | Gate | If | Include | Input | OpenQASM | Output | Pragma | QReg | Qubit | True | Return | Switch | While, ) "#]], ); @@ -48,7 +48,7 @@ fn end_of_version() { "OPENQASM 3;|", &expect![[r#" WordKinds( - Annotation | Break | Continue | CReg | Def | Extern | False | For | Gate | If | Include | Input | Output | Pragma | QReg | Qubit | True | Return | Switch | While, + Annotation | Break | Continue | CReg | Def | End | Extern | False | For | Gate | If | Include | Input | Output | Pragma | QReg | Qubit | True | Return | Switch | While, ) "#]], ); diff --git a/compiler/qsc_qasm3/src/parser/stmt.rs b/compiler/qsc_qasm3/src/parser/stmt.rs index 5e71b5f46f..d18a2e49a0 100644 --- a/compiler/qsc_qasm3/src/parser/stmt.rs +++ b/compiler/qsc_qasm3/src/parser/stmt.rs @@ -18,8 +18,8 @@ use crate::{ ast::{ list_from_iter, AccessControl, AngleType, Annotation, ArrayBaseTypeKind, ArrayReferenceType, ArrayType, BitType, Block, BreakStmt, ClassicalDeclarationStmt, - ComplexType, ConstantDeclaration, ContinueStmt, DefStmt, EnumerableSet, Expr, ExprStmt, - ExternDecl, ExternParameter, FloatType, ForStmt, IODeclaration, IOKeyword, Ident, + ComplexType, ConstantDeclaration, ContinueStmt, DefStmt, EndStmt, EnumerableSet, Expr, + ExprStmt, ExternDecl, ExternParameter, FloatType, ForStmt, IODeclaration, IOKeyword, Ident, Identifier, IfStmt, IncludeStmt, IntType, List, LiteralKind, Pragma, QuantumGateDefinition, QubitDeclaration, RangeDefinition, ReturnStmt, ScalarType, ScalarTypeKind, Stmt, StmtKind, SwitchStmt, TypeDef, TypedParameter, UIntType, WhileLoop, @@ -75,6 +75,8 @@ pub(super) fn parse(s: &mut ParserContext) -> Result> { Box::new(StmtKind::Continue(stmt)) } else if let Some(stmt) = opt(s, parse_break_stmt)? { Box::new(StmtKind::Break(stmt)) + } else if let Some(stmt) = opt(s, parse_end_stmt)? { + Box::new(StmtKind::End(stmt)) } else if let Some(stmt) = opt(s, parse_expression_stmt)? { Box::new(StmtKind::ExprStmt(stmt)) } else { @@ -1038,6 +1040,15 @@ fn parse_break_stmt(s: &mut ParserContext) -> Result { Ok(BreakStmt { span: s.span(lo) }) } +/// Grammar: `END SEMICOLON`. +fn parse_end_stmt(s: &mut ParserContext) -> Result { + let lo = s.peek().span.lo; + token(s, TokenKind::Keyword(Keyword::End))?; + recovering_semi(s); + Ok(EndStmt { span: s.span(lo) }) +} + +/// GRAMMAR: `expression SEMICOLON`. fn parse_expression_stmt(s: &mut ParserContext) -> Result { let lo = s.peek().span.lo; let expr = expr::expr(s)?; From 5afe7a1dce3e364a93b3bffdcbc544f0c7a9f31a Mon Sep 17 00:00:00 2001 From: Oscar Puente <156957451+orpuente-MS@users.noreply.github.com> Date: Thu, 20 Feb 2025 12:45:46 -0800 Subject: [PATCH 019/108] add alias declarations --- compiler/qsc_qasm3/src/ast.rs | 24 +++++-------------- .../qsc_qasm3/src/parser/completion/tests.rs | 4 ++-- compiler/qsc_qasm3/src/parser/expr.rs | 16 +++++++++++-- compiler/qsc_qasm3/src/parser/stmt.rs | 18 +++++++++++++- compiler/qsc_qasm3/src/parser/stmt/tests.rs | 1 + .../qsc_qasm3/src/parser/stmt/tests/alias.rs | 24 +++++++++++++++++++ 6 files changed, 64 insertions(+), 23 deletions(-) create mode 100644 compiler/qsc_qasm3/src/parser/stmt/tests/alias.rs diff --git a/compiler/qsc_qasm3/src/ast.rs b/compiler/qsc_qasm3/src/ast.rs index c27e701a7a..5b5e8405fc 100644 --- a/compiler/qsc_qasm3/src/ast.rs +++ b/compiler/qsc_qasm3/src/ast.rs @@ -326,18 +326,18 @@ impl Display for HardwareQubit { } #[derive(Clone, Debug)] -pub struct Alias { - pub ident: Box, - pub expr: Box>, +pub struct AliasDeclStmt { + pub ident: Identifier, + pub exprs: List, pub span: Span, } -impl Display for Alias { +impl Display for AliasDeclStmt { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { let mut indent = set_indentation(indented(f), 0); write!(indent, "Alias {}: {}", self.span, self.ident)?; indent = set_indentation(indent, 1); - for expr in &*self.expr { + for expr in &*self.exprs { write!(indent, "\n{expr}")?; } Ok(()) @@ -378,7 +378,7 @@ impl Display for AssignOp { /// A statement kind. #[derive(Clone, Debug, Default)] pub enum StmtKind { - Alias(Alias), + Alias(AliasDeclStmt), Assign(Assign), AssignOp(AssignOp), Barrier(BarrierStmt), @@ -629,18 +629,6 @@ impl Display for IndexedIdent { } } -#[derive(Clone, Debug)] -pub struct AliasStmt { - pub span: Span, - pub kind: Box, -} - -impl Display for AliasStmt { - fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { - write!(f, "AliasStmt {}: {}", self.span, self.kind) - } -} - #[derive(Clone, Debug)] pub struct ExprStmt { pub span: Span, diff --git a/compiler/qsc_qasm3/src/parser/completion/tests.rs b/compiler/qsc_qasm3/src/parser/completion/tests.rs index 515b53eec5..2abda50e4e 100644 --- a/compiler/qsc_qasm3/src/parser/completion/tests.rs +++ b/compiler/qsc_qasm3/src/parser/completion/tests.rs @@ -36,7 +36,7 @@ fn begin_document() { "|OPENQASM 3;", &expect![[r#" WordKinds( - Annotation | Break | Continue | CReg | Def | End | Extern | False | For | Gate | If | Include | Input | OpenQASM | Output | Pragma | QReg | Qubit | True | Return | Switch | While, + Annotation | Break | Continue | CReg | Def | End | Extern | False | For | Gate | If | Include | Input | Let | OpenQASM | Output | Pragma | QReg | Qubit | True | Return | Switch | While, ) "#]], ); @@ -48,7 +48,7 @@ fn end_of_version() { "OPENQASM 3;|", &expect![[r#" WordKinds( - Annotation | Break | Continue | CReg | Def | End | Extern | False | For | Gate | If | Include | Input | Output | Pragma | QReg | Qubit | True | Return | Switch | While, + Annotation | Break | Continue | CReg | Def | End | Extern | False | For | Gate | If | Include | Input | Let | Output | Pragma | QReg | Qubit | True | Return | Switch | While, ) "#]], ); diff --git a/compiler/qsc_qasm3/src/parser/expr.rs b/compiler/qsc_qasm3/src/parser/expr.rs index be8c1f1fa6..c67fb560de 100644 --- a/compiler/qsc_qasm3/src/parser/expr.rs +++ b/compiler/qsc_qasm3/src/parser/expr.rs @@ -18,8 +18,8 @@ use crate::{ ast::{ self, list_from_iter, AssignExpr, AssignOpExpr, BinOp, BinaryOpExpr, Cast, DiscreteSet, Expr, ExprKind, ExprStmt, FunctionCall, GateOperand, HardwareQubit, Ident, IndexElement, - IndexExpr, IndexSetItem, IndexedIdent, Lit, LiteralKind, MeasureExpr, RangeDefinition, - TypeDef, UnaryOp, ValueExpression, Version, + IndexExpr, IndexSetItem, IndexedIdent, List, Lit, LiteralKind, MeasureExpr, + RangeDefinition, TypeDef, UnaryOp, ValueExpression, Version, }, keyword::Keyword, lex::{ @@ -777,3 +777,15 @@ fn index_operand(s: &mut ParserContext) -> Result { recovering_token(s, TokenKind::Close(Delim::Bracket)); Ok(index) } + +/// This expressions are not part of the expression tree +/// and are only used in alias statements. +/// Grammar: +pub fn alias_expr(s: &mut ParserContext) -> Result> { + let mut exprs = Vec::new(); + exprs.push(expr(s)?); + while opt(s, |s| token(s, TokenKind::PlusPlus))?.is_some() { + exprs.push(expr(s)?); + } + Ok(list_from_iter(exprs)) +} diff --git a/compiler/qsc_qasm3/src/parser/stmt.rs b/compiler/qsc_qasm3/src/parser/stmt.rs index d18a2e49a0..9ce6c2fd3a 100644 --- a/compiler/qsc_qasm3/src/parser/stmt.rs +++ b/compiler/qsc_qasm3/src/parser/stmt.rs @@ -16,7 +16,7 @@ use super::{ }; use crate::{ ast::{ - list_from_iter, AccessControl, AngleType, Annotation, ArrayBaseTypeKind, + list_from_iter, AccessControl, AliasDeclStmt, AngleType, Annotation, ArrayBaseTypeKind, ArrayReferenceType, ArrayType, BitType, Block, BreakStmt, ClassicalDeclarationStmt, ComplexType, ConstantDeclaration, ContinueStmt, DefStmt, EndStmt, EnumerableSet, Expr, ExprStmt, ExternDecl, ExternParameter, FloatType, ForStmt, IODeclaration, IOKeyword, Ident, @@ -79,6 +79,8 @@ pub(super) fn parse(s: &mut ParserContext) -> Result> { Box::new(StmtKind::End(stmt)) } else if let Some(stmt) = opt(s, parse_expression_stmt)? { Box::new(StmtKind::ExprStmt(stmt)) + } else if let Some(alias) = opt(s, parse_alias_stmt)? { + Box::new(StmtKind::Alias(alias)) } else { return Err(Error::new(ErrorKind::Rule( "statement", @@ -1058,3 +1060,17 @@ fn parse_expression_stmt(s: &mut ParserContext) -> Result { expr, }) } + +fn parse_alias_stmt(s: &mut ParserContext) -> Result { + let lo = s.peek().span.lo; + token(s, TokenKind::Keyword(Keyword::Let))?; + let ident = Identifier::Ident(Box::new(prim::ident(s)?)); + token(s, TokenKind::Eq)?; + let exprs = expr::alias_expr(s)?; + + Ok(AliasDeclStmt { + ident, + exprs, + span: s.span(lo), + }) +} diff --git a/compiler/qsc_qasm3/src/parser/stmt/tests.rs b/compiler/qsc_qasm3/src/parser/stmt/tests.rs index 7fbaeee543..e729691c4f 100644 --- a/compiler/qsc_qasm3/src/parser/stmt/tests.rs +++ b/compiler/qsc_qasm3/src/parser/stmt/tests.rs @@ -1,6 +1,7 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. +mod alias; mod annotation; mod classical_decl; mod def; diff --git a/compiler/qsc_qasm3/src/parser/stmt/tests/alias.rs b/compiler/qsc_qasm3/src/parser/stmt/tests/alias.rs new file mode 100644 index 0000000000..3f518e463e --- /dev/null +++ b/compiler/qsc_qasm3/src/parser/stmt/tests/alias.rs @@ -0,0 +1,24 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +use crate::parser::stmt::parse; +use crate::parser::tests::check; +use expect_test::expect; + +#[test] +fn alias_decl_stmt() { + check(parse, "let x = a[1:2] ++ b ++ c[1:2:3]", &expect![[r#" + Stmt [0-31] + StmtKind: Alias [0-31]: Ident [4-5] "x" + Expr [8-14]: IndexExpr [9-14]: Expr [8-9]: Ident [8-9] "a", IndexElement: + Range: [10-13] + start: Expr [10-11]: Lit: Int(1) + + end: Expr [12-13]: Lit: Int(2) + Expr [18-19]: Ident [18-19] "b" + Expr [23-31]: IndexExpr [24-31]: Expr [23-24]: Ident [23-24] "c", IndexElement: + Range: [25-30] + start: Expr [25-26]: Lit: Int(1) + step: Expr [27-28]: Lit: Int(2) + end: Expr [29-30]: Lit: Int(3)"#]]); +} From a968393c05c421f528ac2a1739a59e42b5329a73 Mon Sep 17 00:00:00 2001 From: Oscar Puente <156957451+orpuente-MS@users.noreply.github.com> Date: Thu, 20 Feb 2025 12:48:26 -0800 Subject: [PATCH 020/108] format alias.rs file --- compiler/qsc_qasm3/src/parser/stmt/tests/alias.rs | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/compiler/qsc_qasm3/src/parser/stmt/tests/alias.rs b/compiler/qsc_qasm3/src/parser/stmt/tests/alias.rs index 3f518e463e..a7fa360090 100644 --- a/compiler/qsc_qasm3/src/parser/stmt/tests/alias.rs +++ b/compiler/qsc_qasm3/src/parser/stmt/tests/alias.rs @@ -7,7 +7,10 @@ use expect_test::expect; #[test] fn alias_decl_stmt() { - check(parse, "let x = a[1:2] ++ b ++ c[1:2:3]", &expect![[r#" + check( + parse, + "let x = a[1:2] ++ b ++ c[1:2:3]", + &expect![[r#" Stmt [0-31] StmtKind: Alias [0-31]: Ident [4-5] "x" Expr [8-14]: IndexExpr [9-14]: Expr [8-9]: Ident [8-9] "a", IndexElement: @@ -20,5 +23,6 @@ fn alias_decl_stmt() { Range: [25-30] start: Expr [25-26]: Lit: Int(1) step: Expr [27-28]: Lit: Int(2) - end: Expr [29-30]: Lit: Int(3)"#]]); + end: Expr [29-30]: Lit: Int(3)"#]], + ); } From e85af013db0c93cbf75ca962272f6d1eb12f18ff Mon Sep 17 00:00:00 2001 From: Oscar Puente <156957451+orpuente-MS@users.noreply.github.com> Date: Thu, 20 Feb 2025 14:28:15 -0800 Subject: [PATCH 021/108] fixes during PR review with swernli --- compiler/qsc_qasm3/src/parser/expr.rs | 4 +- compiler/qsc_qasm3/src/parser/stmt.rs | 13 ++- .../qsc_qasm3/src/parser/stmt/tests/alias.rs | 28 +++---- .../src/parser/stmt/tests/if_stmt.rs | 82 +++++++++++-------- 4 files changed, 70 insertions(+), 57 deletions(-) diff --git a/compiler/qsc_qasm3/src/parser/expr.rs b/compiler/qsc_qasm3/src/parser/expr.rs index c67fb560de..f3772a7f36 100644 --- a/compiler/qsc_qasm3/src/parser/expr.rs +++ b/compiler/qsc_qasm3/src/parser/expr.rs @@ -517,7 +517,7 @@ fn index_set_item(s: &mut ParserContext) -> Result { })); } - // If there was a second semicolon, the second expression was the step. + // If there was a second colon, the second expression was the step. let step = end; let end = opt(s, expr)?; @@ -780,7 +780,7 @@ fn index_operand(s: &mut ParserContext) -> Result { /// This expressions are not part of the expression tree /// and are only used in alias statements. -/// Grammar: +/// Grammar: `expression (DOUBLE_PLUS expression)*`. pub fn alias_expr(s: &mut ParserContext) -> Result> { let mut exprs = Vec::new(); exprs.push(expr(s)?); diff --git a/compiler/qsc_qasm3/src/parser/stmt.rs b/compiler/qsc_qasm3/src/parser/stmt.rs index 9ce6c2fd3a..6e7014c417 100644 --- a/compiler/qsc_qasm3/src/parser/stmt.rs +++ b/compiler/qsc_qasm3/src/parser/stmt.rs @@ -919,7 +919,7 @@ fn parse_block_or_stmt(s: &mut ParserContext) -> Result> { } } -/// Grammar ` LPAREN expression RPAREN if_body=statementOrScope (ELSE else_body=statementOrScope)?`. +/// Grammar `IF LPAREN expression RPAREN if_body=statementOrScope (ELSE else_body=statementOrScope)?`. /// Source: . pub fn parse_if_stmt(s: &mut ParserContext) -> Result { let lo = s.peek().span.lo; @@ -958,12 +958,9 @@ fn for_loop_range_expr(s: &mut ParserContext) -> Result { // If we find a third expr, then the second expr was the `step`. // and this third expr is the actual `end`. - if let Some(expr) = opt(s, |s| { - token(s, TokenKind::Colon)?; - expr::expr(s) - })? { + if token(s, TokenKind::Colon).is_ok() { step = end; - end = Some(expr); + end = Some(expr::expr(s)?); } recovering_token(s, TokenKind::Close(Delim::Bracket)); @@ -1050,7 +1047,7 @@ fn parse_end_stmt(s: &mut ParserContext) -> Result { Ok(EndStmt { span: s.span(lo) }) } -/// GRAMMAR: `expression SEMICOLON`. +/// Grammar: `expression SEMICOLON`. fn parse_expression_stmt(s: &mut ParserContext) -> Result { let lo = s.peek().span.lo; let expr = expr::expr(s)?; @@ -1061,12 +1058,14 @@ fn parse_expression_stmt(s: &mut ParserContext) -> Result { }) } +/// Grammar: `LET Identifier EQUALS aliasExpression SEMICOLON`. fn parse_alias_stmt(s: &mut ParserContext) -> Result { let lo = s.peek().span.lo; token(s, TokenKind::Keyword(Keyword::Let))?; let ident = Identifier::Ident(Box::new(prim::ident(s)?)); token(s, TokenKind::Eq)?; let exprs = expr::alias_expr(s)?; + recovering_semi(s); Ok(AliasDeclStmt { ident, diff --git a/compiler/qsc_qasm3/src/parser/stmt/tests/alias.rs b/compiler/qsc_qasm3/src/parser/stmt/tests/alias.rs index a7fa360090..65d53fc6c7 100644 --- a/compiler/qsc_qasm3/src/parser/stmt/tests/alias.rs +++ b/compiler/qsc_qasm3/src/parser/stmt/tests/alias.rs @@ -9,20 +9,20 @@ use expect_test::expect; fn alias_decl_stmt() { check( parse, - "let x = a[1:2] ++ b ++ c[1:2:3]", + "let x = a[1:2] ++ b ++ c[1:2:3];", &expect![[r#" - Stmt [0-31] - StmtKind: Alias [0-31]: Ident [4-5] "x" - Expr [8-14]: IndexExpr [9-14]: Expr [8-9]: Ident [8-9] "a", IndexElement: - Range: [10-13] - start: Expr [10-11]: Lit: Int(1) - - end: Expr [12-13]: Lit: Int(2) - Expr [18-19]: Ident [18-19] "b" - Expr [23-31]: IndexExpr [24-31]: Expr [23-24]: Ident [23-24] "c", IndexElement: - Range: [25-30] - start: Expr [25-26]: Lit: Int(1) - step: Expr [27-28]: Lit: Int(2) - end: Expr [29-30]: Lit: Int(3)"#]], + Stmt [0-32] + StmtKind: Alias [0-32]: Ident [4-5] "x" + Expr [8-14]: IndexExpr [9-14]: Expr [8-9]: Ident [8-9] "a", IndexElement: + Range: [10-13] + start: Expr [10-11]: Lit: Int(1) + + end: Expr [12-13]: Lit: Int(2) + Expr [18-19]: Ident [18-19] "b" + Expr [23-31]: IndexExpr [24-31]: Expr [23-24]: Ident [23-24] "c", IndexElement: + Range: [25-30] + start: Expr [25-26]: Lit: Int(1) + step: Expr [27-28]: Lit: Int(2) + end: Expr [29-30]: Lit: Int(3)"#]], ); } diff --git a/compiler/qsc_qasm3/src/parser/stmt/tests/if_stmt.rs b/compiler/qsc_qasm3/src/parser/stmt/tests/if_stmt.rs index 1e265ba04b..fe1ae6dfdc 100644 --- a/compiler/qsc_qasm3/src/parser/stmt/tests/if_stmt.rs +++ b/compiler/qsc_qasm3/src/parser/stmt/tests/if_stmt.rs @@ -7,22 +7,26 @@ fn simple_if_stmt() { parse, " if (x == y) { - int a = 0; + a = 0; } else { - int a = 1; + a = 1; } ", &expect![[r#" - Stmt [5-75] - StmtKind: IfStmt [5-75]: Expr [8-16]: Paren: + Stmt [5-67] + StmtKind: IfStmt [5-67]: Expr [8-16]: Paren: Expr [9-15]: BinOp (Eq): Expr [9-10]: Ident [9-10] "x" Expr [14-15]: Ident [14-15] "y" - Stmt [27-37] - StmtKind: ClassicalDeclarationStmt [27-37]: ClassicalType [27-30]: IntType [27-30], Ident [31-32] "a", ValueExpression ExprStmt [35-36]: Expr [35-36]: Lit: Int(0) + Stmt [27-33] + StmtKind: ExprStmt [27-33]: Expr [27-32]: Assign: + Expr [27-28]: Ident [27-28] "a" + Expr [31-32]: Lit: Int(0) Else: - Stmt [59-69] - StmtKind: ClassicalDeclarationStmt [59-69]: ClassicalType [59-62]: IntType [59-62], Ident [63-64] "a", ValueExpression ExprStmt [67-68]: Expr [67-68]: Lit: Int(1)"#]], + Stmt [55-61] + StmtKind: ExprStmt [55-61]: Expr [55-60]: Assign: + Expr [55-56]: Ident [55-56] "a" + Expr [59-60]: Lit: Int(1)"#]], ); } @@ -32,17 +36,19 @@ fn if_stmt_missing_else() { parse, " if (x == y) { - int a = 0; + a = 0; } ", &expect![[r#" - Stmt [5-43] - StmtKind: IfStmt [5-43]: Expr [8-16]: Paren: + Stmt [5-39] + StmtKind: IfStmt [5-39]: Expr [8-16]: Paren: Expr [9-15]: BinOp (Eq): Expr [9-10]: Ident [9-10] "x" Expr [14-15]: Ident [14-15] "y" - Stmt [27-37] - StmtKind: ClassicalDeclarationStmt [27-37]: ClassicalType [27-30]: IntType [27-30], Ident [31-32] "a", ValueExpression ExprStmt [35-36]: Expr [35-36]: Lit: Int(0)"#]], + Stmt [27-33] + StmtKind: ExprStmt [27-33]: Expr [27-32]: Assign: + Expr [27-28]: Ident [27-28] "a" + Expr [31-32]: Lit: Int(0)"#]], ); } @@ -53,44 +59,52 @@ fn nested_if_stmts() { " if (x == y) { if (x1 == y1) { - int a = 0; + a = 0; } else { - int a = 1; + a = 1; } } else { if (x2 == y2) { - int a = 2; + a = 2; } else { - int a = 3; + a = 3; } } ", &expect![[r#" - Stmt [5-231] - StmtKind: IfStmt [5-231]: Expr [8-16]: Paren: + Stmt [5-215] + StmtKind: IfStmt [5-215]: Expr [8-16]: Paren: Expr [9-15]: BinOp (Eq): Expr [9-10]: Ident [9-10] "x" Expr [14-15]: Ident [14-15] "y" - Stmt [27-115] - StmtKind: IfStmt [27-115]: Expr [30-40]: Paren: + Stmt [27-107] + StmtKind: IfStmt [27-107]: Expr [30-40]: Paren: Expr [31-39]: BinOp (Eq): Expr [31-33]: Ident [31-33] "x1" Expr [37-39]: Ident [37-39] "y1" - Stmt [55-65] - StmtKind: ClassicalDeclarationStmt [55-65]: ClassicalType [55-58]: IntType [55-58], Ident [59-60] "a", ValueExpression ExprStmt [63-64]: Expr [63-64]: Lit: Int(0) + Stmt [55-61] + StmtKind: ExprStmt [55-61]: Expr [55-60]: Assign: + Expr [55-56]: Ident [55-56] "a" + Expr [59-60]: Lit: Int(0) Else: - Stmt [95-105] - StmtKind: ClassicalDeclarationStmt [95-105]: ClassicalType [95-98]: IntType [95-98], Ident [99-100] "a", ValueExpression ExprStmt [103-104]: Expr [103-104]: Lit: Int(1) + Stmt [91-97] + StmtKind: ExprStmt [91-97]: Expr [91-96]: Assign: + Expr [91-92]: Ident [91-92] "a" + Expr [95-96]: Lit: Int(1) Else: - Stmt [137-225] - StmtKind: IfStmt [137-225]: Expr [140-150]: Paren: - Expr [141-149]: BinOp (Eq): - Expr [141-143]: Ident [141-143] "x2" - Expr [147-149]: Ident [147-149] "y2" - Stmt [165-175] - StmtKind: ClassicalDeclarationStmt [165-175]: ClassicalType [165-168]: IntType [165-168], Ident [169-170] "a", ValueExpression ExprStmt [173-174]: Expr [173-174]: Lit: Int(2) + Stmt [129-209] + StmtKind: IfStmt [129-209]: Expr [132-142]: Paren: + Expr [133-141]: BinOp (Eq): + Expr [133-135]: Ident [133-135] "x2" + Expr [139-141]: Ident [139-141] "y2" + Stmt [157-163] + StmtKind: ExprStmt [157-163]: Expr [157-162]: Assign: + Expr [157-158]: Ident [157-158] "a" + Expr [161-162]: Lit: Int(2) Else: - Stmt [205-215] - StmtKind: ClassicalDeclarationStmt [205-215]: ClassicalType [205-208]: IntType [205-208], Ident [209-210] "a", ValueExpression ExprStmt [213-214]: Expr [213-214]: Lit: Int(3)"#]], + Stmt [193-199] + StmtKind: ExprStmt [193-199]: Expr [193-198]: Assign: + Expr [193-194]: Ident [193-194] "a" + Expr [197-198]: Lit: Int(3)"#]], ); } From fce7907833601f7463a8d74057be53744ab0c3b0 Mon Sep 17 00:00:00 2001 From: Oscar Puente <156957451+orpuente-MS@users.noreply.github.com> Date: Mon, 24 Feb 2025 15:57:42 -0800 Subject: [PATCH 022/108] gate calls --- compiler/qsc_qasm3/src/ast.rs | 99 ++++++------- compiler/qsc_qasm3/src/lex/cooked.rs | 58 ++++++-- compiler/qsc_qasm3/src/lex/cooked/tests.rs | 1 + compiler/qsc_qasm3/src/parser/expr.rs | 28 ++-- compiler/qsc_qasm3/src/parser/stmt.rs | 133 ++++++++++++++++-- compiler/qsc_qasm3/src/parser/stmt/tests.rs | 2 + .../src/parser/stmt/tests/classical_decl.rs | 82 +++++------ .../qsc_qasm3/src/parser/stmt/tests/def.rs | 16 +-- .../src/parser/stmt/tests/expr_stmt.rs | 42 ++++++ .../src/parser/stmt/tests/extern_decl.rs | 10 +- .../src/parser/stmt/tests/for_loops.rs | 3 + .../src/parser/stmt/tests/gate_call.rs | 102 ++++++++++++++ .../src/parser/stmt/tests/gate_def.rs | 2 +- .../src/parser/stmt/tests/if_stmt.rs | 3 + .../src/parser/stmt/tests/io_decl.rs | 24 ++-- .../src/parser/stmt/tests/old_style_decl.rs | 4 +- .../src/parser/stmt/tests/quantum_decl.rs | 2 +- .../src/parser/stmt/tests/switch_stmt.rs | 7 +- .../src/parser/stmt/tests/while_loops.rs | 3 + 19 files changed, 448 insertions(+), 173 deletions(-) create mode 100644 compiler/qsc_qasm3/src/parser/stmt/tests/expr_stmt.rs create mode 100644 compiler/qsc_qasm3/src/parser/stmt/tests/gate_call.rs diff --git a/compiler/qsc_qasm3/src/ast.rs b/compiler/qsc_qasm3/src/ast.rs index 5b5e8405fc..dcbf18d2f6 100644 --- a/compiler/qsc_qasm3/src/ast.rs +++ b/compiler/qsc_qasm3/src/ast.rs @@ -506,7 +506,7 @@ impl Display for IfStmt { #[derive(Clone, Debug)] pub struct DelayStmt { pub span: Span, - pub duration: ExprStmt, + pub duration: Expr, } impl Display for DelayStmt { @@ -688,12 +688,31 @@ pub struct RangeDefinition { #[derive(Clone, Debug)] pub struct QuantumGateModifier { pub span: Span, - pub qubit: Box, + pub kind: GateModifierKind, } impl Display for QuantumGateModifier { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { - write!(f, "QuantumGateModifier {}: {}", self.span, self.qubit) + write!(f, "QuantumGateModifier {}: {}", self.span, self.kind) + } +} + +#[derive(Clone, Debug)] +pub enum GateModifierKind { + Inv, + Pow(Expr), + Ctrl(Option), + NegCtrl(Option), +} + +impl Display for GateModifierKind { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + match self { + GateModifierKind::Inv => write!(f, "Inv"), + GateModifierKind::Pow(expr) => write!(f, "Pow {expr}"), + GateModifierKind::Ctrl(expr) => write!(f, "Ctrl {expr:?}"), + GateModifierKind::NegCtrl(expr) => write!(f, "NegCtrl {expr:?}"), + } } } @@ -738,7 +757,7 @@ impl Display for ClassicalArgument { #[derive(Clone, Debug)] pub enum ExternParameter { Scalar(ScalarType, Span), - Quantum(Option, Span), + Quantum(Option, Span), ArrayReference(ArrayReferenceType, Span), } @@ -845,7 +864,7 @@ impl Display for ArrayBaseTypeKind { #[derive(Clone, Debug)] pub struct IntType { - pub size: Option, + pub size: Option, pub span: Span, } @@ -861,7 +880,7 @@ impl Display for IntType { #[derive(Clone, Debug)] pub struct UIntType { - pub size: Option, + pub size: Option, pub span: Span, } @@ -877,7 +896,7 @@ impl Display for UIntType { #[derive(Clone, Debug)] pub struct FloatType { - pub size: Option, + pub size: Option, pub span: Span, } @@ -909,7 +928,7 @@ impl Display for ComplexType { #[derive(Clone, Debug)] pub struct AngleType { - pub size: Option, + pub size: Option, pub span: Span, } @@ -925,7 +944,7 @@ impl Display for AngleType { #[derive(Clone, Debug)] pub struct BitType { - pub size: Option, + pub size: Option, pub span: Span, } @@ -1015,7 +1034,7 @@ impl Display for AccessControl { #[derive(Clone, Debug)] pub struct QuantumArgument { pub span: Span, - pub expr: Option, + pub expr: Option, } impl Display for QuantumArgument { @@ -1074,7 +1093,7 @@ impl Display for IncludeStmt { pub struct QubitDeclaration { pub span: Span, pub qubit: Box, - pub size: Option, + pub size: Option, } impl Display for QubitDeclaration { @@ -1196,9 +1215,9 @@ pub struct GateCall { pub span: Span, pub modifiers: List, pub name: Identifier, - pub args: List, - pub qubits: List>, - pub duration: Option, + pub args: List, + pub qubits: List, + pub duration: Option, } impl Display for GateCall { @@ -1222,7 +1241,7 @@ impl Display for GateCall { pub struct QuantumPhase { pub span: Span, pub modifiers: List, - pub arg: ExprStmt, + pub arg: Expr, pub qubits: List>, } @@ -1240,7 +1259,7 @@ impl Display for QuantumPhase { #[derive(Clone, Debug)] pub struct DelayInstruction { span: Span, - duration: ExprStmt, + duration: Expr, qubits: List>, } @@ -1258,7 +1277,7 @@ impl Display for DelayInstruction { #[derive(Clone, Debug)] pub struct BoxStmt { pub span: Span, - pub duration: Option, + pub duration: Option, pub body: List, } @@ -1322,7 +1341,7 @@ impl Display for ClassicalDeclarationStmt { #[derive(Clone, Debug)] pub enum ValueExpression { - Expr(ExprStmt), + Expr(Expr), Measurement(MeasureExpr), } @@ -1358,7 +1377,7 @@ pub struct ConstantDeclaration { pub span: Span, pub r#type: TypeDef, pub identifier: Box, - pub init_expr: Box, + pub init_expr: Expr, } impl Display for ConstantDeclaration { @@ -1429,7 +1448,7 @@ impl Display for CalibrationDefinition { #[derive(Clone, Debug)] pub enum CalibrationArgument { Classical(ClassicalArgument), - Expr(ExprStmt), + Expr(Expr), } impl Display for CalibrationArgument { @@ -1444,7 +1463,7 @@ impl Display for CalibrationArgument { #[derive(Clone, Debug)] pub enum TypedParameter { Scalar(ScalarType, Box, Span), - Quantum(Option, Box, Span), + Quantum(Option, Box, Span), ArrayReference(ArrayReferenceType, Box, Span), } @@ -1702,7 +1721,6 @@ pub enum ExprKind { Lit(Lit), FunctionCall(FunctionCall), Cast(Cast), - Concatenation(Concatenation), IndexExpr(IndexExpr), Paren(Expr), } @@ -1718,7 +1736,6 @@ impl Display for ExprKind { ExprKind::Lit(lit) => write!(f, "{lit}"), ExprKind::FunctionCall(call) => write!(f, "{call}"), ExprKind::Cast(cast) => display_cast(indent, cast), - ExprKind::Concatenation(concat) => write!(f, "{concat}"), ExprKind::IndexExpr(index) => write!(f, "{index}"), ExprKind::Assign(expr) => write!(f, "{expr}"), ExprKind::AssignOp(expr) => write!(f, "{expr}"), @@ -1899,23 +1916,6 @@ impl fmt::Display for Version { } } -#[derive(Clone, Debug)] -pub struct Concatenation { - lhs: ExprStmt, - rhs: ExprStmt, -} - -impl Display for Concatenation { - fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { - let mut indent = set_indentation(indented(f), 0); - write!(indent, "Concatenation:")?; - indent = set_indentation(indent, 1); - write!(indent, "\n{}", self.lhs)?; - write!(indent, "\n{}", self.rhs)?; - Ok(()) - } -} - #[derive(Clone, Debug)] pub enum IndexElement { DiscreteSet(DiscreteSet), @@ -1984,25 +1984,6 @@ impl Display for AssignmentOp { } } -#[derive(Clone, Debug)] -pub enum GateModifierName { - Inv, - Pow, - Ctrl, - NegCtrl, -} - -impl Display for GateModifierName { - fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { - match self { - GateModifierName::Inv => write!(f, "inv"), - GateModifierName::Pow => write!(f, "pow"), - GateModifierName::Ctrl => write!(f, "ctrl"), - GateModifierName::NegCtrl => write!(f, "negctrl"), - } - } -} - #[derive(Clone, Debug)] pub enum IOKeyword { Input, diff --git a/compiler/qsc_qasm3/src/lex/cooked.rs b/compiler/qsc_qasm3/src/lex/cooked.rs index 8838cda2b7..e15544f0e5 100644 --- a/compiler/qsc_qasm3/src/lex/cooked.rs +++ b/compiler/qsc_qasm3/src/lex/cooked.rs @@ -125,6 +125,8 @@ pub enum TokenKind { PlusPlus, /// `->` Arrow, + /// `@` + At, // Operators, ClosedBinOp(ClosedBinOp), @@ -173,6 +175,7 @@ impl Display for TokenKind { TokenKind::Comma => write!(f, "`,`"), TokenKind::PlusPlus => write!(f, "`++`"), TokenKind::Arrow => write!(f, "`->`"), + TokenKind::At => write!(f, "`@`"), TokenKind::ClosedBinOp(op) => write!(f, "`{op}`"), TokenKind::BinOpEq(op) => write!(f, "`{op}=`"), TokenKind::ComparisonOp(op) => write!(f, "`{op}`"), @@ -404,6 +407,12 @@ pub(crate) struct Lexer<'a> { // This uses a `Peekable` iterator over the raw lexer, which allows for one token lookahead. tokens: Peekable>, + + /// This flag is used to detect annotations at the + /// beginning of a file. Normally annotations are + /// detected because there is a Newline followed by an `@`, + /// but there is no newline at the beginning of a file. + beginning_of_file: bool, } impl<'a> Lexer<'a> { @@ -415,6 +424,7 @@ impl<'a> Lexer<'a> { .try_into() .expect("input length should fit into u32"), tokens: raw::Lexer::new(input).peekable(), + beginning_of_file: true, } } @@ -503,8 +513,27 @@ impl<'a> Lexer<'a> { hi: token.offset, })) } - raw::TokenKind::Comment(_) | raw::TokenKind::Newline | raw::TokenKind::Whitespace => { - Ok(None) + raw::TokenKind::Comment(_) | raw::TokenKind::Whitespace => Ok(None), + raw::TokenKind::Newline => { + // AnnotationKeyword: '@' Identifier ('.' Identifier)* -> pushMode(EAT_TO_LINE_END); + self.next_if_eq(raw::TokenKind::Whitespace); + match self.tokens.peek() { + Some(token) if token.kind == raw::TokenKind::Single(Single::At) => { + let token = self.tokens.next().expect("self.tokens.peek() was Some(_)"); + let complete = TokenKind::Annotation; + self.expect(raw::TokenKind::Ident, complete); + self.eat_to_end_of_line(); + let kind = Some(complete); + return Ok(kind.map(|kind| { + let span = Span { + lo: token.offset, + hi: self.offset(), + }; + Token { kind, span } + })); + } + _ => Ok(None), + } } raw::TokenKind::Ident => { let ident = &self.input[(token.offset as usize)..(self.offset() as usize)]; @@ -620,11 +649,14 @@ impl<'a> Lexer<'a> { } } Single::At => { - // AnnotationKeyword: '@' Identifier ('.' Identifier)* -> pushMode(EAT_TO_LINE_END); - let complete = TokenKind::Annotation; - self.expect(raw::TokenKind::Ident, complete); - self.eat_to_end_of_line(); - Ok(complete) + if self.beginning_of_file { + let complete = TokenKind::Annotation; + self.expect(raw::TokenKind::Ident, complete); + self.eat_to_end_of_line(); + Ok(complete) + } else { + Ok(TokenKind::At) + } } Single::Bang => { if self.next_if_eq_single(Single::Eq) { @@ -739,9 +771,15 @@ impl Iterator for Lexer<'_> { fn next(&mut self) -> Option { while let Some(token) = self.tokens.next() { match self.cook(&token) { - Ok(None) => {} - Ok(Some(token)) => return Some(Ok(token)), - Err(err) => return Some(Err(err)), + Ok(None) => self.beginning_of_file = false, + Ok(Some(token)) => { + self.beginning_of_file = false; + return Some(Ok(token)); + } + Err(err) => { + self.beginning_of_file = false; + return Some(Err(err)); + } } } diff --git a/compiler/qsc_qasm3/src/lex/cooked/tests.rs b/compiler/qsc_qasm3/src/lex/cooked/tests.rs index b025519177..03d169d82f 100644 --- a/compiler/qsc_qasm3/src/lex/cooked/tests.rs +++ b/compiler/qsc_qasm3/src/lex/cooked/tests.rs @@ -40,6 +40,7 @@ fn op_string(kind: TokenKind) -> Option { TokenKind::Measure => Some("measure".to_string()), TokenKind::Semicolon => Some(";".to_string()), TokenKind::Arrow => Some("->".to_string()), + TokenKind::At => Some("@".to_string()), TokenKind::ClosedBinOp(op) => Some(op.to_string()), TokenKind::BinOpEq(super::ClosedBinOp::AmpAmp | super::ClosedBinOp::BarBar) | TokenKind::Literal(_) diff --git a/compiler/qsc_qasm3/src/parser/expr.rs b/compiler/qsc_qasm3/src/parser/expr.rs index f3772a7f36..c2f4d085fa 100644 --- a/compiler/qsc_qasm3/src/parser/expr.rs +++ b/compiler/qsc_qasm3/src/parser/expr.rs @@ -17,9 +17,9 @@ use qsc_data_structures::span::Span; use crate::{ ast::{ self, list_from_iter, AssignExpr, AssignOpExpr, BinOp, BinaryOpExpr, Cast, DiscreteSet, - Expr, ExprKind, ExprStmt, FunctionCall, GateOperand, HardwareQubit, Ident, IndexElement, - IndexExpr, IndexSetItem, IndexedIdent, List, Lit, LiteralKind, MeasureExpr, - RangeDefinition, TypeDef, UnaryOp, ValueExpression, Version, + Expr, ExprKind, FunctionCall, GateOperand, HardwareQubit, Ident, IndexElement, IndexExpr, + IndexSetItem, IndexedIdent, List, Lit, LiteralKind, MeasureExpr, RangeDefinition, TypeDef, + UnaryOp, ValueExpression, Version, }, keyword::Keyword, lex::{ @@ -673,15 +673,11 @@ fn unescape(s: &str) -> std::result::Result { Ok(buf) } -pub(super) fn designator(s: &mut ParserContext) -> Result { - let lo = s.peek().span.lo; +pub(super) fn designator(s: &mut ParserContext) -> Result { token(s, TokenKind::Open(Delim::Bracket))?; let expr = expr(s)?; - token(s, TokenKind::Close(Delim::Bracket))?; - Ok(ExprStmt { - span: s.span(lo), - expr, - }) + recovering_token(s, TokenKind::Close(Delim::Bracket)); + Ok(expr) } /// A literal array is a list of literal array elements. @@ -708,23 +704,17 @@ fn lit_array_element(s: &mut ParserContext) -> Result { } pub(super) fn value_expr(s: &mut ParserContext) -> Result> { - let lo = s.peek().span.lo; if let Some(measurement) = opt(s, measure_expr)? { return Ok(Box::new(ValueExpression::Measurement(measurement))); } - let expr = if let Some(expr) = opt(s, expr_stmt)? { + let expr = if let Some(expr) = opt(s, expr)? { expr } else { lit_array(s)? }; - let stmt = ExprStmt { - span: s.span(lo), - expr, - }; - - Ok(Box::new(ValueExpression::Expr(stmt))) + Ok(Box::new(ValueExpression::Expr(expr))) } pub(crate) fn expr_list(s: &mut ParserContext) -> Result> { @@ -741,7 +731,7 @@ fn measure_expr(s: &mut ParserContext) -> Result { }) } -fn gate_operand(s: &mut ParserContext) -> Result { +pub(crate) fn gate_operand(s: &mut ParserContext) -> Result { if let Some(indexed_ident) = opt(s, indexed_identifier)? { return Ok(GateOperand::IndexedIdent(Box::new(indexed_ident))); } diff --git a/compiler/qsc_qasm3/src/parser/stmt.rs b/compiler/qsc_qasm3/src/parser/stmt.rs index 6e7014c417..7bdaadd8f9 100644 --- a/compiler/qsc_qasm3/src/parser/stmt.rs +++ b/compiler/qsc_qasm3/src/parser/stmt.rs @@ -10,7 +10,7 @@ use std::rc::Rc; use super::{ completion::WordKinds, error::{Error, ErrorKind}, - expr::{self, designator}, + expr::{self, designator, gate_operand}, prim::{self, barrier, many, opt, recovering, recovering_semi, recovering_token, seq, shorten}, Result, }; @@ -19,10 +19,12 @@ use crate::{ list_from_iter, AccessControl, AliasDeclStmt, AngleType, Annotation, ArrayBaseTypeKind, ArrayReferenceType, ArrayType, BitType, Block, BreakStmt, ClassicalDeclarationStmt, ComplexType, ConstantDeclaration, ContinueStmt, DefStmt, EndStmt, EnumerableSet, Expr, - ExprStmt, ExternDecl, ExternParameter, FloatType, ForStmt, IODeclaration, IOKeyword, Ident, - Identifier, IfStmt, IncludeStmt, IntType, List, LiteralKind, Pragma, QuantumGateDefinition, - QubitDeclaration, RangeDefinition, ReturnStmt, ScalarType, ScalarTypeKind, Stmt, StmtKind, - SwitchStmt, TypeDef, TypedParameter, UIntType, WhileLoop, + ExprKind, ExprStmt, ExternDecl, ExternParameter, FloatType, ForStmt, FunctionCall, + GateCall, GateModifierKind, IODeclaration, IOKeyword, Ident, Identifier, IfStmt, + IncludeStmt, IntType, List, LiteralKind, Pragma, QuantumGateDefinition, + QuantumGateModifier, QuantumStmt, QubitDeclaration, RangeDefinition, ReturnStmt, + ScalarType, ScalarTypeKind, Stmt, StmtKind, SwitchStmt, TypeDef, TypedParameter, UIntType, + WhileLoop, }, keyword::Keyword, lex::{ @@ -77,6 +79,8 @@ pub(super) fn parse(s: &mut ParserContext) -> Result> { Box::new(StmtKind::Break(stmt)) } else if let Some(stmt) = opt(s, parse_end_stmt)? { Box::new(StmtKind::End(stmt)) + } else if let Some(stmt_kind) = opt(s, parse_gate_call_stmt)? { + Box::new(stmt_kind) } else if let Some(stmt) = opt(s, parse_expression_stmt)? { Box::new(StmtKind::ExprStmt(stmt)) } else if let Some(alias) = opt(s, parse_alias_stmt)? { @@ -452,7 +456,7 @@ fn parse_quantum_decl(s: &mut ParserContext) -> Result { })) } -fn qubit_type(s: &mut ParserContext<'_>) -> Result> { +fn qubit_type(s: &mut ParserContext<'_>) -> Result> { token(s, TokenKind::Keyword(crate::keyword::Keyword::Qubit))?; let size = opt(s, designator)?; Ok(size) @@ -521,10 +525,7 @@ fn parse_classical_decl(s: &mut ParserContext) -> Result { span: s.span(lo), r#type: ty, identifier, - init_expr: Box::new(ExprStmt { - span: init_expr.span, - expr: init_expr, - }), + init_expr, }; StmtKind::ConstDecl(decl) } else { @@ -657,20 +658,20 @@ fn qreg_decl(s: &mut ParserContext) -> Result { })) } -fn extern_creg_type(s: &mut ParserContext) -> Result> { +fn extern_creg_type(s: &mut ParserContext) -> Result> { token(s, TokenKind::Keyword(crate::keyword::Keyword::CReg))?; let size = opt(s, designator)?; Ok(size) } -fn creg_type(s: &mut ParserContext) -> Result<(Box, Option)> { +fn creg_type(s: &mut ParserContext) -> Result<(Box, Option)> { token(s, TokenKind::Keyword(crate::keyword::Keyword::CReg))?; let name = Box::new(prim::ident(s)?); let size = opt(s, designator)?; Ok((name, size)) } -fn qreg_type(s: &mut ParserContext) -> Result<(Box, Option)> { +fn qreg_type(s: &mut ParserContext) -> Result<(Box, Option)> { token(s, TokenKind::Keyword(crate::keyword::Keyword::QReg))?; let name = Box::new(prim::ident(s)?); let size = opt(s, designator)?; @@ -1073,3 +1074,109 @@ fn parse_alias_stmt(s: &mut ParserContext) -> Result { span: s.span(lo), }) } + +/// In QASM3, it is hard to disambiguate between a quantum-gate-call missing modifiers +/// and expression statements. Consider the following expressions: +/// 1. `Rx(2, 3) q0;` +/// 2. `Rx(2, 3) + q0;` +/// 3. `Rx(2, 3);` +/// 4. `Rx;` +/// +/// (1) is a quantum-gate-call, (2) is a binary operation, (3) is a function call, and +/// (4) is an identifer. We don't know for sure until we see the what is beyond the gate +/// name and its potential classical parameters. +/// +/// Therefore, we parse the gate name and its potential parameters using the expr parser. +/// If the expr is a function call or an identifier and it is followed by qubit arguments, +/// we reinterpret the expression as a quantum gate. +/// +/// Grammar: +/// `gateModifier* Identifier (LPAREN expressionList? RPAREN)? designator? gateOperandList SEMICOLON +/// | gateModifier* GPHASE (LPAREN expressionList? RPAREN)? designator? gateOperandList? SEMICOLON`. +fn parse_gate_call_stmt(s: &mut ParserContext) -> Result { + let lo = s.peek().span.lo; + let modifiers = list_from_iter(many(s, gate_modifier)?); + + // As explained in the docstring, we parse the gate using the expr parser. + let gate_or_expr = expr::expr(s)?; + + let duration = opt(s, designator)?; + let qubits = list_from_iter(many(s, gate_operand)?); + recovering_semi(s); + + // If didn't parse modifiers, a duration, nor qubit args then this is an expr, not a gate call. + if modifiers.is_empty() && duration.is_none() && qubits.is_empty() { + return Ok(StmtKind::ExprStmt(ExprStmt { + span: s.span(lo), + expr: gate_or_expr, + })); + } + + let (name, args) = match *gate_or_expr.kind { + ExprKind::FunctionCall(FunctionCall { name, args, .. }) => (name, args), + ExprKind::Ident(ident) => (Identifier::Ident(Box::new(ident)), Default::default()), + _ => { + return Err(Error::new(ErrorKind::ExpectedItem( + TokenKind::Identifier, + gate_or_expr.span, + ))) + } + }; + + let quantum_stmt = QuantumStmt { + span: s.span(lo), + kind: crate::ast::QuantumStmtKind::Gate(GateCall { + span: s.span(lo), + modifiers, + name, + args, + qubits, + duration, + }), + }; + + Ok(StmtKind::Quantum(quantum_stmt)) +} + +/// Grammar: +/// `( +/// INV +/// | POW LPAREN expression RPAREN +/// | (CTRL | NEGCTRL) (LPAREN expression RPAREN)? +/// ) AT`. +fn gate_modifier(s: &mut ParserContext) -> Result { + let lo = s.peek().span.lo; + + let kind = if opt(s, |s| token(s, TokenKind::Inv))?.is_some() { + GateModifierKind::Inv + } else if opt(s, |s| token(s, TokenKind::Pow))?.is_some() { + token(s, TokenKind::Open(Delim::Paren))?; + let expr = expr::expr(s)?; + recovering_token(s, TokenKind::Close(Delim::Paren)); + GateModifierKind::Pow(expr) + } else if opt(s, |s| token(s, TokenKind::Ctrl))?.is_some() { + let expr = opt(s, |s| { + token(s, TokenKind::Open(Delim::Paren))?; + let expr = expr::expr(s)?; + recovering_token(s, TokenKind::Close(Delim::Paren)); + Ok(expr) + })?; + GateModifierKind::Ctrl(expr) + } else { + token(s, TokenKind::NegCtrl)?; + let expr = opt(s, |s| { + token(s, TokenKind::Open(Delim::Paren))?; + let expr = expr::expr(s)?; + recovering_token(s, TokenKind::Close(Delim::Paren)); + Ok(expr) + })?; + GateModifierKind::NegCtrl(expr) + }; + + recovering_token(s, TokenKind::At); + + Ok(QuantumGateModifier { + span: s.span(lo), + kind, + }) +} diff --git a/compiler/qsc_qasm3/src/parser/stmt/tests.rs b/compiler/qsc_qasm3/src/parser/stmt/tests.rs index e729691c4f..14fcea83ad 100644 --- a/compiler/qsc_qasm3/src/parser/stmt/tests.rs +++ b/compiler/qsc_qasm3/src/parser/stmt/tests.rs @@ -5,8 +5,10 @@ mod alias; mod annotation; mod classical_decl; mod def; +mod expr_stmt; mod extern_decl; mod for_loops; +mod gate_call; mod gate_def; mod if_stmt; mod io_decl; diff --git a/compiler/qsc_qasm3/src/parser/stmt/tests/classical_decl.rs b/compiler/qsc_qasm3/src/parser/stmt/tests/classical_decl.rs index 6159810966..4642879f27 100644 --- a/compiler/qsc_qasm3/src/parser/stmt/tests/classical_decl.rs +++ b/compiler/qsc_qasm3/src/parser/stmt/tests/classical_decl.rs @@ -25,7 +25,7 @@ fn bit_decl_bit_lit() { "bit b = 1;", &expect![[r#" Stmt [0-10] - StmtKind: ClassicalDeclarationStmt [0-10]: ClassicalType [0-3]: BitType, Ident [4-5] "b", ValueExpression ExprStmt [8-9]: Expr [8-9]: Lit: Int(1)"#]], + StmtKind: ClassicalDeclarationStmt [0-10]: ClassicalType [0-3]: BitType, Ident [4-5] "b", ValueExpression Expr [8-9]: Lit: Int(1)"#]], ); } @@ -36,7 +36,7 @@ fn const_bit_decl_bit_lit() { "const bit b = 1;", &expect![[r#" Stmt [0-16] - StmtKind: ConstantDeclaration [0-16]: ClassicalType [6-9]: BitType, Ident [10-11] "b", ExprStmt [14-15]: Expr [14-15]: Lit: Int(1)"#]], + StmtKind: ConstantDeclaration [0-16]: ClassicalType [6-9]: BitType, Ident [10-11] "b", Expr [14-15]: Lit: Int(1)"#]], ); } @@ -47,7 +47,7 @@ fn bit_array_decl() { "bit[2] b;", &expect![[r#" Stmt [0-9] - StmtKind: ClassicalDeclarationStmt [0-9]: ClassicalType [0-6]: BitType [0-6]: ExprStmt [3-6]: Expr [4-5]: Lit: Int(2), Ident [7-8] "b""#]], + StmtKind: ClassicalDeclarationStmt [0-9]: ClassicalType [0-6]: BitType [0-6]: Expr [4-5]: Lit: Int(2), Ident [7-8] "b""#]], ); } @@ -58,7 +58,7 @@ fn bit_array_decl_bit_lit() { "bit[2] b = \"11\";", &expect![[r#" Stmt [0-16] - StmtKind: ClassicalDeclarationStmt [0-16]: ClassicalType [0-6]: BitType [0-6]: ExprStmt [3-6]: Expr [4-5]: Lit: Int(2), Ident [7-8] "b", ValueExpression ExprStmt [11-15]: Expr [11-15]: Lit: Bitstring("11")"#]], + StmtKind: ClassicalDeclarationStmt [0-16]: ClassicalType [0-6]: BitType [0-6]: Expr [4-5]: Lit: Int(2), Ident [7-8] "b", ValueExpression Expr [11-15]: Lit: Bitstring("11")"#]], ); } @@ -69,7 +69,7 @@ fn const_bit_array_decl_bit_lit() { "const bit[2] b = \"11\";", &expect![[r#" Stmt [0-22] - StmtKind: ConstantDeclaration [0-22]: ClassicalType [6-12]: BitType [6-12]: ExprStmt [9-12]: Expr [10-11]: Lit: Int(2), Ident [13-14] "b", ExprStmt [17-21]: Expr [17-21]: Lit: Bitstring("11")"#]], + StmtKind: ConstantDeclaration [0-22]: ClassicalType [6-12]: BitType [6-12]: Expr [10-11]: Lit: Int(2), Ident [13-14] "b", Expr [17-21]: Lit: Bitstring("11")"#]], ); } @@ -91,7 +91,7 @@ fn bool_decl_int_lit() { "bool b = 1;", &expect![[r#" Stmt [0-11] - StmtKind: ClassicalDeclarationStmt [0-11]: ClassicalType [0-4]: BoolType, Ident [5-6] "b", ValueExpression ExprStmt [9-10]: Expr [9-10]: Lit: Int(1)"#]], + StmtKind: ClassicalDeclarationStmt [0-11]: ClassicalType [0-4]: BoolType, Ident [5-6] "b", ValueExpression Expr [9-10]: Lit: Int(1)"#]], ); } @@ -102,7 +102,7 @@ fn const_bool_decl_bool_lit() { "const bool b = true;", &expect![[r#" Stmt [0-20] - StmtKind: ConstantDeclaration [0-20]: ClassicalType [6-10]: BoolType, Ident [11-12] "b", ExprStmt [15-19]: Expr [15-19]: Lit: Bool(true)"#]], + StmtKind: ConstantDeclaration [0-20]: ClassicalType [6-10]: BoolType, Ident [11-12] "b", Expr [15-19]: Lit: Bool(true)"#]], ); } @@ -124,7 +124,7 @@ fn complex_decl_complex_lit() { "complex c = 1im;", &expect![[r#" Stmt [0-16] - StmtKind: ClassicalDeclarationStmt [0-16]: ClassicalType [0-7]: ComplexType [0-7], Ident [8-9] "c", ValueExpression ExprStmt [12-15]: Expr [12-15]: Lit: Imaginary(1.0)"#]], + StmtKind: ClassicalDeclarationStmt [0-16]: ClassicalType [0-7]: ComplexType [0-7], Ident [8-9] "c", ValueExpression Expr [12-15]: Lit: Imaginary(1.0)"#]], ); } @@ -135,7 +135,7 @@ fn const_complex_decl_complex_lit() { "const complex c = 1im;", &expect![[r#" Stmt [0-22] - StmtKind: ConstantDeclaration [0-22]: ClassicalType [6-13]: ComplexType [6-13], Ident [14-15] "c", ExprStmt [18-21]: Expr [18-21]: Lit: Imaginary(1.0)"#]], + StmtKind: ConstantDeclaration [0-22]: ClassicalType [6-13]: ComplexType [6-13], Ident [14-15] "c", Expr [18-21]: Lit: Imaginary(1.0)"#]], ); } @@ -173,7 +173,7 @@ fn complex_sized_decl() { "complex[float[32]] c;", &expect![[r#" Stmt [0-21] - StmtKind: ClassicalDeclarationStmt [0-21]: ClassicalType [0-18]: ComplexType[float[FloatType[ExprStmt [13-17]: Expr [14-16]: Lit: Int(32)]: [8-17]]]: [0-18], Ident [19-20] "c""#]], + StmtKind: ClassicalDeclarationStmt [0-21]: ClassicalType [0-18]: ComplexType[float[FloatType[Expr [14-16]: Lit: Int(32)]: [8-17]]]: [0-18], Ident [19-20] "c""#]], ); } @@ -206,7 +206,7 @@ fn complex_sized_decl_complex_lit() { "complex[float[32]] c = 1im;", &expect![[r#" Stmt [0-27] - StmtKind: ClassicalDeclarationStmt [0-27]: ClassicalType [0-18]: ComplexType[float[FloatType[ExprStmt [13-17]: Expr [14-16]: Lit: Int(32)]: [8-17]]]: [0-18], Ident [19-20] "c", ValueExpression ExprStmt [23-26]: Expr [23-26]: Lit: Imaginary(1.0)"#]], + StmtKind: ClassicalDeclarationStmt [0-27]: ClassicalType [0-18]: ComplexType[float[FloatType[Expr [14-16]: Lit: Int(32)]: [8-17]]]: [0-18], Ident [19-20] "c", ValueExpression Expr [23-26]: Lit: Imaginary(1.0)"#]], ); } @@ -217,7 +217,7 @@ fn const_complex_sized_decl_complex_lit() { "const complex[float[32]] c = 1im;", &expect![[r#" Stmt [0-33] - StmtKind: ConstantDeclaration [0-33]: ClassicalType [6-24]: ComplexType[float[FloatType[ExprStmt [19-23]: Expr [20-22]: Lit: Int(32)]: [14-23]]]: [6-24], Ident [25-26] "c", ExprStmt [29-32]: Expr [29-32]: Lit: Imaginary(1.0)"#]], + StmtKind: ConstantDeclaration [0-33]: ClassicalType [6-24]: ComplexType[float[FloatType[Expr [20-22]: Lit: Int(32)]: [14-23]]]: [6-24], Ident [25-26] "c", Expr [29-32]: Lit: Imaginary(1.0)"#]], ); } @@ -239,7 +239,7 @@ fn int_decl_int_lit() { "int i = 1;", &expect![[r#" Stmt [0-10] - StmtKind: ClassicalDeclarationStmt [0-10]: ClassicalType [0-3]: IntType [0-3], Ident [4-5] "i", ValueExpression ExprStmt [8-9]: Expr [8-9]: Lit: Int(1)"#]], + StmtKind: ClassicalDeclarationStmt [0-10]: ClassicalType [0-3]: IntType [0-3], Ident [4-5] "i", ValueExpression Expr [8-9]: Lit: Int(1)"#]], ); } @@ -250,7 +250,7 @@ fn const_int_decl_int_lit() { "const int i = 1;", &expect![[r#" Stmt [0-16] - StmtKind: ConstantDeclaration [0-16]: ClassicalType [6-9]: IntType [6-9], Ident [10-11] "i", ExprStmt [14-15]: Expr [14-15]: Lit: Int(1)"#]], + StmtKind: ConstantDeclaration [0-16]: ClassicalType [6-9]: IntType [6-9], Ident [10-11] "i", Expr [14-15]: Lit: Int(1)"#]], ); } @@ -261,7 +261,7 @@ fn int_sized_decl() { "int[32] i;", &expect![[r#" Stmt [0-10] - StmtKind: ClassicalDeclarationStmt [0-10]: ClassicalType [0-7]: IntType[ExprStmt [3-7]: Expr [4-6]: Lit: Int(32)]: [0-7], Ident [8-9] "i""#]], + StmtKind: ClassicalDeclarationStmt [0-10]: ClassicalType [0-7]: IntType[Expr [4-6]: Lit: Int(32)]: [0-7], Ident [8-9] "i""#]], ); } @@ -272,7 +272,7 @@ fn int_sized_decl_int_lit() { "int[32] i = 1;", &expect![[r#" Stmt [0-14] - StmtKind: ClassicalDeclarationStmt [0-14]: ClassicalType [0-7]: IntType[ExprStmt [3-7]: Expr [4-6]: Lit: Int(32)]: [0-7], Ident [8-9] "i", ValueExpression ExprStmt [12-13]: Expr [12-13]: Lit: Int(1)"#]], + StmtKind: ClassicalDeclarationStmt [0-14]: ClassicalType [0-7]: IntType[Expr [4-6]: Lit: Int(32)]: [0-7], Ident [8-9] "i", ValueExpression Expr [12-13]: Lit: Int(1)"#]], ); } @@ -283,7 +283,7 @@ fn const_int_sized_decl_int_lit() { "const int[32] i = 1;", &expect![[r#" Stmt [0-20] - StmtKind: ConstantDeclaration [0-20]: ClassicalType [6-13]: IntType[ExprStmt [9-13]: Expr [10-12]: Lit: Int(32)]: [6-13], Ident [14-15] "i", ExprStmt [18-19]: Expr [18-19]: Lit: Int(1)"#]], + StmtKind: ConstantDeclaration [0-20]: ClassicalType [6-13]: IntType[Expr [10-12]: Lit: Int(32)]: [6-13], Ident [14-15] "i", Expr [18-19]: Lit: Int(1)"#]], ); } @@ -305,7 +305,7 @@ fn uint_decl_uint_lit() { "uint i = 1;", &expect![[r#" Stmt [0-11] - StmtKind: ClassicalDeclarationStmt [0-11]: ClassicalType [0-4]: UIntType [0-4], Ident [5-6] "i", ValueExpression ExprStmt [9-10]: Expr [9-10]: Lit: Int(1)"#]], + StmtKind: ClassicalDeclarationStmt [0-11]: ClassicalType [0-4]: UIntType [0-4], Ident [5-6] "i", ValueExpression Expr [9-10]: Lit: Int(1)"#]], ); } @@ -316,7 +316,7 @@ fn const_uint_decl_uint_lit() { "const uint i = 1;", &expect![[r#" Stmt [0-17] - StmtKind: ConstantDeclaration [0-17]: ClassicalType [6-10]: UIntType [6-10], Ident [11-12] "i", ExprStmt [15-16]: Expr [15-16]: Lit: Int(1)"#]], + StmtKind: ConstantDeclaration [0-17]: ClassicalType [6-10]: UIntType [6-10], Ident [11-12] "i", Expr [15-16]: Lit: Int(1)"#]], ); } @@ -327,7 +327,7 @@ fn uint_sized_decl() { "uint[32] i;", &expect![[r#" Stmt [0-11] - StmtKind: ClassicalDeclarationStmt [0-11]: ClassicalType [0-8]: UIntType[ExprStmt [4-8]: Expr [5-7]: Lit: Int(32)]: [0-8], Ident [9-10] "i""#]], + StmtKind: ClassicalDeclarationStmt [0-11]: ClassicalType [0-8]: UIntType[Expr [5-7]: Lit: Int(32)]: [0-8], Ident [9-10] "i""#]], ); } @@ -338,7 +338,7 @@ fn uint_sized_decl_uint_lit() { "uint[32] i = 1;", &expect![[r#" Stmt [0-15] - StmtKind: ClassicalDeclarationStmt [0-15]: ClassicalType [0-8]: UIntType[ExprStmt [4-8]: Expr [5-7]: Lit: Int(32)]: [0-8], Ident [9-10] "i", ValueExpression ExprStmt [13-14]: Expr [13-14]: Lit: Int(1)"#]], + StmtKind: ClassicalDeclarationStmt [0-15]: ClassicalType [0-8]: UIntType[Expr [5-7]: Lit: Int(32)]: [0-8], Ident [9-10] "i", ValueExpression Expr [13-14]: Lit: Int(1)"#]], ); } @@ -349,7 +349,7 @@ fn const_uint_sized_decl_uint_lit() { "const uint[32] i = 1;", &expect![[r#" Stmt [0-21] - StmtKind: ConstantDeclaration [0-21]: ClassicalType [6-14]: UIntType[ExprStmt [10-14]: Expr [11-13]: Lit: Int(32)]: [6-14], Ident [15-16] "i", ExprStmt [19-20]: Expr [19-20]: Lit: Int(1)"#]], + StmtKind: ConstantDeclaration [0-21]: ClassicalType [6-14]: UIntType[Expr [11-13]: Lit: Int(32)]: [6-14], Ident [15-16] "i", Expr [19-20]: Lit: Int(1)"#]], ); } @@ -371,7 +371,7 @@ fn float_decl_float_lit() { "float f = 1;", &expect![[r#" Stmt [0-12] - StmtKind: ClassicalDeclarationStmt [0-12]: ClassicalType [0-5]: FloatType [0-5], Ident [6-7] "f", ValueExpression ExprStmt [10-11]: Expr [10-11]: Lit: Int(1)"#]], + StmtKind: ClassicalDeclarationStmt [0-12]: ClassicalType [0-5]: FloatType [0-5], Ident [6-7] "f", ValueExpression Expr [10-11]: Lit: Int(1)"#]], ); } @@ -382,7 +382,7 @@ fn const_float_decl_float_lit() { "const float f = 1.0;", &expect![[r#" Stmt [0-20] - StmtKind: ConstantDeclaration [0-20]: ClassicalType [6-11]: FloatType [6-11], Ident [12-13] "f", ExprStmt [16-19]: Expr [16-19]: Lit: Float(1.0)"#]], + StmtKind: ConstantDeclaration [0-20]: ClassicalType [6-11]: FloatType [6-11], Ident [12-13] "f", Expr [16-19]: Lit: Float(1.0)"#]], ); } @@ -393,7 +393,7 @@ fn float_sized_decl() { "float[32] f;", &expect![[r#" Stmt [0-12] - StmtKind: ClassicalDeclarationStmt [0-12]: ClassicalType [0-9]: FloatType[ExprStmt [5-9]: Expr [6-8]: Lit: Int(32)]: [0-9], Ident [10-11] "f""#]], + StmtKind: ClassicalDeclarationStmt [0-12]: ClassicalType [0-9]: FloatType[Expr [6-8]: Lit: Int(32)]: [0-9], Ident [10-11] "f""#]], ); } @@ -404,7 +404,7 @@ fn float_sized_decl_float_lit() { "float[32] f = 1.0;", &expect![[r#" Stmt [0-18] - StmtKind: ClassicalDeclarationStmt [0-18]: ClassicalType [0-9]: FloatType[ExprStmt [5-9]: Expr [6-8]: Lit: Int(32)]: [0-9], Ident [10-11] "f", ValueExpression ExprStmt [14-17]: Expr [14-17]: Lit: Float(1.0)"#]], + StmtKind: ClassicalDeclarationStmt [0-18]: ClassicalType [0-9]: FloatType[Expr [6-8]: Lit: Int(32)]: [0-9], Ident [10-11] "f", ValueExpression Expr [14-17]: Lit: Float(1.0)"#]], ); } @@ -415,7 +415,7 @@ fn const_float_sized_decl_float_lit() { "const float[32] f = 1;", &expect![[r#" Stmt [0-22] - StmtKind: ConstantDeclaration [0-22]: ClassicalType [6-15]: FloatType[ExprStmt [11-15]: Expr [12-14]: Lit: Int(32)]: [6-15], Ident [16-17] "f", ExprStmt [20-21]: Expr [20-21]: Lit: Int(1)"#]], + StmtKind: ConstantDeclaration [0-22]: ClassicalType [6-15]: FloatType[Expr [12-14]: Lit: Int(32)]: [6-15], Ident [16-17] "f", Expr [20-21]: Lit: Int(1)"#]], ); } @@ -437,7 +437,7 @@ fn angle_decl_angle_lit() { "angle a = 1.0;", &expect![[r#" Stmt [0-14] - StmtKind: ClassicalDeclarationStmt [0-14]: ClassicalType [0-5]: AngleType [0-5], Ident [6-7] "a", ValueExpression ExprStmt [10-13]: Expr [10-13]: Lit: Float(1.0)"#]], + StmtKind: ClassicalDeclarationStmt [0-14]: ClassicalType [0-5]: AngleType [0-5], Ident [6-7] "a", ValueExpression Expr [10-13]: Lit: Float(1.0)"#]], ); } @@ -468,7 +468,7 @@ fn const_angle_decl_angle_lit() { "const angle a = 1.0;", &expect![[r#" Stmt [0-20] - StmtKind: ConstantDeclaration [0-20]: ClassicalType [6-11]: AngleType [6-11], Ident [12-13] "a", ExprStmt [16-19]: Expr [16-19]: Lit: Float(1.0)"#]], + StmtKind: ConstantDeclaration [0-20]: ClassicalType [6-11]: AngleType [6-11], Ident [12-13] "a", Expr [16-19]: Lit: Float(1.0)"#]], ); } @@ -479,7 +479,7 @@ fn angle_sized_decl() { "angle[32] a;", &expect![[r#" Stmt [0-12] - StmtKind: ClassicalDeclarationStmt [0-12]: ClassicalType [0-9]: AngleType [0-9]: ExprStmt [5-9]: Expr [6-8]: Lit: Int(32), Ident [10-11] "a""#]], + StmtKind: ClassicalDeclarationStmt [0-12]: ClassicalType [0-9]: AngleType [0-9]: Expr [6-8]: Lit: Int(32), Ident [10-11] "a""#]], ); } @@ -490,7 +490,7 @@ fn angle_sized_decl_angle_lit() { "angle[32] a = 1.0;", &expect![[r#" Stmt [0-18] - StmtKind: ClassicalDeclarationStmt [0-18]: ClassicalType [0-9]: AngleType [0-9]: ExprStmt [5-9]: Expr [6-8]: Lit: Int(32), Ident [10-11] "a", ValueExpression ExprStmt [14-17]: Expr [14-17]: Lit: Float(1.0)"#]], + StmtKind: ClassicalDeclarationStmt [0-18]: ClassicalType [0-9]: AngleType [0-9]: Expr [6-8]: Lit: Int(32), Ident [10-11] "a", ValueExpression Expr [14-17]: Lit: Float(1.0)"#]], ); } @@ -501,7 +501,7 @@ fn const_angle_sized_decl_angle_lit() { "const angle[32] a = 1.0;", &expect![[r#" Stmt [0-24] - StmtKind: ConstantDeclaration [0-24]: ClassicalType [6-15]: AngleType [6-15]: ExprStmt [11-15]: Expr [12-14]: Lit: Int(32), Ident [16-17] "a", ExprStmt [20-23]: Expr [20-23]: Lit: Float(1.0)"#]], + StmtKind: ConstantDeclaration [0-24]: ClassicalType [6-15]: AngleType [6-15]: Expr [12-14]: Lit: Int(32), Ident [16-17] "a", Expr [20-23]: Lit: Float(1.0)"#]], ); } @@ -677,7 +677,7 @@ fn empty_array_decl() { &expect![[r#" Stmt [0-23] StmtKind: ClassicalDeclarationStmt [0-23]: ArrayType [0-13]: ArrayBaseTypeKind IntType [6-9] - Expr [11-12]: Lit: Int(0), Ident [14-17] "arr", ValueExpression ExprStmt [20-22]: Expr [20-22]: Lit: Array:"#]], + Expr [11-12]: Lit: Int(0), Ident [14-17] "arr", ValueExpression Expr [20-22]: Lit: Array:"#]], ); } @@ -687,12 +687,12 @@ fn simple_array_decl() { parse, "array[int[32], 3] arr = {1, 2, 3};", &expect![[r#" - Stmt [0-34] - StmtKind: ClassicalDeclarationStmt [0-34]: ArrayType [0-17]: ArrayBaseTypeKind IntType[ExprStmt [9-13]: Expr [10-12]: Lit: Int(32)]: [6-13] - Expr [15-16]: Lit: Int(3), Ident [18-21] "arr", ValueExpression ExprStmt [24-33]: Expr [24-33]: Lit: Array: - Expr { span: Span { lo: 25, hi: 26 }, kind: Lit(Lit { span: Span { lo: 25, hi: 26 }, kind: Int(1) }) } - Expr { span: Span { lo: 28, hi: 29 }, kind: Lit(Lit { span: Span { lo: 28, hi: 29 }, kind: Int(2) }) } - Expr { span: Span { lo: 31, hi: 32 }, kind: Lit(Lit { span: Span { lo: 31, hi: 32 }, kind: Int(3) }) }"#]], + Stmt [0-34] + StmtKind: ClassicalDeclarationStmt [0-34]: ArrayType [0-17]: ArrayBaseTypeKind IntType[Expr [10-12]: Lit: Int(32)]: [6-13] + Expr [15-16]: Lit: Int(3), Ident [18-21] "arr", ValueExpression Expr [24-33]: Lit: Array: + Expr { span: Span { lo: 25, hi: 26 }, kind: Lit(Lit { span: Span { lo: 25, hi: 26 }, kind: Int(1) }) } + Expr { span: Span { lo: 28, hi: 29 }, kind: Lit(Lit { span: Span { lo: 28, hi: 29 }, kind: Int(2) }) } + Expr { span: Span { lo: 31, hi: 32 }, kind: Lit(Lit { span: Span { lo: 31, hi: 32 }, kind: Int(3) }) }"#]], ); } @@ -703,9 +703,9 @@ fn nested_array_decl() { "array[int[32], 3, 2] arr = {{1, 2}, {3, 4}, {5, 6}};", &expect![[r#" Stmt [0-52] - StmtKind: ClassicalDeclarationStmt [0-52]: ArrayType [0-20]: ArrayBaseTypeKind IntType[ExprStmt [9-13]: Expr [10-12]: Lit: Int(32)]: [6-13] + StmtKind: ClassicalDeclarationStmt [0-52]: ArrayType [0-20]: ArrayBaseTypeKind IntType[Expr [10-12]: Lit: Int(32)]: [6-13] Expr [15-16]: Lit: Int(3) - Expr [18-19]: Lit: Int(2), Ident [21-24] "arr", ValueExpression ExprStmt [27-51]: Expr [27-51]: Lit: Array: + Expr [18-19]: Lit: Int(2), Ident [21-24] "arr", ValueExpression Expr [27-51]: Lit: Array: Expr { span: Span { lo: 28, hi: 34 }, kind: Lit(Lit { span: Span { lo: 28, hi: 34 }, kind: Array([Expr { span: Span { lo: 29, hi: 30 }, kind: Lit(Lit { span: Span { lo: 29, hi: 30 }, kind: Int(1) }) }, Expr { span: Span { lo: 32, hi: 33 }, kind: Lit(Lit { span: Span { lo: 32, hi: 33 }, kind: Int(2) }) }]) }) } Expr { span: Span { lo: 36, hi: 42 }, kind: Lit(Lit { span: Span { lo: 36, hi: 42 }, kind: Array([Expr { span: Span { lo: 37, hi: 38 }, kind: Lit(Lit { span: Span { lo: 37, hi: 38 }, kind: Int(3) }) }, Expr { span: Span { lo: 40, hi: 41 }, kind: Lit(Lit { span: Span { lo: 40, hi: 41 }, kind: Int(4) }) }]) }) } Expr { span: Span { lo: 44, hi: 50 }, kind: Lit(Lit { span: Span { lo: 44, hi: 50 }, kind: Array([Expr { span: Span { lo: 45, hi: 46 }, kind: Lit(Lit { span: Span { lo: 45, hi: 46 }, kind: Int(5) }) }, Expr { span: Span { lo: 48, hi: 49 }, kind: Lit(Lit { span: Span { lo: 48, hi: 49 }, kind: Int(6) }) }]) }) }"#]], diff --git a/compiler/qsc_qasm3/src/parser/stmt/tests/def.rs b/compiler/qsc_qasm3/src/parser/stmt/tests/def.rs index a67bd8b88d..d9f82d4b77 100644 --- a/compiler/qsc_qasm3/src/parser/stmt/tests/def.rs +++ b/compiler/qsc_qasm3/src/parser/stmt/tests/def.rs @@ -91,9 +91,9 @@ fn classical_subroutine() { "def square(int[32] x) -> int { return x ** 2; }", &expect![[r#" Stmt [0-47] - StmtKind: DefStmt [0-47]: Ident [4-10] "square"([11-20] Ident [19-20] "x": ClassicalType [11-18]: IntType[ExprStmt [14-18]: Expr [15-17]: Lit: Int(32)]: [11-18]) + StmtKind: DefStmt [0-47]: Ident [4-10] "square"([11-20] Ident [19-20] "x": ClassicalType [11-18]: IntType[Expr [15-17]: Lit: Int(32)]: [11-18]) Stmt [31-45] - StmtKind: ReturnStmt [31-45]: ValueExpression ExprStmt [38-44]: Expr [38-44]: BinOp (Exp): + StmtKind: ReturnStmt [31-45]: ValueExpression Expr [38-44]: BinOp (Exp): Expr [38-39]: Ident [38-39] "x" Expr [43-44]: Lit: Int(2) ClassicalType [25-28]: IntType [25-28]"#]], @@ -107,7 +107,7 @@ fn quantum_args() { "def x(qubit q, qubit[n] qubits) { }", &expect![[r#" Stmt [0-35] - StmtKind: DefStmt [0-35]: Ident [4-5] "x"([6-13] Ident [12-13] "q": qubit, [15-30] Ident [24-30] "qubits": qubit[ExprStmt [20-23]: Expr [21-22]: Ident [21-22] "n"]) "#]], + StmtKind: DefStmt [0-35]: Ident [4-5] "x"([6-13] Ident [12-13] "q": qubit, [15-30] Ident [24-30] "qubits": qubit[Expr [21-22]: Ident [21-22] "n"]) "#]], ); } @@ -118,9 +118,9 @@ fn old_style_args() { "def test(creg c, qreg q, creg c2[2], qreg q4[4]) -> int { return x ** 2; }", &expect![[r#" Stmt [0-74] - StmtKind: DefStmt [0-74]: Ident [4-8] "test"([9-15] Ident [14-15] "c": ClassicalType [9-15]: BitType, [17-23] Ident [22-23] "q": qubit, [25-35] Ident [30-32] "c2": ClassicalType [25-35]: BitType [25-35]: ExprStmt [32-35]: Expr [33-34]: Lit: Int(2), [37-47] Ident [42-44] "q4": qubit[ExprStmt [44-47]: Expr [45-46]: Lit: Int(4)]) + StmtKind: DefStmt [0-74]: Ident [4-8] "test"([9-15] Ident [14-15] "c": ClassicalType [9-15]: BitType, [17-23] Ident [22-23] "q": qubit, [25-35] Ident [30-32] "c2": ClassicalType [25-35]: BitType [25-35]: Expr [33-34]: Lit: Int(2), [37-47] Ident [42-44] "q4": qubit[Expr [45-46]: Lit: Int(4)]) Stmt [58-72] - StmtKind: ReturnStmt [58-72]: ValueExpression ExprStmt [65-71]: Expr [65-71]: BinOp (Exp): + StmtKind: ReturnStmt [58-72]: ValueExpression Expr [65-71]: BinOp (Exp): Expr [65-66]: Ident [65-66] "x" Expr [70-71]: Lit: Int(2) ClassicalType [52-55]: IntType [52-55]"#]], @@ -134,7 +134,7 @@ fn readonly_array_arg_with_int_dims() { "def specified_sub(readonly array[int[8], 2, 10] arr_arg) {}", &expect![[r#" Stmt [0-59] - StmtKind: DefStmt [0-59]: Ident [4-17] "specified_sub"([18-55] Ident [48-55] "arr_arg": ArrayReferenceType [18-47]: ArrayBaseTypeKind IntType[ExprStmt [36-39]: Expr [37-38]: Lit: Int(8)]: [33-39] + StmtKind: DefStmt [0-59]: Ident [4-17] "specified_sub"([18-55] Ident [48-55] "arr_arg": ArrayReferenceType [18-47]: ArrayBaseTypeKind IntType[Expr [37-38]: Lit: Int(8)]: [33-39] Expr [41-42]: Lit: Int(2) Expr [44-46]: Lit: Int(10)) "#]], ); @@ -147,7 +147,7 @@ fn readonly_array_arg_with_dim() { "def arr_subroutine(readonly array[int[8], #dim = 1] arr_arg) {}", &expect![[r#" Stmt [0-63] - StmtKind: DefStmt [0-63]: Ident [4-18] "arr_subroutine"([19-59] Ident [52-59] "arr_arg": ArrayReferenceType [19-51]: ArrayBaseTypeKind IntType[ExprStmt [37-40]: Expr [38-39]: Lit: Int(8)]: [34-40] + StmtKind: DefStmt [0-63]: Ident [4-18] "arr_subroutine"([19-59] Ident [52-59] "arr_arg": ArrayReferenceType [19-51]: ArrayBaseTypeKind IntType[Expr [38-39]: Lit: Int(8)]: [34-40] Expr [49-50]: Lit: Int(1)) "#]], ); } @@ -159,7 +159,7 @@ fn mutable_array_arg() { "def mut_subroutine(mutable array[int[8], #dim = 1] arr_arg) {}", &expect![[r#" Stmt [0-62] - StmtKind: DefStmt [0-62]: Ident [4-18] "mut_subroutine"([19-58] Ident [51-58] "arr_arg": ArrayReferenceType [19-50]: ArrayBaseTypeKind IntType[ExprStmt [36-39]: Expr [37-38]: Lit: Int(8)]: [33-39] + StmtKind: DefStmt [0-62]: Ident [4-18] "mut_subroutine"([19-58] Ident [51-58] "arr_arg": ArrayReferenceType [19-50]: ArrayBaseTypeKind IntType[Expr [37-38]: Lit: Int(8)]: [33-39] Expr [48-49]: Lit: Int(1)) "#]], ); } diff --git a/compiler/qsc_qasm3/src/parser/stmt/tests/expr_stmt.rs b/compiler/qsc_qasm3/src/parser/stmt/tests/expr_stmt.rs new file mode 100644 index 0000000000..964a6f3e76 --- /dev/null +++ b/compiler/qsc_qasm3/src/parser/stmt/tests/expr_stmt.rs @@ -0,0 +1,42 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +use crate::parser::stmt::parse; +use crate::parser::tests::check; +use expect_test::expect; + +#[test] +fn identifier() { + check( + parse, + "H;", + &expect![[r#" + Stmt [0-2] + StmtKind: ExprStmt [0-2]: Expr [0-1]: Ident [0-1] "H""#]], + ); +} + +#[test] +fn identifier_plus_number() { + check( + parse, + "H + 2;", + &expect![[r#" + Stmt [0-6] + StmtKind: ExprStmt [0-6]: Expr [0-5]: BinOp (Add): + Expr [0-1]: Ident [0-1] "H" + Expr [4-5]: Lit: Int(2)"#]], + ); +} + +#[test] +fn function_call() { + check( + parse, + "f(2);", + &expect![[r#" + Stmt [0-5] + StmtKind: ExprStmt [0-5]: Expr [0-4]: FunctionCall [0-4]: Ident [0-1] "f" + Expr [2-3]: Lit: Int(2)"#]], + ); +} diff --git a/compiler/qsc_qasm3/src/parser/stmt/tests/extern_decl.rs b/compiler/qsc_qasm3/src/parser/stmt/tests/extern_decl.rs index 8f5eddc341..2771da1428 100644 --- a/compiler/qsc_qasm3/src/parser/stmt/tests/extern_decl.rs +++ b/compiler/qsc_qasm3/src/parser/stmt/tests/extern_decl.rs @@ -52,7 +52,7 @@ fn sized_bit_param_bit_ret_decl() { &expect![[r#" Stmt [0-24] StmtKind: ExternDecl [0-24]: Ident [7-8] "x" - [9-15]: ClassicalType [9-15]: BitType [9-15]: ExprStmt [12-15]: Expr [13-14]: Ident [13-14] "n" + [9-15]: ClassicalType [9-15]: BitType [9-15]: Expr [13-14]: Ident [13-14] "n" ClassicalType [20-23]: BitType"#]], ); } @@ -65,7 +65,7 @@ fn sized_creg_param_bit_ret_decl() { &expect![[r#" Stmt [0-25] StmtKind: ExternDecl [0-25]: Ident [7-8] "x" - [9-16]: ClassicalType [9-16]: BitType [9-16]: ExprStmt [13-16]: Expr [14-15]: Ident [14-15] "n" + [9-16]: ClassicalType [9-16]: BitType [9-16]: Expr [14-15]: Ident [14-15] "n" ClassicalType [21-24]: BitType"#]], ); } @@ -91,7 +91,7 @@ fn readonly_array_arg_with_int_dims() { &expect![[r#" Stmt [0-40] StmtKind: ExternDecl [0-40]: Ident [7-8] "x" - [9-38]: ArrayReferenceType [9-38]: ArrayBaseTypeKind IntType[ExprStmt [27-30]: Expr [28-29]: Lit: Int(8)]: [24-30] + [9-38]: ArrayReferenceType [9-38]: ArrayBaseTypeKind IntType[Expr [28-29]: Lit: Int(8)]: [24-30] Expr [32-33]: Lit: Int(2) Expr [35-37]: Lit: Int(10)"#]], ); @@ -105,7 +105,7 @@ fn readonly_array_arg_with_dim() { &expect![[r#" Stmt [0-43] StmtKind: ExternDecl [0-43]: Ident [7-8] "x" - [9-41]: ArrayReferenceType [9-41]: ArrayBaseTypeKind IntType[ExprStmt [27-30]: Expr [28-29]: Lit: Int(8)]: [24-30] + [9-41]: ArrayReferenceType [9-41]: ArrayBaseTypeKind IntType[Expr [28-29]: Lit: Int(8)]: [24-30] Expr [39-40]: Lit: Int(1)"#]], ); } @@ -118,7 +118,7 @@ fn mutable_array_arg() { &expect![[r#" Stmt [0-42] StmtKind: ExternDecl [0-42]: Ident [7-8] "x" - [9-40]: ArrayReferenceType [9-40]: ArrayBaseTypeKind IntType[ExprStmt [26-29]: Expr [27-28]: Lit: Int(8)]: [23-29] + [9-40]: ArrayReferenceType [9-40]: ArrayBaseTypeKind IntType[Expr [27-28]: Lit: Int(8)]: [23-29] Expr [38-39]: Lit: Int(1)"#]], ); } diff --git a/compiler/qsc_qasm3/src/parser/stmt/tests/for_loops.rs b/compiler/qsc_qasm3/src/parser/stmt/tests/for_loops.rs index 1d6aa1f1e9..8ce396ef6b 100644 --- a/compiler/qsc_qasm3/src/parser/stmt/tests/for_loops.rs +++ b/compiler/qsc_qasm3/src/parser/stmt/tests/for_loops.rs @@ -1,3 +1,6 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + use crate::parser::{stmt::parse, tests::check}; use expect_test::expect; diff --git a/compiler/qsc_qasm3/src/parser/stmt/tests/gate_call.rs b/compiler/qsc_qasm3/src/parser/stmt/tests/gate_call.rs new file mode 100644 index 0000000000..ef2ff116a7 --- /dev/null +++ b/compiler/qsc_qasm3/src/parser/stmt/tests/gate_call.rs @@ -0,0 +1,102 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +use crate::parser::stmt::parse; +use crate::parser::tests::check; +use expect_test::expect; + +#[test] +fn gate_call() { + check( + parse, + "H q0;", + &expect![[r#" + Stmt [0-5] + StmtKind: QuantumStmt [0-5]: GateCall [0-5]: Ident [0-1] "H" + GateOperand IndexedIdent [2-4]: Ident [2-4] "q0"[]"#]], + ); +} + +#[test] +fn gate_call_qubit_register() { + check( + parse, + "H q[2];", + &expect![[r#" + Stmt [0-7] + StmtKind: QuantumStmt [0-7]: GateCall [0-7]: Ident [0-1] "H" + GateOperand IndexedIdent [2-6]: Ident [2-3] "q"[ + IndexElement: + IndexSetItem Expr [4-5]: Lit: Int(2)]"#]], + ); +} + +#[test] +fn gate_multiple_qubits() { + check( + parse, + "CNOT q0 q[4];", + &expect![[r#" + Stmt [0-13] + StmtKind: QuantumStmt [0-13]: GateCall [0-13]: Ident [0-4] "CNOT" + GateOperand IndexedIdent [5-7]: Ident [5-7] "q0"[] + GateOperand IndexedIdent [8-12]: Ident [8-9] "q"[ + IndexElement: + IndexSetItem Expr [10-11]: Lit: Int(4)]"#]], + ); +} + +#[test] +fn gate_no_qubits() { + check( + parse, + "inv @ H;", + &expect![[r#" + Stmt [0-8] + StmtKind: QuantumStmt [0-8]: GateCall [0-8]: Ident [6-7] "H""#]], + ); +} + +#[test] +fn gate_call_with_parameters() { + check( + parse, + "Rx(pi / 2) q0;", + &expect![[r#" + Stmt [0-14] + StmtKind: QuantumStmt [0-14]: GateCall [0-14]: Ident [0-2] "Rx" + Expr [3-9]: BinOp (Div): + Expr [3-5]: Ident [3-5] "pi" + Expr [8-9]: Lit: Int(2) + GateOperand IndexedIdent [11-13]: Ident [11-13] "q0"[]"#]], + ); +} + +#[test] +fn gate_call_inv_modifier() { + check( + parse, + "inv @ H q0;", + &expect![[r#" + Stmt [0-11] + StmtKind: QuantumStmt [0-11]: GateCall [0-11]: Ident [6-7] "H" + GateOperand IndexedIdent [8-10]: Ident [8-10] "q0"[]"#]], + ); +} + +#[test] +fn gate_call_ctrl_inv_modifiers() { + check( + parse, + "ctrl(2) @ inv @ Rx(pi / 2) c1 c2 q0;", + &expect![[r#" + Stmt [0-36] + StmtKind: QuantumStmt [0-36]: GateCall [0-36]: Ident [16-18] "Rx" + Expr [19-25]: BinOp (Div): + Expr [19-21]: Ident [19-21] "pi" + Expr [24-25]: Lit: Int(2) + GateOperand IndexedIdent [27-29]: Ident [27-29] "c1"[] + GateOperand IndexedIdent [30-32]: Ident [30-32] "c2"[] + GateOperand IndexedIdent [33-35]: Ident [33-35] "q0"[]"#]], + ); +} diff --git a/compiler/qsc_qasm3/src/parser/stmt/tests/gate_def.rs b/compiler/qsc_qasm3/src/parser/stmt/tests/gate_def.rs index 24b8fb4ce2..a45c07effa 100644 --- a/compiler/qsc_qasm3/src/parser/stmt/tests/gate_def.rs +++ b/compiler/qsc_qasm3/src/parser/stmt/tests/gate_def.rs @@ -137,7 +137,7 @@ fn two_qubits_two_classical_with_body() { StmtKind: Gate [0-45]: Ident [5-9] "c2q2"(Ident [10-11] "a", Ident [13-14] "b") Ident [16-17] "c", Ident [19-20] "d" Stmt [23-43] - StmtKind: ClassicalDeclarationStmt [23-43]: ClassicalType [23-32]: FloatType[ExprStmt [28-32]: Expr [29-31]: Lit: Int(32)]: [23-32], Ident [33-34] "x", ValueExpression ExprStmt [37-42]: Expr [37-42]: BinOp (Sub): + StmtKind: ClassicalDeclarationStmt [23-43]: ClassicalType [23-32]: FloatType[Expr [29-31]: Lit: Int(32)]: [23-32], Ident [33-34] "x", ValueExpression Expr [37-42]: BinOp (Sub): Expr [37-38]: Ident [37-38] "a" Expr [41-42]: Ident [41-42] "b""#]], ); diff --git a/compiler/qsc_qasm3/src/parser/stmt/tests/if_stmt.rs b/compiler/qsc_qasm3/src/parser/stmt/tests/if_stmt.rs index fe1ae6dfdc..c4239cade0 100644 --- a/compiler/qsc_qasm3/src/parser/stmt/tests/if_stmt.rs +++ b/compiler/qsc_qasm3/src/parser/stmt/tests/if_stmt.rs @@ -1,3 +1,6 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + use crate::parser::{stmt::parse, tests::check}; use expect_test::expect; diff --git a/compiler/qsc_qasm3/src/parser/stmt/tests/io_decl.rs b/compiler/qsc_qasm3/src/parser/stmt/tests/io_decl.rs index b291ee1c6e..cfcae5cb3e 100644 --- a/compiler/qsc_qasm3/src/parser/stmt/tests/io_decl.rs +++ b/compiler/qsc_qasm3/src/parser/stmt/tests/io_decl.rs @@ -36,7 +36,7 @@ fn input_bit_array_decl() { "input bit[2] b;", &expect![[r#" Stmt [0-15] - StmtKind: IODeclaration [0-15]: input, ClassicalType [6-12]: BitType [6-12]: ExprStmt [9-12]: Expr [10-11]: Lit: Int(2), Ident [13-14] "b""#]], + StmtKind: IODeclaration [0-15]: input, ClassicalType [6-12]: BitType [6-12]: Expr [10-11]: Lit: Int(2), Ident [13-14] "b""#]], ); } @@ -47,7 +47,7 @@ fn output_bit_array_decl() { "output bit[2] b;", &expect![[r#" Stmt [0-16] - StmtKind: IODeclaration [0-16]: output, ClassicalType [7-13]: BitType [7-13]: ExprStmt [10-13]: Expr [11-12]: Lit: Int(2), Ident [14-15] "b""#]], + StmtKind: IODeclaration [0-16]: output, ClassicalType [7-13]: BitType [7-13]: Expr [11-12]: Lit: Int(2), Ident [14-15] "b""#]], ); } @@ -102,7 +102,7 @@ fn input_complex_sized_decl() { "input complex[float[32]] c;", &expect![[r#" Stmt [0-27] - StmtKind: IODeclaration [0-27]: input, ClassicalType [6-24]: ComplexType[float[FloatType[ExprStmt [19-23]: Expr [20-22]: Lit: Int(32)]: [14-23]]]: [6-24], Ident [25-26] "c""#]], + StmtKind: IODeclaration [0-27]: input, ClassicalType [6-24]: ComplexType[float[FloatType[Expr [20-22]: Lit: Int(32)]: [14-23]]]: [6-24], Ident [25-26] "c""#]], ); } @@ -113,7 +113,7 @@ fn output_complex_sized_decl() { "output complex[float[32]] c;", &expect![[r#" Stmt [0-28] - StmtKind: IODeclaration [0-28]: output, ClassicalType [7-25]: ComplexType[float[FloatType[ExprStmt [20-24]: Expr [21-23]: Lit: Int(32)]: [15-24]]]: [7-25], Ident [26-27] "c""#]], + StmtKind: IODeclaration [0-28]: output, ClassicalType [7-25]: ComplexType[float[FloatType[Expr [21-23]: Lit: Int(32)]: [15-24]]]: [7-25], Ident [26-27] "c""#]], ); } @@ -146,7 +146,7 @@ fn input_int_sized_decl() { "input int[32] i;", &expect![[r#" Stmt [0-16] - StmtKind: IODeclaration [0-16]: input, ClassicalType [6-13]: IntType[ExprStmt [9-13]: Expr [10-12]: Lit: Int(32)]: [6-13], Ident [14-15] "i""#]], + StmtKind: IODeclaration [0-16]: input, ClassicalType [6-13]: IntType[Expr [10-12]: Lit: Int(32)]: [6-13], Ident [14-15] "i""#]], ); } @@ -157,7 +157,7 @@ fn output_int_sized_decl() { "output int[32] i;", &expect![[r#" Stmt [0-17] - StmtKind: IODeclaration [0-17]: output, ClassicalType [7-14]: IntType[ExprStmt [10-14]: Expr [11-13]: Lit: Int(32)]: [7-14], Ident [15-16] "i""#]], + StmtKind: IODeclaration [0-17]: output, ClassicalType [7-14]: IntType[Expr [11-13]: Lit: Int(32)]: [7-14], Ident [15-16] "i""#]], ); } @@ -190,7 +190,7 @@ fn input_uint_sized_decl() { "input uint[32] i;", &expect![[r#" Stmt [0-17] - StmtKind: IODeclaration [0-17]: input, ClassicalType [6-14]: UIntType[ExprStmt [10-14]: Expr [11-13]: Lit: Int(32)]: [6-14], Ident [15-16] "i""#]], + StmtKind: IODeclaration [0-17]: input, ClassicalType [6-14]: UIntType[Expr [11-13]: Lit: Int(32)]: [6-14], Ident [15-16] "i""#]], ); } @@ -201,7 +201,7 @@ fn output_uint_sized_decl() { "output uint[32] i;", &expect![[r#" Stmt [0-18] - StmtKind: IODeclaration [0-18]: output, ClassicalType [7-15]: UIntType[ExprStmt [11-15]: Expr [12-14]: Lit: Int(32)]: [7-15], Ident [16-17] "i""#]], + StmtKind: IODeclaration [0-18]: output, ClassicalType [7-15]: UIntType[Expr [12-14]: Lit: Int(32)]: [7-15], Ident [16-17] "i""#]], ); } @@ -234,7 +234,7 @@ fn input_float_sized_decl() { "input float[32] f;", &expect![[r#" Stmt [0-18] - StmtKind: IODeclaration [0-18]: input, ClassicalType [6-15]: FloatType[ExprStmt [11-15]: Expr [12-14]: Lit: Int(32)]: [6-15], Ident [16-17] "f""#]], + StmtKind: IODeclaration [0-18]: input, ClassicalType [6-15]: FloatType[Expr [12-14]: Lit: Int(32)]: [6-15], Ident [16-17] "f""#]], ); } @@ -245,7 +245,7 @@ fn output_float_sized_decl() { "output float[32] f;", &expect![[r#" Stmt [0-19] - StmtKind: IODeclaration [0-19]: output, ClassicalType [7-16]: FloatType[ExprStmt [12-16]: Expr [13-15]: Lit: Int(32)]: [7-16], Ident [17-18] "f""#]], + StmtKind: IODeclaration [0-19]: output, ClassicalType [7-16]: FloatType[Expr [13-15]: Lit: Int(32)]: [7-16], Ident [17-18] "f""#]], ); } @@ -278,7 +278,7 @@ fn input_angle_sized_decl() { "input angle[32] a;", &expect![[r#" Stmt [0-18] - StmtKind: IODeclaration [0-18]: input, ClassicalType [6-15]: AngleType [6-15]: ExprStmt [11-15]: Expr [12-14]: Lit: Int(32), Ident [16-17] "a""#]], + StmtKind: IODeclaration [0-18]: input, ClassicalType [6-15]: AngleType [6-15]: Expr [12-14]: Lit: Int(32), Ident [16-17] "a""#]], ); } @@ -289,7 +289,7 @@ fn output_angle_sized_decl() { "output angle[32] a;", &expect![[r#" Stmt [0-19] - StmtKind: IODeclaration [0-19]: output, ClassicalType [7-16]: AngleType [7-16]: ExprStmt [12-16]: Expr [13-15]: Lit: Int(32), Ident [17-18] "a""#]], + StmtKind: IODeclaration [0-19]: output, ClassicalType [7-16]: AngleType [7-16]: Expr [13-15]: Lit: Int(32), Ident [17-18] "a""#]], ); } diff --git a/compiler/qsc_qasm3/src/parser/stmt/tests/old_style_decl.rs b/compiler/qsc_qasm3/src/parser/stmt/tests/old_style_decl.rs index 87db062e4c..69152c07ff 100644 --- a/compiler/qsc_qasm3/src/parser/stmt/tests/old_style_decl.rs +++ b/compiler/qsc_qasm3/src/parser/stmt/tests/old_style_decl.rs @@ -25,7 +25,7 @@ fn creg_array_decl() { "creg c[n];", &expect![[r#" Stmt [0-10] - StmtKind: ClassicalDeclarationStmt [0-10]: ClassicalType [0-10]: BitType [0-10]: ExprStmt [6-9]: Expr [7-8]: Ident [7-8] "n", Ident [5-6] "c""#]], + StmtKind: ClassicalDeclarationStmt [0-10]: ClassicalType [0-10]: BitType [0-10]: Expr [7-8]: Ident [7-8] "n", Ident [5-6] "c""#]], ); } @@ -47,6 +47,6 @@ fn qreg_array_decl() { "qreg q[n];", &expect![[r#" Stmt [0-10] - StmtKind: QubitDeclaration [0-10]: Ident [5-6] "q", ExprStmt [6-9]: Expr [7-8]: Ident [7-8] "n""#]], + StmtKind: QubitDeclaration [0-10]: Ident [5-6] "q", Expr [7-8]: Ident [7-8] "n""#]], ); } diff --git a/compiler/qsc_qasm3/src/parser/stmt/tests/quantum_decl.rs b/compiler/qsc_qasm3/src/parser/stmt/tests/quantum_decl.rs index c2d5e4337c..93d18cec15 100644 --- a/compiler/qsc_qasm3/src/parser/stmt/tests/quantum_decl.rs +++ b/compiler/qsc_qasm3/src/parser/stmt/tests/quantum_decl.rs @@ -77,7 +77,7 @@ fn quantum_decl_with_designator() { "qubit[5] qubits;", &expect![[r#" Stmt [0-16] - StmtKind: QubitDeclaration [0-16]: Ident [9-15] "qubits", ExprStmt [5-8]: Expr [6-7]: Lit: Int(5)"#]], + StmtKind: QubitDeclaration [0-16]: Ident [9-15] "qubits", Expr [6-7]: Lit: Int(5)"#]], ); } diff --git a/compiler/qsc_qasm3/src/parser/stmt/tests/switch_stmt.rs b/compiler/qsc_qasm3/src/parser/stmt/tests/switch_stmt.rs index 6077fa366d..d32cce4a7f 100644 --- a/compiler/qsc_qasm3/src/parser/stmt/tests/switch_stmt.rs +++ b/compiler/qsc_qasm3/src/parser/stmt/tests/switch_stmt.rs @@ -1,3 +1,6 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + use crate::parser::{stmt::parse_switch_stmt, tests::check}; use expect_test::expect; @@ -153,12 +156,12 @@ fn multiple_cases() { Expr [37-38]: Lit: Int(0) Block [39-53]: Stmt [41-51] - StmtKind: ClassicalDeclarationStmt [41-51]: ClassicalType [41-44]: IntType [41-44], Ident [45-46] "x", ValueExpression ExprStmt [49-50]: Expr [49-50]: Lit: Int(0) + StmtKind: ClassicalDeclarationStmt [41-51]: ClassicalType [41-44]: IntType [41-44], Ident [45-46] "x", ValueExpression Expr [49-50]: Lit: Int(0) Labels: Expr [69-70]: Lit: Int(1) Block [71-85]: Stmt [73-83] - StmtKind: ClassicalDeclarationStmt [73-83]: ClassicalType [73-76]: IntType [73-76], Ident [77-78] "y", ValueExpression ExprStmt [81-82]: Expr [81-82]: Lit: Int(1) + StmtKind: ClassicalDeclarationStmt [73-83]: ClassicalType [73-76]: IntType [73-76], Ident [77-78] "y", ValueExpression Expr [81-82]: Lit: Int(1) "#]], ); } diff --git a/compiler/qsc_qasm3/src/parser/stmt/tests/while_loops.rs b/compiler/qsc_qasm3/src/parser/stmt/tests/while_loops.rs index 9e5bde86a8..f444682c90 100644 --- a/compiler/qsc_qasm3/src/parser/stmt/tests/while_loops.rs +++ b/compiler/qsc_qasm3/src/parser/stmt/tests/while_loops.rs @@ -1,3 +1,6 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + use crate::parser::{stmt::parse, tests::check}; use expect_test::expect; From 6f8acf4df2441138b6cfc0ffe33bb446930355e4 Mon Sep 17 00:00:00 2001 From: Oscar Puente <156957451+orpuente-MS@users.noreply.github.com> Date: Mon, 24 Feb 2025 17:01:42 -0800 Subject: [PATCH 023/108] update expect tests --- compiler/qsc_qasm3/src/parser/expr/tests.rs | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/compiler/qsc_qasm3/src/parser/expr/tests.rs b/compiler/qsc_qasm3/src/parser/expr/tests.rs index 808c3f6781..655084d9d5 100644 --- a/compiler/qsc_qasm3/src/parser/expr/tests.rs +++ b/compiler/qsc_qasm3/src/parser/expr/tests.rs @@ -765,7 +765,7 @@ fn cast_to_bit_with_designator() { "bit[4](0)", &expect![[r#" Expr [0-9]: Cast [0-9]: - ClassicalType [0-6]: BitType [0-6]: ExprStmt [3-6]: Expr [4-5]: Lit: Int(4) + ClassicalType [0-6]: BitType [0-6]: Expr [4-5]: Lit: Int(4) Expr [0-9]: Paren: Expr [7-8]: Lit: Int(0)"#]], ); @@ -791,7 +791,7 @@ fn cast_to_int_with_designator() { "int[64](0)", &expect![[r#" Expr [0-10]: Cast [0-10]: - ClassicalType [0-7]: IntType[ExprStmt [3-7]: Expr [4-6]: Lit: Int(64)]: [0-7] + ClassicalType [0-7]: IntType[Expr [4-6]: Lit: Int(64)]: [0-7] Expr [0-10]: Paren: Expr [8-9]: Lit: Int(0)"#]], ); @@ -817,7 +817,7 @@ fn cast_to_uint_with_designator() { "uint[64](0)", &expect![[r#" Expr [0-11]: Cast [0-11]: - ClassicalType [0-8]: UIntType[ExprStmt [4-8]: Expr [5-7]: Lit: Int(64)]: [0-8] + ClassicalType [0-8]: UIntType[Expr [5-7]: Lit: Int(64)]: [0-8] Expr [0-11]: Paren: Expr [9-10]: Lit: Int(0)"#]], ); @@ -843,7 +843,7 @@ fn cast_to_float_with_designator() { "float[64](0)", &expect![[r#" Expr [0-12]: Cast [0-12]: - ClassicalType [0-9]: FloatType[ExprStmt [5-9]: Expr [6-8]: Lit: Int(64)]: [0-9] + ClassicalType [0-9]: FloatType[Expr [6-8]: Lit: Int(64)]: [0-9] Expr [0-12]: Paren: Expr [10-11]: Lit: Int(0)"#]], ); @@ -869,7 +869,7 @@ fn cast_to_complex_with_designator() { "complex[float[64]](0)", &expect![[r#" Expr [0-21]: Cast [0-21]: - ClassicalType [0-18]: ComplexType[float[FloatType[ExprStmt [13-17]: Expr [14-16]: Lit: Int(64)]: [8-17]]]: [0-18] + ClassicalType [0-18]: ComplexType[float[FloatType[Expr [14-16]: Lit: Int(64)]: [8-17]]]: [0-18] Expr [0-21]: Paren: Expr [19-20]: Lit: Int(0)"#]], ); @@ -921,7 +921,7 @@ fn cast_to_int_array() { "array[int[64], 4](0)", &expect![[r#" Expr [0-20]: Cast [0-20]: - ArrayType [0-17]: ArrayBaseTypeKind IntType[ExprStmt [9-13]: Expr [10-12]: Lit: Int(64)]: [6-13] + ArrayType [0-17]: ArrayBaseTypeKind IntType[Expr [10-12]: Lit: Int(64)]: [6-13] Expr [15-16]: Lit: Int(4) Expr [0-20]: Paren: Expr [18-19]: Lit: Int(0)"#]], @@ -935,7 +935,7 @@ fn cast_to_uint_array() { "array[uint[64], 4](0)", &expect![[r#" Expr [0-21]: Cast [0-21]: - ArrayType [0-18]: ArrayBaseTypeKind UIntType[ExprStmt [10-14]: Expr [11-13]: Lit: Int(64)]: [6-14] + ArrayType [0-18]: ArrayBaseTypeKind UIntType[Expr [11-13]: Lit: Int(64)]: [6-14] Expr [16-17]: Lit: Int(4) Expr [0-21]: Paren: Expr [19-20]: Lit: Int(0)"#]], @@ -949,7 +949,7 @@ fn cast_to_float_array() { "array[float[64], 4](0)", &expect![[r#" Expr [0-22]: Cast [0-22]: - ArrayType [0-19]: ArrayBaseTypeKind FloatType[ExprStmt [11-15]: Expr [12-14]: Lit: Int(64)]: [6-15] + ArrayType [0-19]: ArrayBaseTypeKind FloatType[Expr [12-14]: Lit: Int(64)]: [6-15] Expr [17-18]: Lit: Int(4) Expr [0-22]: Paren: Expr [20-21]: Lit: Int(0)"#]], @@ -963,7 +963,7 @@ fn cast_to_angle_array() { "array[angle[64], 4](0)", &expect![[r#" Expr [0-22]: Cast [0-22]: - ArrayType [0-19]: ArrayBaseTypeKind AngleType [6-15]: ExprStmt [11-15]: Expr [12-14]: Lit: Int(64) + ArrayType [0-19]: ArrayBaseTypeKind AngleType [6-15]: Expr [12-14]: Lit: Int(64) Expr [17-18]: Lit: Int(4) Expr [0-22]: Paren: Expr [20-21]: Lit: Int(0)"#]], @@ -1005,7 +1005,7 @@ fn cast_to_complex_array() { "array[complex[float[32]], 4](0)", &expect![[r#" Expr [0-31]: Cast [0-31]: - ArrayType [0-28]: ArrayBaseTypeKind ComplexType[float[FloatType[ExprStmt [19-23]: Expr [20-22]: Lit: Int(32)]: [14-23]]]: [6-24] + ArrayType [0-28]: ArrayBaseTypeKind ComplexType[float[FloatType[Expr [20-22]: Lit: Int(32)]: [14-23]]]: [6-24] Expr [26-27]: Lit: Int(4) Expr [0-31]: Paren: Expr [29-30]: Lit: Int(0)"#]], From 459ff22648a661bb071bc84ff245f3d7556773e5 Mon Sep 17 00:00:00 2001 From: Oscar Puente <156957451+orpuente-MS@users.noreply.github.com> Date: Tue, 25 Feb 2025 09:24:08 -0800 Subject: [PATCH 024/108] gate-call qubit args should be comma separated --- compiler/qsc_qasm3/src/ast.rs | 11 ++++++++- compiler/qsc_qasm3/src/parser/stmt.rs | 10 +++++--- .../src/parser/stmt/tests/gate_call.rs | 24 +++++++++---------- 3 files changed, 29 insertions(+), 16 deletions(-) diff --git a/compiler/qsc_qasm3/src/ast.rs b/compiler/qsc_qasm3/src/ast.rs index dcbf18d2f6..fb00c6df1d 100644 --- a/compiler/qsc_qasm3/src/ast.rs +++ b/compiler/qsc_qasm3/src/ast.rs @@ -298,10 +298,12 @@ impl Display for UnaryOp { } } -#[derive(Clone, Debug)] +#[derive(Clone, Debug, Default)] pub enum GateOperand { IndexedIdent(Box), HardwareQubit(Box), + #[default] + Err, } impl Display for GateOperand { @@ -309,10 +311,17 @@ impl Display for GateOperand { match self { GateOperand::IndexedIdent(ident) => write!(f, "GateOperand {ident}"), GateOperand::HardwareQubit(qubit) => write!(f, "GateOperand {qubit}"), + GateOperand::Err => write!(f, "Error"), } } } +impl WithSpan for GateOperand { + fn with_span(self, _span: Span) -> Self { + self + } +} + #[derive(Clone, Debug)] pub struct HardwareQubit { pub span: Span, diff --git a/compiler/qsc_qasm3/src/parser/stmt.rs b/compiler/qsc_qasm3/src/parser/stmt.rs index 7bdaadd8f9..b63c099557 100644 --- a/compiler/qsc_qasm3/src/parser/stmt.rs +++ b/compiler/qsc_qasm3/src/parser/stmt.rs @@ -20,8 +20,8 @@ use crate::{ ArrayReferenceType, ArrayType, BitType, Block, BreakStmt, ClassicalDeclarationStmt, ComplexType, ConstantDeclaration, ContinueStmt, DefStmt, EndStmt, EnumerableSet, Expr, ExprKind, ExprStmt, ExternDecl, ExternParameter, FloatType, ForStmt, FunctionCall, - GateCall, GateModifierKind, IODeclaration, IOKeyword, Ident, Identifier, IfStmt, - IncludeStmt, IntType, List, LiteralKind, Pragma, QuantumGateDefinition, + GateCall, GateModifierKind, GateOperand, IODeclaration, IOKeyword, Ident, Identifier, + IfStmt, IncludeStmt, IntType, List, LiteralKind, Pragma, QuantumGateDefinition, QuantumGateModifier, QuantumStmt, QubitDeclaration, RangeDefinition, ReturnStmt, ScalarType, ScalarTypeKind, Stmt, StmtKind, SwitchStmt, TypeDef, TypedParameter, UIntType, WhileLoop, @@ -1101,7 +1101,7 @@ fn parse_gate_call_stmt(s: &mut ParserContext) -> Result { let gate_or_expr = expr::expr(s)?; let duration = opt(s, designator)?; - let qubits = list_from_iter(many(s, gate_operand)?); + let qubits = gate_operands(s)?; recovering_semi(s); // If didn't parse modifiers, a duration, nor qubit args then this is an expr, not a gate call. @@ -1180,3 +1180,7 @@ fn gate_modifier(s: &mut ParserContext) -> Result { kind, }) } + +fn gate_operands(s: &mut ParserContext) -> Result> { + Ok(list_from_iter(seq(s, gate_operand)?.0)) +} diff --git a/compiler/qsc_qasm3/src/parser/stmt/tests/gate_call.rs b/compiler/qsc_qasm3/src/parser/stmt/tests/gate_call.rs index ef2ff116a7..cf4d130848 100644 --- a/compiler/qsc_qasm3/src/parser/stmt/tests/gate_call.rs +++ b/compiler/qsc_qasm3/src/parser/stmt/tests/gate_call.rs @@ -35,14 +35,14 @@ fn gate_call_qubit_register() { fn gate_multiple_qubits() { check( parse, - "CNOT q0 q[4];", + "CNOT q0, q[4];", &expect![[r#" - Stmt [0-13] - StmtKind: QuantumStmt [0-13]: GateCall [0-13]: Ident [0-4] "CNOT" - GateOperand IndexedIdent [5-7]: Ident [5-7] "q0"[] - GateOperand IndexedIdent [8-12]: Ident [8-9] "q"[ - IndexElement: - IndexSetItem Expr [10-11]: Lit: Int(4)]"#]], + Stmt [0-14] + StmtKind: QuantumStmt [0-14]: GateCall [0-14]: Ident [0-4] "CNOT" + GateOperand IndexedIdent [5-7]: Ident [5-7] "q0"[] + GateOperand IndexedIdent [9-13]: Ident [9-10] "q"[ + IndexElement: + IndexSetItem Expr [11-12]: Lit: Int(4)]"#]], ); } @@ -88,15 +88,15 @@ fn gate_call_inv_modifier() { fn gate_call_ctrl_inv_modifiers() { check( parse, - "ctrl(2) @ inv @ Rx(pi / 2) c1 c2 q0;", + "ctrl(2) @ inv @ Rx(pi / 2) c1, c2, q0;", &expect![[r#" - Stmt [0-36] - StmtKind: QuantumStmt [0-36]: GateCall [0-36]: Ident [16-18] "Rx" + Stmt [0-38] + StmtKind: QuantumStmt [0-38]: GateCall [0-38]: Ident [16-18] "Rx" Expr [19-25]: BinOp (Div): Expr [19-21]: Ident [19-21] "pi" Expr [24-25]: Lit: Int(2) GateOperand IndexedIdent [27-29]: Ident [27-29] "c1"[] - GateOperand IndexedIdent [30-32]: Ident [30-32] "c2"[] - GateOperand IndexedIdent [33-35]: Ident [33-35] "q0"[]"#]], + GateOperand IndexedIdent [31-33]: Ident [31-33] "c2"[] + GateOperand IndexedIdent [35-37]: Ident [35-37] "q0"[]"#]], ); } From edf3f9d97557948078aad8d93d4a376e3fbd01ef Mon Sep 17 00:00:00 2001 From: Oscar Puente <156957451+orpuente-MS@users.noreply.github.com> Date: Tue, 25 Feb 2025 14:34:26 -0800 Subject: [PATCH 025/108] add barrier, box, calgrammar, cal, defcal, delay, measure arrow, and reset statements --- compiler/qsc_qasm3/src/ast.rs | 100 +++------ compiler/qsc_qasm3/src/parser/error.rs | 4 + compiler/qsc_qasm3/src/parser/expr.rs | 4 +- compiler/qsc_qasm3/src/parser/stmt.rs | 281 +++++++++++++++++++++++-- 4 files changed, 288 insertions(+), 101 deletions(-) diff --git a/compiler/qsc_qasm3/src/ast.rs b/compiler/qsc_qasm3/src/ast.rs index fb00c6df1d..3a81cdb765 100644 --- a/compiler/qsc_qasm3/src/ast.rs +++ b/compiler/qsc_qasm3/src/ast.rs @@ -409,13 +409,14 @@ pub enum StmtKind { ExternDecl(ExternDecl), For(ForStmt), If(IfStmt), + GateCall(GateCall), + GPhase(QuantumPhase), Include(IncludeStmt), IODeclaration(IODeclaration), Measure(MeasureStmt), Pragma(Pragma), QuantumGateDefinition(QuantumGateDefinition), QuantumDecl(QubitDeclaration), - Quantum(QuantumStmt), Reset(ResetStmt), Return(ReturnStmt), Switch(SwitchStmt), @@ -449,6 +450,8 @@ impl Display for StmtKind { StmtKind::ExprStmt(expr) => write!(f, "{expr}"), StmtKind::ExternDecl(decl) => write!(f, "{decl}"), StmtKind::For(for_stmt) => write!(f, "{for_stmt}"), + StmtKind::GateCall(gate_call) => write!(f, "{gate_call}"), + StmtKind::GPhase(gphase) => write!(f, "{gphase}"), StmtKind::If(if_stmt) => write!(f, "{if_stmt}"), StmtKind::Include(include) => write!(f, "{include}"), StmtKind::IODeclaration(io) => write!(f, "{io}"), @@ -456,7 +459,6 @@ impl Display for StmtKind { StmtKind::Pragma(pragma) => write!(f, "{pragma}"), StmtKind::QuantumGateDefinition(gate) => write!(f, "{gate}"), StmtKind::QuantumDecl(decl) => write!(f, "{decl}"), - StmtKind::Quantum(quantum_stmt) => write!(f, "{quantum_stmt}"), StmtKind::Reset(reset_stmt) => write!(f, "{reset_stmt}"), StmtKind::Return(return_stmt) => write!(f, "{return_stmt}"), StmtKind::Switch(switch_stmt) => write!(f, "{switch_stmt}"), @@ -479,11 +481,13 @@ impl Display for CalibrationGrammarStmt { } #[derive(Clone, Debug)] -pub struct DefCalStmt {} +pub struct DefCalStmt { + pub span: Span, +} impl Display for DefCalStmt { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { - write!(f, "DefCalStmt") + write!(f, "DefCalStmt {}", self.span) } } @@ -512,18 +516,6 @@ impl Display for IfStmt { } } -#[derive(Clone, Debug)] -pub struct DelayStmt { - pub span: Span, - pub duration: Expr, -} - -impl Display for DelayStmt { - fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { - write!(f, "DelayStmt {}: {}", self.span, self.duration) - } -} - #[derive(Clone, Debug)] pub struct BarrierStmt { pub span: Span, @@ -725,18 +717,6 @@ impl Display for GateModifierKind { } } -#[derive(Clone, Debug)] -pub struct QuantumMeasurement { - pub span: Span, - pub qubit: Box, -} - -impl Display for QuantumMeasurement { - fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { - write!(f, "QuantumMeasurement {}: {}", self.span, self.qubit) - } -} - #[derive(Clone, Debug)] pub struct ClassicalArgument { pub span: Span, @@ -1184,41 +1164,6 @@ impl Display for ExternDecl { } } -#[derive(Clone, Debug)] -pub struct QuantumStmt { - pub span: Span, - pub kind: QuantumStmtKind, -} - -impl Display for QuantumStmt { - fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { - write!(f, "QuantumStmt {}: {}", self.span, self.kind) - } -} - -#[derive(Clone, Debug)] -pub enum QuantumStmtKind { - Gate(GateCall), - Phase(QuantumPhase), - Barrier(List), - Reset(List>), - DelayInstruction(DelayInstruction), - Box(BoxStmt), -} - -impl Display for QuantumStmtKind { - fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { - match self { - QuantumStmtKind::Gate(gate) => write!(f, "{gate}"), - QuantumStmtKind::Phase(phase) => write!(f, "{phase}"), - QuantumStmtKind::Barrier(barrier) => write!(f, "{barrier:?}"), - QuantumStmtKind::Reset(reset) => write!(f, "{reset:?}"), - QuantumStmtKind::DelayInstruction(delay) => write!(f, "{delay:?}"), - QuantumStmtKind::Box(box_stmt) => write!(f, "{box_stmt:?}"), - } - } -} - #[derive(Clone, Debug)] pub struct GateCall { pub span: Span, @@ -1266,13 +1211,13 @@ impl Display for QuantumPhase { } #[derive(Clone, Debug)] -pub struct DelayInstruction { - span: Span, - duration: Expr, - qubits: List>, +pub struct DelayStmt { + pub span: Span, + pub duration: Expr, + pub qubits: List, } -impl Display for DelayInstruction { +impl Display for DelayStmt { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { let mut indent = set_indentation(indented(f), 0); write!(indent, "DelayInstruction {}: {}", self.span, self.duration)?; @@ -1287,7 +1232,7 @@ impl Display for DelayInstruction { pub struct BoxStmt { pub span: Span, pub duration: Option, - pub body: List, + pub body: List, } impl Display for BoxStmt { @@ -1308,16 +1253,20 @@ impl Display for BoxStmt { #[derive(Clone, Debug)] pub struct MeasureStmt { pub span: Span, - pub measure: QuantumMeasurement, - pub target: Option>, + pub measurement: MeasureExpr, + pub target: Option>, } impl Display for MeasureStmt { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { if let Some(target) = &self.target { - write!(f, "MeasureStmt {}: {}, {}", self.span, self.measure, target) + write!( + f, + "MeasureStmt {}: {}, {}", + self.span, self.measurement, target + ) } else { - write!(f, "MeasureStmt {}: {}", self.span, self.measure) + write!(f, "MeasureStmt {}: {}", self.span, self.measurement) } } } @@ -1417,13 +1366,12 @@ impl Display for CalibrationGrammarDeclaration { #[derive(Clone, Debug)] pub struct CalibrationStmt { - span: Span, - body: String, + pub span: Span, } impl Display for CalibrationStmt { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { - write!(f, "CalibrationStmt {}: {}", self.span, self.body) + write!(f, "CalibrationStmt {}", self.span) } } diff --git a/compiler/qsc_qasm3/src/parser/error.rs b/compiler/qsc_qasm3/src/parser/error.rs index a4853cc3b4..4129ede837 100644 --- a/compiler/qsc_qasm3/src/parser/error.rs +++ b/compiler/qsc_qasm3/src/parser/error.rs @@ -101,6 +101,9 @@ pub enum ErrorKind { #[error("expected {0}, found {1}")] #[diagnostic(code("Qasm3.Parse.Rule"))] Rule(&'static str, TokenKind, #[label] Span), + #[error("invalid classical statement in box")] + #[diagnostic(code("Qasm3.Parse.ClassicalStmtInBox"))] + ClassicalStmtInBox(#[label] Span), #[error("expected {0}, found {1}")] #[diagnostic(code("Qasm3.Parse.Convert"))] Convert(&'static str, &'static str, #[label] Span), @@ -132,6 +135,7 @@ impl ErrorKind { Self::Escape(ch, span) => Self::Escape(ch, span + offset), Self::Token(expected, actual, span) => Self::Token(expected, actual, span + offset), Self::Rule(name, token, span) => Self::Rule(name, token, span + offset), + Self::ClassicalStmtInBox(span) => Self::ClassicalStmtInBox(span + offset), Self::Convert(expected, actual, span) => Self::Convert(expected, actual, span + offset), Self::MissingSemi(span) => Self::MissingSemi(span + offset), Self::MissingParens(span) => Self::MissingParens(span + offset), diff --git a/compiler/qsc_qasm3/src/parser/expr.rs b/compiler/qsc_qasm3/src/parser/expr.rs index c2f4d085fa..1610bd39dc 100644 --- a/compiler/qsc_qasm3/src/parser/expr.rs +++ b/compiler/qsc_qasm3/src/parser/expr.rs @@ -721,7 +721,7 @@ pub(crate) fn expr_list(s: &mut ParserContext) -> Result> { seq(s, expr).map(|pair| pair.0) } -fn measure_expr(s: &mut ParserContext) -> Result { +pub(crate) fn measure_expr(s: &mut ParserContext) -> Result { let lo = s.peek().span.lo; token(s, TokenKind::Measure)?; @@ -749,7 +749,7 @@ fn hardware_qubit(s: &mut ParserContext) -> Result { }) } -fn indexed_identifier(s: &mut ParserContext) -> Result { +pub(crate) fn indexed_identifier(s: &mut ParserContext) -> Result { let lo = s.peek().span.lo; let name: Ident = ident(s)?; let indices = list_from_iter(many(s, index_operand)?); diff --git a/compiler/qsc_qasm3/src/parser/stmt.rs b/compiler/qsc_qasm3/src/parser/stmt.rs index b63c099557..6cb9fc6774 100644 --- a/compiler/qsc_qasm3/src/parser/stmt.rs +++ b/compiler/qsc_qasm3/src/parser/stmt.rs @@ -10,21 +10,22 @@ use std::rc::Rc; use super::{ completion::WordKinds, error::{Error, ErrorKind}, - expr::{self, designator, gate_operand}, + expr::{self, designator, gate_operand, indexed_identifier}, prim::{self, barrier, many, opt, recovering, recovering_semi, recovering_token, seq, shorten}, Result, }; use crate::{ ast::{ list_from_iter, AccessControl, AliasDeclStmt, AngleType, Annotation, ArrayBaseTypeKind, - ArrayReferenceType, ArrayType, BitType, Block, BreakStmt, ClassicalDeclarationStmt, - ComplexType, ConstantDeclaration, ContinueStmt, DefStmt, EndStmt, EnumerableSet, Expr, - ExprKind, ExprStmt, ExternDecl, ExternParameter, FloatType, ForStmt, FunctionCall, + ArrayReferenceType, ArrayType, BarrierStmt, BitType, Block, BoxStmt, BreakStmt, + CalibrationGrammarStmt, CalibrationStmt, ClassicalDeclarationStmt, ComplexType, + ConstantDeclaration, ContinueStmt, DefCalStmt, DefStmt, DelayStmt, EndStmt, EnumerableSet, + Expr, ExprKind, ExprStmt, ExternDecl, ExternParameter, FloatType, ForStmt, FunctionCall, GateCall, GateModifierKind, GateOperand, IODeclaration, IOKeyword, Ident, Identifier, - IfStmt, IncludeStmt, IntType, List, LiteralKind, Pragma, QuantumGateDefinition, - QuantumGateModifier, QuantumStmt, QubitDeclaration, RangeDefinition, ReturnStmt, - ScalarType, ScalarTypeKind, Stmt, StmtKind, SwitchStmt, TypeDef, TypedParameter, UIntType, - WhileLoop, + IfStmt, IncludeStmt, IntType, List, LiteralKind, MeasureStmt, Pragma, + QuantumGateDefinition, QuantumGateModifier, QubitDeclaration, RangeDefinition, ResetStmt, + ReturnStmt, ScalarType, ScalarTypeKind, Stmt, StmtKind, SwitchStmt, TypeDef, + TypedParameter, UIntType, WhileLoop, }, keyword::Keyword, lex::{ @@ -85,6 +86,22 @@ pub(super) fn parse(s: &mut ParserContext) -> Result> { Box::new(StmtKind::ExprStmt(stmt)) } else if let Some(alias) = opt(s, parse_alias_stmt)? { Box::new(StmtKind::Alias(alias)) + } else if let Some(stmt) = opt(s, parse_box)? { + Box::new(StmtKind::Box(stmt)) + } else if let Some(stmt) = opt(s, parse_calibration_grammar_stmt)? { + Box::new(StmtKind::CalibrationGrammar(stmt)) + } else if let Some(stmt) = opt(s, parse_defcal_stmt)? { + Box::new(StmtKind::DefCal(stmt)) + } else if let Some(stmt) = opt(s, parse_cal)? { + Box::new(StmtKind::Cal(stmt)) + } else if let Some(stmt) = opt(s, parse_barrier)? { + Box::new(StmtKind::Barrier(stmt)) + } else if let Some(stmt) = opt(s, parse_delay)? { + Box::new(StmtKind::DelayStmt(stmt)) + } else if let Some(stmt) = opt(s, parse_reset)? { + Box::new(StmtKind::Reset(stmt)) + } else if let Some(stmt) = opt(s, parse_measure_stmt)? { + Box::new(StmtKind::Measure(stmt)) } else { return Err(Error::new(ErrorKind::Rule( "statement", @@ -191,7 +208,7 @@ fn parse_include(s: &mut ParserContext) -> Result { span: s.span(lo), filename: v.to_string(), }; - token(s, TokenKind::Semicolon)?; + recovering_semi(s); return Ok(StmtKind::Include(r)); } }; @@ -409,6 +426,8 @@ fn return_sig(s: &mut ParserContext) -> Result { scalar_type(s) } +/// Grammar: +/// `GATE Identifier (LPAREN params=identifierList? RPAREN)? qubits=identifierList scope`. fn parse_gatedef(s: &mut ParserContext) -> Result { let lo = s.peek().span.lo; token(s, TokenKind::Keyword(crate::keyword::Keyword::Gate))?; @@ -1075,6 +1094,44 @@ fn parse_alias_stmt(s: &mut ParserContext) -> Result { }) } +fn parse_boxable_stmt(s: &mut ParserContext) -> Result { + let stmt = *parse(s)?; + match &*stmt.kind { + StmtKind::Barrier(_) | StmtKind::Break(_) | StmtKind::DelayStmt(_) | StmtKind::Reset(_) => { + Ok(stmt) + } + _ => Err(Error::new(ErrorKind::ClassicalStmtInBox(stmt.span))), + } +} + +fn parse_many_boxable_stmt(s: &mut ParserContext) -> Result> { + let stmts = many(s, |s| { + recovering( + s, + |span| Stmt { + span, + kind: Box::new(StmtKind::Err), + annotations: Box::new([]), + }, + &[TokenKind::Semicolon], + parse_boxable_stmt, + ) + }); + + Ok(list_from_iter(stmts?)) +} + +fn parse_box_body(s: &mut ParserContext) -> Result> { + token(s, TokenKind::Open(Delim::Brace))?; + let stmts = barrier( + s, + &[TokenKind::Close(Delim::Brace)], + parse_many_boxable_stmt, + )?; + recovering_token(s, TokenKind::Close(Delim::Brace)); + Ok(stmts) +} + /// In QASM3, it is hard to disambiguate between a quantum-gate-call missing modifiers /// and expression statements. Consider the following expressions: /// 1. `Rx(2, 3) q0;` @@ -1101,7 +1158,7 @@ fn parse_gate_call_stmt(s: &mut ParserContext) -> Result { let gate_or_expr = expr::expr(s)?; let duration = opt(s, designator)?; - let qubits = gate_operands(s)?; + let qubits = gate_operand_list(s)?; recovering_semi(s); // If didn't parse modifiers, a duration, nor qubit args then this is an expr, not a gate call. @@ -1123,19 +1180,14 @@ fn parse_gate_call_stmt(s: &mut ParserContext) -> Result { } }; - let quantum_stmt = QuantumStmt { + Ok(StmtKind::GateCall(GateCall { span: s.span(lo), - kind: crate::ast::QuantumStmtKind::Gate(GateCall { - span: s.span(lo), - modifiers, - name, - args, - qubits, - duration, - }), - }; - - Ok(StmtKind::Quantum(quantum_stmt)) + modifiers, + name, + args, + qubits, + duration, + })) } /// Grammar: @@ -1181,6 +1233,189 @@ fn gate_modifier(s: &mut ParserContext) -> Result { }) } -fn gate_operands(s: &mut ParserContext) -> Result> { +/// Grammar: `gateOperand (COMMA gateOperand)* COMMA?`. +fn gate_operand_list(s: &mut ParserContext) -> Result> { Ok(list_from_iter(seq(s, gate_operand)?.0)) } + +/// Grammar: `DEFCALGRAMMAR StringLiteral SEMICOLON`. +fn parse_calibration_grammar_stmt(s: &mut ParserContext) -> Result { + let lo = s.peek().span.lo; + s.expect(WordKinds::DefCalGrammar); + token(s, TokenKind::Keyword(Keyword::DefCalGrammar))?; + let next = s.peek(); + let v = expr::lit(s)?; + + if let Some(v) = v { + recovering_semi(s); + if let LiteralKind::String(v) = v.kind { + return Ok(CalibrationGrammarStmt { + span: s.span(lo), + name: v.to_string(), + }); + } + }; + + Err(Error::new(ErrorKind::Rule( + "calibration grammar statement", + TokenKind::Literal(Literal::String), + next.span, + ))) +} + +/// We don't support `defcal` statements. +/// Grammar: `DEFCAL pushmode(eatUntilOpenBrace) pushmode(eatUntilBalancedClosingBrace)`. +fn parse_defcal_stmt(s: &mut ParserContext) -> Result { + let lo = s.peek().span.lo; + s.expect(WordKinds::DefCal); + token(s, TokenKind::Keyword(Keyword::DefCal))?; + + // Once we have parsed the `defcal` token, we eat all the tokens until we see an open brace. + while !matches!( + s.peek().kind, + TokenKind::Open(Delim::Brace) | TokenKind::Eof + ) { + s.advance(); + } + + token(s, TokenKind::Open(Delim::Brace))?; + let mut level: u32 = 1; + + loop { + match s.peek().kind { + TokenKind::Eof => { + s.advance(); + return Err(Error::new(ErrorKind::Token( + TokenKind::Close(Delim::Brace), + TokenKind::Eof, + s.span(lo), + ))); + } + TokenKind::Open(Delim::Brace) => { + s.advance(); + level += 1; + } + TokenKind::Close(Delim::Brace) => { + s.advance(); + level -= 1; + if level == 0 { + return Ok(DefCalStmt { span: s.span(lo) }); + } + } + _ => (), + } + } +} + +/// We don't support `cal` block statements. +/// Grammar: `CAL OPEN_BRACE pushmode(eatUntilBalancedClosingBrace)`. +fn parse_cal(s: &mut ParserContext) -> Result { + let lo = s.peek().span.lo; + s.expect(WordKinds::Cal); + token(s, TokenKind::Keyword(Keyword::Cal))?; + token(s, TokenKind::Open(Delim::Brace))?; + let mut level: u32 = 1; + + loop { + match s.peek().kind { + TokenKind::Eof => { + s.advance(); + return Err(Error::new(ErrorKind::Token( + TokenKind::Close(Delim::Brace), + TokenKind::Eof, + s.span(lo), + ))); + } + TokenKind::Open(Delim::Brace) => { + s.advance(); + level += 1; + } + TokenKind::Close(Delim::Brace) => { + s.advance(); + level -= 1; + if level == 0 { + return Ok(CalibrationStmt { span: s.span(lo) }); + } + } + _ => (), + } + } +} + +/// Grammar: `BARRIER gateOperandList? SEMICOLON`. +fn parse_barrier(s: &mut ParserContext) -> Result { + let lo = s.peek().span.lo; + s.expect(WordKinds::Barrier); + token(s, TokenKind::Keyword(Keyword::Barrier))?; + let qubits = gate_operand_list(s)?; + recovering_semi(s); + + Ok(BarrierStmt { + span: s.span(lo), + qubits, + }) +} + +/// Grammar: `BOX designator? scope`. +fn parse_box(s: &mut ParserContext) -> Result { + let lo = s.peek().span.lo; + s.expect(WordKinds::Box); + token(s, TokenKind::Keyword(Keyword::Box))?; + let duration = opt(s, designator)?; + let body = parse_box_body(s)?; + + Ok(BoxStmt { + span: s.span(lo), + duration, + body, + }) +} + +/// Grammar: `DELAY designator gateOperandList? SEMICOLON`. +fn parse_delay(s: &mut ParserContext) -> Result { + let lo = s.peek().span.lo; + s.expect(WordKinds::Delay); + token(s, TokenKind::Keyword(Keyword::Delay))?; + let duration = designator(s)?; + let qubits = gate_operand_list(s)?; + recovering_semi(s); + + Ok(DelayStmt { + span: s.span(lo), + duration, + qubits, + }) +} + +/// Grammar: `RESET gateOperand SEMICOLON`. +fn parse_reset(s: &mut ParserContext) -> Result { + let lo = s.peek().span.lo; + s.expect(WordKinds::Barrier); + token(s, TokenKind::Keyword(Keyword::Barrier))?; + let operand = Box::new(gate_operand(s)?); + recovering_semi(s); + + Ok(ResetStmt { + span: s.span(lo), + operand, + }) +} + +/// Grammar: `measureExpression (ARROW indexedIdentifier)? SEMICOLON`. +fn parse_measure_stmt(s: &mut ParserContext) -> Result { + let lo = s.peek().span.lo; + let measure = expr::measure_expr(s)?; + + let target = opt(s, |s| { + token(s, TokenKind::Arrow)?; + Ok(Box::new(indexed_identifier(s)?)) + })?; + + recovering_semi(s); + + Ok(MeasureStmt { + span: s.span(lo), + measurement: measure, + target, + }) +} From e3d185453689340fcd8837fd82843ad541952704 Mon Sep 17 00:00:00 2001 From: Oscar Puente <156957451+orpuente-MS@users.noreply.github.com> Date: Tue, 25 Feb 2025 14:36:12 -0800 Subject: [PATCH 026/108] update completions tests --- compiler/qsc_qasm3/src/parser/completion/tests.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/compiler/qsc_qasm3/src/parser/completion/tests.rs b/compiler/qsc_qasm3/src/parser/completion/tests.rs index 2abda50e4e..40c5dc32cc 100644 --- a/compiler/qsc_qasm3/src/parser/completion/tests.rs +++ b/compiler/qsc_qasm3/src/parser/completion/tests.rs @@ -36,7 +36,7 @@ fn begin_document() { "|OPENQASM 3;", &expect![[r#" WordKinds( - Annotation | Break | Continue | CReg | Def | End | Extern | False | For | Gate | If | Include | Input | Let | OpenQASM | Output | Pragma | QReg | Qubit | True | Return | Switch | While, + Annotation | Barrier | Box | Break | Cal | Continue | CReg | Def | DefCal | DefCalGrammar | Delay | End | Extern | False | For | Gate | If | Include | Input | Let | OpenQASM | Output | Pragma | QReg | Qubit | True | Return | Switch | While, ) "#]], ); @@ -48,7 +48,7 @@ fn end_of_version() { "OPENQASM 3;|", &expect![[r#" WordKinds( - Annotation | Break | Continue | CReg | Def | End | Extern | False | For | Gate | If | Include | Input | Let | Output | Pragma | QReg | Qubit | True | Return | Switch | While, + Annotation | Barrier | Box | Break | Cal | Continue | CReg | Def | DefCal | DefCalGrammar | Delay | End | Extern | False | For | Gate | If | Include | Input | Let | Output | Pragma | QReg | Qubit | True | Return | Switch | While, ) "#]], ); From 5223d691d8d20de16c7e76717fb8a6f555a01562 Mon Sep 17 00:00:00 2001 From: Oscar Puente <156957451+orpuente-MS@users.noreply.github.com> Date: Tue, 25 Feb 2025 15:33:45 -0800 Subject: [PATCH 027/108] add unit tests --- compiler/qsc_qasm3/src/parser/stmt.rs | 19 +++--- compiler/qsc_qasm3/src/parser/stmt/tests.rs | 8 +++ .../src/parser/stmt/tests/barrier.rs | 33 +++++++++++ .../src/parser/stmt/tests/box_stmt.rs | 58 +++++++++++++++++++ .../qsc_qasm3/src/parser/stmt/tests/cal.rs | 44 ++++++++++++++ .../src/parser/stmt/tests/cal_grammar.rs | 13 +++++ .../qsc_qasm3/src/parser/stmt/tests/defcal.rs | 44 ++++++++++++++ .../qsc_qasm3/src/parser/stmt/tests/delay.rs | 19 ++++++ .../src/parser/stmt/tests/gate_call.rs | 36 ++++++------ .../src/parser/stmt/tests/measure.rs | 40 +++++++++++++ .../qsc_qasm3/src/parser/stmt/tests/reset.rs | 29 ++++++++++ 11 files changed, 317 insertions(+), 26 deletions(-) create mode 100644 compiler/qsc_qasm3/src/parser/stmt/tests/barrier.rs create mode 100644 compiler/qsc_qasm3/src/parser/stmt/tests/box_stmt.rs create mode 100644 compiler/qsc_qasm3/src/parser/stmt/tests/cal.rs create mode 100644 compiler/qsc_qasm3/src/parser/stmt/tests/cal_grammar.rs create mode 100644 compiler/qsc_qasm3/src/parser/stmt/tests/defcal.rs create mode 100644 compiler/qsc_qasm3/src/parser/stmt/tests/delay.rs create mode 100644 compiler/qsc_qasm3/src/parser/stmt/tests/measure.rs create mode 100644 compiler/qsc_qasm3/src/parser/stmt/tests/reset.rs diff --git a/compiler/qsc_qasm3/src/parser/stmt.rs b/compiler/qsc_qasm3/src/parser/stmt.rs index 6cb9fc6774..8e22f6a53d 100644 --- a/compiler/qsc_qasm3/src/parser/stmt.rs +++ b/compiler/qsc_qasm3/src/parser/stmt.rs @@ -1097,9 +1097,12 @@ fn parse_alias_stmt(s: &mut ParserContext) -> Result { fn parse_boxable_stmt(s: &mut ParserContext) -> Result { let stmt = *parse(s)?; match &*stmt.kind { - StmtKind::Barrier(_) | StmtKind::Break(_) | StmtKind::DelayStmt(_) | StmtKind::Reset(_) => { - Ok(stmt) - } + StmtKind::Barrier(_) + | StmtKind::Break(_) + | StmtKind::DelayStmt(_) + | StmtKind::Reset(_) + | StmtKind::GateCall(_) + | StmtKind::GPhase(_) => Ok(stmt), _ => Err(Error::new(ErrorKind::ClassicalStmtInBox(stmt.span))), } } @@ -1302,7 +1305,7 @@ fn parse_defcal_stmt(s: &mut ParserContext) -> Result { return Ok(DefCalStmt { span: s.span(lo) }); } } - _ => (), + _ => s.advance(), } } } @@ -1337,7 +1340,7 @@ fn parse_cal(s: &mut ParserContext) -> Result { return Ok(CalibrationStmt { span: s.span(lo) }); } } - _ => (), + _ => s.advance(), } } } @@ -1375,7 +1378,7 @@ fn parse_box(s: &mut ParserContext) -> Result { fn parse_delay(s: &mut ParserContext) -> Result { let lo = s.peek().span.lo; s.expect(WordKinds::Delay); - token(s, TokenKind::Keyword(Keyword::Delay))?; + token(s, TokenKind::Delay)?; let duration = designator(s)?; let qubits = gate_operand_list(s)?; recovering_semi(s); @@ -1390,8 +1393,8 @@ fn parse_delay(s: &mut ParserContext) -> Result { /// Grammar: `RESET gateOperand SEMICOLON`. fn parse_reset(s: &mut ParserContext) -> Result { let lo = s.peek().span.lo; - s.expect(WordKinds::Barrier); - token(s, TokenKind::Keyword(Keyword::Barrier))?; + s.expect(WordKinds::Reset); + token(s, TokenKind::Reset)?; let operand = Box::new(gate_operand(s)?); recovering_semi(s); diff --git a/compiler/qsc_qasm3/src/parser/stmt/tests.rs b/compiler/qsc_qasm3/src/parser/stmt/tests.rs index 14fcea83ad..9b63eefbb3 100644 --- a/compiler/qsc_qasm3/src/parser/stmt/tests.rs +++ b/compiler/qsc_qasm3/src/parser/stmt/tests.rs @@ -3,8 +3,14 @@ mod alias; mod annotation; +mod barrier; +mod box_stmt; +mod cal; +mod cal_grammar; mod classical_decl; mod def; +mod defcal; +mod delay; mod expr_stmt; mod extern_decl; mod for_loops; @@ -12,8 +18,10 @@ mod gate_call; mod gate_def; mod if_stmt; mod io_decl; +mod measure; mod old_style_decl; mod pragma; mod quantum_decl; +mod reset; mod switch_stmt; mod while_loops; diff --git a/compiler/qsc_qasm3/src/parser/stmt/tests/barrier.rs b/compiler/qsc_qasm3/src/parser/stmt/tests/barrier.rs new file mode 100644 index 0000000000..c925c89a8c --- /dev/null +++ b/compiler/qsc_qasm3/src/parser/stmt/tests/barrier.rs @@ -0,0 +1,33 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +use crate::parser::stmt::parse; +use crate::parser::tests::check; +use expect_test::expect; + +#[test] +fn barrier() { + check( + parse, + "barrier r, q[0], $2;", + &expect![[r#" + Stmt [0-20] + StmtKind: Barrier [0-20]: [ + GateOperand IndexedIdent [8-9]: Ident [8-9] "r"[] + GateOperand IndexedIdent [11-15]: Ident [11-12] "q"[ + IndexElement: + IndexSetItem Expr [13-14]: Lit: Int(0)] + GateOperand HardwareQubit [17-19]: 2]"#]], + ); +} + +#[test] +fn barrier_no_args() { + check( + parse, + "barrier;", + &expect![[r#" + Stmt [0-8] + StmtKind: Barrier [0-8]: []"#]], + ); +} diff --git a/compiler/qsc_qasm3/src/parser/stmt/tests/box_stmt.rs b/compiler/qsc_qasm3/src/parser/stmt/tests/box_stmt.rs new file mode 100644 index 0000000000..81e63ad80a --- /dev/null +++ b/compiler/qsc_qasm3/src/parser/stmt/tests/box_stmt.rs @@ -0,0 +1,58 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +use crate::parser::stmt::parse; +use crate::parser::tests::check; +use expect_test::expect; + +#[test] +fn box_stmt() { + check( + parse, + " + box { + H q0; + Rx(2.4) q1; + }", + &expect![[r#" + Stmt [5-50] + StmtKind: BoxStmt [5-50]: + Stmt [19-24] + StmtKind: GateCall [19-24]: Ident [19-20] "H" + GateOperand IndexedIdent [21-23]: Ident [21-23] "q0"[] + Stmt [33-44] + StmtKind: GateCall [33-44]: Ident [33-35] "Rx" + Expr [36-39]: Lit: Float(2.4) + GateOperand IndexedIdent [41-43]: Ident [41-43] "q1"[]"#]], + ); +} + +#[test] +fn box_stmt_with_invalid_instruction() { + check( + parse, + "box { + H q0; + 2 + 4; + }", + &expect![[r#" + Stmt [0-40] + StmtKind: BoxStmt [0-40]: + Stmt [14-19] + StmtKind: GateCall [14-19]: Ident [14-15] "H" + GateOperand IndexedIdent [16-18]: Ident [16-18] "q0"[] + Stmt [28-34] + StmtKind: Err + + [ + Error( + ClassicalStmtInBox( + Span { + lo: 28, + hi: 34, + }, + ), + ), + ]"#]], + ); +} diff --git a/compiler/qsc_qasm3/src/parser/stmt/tests/cal.rs b/compiler/qsc_qasm3/src/parser/stmt/tests/cal.rs new file mode 100644 index 0000000000..da1ce82d60 --- /dev/null +++ b/compiler/qsc_qasm3/src/parser/stmt/tests/cal.rs @@ -0,0 +1,44 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +use crate::parser::stmt::parse; +use crate::parser::tests::check; +use expect_test::expect; + +#[test] +fn cal_block_with_unbalanced_braces_errors() { + check( + parse, + "cal { { }", + &expect![[r#" + Error( + Token( + Close( + Brace, + ), + Eof, + Span { + lo: 0, + hi: 9, + }, + ), + ) + "#]], + ); +} + +#[test] +fn cal_block_accept_any_tokens_inside() { + check( + parse, + " + cal { + faoi foaijdf a; + fkfm )( + .314 + }", + &expect![[r#" + Stmt [5-69] + StmtKind: CalibrationStmt [5-69]"#]], + ); +} diff --git a/compiler/qsc_qasm3/src/parser/stmt/tests/cal_grammar.rs b/compiler/qsc_qasm3/src/parser/stmt/tests/cal_grammar.rs new file mode 100644 index 0000000000..c1b1c7e6e7 --- /dev/null +++ b/compiler/qsc_qasm3/src/parser/stmt/tests/cal_grammar.rs @@ -0,0 +1,13 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +use crate::parser::stmt::parse; +use crate::parser::tests::check; +use expect_test::expect; + +#[test] +fn defcalgrammar() { + check(parse, r#"defcalgrammar "openpulse";"#, &expect![[r#" + Stmt [0-26] + StmtKind: CalibrationGrammarStmt [0-26]: openpulse"#]]); +} diff --git a/compiler/qsc_qasm3/src/parser/stmt/tests/defcal.rs b/compiler/qsc_qasm3/src/parser/stmt/tests/defcal.rs new file mode 100644 index 0000000000..986f833810 --- /dev/null +++ b/compiler/qsc_qasm3/src/parser/stmt/tests/defcal.rs @@ -0,0 +1,44 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +use crate::parser::stmt::parse; +use crate::parser::tests::check; +use expect_test::expect; + +#[test] +fn defcal_block_with_unbalanced_braces_errors() { + check( + parse, + "defcal foo q { { }", + &expect![[r#" + Error( + Token( + Close( + Brace, + ), + Eof, + Span { + lo: 0, + hi: 18, + }, + ), + ) + "#]], + ); +} + +#[test] +fn cal_block_accept_any_tokens_inside() { + check( + parse, + " + defcal foo(a, b) q0 q1 { + faoi foaijdf a; + fkfm )( + .314 + }", + &expect![[r#" + Stmt [5-88] + StmtKind: DefCalStmt [5-88]"#]], + ); +} diff --git a/compiler/qsc_qasm3/src/parser/stmt/tests/delay.rs b/compiler/qsc_qasm3/src/parser/stmt/tests/delay.rs new file mode 100644 index 0000000000..65954342e9 --- /dev/null +++ b/compiler/qsc_qasm3/src/parser/stmt/tests/delay.rs @@ -0,0 +1,19 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +use crate::parser::stmt::parse; +use crate::parser::tests::check; +use expect_test::expect; + +#[test] +fn delay() { + check(parse, "delay[a] q[0], q[1];", &expect![[r#" + Stmt [0-20] + StmtKind: DelayInstruction [0-20]: Expr [6-7]: Ident [6-7] "a" + GateOperand IndexedIdent [9-13]: Ident [9-10] "q"[ + IndexElement: + IndexSetItem Expr [11-12]: Lit: Int(0)] + GateOperand IndexedIdent [15-19]: Ident [15-16] "q"[ + IndexElement: + IndexSetItem Expr [17-18]: Lit: Int(1)]"#]]); +} diff --git a/compiler/qsc_qasm3/src/parser/stmt/tests/gate_call.rs b/compiler/qsc_qasm3/src/parser/stmt/tests/gate_call.rs index cf4d130848..3a9c4f4f27 100644 --- a/compiler/qsc_qasm3/src/parser/stmt/tests/gate_call.rs +++ b/compiler/qsc_qasm3/src/parser/stmt/tests/gate_call.rs @@ -11,9 +11,9 @@ fn gate_call() { parse, "H q0;", &expect![[r#" - Stmt [0-5] - StmtKind: QuantumStmt [0-5]: GateCall [0-5]: Ident [0-1] "H" - GateOperand IndexedIdent [2-4]: Ident [2-4] "q0"[]"#]], + Stmt [0-5] + StmtKind: GateCall [0-5]: Ident [0-1] "H" + GateOperand IndexedIdent [2-4]: Ident [2-4] "q0"[]"#]], ); } @@ -23,11 +23,11 @@ fn gate_call_qubit_register() { parse, "H q[2];", &expect![[r#" - Stmt [0-7] - StmtKind: QuantumStmt [0-7]: GateCall [0-7]: Ident [0-1] "H" - GateOperand IndexedIdent [2-6]: Ident [2-3] "q"[ - IndexElement: - IndexSetItem Expr [4-5]: Lit: Int(2)]"#]], + Stmt [0-7] + StmtKind: GateCall [0-7]: Ident [0-1] "H" + GateOperand IndexedIdent [2-6]: Ident [2-3] "q"[ + IndexElement: + IndexSetItem Expr [4-5]: Lit: Int(2)]"#]], ); } @@ -38,7 +38,7 @@ fn gate_multiple_qubits() { "CNOT q0, q[4];", &expect![[r#" Stmt [0-14] - StmtKind: QuantumStmt [0-14]: GateCall [0-14]: Ident [0-4] "CNOT" + StmtKind: GateCall [0-14]: Ident [0-4] "CNOT" GateOperand IndexedIdent [5-7]: Ident [5-7] "q0"[] GateOperand IndexedIdent [9-13]: Ident [9-10] "q"[ IndexElement: @@ -53,7 +53,7 @@ fn gate_no_qubits() { "inv @ H;", &expect![[r#" Stmt [0-8] - StmtKind: QuantumStmt [0-8]: GateCall [0-8]: Ident [6-7] "H""#]], + StmtKind: GateCall [0-8]: Ident [6-7] "H""#]], ); } @@ -63,12 +63,12 @@ fn gate_call_with_parameters() { parse, "Rx(pi / 2) q0;", &expect![[r#" - Stmt [0-14] - StmtKind: QuantumStmt [0-14]: GateCall [0-14]: Ident [0-2] "Rx" - Expr [3-9]: BinOp (Div): - Expr [3-5]: Ident [3-5] "pi" - Expr [8-9]: Lit: Int(2) - GateOperand IndexedIdent [11-13]: Ident [11-13] "q0"[]"#]], + Stmt [0-14] + StmtKind: GateCall [0-14]: Ident [0-2] "Rx" + Expr [3-9]: BinOp (Div): + Expr [3-5]: Ident [3-5] "pi" + Expr [8-9]: Lit: Int(2) + GateOperand IndexedIdent [11-13]: Ident [11-13] "q0"[]"#]], ); } @@ -79,7 +79,7 @@ fn gate_call_inv_modifier() { "inv @ H q0;", &expect![[r#" Stmt [0-11] - StmtKind: QuantumStmt [0-11]: GateCall [0-11]: Ident [6-7] "H" + StmtKind: GateCall [0-11]: Ident [6-7] "H" GateOperand IndexedIdent [8-10]: Ident [8-10] "q0"[]"#]], ); } @@ -91,7 +91,7 @@ fn gate_call_ctrl_inv_modifiers() { "ctrl(2) @ inv @ Rx(pi / 2) c1, c2, q0;", &expect![[r#" Stmt [0-38] - StmtKind: QuantumStmt [0-38]: GateCall [0-38]: Ident [16-18] "Rx" + StmtKind: GateCall [0-38]: Ident [16-18] "Rx" Expr [19-25]: BinOp (Div): Expr [19-21]: Ident [19-21] "pi" Expr [24-25]: Lit: Int(2) diff --git a/compiler/qsc_qasm3/src/parser/stmt/tests/measure.rs b/compiler/qsc_qasm3/src/parser/stmt/tests/measure.rs new file mode 100644 index 0000000000..946cd7b251 --- /dev/null +++ b/compiler/qsc_qasm3/src/parser/stmt/tests/measure.rs @@ -0,0 +1,40 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +use crate::parser::stmt::parse; +use crate::parser::tests::check; +use expect_test::expect; + +#[test] +fn measure_identifier() { + check(parse, "measure q;", &expect![[r#" + Stmt [0-10] + StmtKind: MeasureStmt [0-10]: MeasureExpr [0-7]: GateOperand IndexedIdent [8-9]: Ident [8-9] "q"[]"#]]); +} + +#[test] +fn measure_indented_ident() { + check(parse, "measure q[2];", &expect![[r#" + Stmt [0-13] + StmtKind: MeasureStmt [0-13]: MeasureExpr [0-7]: GateOperand IndexedIdent [8-12]: Ident [8-9] "q"[ + IndexElement: + IndexSetItem Expr [10-11]: Lit: Int(2)]"#]]); +} + +#[test] +fn measure_hardware_qubit() { + check(parse, "measure $42;", &expect![[r#" + Stmt [0-12] + StmtKind: MeasureStmt [0-12]: MeasureExpr [0-7]: GateOperand HardwareQubit [8-11]: 42"#]]); +} + +#[test] +fn measure_arrow() { + check(parse, "measure q[2] -> a[4];", &expect![[r#" + Stmt [0-21] + StmtKind: MeasureStmt [0-21]: MeasureExpr [0-7]: GateOperand IndexedIdent [8-12]: Ident [8-9] "q"[ + IndexElement: + IndexSetItem Expr [10-11]: Lit: Int(2)], IndexedIdent [16-20]: Ident [16-17] "a"[ + IndexElement: + IndexSetItem Expr [18-19]: Lit: Int(4)]"#]]); +} diff --git a/compiler/qsc_qasm3/src/parser/stmt/tests/reset.rs b/compiler/qsc_qasm3/src/parser/stmt/tests/reset.rs new file mode 100644 index 0000000000..09bd24733f --- /dev/null +++ b/compiler/qsc_qasm3/src/parser/stmt/tests/reset.rs @@ -0,0 +1,29 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +use crate::parser::stmt::parse; +use crate::parser::tests::check; +use expect_test::expect; + +#[test] +fn reset_ident() { + check(parse, "reset a;", &expect![[r#" + Stmt [0-8] + StmtKind: ResetStmt [0-8]: GateOperand IndexedIdent [6-7]: Ident [6-7] "a"[]"#]]); +} + +#[test] +fn reset_indexed_ident() { + check(parse, "reset a[1];", &expect![[r#" + Stmt [0-11] + StmtKind: ResetStmt [0-11]: GateOperand IndexedIdent [6-10]: Ident [6-7] "a"[ + IndexElement: + IndexSetItem Expr [8-9]: Lit: Int(1)]"#]]); +} + +#[test] +fn reset_hardware_qubit() { + check(parse, "reset $42;", &expect![[r#" + Stmt [0-10] + StmtKind: ResetStmt [0-10]: GateOperand HardwareQubit [6-9]: 42"#]]); +} From 82d2736e26942e3603eae3f644092d24135668bb Mon Sep 17 00:00:00 2001 From: Oscar Puente <156957451+orpuente-MS@users.noreply.github.com> Date: Tue, 25 Feb 2025 15:41:20 -0800 Subject: [PATCH 028/108] fix formatting --- .../src/parser/stmt/tests/cal_grammar.rs | 8 +++-- .../qsc_qasm3/src/parser/stmt/tests/def.rs | 8 ++--- .../qsc_qasm3/src/parser/stmt/tests/delay.rs | 8 +++-- .../src/parser/stmt/tests/measure.rs | 32 ++++++++++++++----- .../qsc_qasm3/src/parser/stmt/tests/reset.rs | 24 ++++++++++---- 5 files changed, 58 insertions(+), 22 deletions(-) diff --git a/compiler/qsc_qasm3/src/parser/stmt/tests/cal_grammar.rs b/compiler/qsc_qasm3/src/parser/stmt/tests/cal_grammar.rs index c1b1c7e6e7..0416e8bb03 100644 --- a/compiler/qsc_qasm3/src/parser/stmt/tests/cal_grammar.rs +++ b/compiler/qsc_qasm3/src/parser/stmt/tests/cal_grammar.rs @@ -7,7 +7,11 @@ use expect_test::expect; #[test] fn defcalgrammar() { - check(parse, r#"defcalgrammar "openpulse";"#, &expect![[r#" + check( + parse, + r#"defcalgrammar "openpulse";"#, + &expect![[r#" Stmt [0-26] - StmtKind: CalibrationGrammarStmt [0-26]: openpulse"#]]); + StmtKind: CalibrationGrammarStmt [0-26]: openpulse"#]], + ); } diff --git a/compiler/qsc_qasm3/src/parser/stmt/tests/def.rs b/compiler/qsc_qasm3/src/parser/stmt/tests/def.rs index d9f82d4b77..7f1babf50d 100644 --- a/compiler/qsc_qasm3/src/parser/stmt/tests/def.rs +++ b/compiler/qsc_qasm3/src/parser/stmt/tests/def.rs @@ -47,7 +47,7 @@ fn missing_args_with_delim_error() { "def x(,) { }", &expect![[r#" Stmt [0-12] - StmtKind: DefStmt [0-12]: Ident [4-5] "x"([6-6] Ident [0-0] "": ClassicalType [0-0]: Err) + StmtKind: DefStmt [0-12]: Ident [4-5] "x"([6-6] Ident [0-0] "": ClassicalType [0-0]: Err) [ Error( @@ -69,7 +69,7 @@ fn args_with_extra_delim_err_ty() { "def x(int a,,int b) { }", &expect![[r#" Stmt [0-23] - StmtKind: DefStmt [0-23]: Ident [4-5] "x"([6-11] Ident [10-11] "a": ClassicalType [6-9]: IntType [6-9], [12-12] Ident [0-0] "": ClassicalType [0-0]: Err, [13-18] Ident [17-18] "b": ClassicalType [13-16]: IntType [13-16]) + StmtKind: DefStmt [0-23]: Ident [4-5] "x"([6-11] Ident [10-11] "a": ClassicalType [6-9]: IntType [6-9], [12-12] Ident [0-0] "": ClassicalType [0-0]: Err, [13-18] Ident [17-18] "b": ClassicalType [13-16]: IntType [13-16]) [ Error( @@ -91,7 +91,7 @@ fn classical_subroutine() { "def square(int[32] x) -> int { return x ** 2; }", &expect![[r#" Stmt [0-47] - StmtKind: DefStmt [0-47]: Ident [4-10] "square"([11-20] Ident [19-20] "x": ClassicalType [11-18]: IntType[Expr [15-17]: Lit: Int(32)]: [11-18]) + StmtKind: DefStmt [0-47]: Ident [4-10] "square"([11-20] Ident [19-20] "x": ClassicalType [11-18]: IntType[Expr [15-17]: Lit: Int(32)]: [11-18]) Stmt [31-45] StmtKind: ReturnStmt [31-45]: ValueExpression Expr [38-44]: BinOp (Exp): Expr [38-39]: Ident [38-39] "x" @@ -118,7 +118,7 @@ fn old_style_args() { "def test(creg c, qreg q, creg c2[2], qreg q4[4]) -> int { return x ** 2; }", &expect![[r#" Stmt [0-74] - StmtKind: DefStmt [0-74]: Ident [4-8] "test"([9-15] Ident [14-15] "c": ClassicalType [9-15]: BitType, [17-23] Ident [22-23] "q": qubit, [25-35] Ident [30-32] "c2": ClassicalType [25-35]: BitType [25-35]: Expr [33-34]: Lit: Int(2), [37-47] Ident [42-44] "q4": qubit[Expr [45-46]: Lit: Int(4)]) + StmtKind: DefStmt [0-74]: Ident [4-8] "test"([9-15] Ident [14-15] "c": ClassicalType [9-15]: BitType, [17-23] Ident [22-23] "q": qubit, [25-35] Ident [30-32] "c2": ClassicalType [25-35]: BitType [25-35]: Expr [33-34]: Lit: Int(2), [37-47] Ident [42-44] "q4": qubit[Expr [45-46]: Lit: Int(4)]) Stmt [58-72] StmtKind: ReturnStmt [58-72]: ValueExpression Expr [65-71]: BinOp (Exp): Expr [65-66]: Ident [65-66] "x" diff --git a/compiler/qsc_qasm3/src/parser/stmt/tests/delay.rs b/compiler/qsc_qasm3/src/parser/stmt/tests/delay.rs index 65954342e9..463ba062f0 100644 --- a/compiler/qsc_qasm3/src/parser/stmt/tests/delay.rs +++ b/compiler/qsc_qasm3/src/parser/stmt/tests/delay.rs @@ -7,7 +7,10 @@ use expect_test::expect; #[test] fn delay() { - check(parse, "delay[a] q[0], q[1];", &expect![[r#" + check( + parse, + "delay[a] q[0], q[1];", + &expect![[r#" Stmt [0-20] StmtKind: DelayInstruction [0-20]: Expr [6-7]: Ident [6-7] "a" GateOperand IndexedIdent [9-13]: Ident [9-10] "q"[ @@ -15,5 +18,6 @@ fn delay() { IndexSetItem Expr [11-12]: Lit: Int(0)] GateOperand IndexedIdent [15-19]: Ident [15-16] "q"[ IndexElement: - IndexSetItem Expr [17-18]: Lit: Int(1)]"#]]); + IndexSetItem Expr [17-18]: Lit: Int(1)]"#]], + ); } diff --git a/compiler/qsc_qasm3/src/parser/stmt/tests/measure.rs b/compiler/qsc_qasm3/src/parser/stmt/tests/measure.rs index 946cd7b251..05950318df 100644 --- a/compiler/qsc_qasm3/src/parser/stmt/tests/measure.rs +++ b/compiler/qsc_qasm3/src/parser/stmt/tests/measure.rs @@ -7,34 +7,50 @@ use expect_test::expect; #[test] fn measure_identifier() { - check(parse, "measure q;", &expect![[r#" + check( + parse, + "measure q;", + &expect![[r#" Stmt [0-10] - StmtKind: MeasureStmt [0-10]: MeasureExpr [0-7]: GateOperand IndexedIdent [8-9]: Ident [8-9] "q"[]"#]]); + StmtKind: MeasureStmt [0-10]: MeasureExpr [0-7]: GateOperand IndexedIdent [8-9]: Ident [8-9] "q"[]"#]], + ); } #[test] fn measure_indented_ident() { - check(parse, "measure q[2];", &expect![[r#" + check( + parse, + "measure q[2];", + &expect![[r#" Stmt [0-13] StmtKind: MeasureStmt [0-13]: MeasureExpr [0-7]: GateOperand IndexedIdent [8-12]: Ident [8-9] "q"[ IndexElement: - IndexSetItem Expr [10-11]: Lit: Int(2)]"#]]); + IndexSetItem Expr [10-11]: Lit: Int(2)]"#]], + ); } #[test] fn measure_hardware_qubit() { - check(parse, "measure $42;", &expect![[r#" + check( + parse, + "measure $42;", + &expect![[r#" Stmt [0-12] - StmtKind: MeasureStmt [0-12]: MeasureExpr [0-7]: GateOperand HardwareQubit [8-11]: 42"#]]); + StmtKind: MeasureStmt [0-12]: MeasureExpr [0-7]: GateOperand HardwareQubit [8-11]: 42"#]], + ); } #[test] fn measure_arrow() { - check(parse, "measure q[2] -> a[4];", &expect![[r#" + check( + parse, + "measure q[2] -> a[4];", + &expect![[r#" Stmt [0-21] StmtKind: MeasureStmt [0-21]: MeasureExpr [0-7]: GateOperand IndexedIdent [8-12]: Ident [8-9] "q"[ IndexElement: IndexSetItem Expr [10-11]: Lit: Int(2)], IndexedIdent [16-20]: Ident [16-17] "a"[ IndexElement: - IndexSetItem Expr [18-19]: Lit: Int(4)]"#]]); + IndexSetItem Expr [18-19]: Lit: Int(4)]"#]], + ); } diff --git a/compiler/qsc_qasm3/src/parser/stmt/tests/reset.rs b/compiler/qsc_qasm3/src/parser/stmt/tests/reset.rs index 09bd24733f..863df34196 100644 --- a/compiler/qsc_qasm3/src/parser/stmt/tests/reset.rs +++ b/compiler/qsc_qasm3/src/parser/stmt/tests/reset.rs @@ -7,23 +7,35 @@ use expect_test::expect; #[test] fn reset_ident() { - check(parse, "reset a;", &expect![[r#" + check( + parse, + "reset a;", + &expect![[r#" Stmt [0-8] - StmtKind: ResetStmt [0-8]: GateOperand IndexedIdent [6-7]: Ident [6-7] "a"[]"#]]); + StmtKind: ResetStmt [0-8]: GateOperand IndexedIdent [6-7]: Ident [6-7] "a"[]"#]], + ); } #[test] fn reset_indexed_ident() { - check(parse, "reset a[1];", &expect![[r#" + check( + parse, + "reset a[1];", + &expect![[r#" Stmt [0-11] StmtKind: ResetStmt [0-11]: GateOperand IndexedIdent [6-10]: Ident [6-7] "a"[ IndexElement: - IndexSetItem Expr [8-9]: Lit: Int(1)]"#]]); + IndexSetItem Expr [8-9]: Lit: Int(1)]"#]], + ); } #[test] fn reset_hardware_qubit() { - check(parse, "reset $42;", &expect![[r#" + check( + parse, + "reset $42;", + &expect![[r#" Stmt [0-10] - StmtKind: ResetStmt [0-10]: GateOperand HardwareQubit [6-9]: 42"#]]); + StmtKind: ResetStmt [0-10]: GateOperand HardwareQubit [6-9]: 42"#]], + ); } From 77c0c4743aca29fae5a0bf0f004b415c32d0e149 Mon Sep 17 00:00:00 2001 From: Oscar Puente <156957451+orpuente-MS@users.noreply.github.com> Date: Tue, 25 Feb 2025 15:43:04 -0800 Subject: [PATCH 029/108] update completions tests --- compiler/qsc_qasm3/src/parser/completion/tests.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/compiler/qsc_qasm3/src/parser/completion/tests.rs b/compiler/qsc_qasm3/src/parser/completion/tests.rs index 40c5dc32cc..c4d70fab6e 100644 --- a/compiler/qsc_qasm3/src/parser/completion/tests.rs +++ b/compiler/qsc_qasm3/src/parser/completion/tests.rs @@ -36,7 +36,7 @@ fn begin_document() { "|OPENQASM 3;", &expect![[r#" WordKinds( - Annotation | Barrier | Box | Break | Cal | Continue | CReg | Def | DefCal | DefCalGrammar | Delay | End | Extern | False | For | Gate | If | Include | Input | Let | OpenQASM | Output | Pragma | QReg | Qubit | True | Return | Switch | While, + Annotation | Barrier | Box | Break | Cal | Continue | CReg | Def | DefCal | DefCalGrammar | Delay | End | Extern | False | For | Gate | If | Include | Input | Let | OpenQASM | Output | Pragma | QReg | Qubit | Reset | True | Return | Switch | While, ) "#]], ); @@ -48,7 +48,7 @@ fn end_of_version() { "OPENQASM 3;|", &expect![[r#" WordKinds( - Annotation | Barrier | Box | Break | Cal | Continue | CReg | Def | DefCal | DefCalGrammar | Delay | End | Extern | False | For | Gate | If | Include | Input | Let | Output | Pragma | QReg | Qubit | True | Return | Switch | While, + Annotation | Barrier | Box | Break | Cal | Continue | CReg | Def | DefCal | DefCalGrammar | Delay | End | Extern | False | For | Gate | If | Include | Input | Let | Output | Pragma | QReg | Qubit | Reset | True | Return | Switch | While, ) "#]], ); From 13975fa8557b868373dddb10f9d73bf1f06aeef4 Mon Sep 17 00:00:00 2001 From: Oscar Puente <156957451+orpuente-MS@users.noreply.github.com> Date: Tue, 25 Feb 2025 15:53:41 -0800 Subject: [PATCH 030/108] update unit tests --- compiler/qsc_qasm3/src/parser/stmt/tests/def.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/compiler/qsc_qasm3/src/parser/stmt/tests/def.rs b/compiler/qsc_qasm3/src/parser/stmt/tests/def.rs index 7f1babf50d..d9f82d4b77 100644 --- a/compiler/qsc_qasm3/src/parser/stmt/tests/def.rs +++ b/compiler/qsc_qasm3/src/parser/stmt/tests/def.rs @@ -47,7 +47,7 @@ fn missing_args_with_delim_error() { "def x(,) { }", &expect![[r#" Stmt [0-12] - StmtKind: DefStmt [0-12]: Ident [4-5] "x"([6-6] Ident [0-0] "": ClassicalType [0-0]: Err) + StmtKind: DefStmt [0-12]: Ident [4-5] "x"([6-6] Ident [0-0] "": ClassicalType [0-0]: Err) [ Error( @@ -69,7 +69,7 @@ fn args_with_extra_delim_err_ty() { "def x(int a,,int b) { }", &expect![[r#" Stmt [0-23] - StmtKind: DefStmt [0-23]: Ident [4-5] "x"([6-11] Ident [10-11] "a": ClassicalType [6-9]: IntType [6-9], [12-12] Ident [0-0] "": ClassicalType [0-0]: Err, [13-18] Ident [17-18] "b": ClassicalType [13-16]: IntType [13-16]) + StmtKind: DefStmt [0-23]: Ident [4-5] "x"([6-11] Ident [10-11] "a": ClassicalType [6-9]: IntType [6-9], [12-12] Ident [0-0] "": ClassicalType [0-0]: Err, [13-18] Ident [17-18] "b": ClassicalType [13-16]: IntType [13-16]) [ Error( @@ -91,7 +91,7 @@ fn classical_subroutine() { "def square(int[32] x) -> int { return x ** 2; }", &expect![[r#" Stmt [0-47] - StmtKind: DefStmt [0-47]: Ident [4-10] "square"([11-20] Ident [19-20] "x": ClassicalType [11-18]: IntType[Expr [15-17]: Lit: Int(32)]: [11-18]) + StmtKind: DefStmt [0-47]: Ident [4-10] "square"([11-20] Ident [19-20] "x": ClassicalType [11-18]: IntType[Expr [15-17]: Lit: Int(32)]: [11-18]) Stmt [31-45] StmtKind: ReturnStmt [31-45]: ValueExpression Expr [38-44]: BinOp (Exp): Expr [38-39]: Ident [38-39] "x" @@ -118,7 +118,7 @@ fn old_style_args() { "def test(creg c, qreg q, creg c2[2], qreg q4[4]) -> int { return x ** 2; }", &expect![[r#" Stmt [0-74] - StmtKind: DefStmt [0-74]: Ident [4-8] "test"([9-15] Ident [14-15] "c": ClassicalType [9-15]: BitType, [17-23] Ident [22-23] "q": qubit, [25-35] Ident [30-32] "c2": ClassicalType [25-35]: BitType [25-35]: Expr [33-34]: Lit: Int(2), [37-47] Ident [42-44] "q4": qubit[Expr [45-46]: Lit: Int(4)]) + StmtKind: DefStmt [0-74]: Ident [4-8] "test"([9-15] Ident [14-15] "c": ClassicalType [9-15]: BitType, [17-23] Ident [22-23] "q": qubit, [25-35] Ident [30-32] "c2": ClassicalType [25-35]: BitType [25-35]: Expr [33-34]: Lit: Int(2), [37-47] Ident [42-44] "q4": qubit[Expr [45-46]: Lit: Int(4)]) Stmt [58-72] StmtKind: ReturnStmt [58-72]: ValueExpression Expr [65-71]: BinOp (Exp): Expr [65-66]: Ident [65-66] "x" From fa32afa31519a20745a686bd0d8282e2279a1c20 Mon Sep 17 00:00:00 2001 From: Oscar Puente <156957451+orpuente-MS@users.noreply.github.com> Date: Wed, 26 Feb 2025 09:58:26 -0800 Subject: [PATCH 031/108] add support for timing literals --- compiler/qsc_qasm3/src/parser/expr.rs | 38 ++++++++++++++++++++++----- 1 file changed, 31 insertions(+), 7 deletions(-) diff --git a/compiler/qsc_qasm3/src/parser/expr.rs b/compiler/qsc_qasm3/src/parser/expr.rs index 1610bd39dc..e424406cc5 100644 --- a/compiler/qsc_qasm3/src/parser/expr.rs +++ b/compiler/qsc_qasm3/src/parser/expr.rs @@ -18,12 +18,12 @@ use crate::{ ast::{ self, list_from_iter, AssignExpr, AssignOpExpr, BinOp, BinaryOpExpr, Cast, DiscreteSet, Expr, ExprKind, FunctionCall, GateOperand, HardwareQubit, Ident, IndexElement, IndexExpr, - IndexSetItem, IndexedIdent, List, Lit, LiteralKind, MeasureExpr, RangeDefinition, TypeDef, - UnaryOp, ValueExpression, Version, + IndexSetItem, IndexedIdent, List, Lit, LiteralKind, MeasureExpr, RangeDefinition, TimeUnit, + TypeDef, UnaryOp, ValueExpression, Version, }, keyword::Keyword, lex::{ - cooked::{ComparisonOp, Literal}, + cooked::{ComparisonOp, Literal, TimingLiteralKind}, ClosedBinOp, Delim, Radix, Token, TokenKind, }, parser::{ @@ -308,10 +308,7 @@ fn lit_token(lexeme: &str, token: Token) -> Result> { span: token.span, })) } - Literal::Timing(_timing_literal_kind) => Err(Error::new(ErrorKind::Lit( - "unimplemented: timing literal", - token.span, - ))), + Literal::Timing(kind) => timing_literal(lexeme, token, kind), }, TokenKind::Keyword(Keyword::True) => Ok(Some(Lit { kind: LiteralKind::Bool(true), @@ -410,6 +407,33 @@ fn lit_bigint(lexeme: &str, radix: u32) -> Option { } } +fn timing_literal(lexeme: &str, token: Token, kind: TimingLiteralKind) -> Result> { + { + let lexeme = lexeme + .chars() + .filter(|x| *x != '_') + .take_while(|x| x.is_numeric() || *x == '.') + .collect::(); + + let value = lexeme + .parse() + .map_err(|_| Error::new(ErrorKind::Lit("timing", token.span)))?; + + let unit = match kind { + TimingLiteralKind::Dt => TimeUnit::Dt, + TimingLiteralKind::Ns => TimeUnit::Ns, + TimingLiteralKind::Us => TimeUnit::Us, + TimingLiteralKind::Ms => TimeUnit::Ms, + TimingLiteralKind::S => TimeUnit::S, + }; + + Ok(Some(Lit { + span: token.span, + kind: LiteralKind::Duration { value, unit }, + })) + } +} + pub(crate) fn paren_expr(s: &mut ParserContext, lo: u32) -> Result { let (mut exprs, final_sep) = seq(s, expr)?; token(s, TokenKind::Close(Delim::Paren))?; From 4593e8f4d399d6e7749f555f9b70a491636fe6d4 Mon Sep 17 00:00:00 2001 From: Oscar Puente <156957451+orpuente-MS@users.noreply.github.com> Date: Wed, 26 Feb 2025 10:16:51 -0800 Subject: [PATCH 032/108] improve TODO messages --- compiler/qsc_qasm3/src/ast.rs | 7 +++++-- compiler/qsc_qasm3/src/parser/expr.rs | 4 +++- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/compiler/qsc_qasm3/src/ast.rs b/compiler/qsc_qasm3/src/ast.rs index 3a81cdb765..f954f16220 100644 --- a/compiler/qsc_qasm3/src/ast.rs +++ b/compiler/qsc_qasm3/src/ast.rs @@ -26,8 +26,11 @@ fn set_indentation<'a, 'b>( } } -// TODO: profile this with iai-callgrind in a large OpenQASM3 -// sample to verify that is actually faster than using Vec. +// TODO: Profile this with iai-callgrind in a large OpenQASM3 +// sample to verify that is actually faster than using Vec. +// Even though Box uses less stack space, it reduces cache +// locality, because now you need to be jumping around in +// memory to read contiguous elements of a list. /// An alternative to `Vec` that uses less stack space. pub(crate) type List = Box<[Box]>; diff --git a/compiler/qsc_qasm3/src/parser/expr.rs b/compiler/qsc_qasm3/src/parser/expr.rs index e424406cc5..5f7cd47cc1 100644 --- a/compiler/qsc_qasm3/src/parser/expr.rs +++ b/compiler/qsc_qasm3/src/parser/expr.rs @@ -59,7 +59,9 @@ enum OpKind { Index, } -// TODO: This seems to be an unnecessary wrapper. Consider removing. +// TODO: This seems to be an unnecessary wrapper. +// OpName::Keyword is never used. +// Consider removing. #[derive(Clone, Copy)] enum OpName { Token(TokenKind), From d6583c8c7e2daded37ec39c89e8a15f69f6c48f9 Mon Sep 17 00:00:00 2001 From: Oscar Puente <156957451+orpuente-MS@users.noreply.github.com> Date: Wed, 26 Feb 2025 15:48:36 -0800 Subject: [PATCH 033/108] address comments in PR review --- compiler/qsc_qasm3/src/parser/stmt.rs | 15 ++++++++++++--- .../qsc_qasm3/src/parser/stmt/tests/box_stmt.rs | 8 ++++++-- 2 files changed, 18 insertions(+), 5 deletions(-) diff --git a/compiler/qsc_qasm3/src/parser/stmt.rs b/compiler/qsc_qasm3/src/parser/stmt.rs index 8e22f6a53d..d3d125d340 100644 --- a/compiler/qsc_qasm3/src/parser/stmt.rs +++ b/compiler/qsc_qasm3/src/parser/stmt.rs @@ -1103,7 +1103,14 @@ fn parse_boxable_stmt(s: &mut ParserContext) -> Result { | StmtKind::Reset(_) | StmtKind::GateCall(_) | StmtKind::GPhase(_) => Ok(stmt), - _ => Err(Error::new(ErrorKind::ClassicalStmtInBox(stmt.span))), + _ => { + s.push_error(Error::new(ErrorKind::ClassicalStmtInBox(stmt.span))); + Ok(Stmt { + span: stmt.span, + annotations: stmt.annotations, + kind: Box::new(StmtKind::Err), + }) + } } } @@ -1266,7 +1273,8 @@ fn parse_calibration_grammar_stmt(s: &mut ParserContext) -> Result Result { let lo = s.peek().span.lo; @@ -1310,7 +1318,8 @@ fn parse_defcal_stmt(s: &mut ParserContext) -> Result { } } -/// We don't support `cal` block statements. +/// We don't support `cal` block statements in the compiler. Therefore +/// the parser just goes through the tokens in a cal block and ignores them. /// Grammar: `CAL OPEN_BRACE pushmode(eatUntilBalancedClosingBrace)`. fn parse_cal(s: &mut ParserContext) -> Result { let lo = s.peek().span.lo; diff --git a/compiler/qsc_qasm3/src/parser/stmt/tests/box_stmt.rs b/compiler/qsc_qasm3/src/parser/stmt/tests/box_stmt.rs index 81e63ad80a..31bd544bba 100644 --- a/compiler/qsc_qasm3/src/parser/stmt/tests/box_stmt.rs +++ b/compiler/qsc_qasm3/src/parser/stmt/tests/box_stmt.rs @@ -34,15 +34,19 @@ fn box_stmt_with_invalid_instruction() { "box { H q0; 2 + 4; + X q1; }", &expect![[r#" - Stmt [0-40] - StmtKind: BoxStmt [0-40]: + Stmt [0-54] + StmtKind: BoxStmt [0-54]: Stmt [14-19] StmtKind: GateCall [14-19]: Ident [14-15] "H" GateOperand IndexedIdent [16-18]: Ident [16-18] "q0"[] Stmt [28-34] StmtKind: Err + Stmt [43-48] + StmtKind: GateCall [43-48]: Ident [43-44] "X" + GateOperand IndexedIdent [45-47]: Ident [45-47] "q1"[] [ Error( From cee7f398a86de506705dbb1c285a8378911b2bb6 Mon Sep 17 00:00:00 2001 From: Oscar Puente <156957451+orpuente-MS@users.noreply.github.com> Date: Wed, 26 Feb 2025 16:31:48 -0800 Subject: [PATCH 034/108] add gphase --- compiler/qsc_qasm3/src/ast.rs | 19 ++-- compiler/qsc_qasm3/src/parser/error.rs | 4 + compiler/qsc_qasm3/src/parser/stmt.rs | 41 ++++++- compiler/qsc_qasm3/src/parser/stmt/tests.rs | 1 + .../src/parser/stmt/tests/gate_call.rs | 13 ++- .../qsc_qasm3/src/parser/stmt/tests/gphase.rs | 102 ++++++++++++++++++ .../src/parser/stmt/tests/measure.rs | 25 +++-- 7 files changed, 188 insertions(+), 17 deletions(-) create mode 100644 compiler/qsc_qasm3/src/parser/stmt/tests/gphase.rs diff --git a/compiler/qsc_qasm3/src/ast.rs b/compiler/qsc_qasm3/src/ast.rs index f954f16220..606d8d86ce 100644 --- a/compiler/qsc_qasm3/src/ast.rs +++ b/compiler/qsc_qasm3/src/ast.rs @@ -413,7 +413,7 @@ pub enum StmtKind { For(ForStmt), If(IfStmt), GateCall(GateCall), - GPhase(QuantumPhase), + GPhase(GPhase), Include(IncludeStmt), IODeclaration(IODeclaration), Measure(MeasureStmt), @@ -1195,20 +1195,27 @@ impl Display for GateCall { } #[derive(Clone, Debug)] -pub struct QuantumPhase { +pub struct GPhase { pub span: Span, pub modifiers: List, - pub arg: Expr, - pub qubits: List>, + pub args: List, + pub qubits: List, + pub duration: Option, } -impl Display for QuantumPhase { +impl Display for GPhase { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { let mut indent = set_indentation(indented(f), 0); - write!(indent, "QuantumPhase {}: {}", self.span, self.arg)?; + write!(indent, "GPhase {}:", self.span)?; + for arg in &self.args { + write!(indent, "\n{arg}")?; + } for qubit in &self.qubits { write!(indent, "\n{qubit}")?; } + if let Some(duration) = &self.duration { + write!(indent, "\n{duration}")?; + } Ok(()) } } diff --git a/compiler/qsc_qasm3/src/parser/error.rs b/compiler/qsc_qasm3/src/parser/error.rs index 4129ede837..b01b3350d2 100644 --- a/compiler/qsc_qasm3/src/parser/error.rs +++ b/compiler/qsc_qasm3/src/parser/error.rs @@ -122,6 +122,9 @@ pub enum ErrorKind { #[error("missing switch statement case labels")] #[diagnostic(code("Qasm3.Parse.MissingSwitchCaseLabels"))] MissingSwitchCaseLabels(#[label] Span), + #[error("missing switch statement case labels")] + #[diagnostic(code("Qasm3.Parse.MissingGateCallOperands"))] + MissingGateCallOperands(#[label] Span), #[error("expected an item or closing brace, found {0}")] #[diagnostic(code("Qasm3.Parse.ExpectedItem"))] ExpectedItem(TokenKind, #[label] Span), @@ -143,6 +146,7 @@ impl ErrorKind { Self::MissingSeqEntry(span) => Self::MissingSeqEntry(span + offset), Self::MissingSwitchCases(span) => Self::MissingSwitchCases(span + offset), Self::MissingSwitchCaseLabels(span) => Self::MissingSwitchCaseLabels(span + offset), + Self::MissingGateCallOperands(span) => Self::MissingGateCallOperands(span + offset), Self::ExpectedItem(token, span) => Self::ExpectedItem(token, span + offset), } } diff --git a/compiler/qsc_qasm3/src/parser/stmt.rs b/compiler/qsc_qasm3/src/parser/stmt.rs index d3d125d340..1911554183 100644 --- a/compiler/qsc_qasm3/src/parser/stmt.rs +++ b/compiler/qsc_qasm3/src/parser/stmt.rs @@ -21,8 +21,8 @@ use crate::{ CalibrationGrammarStmt, CalibrationStmt, ClassicalDeclarationStmt, ComplexType, ConstantDeclaration, ContinueStmt, DefCalStmt, DefStmt, DelayStmt, EndStmt, EnumerableSet, Expr, ExprKind, ExprStmt, ExternDecl, ExternParameter, FloatType, ForStmt, FunctionCall, - GateCall, GateModifierKind, GateOperand, IODeclaration, IOKeyword, Ident, Identifier, - IfStmt, IncludeStmt, IntType, List, LiteralKind, MeasureStmt, Pragma, + GPhase, GateCall, GateModifierKind, GateOperand, IODeclaration, IOKeyword, Ident, + Identifier, IfStmt, IncludeStmt, IntType, List, LiteralKind, MeasureStmt, Pragma, QuantumGateDefinition, QuantumGateModifier, QubitDeclaration, RangeDefinition, ResetStmt, ReturnStmt, ScalarType, ScalarTypeKind, Stmt, StmtKind, SwitchStmt, TypeDef, TypedParameter, UIntType, WhileLoop, @@ -1164,6 +1164,12 @@ fn parse_gate_call_stmt(s: &mut ParserContext) -> Result { let lo = s.peek().span.lo; let modifiers = list_from_iter(many(s, gate_modifier)?); + // If the next token is `gphase`, parse a gphase instead, which has optional operands. + if s.peek().kind == TokenKind::GPhase { + let gphase = parse_gphase(s, lo, modifiers)?; + return Ok(StmtKind::GPhase(gphase)); + } + // As explained in the docstring, we parse the gate using the expr parser. let gate_or_expr = expr::expr(s)?; @@ -1190,6 +1196,10 @@ fn parse_gate_call_stmt(s: &mut ParserContext) -> Result { } }; + if qubits.is_empty() { + s.push_error(Error::new(ErrorKind::MissingGateCallOperands(s.span(lo)))); + } + Ok(StmtKind::GateCall(GateCall { span: s.span(lo), modifiers, @@ -1200,6 +1210,33 @@ fn parse_gate_call_stmt(s: &mut ParserContext) -> Result { })) } +fn parse_gphase( + s: &mut ParserContext, + lo: u32, + modifiers: List, +) -> Result { + token(s, TokenKind::GPhase)?; + let args = opt(s, |s| { + token(s, TokenKind::Open(Delim::Paren))?; + let exprs = expr::expr_list(s)?; + recovering_token(s, TokenKind::Close(Delim::Paren)); + Ok(list_from_iter(exprs)) + })? + .unwrap_or_default(); + + let duration = opt(s, designator)?; + let qubits = gate_operand_list(s)?; + recovering_semi(s); + + Ok(GPhase { + span: s.span(lo), + modifiers, + args, + qubits, + duration, + }) +} + /// Grammar: /// `( /// INV diff --git a/compiler/qsc_qasm3/src/parser/stmt/tests.rs b/compiler/qsc_qasm3/src/parser/stmt/tests.rs index 9b63eefbb3..5e964c6104 100644 --- a/compiler/qsc_qasm3/src/parser/stmt/tests.rs +++ b/compiler/qsc_qasm3/src/parser/stmt/tests.rs @@ -16,6 +16,7 @@ mod extern_decl; mod for_loops; mod gate_call; mod gate_def; +mod gphase; mod if_stmt; mod io_decl; mod measure; diff --git a/compiler/qsc_qasm3/src/parser/stmt/tests/gate_call.rs b/compiler/qsc_qasm3/src/parser/stmt/tests/gate_call.rs index 3a9c4f4f27..4e273cdc96 100644 --- a/compiler/qsc_qasm3/src/parser/stmt/tests/gate_call.rs +++ b/compiler/qsc_qasm3/src/parser/stmt/tests/gate_call.rs @@ -53,7 +53,18 @@ fn gate_no_qubits() { "inv @ H;", &expect![[r#" Stmt [0-8] - StmtKind: GateCall [0-8]: Ident [6-7] "H""#]], + StmtKind: GateCall [0-8]: Ident [6-7] "H" + + [ + Error( + MissingGateCallOperands( + Span { + lo: 0, + hi: 8, + }, + ), + ), + ]"#]], ); } diff --git a/compiler/qsc_qasm3/src/parser/stmt/tests/gphase.rs b/compiler/qsc_qasm3/src/parser/stmt/tests/gphase.rs new file mode 100644 index 0000000000..0a04b90719 --- /dev/null +++ b/compiler/qsc_qasm3/src/parser/stmt/tests/gphase.rs @@ -0,0 +1,102 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +use crate::parser::stmt::parse; +use crate::parser::tests::check; +use expect_test::expect; + +#[test] +fn gphase() { + check( + parse, + "gphase q0;", + &expect![[r#" + Stmt [0-10] + StmtKind: GPhase [0-10]: + GateOperand IndexedIdent [7-9]: Ident [7-9] "q0"[]"#]], + ); +} + +#[test] +fn gphase_qubit_register() { + check( + parse, + "gphase q[2];", + &expect![[r#" + Stmt [0-12] + StmtKind: GPhase [0-12]: + GateOperand IndexedIdent [7-11]: Ident [7-8] "q"[ + IndexElement: + IndexSetItem Expr [9-10]: Lit: Int(2)]"#]], + ); +} + +#[test] +fn gphase_multiple_qubits() { + check( + parse, + "gphase q0, q[4];", + &expect![[r#" + Stmt [0-16] + StmtKind: GPhase [0-16]: + GateOperand IndexedIdent [7-9]: Ident [7-9] "q0"[] + GateOperand IndexedIdent [11-15]: Ident [11-12] "q"[ + IndexElement: + IndexSetItem Expr [13-14]: Lit: Int(4)]"#]], + ); +} + +#[test] +fn gphase_no_qubits() { + check( + parse, + "inv @ gphase;", + &expect![[r#" + Stmt [0-13] + StmtKind: GPhase [0-13]:"#]], + ); +} + +#[test] +fn gphase_with_parameters() { + check( + parse, + "gphase(pi / 2) q0;", + &expect![[r#" + Stmt [0-18] + StmtKind: GPhase [0-18]: + Expr [7-13]: BinOp (Div): + Expr [7-9]: Ident [7-9] "pi" + Expr [12-13]: Lit: Int(2) + GateOperand IndexedIdent [15-17]: Ident [15-17] "q0"[]"#]], + ); +} + +#[test] +fn gphase_inv_modifier() { + check( + parse, + "inv @ gphase q0;", + &expect![[r#" + Stmt [0-16] + StmtKind: GPhase [0-16]: + GateOperand IndexedIdent [13-15]: Ident [13-15] "q0"[]"#]], + ); +} + +#[test] +fn gphase_ctrl_inv_modifiers() { + check( + parse, + "ctrl(2) @ inv @ gphase(pi / 2) c1, c2, q0;", + &expect![[r#" + Stmt [0-42] + StmtKind: GPhase [0-42]: + Expr [23-29]: BinOp (Div): + Expr [23-25]: Ident [23-25] "pi" + Expr [28-29]: Lit: Int(2) + GateOperand IndexedIdent [31-33]: Ident [31-33] "c1"[] + GateOperand IndexedIdent [35-37]: Ident [35-37] "c2"[] + GateOperand IndexedIdent [39-41]: Ident [39-41] "q0"[]"#]], + ); +} diff --git a/compiler/qsc_qasm3/src/parser/stmt/tests/measure.rs b/compiler/qsc_qasm3/src/parser/stmt/tests/measure.rs index 05950318df..ffb621efa9 100644 --- a/compiler/qsc_qasm3/src/parser/stmt/tests/measure.rs +++ b/compiler/qsc_qasm3/src/parser/stmt/tests/measure.rs @@ -41,16 +41,25 @@ fn measure_hardware_qubit() { } #[test] -fn measure_arrow() { +fn measure_arrow_into_ident() { check( parse, - "measure q[2] -> a[4];", + "measure q -> a;", &expect![[r#" - Stmt [0-21] - StmtKind: MeasureStmt [0-21]: MeasureExpr [0-7]: GateOperand IndexedIdent [8-12]: Ident [8-9] "q"[ - IndexElement: - IndexSetItem Expr [10-11]: Lit: Int(2)], IndexedIdent [16-20]: Ident [16-17] "a"[ - IndexElement: - IndexSetItem Expr [18-19]: Lit: Int(4)]"#]], + Stmt [0-15] + StmtKind: MeasureStmt [0-15]: MeasureExpr [0-7]: GateOperand IndexedIdent [8-9]: Ident [8-9] "q"[], IndexedIdent [13-14]: Ident [13-14] "a"[]"#]], + ); +} + +#[test] +fn measure_arrow_into_indented_ident() { + check( + parse, + "measure q -> a[1];", + &expect![[r#" + Stmt [0-18] + StmtKind: MeasureStmt [0-18]: MeasureExpr [0-7]: GateOperand IndexedIdent [8-9]: Ident [8-9] "q"[], IndexedIdent [13-17]: Ident [13-14] "a"[ + IndexElement: + IndexSetItem Expr [15-16]: Lit: Int(1)]"#]], ); } From 044203f64d5f7aabe451fdc96dce31361a59b114 Mon Sep 17 00:00:00 2001 From: Oscar Puente <156957451+orpuente-MS@users.noreply.github.com> Date: Thu, 27 Feb 2025 09:04:36 -0800 Subject: [PATCH 035/108] add unit test for box with designator --- .../src/parser/stmt/tests/box_stmt.rs | 22 +++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/compiler/qsc_qasm3/src/parser/stmt/tests/box_stmt.rs b/compiler/qsc_qasm3/src/parser/stmt/tests/box_stmt.rs index 31bd544bba..463f5fa21c 100644 --- a/compiler/qsc_qasm3/src/parser/stmt/tests/box_stmt.rs +++ b/compiler/qsc_qasm3/src/parser/stmt/tests/box_stmt.rs @@ -27,6 +27,28 @@ fn box_stmt() { ); } +#[test] +fn box_stmt_with_designator() { + check( + parse, + " + box[4us] { + H q0; + Rx(2.4) q1; + }", + &expect![[r#" + Stmt [5-55] + StmtKind: BoxStmt [5-55]: Expr [9-12]: Lit: Duration(4.0, Us) + Stmt [24-29] + StmtKind: GateCall [24-29]: Ident [24-25] "H" + GateOperand IndexedIdent [26-28]: Ident [26-28] "q0"[] + Stmt [38-49] + StmtKind: GateCall [38-49]: Ident [38-40] "Rx" + Expr [41-44]: Lit: Float(2.4) + GateOperand IndexedIdent [46-48]: Ident [46-48] "q1"[]"#]], + ); +} + #[test] fn box_stmt_with_invalid_instruction() { check( From 3f78884ddf3acb6217f4dd8cfeb35cbf31f84377 Mon Sep 17 00:00:00 2001 From: Oscar Puente <156957451+orpuente-MS@users.noreply.github.com> Date: Thu, 27 Feb 2025 09:55:46 -0800 Subject: [PATCH 036/108] require exactly one angle argument in `gphase` --- compiler/qsc_qasm3/src/parser/error.rs | 4 + compiler/qsc_qasm3/src/parser/stmt.rs | 18 ++-- .../qsc_qasm3/src/parser/stmt/tests/gphase.rs | 95 ++++++++++++------- 3 files changed, 73 insertions(+), 44 deletions(-) diff --git a/compiler/qsc_qasm3/src/parser/error.rs b/compiler/qsc_qasm3/src/parser/error.rs index b01b3350d2..bfa8a30a98 100644 --- a/compiler/qsc_qasm3/src/parser/error.rs +++ b/compiler/qsc_qasm3/src/parser/error.rs @@ -128,6 +128,9 @@ pub enum ErrorKind { #[error("expected an item or closing brace, found {0}")] #[diagnostic(code("Qasm3.Parse.ExpectedItem"))] ExpectedItem(TokenKind, #[label] Span), + #[error("gphase gate requires exactly one angle")] + #[diagnostic(code("Qasm3.Parse.GPhaseInvalidArguments"))] + GPhaseInvalidArguments(#[label] Span), } impl ErrorKind { @@ -148,6 +151,7 @@ impl ErrorKind { Self::MissingSwitchCaseLabels(span) => Self::MissingSwitchCaseLabels(span + offset), Self::MissingGateCallOperands(span) => Self::MissingGateCallOperands(span + offset), Self::ExpectedItem(token, span) => Self::ExpectedItem(token, span + offset), + Self::GPhaseInvalidArguments(span) => Self::GPhaseInvalidArguments(span + offset), } } } diff --git a/compiler/qsc_qasm3/src/parser/stmt.rs b/compiler/qsc_qasm3/src/parser/stmt.rs index 1911554183..1ba08f86fc 100644 --- a/compiler/qsc_qasm3/src/parser/stmt.rs +++ b/compiler/qsc_qasm3/src/parser/stmt.rs @@ -197,8 +197,7 @@ pub fn parse_annotation(s: &mut ParserContext) -> Result> { fn parse_include(s: &mut ParserContext) -> Result { let lo = s.peek().span.lo; - s.expect(WordKinds::Include); - token(s, TokenKind::Keyword(crate::keyword::Keyword::Include))?; + token(s, TokenKind::Keyword(Keyword::Include))?; let next = s.peek(); let v = expr::lit(s)?; @@ -1216,6 +1215,8 @@ fn parse_gphase( modifiers: List, ) -> Result { token(s, TokenKind::GPhase)?; + + let args_lo = s.peek().span.lo; let args = opt(s, |s| { token(s, TokenKind::Open(Delim::Paren))?; let exprs = expr::expr_list(s)?; @@ -1224,6 +1225,12 @@ fn parse_gphase( })? .unwrap_or_default(); + if args.len() != 1 { + s.push_error(Error::new(ErrorKind::GPhaseInvalidArguments( + s.span(args_lo), + ))); + } + let duration = opt(s, designator)?; let qubits = gate_operand_list(s)?; recovering_semi(s); @@ -1288,7 +1295,6 @@ fn gate_operand_list(s: &mut ParserContext) -> Result> { /// Grammar: `DEFCALGRAMMAR StringLiteral SEMICOLON`. fn parse_calibration_grammar_stmt(s: &mut ParserContext) -> Result { let lo = s.peek().span.lo; - s.expect(WordKinds::DefCalGrammar); token(s, TokenKind::Keyword(Keyword::DefCalGrammar))?; let next = s.peek(); let v = expr::lit(s)?; @@ -1315,7 +1321,6 @@ fn parse_calibration_grammar_stmt(s: &mut ParserContext) -> Result Result { let lo = s.peek().span.lo; - s.expect(WordKinds::DefCal); token(s, TokenKind::Keyword(Keyword::DefCal))?; // Once we have parsed the `defcal` token, we eat all the tokens until we see an open brace. @@ -1360,7 +1365,6 @@ fn parse_defcal_stmt(s: &mut ParserContext) -> Result { /// Grammar: `CAL OPEN_BRACE pushmode(eatUntilBalancedClosingBrace)`. fn parse_cal(s: &mut ParserContext) -> Result { let lo = s.peek().span.lo; - s.expect(WordKinds::Cal); token(s, TokenKind::Keyword(Keyword::Cal))?; token(s, TokenKind::Open(Delim::Brace))?; let mut level: u32 = 1; @@ -1394,7 +1398,6 @@ fn parse_cal(s: &mut ParserContext) -> Result { /// Grammar: `BARRIER gateOperandList? SEMICOLON`. fn parse_barrier(s: &mut ParserContext) -> Result { let lo = s.peek().span.lo; - s.expect(WordKinds::Barrier); token(s, TokenKind::Keyword(Keyword::Barrier))?; let qubits = gate_operand_list(s)?; recovering_semi(s); @@ -1408,7 +1411,6 @@ fn parse_barrier(s: &mut ParserContext) -> Result { /// Grammar: `BOX designator? scope`. fn parse_box(s: &mut ParserContext) -> Result { let lo = s.peek().span.lo; - s.expect(WordKinds::Box); token(s, TokenKind::Keyword(Keyword::Box))?; let duration = opt(s, designator)?; let body = parse_box_body(s)?; @@ -1423,7 +1425,6 @@ fn parse_box(s: &mut ParserContext) -> Result { /// Grammar: `DELAY designator gateOperandList? SEMICOLON`. fn parse_delay(s: &mut ParserContext) -> Result { let lo = s.peek().span.lo; - s.expect(WordKinds::Delay); token(s, TokenKind::Delay)?; let duration = designator(s)?; let qubits = gate_operand_list(s)?; @@ -1439,7 +1440,6 @@ fn parse_delay(s: &mut ParserContext) -> Result { /// Grammar: `RESET gateOperand SEMICOLON`. fn parse_reset(s: &mut ParserContext) -> Result { let lo = s.peek().span.lo; - s.expect(WordKinds::Reset); token(s, TokenKind::Reset)?; let operand = Box::new(gate_operand(s)?); recovering_semi(s); diff --git a/compiler/qsc_qasm3/src/parser/stmt/tests/gphase.rs b/compiler/qsc_qasm3/src/parser/stmt/tests/gphase.rs index 0a04b90719..773611b83c 100644 --- a/compiler/qsc_qasm3/src/parser/stmt/tests/gphase.rs +++ b/compiler/qsc_qasm3/src/parser/stmt/tests/gphase.rs @@ -9,11 +9,26 @@ use expect_test::expect; fn gphase() { check( parse, - "gphase q0;", + "gphase(pi / 2);", &expect![[r#" - Stmt [0-10] - StmtKind: GPhase [0-10]: - GateOperand IndexedIdent [7-9]: Ident [7-9] "q0"[]"#]], + Stmt [0-15] + StmtKind: GPhase [0-15]: + Expr [7-13]: BinOp (Div): + Expr [7-9]: Ident [7-9] "pi" + Expr [12-13]: Lit: Int(2)"#]], + ); +} + +#[test] +fn gphase_qubit_ident() { + check( + parse, + "gphase(a) q0;", + &expect![[r#" + Stmt [0-13] + StmtKind: GPhase [0-13]: + Expr [7-8]: Ident [7-8] "a" + GateOperand IndexedIdent [10-12]: Ident [10-12] "q0"[]"#]], ); } @@ -21,13 +36,14 @@ fn gphase() { fn gphase_qubit_register() { check( parse, - "gphase q[2];", + "gphase(a) q[2];", &expect![[r#" - Stmt [0-12] - StmtKind: GPhase [0-12]: - GateOperand IndexedIdent [7-11]: Ident [7-8] "q"[ + Stmt [0-15] + StmtKind: GPhase [0-15]: + Expr [7-8]: Ident [7-8] "a" + GateOperand IndexedIdent [10-14]: Ident [10-11] "q"[ IndexElement: - IndexSetItem Expr [9-10]: Lit: Int(2)]"#]], + IndexSetItem Expr [12-13]: Lit: Int(2)]"#]], ); } @@ -35,25 +51,37 @@ fn gphase_qubit_register() { fn gphase_multiple_qubits() { check( parse, - "gphase q0, q[4];", + "gphase(a) q0, q[4];", &expect![[r#" - Stmt [0-16] - StmtKind: GPhase [0-16]: - GateOperand IndexedIdent [7-9]: Ident [7-9] "q0"[] - GateOperand IndexedIdent [11-15]: Ident [11-12] "q"[ + Stmt [0-19] + StmtKind: GPhase [0-19]: + Expr [7-8]: Ident [7-8] "a" + GateOperand IndexedIdent [10-12]: Ident [10-12] "q0"[] + GateOperand IndexedIdent [14-18]: Ident [14-15] "q"[ IndexElement: - IndexSetItem Expr [13-14]: Lit: Int(4)]"#]], + IndexSetItem Expr [16-17]: Lit: Int(4)]"#]], ); } #[test] -fn gphase_no_qubits() { +fn gphase_no_arguments() { check( parse, - "inv @ gphase;", + "gphase;", &expect![[r#" - Stmt [0-13] - StmtKind: GPhase [0-13]:"#]], + Stmt [0-7] + StmtKind: GPhase [0-7]: + + [ + Error( + GPhaseInvalidArguments( + Span { + lo: 6, + hi: 6, + }, + ), + ), + ]"#]], ); } @@ -61,14 +89,13 @@ fn gphase_no_qubits() { fn gphase_with_parameters() { check( parse, - "gphase(pi / 2) q0;", + "gphase(pi / 2);", &expect![[r#" - Stmt [0-18] - StmtKind: GPhase [0-18]: + Stmt [0-15] + StmtKind: GPhase [0-15]: Expr [7-13]: BinOp (Div): Expr [7-9]: Ident [7-9] "pi" - Expr [12-13]: Lit: Int(2) - GateOperand IndexedIdent [15-17]: Ident [15-17] "q0"[]"#]], + Expr [12-13]: Lit: Int(2)"#]], ); } @@ -76,11 +103,11 @@ fn gphase_with_parameters() { fn gphase_inv_modifier() { check( parse, - "inv @ gphase q0;", + "inv @ gphase(a);", &expect![[r#" Stmt [0-16] StmtKind: GPhase [0-16]: - GateOperand IndexedIdent [13-15]: Ident [13-15] "q0"[]"#]], + Expr [13-14]: Ident [13-14] "a""#]], ); } @@ -88,15 +115,13 @@ fn gphase_inv_modifier() { fn gphase_ctrl_inv_modifiers() { check( parse, - "ctrl(2) @ inv @ gphase(pi / 2) c1, c2, q0;", + "ctrl @ inv @ gphase(pi / 2) q0;", &expect![[r#" - Stmt [0-42] - StmtKind: GPhase [0-42]: - Expr [23-29]: BinOp (Div): - Expr [23-25]: Ident [23-25] "pi" - Expr [28-29]: Lit: Int(2) - GateOperand IndexedIdent [31-33]: Ident [31-33] "c1"[] - GateOperand IndexedIdent [35-37]: Ident [35-37] "c2"[] - GateOperand IndexedIdent [39-41]: Ident [39-41] "q0"[]"#]], + Stmt [0-31] + StmtKind: GPhase [0-31]: + Expr [20-26]: BinOp (Div): + Expr [20-22]: Ident [20-22] "pi" + Expr [25-26]: Lit: Int(2) + GateOperand IndexedIdent [28-30]: Ident [28-30] "q0"[]"#]], ); } From c758fceb821bc51d030be19b38a4f28dba170002 Mon Sep 17 00:00:00 2001 From: Oscar Puente <156957451+orpuente-MS@users.noreply.github.com> Date: Thu, 27 Feb 2025 11:33:54 -0800 Subject: [PATCH 037/108] fix parse_include and parse_cal_grammar --- compiler/qsc_qasm3/src/parser/stmt.rs | 33 +++++++------- compiler/qsc_qasm3/src/parser/stmt/tests.rs | 1 + .../src/parser/stmt/tests/cal_grammar.rs | 44 +++++++++++++++++++ .../src/parser/stmt/tests/include.rs | 41 +++++++++++++++++ 4 files changed, 103 insertions(+), 16 deletions(-) create mode 100644 compiler/qsc_qasm3/src/parser/stmt/tests/include.rs diff --git a/compiler/qsc_qasm3/src/parser/stmt.rs b/compiler/qsc_qasm3/src/parser/stmt.rs index 1ba08f86fc..9d682c6445 100644 --- a/compiler/qsc_qasm3/src/parser/stmt.rs +++ b/compiler/qsc_qasm3/src/parser/stmt.rs @@ -200,20 +200,20 @@ fn parse_include(s: &mut ParserContext) -> Result { token(s, TokenKind::Keyword(Keyword::Include))?; let next = s.peek(); - let v = expr::lit(s)?; - if let Some(v) = v { - if let LiteralKind::String(v) = v.kind { - let r = IncludeStmt { + let lit = expr::lit(s)?; + recovering_semi(s); + + if let Some(lit) = lit { + if let LiteralKind::String(v) = lit.kind { + return Ok(StmtKind::Include(IncludeStmt { span: s.span(lo), filename: v.to_string(), - }; - recovering_semi(s); - return Ok(StmtKind::Include(r)); + })); } }; Err(Error::new(ErrorKind::Rule( - "include statement", - TokenKind::Literal(Literal::String), + "string literal", + next.kind, next.span, ))) } @@ -1296,22 +1296,23 @@ fn gate_operand_list(s: &mut ParserContext) -> Result> { fn parse_calibration_grammar_stmt(s: &mut ParserContext) -> Result { let lo = s.peek().span.lo; token(s, TokenKind::Keyword(Keyword::DefCalGrammar))?; + let next = s.peek(); - let v = expr::lit(s)?; + let lit = expr::lit(s)?; - if let Some(v) = v { - recovering_semi(s); - if let LiteralKind::String(v) = v.kind { + recovering_semi(s); + if let Some(lit) = lit { + if let LiteralKind::String(name) = lit.kind { return Ok(CalibrationGrammarStmt { span: s.span(lo), - name: v.to_string(), + name: name.to_string(), }); } }; Err(Error::new(ErrorKind::Rule( - "calibration grammar statement", - TokenKind::Literal(Literal::String), + "string literal", + next.kind, next.span, ))) } diff --git a/compiler/qsc_qasm3/src/parser/stmt/tests.rs b/compiler/qsc_qasm3/src/parser/stmt/tests.rs index 5e964c6104..0e3a2f74a9 100644 --- a/compiler/qsc_qasm3/src/parser/stmt/tests.rs +++ b/compiler/qsc_qasm3/src/parser/stmt/tests.rs @@ -18,6 +18,7 @@ mod gate_call; mod gate_def; mod gphase; mod if_stmt; +mod include; mod io_decl; mod measure; mod old_style_decl; diff --git a/compiler/qsc_qasm3/src/parser/stmt/tests/cal_grammar.rs b/compiler/qsc_qasm3/src/parser/stmt/tests/cal_grammar.rs index 0416e8bb03..f5ea7b2380 100644 --- a/compiler/qsc_qasm3/src/parser/stmt/tests/cal_grammar.rs +++ b/compiler/qsc_qasm3/src/parser/stmt/tests/cal_grammar.rs @@ -15,3 +15,47 @@ fn defcalgrammar() { StmtKind: CalibrationGrammarStmt [0-26]: openpulse"#]], ); } + +#[test] +fn defcalgrammar_with_non_string_literal() { + check( + parse, + r#"defcalgrammar 5;"#, + &expect![[r#" + Error( + Rule( + "string literal", + Literal( + Integer( + Decimal, + ), + ), + Span { + lo: 14, + hi: 15, + }, + ), + ) + "#]], + ); +} + +#[test] +fn defcalgrammar_with_no_literal() { + check( + parse, + r#"defcalgrammar;"#, + &expect![[r#" + Error( + Rule( + "string literal", + Semicolon, + Span { + lo: 13, + hi: 14, + }, + ), + ) + "#]], + ); +} diff --git a/compiler/qsc_qasm3/src/parser/stmt/tests/include.rs b/compiler/qsc_qasm3/src/parser/stmt/tests/include.rs new file mode 100644 index 0000000000..6bba818125 --- /dev/null +++ b/compiler/qsc_qasm3/src/parser/stmt/tests/include.rs @@ -0,0 +1,41 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +use crate::parser::{stmt::parse, tests::check}; +use expect_test::expect; + +#[test] +fn include_with_no_literal() { + check(parse, "include;", &expect![[r#" + Error( + Rule( + "string literal", + Semicolon, + Span { + lo: 7, + hi: 8, + }, + ), + ) + "#]]); +} + +#[test] +fn include_with_non_string_literal() { + check(parse, "include 5;", &expect![[r#" + Error( + Rule( + "string literal", + Literal( + Integer( + Decimal, + ), + ), + Span { + lo: 8, + hi: 9, + }, + ), + ) + "#]]); +} From 7ec3dbe34d0919a3a671a2a22c4735dae84a268d Mon Sep 17 00:00:00 2001 From: Oscar Puente <156957451+orpuente-MS@users.noreply.github.com> Date: Thu, 27 Feb 2025 13:01:52 -0800 Subject: [PATCH 038/108] PR review fixes --- compiler/qsc_qasm3/src/ast.rs | 10 +-- compiler/qsc_qasm3/src/parser/stmt.rs | 97 ++++++++++++++------------- 2 files changed, 54 insertions(+), 53 deletions(-) diff --git a/compiler/qsc_qasm3/src/ast.rs b/compiler/qsc_qasm3/src/ast.rs index 606d8d86ce..15f9fc028d 100644 --- a/compiler/qsc_qasm3/src/ast.rs +++ b/compiler/qsc_qasm3/src/ast.rs @@ -400,11 +400,11 @@ pub enum StmtKind { Cal(CalibrationStmt), CalibrationGrammar(CalibrationGrammarStmt), ClassicalDecl(ClassicalDeclarationStmt), - ConstDecl(ConstantDeclaration), + ConstDecl(ConstantDeclStmt), Continue(ContinueStmt), Def(DefStmt), DefCal(DefCalStmt), - DelayStmt(DelayStmt), + Delay(DelayStmt), /// An empty statement. Empty, End(EndStmt), @@ -447,7 +447,7 @@ impl Display for StmtKind { StmtKind::Continue(continue_stmt) => write!(f, "{continue_stmt}"), StmtKind::Def(def) => write!(f, "{def}"), StmtKind::DefCal(defcal) => write!(f, "{defcal}"), - StmtKind::DelayStmt(delay) => write!(f, "{delay}"), + StmtKind::Delay(delay) => write!(f, "{delay}"), StmtKind::Empty => write!(f, "Empty"), StmtKind::End(end_stmt) => write!(f, "{end_stmt}"), StmtKind::ExprStmt(expr) => write!(f, "{expr}"), @@ -1341,14 +1341,14 @@ impl Display for IODeclaration { } #[derive(Clone, Debug)] -pub struct ConstantDeclaration { +pub struct ConstantDeclStmt { pub span: Span, pub r#type: TypeDef, pub identifier: Box, pub init_expr: Expr, } -impl Display for ConstantDeclaration { +impl Display for ConstantDeclStmt { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { write!( f, diff --git a/compiler/qsc_qasm3/src/parser/stmt.rs b/compiler/qsc_qasm3/src/parser/stmt.rs index 9d682c6445..45639e812c 100644 --- a/compiler/qsc_qasm3/src/parser/stmt.rs +++ b/compiler/qsc_qasm3/src/parser/stmt.rs @@ -19,7 +19,7 @@ use crate::{ list_from_iter, AccessControl, AliasDeclStmt, AngleType, Annotation, ArrayBaseTypeKind, ArrayReferenceType, ArrayType, BarrierStmt, BitType, Block, BoxStmt, BreakStmt, CalibrationGrammarStmt, CalibrationStmt, ClassicalDeclarationStmt, ComplexType, - ConstantDeclaration, ContinueStmt, DefCalStmt, DefStmt, DelayStmt, EndStmt, EnumerableSet, + ConstantDeclStmt, ContinueStmt, DefCalStmt, DefStmt, DelayStmt, EndStmt, EnumerableSet, Expr, ExprKind, ExprStmt, ExternDecl, ExternParameter, FloatType, ForStmt, FunctionCall, GPhase, GateCall, GateModifierKind, GateOperand, IODeclaration, IOKeyword, Ident, Identifier, IfStmt, IncludeStmt, IntType, List, LiteralKind, MeasureStmt, Pragma, @@ -28,10 +28,7 @@ use crate::{ TypedParameter, UIntType, WhileLoop, }, keyword::Keyword, - lex::{ - cooked::{Literal, Type}, - Delim, TokenKind, - }, + lex::{cooked::Type, Delim, TokenKind}, }; use super::{prim::token, ParserContext}; @@ -97,7 +94,7 @@ pub(super) fn parse(s: &mut ParserContext) -> Result> { } else if let Some(stmt) = opt(s, parse_barrier)? { Box::new(StmtKind::Barrier(stmt)) } else if let Some(stmt) = opt(s, parse_delay)? { - Box::new(StmtKind::DelayStmt(stmt)) + Box::new(StmtKind::Delay(stmt)) } else if let Some(stmt) = opt(s, parse_reset)? { Box::new(StmtKind::Reset(stmt)) } else if let Some(stmt) = opt(s, parse_measure_stmt)? { @@ -539,7 +536,7 @@ fn parse_classical_decl(s: &mut ParserContext) -> Result { token(s, TokenKind::Eq)?; let init_expr = expr::expr(s)?; recovering_semi(s); - let decl = ConstantDeclaration { + let decl = ConstantDeclStmt { span: s.span(lo), r#type: ty, identifier, @@ -1093,24 +1090,29 @@ fn parse_alias_stmt(s: &mut ParserContext) -> Result { }) } -fn parse_boxable_stmt(s: &mut ParserContext) -> Result { - let stmt = *parse(s)?; - match &*stmt.kind { - StmtKind::Barrier(_) - | StmtKind::Break(_) - | StmtKind::DelayStmt(_) - | StmtKind::Reset(_) - | StmtKind::GateCall(_) - | StmtKind::GPhase(_) => Ok(stmt), - _ => { - s.push_error(Error::new(ErrorKind::ClassicalStmtInBox(stmt.span))); - Ok(Stmt { - span: stmt.span, - annotations: stmt.annotations, - kind: Box::new(StmtKind::Err), - }) - } - } +/// Grammar: `BOX designator? scope`. +fn parse_box(s: &mut ParserContext) -> Result { + let lo = s.peek().span.lo; + token(s, TokenKind::Keyword(Keyword::Box))?; + let duration = opt(s, designator)?; + let body = parse_box_body(s)?; + + Ok(BoxStmt { + span: s.span(lo), + duration, + body, + }) +} + +fn parse_box_body(s: &mut ParserContext) -> Result> { + token(s, TokenKind::Open(Delim::Brace))?; + let stmts = barrier( + s, + &[TokenKind::Close(Delim::Brace)], + parse_many_boxable_stmt, + )?; + recovering_token(s, TokenKind::Close(Delim::Brace)); + Ok(stmts) } fn parse_many_boxable_stmt(s: &mut ParserContext) -> Result> { @@ -1130,15 +1132,28 @@ fn parse_many_boxable_stmt(s: &mut ParserContext) -> Result> { Ok(list_from_iter(stmts?)) } -fn parse_box_body(s: &mut ParserContext) -> Result> { - token(s, TokenKind::Open(Delim::Brace))?; - let stmts = barrier( - s, - &[TokenKind::Close(Delim::Brace)], - parse_many_boxable_stmt, - )?; - recovering_token(s, TokenKind::Close(Delim::Brace)); - Ok(stmts) +/// These "boxable" stmts were taken from the reference parser at +/// . +/// Search for the definition of `Box` there, and then for all the classes +/// inhereting from `QuantumStatement`. +fn parse_boxable_stmt(s: &mut ParserContext) -> Result { + let stmt = *parse(s)?; + match &*stmt.kind { + StmtKind::Barrier(_) + | StmtKind::Delay(_) + | StmtKind::Reset(_) + | StmtKind::GateCall(_) + | StmtKind::GPhase(_) + | StmtKind::Box(_) => Ok(stmt), + _ => { + s.push_error(Error::new(ErrorKind::ClassicalStmtInBox(stmt.span))); + Ok(Stmt { + span: stmt.span, + annotations: stmt.annotations, + kind: Box::new(StmtKind::Err), + }) + } + } } /// In QASM3, it is hard to disambiguate between a quantum-gate-call missing modifiers @@ -1409,20 +1424,6 @@ fn parse_barrier(s: &mut ParserContext) -> Result { }) } -/// Grammar: `BOX designator? scope`. -fn parse_box(s: &mut ParserContext) -> Result { - let lo = s.peek().span.lo; - token(s, TokenKind::Keyword(Keyword::Box))?; - let duration = opt(s, designator)?; - let body = parse_box_body(s)?; - - Ok(BoxStmt { - span: s.span(lo), - duration, - body, - }) -} - /// Grammar: `DELAY designator gateOperandList? SEMICOLON`. fn parse_delay(s: &mut ParserContext) -> Result { let lo = s.peek().span.lo; From 7860075912479b306a712e5f552141572e4fa8c7 Mon Sep 17 00:00:00 2001 From: Oscar Puente <156957451+orpuente-MS@users.noreply.github.com> Date: Thu, 27 Feb 2025 16:16:41 -0800 Subject: [PATCH 039/108] fixes during PR review --- .../src/parser/stmt/tests/gate_call.rs | 61 +++++++++++++++++++ 1 file changed, 61 insertions(+) diff --git a/compiler/qsc_qasm3/src/parser/stmt/tests/gate_call.rs b/compiler/qsc_qasm3/src/parser/stmt/tests/gate_call.rs index 4e273cdc96..a2d3abc2bb 100644 --- a/compiler/qsc_qasm3/src/parser/stmt/tests/gate_call.rs +++ b/compiler/qsc_qasm3/src/parser/stmt/tests/gate_call.rs @@ -111,3 +111,64 @@ fn gate_call_ctrl_inv_modifiers() { GateOperand IndexedIdent [35-37]: Ident [35-37] "q0"[]"#]], ); } + +#[test] +fn function_call_plus_ident() { + check(parse, "Name(2, 3) + a;", &expect![]); +} + +#[test] +fn function_call() { + check(parse, "Name(2, 3);", &expect![]); +} + +#[test] +fn indexed_function_call() { + check(parse, "Name(2, 3)[1];", &expect![]); +} + +#[test] +fn multi_indexed_function_call() { + check(parse, "Name(2, 3)[1, 0];", &expect![]); +} + +#[test] +fn ident() { + check(parse, "Name;", &expect![]); +} + +#[test] +fn gate_call_with_designator() { + check( + parse, + "H[2us] q;", + &expect![[r#" + Stmt [0-9] + StmtKind: GateCall [0-9]: Ident [0-1] "H" + GateOperand IndexedIdent [7-8]: Ident [7-8] "q"[] + Expr [2-5]: Lit: Duration(2.0, Us)"#]], + ); +} + +#[test] +fn gate_call_with_invalid_designator() { + check( + parse, + "H[2us][3] q;", + &expect![[r#" + Error( + InvalidGateCallDesignator( + Span { + lo: 6, + hi: 9, + }, + ), + ) + "#]], + ); +} + +#[test] +fn index_expr() { + check(parse, "Name[1];", &expect![]); +} From b370e92757283537523f24be4762c50e78bb5507 Mon Sep 17 00:00:00 2001 From: Oscar Puente <156957451+orpuente-MS@users.noreply.github.com> Date: Thu, 27 Feb 2025 16:16:51 -0800 Subject: [PATCH 040/108] fixes during PR review --- compiler/qsc_qasm3/src/ast.rs | 4 +- compiler/qsc_qasm3/src/lex/cooked.rs | 6 -- compiler/qsc_qasm3/src/lex/cooked/tests.rs | 2 - compiler/qsc_qasm3/src/parser/error.rs | 4 + compiler/qsc_qasm3/src/parser/expr.rs | 2 +- compiler/qsc_qasm3/src/parser/stmt.rs | 82 ++++++++++++++++--- .../src/parser/stmt/tests/gate_call.rs | 38 +++++++-- 7 files changed, 109 insertions(+), 29 deletions(-) diff --git a/compiler/qsc_qasm3/src/ast.rs b/compiler/qsc_qasm3/src/ast.rs index 15f9fc028d..d01e08b36d 100644 --- a/compiler/qsc_qasm3/src/ast.rs +++ b/compiler/qsc_qasm3/src/ast.rs @@ -1171,7 +1171,7 @@ impl Display for ExternDecl { pub struct GateCall { pub span: Span, pub modifiers: List, - pub name: Identifier, + pub name: Ident, pub args: List, pub qubits: List, pub duration: Option, @@ -1768,7 +1768,7 @@ impl Display for BinaryOpExpr { #[derive(Clone, Debug)] pub struct FunctionCall { pub span: Span, - pub name: Identifier, + pub name: Ident, pub args: List, } diff --git a/compiler/qsc_qasm3/src/lex/cooked.rs b/compiler/qsc_qasm3/src/lex/cooked.rs index e15544f0e5..c891ef4519 100644 --- a/compiler/qsc_qasm3/src/lex/cooked.rs +++ b/compiler/qsc_qasm3/src/lex/cooked.rs @@ -100,8 +100,6 @@ pub enum TokenKind { NegCtrl, Dim, DurationOf, - Delay, - Reset, Measure, Literal(Literal), @@ -159,8 +157,6 @@ impl Display for TokenKind { TokenKind::NegCtrl => write!(f, "negctrl"), TokenKind::Dim => write!(f, "dim"), TokenKind::DurationOf => write!(f, "durationof"), - TokenKind::Delay => write!(f, "delay"), - TokenKind::Reset => write!(f, "reset"), TokenKind::Measure => write!(f, "measure"), TokenKind::Literal(literal) => write!(f, "literal `{literal}`"), TokenKind::Open(Delim::Brace) => write!(f, "`{{`"), @@ -749,8 +745,6 @@ impl<'a> Lexer<'a> { "negctrl" => TokenKind::NegCtrl, "dim" => TokenKind::Dim, "durationof" => TokenKind::DurationOf, - "delay" => TokenKind::Delay, - "reset" => TokenKind::Reset, "measure" => TokenKind::Measure, ident => { if let Ok(keyword) = ident.parse::() { diff --git a/compiler/qsc_qasm3/src/lex/cooked/tests.rs b/compiler/qsc_qasm3/src/lex/cooked/tests.rs index 03d169d82f..89d2bf0b5a 100644 --- a/compiler/qsc_qasm3/src/lex/cooked/tests.rs +++ b/compiler/qsc_qasm3/src/lex/cooked/tests.rs @@ -35,8 +35,6 @@ fn op_string(kind: TokenKind) -> Option { TokenKind::NegCtrl => Some("negctrl".to_string()), TokenKind::Dim => Some("dim".to_string()), TokenKind::DurationOf => Some("durationof".to_string()), - TokenKind::Delay => Some("delay".to_string()), - TokenKind::Reset => Some("reset".to_string()), TokenKind::Measure => Some("measure".to_string()), TokenKind::Semicolon => Some(";".to_string()), TokenKind::Arrow => Some("->".to_string()), diff --git a/compiler/qsc_qasm3/src/parser/error.rs b/compiler/qsc_qasm3/src/parser/error.rs index bfa8a30a98..ce4d35dfe6 100644 --- a/compiler/qsc_qasm3/src/parser/error.rs +++ b/compiler/qsc_qasm3/src/parser/error.rs @@ -131,6 +131,9 @@ pub enum ErrorKind { #[error("gphase gate requires exactly one angle")] #[diagnostic(code("Qasm3.Parse.GPhaseInvalidArguments"))] GPhaseInvalidArguments(#[label] Span), + #[error("invalid gate call designator")] + #[diagnostic(code("Qasm3.Parse.InvalidGateCallDesignator"))] + InvalidGateCallDesignator(#[label] Span), } impl ErrorKind { @@ -152,6 +155,7 @@ impl ErrorKind { Self::MissingGateCallOperands(span) => Self::MissingGateCallOperands(span + offset), Self::ExpectedItem(token, span) => Self::ExpectedItem(token, span + offset), Self::GPhaseInvalidArguments(span) => Self::GPhaseInvalidArguments(span + offset), + Self::InvalidGateCallDesignator(span) => Self::InvalidGateCallDesignator(span + offset), } } } diff --git a/compiler/qsc_qasm3/src/parser/expr.rs b/compiler/qsc_qasm3/src/parser/expr.rs index 5f7cd47cc1..139b494892 100644 --- a/compiler/qsc_qasm3/src/parser/expr.rs +++ b/compiler/qsc_qasm3/src/parser/expr.rs @@ -462,7 +462,7 @@ fn funcall(s: &mut ParserContext, ident: ast::Ident) -> Result { token(s, TokenKind::Close(Delim::Paren))?; Ok(ExprKind::FunctionCall(FunctionCall { span: s.span(lo), - name: ast::Identifier::Ident(Box::new(ident)), + name: ident, args: args.into_iter().map(Box::new).collect(), })) } diff --git a/compiler/qsc_qasm3/src/parser/stmt.rs b/compiler/qsc_qasm3/src/parser/stmt.rs index 45639e812c..e2240c583f 100644 --- a/compiler/qsc_qasm3/src/parser/stmt.rs +++ b/compiler/qsc_qasm3/src/parser/stmt.rs @@ -22,10 +22,10 @@ use crate::{ ConstantDeclStmt, ContinueStmt, DefCalStmt, DefStmt, DelayStmt, EndStmt, EnumerableSet, Expr, ExprKind, ExprStmt, ExternDecl, ExternParameter, FloatType, ForStmt, FunctionCall, GPhase, GateCall, GateModifierKind, GateOperand, IODeclaration, IOKeyword, Ident, - Identifier, IfStmt, IncludeStmt, IntType, List, LiteralKind, MeasureStmt, Pragma, - QuantumGateDefinition, QuantumGateModifier, QubitDeclaration, RangeDefinition, ResetStmt, - ReturnStmt, ScalarType, ScalarTypeKind, Stmt, StmtKind, SwitchStmt, TypeDef, - TypedParameter, UIntType, WhileLoop, + Identifier, IfStmt, IncludeStmt, IndexElement, IndexExpr, IndexSetItem, IntType, List, + LiteralKind, MeasureStmt, Pragma, QuantumGateDefinition, QuantumGateModifier, + QubitDeclaration, RangeDefinition, ResetStmt, ReturnStmt, ScalarType, ScalarTypeKind, Stmt, + StmtKind, SwitchStmt, TypeDef, TypedParameter, UIntType, WhileLoop, }, keyword::Keyword, lex::{cooked::Type, Delim, TokenKind}, @@ -1158,10 +1158,14 @@ fn parse_boxable_stmt(s: &mut ParserContext) -> Result { /// In QASM3, it is hard to disambiguate between a quantum-gate-call missing modifiers /// and expression statements. Consider the following expressions: -/// 1. `Rx(2, 3) q0;` -/// 2. `Rx(2, 3) + q0;` -/// 3. `Rx(2, 3);` -/// 4. `Rx;` +/// 1. `Ident(2, 3) a;` +/// 2. `Ident(2, 3) + a * b;` +/// 3. `Ident(2, 3);` +/// 4. `Ident(2, 3)[1];` +/// 5. `Ident;` +/// 6. `Ident[4us] q;` +/// 7. `Ident[4];` +/// 8. `Ident q;` /// /// (1) is a quantum-gate-call, (2) is a binary operation, (3) is a function call, and /// (4) is an identifer. We don't know for sure until we see the what is beyond the gate @@ -1184,10 +1188,19 @@ fn parse_gate_call_stmt(s: &mut ParserContext) -> Result { return Ok(StmtKind::GPhase(gphase)); } + // 1. ident = ... + // 2. parameters? = ... Option + // 3. designator? = ... + // 4. qubits = ... -> qubits.is_empty() + + // cases: (no qubits) + // ident + parameters -> function call + // ident + designator -> indexed ident + // As explained in the docstring, we parse the gate using the expr parser. let gate_or_expr = expr::expr(s)?; - let duration = opt(s, designator)?; + let mut duration = opt(s, designator)?; let qubits = gate_operand_list(s)?; recovering_semi(s); @@ -1199,9 +1212,11 @@ fn parse_gate_call_stmt(s: &mut ParserContext) -> Result { })); } + // Reinterpret the function call or ident as a gate call. let (name, args) = match *gate_or_expr.kind { ExprKind::FunctionCall(FunctionCall { name, args, .. }) => (name, args), - ExprKind::Ident(ident) => (Identifier::Ident(Box::new(ident)), Default::default()), + ExprKind::Ident(ident) => (ident, Default::default()), + ExprKind::IndexExpr(index_expr) => reinterpret_index_expr(index_expr, &mut duration)?, _ => { return Err(Error::new(ErrorKind::ExpectedItem( TokenKind::Identifier, @@ -1224,6 +1239,49 @@ fn parse_gate_call_stmt(s: &mut ParserContext) -> Result { })) } +/// This helper function reinterprets an indexed expression as +/// a gate call. There are two valid cases we are interested in: +/// 1. Ident[4] +/// 2. Ident(2, 3)[4] +/// +/// Case (1) is an indexed identifier, in which case we want to +/// reinterpret it as a gate followed by a designator. +/// Case (2) is an indexed function call, in which case we want to +/// reinterpret it as a parametrized gate followed by a designator. +fn reinterpret_index_expr( + index_expr: IndexExpr, + duration: &mut Option, +) -> Result<(Ident, List)> { + let IndexExpr { + collection, index, .. + } = index_expr; + + if let IndexElement::IndexSet(set) = index { + if set.len() == 1 { + let first_elt: IndexSetItem = (*set[0]).clone(); + if let IndexSetItem::Expr(expr) = first_elt { + if duration.is_none() { + match *collection.kind { + ExprKind::Ident(name) => { + *duration = Some(expr); + return Ok((name, Default::default())); + } + ExprKind::FunctionCall(FunctionCall { name, args, .. }) => { + *duration = Some(expr); + return Ok((name, args)); + } + _ => (), + } + } + } + } + } + + Err(Error::new(ErrorKind::InvalidGateCallDesignator( + index_expr.span, + ))) +} + fn parse_gphase( s: &mut ParserContext, lo: u32, @@ -1427,7 +1485,7 @@ fn parse_barrier(s: &mut ParserContext) -> Result { /// Grammar: `DELAY designator gateOperandList? SEMICOLON`. fn parse_delay(s: &mut ParserContext) -> Result { let lo = s.peek().span.lo; - token(s, TokenKind::Delay)?; + token(s, TokenKind::Keyword(Keyword::Delay))?; let duration = designator(s)?; let qubits = gate_operand_list(s)?; recovering_semi(s); @@ -1442,7 +1500,7 @@ fn parse_delay(s: &mut ParserContext) -> Result { /// Grammar: `RESET gateOperand SEMICOLON`. fn parse_reset(s: &mut ParserContext) -> Result { let lo = s.peek().span.lo; - token(s, TokenKind::Reset)?; + token(s, TokenKind::Keyword(Keyword::Reset))?; let operand = Box::new(gate_operand(s)?); recovering_semi(s); diff --git a/compiler/qsc_qasm3/src/parser/stmt/tests/gate_call.rs b/compiler/qsc_qasm3/src/parser/stmt/tests/gate_call.rs index a2d3abc2bb..4f91d074cb 100644 --- a/compiler/qsc_qasm3/src/parser/stmt/tests/gate_call.rs +++ b/compiler/qsc_qasm3/src/parser/stmt/tests/gate_call.rs @@ -114,27 +114,50 @@ fn gate_call_ctrl_inv_modifiers() { #[test] fn function_call_plus_ident() { - check(parse, "Name(2, 3) + a;", &expect![]); + check(parse, "Name(2, 3) + a;", &expect![[r#" + Stmt [0-15] + StmtKind: ExprStmt [0-15]: Expr [0-14]: BinOp (Add): + Expr [0-10]: FunctionCall [0-10]: Ident [0-4] "Name" + Expr [5-6]: Lit: Int(2) + Expr [8-9]: Lit: Int(3) + Expr [13-14]: Ident [13-14] "a""#]]); } #[test] fn function_call() { - check(parse, "Name(2, 3);", &expect![]); + check(parse, "Name(2, 3);", &expect![[r#" + Stmt [0-11] + StmtKind: ExprStmt [0-11]: Expr [0-10]: FunctionCall [0-10]: Ident [0-4] "Name" + Expr [5-6]: Lit: Int(2) + Expr [8-9]: Lit: Int(3)"#]]); } #[test] fn indexed_function_call() { - check(parse, "Name(2, 3)[1];", &expect![]); + check(parse, "Name(2, 3)[1];", &expect![[r#" + Stmt [0-14] + StmtKind: ExprStmt [0-14]: Expr [0-13]: IndexExpr [10-13]: Expr [0-10]: FunctionCall [0-10]: Ident [0-4] "Name" + Expr [5-6]: Lit: Int(2) + Expr [8-9]: Lit: Int(3), IndexElement: + IndexSetItem Expr [11-12]: Lit: Int(1)"#]]); } #[test] fn multi_indexed_function_call() { - check(parse, "Name(2, 3)[1, 0];", &expect![]); + check(parse, "Name(2, 3)[1, 0];", &expect![[r#" + Stmt [0-17] + StmtKind: ExprStmt [0-17]: Expr [0-16]: IndexExpr [10-16]: Expr [0-10]: FunctionCall [0-10]: Ident [0-4] "Name" + Expr [5-6]: Lit: Int(2) + Expr [8-9]: Lit: Int(3), IndexElement: + IndexSetItem Expr [11-12]: Lit: Int(1) + IndexSetItem Expr [14-15]: Lit: Int(0)"#]]); } #[test] fn ident() { - check(parse, "Name;", &expect![]); + check(parse, "Name;", &expect![[r#" + Stmt [0-5] + StmtKind: ExprStmt [0-5]: Expr [0-4]: Ident [0-4] "Name""#]]); } #[test] @@ -170,5 +193,8 @@ fn gate_call_with_invalid_designator() { #[test] fn index_expr() { - check(parse, "Name[1];", &expect![]); + check(parse, "Name[1];", &expect![[r#" + Stmt [0-8] + StmtKind: ExprStmt [0-8]: Expr [0-7]: IndexExpr [4-7]: Expr [0-4]: Ident [0-4] "Name", IndexElement: + IndexSetItem Expr [5-6]: Lit: Int(1)"#]]); } From 2ebcec21ca7c2e5e2c8d75c94c40e71afd3d70c9 Mon Sep 17 00:00:00 2001 From: Oscar Puente <156957451+orpuente-MS@users.noreply.github.com> Date: Fri, 28 Feb 2025 00:07:52 -0800 Subject: [PATCH 041/108] add unit tests for gate_call --- .../src/parser/stmt/tests/expr_stmt.rs | 122 +++++++++++++++++- .../src/parser/stmt/tests/gate_call.rs | 49 +++---- 2 files changed, 143 insertions(+), 28 deletions(-) diff --git a/compiler/qsc_qasm3/src/parser/stmt/tests/expr_stmt.rs b/compiler/qsc_qasm3/src/parser/stmt/tests/expr_stmt.rs index 964a6f3e76..20af84e5d1 100644 --- a/compiler/qsc_qasm3/src/parser/stmt/tests/expr_stmt.rs +++ b/compiler/qsc_qasm3/src/parser/stmt/tests/expr_stmt.rs @@ -29,14 +29,128 @@ fn identifier_plus_number() { ); } +// These are negative unit tests for gate calls: + +#[test] +fn function_call_plus_ident() { + check( + parse, + "Name(2, 3) + a;", + &expect![[r#" + Stmt [0-15] + StmtKind: ExprStmt [0-15]: Expr [0-14]: BinOp (Add): + Expr [0-10]: FunctionCall [0-10]: Ident [0-4] "Name" + Expr [5-6]: Lit: Int(2) + Expr [8-9]: Lit: Int(3) + Expr [13-14]: Ident [13-14] "a""#]], + ); +} + #[test] fn function_call() { check( parse, - "f(2);", + "Name(2, 3);", + &expect![[r#" + Stmt [0-11] + StmtKind: ExprStmt [0-11]: Expr [0-10]: FunctionCall [0-10]: Ident [0-4] "Name" + Expr [5-6]: Lit: Int(2) + Expr [8-9]: Lit: Int(3)"#]], + ); +} + +#[test] +fn indexed_function_call() { + check( + parse, + "Name(2, 3)[1];", + &expect![[r#" + Stmt [0-14] + StmtKind: ExprStmt [0-14]: Expr [0-13]: IndexExpr [10-13]: Expr [0-10]: FunctionCall [0-10]: Ident [0-4] "Name" + Expr [5-6]: Lit: Int(2) + Expr [8-9]: Lit: Int(3), IndexElement: + IndexSetItem Expr [11-12]: Lit: Int(1)"#]], + ); +} + +#[test] +fn multi_indexed_function_call() { + check( + parse, + "Name(2, 3)[1, 0];", + &expect![[r#" + Stmt [0-17] + StmtKind: ExprStmt [0-17]: Expr [0-16]: IndexExpr [10-16]: Expr [0-10]: FunctionCall [0-10]: Ident [0-4] "Name" + Expr [5-6]: Lit: Int(2) + Expr [8-9]: Lit: Int(3), IndexElement: + IndexSetItem Expr [11-12]: Lit: Int(1) + IndexSetItem Expr [14-15]: Lit: Int(0)"#]], + ); +} + +#[test] +fn ident() { + check( + parse, + "Name;", + &expect![[r#" + Stmt [0-5] + StmtKind: ExprStmt [0-5]: Expr [0-4]: Ident [0-4] "Name""#]], + ); +} + +#[test] +fn index_expr() { + check( + parse, + "Name[1];", + &expect![[r#" + Stmt [0-8] + StmtKind: ExprStmt [0-8]: Expr [0-7]: IndexExpr [4-7]: Expr [0-4]: Ident [0-4] "Name", IndexElement: + IndexSetItem Expr [5-6]: Lit: Int(1)"#]], + ); +} + +#[test] +fn cast_expr() { + check( + parse, + "bit(0);", + &expect![[r#" + Error( + Rule( + "identifier", + Open( + Paren, + ), + Span { + lo: 3, + hi: 4, + }, + ), + ) + "#]], + ); +} + +#[test] +fn cast_expr_with_designator() { + check( + parse, + "bit[45](0);", &expect![[r#" - Stmt [0-5] - StmtKind: ExprStmt [0-5]: Expr [0-4]: FunctionCall [0-4]: Ident [0-1] "f" - Expr [2-3]: Lit: Int(2)"#]], + Error( + Rule( + "identifier", + Open( + Paren, + ), + Span { + lo: 7, + hi: 8, + }, + ), + ) + "#]], ); } diff --git a/compiler/qsc_qasm3/src/parser/stmt/tests/gate_call.rs b/compiler/qsc_qasm3/src/parser/stmt/tests/gate_call.rs index 4f91d074cb..a155b823f0 100644 --- a/compiler/qsc_qasm3/src/parser/stmt/tests/gate_call.rs +++ b/compiler/qsc_qasm3/src/parser/stmt/tests/gate_call.rs @@ -114,50 +114,59 @@ fn gate_call_ctrl_inv_modifiers() { #[test] fn function_call_plus_ident() { - check(parse, "Name(2, 3) + a;", &expect![[r#" + check( + parse, + "Name(2, 3) + a q;", + &expect![[r#" Stmt [0-15] StmtKind: ExprStmt [0-15]: Expr [0-14]: BinOp (Add): Expr [0-10]: FunctionCall [0-10]: Ident [0-4] "Name" Expr [5-6]: Lit: Int(2) Expr [8-9]: Lit: Int(3) - Expr [13-14]: Ident [13-14] "a""#]]); + Expr [13-14]: Ident [13-14] "a""#]], + ); } #[test] fn function_call() { - check(parse, "Name(2, 3);", &expect![[r#" + check( + parse, + "Name(2, 3) q;", + &expect![[r#" Stmt [0-11] StmtKind: ExprStmt [0-11]: Expr [0-10]: FunctionCall [0-10]: Ident [0-4] "Name" Expr [5-6]: Lit: Int(2) - Expr [8-9]: Lit: Int(3)"#]]); + Expr [8-9]: Lit: Int(3)"#]], + ); } #[test] fn indexed_function_call() { - check(parse, "Name(2, 3)[1];", &expect![[r#" + check( + parse, + "Name(2, 3)[1] q;", + &expect![[r#" Stmt [0-14] StmtKind: ExprStmt [0-14]: Expr [0-13]: IndexExpr [10-13]: Expr [0-10]: FunctionCall [0-10]: Ident [0-4] "Name" Expr [5-6]: Lit: Int(2) Expr [8-9]: Lit: Int(3), IndexElement: - IndexSetItem Expr [11-12]: Lit: Int(1)"#]]); + IndexSetItem Expr [11-12]: Lit: Int(1)"#]], + ); } #[test] -fn multi_indexed_function_call() { - check(parse, "Name(2, 3)[1, 0];", &expect![[r#" +fn multi_indexed_gate_call() { + check( + parse, + "Name(2, 3)[1, 0] q;", + &expect![[r#" Stmt [0-17] StmtKind: ExprStmt [0-17]: Expr [0-16]: IndexExpr [10-16]: Expr [0-10]: FunctionCall [0-10]: Ident [0-4] "Name" Expr [5-6]: Lit: Int(2) Expr [8-9]: Lit: Int(3), IndexElement: IndexSetItem Expr [11-12]: Lit: Int(1) - IndexSetItem Expr [14-15]: Lit: Int(0)"#]]); -} - -#[test] -fn ident() { - check(parse, "Name;", &expect![[r#" - Stmt [0-5] - StmtKind: ExprStmt [0-5]: Expr [0-4]: Ident [0-4] "Name""#]]); + IndexSetItem Expr [14-15]: Lit: Int(0)"#]], + ); } #[test] @@ -190,11 +199,3 @@ fn gate_call_with_invalid_designator() { "#]], ); } - -#[test] -fn index_expr() { - check(parse, "Name[1];", &expect![[r#" - Stmt [0-8] - StmtKind: ExprStmt [0-8]: Expr [0-7]: IndexExpr [4-7]: Expr [0-4]: Ident [0-4] "Name", IndexElement: - IndexSetItem Expr [5-6]: Lit: Int(1)"#]]); -} From e7d5da8adc5d8c16898bc26ea531bb18a5683d16 Mon Sep 17 00:00:00 2001 From: Oscar Puente <156957451+orpuente-MS@users.noreply.github.com> Date: Fri, 28 Feb 2025 09:24:38 -0800 Subject: [PATCH 042/108] change unit tests names --- .../src/parser/stmt/tests/gate_call.rs | 55 +++++++++++-------- 1 file changed, 32 insertions(+), 23 deletions(-) diff --git a/compiler/qsc_qasm3/src/parser/stmt/tests/gate_call.rs b/compiler/qsc_qasm3/src/parser/stmt/tests/gate_call.rs index a155b823f0..98af770cd0 100644 --- a/compiler/qsc_qasm3/src/parser/stmt/tests/gate_call.rs +++ b/compiler/qsc_qasm3/src/parser/stmt/tests/gate_call.rs @@ -47,7 +47,7 @@ fn gate_multiple_qubits() { } #[test] -fn gate_no_qubits() { +fn gate_with_no_qubits() { check( parse, "inv @ H;", @@ -113,44 +113,50 @@ fn gate_call_ctrl_inv_modifiers() { } #[test] -fn function_call_plus_ident() { +fn binary_expr_qubit() { check( parse, "Name(2, 3) + a q;", &expect![[r#" - Stmt [0-15] - StmtKind: ExprStmt [0-15]: Expr [0-14]: BinOp (Add): - Expr [0-10]: FunctionCall [0-10]: Ident [0-4] "Name" - Expr [5-6]: Lit: Int(2) - Expr [8-9]: Lit: Int(3) - Expr [13-14]: Ident [13-14] "a""#]], + Error( + ExpectedItem( + Identifier, + Span { + lo: 0, + hi: 14, + }, + ), + ) + "#]], ); } #[test] -fn function_call() { +fn parametrized_gate_call() { check( parse, "Name(2, 3) q;", &expect![[r#" - Stmt [0-11] - StmtKind: ExprStmt [0-11]: Expr [0-10]: FunctionCall [0-10]: Ident [0-4] "Name" + Stmt [0-13] + StmtKind: GateCall [0-13]: Ident [0-4] "Name" Expr [5-6]: Lit: Int(2) - Expr [8-9]: Lit: Int(3)"#]], + Expr [8-9]: Lit: Int(3) + GateOperand IndexedIdent [11-12]: Ident [11-12] "q"[]"#]], ); } #[test] -fn indexed_function_call() { +fn parametrized_gate_call_with_designator() { check( parse, "Name(2, 3)[1] q;", &expect![[r#" - Stmt [0-14] - StmtKind: ExprStmt [0-14]: Expr [0-13]: IndexExpr [10-13]: Expr [0-10]: FunctionCall [0-10]: Ident [0-4] "Name" + Stmt [0-16] + StmtKind: GateCall [0-16]: Ident [0-4] "Name" Expr [5-6]: Lit: Int(2) - Expr [8-9]: Lit: Int(3), IndexElement: - IndexSetItem Expr [11-12]: Lit: Int(1)"#]], + Expr [8-9]: Lit: Int(3) + GateOperand IndexedIdent [14-15]: Ident [14-15] "q"[] + Expr [11-12]: Lit: Int(1)"#]], ); } @@ -160,12 +166,15 @@ fn multi_indexed_gate_call() { parse, "Name(2, 3)[1, 0] q;", &expect![[r#" - Stmt [0-17] - StmtKind: ExprStmt [0-17]: Expr [0-16]: IndexExpr [10-16]: Expr [0-10]: FunctionCall [0-10]: Ident [0-4] "Name" - Expr [5-6]: Lit: Int(2) - Expr [8-9]: Lit: Int(3), IndexElement: - IndexSetItem Expr [11-12]: Lit: Int(1) - IndexSetItem Expr [14-15]: Lit: Int(0)"#]], + Error( + InvalidGateCallDesignator( + Span { + lo: 10, + hi: 16, + }, + ), + ) + "#]], ); } From 77ccb8036ba48de00598c0626ac8bc40801a8637 Mon Sep 17 00:00:00 2001 From: Oscar Puente <156957451+orpuente-MS@users.noreply.github.com> Date: Fri, 28 Feb 2025 09:35:00 -0800 Subject: [PATCH 043/108] format file --- .../qsc_qasm3/src/parser/stmt/tests/include.rs | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/compiler/qsc_qasm3/src/parser/stmt/tests/include.rs b/compiler/qsc_qasm3/src/parser/stmt/tests/include.rs index 6bba818125..6acdd8c0b7 100644 --- a/compiler/qsc_qasm3/src/parser/stmt/tests/include.rs +++ b/compiler/qsc_qasm3/src/parser/stmt/tests/include.rs @@ -6,7 +6,10 @@ use expect_test::expect; #[test] fn include_with_no_literal() { - check(parse, "include;", &expect![[r#" + check( + parse, + "include;", + &expect![[r#" Error( Rule( "string literal", @@ -17,12 +20,16 @@ fn include_with_no_literal() { }, ), ) - "#]]); + "#]], + ); } #[test] fn include_with_non_string_literal() { - check(parse, "include 5;", &expect![[r#" + check( + parse, + "include 5;", + &expect![[r#" Error( Rule( "string literal", @@ -37,5 +44,6 @@ fn include_with_non_string_literal() { }, ), ) - "#]]); + "#]], + ); } From 1ccd516cb897d892d0291112571dad80391052f4 Mon Sep 17 00:00:00 2001 From: Oscar Puente <156957451+orpuente-MS@users.noreply.github.com> Date: Fri, 28 Feb 2025 13:39:34 -0800 Subject: [PATCH 044/108] disambiguate between cast expr_stmts and classical decls --- compiler/qsc_qasm3/src/ast.rs | 10 ++ .../qsc_qasm3/src/parser/completion/tests.rs | 4 +- compiler/qsc_qasm3/src/parser/expr.rs | 41 +++-- compiler/qsc_qasm3/src/parser/stmt.rs | 144 ++++++++++-------- .../src/parser/stmt/tests/expr_stmt.rs | 34 +---- .../src/parser/stmt/tests/if_stmt.rs | 35 ++--- .../src/parser/stmt/tests/switch_stmt.rs | 18 +-- .../src/parser/stmt/tests/while_loops.rs | 28 ++-- 8 files changed, 148 insertions(+), 166 deletions(-) diff --git a/compiler/qsc_qasm3/src/ast.rs b/compiler/qsc_qasm3/src/ast.rs index d01e08b36d..295a59f16f 100644 --- a/compiler/qsc_qasm3/src/ast.rs +++ b/compiler/qsc_qasm3/src/ast.rs @@ -957,6 +957,16 @@ pub enum TypeDef { ArrayReference(ArrayReferenceType), } +impl TypeDef { + pub fn span(&self) -> Span { + match self { + TypeDef::Scalar(ident) => ident.span, + TypeDef::Array(array) => array.span, + TypeDef::ArrayReference(array) => array.span, + } + } +} + impl Display for TypeDef { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { match self { diff --git a/compiler/qsc_qasm3/src/parser/completion/tests.rs b/compiler/qsc_qasm3/src/parser/completion/tests.rs index c4d70fab6e..13d3ebe40b 100644 --- a/compiler/qsc_qasm3/src/parser/completion/tests.rs +++ b/compiler/qsc_qasm3/src/parser/completion/tests.rs @@ -36,7 +36,7 @@ fn begin_document() { "|OPENQASM 3;", &expect![[r#" WordKinds( - Annotation | Barrier | Box | Break | Cal | Continue | CReg | Def | DefCal | DefCalGrammar | Delay | End | Extern | False | For | Gate | If | Include | Input | Let | OpenQASM | Output | Pragma | QReg | Qubit | Reset | True | Return | Switch | While, + Annotation | Barrier | Box | Break | Cal | Const | Continue | CReg | Def | DefCal | DefCalGrammar | Delay | End | Extern | False | For | Gate | If | Include | Input | Let | OpenQASM | Output | Pragma | QReg | Qubit | Reset | True | Return | Switch | While, ) "#]], ); @@ -48,7 +48,7 @@ fn end_of_version() { "OPENQASM 3;|", &expect![[r#" WordKinds( - Annotation | Barrier | Box | Break | Cal | Continue | CReg | Def | DefCal | DefCalGrammar | Delay | End | Extern | False | For | Gate | If | Include | Input | Let | Output | Pragma | QReg | Qubit | Reset | True | Return | Switch | While, + Annotation | Barrier | Box | Break | Cal | Const | Continue | CReg | Def | DefCal | DefCalGrammar | Delay | End | Extern | False | For | Gate | If | Include | Input | Let | Output | Pragma | QReg | Qubit | Reset | True | Return | Switch | While, ) "#]], ); diff --git a/compiler/qsc_qasm3/src/parser/expr.rs b/compiler/qsc_qasm3/src/parser/expr.rs index 139b494892..033fa86cf7 100644 --- a/compiler/qsc_qasm3/src/parser/expr.rs +++ b/compiler/qsc_qasm3/src/parser/expr.rs @@ -68,10 +68,12 @@ enum OpName { Keyword(Keyword), } +// TODO: This seems to be an unnecessary wrapper. +// We ended up removing the OpContext::Stmt variant. +// Consider removing. #[derive(Clone, Copy)] enum OpContext { Precedence(u8), - Stmt, } #[derive(Clone, Copy)] @@ -86,14 +88,13 @@ pub(super) fn expr(s: &mut ParserContext) -> Result { expr_op(s, OpContext::Precedence(0)) } -pub(super) fn expr_stmt(s: &mut ParserContext) -> Result { - expr_op(s, OpContext::Stmt) +pub(super) fn expr_with_lhs(s: &mut ParserContext, lhs: Expr) -> Result { + expr_op_with_lhs(s, OpContext::Precedence(0), lhs) } fn expr_op(s: &mut ParserContext, context: OpContext) -> Result { let lo = s.peek().span.lo; - - let mut lhs = if let Some(op) = prefix_op(op_name(s)) { + let lhs = if let Some(op) = prefix_op(op_name(s)) { s.advance(); let rhs = expr_op(s, OpContext::Precedence(op.precedence))?; Expr { @@ -107,10 +108,13 @@ fn expr_op(s: &mut ParserContext, context: OpContext) -> Result { expr_base(s)? }; - let min_precedence = match context { - OpContext::Precedence(p) => p, - OpContext::Stmt => 0, - }; + expr_op_with_lhs(s, context, lhs) +} + +fn expr_op_with_lhs(s: &mut ParserContext, context: OpContext, mut lhs: Expr) -> Result { + let lo = lhs.span.lo; + + let OpContext::Precedence(min_precedence) = context; while let Some(op) = infix_op(op_name(s)) { if op.precedence < min_precedence { @@ -166,15 +170,9 @@ fn expr_base(s: &mut ParserContext) -> Result { Ok(Some(r#type)) => { // If we have a type, we expect to see a // parenthesized expression next. - token(s, TokenKind::Open(Delim::Paren))?; - let arg = paren_expr(s, lo)?; Ok(Expr { span: s.span(lo), - kind: Box::new(ExprKind::Cast(Cast { - span: s.span(lo), - r#type, - arg, - })), + kind: Box::new(cast_op(s, r#type)?), }) } Ok(None) => { @@ -468,13 +466,10 @@ fn funcall(s: &mut ParserContext, ident: ast::Ident) -> Result { } fn cast_op(s: &mut ParserContext, r#type: TypeDef) -> Result { - let lo = match &r#type { - TypeDef::Scalar(ident) => ident.span.lo, - TypeDef::Array(array) => array.span.lo, - TypeDef::ArrayReference(array) => array.span.lo, - }; - let arg = paren_expr(s, lo)?; - token(s, TokenKind::Close(Delim::Paren))?; + let lo = r#type.span().lo; + token(s, TokenKind::Open(Delim::Paren))?; + let arg = expr(s)?; + recovering_token(s, TokenKind::Close(Delim::Paren)); Ok(ExprKind::Cast(Cast { span: s.span(lo), r#type, diff --git a/compiler/qsc_qasm3/src/parser/stmt.rs b/compiler/qsc_qasm3/src/parser/stmt.rs index e2240c583f..13508e287c 100644 --- a/compiler/qsc_qasm3/src/parser/stmt.rs +++ b/compiler/qsc_qasm3/src/parser/stmt.rs @@ -18,7 +18,7 @@ use crate::{ ast::{ list_from_iter, AccessControl, AliasDeclStmt, AngleType, Annotation, ArrayBaseTypeKind, ArrayReferenceType, ArrayType, BarrierStmt, BitType, Block, BoxStmt, BreakStmt, - CalibrationGrammarStmt, CalibrationStmt, ClassicalDeclarationStmt, ComplexType, + CalibrationGrammarStmt, CalibrationStmt, Cast, ClassicalDeclarationStmt, ComplexType, ConstantDeclStmt, ContinueStmt, DefCalStmt, DefStmt, DelayStmt, EndStmt, EnumerableSet, Expr, ExprKind, ExprStmt, ExternDecl, ExternParameter, FloatType, ForStmt, FunctionCall, GPhase, GateCall, GateModifierKind, GateOperand, IODeclaration, IOKeyword, Ident, @@ -33,6 +33,7 @@ use crate::{ use super::{prim::token, ParserContext}; +#[allow(clippy::too_many_lines)] pub(super) fn parse(s: &mut ParserContext) -> Result> { let lo = s.peek().span.lo; if let Some(pragma) = opt(s, parse_pragma)? { @@ -57,7 +58,35 @@ pub(super) fn parse(s: &mut ParserContext) -> Result> { Box::new(decl) } else if let Some(include) = opt(s, parse_include)? { Box::new(include) - } else if let Some(decl) = opt(s, parse_local)? { + } else if let Some(ty) = opt(s, scalar_or_array_type)? { + if matches!(s.peek().kind, TokenKind::Identifier) { + Box::new(parse_non_constant_classical_decl(s, ty, lo)?) + } else { + token(s, TokenKind::Open(Delim::Paren))?; + let arg = expr::expr(s)?; + recovering_token(s, TokenKind::Close(Delim::Paren)); + let cast_expr = Expr { + span: s.span(lo), + kind: Box::new(ExprKind::Cast(Cast { + span: s.span(lo), + r#type: ty, + arg, + })), + }; + Box::new(StmtKind::ExprStmt(ExprStmt { + span: s.span(lo), + expr: cast_expr, + })) + } + } else if let Some(decl) = opt(s, parse_constant_classical_decl)? { + Box::new(decl) + } else if let Some(decl) = opt(s, parse_quantum_decl)? { + Box::new(decl) + } else if let Some(decl) = opt(s, parse_io_decl)? { + Box::new(decl) + } else if let Some(decl) = opt(s, qreg_decl)? { + Box::new(decl) + } else if let Some(decl) = opt(s, creg_decl)? { Box::new(decl) } else if let Some(decl) = opt(s, parse_extern)? { Box::new(decl) @@ -79,7 +108,7 @@ pub(super) fn parse(s: &mut ParserContext) -> Result> { Box::new(StmtKind::End(stmt)) } else if let Some(stmt_kind) = opt(s, parse_gate_call_stmt)? { Box::new(stmt_kind) - } else if let Some(stmt) = opt(s, parse_expression_stmt)? { + } else if let Some(stmt) = opt(s, |s| parse_expression_stmt(s, None))? { Box::new(StmtKind::ExprStmt(stmt)) } else if let Some(alias) = opt(s, parse_alias_stmt)? { Box::new(StmtKind::Alias(alias)) @@ -262,29 +291,6 @@ fn parse_pragma(s: &mut ParserContext) -> Result { }) } -// qreg and creg are separate from classical and quantum declarations -// simply for performance reasons. The latter are more common and old -// style declarations should be rare. -fn parse_local(s: &mut ParserContext) -> Result { - if let Some(decl) = opt(s, parse_classical_decl)? { - Ok(decl) - } else if let Some(decl) = opt(s, parse_quantum_decl)? { - Ok(decl) - } else if let Some(decl) = opt(s, parse_io_decl)? { - Ok(decl) - } else if let Some(decl) = opt(s, qreg_decl)? { - Ok(decl) - } else if let Some(decl) = opt(s, creg_decl)? { - Ok(decl) - } else { - Err(Error::new(ErrorKind::Rule( - "local declaration", - s.peek().kind, - s.peek().span, - ))) - } -} - fn parse_extern(s: &mut ParserContext) -> Result { let lo = s.peek().span.lo; token(s, TokenKind::Keyword(crate::keyword::Keyword::Extern))?; @@ -520,47 +526,45 @@ pub fn scalar_or_array_type(s: &mut ParserContext) -> Result { ))) } -fn parse_classical_decl(s: &mut ParserContext) -> Result { - let lo = s.peek().span.lo; - let is_const = if s.peek().kind == TokenKind::Keyword(crate::keyword::Keyword::Const) { +fn parse_non_constant_classical_decl( + s: &mut ParserContext, + ty: TypeDef, + lo: u32, +) -> Result { + let identifier = Box::new(prim::ident(s)?); + let init_expr = if s.peek().kind == TokenKind::Eq { s.advance(); - true + Some(expr::value_expr(s)?) } else { - false + None + }; + recovering_semi(s); + let decl = ClassicalDeclarationStmt { + span: s.span(lo), + r#type: ty, + identifier, + init_expr, }; - let ty = scalar_or_array_type(s)?; - let identifier = Box::new(prim::ident(s)?); + Ok(StmtKind::ClassicalDecl(decl)) +} - let stmt = if is_const { - token(s, TokenKind::Eq)?; - let init_expr = expr::expr(s)?; - recovering_semi(s); - let decl = ConstantDeclStmt { - span: s.span(lo), - r#type: ty, - identifier, - init_expr, - }; - StmtKind::ConstDecl(decl) - } else { - let init_expr = if s.peek().kind == TokenKind::Eq { - s.advance(); - Some(expr::value_expr(s)?) - } else { - None - }; - recovering_semi(s); - let decl = ClassicalDeclarationStmt { - span: s.span(lo), - r#type: ty, - identifier, - init_expr, - }; - StmtKind::ClassicalDecl(decl) +fn parse_constant_classical_decl(s: &mut ParserContext) -> Result { + let lo = s.peek().span.lo; + token(s, TokenKind::Keyword(Keyword::Const))?; + let ty = scalar_or_array_type(s)?; + let identifier = Box::new(prim::ident(s)?); + token(s, TokenKind::Eq)?; + let init_expr = expr::expr(s)?; + recovering_semi(s); + let decl = ConstantDeclStmt { + span: s.span(lo), + r#type: ty, + identifier, + init_expr, }; - Ok(stmt) + Ok(StmtKind::ConstDecl(decl)) } pub(super) fn array_type(s: &mut ParserContext) -> Result { @@ -880,7 +884,8 @@ pub fn parse_switch_stmt(s: &mut ParserContext) -> Result { // Controlling expression. token(s, TokenKind::Open(Delim::Paren))?; - let controlling_expr = expr::paren_expr(s, lo)?; + let controlling_expr = expr::expr(s)?; + recovering_token(s, TokenKind::Close(Delim::Paren)); // Open cases bracket. token(s, TokenKind::Open(Delim::Brace))?; @@ -940,9 +945,10 @@ fn parse_block_or_stmt(s: &mut ParserContext) -> Result> { pub fn parse_if_stmt(s: &mut ParserContext) -> Result { let lo = s.peek().span.lo; token(s, TokenKind::Keyword(Keyword::If))?; - let paren_expr_lo = s.peek().span.lo; token(s, TokenKind::Open(Delim::Paren))?; - let condition = expr::paren_expr(s, paren_expr_lo)?; + let condition = expr::expr(s)?; + recovering_token(s, TokenKind::Close(Delim::Paren)); + let if_block = parse_block_or_stmt(s)?; let else_block = if opt(s, |s| token(s, TokenKind::Keyword(Keyword::Else)))?.is_some() { Some(parse_block_or_stmt(s)?) @@ -1027,9 +1033,9 @@ pub fn parse_for_loop(s: &mut ParserContext) -> Result { pub fn parse_while_loop(s: &mut ParserContext) -> Result { let lo = s.peek().span.lo; token(s, TokenKind::Keyword(Keyword::While))?; - let paren_expr_lo = s.peek().span.lo; token(s, TokenKind::Open(Delim::Paren))?; - let while_condition = expr::paren_expr(s, paren_expr_lo)?; + let while_condition = expr::expr(s)?; + recovering_token(s, TokenKind::Close(Delim::Paren)); let block = parse_block_or_stmt(s)?; Ok(WhileLoop { @@ -1064,9 +1070,13 @@ fn parse_end_stmt(s: &mut ParserContext) -> Result { } /// Grammar: `expression SEMICOLON`. -fn parse_expression_stmt(s: &mut ParserContext) -> Result { +fn parse_expression_stmt(s: &mut ParserContext, lhs: Option) -> Result { let lo = s.peek().span.lo; - let expr = expr::expr(s)?; + let expr = if let Some(lhs) = lhs { + expr::expr_with_lhs(s, lhs)? + } else { + expr::expr(s)? + }; recovering_semi(s); Ok(ExprStmt { span: s.span(lo), diff --git a/compiler/qsc_qasm3/src/parser/stmt/tests/expr_stmt.rs b/compiler/qsc_qasm3/src/parser/stmt/tests/expr_stmt.rs index 20af84e5d1..7799100efb 100644 --- a/compiler/qsc_qasm3/src/parser/stmt/tests/expr_stmt.rs +++ b/compiler/qsc_qasm3/src/parser/stmt/tests/expr_stmt.rs @@ -117,19 +117,10 @@ fn cast_expr() { parse, "bit(0);", &expect![[r#" - Error( - Rule( - "identifier", - Open( - Paren, - ), - Span { - lo: 3, - hi: 4, - }, - ), - ) - "#]], + Stmt [0-6] + StmtKind: ExprStmt [0-6]: Expr [0-6]: Cast [0-6]: + ClassicalType [0-3]: BitType + Expr [4-5]: Lit: Int(0)"#]], ); } @@ -139,18 +130,9 @@ fn cast_expr_with_designator() { parse, "bit[45](0);", &expect![[r#" - Error( - Rule( - "identifier", - Open( - Paren, - ), - Span { - lo: 7, - hi: 8, - }, - ), - ) - "#]], + Stmt [0-10] + StmtKind: ExprStmt [0-10]: Expr [0-10]: Cast [0-10]: + ClassicalType [0-7]: BitType [0-7]: Expr [4-6]: Lit: Int(45) + Expr [8-9]: Lit: Int(0)"#]], ); } diff --git a/compiler/qsc_qasm3/src/parser/stmt/tests/if_stmt.rs b/compiler/qsc_qasm3/src/parser/stmt/tests/if_stmt.rs index c4239cade0..4d99ccfda8 100644 --- a/compiler/qsc_qasm3/src/parser/stmt/tests/if_stmt.rs +++ b/compiler/qsc_qasm3/src/parser/stmt/tests/if_stmt.rs @@ -17,10 +17,9 @@ fn simple_if_stmt() { ", &expect![[r#" Stmt [5-67] - StmtKind: IfStmt [5-67]: Expr [8-16]: Paren: - Expr [9-15]: BinOp (Eq): - Expr [9-10]: Ident [9-10] "x" - Expr [14-15]: Ident [14-15] "y" + StmtKind: IfStmt [5-67]: Expr [9-15]: BinOp (Eq): + Expr [9-10]: Ident [9-10] "x" + Expr [14-15]: Ident [14-15] "y" Stmt [27-33] StmtKind: ExprStmt [27-33]: Expr [27-32]: Assign: Expr [27-28]: Ident [27-28] "a" @@ -44,10 +43,9 @@ fn if_stmt_missing_else() { ", &expect![[r#" Stmt [5-39] - StmtKind: IfStmt [5-39]: Expr [8-16]: Paren: - Expr [9-15]: BinOp (Eq): - Expr [9-10]: Ident [9-10] "x" - Expr [14-15]: Ident [14-15] "y" + StmtKind: IfStmt [5-39]: Expr [9-15]: BinOp (Eq): + Expr [9-10]: Ident [9-10] "x" + Expr [14-15]: Ident [14-15] "y" Stmt [27-33] StmtKind: ExprStmt [27-33]: Expr [27-32]: Assign: Expr [27-28]: Ident [27-28] "a" @@ -76,15 +74,13 @@ fn nested_if_stmts() { ", &expect![[r#" Stmt [5-215] - StmtKind: IfStmt [5-215]: Expr [8-16]: Paren: - Expr [9-15]: BinOp (Eq): - Expr [9-10]: Ident [9-10] "x" - Expr [14-15]: Ident [14-15] "y" + StmtKind: IfStmt [5-215]: Expr [9-15]: BinOp (Eq): + Expr [9-10]: Ident [9-10] "x" + Expr [14-15]: Ident [14-15] "y" Stmt [27-107] - StmtKind: IfStmt [27-107]: Expr [30-40]: Paren: - Expr [31-39]: BinOp (Eq): - Expr [31-33]: Ident [31-33] "x1" - Expr [37-39]: Ident [37-39] "y1" + StmtKind: IfStmt [27-107]: Expr [31-39]: BinOp (Eq): + Expr [31-33]: Ident [31-33] "x1" + Expr [37-39]: Ident [37-39] "y1" Stmt [55-61] StmtKind: ExprStmt [55-61]: Expr [55-60]: Assign: Expr [55-56]: Ident [55-56] "a" @@ -96,10 +92,9 @@ fn nested_if_stmts() { Expr [95-96]: Lit: Int(1) Else: Stmt [129-209] - StmtKind: IfStmt [129-209]: Expr [132-142]: Paren: - Expr [133-141]: BinOp (Eq): - Expr [133-135]: Ident [133-135] "x2" - Expr [139-141]: Ident [139-141] "y2" + StmtKind: IfStmt [129-209]: Expr [133-141]: BinOp (Eq): + Expr [133-135]: Ident [133-135] "x2" + Expr [139-141]: Ident [139-141] "y2" Stmt [157-163] StmtKind: ExprStmt [157-163]: Expr [157-162]: Assign: Expr [157-158]: Ident [157-158] "a" diff --git a/compiler/qsc_qasm3/src/parser/stmt/tests/switch_stmt.rs b/compiler/qsc_qasm3/src/parser/stmt/tests/switch_stmt.rs index d32cce4a7f..cc6cfdd706 100644 --- a/compiler/qsc_qasm3/src/parser/stmt/tests/switch_stmt.rs +++ b/compiler/qsc_qasm3/src/parser/stmt/tests/switch_stmt.rs @@ -16,8 +16,7 @@ fn simple_switch() { ", &expect![[r#" SwitchStmt [9-72]: - Target: Expr [9-19]: Paren: - Expr [17-18]: Ident [17-18] "x" + Target: Expr [17-18]: Ident [17-18] "x" Cases: Labels: Expr [37-38]: Lit: Int(1) @@ -36,8 +35,7 @@ fn no_cases_no_default() { ", &expect![[r#" SwitchStmt [9-22]: - Target: Expr [9-19]: Paren: - Expr [17-18]: Ident [17-18] "x" + Target: Expr [17-18]: Ident [17-18] "x" @@ -65,8 +63,7 @@ fn no_cases() { ", &expect![[r#" SwitchStmt [9-52]: - Target: Expr [9-19]: Paren: - Expr [17-18]: Ident [17-18] "x" + Target: Expr [17-18]: Ident [17-18] "x" Default Case: Block [40-42]: @@ -95,8 +92,7 @@ fn no_default() { ", &expect![[r#" SwitchStmt [9-54]: - Target: Expr [9-19]: Paren: - Expr [17-18]: Ident [17-18] "x" + Target: Expr [17-18]: Ident [17-18] "x" Cases: Labels: Expr [37-38]: Lit: Int(0) @@ -117,8 +113,7 @@ fn case_with_no_labels() { ", &expect![[r#" SwitchStmt [9-49]: - Target: Expr [9-19]: Paren: - Expr [17-18]: Ident [17-18] "x" + Target: Expr [17-18]: Ident [17-18] "x" Cases: Block [37-39]: @@ -149,8 +144,7 @@ fn multiple_cases() { ", &expect![[r#" SwitchStmt [9-95]: - Target: Expr [9-19]: Paren: - Expr [17-18]: Ident [17-18] "x" + Target: Expr [17-18]: Ident [17-18] "x" Cases: Labels: Expr [37-38]: Lit: Int(0) diff --git a/compiler/qsc_qasm3/src/parser/stmt/tests/while_loops.rs b/compiler/qsc_qasm3/src/parser/stmt/tests/while_loops.rs index f444682c90..fe7576bbb7 100644 --- a/compiler/qsc_qasm3/src/parser/stmt/tests/while_loops.rs +++ b/compiler/qsc_qasm3/src/parser/stmt/tests/while_loops.rs @@ -14,10 +14,9 @@ fn simple_while() { }", &expect![[r#" Stmt [5-42] - StmtKind: WhileLoop [5-42]: Expr [11-19]: Paren: - Expr [12-18]: BinOp (Neq): - Expr [12-13]: Ident [12-13] "x" - Expr [17-18]: Lit: Int(2) + StmtKind: WhileLoop [5-42]: Expr [12-18]: BinOp (Neq): + Expr [12-13]: Ident [12-13] "x" + Expr [17-18]: Lit: Int(2) Stmt [30-36] StmtKind: ExprStmt [30-36]: Expr [30-35]: Assign: Expr [30-31]: Ident [30-31] "a" @@ -34,10 +33,9 @@ fn while_stmt_body() { a = 0;", &expect![[r#" Stmt [5-34] - StmtKind: WhileLoop [5-34]: Expr [11-19]: Paren: - Expr [12-18]: BinOp (Neq): - Expr [12-13]: Ident [12-13] "x" - Expr [17-18]: Lit: Int(2) + StmtKind: WhileLoop [5-34]: Expr [12-18]: BinOp (Neq): + Expr [12-13]: Ident [12-13] "x" + Expr [17-18]: Lit: Int(2) Stmt [28-34] StmtKind: ExprStmt [28-34]: Expr [28-33]: Assign: Expr [28-29]: Ident [28-29] "a" @@ -56,10 +54,9 @@ fn while_loop_with_continue_stmt() { }", &expect![[r#" Stmt [5-60] - StmtKind: WhileLoop [5-60]: Expr [11-19]: Paren: - Expr [12-18]: BinOp (Neq): - Expr [12-13]: Ident [12-13] "x" - Expr [17-18]: Lit: Int(2) + StmtKind: WhileLoop [5-60]: Expr [12-18]: BinOp (Neq): + Expr [12-13]: Ident [12-13] "x" + Expr [17-18]: Lit: Int(2) Stmt [30-36] StmtKind: ExprStmt [30-36]: Expr [30-35]: Assign: Expr [30-31]: Ident [30-31] "a" @@ -80,10 +77,9 @@ fn while_loop_with_break_stmt() { }", &expect![[r#" Stmt [5-57] - StmtKind: WhileLoop [5-57]: Expr [11-19]: Paren: - Expr [12-18]: BinOp (Neq): - Expr [12-13]: Ident [12-13] "x" - Expr [17-18]: Lit: Int(2) + StmtKind: WhileLoop [5-57]: Expr [12-18]: BinOp (Neq): + Expr [12-13]: Ident [12-13] "x" + Expr [17-18]: Lit: Int(2) Stmt [30-36] StmtKind: ExprStmt [30-36]: Expr [30-35]: Assign: Expr [30-31]: Ident [30-31] "a" From cc55a3b2850110d57648adf3013e4d61acabb3c9 Mon Sep 17 00:00:00 2001 From: Oscar Puente <156957451+orpuente-MS@users.noreply.github.com> Date: Fri, 28 Feb 2025 13:46:30 -0800 Subject: [PATCH 045/108] update cast unit tests --- compiler/qsc_qasm3/src/parser/expr/tests.rs | 100 ++++++++------------ 1 file changed, 40 insertions(+), 60 deletions(-) diff --git a/compiler/qsc_qasm3/src/parser/expr/tests.rs b/compiler/qsc_qasm3/src/parser/expr/tests.rs index 655084d9d5..6e53530b95 100644 --- a/compiler/qsc_qasm3/src/parser/expr/tests.rs +++ b/compiler/qsc_qasm3/src/parser/expr/tests.rs @@ -751,10 +751,9 @@ fn cast_to_bit() { expr, "bit(0)", &expect![[r#" - Expr [0-6]: Cast [0-6]: + Expr [0-3]: Cast [0-6]: ClassicalType [0-3]: BitType - Expr [0-6]: Paren: - Expr [4-5]: Lit: Int(0)"#]], + Expr [4-5]: Lit: Int(0)"#]], ); } @@ -764,10 +763,9 @@ fn cast_to_bit_with_designator() { expr, "bit[4](0)", &expect![[r#" - Expr [0-9]: Cast [0-9]: + Expr [0-6]: Cast [0-9]: ClassicalType [0-6]: BitType [0-6]: Expr [4-5]: Lit: Int(4) - Expr [0-9]: Paren: - Expr [7-8]: Lit: Int(0)"#]], + Expr [7-8]: Lit: Int(0)"#]], ); } @@ -777,10 +775,9 @@ fn cast_to_int() { expr, "int(0)", &expect![[r#" - Expr [0-6]: Cast [0-6]: + Expr [0-3]: Cast [0-6]: ClassicalType [0-3]: IntType [0-3] - Expr [0-6]: Paren: - Expr [4-5]: Lit: Int(0)"#]], + Expr [4-5]: Lit: Int(0)"#]], ); } @@ -790,10 +787,9 @@ fn cast_to_int_with_designator() { expr, "int[64](0)", &expect![[r#" - Expr [0-10]: Cast [0-10]: + Expr [0-7]: Cast [0-10]: ClassicalType [0-7]: IntType[Expr [4-6]: Lit: Int(64)]: [0-7] - Expr [0-10]: Paren: - Expr [8-9]: Lit: Int(0)"#]], + Expr [8-9]: Lit: Int(0)"#]], ); } @@ -803,10 +799,9 @@ fn cast_to_uint() { expr, "uint(0)", &expect![[r#" - Expr [0-7]: Cast [0-7]: + Expr [0-4]: Cast [0-7]: ClassicalType [0-4]: UIntType [0-4] - Expr [0-7]: Paren: - Expr [5-6]: Lit: Int(0)"#]], + Expr [5-6]: Lit: Int(0)"#]], ); } @@ -816,10 +811,9 @@ fn cast_to_uint_with_designator() { expr, "uint[64](0)", &expect![[r#" - Expr [0-11]: Cast [0-11]: + Expr [0-8]: Cast [0-11]: ClassicalType [0-8]: UIntType[Expr [5-7]: Lit: Int(64)]: [0-8] - Expr [0-11]: Paren: - Expr [9-10]: Lit: Int(0)"#]], + Expr [9-10]: Lit: Int(0)"#]], ); } @@ -829,10 +823,9 @@ fn cast_to_float() { expr, "float(0)", &expect![[r#" - Expr [0-8]: Cast [0-8]: + Expr [0-5]: Cast [0-8]: ClassicalType [0-5]: FloatType [0-5] - Expr [0-8]: Paren: - Expr [6-7]: Lit: Int(0)"#]], + Expr [6-7]: Lit: Int(0)"#]], ); } @@ -842,10 +835,9 @@ fn cast_to_float_with_designator() { expr, "float[64](0)", &expect![[r#" - Expr [0-12]: Cast [0-12]: + Expr [0-9]: Cast [0-12]: ClassicalType [0-9]: FloatType[Expr [6-8]: Lit: Int(64)]: [0-9] - Expr [0-12]: Paren: - Expr [10-11]: Lit: Int(0)"#]], + Expr [10-11]: Lit: Int(0)"#]], ); } @@ -855,10 +847,9 @@ fn cast_to_complex() { expr, "complex[float](0)", &expect![[r#" - Expr [0-17]: Cast [0-17]: + Expr [0-14]: Cast [0-17]: ClassicalType [0-14]: ComplexType[float[FloatType [8-13]]]: [0-14] - Expr [0-17]: Paren: - Expr [15-16]: Lit: Int(0)"#]], + Expr [15-16]: Lit: Int(0)"#]], ); } @@ -868,10 +859,9 @@ fn cast_to_complex_with_designator() { expr, "complex[float[64]](0)", &expect![[r#" - Expr [0-21]: Cast [0-21]: + Expr [0-18]: Cast [0-21]: ClassicalType [0-18]: ComplexType[float[FloatType[Expr [14-16]: Lit: Int(64)]: [8-17]]]: [0-18] - Expr [0-21]: Paren: - Expr [19-20]: Lit: Int(0)"#]], + Expr [19-20]: Lit: Int(0)"#]], ); } @@ -881,10 +871,9 @@ fn cast_to_bool() { expr, "bool(0)", &expect![[r#" - Expr [0-7]: Cast [0-7]: + Expr [0-4]: Cast [0-7]: ClassicalType [0-4]: BoolType - Expr [0-7]: Paren: - Expr [5-6]: Lit: Int(0)"#]], + Expr [5-6]: Lit: Int(0)"#]], ); } @@ -894,10 +883,9 @@ fn cast_to_duration() { expr, "duration(0)", &expect![[r#" - Expr [0-11]: Cast [0-11]: + Expr [0-8]: Cast [0-11]: ClassicalType [0-8]: Duration - Expr [0-11]: Paren: - Expr [9-10]: Lit: Int(0)"#]], + Expr [9-10]: Lit: Int(0)"#]], ); } @@ -907,10 +895,9 @@ fn cast_to_stretch() { expr, "stretch(0)", &expect![[r#" - Expr [0-10]: Cast [0-10]: + Expr [0-7]: Cast [0-10]: ClassicalType [0-7]: Stretch - Expr [0-10]: Paren: - Expr [8-9]: Lit: Int(0)"#]], + Expr [8-9]: Lit: Int(0)"#]], ); } @@ -920,11 +907,10 @@ fn cast_to_int_array() { expr, "array[int[64], 4](0)", &expect![[r#" - Expr [0-20]: Cast [0-20]: + Expr [0-17]: Cast [0-20]: ArrayType [0-17]: ArrayBaseTypeKind IntType[Expr [10-12]: Lit: Int(64)]: [6-13] Expr [15-16]: Lit: Int(4) - Expr [0-20]: Paren: - Expr [18-19]: Lit: Int(0)"#]], + Expr [18-19]: Lit: Int(0)"#]], ); } @@ -934,11 +920,10 @@ fn cast_to_uint_array() { expr, "array[uint[64], 4](0)", &expect![[r#" - Expr [0-21]: Cast [0-21]: + Expr [0-18]: Cast [0-21]: ArrayType [0-18]: ArrayBaseTypeKind UIntType[Expr [11-13]: Lit: Int(64)]: [6-14] Expr [16-17]: Lit: Int(4) - Expr [0-21]: Paren: - Expr [19-20]: Lit: Int(0)"#]], + Expr [19-20]: Lit: Int(0)"#]], ); } @@ -948,11 +933,10 @@ fn cast_to_float_array() { expr, "array[float[64], 4](0)", &expect![[r#" - Expr [0-22]: Cast [0-22]: + Expr [0-19]: Cast [0-22]: ArrayType [0-19]: ArrayBaseTypeKind FloatType[Expr [12-14]: Lit: Int(64)]: [6-15] Expr [17-18]: Lit: Int(4) - Expr [0-22]: Paren: - Expr [20-21]: Lit: Int(0)"#]], + Expr [20-21]: Lit: Int(0)"#]], ); } @@ -962,11 +946,10 @@ fn cast_to_angle_array() { expr, "array[angle[64], 4](0)", &expect![[r#" - Expr [0-22]: Cast [0-22]: + Expr [0-19]: Cast [0-22]: ArrayType [0-19]: ArrayBaseTypeKind AngleType [6-15]: Expr [12-14]: Lit: Int(64) Expr [17-18]: Lit: Int(4) - Expr [0-22]: Paren: - Expr [20-21]: Lit: Int(0)"#]], + Expr [20-21]: Lit: Int(0)"#]], ); } @@ -976,11 +959,10 @@ fn cast_to_bool_array() { expr, "array[bool, 4](0)", &expect![[r#" - Expr [0-17]: Cast [0-17]: + Expr [0-14]: Cast [0-17]: ArrayType [0-14]: ArrayBaseTypeKind BoolType Expr [12-13]: Lit: Int(4) - Expr [0-17]: Paren: - Expr [15-16]: Lit: Int(0)"#]], + Expr [15-16]: Lit: Int(0)"#]], ); } @@ -990,11 +972,10 @@ fn cast_to_duration_array() { expr, "array[duration, 4](0)", &expect![[r#" - Expr [0-21]: Cast [0-21]: + Expr [0-18]: Cast [0-21]: ArrayType [0-18]: ArrayBaseTypeKind DurationType Expr [16-17]: Lit: Int(4) - Expr [0-21]: Paren: - Expr [19-20]: Lit: Int(0)"#]], + Expr [19-20]: Lit: Int(0)"#]], ); } @@ -1004,11 +985,10 @@ fn cast_to_complex_array() { expr, "array[complex[float[32]], 4](0)", &expect![[r#" - Expr [0-31]: Cast [0-31]: + Expr [0-28]: Cast [0-31]: ArrayType [0-28]: ArrayBaseTypeKind ComplexType[float[FloatType[Expr [20-22]: Lit: Int(32)]: [14-23]]]: [6-24] Expr [26-27]: Lit: Int(4) - Expr [0-31]: Paren: - Expr [29-30]: Lit: Int(0)"#]], + Expr [29-30]: Lit: Int(0)"#]], ); } From abada593abc19fc9c0adec1c1aa03a1f4a196d00 Mon Sep 17 00:00:00 2001 From: Oscar Puente <156957451+orpuente-MS@users.noreply.github.com> Date: Fri, 28 Feb 2025 15:40:19 -0800 Subject: [PATCH 046/108] make expr unit tests also run for expr_stmts by adding a semicolon at the end --- compiler/qsc_qasm3/src/parser/expr.rs | 3 +- compiler/qsc_qasm3/src/parser/expr/tests.rs | 431 +++++++----------- compiler/qsc_qasm3/src/parser/stmt.rs | 15 +- .../src/parser/stmt/tests/classical_decl.rs | 113 +---- compiler/qsc_qasm3/src/parser/tests.rs | 1 - 5 files changed, 192 insertions(+), 371 deletions(-) diff --git a/compiler/qsc_qasm3/src/parser/expr.rs b/compiler/qsc_qasm3/src/parser/expr.rs index 033fa86cf7..218569b7ba 100644 --- a/compiler/qsc_qasm3/src/parser/expr.rs +++ b/compiler/qsc_qasm3/src/parser/expr.rs @@ -170,9 +170,10 @@ fn expr_base(s: &mut ParserContext) -> Result { Ok(Some(r#type)) => { // If we have a type, we expect to see a // parenthesized expression next. + let kind = Box::new(cast_op(s, r#type)?); Ok(Expr { span: s.span(lo), - kind: Box::new(cast_op(s, r#type)?), + kind, }) } Ok(None) => { diff --git a/compiler/qsc_qasm3/src/parser/expr/tests.rs b/compiler/qsc_qasm3/src/parser/expr/tests.rs index 6e53530b95..e01d468c27 100644 --- a/compiler/qsc_qasm3/src/parser/expr/tests.rs +++ b/compiler/qsc_qasm3/src/parser/expr/tests.rs @@ -1,31 +1,72 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. -use crate::parser::tests::check; - use super::expr; - -use expect_test::expect; +use crate::{ + ast::StmtKind, + parser::{scan::ParserContext, stmt, tests::check, Parser}, +}; +use expect_test::{expect, Expect}; + +fn check_map( + mut parser: impl Parser, + input: &str, + expect: &Expect, + f: impl FnOnce(&T) -> String, +) { + let mut scanner = ParserContext::new(input); + let result = parser(&mut scanner); + let errors = scanner.into_errors(); + match result { + Ok(value) if errors.is_empty() => expect.assert_eq(&f(&value)), + Ok(value) => expect.assert_eq(&format!("{}\n\n{errors:#?}", f(&value))), + Err(error) if errors.is_empty() => expect.assert_debug_eq(&error), + Err(error) => expect.assert_eq(&format!("{error:#?}\n\n{errors:#?}")), + } +} + +/// This function checks two things: +/// 1. That the input `Expr` is parsed correctly. +/// 2. That if we add a semicolon at the end it parses correctly as a `ExprStmt` +/// containing the same `Expr` inside. +fn check_expr(input: &str, expect: &Expect) { + // Do the usual expect test check. + check(expr, input, expect); + + // Parse the expr with the expr parser. + let expr = expr(&mut ParserContext::new(input)).map(Some); + + // Add a semicolon and parser with the stmt parser. + let expr_stmt = stmt::parse(&mut ParserContext::new(&format!("{input};"))); + + // Extract the inner expr. + let inner_expr = expr_stmt.map(|ok| match *ok.kind { + StmtKind::ExprStmt(expr) => Some(expr.expr), + _ => None, + }); + + // Check that they are equal. + assert_eq!(format!("{expr:?}"), format!("{inner_expr:?}")); +} #[test] fn lit_int() { - check(expr, "123", &expect!["Expr [0-3]: Lit: Int(123)"]); + check_expr("123", &expect!["Expr [0-3]: Lit: Int(123)"]); } #[test] fn lit_int_underscore() { - check(expr, "123_456", &expect!["Expr [0-7]: Lit: Int(123456)"]); + check_expr("123_456", &expect!["Expr [0-7]: Lit: Int(123456)"]); } #[test] fn lit_int_leading_zero() { - check(expr, "0123", &expect!["Expr [0-4]: Lit: Int(123)"]); + check_expr("0123", &expect!["Expr [0-4]: Lit: Int(123)"]); } #[test] fn lit_int_max() { - check( - expr, + check_expr( "9_223_372_036_854_775_807", &expect!["Expr [0-25]: Lit: Int(9223372036854775807)"], ); @@ -38,8 +79,7 @@ fn lit_int_max() { // of i64::MIN also need to be tested. #[test] fn lit_int_overflow_min() { - check( - expr, + check_expr( "9_223_372_036_854_775_808", &expect!["Expr [0-25]: Lit: Int(-9223372036854775808)"], ); @@ -47,8 +87,7 @@ fn lit_int_overflow_min() { #[test] fn lit_int_overflow_min_hexadecimal() { - check( - expr, + check_expr( "0x8000000000000000", &expect!["Expr [0-18]: Lit: Int(-9223372036854775808)"], ); @@ -56,8 +95,7 @@ fn lit_int_overflow_min_hexadecimal() { #[test] fn lit_int_overflow_min_binary() { - check( - expr, + check_expr( "0b1000000000000000000000000000000000000000000000000000000000000000", &expect!["Expr [0-66]: Lit: Int(-9223372036854775808)"], ); @@ -65,8 +103,7 @@ fn lit_int_overflow_min_binary() { #[test] fn lit_int_too_big_for_i64() { - check( - expr, + check_expr( "9_223_372_036_854_775_809", &expect!["Expr [0-25]: Lit: BigInt(9223372036854775809)"], ); @@ -74,8 +111,7 @@ fn lit_int_too_big_for_i64() { #[test] fn lit_int_too_big_hexadecimal_promotes_to_bigint() { - check( - expr, + check_expr( "0x8000000000000001", &expect!["Expr [0-18]: Lit: BigInt(9223372036854775809)"], ); @@ -83,8 +119,7 @@ fn lit_int_too_big_hexadecimal_promotes_to_bigint() { #[test] fn lit_int_too_big_binary_promotes_to_bigint() { - check( - expr, + check_expr( "0b1000000000000000000000000000000000000000000000000000000000000001", &expect!["Expr [0-66]: Lit: BigInt(9223372036854775809)"], ); @@ -95,10 +130,8 @@ fn lit_int_too_big_binary_promotes_to_bigint() { // of i64::MIN. This will wrap to a negative value, and then negate of i64::MIN is i64::MIN, so // the correct value is achieved at runtime. #[test] -#[ignore = "Re-enable when we support unary ops"] fn lit_int_min() { - check( - expr, + check_expr( "-9_223_372_036_854_775_808", &expect![[r#" Expr [0-26]: UnOp (Neg): @@ -108,23 +141,22 @@ fn lit_int_min() { #[test] fn lit_int_hexadecimal() { - check(expr, "0x1a2b3c", &expect!["Expr [0-8]: Lit: Int(1715004)"]); + check_expr("0x1a2b3c", &expect!["Expr [0-8]: Lit: Int(1715004)"]); } #[test] fn lit_int_octal() { - check(expr, "0o1234567", &expect!["Expr [0-9]: Lit: Int(342391)"]); + check_expr("0o1234567", &expect!["Expr [0-9]: Lit: Int(342391)"]); } #[test] fn lit_int_binary() { - check(expr, "0b10110", &expect!["Expr [0-7]: Lit: Int(22)"]); + check_expr("0b10110", &expect!["Expr [0-7]: Lit: Int(22)"]); } #[test] fn lit_bigint_hexadecimal() { - check( - expr, + check_expr( "0x1a2b3c1a2b3c1a2b3c1a", &expect!["Expr [0-22]: Lit: BigInt(123579069371309093501978)"], ); @@ -132,8 +164,7 @@ fn lit_bigint_hexadecimal() { #[test] fn lit_bigint_hexadecimal_capital_x() { - check( - expr, + check_expr( "0X1a2b3c1a2b3c1a2b3c1a", &expect!["Expr [0-22]: Lit: BigInt(123579069371309093501978)"], ); @@ -141,8 +172,7 @@ fn lit_bigint_hexadecimal_capital_x() { #[test] fn lit_bigint_octal() { - check( - expr, + check_expr( "0o1234567123456712345671234", &expect!["Expr [0-27]: Lit: BigInt(6167970861177743307420)"], ); @@ -150,8 +180,7 @@ fn lit_bigint_octal() { #[test] fn lit_bigint_octal_capital_o() { - check( - expr, + check_expr( "0O1234567123456712345671234", &expect!["Expr [0-27]: Lit: BigInt(6167970861177743307420)"], ); @@ -159,8 +188,7 @@ fn lit_bigint_octal_capital_o() { #[test] fn lit_bigint_binary() { - check( - expr, + check_expr( "0b1011010110101101011010110101101011010110101101011010110101101011", &expect!["Expr [0-66]: Lit: BigInt(13091237729729359211)"], ); @@ -168,8 +196,7 @@ fn lit_bigint_binary() { #[test] fn lit_bigint_binary_capital_b() { - check( - expr, + check_expr( "0B1011010110101101011010110101101011010110101101011010110101101011", &expect!["Expr [0-66]: Lit: BigInt(13091237729729359211)"], ); @@ -177,37 +204,32 @@ fn lit_bigint_binary_capital_b() { #[test] fn lit_float() { - check(expr, "1.23", &expect!["Expr [0-4]: Lit: Float(1.23)"]); + check_expr("1.23", &expect!["Expr [0-4]: Lit: Float(1.23)"]); } #[test] fn lit_float_leading_dot() { - check(expr, ".23", &expect!["Expr [0-3]: Lit: Float(0.23)"]); + check_expr(".23", &expect!["Expr [0-3]: Lit: Float(0.23)"]); } #[test] fn lit_float_trailing_dot() { - check(expr, "1.", &expect!["Expr [0-2]: Lit: Float(1.0)"]); + check_expr("1.", &expect!["Expr [0-2]: Lit: Float(1.0)"]); } #[test] fn lit_float_underscore() { - check( - expr, - "123_456.78", - &expect!["Expr [0-10]: Lit: Float(123456.78)"], - ); + check_expr("123_456.78", &expect!["Expr [0-10]: Lit: Float(123456.78)"]); } #[test] fn lit_float_leading_zero() { - check(expr, "0.23", &expect!["Expr [0-4]: Lit: Float(0.23)"]); + check_expr("0.23", &expect!["Expr [0-4]: Lit: Float(0.23)"]); } #[test] fn lit_float_trailing_exp_0() { - check( - expr, + check_expr( "0e", &expect![[r#" Error( @@ -225,8 +247,7 @@ fn lit_float_trailing_exp_0() { #[test] fn lit_float_trailing_exp_1() { - check( - expr, + check_expr( "1e", &expect![[r#" Error( @@ -244,8 +265,7 @@ fn lit_float_trailing_exp_1() { #[test] fn lit_float_trailing_dot_trailing_exp() { - check( - expr, + check_expr( "1.e", &expect![[r#" Error( @@ -263,8 +283,7 @@ fn lit_float_trailing_dot_trailing_exp() { #[test] fn lit_float_dot_trailing_exp() { - check( - expr, + check_expr( "1.2e", &expect![[r#" Error( @@ -282,8 +301,7 @@ fn lit_float_dot_trailing_exp() { #[test] fn lit_float_trailing_exp_dot() { - check( - expr, + check_expr( "1e.", &expect![[r#" Error( @@ -300,55 +318,26 @@ fn lit_float_trailing_exp_dot() { } #[test] -#[ignore = "Re-enable when we support more than literals"] fn lit_int_hexadecimal_dot() { - check( - expr, + check_expr( "0x123.45", - &expect![[r#" - Expr [0-6]: Field: - Expr [0-5]: Lit: Int(291) - Err - - [ - Error( - Rule( - "identifier", - Int( - Decimal, - ), - Span { - lo: 6, - hi: 8, - }, - ), - ), - ]"#]], + &expect!["Expr [0-5]: Lit: Int(291)"], ); } #[test] fn lit_string() { - check( - expr, - r#""foo""#, - &expect![[r#"Expr [0-5]: Lit: String("foo")"#]], - ); + check_expr(r#""foo""#, &expect![[r#"Expr [0-5]: Lit: String("foo")"#]]); } #[test] fn lit_string_single_quote() { - check( - expr, - r#"'foo'"#, - &expect![[r#"Expr [0-5]: Lit: String("foo")"#]], - ); + check_expr(r#"'foo'"#, &expect![[r#"Expr [0-5]: Lit: String("foo")"#]]); } #[test] fn lit_string_escape_quote() { - check( - expr, + check_expr( r#""foo\"bar""#, &expect![[r#"Expr [0-10]: Lit: String("foo\"bar")"#]], ); @@ -356,8 +345,7 @@ fn lit_string_escape_quote() { #[test] fn lit_string_single_quote_escape_double_quote() { - check( - expr, + check_expr( r#"'foo\"bar'"#, &expect![[r#"Expr [0-10]: Lit: String("foo\"bar")"#]], ); @@ -365,80 +353,47 @@ fn lit_string_single_quote_escape_double_quote() { #[test] fn lit_string_escape_backslash() { - check( - expr, - r#""\\""#, - &expect![[r#"Expr [0-4]: Lit: String("\\")"#]], - ); + check_expr(r#""\\""#, &expect![[r#"Expr [0-4]: Lit: String("\\")"#]]); } #[test] fn lit_string_single_quote_escape_backslash() { - check( - expr, - r#"'\\'"#, - &expect![[r#"Expr [0-4]: Lit: String("\\")"#]], - ); + check_expr(r#"'\\'"#, &expect![[r#"Expr [0-4]: Lit: String("\\")"#]]); } #[test] fn lit_string_escape_newline() { - check( - expr, - r#""\n""#, - &expect![[r#"Expr [0-4]: Lit: String("\n")"#]], - ); + check_expr(r#""\n""#, &expect![[r#"Expr [0-4]: Lit: String("\n")"#]]); } #[test] fn lit_string_single_quote_escape_newline() { - check( - expr, - r#"'\n'"#, - &expect![[r#"Expr [0-4]: Lit: String("\n")"#]], - ); + check_expr(r#"'\n'"#, &expect![[r#"Expr [0-4]: Lit: String("\n")"#]]); } #[test] fn lit_string_escape_carriage_return() { - check( - expr, - r#""\r""#, - &expect![[r#"Expr [0-4]: Lit: String("\r")"#]], - ); + check_expr(r#""\r""#, &expect![[r#"Expr [0-4]: Lit: String("\r")"#]]); } #[test] fn lit_string_single_quote_escape_carriage_return() { - check( - expr, - r#"'\r'"#, - &expect![[r#"Expr [0-4]: Lit: String("\r")"#]], - ); + check_expr(r#"'\r'"#, &expect![[r#"Expr [0-4]: Lit: String("\r")"#]]); } #[test] fn lit_string_escape_tab() { - check( - expr, - r#""\t""#, - &expect![[r#"Expr [0-4]: Lit: String("\t")"#]], - ); + check_expr(r#""\t""#, &expect![[r#"Expr [0-4]: Lit: String("\t")"#]]); } #[test] fn lit_string_single_quote_escape_tab() { - check( - expr, - r#"'\t'"#, - &expect![[r#"Expr [0-4]: Lit: String("\t")"#]], - ); + check_expr(r#"'\t'"#, &expect![[r#"Expr [0-4]: Lit: String("\t")"#]]); } #[test] fn lit_string_unknown_escape() { - check( - expr, + check_expr( r#""\x""#, &expect![[r#" Error( @@ -488,23 +443,22 @@ fn lit_string_unmatched_quote() { #[test] fn lit_string_empty() { - check(expr, r#""""#, &expect![[r#"Expr [0-2]: Lit: String("")"#]]); + check_expr(r#""""#, &expect![[r#"Expr [0-2]: Lit: String("")"#]]); } #[test] fn lit_false() { - check(expr, "false", &expect!["Expr [0-5]: Lit: Bool(false)"]); + check_expr("false", &expect!["Expr [0-5]: Lit: Bool(false)"]); } #[test] fn lit_true() { - check(expr, "true", &expect!["Expr [0-4]: Lit: Bool(true)"]); + check_expr("true", &expect!["Expr [0-4]: Lit: Bool(true)"]); } #[test] fn lit_bitstring() { - check( - expr, + check_expr( r#""101010101""#, &expect![[r#"Expr [0-11]: Lit: Bitstring("101010101")"#]], ); @@ -512,8 +466,7 @@ fn lit_bitstring() { #[test] fn lit_bitstring_preserves_leading_zeroes() { - check( - expr, + check_expr( r#""00011000""#, &expect![[r#"Expr [0-10]: Lit: Bitstring("00011000")"#]], ); @@ -521,8 +474,7 @@ fn lit_bitstring_preserves_leading_zeroes() { #[test] fn lit_bitstring_separators() { - check( - expr, + check_expr( r#""10_10_10_101""#, &expect![[r#"Expr [0-14]: Lit: Bitstring("101010101")"#]], ); @@ -562,50 +514,37 @@ fn lit_bitstring_unmatched_quote() { #[test] fn lit_float_imag() { - check( - expr, - r#"10.3im"#, - &expect!["Expr [0-6]: Lit: Imaginary(10.3)"], - ); + check_expr(r#"10.3im"#, &expect!["Expr [0-6]: Lit: Imaginary(10.3)"]); } #[test] fn lit_float_imag_with_spacing() { - check( - expr, - r#"10.3 im"#, - &expect!["Expr [0-8]: Lit: Imaginary(10.3)"], - ); + check_expr(r#"10.3 im"#, &expect!["Expr [0-8]: Lit: Imaginary(10.3)"]); } #[test] fn lit_int_imag() { - check(expr, r#"10"#, &expect!["Expr [0-2]: Lit: Int(10)"]); + check_expr(r#"10im"#, &expect!["Expr [0-4]: Lit: Imaginary(10.0)"]); } #[test] fn lit_int_imag_with_spacing() { - check( - expr, - r#"10 im"#, - &expect!["Expr [0-6]: Lit: Imaginary(10.0)"], - ); + check_expr(r#"10 im"#, &expect!["Expr [0-6]: Lit: Imaginary(10.0)"]); } #[test] fn lit_float_imag_leading_dot() { - check(expr, ".23im", &expect!["Expr [0-5]: Lit: Imaginary(0.23)"]); + check_expr(".23im", &expect!["Expr [0-5]: Lit: Imaginary(0.23)"]); } #[test] fn lit_float_imag_trailing_dot() { - check(expr, "1.im", &expect!["Expr [0-4]: Lit: Imaginary(1.0)"]); + check_expr("1.im", &expect!["Expr [0-4]: Lit: Imaginary(1.0)"]); } #[test] fn lit_float_imag_underscore() { - check( - expr, + check_expr( "123_456.78im", &expect!["Expr [0-12]: Lit: Imaginary(123456.78)"], ); @@ -613,13 +552,12 @@ fn lit_float_imag_underscore() { #[test] fn lit_float_imag_leading_zero() { - check(expr, "0.23im", &expect!["Expr [0-6]: Lit: Imaginary(0.23)"]); + check_expr("0.23im", &expect!["Expr [0-6]: Lit: Imaginary(0.23)"]); } #[test] fn pratt_parsing_mul_add() { - check( - expr, + check_expr( "1 + 2 * 3", &expect![[r#" Expr [0-9]: BinOp (Add): @@ -632,8 +570,7 @@ fn pratt_parsing_mul_add() { #[test] fn pratt_parsing_parens() { - check( - expr, + check_expr( "(1 + 2) * 3", &expect![[r#" Expr [0-11]: BinOp (Mul): @@ -647,8 +584,7 @@ fn pratt_parsing_parens() { #[test] fn prat_parsing_mul_unary() { - check( - expr, + check_expr( "2 * -3", &expect![[r#" Expr [0-6]: BinOp (Mul): @@ -660,8 +596,7 @@ fn prat_parsing_mul_unary() { #[test] fn prat_parsing_unary_mul() { - check( - expr, + check_expr( "-2 * 3", &expect![[r#" Expr [0-6]: BinOp (Mul): @@ -673,8 +608,7 @@ fn prat_parsing_unary_mul() { #[test] fn prat_parsing_exp_funcall() { - check( - expr, + check_expr( "2 ** square(3)", &expect![[r#" Expr [0-14]: BinOp (Exp): @@ -686,8 +620,7 @@ fn prat_parsing_exp_funcall() { #[test] fn prat_parsing_funcall_exp() { - check( - expr, + check_expr( "square(2) ** 3", &expect![[r#" Expr [0-14]: BinOp (Exp): @@ -699,8 +632,7 @@ fn prat_parsing_funcall_exp() { #[test] fn prat_parsing_funcall_exp_arg() { - check( - expr, + check_expr( "square(2 ** 3)", &expect![[r#" Expr [0-14]: FunctionCall [0-14]: Ident [0-6] "square" @@ -712,8 +644,7 @@ fn prat_parsing_funcall_exp_arg() { #[test] fn funcall() { - check( - expr, + check_expr( "square(2)", &expect![[r#" Expr [0-9]: FunctionCall [0-9]: Ident [0-6] "square" @@ -723,8 +654,7 @@ fn funcall() { #[test] fn funcall_multiple_args() { - check( - expr, + check_expr( "square(2, 3)", &expect![[r#" Expr [0-12]: FunctionCall [0-12]: Ident [0-6] "square" @@ -735,8 +665,7 @@ fn funcall_multiple_args() { #[test] fn funcall_multiple_args_trailing_comma() { - check( - expr, + check_expr( "square(2, 3,)", &expect![[r#" Expr [0-13]: FunctionCall [0-13]: Ident [0-6] "square" @@ -747,11 +676,10 @@ fn funcall_multiple_args_trailing_comma() { #[test] fn cast_to_bit() { - check( - expr, + check_expr( "bit(0)", &expect![[r#" - Expr [0-3]: Cast [0-6]: + Expr [0-6]: Cast [0-6]: ClassicalType [0-3]: BitType Expr [4-5]: Lit: Int(0)"#]], ); @@ -759,11 +687,10 @@ fn cast_to_bit() { #[test] fn cast_to_bit_with_designator() { - check( - expr, + check_expr( "bit[4](0)", &expect![[r#" - Expr [0-6]: Cast [0-9]: + Expr [0-9]: Cast [0-9]: ClassicalType [0-6]: BitType [0-6]: Expr [4-5]: Lit: Int(4) Expr [7-8]: Lit: Int(0)"#]], ); @@ -771,11 +698,10 @@ fn cast_to_bit_with_designator() { #[test] fn cast_to_int() { - check( - expr, + check_expr( "int(0)", &expect![[r#" - Expr [0-3]: Cast [0-6]: + Expr [0-6]: Cast [0-6]: ClassicalType [0-3]: IntType [0-3] Expr [4-5]: Lit: Int(0)"#]], ); @@ -783,11 +709,10 @@ fn cast_to_int() { #[test] fn cast_to_int_with_designator() { - check( - expr, + check_expr( "int[64](0)", &expect![[r#" - Expr [0-7]: Cast [0-10]: + Expr [0-10]: Cast [0-10]: ClassicalType [0-7]: IntType[Expr [4-6]: Lit: Int(64)]: [0-7] Expr [8-9]: Lit: Int(0)"#]], ); @@ -795,11 +720,10 @@ fn cast_to_int_with_designator() { #[test] fn cast_to_uint() { - check( - expr, + check_expr( "uint(0)", &expect![[r#" - Expr [0-4]: Cast [0-7]: + Expr [0-7]: Cast [0-7]: ClassicalType [0-4]: UIntType [0-4] Expr [5-6]: Lit: Int(0)"#]], ); @@ -807,11 +731,10 @@ fn cast_to_uint() { #[test] fn cast_to_uint_with_designator() { - check( - expr, + check_expr( "uint[64](0)", &expect![[r#" - Expr [0-8]: Cast [0-11]: + Expr [0-11]: Cast [0-11]: ClassicalType [0-8]: UIntType[Expr [5-7]: Lit: Int(64)]: [0-8] Expr [9-10]: Lit: Int(0)"#]], ); @@ -819,11 +742,10 @@ fn cast_to_uint_with_designator() { #[test] fn cast_to_float() { - check( - expr, + check_expr( "float(0)", &expect![[r#" - Expr [0-5]: Cast [0-8]: + Expr [0-8]: Cast [0-8]: ClassicalType [0-5]: FloatType [0-5] Expr [6-7]: Lit: Int(0)"#]], ); @@ -831,11 +753,10 @@ fn cast_to_float() { #[test] fn cast_to_float_with_designator() { - check( - expr, + check_expr( "float[64](0)", &expect![[r#" - Expr [0-9]: Cast [0-12]: + Expr [0-12]: Cast [0-12]: ClassicalType [0-9]: FloatType[Expr [6-8]: Lit: Int(64)]: [0-9] Expr [10-11]: Lit: Int(0)"#]], ); @@ -843,11 +764,10 @@ fn cast_to_float_with_designator() { #[test] fn cast_to_complex() { - check( - expr, + check_expr( "complex[float](0)", &expect![[r#" - Expr [0-14]: Cast [0-17]: + Expr [0-17]: Cast [0-17]: ClassicalType [0-14]: ComplexType[float[FloatType [8-13]]]: [0-14] Expr [15-16]: Lit: Int(0)"#]], ); @@ -855,11 +775,10 @@ fn cast_to_complex() { #[test] fn cast_to_complex_with_designator() { - check( - expr, + check_expr( "complex[float[64]](0)", &expect![[r#" - Expr [0-18]: Cast [0-21]: + Expr [0-21]: Cast [0-21]: ClassicalType [0-18]: ComplexType[float[FloatType[Expr [14-16]: Lit: Int(64)]: [8-17]]]: [0-18] Expr [19-20]: Lit: Int(0)"#]], ); @@ -867,11 +786,10 @@ fn cast_to_complex_with_designator() { #[test] fn cast_to_bool() { - check( - expr, + check_expr( "bool(0)", &expect![[r#" - Expr [0-4]: Cast [0-7]: + Expr [0-7]: Cast [0-7]: ClassicalType [0-4]: BoolType Expr [5-6]: Lit: Int(0)"#]], ); @@ -879,11 +797,10 @@ fn cast_to_bool() { #[test] fn cast_to_duration() { - check( - expr, + check_expr( "duration(0)", &expect![[r#" - Expr [0-8]: Cast [0-11]: + Expr [0-11]: Cast [0-11]: ClassicalType [0-8]: Duration Expr [9-10]: Lit: Int(0)"#]], ); @@ -891,11 +808,10 @@ fn cast_to_duration() { #[test] fn cast_to_stretch() { - check( - expr, + check_expr( "stretch(0)", &expect![[r#" - Expr [0-7]: Cast [0-10]: + Expr [0-10]: Cast [0-10]: ClassicalType [0-7]: Stretch Expr [8-9]: Lit: Int(0)"#]], ); @@ -903,11 +819,10 @@ fn cast_to_stretch() { #[test] fn cast_to_int_array() { - check( - expr, + check_expr( "array[int[64], 4](0)", &expect![[r#" - Expr [0-17]: Cast [0-20]: + Expr [0-20]: Cast [0-20]: ArrayType [0-17]: ArrayBaseTypeKind IntType[Expr [10-12]: Lit: Int(64)]: [6-13] Expr [15-16]: Lit: Int(4) Expr [18-19]: Lit: Int(0)"#]], @@ -916,11 +831,10 @@ fn cast_to_int_array() { #[test] fn cast_to_uint_array() { - check( - expr, + check_expr( "array[uint[64], 4](0)", &expect![[r#" - Expr [0-18]: Cast [0-21]: + Expr [0-21]: Cast [0-21]: ArrayType [0-18]: ArrayBaseTypeKind UIntType[Expr [11-13]: Lit: Int(64)]: [6-14] Expr [16-17]: Lit: Int(4) Expr [19-20]: Lit: Int(0)"#]], @@ -929,11 +843,10 @@ fn cast_to_uint_array() { #[test] fn cast_to_float_array() { - check( - expr, + check_expr( "array[float[64], 4](0)", &expect![[r#" - Expr [0-19]: Cast [0-22]: + Expr [0-22]: Cast [0-22]: ArrayType [0-19]: ArrayBaseTypeKind FloatType[Expr [12-14]: Lit: Int(64)]: [6-15] Expr [17-18]: Lit: Int(4) Expr [20-21]: Lit: Int(0)"#]], @@ -942,11 +855,10 @@ fn cast_to_float_array() { #[test] fn cast_to_angle_array() { - check( - expr, + check_expr( "array[angle[64], 4](0)", &expect![[r#" - Expr [0-19]: Cast [0-22]: + Expr [0-22]: Cast [0-22]: ArrayType [0-19]: ArrayBaseTypeKind AngleType [6-15]: Expr [12-14]: Lit: Int(64) Expr [17-18]: Lit: Int(4) Expr [20-21]: Lit: Int(0)"#]], @@ -955,11 +867,10 @@ fn cast_to_angle_array() { #[test] fn cast_to_bool_array() { - check( - expr, + check_expr( "array[bool, 4](0)", &expect![[r#" - Expr [0-14]: Cast [0-17]: + Expr [0-17]: Cast [0-17]: ArrayType [0-14]: ArrayBaseTypeKind BoolType Expr [12-13]: Lit: Int(4) Expr [15-16]: Lit: Int(0)"#]], @@ -968,11 +879,10 @@ fn cast_to_bool_array() { #[test] fn cast_to_duration_array() { - check( - expr, + check_expr( "array[duration, 4](0)", &expect![[r#" - Expr [0-18]: Cast [0-21]: + Expr [0-21]: Cast [0-21]: ArrayType [0-18]: ArrayBaseTypeKind DurationType Expr [16-17]: Lit: Int(4) Expr [19-20]: Lit: Int(0)"#]], @@ -981,11 +891,10 @@ fn cast_to_duration_array() { #[test] fn cast_to_complex_array() { - check( - expr, + check_expr( "array[complex[float[32]], 4](0)", &expect![[r#" - Expr [0-28]: Cast [0-31]: + Expr [0-31]: Cast [0-31]: ArrayType [0-28]: ArrayBaseTypeKind ComplexType[float[FloatType[Expr [20-22]: Lit: Int(32)]: [14-23]]]: [6-24] Expr [26-27]: Lit: Int(4) Expr [29-30]: Lit: Int(0)"#]], @@ -994,8 +903,7 @@ fn cast_to_complex_array() { #[test] fn index_expr() { - check( - expr, + check_expr( "foo[1]", &expect![[r#" Expr [0-6]: IndexExpr [3-6]: Expr [0-3]: Ident [0-3] "foo", IndexElement: @@ -1005,8 +913,7 @@ fn index_expr() { #[test] fn index_set() { - check( - expr, + check_expr( "foo[{1, 4, 5}]", &expect![[r#" Expr [0-14]: IndexExpr [3-14]: Expr [0-3]: Ident [0-3] "foo", IndexElement DiscreteSet [4-13]: @@ -1018,8 +925,7 @@ fn index_set() { #[test] fn index_multiple_ranges() { - check( - expr, + check_expr( "foo[1:5, 3:7, 4:8]", &expect![[r#" Expr [0-18]: IndexExpr [3-18]: Expr [0-3]: Ident [0-3] "foo", IndexElement: @@ -1040,8 +946,7 @@ fn index_multiple_ranges() { #[test] fn index_range() { - check( - expr, + check_expr( "foo[1:5:2]", &expect![[r#" Expr [0-10]: IndexExpr [3-10]: Expr [0-3]: Ident [0-3] "foo", IndexElement: @@ -1054,8 +959,7 @@ fn index_range() { #[test] fn index_full_range() { - check( - expr, + check_expr( "foo[:]", &expect![[r#" Expr [0-6]: IndexExpr [3-6]: Expr [0-3]: Ident [0-3] "foo", IndexElement: @@ -1068,8 +972,7 @@ fn index_full_range() { #[test] fn index_range_start() { - check( - expr, + check_expr( "foo[1:]", &expect![[r#" Expr [0-7]: IndexExpr [3-7]: Expr [0-3]: Ident [0-3] "foo", IndexElement: @@ -1082,8 +985,7 @@ fn index_range_start() { #[test] fn index_range_end() { - check( - expr, + check_expr( "foo[:5]", &expect![[r#" Expr [0-7]: IndexExpr [3-7]: Expr [0-3]: Ident [0-3] "foo", IndexElement: @@ -1096,8 +998,7 @@ fn index_range_end() { #[test] fn index_range_step() { - check( - expr, + check_expr( "foo[:2:]", &expect![[r#" Expr [0-8]: IndexExpr [3-8]: Expr [0-3]: Ident [0-3] "foo", IndexElement: @@ -1135,8 +1036,7 @@ fn lit_array() { #[test] fn assignment_and_unop() { - check( - expr, + check_expr( "c = a && !b", &expect![[r#" Expr [0-11]: Assign: @@ -1150,8 +1050,7 @@ fn assignment_and_unop() { #[test] fn assignment_unop_and() { - check( - expr, + check_expr( "d = !a && b", &expect![[r#" Expr [0-11]: Assign: diff --git a/compiler/qsc_qasm3/src/parser/stmt.rs b/compiler/qsc_qasm3/src/parser/stmt.rs index 13508e287c..a18ac394ff 100644 --- a/compiler/qsc_qasm3/src/parser/stmt.rs +++ b/compiler/qsc_qasm3/src/parser/stmt.rs @@ -66,17 +66,17 @@ pub(super) fn parse(s: &mut ParserContext) -> Result> { let arg = expr::expr(s)?; recovering_token(s, TokenKind::Close(Delim::Paren)); let cast_expr = Expr { - span: s.span(lo), + span: s.span(ty.span().lo), kind: Box::new(ExprKind::Cast(Cast { - span: s.span(lo), + span: s.span(ty.span().lo), r#type: ty, arg, })), }; - Box::new(StmtKind::ExprStmt(ExprStmt { - span: s.span(lo), - expr: cast_expr, - })) + Box::new(StmtKind::ExprStmt(parse_expression_stmt( + s, + Some(cast_expr), + )?)) } } else if let Some(decl) = opt(s, parse_constant_classical_decl)? { Box::new(decl) @@ -1071,7 +1071,6 @@ fn parse_end_stmt(s: &mut ParserContext) -> Result { /// Grammar: `expression SEMICOLON`. fn parse_expression_stmt(s: &mut ParserContext, lhs: Option) -> Result { - let lo = s.peek().span.lo; let expr = if let Some(lhs) = lhs { expr::expr_with_lhs(s, lhs)? } else { @@ -1079,7 +1078,7 @@ fn parse_expression_stmt(s: &mut ParserContext, lhs: Option) -> Result miette::Result<(), Vec> { let res = parse_all("source0.qasm", all_sources)?; assert!(res.source.includes().len() == 1); - println!("{}", res.source.program); Ok(()) } From 746b2dfea39d78f891c13c9ff34944c1319211b6 Mon Sep 17 00:00:00 2001 From: Oscar Puente <156957451+orpuente-MS@users.noreply.github.com> Date: Fri, 28 Feb 2025 15:48:37 -0800 Subject: [PATCH 047/108] fix formatting --- compiler/qsc_qasm3/src/parser/expr/tests.rs | 20 ++++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) diff --git a/compiler/qsc_qasm3/src/parser/expr/tests.rs b/compiler/qsc_qasm3/src/parser/expr/tests.rs index e01d468c27..4d33c5c52e 100644 --- a/compiler/qsc_qasm3/src/parser/expr/tests.rs +++ b/compiler/qsc_qasm3/src/parser/expr/tests.rs @@ -319,10 +319,7 @@ fn lit_float_trailing_exp_dot() { #[test] fn lit_int_hexadecimal_dot() { - check_expr( - "0x123.45", - &expect!["Expr [0-5]: Lit: Int(291)"], - ); + check_expr("0x123.45", &expect!["Expr [0-5]: Lit: Int(291)"]); } #[test] @@ -1107,3 +1104,18 @@ fn measure_indexed_identifier() { IndexSetItem Expr [18-19]: Lit: Int(2)]"#]], ); } + +#[test] +fn addition_of_casts() { + check_expr( + "bit(0) + bit(1)", + &expect![[r#" + Expr [0-15]: BinOp (Add): + Expr [0-6]: Cast [0-6]: + ClassicalType [0-3]: BitType + Expr [4-5]: Lit: Int(0) + Expr [9-15]: Cast [9-15]: + ClassicalType [9-12]: BitType + Expr [13-14]: Lit: Int(1)"#]], + ); +} From b6116ac9b7655e937aff9b519b340d611e7952e3 Mon Sep 17 00:00:00 2001 From: Oscar Puente <156957451+orpuente-MS@users.noreply.github.com> Date: Fri, 28 Feb 2025 16:29:21 -0800 Subject: [PATCH 048/108] fix lexer bug --- compiler/qsc_qasm3/src/lex/cooked.rs | 15 ++++------ compiler/qsc_qasm3/src/lex/cooked/tests.rs | 31 ++++++++++++++++++++ compiler/qsc_qasm3/src/lex/raw/tests.rs | 32 +++++++++++++++++++++ compiler/qsc_qasm3/src/parser/expr/tests.rs | 19 +----------- 4 files changed, 69 insertions(+), 28 deletions(-) diff --git a/compiler/qsc_qasm3/src/lex/cooked.rs b/compiler/qsc_qasm3/src/lex/cooked.rs index c891ef4519..d3c5c67857 100644 --- a/compiler/qsc_qasm3/src/lex/cooked.rs +++ b/compiler/qsc_qasm3/src/lex/cooked.rs @@ -554,19 +554,14 @@ impl<'a> Lexer<'a> { raw::TokenKind::Number(number) => { // after reading a decimal number or a float there could be a whitespace // followed by a fragment, which will change the type of the literal. - match (self.first(), self.second()) { - (Some(raw::TokenKind::LiteralFragment(fragment)), _) - | ( - Some(raw::TokenKind::Whitespace), - Some(raw::TokenKind::LiteralFragment(fragment)), - ) => { + self.next_if_eq(raw::TokenKind::Whitespace); + + match self.first() { + Some(raw::TokenKind::LiteralFragment(fragment)) => { use self::Literal::{Imaginary, Timing}; use TokenKind::Literal; - // if first() was a whitespace, we need to consume an extra token - if self.first() == Some(raw::TokenKind::Whitespace) { - self.next(); - } + // Consume the fragment. self.next(); Ok(Some(match fragment { diff --git a/compiler/qsc_qasm3/src/lex/cooked/tests.rs b/compiler/qsc_qasm3/src/lex/cooked/tests.rs index 89d2bf0b5a..c721b5e97b 100644 --- a/compiler/qsc_qasm3/src/lex/cooked/tests.rs +++ b/compiler/qsc_qasm3/src/lex/cooked/tests.rs @@ -363,6 +363,37 @@ fn imag_with_whitespace() { ); } +#[test] +fn imag_with_whitespace_semicolon() { + check( + "123 im;", + &expect![[r#" + [ + Ok( + Token { + kind: Literal( + Imaginary, + ), + span: Span { + lo: 0, + hi: 6, + }, + }, + ), + Ok( + Token { + kind: Semicolon, + span: Span { + lo: 6, + hi: 7, + }, + }, + ), + ] + "#]], + ); +} + #[test] fn negative_imag() { check( diff --git a/compiler/qsc_qasm3/src/lex/raw/tests.rs b/compiler/qsc_qasm3/src/lex/raw/tests.rs index af9f480aff..88bbc351cc 100644 --- a/compiler/qsc_qasm3/src/lex/raw/tests.rs +++ b/compiler/qsc_qasm3/src/lex/raw/tests.rs @@ -1274,3 +1274,35 @@ fn hardware_qubit_with_underscore_in_the_middle() { "#]], ); } + +#[test] +fn decimal_space_imag_semicolon() { + check("10 im;", &expect![[r#" + [ + Token { + kind: Number( + Int( + Decimal, + ), + ), + offset: 0, + }, + Token { + kind: Whitespace, + offset: 2, + }, + Token { + kind: LiteralFragment( + Imag, + ), + offset: 4, + }, + Token { + kind: Single( + Semi, + ), + offset: 6, + }, + ] + "#]]); +} diff --git a/compiler/qsc_qasm3/src/parser/expr/tests.rs b/compiler/qsc_qasm3/src/parser/expr/tests.rs index 4d33c5c52e..4cb856efdf 100644 --- a/compiler/qsc_qasm3/src/parser/expr/tests.rs +++ b/compiler/qsc_qasm3/src/parser/expr/tests.rs @@ -4,27 +4,10 @@ use super::expr; use crate::{ ast::StmtKind, - parser::{scan::ParserContext, stmt, tests::check, Parser}, + parser::{scan::ParserContext, stmt, tests::check}, }; use expect_test::{expect, Expect}; -fn check_map( - mut parser: impl Parser, - input: &str, - expect: &Expect, - f: impl FnOnce(&T) -> String, -) { - let mut scanner = ParserContext::new(input); - let result = parser(&mut scanner); - let errors = scanner.into_errors(); - match result { - Ok(value) if errors.is_empty() => expect.assert_eq(&f(&value)), - Ok(value) => expect.assert_eq(&format!("{}\n\n{errors:#?}", f(&value))), - Err(error) if errors.is_empty() => expect.assert_debug_eq(&error), - Err(error) => expect.assert_eq(&format!("{error:#?}\n\n{errors:#?}")), - } -} - /// This function checks two things: /// 1. That the input `Expr` is parsed correctly. /// 2. That if we add a semicolon at the end it parses correctly as a `ExprStmt` From a6e1002a26d13606c99b022de55d266c8fda2631 Mon Sep 17 00:00:00 2001 From: Oscar Puente <156957451+orpuente-MS@users.noreply.github.com> Date: Fri, 28 Feb 2025 16:31:24 -0800 Subject: [PATCH 049/108] format code --- compiler/qsc_qasm3/src/lex/raw/tests.rs | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/compiler/qsc_qasm3/src/lex/raw/tests.rs b/compiler/qsc_qasm3/src/lex/raw/tests.rs index 88bbc351cc..aee13c0ab1 100644 --- a/compiler/qsc_qasm3/src/lex/raw/tests.rs +++ b/compiler/qsc_qasm3/src/lex/raw/tests.rs @@ -1277,7 +1277,9 @@ fn hardware_qubit_with_underscore_in_the_middle() { #[test] fn decimal_space_imag_semicolon() { - check("10 im;", &expect![[r#" + check( + "10 im;", + &expect![[r#" [ Token { kind: Number( @@ -1304,5 +1306,6 @@ fn decimal_space_imag_semicolon() { offset: 6, }, ] - "#]]); + "#]], + ); } From ed2252e245da7ac35ea3a6547942c2958f3cda0a Mon Sep 17 00:00:00 2001 From: Oscar Puente <156957451+orpuente-MS@users.noreply.github.com> Date: Fri, 28 Feb 2025 16:54:30 -0800 Subject: [PATCH 050/108] fix lexer bug and remove `second` from cooked lexer --- compiler/qsc_qasm3/src/lex/cooked.rs | 49 ++++++++++----------- compiler/qsc_qasm3/src/parser/expr/tests.rs | 11 +++++ 2 files changed, 34 insertions(+), 26 deletions(-) diff --git a/compiler/qsc_qasm3/src/lex/cooked.rs b/compiler/qsc_qasm3/src/lex/cooked.rs index d3c5c67857..78f1008158 100644 --- a/compiler/qsc_qasm3/src/lex/cooked.rs +++ b/compiler/qsc_qasm3/src/lex/cooked.rs @@ -461,14 +461,6 @@ impl<'a> Lexer<'a> { self.tokens.peek().map(|i| i.kind) } - /// Returns the second token ahead of the cursor without consuming it. This is slower - /// than [`first`] and should be avoided when possible. - fn second(&self) -> Option { - let mut tokens = self.tokens.clone(); - tokens.next(); - tokens.next().map(|i| i.kind) - } - /// Consumes the characters while they satisfy `f`. Returns the last character eaten, if any. fn eat_while(&mut self, mut f: impl FnMut(raw::TokenKind) -> bool) -> Option { let mut last_eaten: Option = None; @@ -554,26 +546,31 @@ impl<'a> Lexer<'a> { raw::TokenKind::Number(number) => { // after reading a decimal number or a float there could be a whitespace // followed by a fragment, which will change the type of the literal. + let numeric_part_hi = self.offset(); self.next_if_eq(raw::TokenKind::Whitespace); - match self.first() { - Some(raw::TokenKind::LiteralFragment(fragment)) => { - use self::Literal::{Imaginary, Timing}; - use TokenKind::Literal; - - // Consume the fragment. - self.next(); - - Ok(Some(match fragment { - raw::LiteralFragmentKind::Imag => Literal(Imaginary), - raw::LiteralFragmentKind::Dt => Literal(Timing(TimingLiteralKind::Dt)), - raw::LiteralFragmentKind::Ns => Literal(Timing(TimingLiteralKind::Ns)), - raw::LiteralFragmentKind::Us => Literal(Timing(TimingLiteralKind::Us)), - raw::LiteralFragmentKind::Ms => Literal(Timing(TimingLiteralKind::Ms)), - raw::LiteralFragmentKind::S => Literal(Timing(TimingLiteralKind::S)), - })) - } - _ => Ok(Some(number.into())), + if let Some(raw::TokenKind::LiteralFragment(fragment)) = self.first() { + use self::Literal::{Imaginary, Timing}; + use TokenKind::Literal; + + // Consume the fragment. + self.next(); + + Ok(Some(match fragment { + raw::LiteralFragmentKind::Imag => Literal(Imaginary), + raw::LiteralFragmentKind::Dt => Literal(Timing(TimingLiteralKind::Dt)), + raw::LiteralFragmentKind::Ns => Literal(Timing(TimingLiteralKind::Ns)), + raw::LiteralFragmentKind::Us => Literal(Timing(TimingLiteralKind::Us)), + raw::LiteralFragmentKind::Ms => Literal(Timing(TimingLiteralKind::Ms)), + raw::LiteralFragmentKind::S => Literal(Timing(TimingLiteralKind::S)), + })) + } else { + let kind: TokenKind = number.into(); + let span = Span { + lo: token.offset, + hi: numeric_part_hi, + }; + return Ok(Some(Token { kind, span })); } } raw::TokenKind::Single(Single::Sharp) => { diff --git a/compiler/qsc_qasm3/src/parser/expr/tests.rs b/compiler/qsc_qasm3/src/parser/expr/tests.rs index 4cb856efdf..455a3bcdff 100644 --- a/compiler/qsc_qasm3/src/parser/expr/tests.rs +++ b/compiler/qsc_qasm3/src/parser/expr/tests.rs @@ -535,6 +535,17 @@ fn lit_float_imag_leading_zero() { check_expr("0.23im", &expect!["Expr [0-6]: Lit: Imaginary(0.23)"]); } +#[test] +fn pratt_parsing_binary_expr() { + check_expr( + "1 + 2", + &expect![[r#" + Expr [0-5]: BinOp (Add): + Expr [0-1]: Lit: Int(1) + Expr [4-5]: Lit: Int(2)"#]], + ); +} + #[test] fn pratt_parsing_mul_add() { check_expr( From 608fff017c13c4614674132399f463fbfbd7b63a Mon Sep 17 00:00:00 2001 From: Oscar Puente <156957451+orpuente-MS@users.noreply.github.com> Date: Fri, 28 Feb 2025 17:00:12 -0800 Subject: [PATCH 051/108] update test --- compiler/qsc_qasm3/src/parser/stmt/tests/expr_stmt.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/compiler/qsc_qasm3/src/parser/stmt/tests/expr_stmt.rs b/compiler/qsc_qasm3/src/parser/stmt/tests/expr_stmt.rs index 7799100efb..f5a5082ad7 100644 --- a/compiler/qsc_qasm3/src/parser/stmt/tests/expr_stmt.rs +++ b/compiler/qsc_qasm3/src/parser/stmt/tests/expr_stmt.rs @@ -117,8 +117,8 @@ fn cast_expr() { parse, "bit(0);", &expect![[r#" - Stmt [0-6] - StmtKind: ExprStmt [0-6]: Expr [0-6]: Cast [0-6]: + Stmt [0-7] + StmtKind: ExprStmt [0-7]: Expr [0-6]: Cast [0-6]: ClassicalType [0-3]: BitType Expr [4-5]: Lit: Int(0)"#]], ); @@ -130,8 +130,8 @@ fn cast_expr_with_designator() { parse, "bit[45](0);", &expect![[r#" - Stmt [0-10] - StmtKind: ExprStmt [0-10]: Expr [0-10]: Cast [0-10]: + Stmt [0-11] + StmtKind: ExprStmt [0-11]: Expr [0-10]: Cast [0-10]: ClassicalType [0-7]: BitType [0-7]: Expr [4-6]: Lit: Int(45) Expr [8-9]: Lit: Int(0)"#]], ); From 6713fdb57164bd43281cdbe549c1c50ca0e74de7 Mon Sep 17 00:00:00 2001 From: Oscar Puente <156957451+orpuente-MS@users.noreply.github.com> Date: Mon, 3 Mar 2025 09:29:30 -0800 Subject: [PATCH 052/108] remove extra scope --- compiler/qsc_qasm3/src/parser/expr.rs | 44 +++++++++++++-------------- 1 file changed, 21 insertions(+), 23 deletions(-) diff --git a/compiler/qsc_qasm3/src/parser/expr.rs b/compiler/qsc_qasm3/src/parser/expr.rs index 218569b7ba..586951d57a 100644 --- a/compiler/qsc_qasm3/src/parser/expr.rs +++ b/compiler/qsc_qasm3/src/parser/expr.rs @@ -409,30 +409,28 @@ fn lit_bigint(lexeme: &str, radix: u32) -> Option { } fn timing_literal(lexeme: &str, token: Token, kind: TimingLiteralKind) -> Result> { - { - let lexeme = lexeme - .chars() - .filter(|x| *x != '_') - .take_while(|x| x.is_numeric() || *x == '.') - .collect::(); - - let value = lexeme - .parse() - .map_err(|_| Error::new(ErrorKind::Lit("timing", token.span)))?; - - let unit = match kind { - TimingLiteralKind::Dt => TimeUnit::Dt, - TimingLiteralKind::Ns => TimeUnit::Ns, - TimingLiteralKind::Us => TimeUnit::Us, - TimingLiteralKind::Ms => TimeUnit::Ms, - TimingLiteralKind::S => TimeUnit::S, - }; + let lexeme = lexeme + .chars() + .filter(|x| *x != '_') + .take_while(|x| x.is_numeric() || *x == '.') + .collect::(); + + let value = lexeme + .parse() + .map_err(|_| Error::new(ErrorKind::Lit("timing", token.span)))?; + + let unit = match kind { + TimingLiteralKind::Dt => TimeUnit::Dt, + TimingLiteralKind::Ns => TimeUnit::Ns, + TimingLiteralKind::Us => TimeUnit::Us, + TimingLiteralKind::Ms => TimeUnit::Ms, + TimingLiteralKind::S => TimeUnit::S, + }; - Ok(Some(Lit { - span: token.span, - kind: LiteralKind::Duration { value, unit }, - })) - } + Ok(Some(Lit { + span: token.span, + kind: LiteralKind::Duration { value, unit }, + })) } pub(crate) fn paren_expr(s: &mut ParserContext, lo: u32) -> Result { From 5d42598ea375bb07220ba2608109b32c6cc56404 Mon Sep 17 00:00:00 2001 From: orpuente-MS <156957451+orpuente-MS@users.noreply.github.com> Date: Wed, 5 Mar 2025 11:54:53 -0800 Subject: [PATCH 053/108] Tidy up Display functions in QASM3 parser (#2209) This PR doesn't change any behavior. It just touches the Display functions in the QASM3 parser to make unit tests easier to read. --- compiler/qsc_qasm3/src/ast.rs | 981 ++++++------------ compiler/qsc_qasm3/src/ast/display_utils.rs | 206 ++++ compiler/qsc_qasm3/src/parser/expr.rs | 20 +- compiler/qsc_qasm3/src/parser/expr/tests.rs | 449 +++++--- compiler/qsc_qasm3/src/parser/prim/tests.rs | 42 +- compiler/qsc_qasm3/src/parser/stmt.rs | 80 +- .../qsc_qasm3/src/parser/stmt/tests/alias.rs | 52 +- .../src/parser/stmt/tests/annotation.rs | 20 +- .../src/parser/stmt/tests/barrier.rs | 27 +- .../src/parser/stmt/tests/box_stmt.rs | 114 +- .../qsc_qasm3/src/parser/stmt/tests/cal.rs | 5 +- .../src/parser/stmt/tests/cal_grammar.rs | 6 +- .../src/parser/stmt/tests/classical_decl.rs | 528 +++++++--- .../qsc_qasm3/src/parser/stmt/tests/def.rs | 177 +++- .../qsc_qasm3/src/parser/stmt/tests/defcal.rs | 5 +- .../qsc_qasm3/src/parser/stmt/tests/delay.rs | 25 +- .../src/parser/stmt/tests/expr_stmt.rs | 121 ++- .../src/parser/stmt/tests/extern_decl.rs | 156 ++- .../src/parser/stmt/tests/for_loops.rs | 208 ++-- .../src/parser/stmt/tests/gate_call.rs | 186 +++- .../src/parser/stmt/tests/gate_def.rs | 146 ++- .../qsc_qasm3/src/parser/stmt/tests/gphase.rs | 134 ++- .../src/parser/stmt/tests/if_stmt.rs | 139 ++- .../src/parser/stmt/tests/io_decl.rs | 266 +++-- .../src/parser/stmt/tests/measure.rs | 62 +- .../src/parser/stmt/tests/old_style_decl.rs | 32 +- .../qsc_qasm3/src/parser/stmt/tests/pragma.rs | 49 +- .../src/parser/stmt/tests/quantum_decl.rs | 44 +- .../qsc_qasm3/src/parser/stmt/tests/reset.rs | 27 +- .../src/parser/stmt/tests/switch_stmt.rs | 91 +- .../src/parser/stmt/tests/while_loops.rs | 112 +- 31 files changed, 2873 insertions(+), 1637 deletions(-) create mode 100644 compiler/qsc_qasm3/src/ast/display_utils.rs diff --git a/compiler/qsc_qasm3/src/ast.rs b/compiler/qsc_qasm3/src/ast.rs index 295a59f16f..24ca614a38 100644 --- a/compiler/qsc_qasm3/src/ast.rs +++ b/compiler/qsc_qasm3/src/ast.rs @@ -4,28 +4,20 @@ // while we work through the conversion, allow dead code to avoid warnings #![allow(dead_code)] -use indenter::{indented, Indented}; +mod display_utils; + +use display_utils::{ + write_field, write_header, write_indented_list, write_list_field, write_opt_field, + write_opt_list_field, writeln_field, writeln_header, writeln_list_field, writeln_opt_field, +}; use num_bigint::BigInt; use qsc_data_structures::span::{Span, WithSpan}; use std::{ - fmt::{self, Display, Formatter, Write}, + fmt::{self, Display, Formatter}, hash::Hash, rc::Rc, }; -fn set_indentation<'a, 'b>( - indent: Indented<'a, Formatter<'b>>, - level: usize, -) -> Indented<'a, Formatter<'b>> { - match level { - 0 => indent.with_str(""), - 1 => indent.with_str(" "), - 2 => indent.with_str(" "), - 3 => indent.with_str(" "), - _ => unimplemented!("indentation level not supported"), - } -} - // TODO: Profile this with iai-callgrind in a large OpenQASM3 // sample to verify that is actually faster than using Vec. // Even though Box uses less stack space, it reduces cache @@ -47,16 +39,9 @@ pub struct Program { impl Display for Program { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { - let mut indent = set_indentation(indented(f), 0); - write!(indent, "Program {}:", self.span)?; - indent = set_indentation(indent, 1); - if let Some(version) = &self.version { - write!(indent, "\nVersion {version}")?; - } - for stmt in &self.statements { - write!(indent, "\n{stmt}")?; - } - Ok(()) + writeln_header(f, "Program", self.span)?; + writeln_opt_field(f, "version", self.version.as_ref())?; + write_list_field(f, "statements", &self.statements) } } @@ -69,14 +54,9 @@ pub struct Stmt { impl Display for Stmt { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { - let mut indent = set_indentation(indented(f), 0); - write!(indent, "Stmt {}", self.span)?; - indent = set_indentation(indent, 1); - for annotation in &self.annotations { - write!(indent, "\n{annotation}")?; - } - write!(indent, "\n{}", self.kind)?; - Ok(()) + writeln_header(f, "Stmt", self.span)?; + writeln_list_field(f, "annotations", &self.annotations)?; + write_field(f, "kind", &self.kind) } } @@ -88,15 +68,11 @@ pub struct Annotation { } impl Display for Annotation { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { - if let Some(value) = &self.value { - write!( - f, - "Annotation {}: ({}, {})", - self.span, self.identifier, value - ) - } else { - write!(f, "Annotation {}: ({})", self.span, self.identifier) - } + let identifier = format!("\"{}\"", self.identifier); + let value = self.value.as_ref().map(|val| format!("\"{val}\"")); + writeln_header(f, "Annotation", self.span)?; + writeln_field(f, "identifier", &identifier)?; + write_opt_field(f, "value", value.as_ref()) } } @@ -118,18 +94,13 @@ impl Default for PathKind { impl Display for PathKind { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { match self { - PathKind::Ok(path) => write!(f, "{path}")?, + PathKind::Ok(path) => write!(f, "{path}"), PathKind::Err(Some(incomplete_path)) => { - let mut indent = set_indentation(indented(f), 0); - write!(indent, "Err IncompletePath {}:", incomplete_path.span)?; - indent = set_indentation(indent, 1); - for part in &incomplete_path.segments { - write!(indent, "\n{part}")?; - } + write!(f, "Err IncompletePath {}:", incomplete_path.span)?; + write_list_field(f, "segments", &incomplete_path.segments) } - PathKind::Err(None) => write!(f, "Err",)?, + PathKind::Err(None) => write!(f, "Err",), } - Ok(()) } } @@ -162,20 +133,9 @@ pub struct Path { impl Display for Path { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { - if self.segments.is_none() { - write!(f, "Path {} ({})", self.span, self.name)?; - } else { - let mut indent = set_indentation(indented(f), 0); - write!(indent, "Path {}:", self.span)?; - indent = set_indentation(indent, 1); - if let Some(parts) = &self.segments { - for part in parts { - write!(indent, "\n{part}")?; - } - } - write!(indent, "\n{}", self.name)?; - } - Ok(()) + writeln_header(f, "Path", self.span)?; + writeln_field(f, "name", &self.name)?; + write_opt_list_field(f, "segments", self.segments.as_ref()) } } @@ -208,9 +168,11 @@ pub struct MeasureExpr { impl Display for MeasureExpr { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { - write!(f, "MeasureExpr {}: {}", self.span, self.operand) + writeln_header(f, "MeasureExpr", self.span)?; + write_field(f, "operand", &self.operand) } } + /// A binary operator. #[derive(Clone, Copy, Debug)] pub enum BinOp { @@ -312,8 +274,8 @@ pub enum GateOperand { impl Display for GateOperand { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { match self { - GateOperand::IndexedIdent(ident) => write!(f, "GateOperand {ident}"), - GateOperand::HardwareQubit(qubit) => write!(f, "GateOperand {qubit}"), + GateOperand::IndexedIdent(ident) => write!(f, "{ident}"), + GateOperand::HardwareQubit(qubit) => write!(f, "{qubit}"), GateOperand::Err => write!(f, "Error"), } } @@ -339,51 +301,16 @@ impl Display for HardwareQubit { #[derive(Clone, Debug)] pub struct AliasDeclStmt { + pub span: Span, pub ident: Identifier, pub exprs: List, - pub span: Span, } impl Display for AliasDeclStmt { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { - let mut indent = set_indentation(indented(f), 0); - write!(indent, "Alias {}: {}", self.span, self.ident)?; - indent = set_indentation(indent, 1); - for expr in &*self.exprs { - write!(indent, "\n{expr}")?; - } - Ok(()) - } -} - -#[derive(Clone, Debug)] -pub struct Assign { - pub ident: Box, - pub expr: Box, - pub span: Span, -} - -impl Display for Assign { - fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { - write!(f, "Assign {}: {}, {}", self.span, self.ident, self.expr) - } -} - -#[derive(Clone, Debug)] -pub struct AssignOp { - pub op: BinOp, - pub ident: Box, - pub expr: Box, - pub span: Span, -} - -impl Display for AssignOp { - fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { - write!( - f, - "AssignOp {}: {}, {}, {}", - self.span, self.op, self.ident, self.expr - ) + writeln_header(f, "AliasDeclStmt", self.span)?; + writeln_field(f, "ident", &self.ident)?; + write_list_field(f, "exprs", &self.exprs) } } @@ -391,8 +318,6 @@ impl Display for AssignOp { #[derive(Clone, Debug, Default)] pub enum StmtKind { Alias(AliasDeclStmt), - Assign(Assign), - AssignOp(AssignOp), Barrier(BarrierStmt), Box(BoxStmt), Break(BreakStmt), @@ -431,11 +356,8 @@ pub enum StmtKind { impl Display for StmtKind { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { - write!(f, "StmtKind: ")?; match self { StmtKind::Alias(alias) => write!(f, "{alias}"), - StmtKind::Assign(assign) => write!(f, "{assign}"), - StmtKind::AssignOp(assign_op) => write!(f, "{assign_op}"), StmtKind::Barrier(barrier) => write!(f, "{barrier}"), StmtKind::Box(box_stmt) => write!(f, "{box_stmt}"), StmtKind::Break(break_stmt) => write!(f, "{break_stmt}"), @@ -479,7 +401,8 @@ pub struct CalibrationGrammarStmt { impl Display for CalibrationGrammarStmt { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { - write!(f, "CalibrationGrammarStmt {}: {}", self.span, self.name) + writeln_header(f, "CalibrationGrammarStmt", self.span)?; + write_field(f, "name", &self.name) } } @@ -504,18 +427,10 @@ pub struct IfStmt { impl Display for IfStmt { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { - let mut indent = set_indentation(indented(f), 0); - write!(indent, "IfStmt {}: {}", self.span, self.condition)?; - for stmt in &self.if_block { - write!(indent, "\n{stmt}")?; - } - if let Some(else_block) = &self.else_block { - write!(indent, "\nElse:")?; - for stmt in else_block { - write!(indent, "\n{stmt}")?; - } - } - Ok(()) + writeln_header(f, "IfStmt", self.span)?; + writeln_field(f, "condition", &self.condition)?; + writeln_list_field(f, "if_block", &self.if_block)?; + write_opt_list_field(f, "else_block", self.else_block.as_ref()) } } @@ -527,12 +442,8 @@ pub struct BarrierStmt { impl Display for BarrierStmt { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { - let mut indent = set_indentation(indented(f), 0); - write!(indent, "Barrier {}: [", self.span)?; - for qubit in &self.qubits { - write!(indent, "\n{qubit}")?; - } - write!(indent, "]") + writeln_header(f, "BarrierStmt", self.span)?; + write_list_field(f, "operands", &self.qubits) } } @@ -544,7 +455,8 @@ pub struct ResetStmt { impl Display for ResetStmt { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { - write!(f, "ResetStmt {}: {}", self.span, self.operand) + writeln_header(f, "ResetStmt", self.span)?; + write_field(f, "operand", &self.operand) } } @@ -559,17 +471,8 @@ pub struct Block { impl Display for Block { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { - if self.stmts.is_empty() { - write!(f, "Block {}: ", self.span)?; - } else { - let mut indent = set_indentation(indented(f), 0); - write!(indent, "Block {}:", self.span)?; - indent = set_indentation(indent, 1); - for s in &self.stmts { - write!(indent, "\n{s}")?; - } - } - Ok(()) + write_header(f, "Block", self.span)?; + write_indented_list(f, &self.stmts) } } @@ -624,12 +527,9 @@ pub struct IndexedIdent { impl Display for IndexedIdent { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { - write!(f, "IndexedIdent {}: {}[", self.span, self.name)?; - - for index in &self.indices { - write!(f, "\n{index}")?; - } - write!(f, "]") + writeln_header(f, "IndexedIdent", self.span)?; + writeln_field(f, "name", &self.name)?; + write_list_field(f, "indices", &self.indices) } } @@ -641,7 +541,8 @@ pub struct ExprStmt { impl Display for ExprStmt { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { - write!(f, "ExprStmt {}: {}", self.span, self.expr) + writeln_header(f, "ExprStmt", self.span)?; + write_field(f, "expr", &self.expr) } } @@ -666,18 +567,26 @@ impl Display for Expr { #[derive(Clone, Debug)] pub struct DiscreteSet { pub span: Span, - pub values: Box<[Expr]>, + pub values: List, } impl Display for DiscreteSet { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { - let mut indent = set_indentation(indented(f), 0); - write!(indent, "DiscreteSet {}:", self.span)?; - indent = set_indentation(indent, 1); - for value in &self.values { - write!(indent, "\n{value}")?; - } - Ok(()) + writeln_header(f, "DiscreteSet", self.span)?; + write_list_field(f, "values", &self.values) + } +} + +#[derive(Clone, Debug)] +pub struct IndexSet { + pub span: Span, + pub values: List, +} + +impl Display for IndexSet { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + writeln_header(f, "IndexSet", self.span)?; + write_list_field(f, "values", &self.values) } } @@ -689,6 +598,15 @@ pub struct RangeDefinition { pub step: Option, } +impl Display for RangeDefinition { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + writeln_header(f, "RangeDefinition", self.span)?; + writeln_opt_field(f, "start", self.start.as_ref())?; + writeln_opt_field(f, "step", self.step.as_ref())?; + write_opt_field(f, "end", self.end.as_ref()) + } +} + #[derive(Clone, Debug)] pub struct QuantumGateModifier { pub span: Span, @@ -793,7 +711,7 @@ pub struct ScalarType { impl Display for ScalarType { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { - write!(f, "ClassicalType {}: {}", self.span, self.kind) + write!(f, "ScalarType {}: {}", self.span, self.kind) } } @@ -856,97 +774,79 @@ impl Display for ArrayBaseTypeKind { #[derive(Clone, Debug)] pub struct IntType { - pub size: Option, pub span: Span, + pub size: Option, } impl Display for IntType { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { - if let Some(size) = &self.size { - write!(f, "IntType[{}]: {}", size, self.span) - } else { - write!(f, "IntType {}", self.span) - } + writeln_header(f, "IntType", self.span)?; + write_opt_field(f, "size", self.size.as_ref()) } } #[derive(Clone, Debug)] pub struct UIntType { - pub size: Option, pub span: Span, + pub size: Option, } impl Display for UIntType { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { - if let Some(size) = &self.size { - write!(f, "UIntType[{}]: {}", size, self.span) - } else { - write!(f, "UIntType {}", self.span) - } + writeln_header(f, "UIntType", self.span)?; + write_opt_field(f, "size", self.size.as_ref()) } } #[derive(Clone, Debug)] pub struct FloatType { - pub size: Option, pub span: Span, + pub size: Option, } impl Display for FloatType { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { - if let Some(size) = &self.size { - write!(f, "FloatType[{}]: {}", size, self.span) - } else { - write!(f, "FloatType {}", self.span) - } + writeln_header(f, "FloatType", self.span)?; + write_opt_field(f, "size", self.size.as_ref()) } } #[derive(Clone, Debug)] pub struct ComplexType { - pub base_size: Option, pub span: Span, + pub base_size: Option, } impl Display for ComplexType { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { - if let Some(size) = &self.base_size { - write!(f, "ComplexType[float[{}]]: {}", size, self.span) - } else { - write!(f, "ComplexType {}", self.span) - } + writeln_header(f, "ComplexType", self.span)?; + write_opt_field(f, "base_size", self.base_size.as_ref()) } } #[derive(Clone, Debug)] pub struct AngleType { - pub size: Option, pub span: Span, + pub size: Option, } impl Display for AngleType { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { - if let Some(size) = &self.size { - write!(f, "AngleType {}: {}", self.span, size) - } else { - write!(f, "AngleType {}", self.span) - } + writeln_header(f, "AngleType", self.span)?; + write_opt_field(f, "size", self.size.as_ref()) } } #[derive(Clone, Debug)] pub struct BitType { - pub size: Option, pub span: Span, + pub size: Option, } impl Display for BitType { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { - if let Some(size) = &self.size { - write!(f, "BitType {}: {}", self.span, size) - } else { - write!(f, "BitType") - } + writeln_header(f, "BitType", self.span)?; + write_opt_field(f, "size", self.size.as_ref()) } } @@ -986,12 +886,9 @@ pub struct ArrayType { impl Display for ArrayType { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { - let mut indent = set_indentation(indented(f), 0); - write!(indent, "ArrayType {}: {}", self.span, self.base_type)?; - for dimension in &self.dimensions { - write!(indent, "\n{dimension}")?; - } - Ok(()) + writeln_header(f, "ArrayType", self.span)?; + writeln_field(f, "base_type", &self.base_type)?; + write_list_field(f, "dimensions", &self.dimensions) } } @@ -1005,16 +902,10 @@ pub struct ArrayReferenceType { impl Display for ArrayReferenceType { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { - let mut indent = set_indentation(indented(f), 0); - write!( - indent, - "ArrayReferenceType {}: {}", - self.span, self.base_type - )?; - for dimension in &self.dimensions { - write!(indent, "\n{dimension}")?; - } - Ok(()) + writeln_header(f, "ArrayReferenceType", self.span)?; + writeln_field(f, "mutability", &self.mutability)?; + writeln_field(f, "base_type", &self.base_type)?; + writeln_list_field(f, "dimensions", &self.dimensions) } } @@ -1041,7 +932,8 @@ pub struct QuantumArgument { impl Display for QuantumArgument { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { - write!(f, "QuantumArgument {}: {:?}", self.span, self.expr) + writeln_header(f, "QuantumArgument", self.span)?; + write_opt_field(f, "expr", self.expr.as_ref()) } } @@ -1054,28 +946,11 @@ pub struct Pragma { impl Display for Pragma { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { - if let Some(value) = &self.value { - write!(f, "Pragma {}: ({}, {})", self.span, self.identifier, value) - } else { - write!(f, "Pragma {}: ({})", self.span, self.identifier) - } - } -} - -#[derive(Clone, Debug)] -pub struct CompoundStmt { - pub span: Span, - pub statements: List, -} - -impl Display for CompoundStmt { - fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { - let mut indent = set_indentation(indented(f), 0); - write!(indent, "CompoundStmt {}:", self.span)?; - for stmt in &self.statements { - write!(indent, "\n{stmt}")?; - } - Ok(()) + let identifier = format!("\"{}\"", self.identifier); + let value = self.value.as_ref().map(|val| format!("\"{val}\"")); + writeln_header(f, "Pragma", self.span)?; + writeln_field(f, "identifier", &identifier)?; + write_opt_field(f, "value", value.as_ref()) } } @@ -1087,28 +962,23 @@ pub struct IncludeStmt { impl Display for IncludeStmt { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { - write!(f, "IncludeStmt {}: {}", self.span, self.filename) + writeln_header(f, "IncludeStmt", self.span)?; + write_field(f, "filename", &self.filename) } } #[derive(Clone, Debug)] pub struct QubitDeclaration { pub span: Span, - pub qubit: Box, + pub qubit: Ident, pub size: Option, } impl Display for QubitDeclaration { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { - if let Some(size) = &self.size { - write!( - f, - "QubitDeclaration {}: {}, {}", - self.span, self.qubit, size - ) - } else { - write!(f, "QubitDeclaration {}: {}", self.span, self.qubit) - } + writeln_header(f, "QubitDeclaration", self.span)?; + writeln_field(f, "ident", &self.qubit)?; + write_opt_field(f, "size", self.size.as_ref()) } } @@ -1123,35 +993,11 @@ pub struct QuantumGateDefinition { impl Display for QuantumGateDefinition { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { - let mut indent = set_indentation(indented(f), 0); - write!(indent, "Gate {}: {}", self.span, self.ident)?; - write!(indent, "(")?; - if self.params.is_empty() { - write!(indent, "")?; - } else { - let param_str = self - .params - .iter() - .map(std::string::ToString::to_string) - .collect::>() - .join(", "); - write!(indent, "{param_str}")?; - } - write!(indent, ") ")?; - - let qubit_str = self - .qubits - .iter() - .map(std::string::ToString::to_string) - .collect::>() - .join(", "); - write!(indent, "{qubit_str}")?; - - writeln!(indent)?; - for stmt in &self.body.stmts { - write!(indent, "\n{stmt}")?; - } - Ok(()) + writeln_header(f, "Gate", self.span)?; + writeln_field(f, "ident", &self.ident)?; + writeln_list_field(f, "parameters", &self.params)?; + writeln_list_field(f, "qubits", &self.qubits)?; + write_field(f, "body", &self.body) } } @@ -1165,15 +1011,10 @@ pub struct ExternDecl { impl Display for ExternDecl { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { - let mut indent = set_indentation(indented(f), 0); - write!(indent, "ExternDecl {}: {}", self.span, self.ident)?; - for arg in &self.params { - write!(indent, "\n{arg}")?; - } - if let Some(return_type) = &self.return_type { - write!(indent, "\n{return_type}")?; - } - Ok(()) + writeln_header(f, "ExternDecl", self.span)?; + writeln_field(f, "ident", &self.ident)?; + writeln_list_field(f, "parameters", &self.params)?; + write_opt_field(f, "return_type", self.return_type.as_ref()) } } @@ -1189,18 +1030,12 @@ pub struct GateCall { impl Display for GateCall { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { - let mut indent = set_indentation(indented(f), 0); - write!(indent, "GateCall {}: {}", self.span, self.name)?; - for arg in &self.args { - write!(indent, "\n{arg}")?; - } - for qubit in &self.qubits { - write!(indent, "\n{qubit}")?; - } - if let Some(duration) = &self.duration { - write!(indent, "\n{duration}")?; - } - Ok(()) + writeln_header(f, "GateCall", self.span)?; + writeln_list_field(f, "modifiers", &self.modifiers)?; + writeln_field(f, "name", &self.name)?; + writeln_list_field(f, "args", &self.args)?; + writeln_opt_field(f, "duration", self.duration.as_ref())?; + write_list_field(f, "qubits", &self.qubits) } } @@ -1215,18 +1050,11 @@ pub struct GPhase { impl Display for GPhase { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { - let mut indent = set_indentation(indented(f), 0); - write!(indent, "GPhase {}:", self.span)?; - for arg in &self.args { - write!(indent, "\n{arg}")?; - } - for qubit in &self.qubits { - write!(indent, "\n{qubit}")?; - } - if let Some(duration) = &self.duration { - write!(indent, "\n{duration}")?; - } - Ok(()) + writeln_header(f, "GPhase", self.span)?; + writeln_list_field(f, "modifiers", &self.modifiers)?; + writeln_list_field(f, "args", &self.args)?; + writeln_opt_field(f, "duration", self.duration.as_ref())?; + write_list_field(f, "qubits", &self.qubits) } } @@ -1239,12 +1067,9 @@ pub struct DelayStmt { impl Display for DelayStmt { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { - let mut indent = set_indentation(indented(f), 0); - write!(indent, "DelayInstruction {}: {}", self.span, self.duration)?; - for qubit in &self.qubits { - write!(indent, "\n{qubit}")?; - } - Ok(()) + writeln_header(f, "DelayStmt", self.span)?; + writeln_field(f, "duration", &self.duration)?; + write_list_field(f, "qubits", &self.qubits) } } @@ -1257,16 +1082,9 @@ pub struct BoxStmt { impl Display for BoxStmt { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { - let mut indent = set_indentation(indented(f), 0); - if let Some(duration) = &self.duration { - write!(indent, "BoxStmt {}: {}", self.span, duration)?; - } else { - write!(indent, "BoxStmt {}: ", self.span)?; - } - for stmt in &self.body { - write!(indent, "\n{stmt}")?; - } - Ok(()) + writeln_header(f, "BoxStmt", self.span)?; + writeln_opt_field(f, "duration", self.duration.as_ref())?; + write_list_field(f, "body", &self.body) } } @@ -1279,41 +1097,26 @@ pub struct MeasureStmt { impl Display for MeasureStmt { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { - if let Some(target) = &self.target { - write!( - f, - "MeasureStmt {}: {}, {}", - self.span, self.measurement, target - ) - } else { - write!(f, "MeasureStmt {}: {}", self.span, self.measurement) - } + writeln_header(f, "MeasureStmt", self.span)?; + writeln_field(f, "measurement", &self.measurement)?; + write_opt_field(f, "target", self.target.as_ref()) } } #[derive(Clone, Debug)] pub struct ClassicalDeclarationStmt { pub span: Span, - pub r#type: TypeDef, - pub identifier: Box, + pub r#type: Box, + pub identifier: Ident, pub init_expr: Option>, } impl Display for ClassicalDeclarationStmt { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { - if let Some(init_expr) = &self.init_expr { - write!( - f, - "ClassicalDeclarationStmt {}: {}, {}, {}", - self.span, self.r#type, self.identifier, init_expr - ) - } else { - write!( - f, - "ClassicalDeclarationStmt {}: {}, {}", - self.span, self.r#type, self.identifier - ) - } + writeln_header(f, "ClassicalDeclarationStmt", self.span)?; + writeln_field(f, "type", &self.r#type)?; + writeln_field(f, "ident", &self.identifier)?; + write_opt_field(f, "init_expr", self.init_expr.as_ref()) } } @@ -1326,8 +1129,8 @@ pub enum ValueExpression { impl Display for ValueExpression { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { match self { - ValueExpression::Expr(expr) => write!(f, "ValueExpression {expr}"), - ValueExpression::Measurement(measure) => write!(f, "ValueExpression {measure}"), + ValueExpression::Expr(expr) => write!(f, "{expr}"), + ValueExpression::Measurement(measure) => write!(f, "{measure}"), } } } @@ -1342,11 +1145,10 @@ pub struct IODeclaration { impl Display for IODeclaration { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { - write!( - f, - "IODeclaration {}: {}, {}, {}", - self.span, self.io_identifier, self.r#type, self.ident - ) + writeln_header(f, "IODeclaration", self.span)?; + writeln_field(f, "io_keyword", &self.io_identifier)?; + writeln_field(f, "type", &self.r#type)?; + write_field(f, "ident", &self.ident) } } @@ -1360,11 +1162,10 @@ pub struct ConstantDeclStmt { impl Display for ConstantDeclStmt { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { - write!( - f, - "ConstantDeclaration {}: {}, {}, {}", - self.span, self.r#type, self.identifier, self.init_expr - ) + writeln_header(f, "ConstantDeclStmt", self.span)?; + writeln_field(f, "type", &self.r#type)?; + writeln_field(f, "ident", &self.identifier)?; + write_field(f, "init_expr", &self.init_expr) } } @@ -1376,11 +1177,8 @@ pub struct CalibrationGrammarDeclaration { impl Display for CalibrationGrammarDeclaration { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { - write!( - f, - "CalibrationGrammarDeclaration {}: {}", - self.span, self.name - ) + writeln_header(f, "CalibrationGrammarDeclaration", self.span)?; + write_field(f, "name", &self.name) } } @@ -1396,89 +1194,113 @@ impl Display for CalibrationStmt { } #[derive(Clone, Debug)] -pub struct CalibrationDefinition { - span: Span, - name: Identifier, - args: List, - qubits: List, - return_type: Option, - body: String, +pub enum TypedParameter { + Scalar(ScalarTypedParameter), + Quantum(QuantumTypedParameter), + ArrayReference(ArrayTypedParameter), } -impl Display for CalibrationDefinition { - fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { - let mut indent = set_indentation(indented(f), 0); - write!(indent, "CalibrationDefinition {}: {}", self.span, self.name)?; - for arg in &self.args { - write!(indent, "\n{arg}")?; - } - for qubit in &self.qubits { - write!(indent, "\n{qubit}")?; +impl WithSpan for TypedParameter { + fn with_span(self, span: Span) -> Self { + match self { + Self::Scalar(param) => Self::Scalar(param.with_span(span)), + Self::Quantum(param) => Self::Quantum(param.with_span(span)), + Self::ArrayReference(param) => Self::ArrayReference(param.with_span(span)), } - if let Some(return_type) = &self.return_type { - write!(indent, "\n{return_type}")?; + } +} + +impl Display for TypedParameter { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + match self { + Self::Scalar(param) => write!(f, "{param}"), + Self::Quantum(param) => write!(f, "{param}"), + Self::ArrayReference(param) => write!(f, "{param}"), } - write!(indent, "\n{}", self.body) + } +} + +impl Default for TypedParameter { + fn default() -> Self { + Self::Scalar(ScalarTypedParameter { + span: Span::default(), + ident: Ident::default(), + r#type: Box::default(), + }) } } #[derive(Clone, Debug)] -pub enum CalibrationArgument { - Classical(ClassicalArgument), - Expr(Expr), +pub struct ScalarTypedParameter { + pub span: Span, + pub r#type: Box, + pub ident: Ident, } -impl Display for CalibrationArgument { +impl Display for ScalarTypedParameter { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { - match self { - CalibrationArgument::Classical(arg) => write!(f, "CalibrationArgument {arg}"), - CalibrationArgument::Expr(expr) => write!(f, "CalibrationArgument {expr}"), + writeln_header(f, "ScalarTypedParameter", self.span)?; + writeln_field(f, "type", &self.r#type)?; + write_field(f, "ident", &self.ident) + } +} + +impl WithSpan for ScalarTypedParameter { + fn with_span(self, span: Span) -> Self { + let Self { r#type, ident, .. } = self; + Self { + span, + r#type, + ident, } } } #[derive(Clone, Debug)] -pub enum TypedParameter { - Scalar(ScalarType, Box, Span), - Quantum(Option, Box, Span), - ArrayReference(ArrayReferenceType, Box, Span), +pub struct QuantumTypedParameter { + pub span: Span, + pub size: Option, + pub ident: Ident, } -impl WithSpan for TypedParameter { +impl Display for QuantumTypedParameter { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + writeln_header(f, "QuantumTypedParameter", self.span)?; + writeln_opt_field(f, "size", self.size.as_ref())?; + write_field(f, "ident", &self.ident) + } +} + +impl WithSpan for QuantumTypedParameter { fn with_span(self, span: Span) -> Self { - match self { - TypedParameter::Scalar(scalar, ident, _) => TypedParameter::Scalar(scalar, ident, span), - TypedParameter::Quantum(expr, ident, _) => TypedParameter::Quantum(expr, ident, span), - TypedParameter::ArrayReference(array, ident, _) => { - TypedParameter::ArrayReference(array, ident, span) - } - } + let Self { size, ident, .. } = self; + Self { span, size, ident } } } -impl Display for TypedParameter { +#[derive(Clone, Debug)] +pub struct ArrayTypedParameter { + pub span: Span, + pub r#type: Box, + pub ident: Ident, +} + +impl Display for ArrayTypedParameter { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { - match self { - TypedParameter::Scalar(scalar, ident, span) => { - write!(f, "{span} {ident}: {scalar}") - } - TypedParameter::Quantum(expr, ident, span) => { - if let Some(expr) = expr { - write!(f, "{span} {ident}: qubit[{expr}]") - } else { - write!(f, "{span} {ident}: qubit") - } - } - TypedParameter::ArrayReference(array, ident, span) => { - write!(f, "{span} {ident}: {array}") - } - } + writeln_header(f, "ArrayTypedParameter", self.span)?; + writeln_field(f, "type", &self.r#type)?; + write_field(f, "ident", &self.ident) } } -impl Default for TypedParameter { - fn default() -> Self { - TypedParameter::Scalar(ScalarType::default(), Box::default(), Span::default()) +impl WithSpan for ArrayTypedParameter { + fn with_span(self, span: Span) -> Self { + let Self { r#type, ident, .. } = self; + Self { + span, + r#type, + ident, + } } } @@ -1493,29 +1315,11 @@ pub struct DefStmt { impl Display for DefStmt { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { - let mut indent = set_indentation(indented(f), 0); - write!(indent, "DefStmt {}: {}", self.span, self.name)?; - write!(indent, "(")?; - if self.params.is_empty() { - write!(indent, "")?; - } else { - let param_str = self - .params - .iter() - .map(std::string::ToString::to_string) - .collect::>() - .join(", "); - write!(indent, "{param_str}")?; - } - write!(indent, ") ")?; - - for stmt in &self.body.stmts { - write!(indent, "\n{stmt}")?; - } - if let Some(return_type) = &self.return_type { - write!(indent, "\n{return_type}")?; - } - Ok(()) + writeln_header(f, "DefStmt", self.span)?; + writeln_field(f, "ident", &self.name)?; + writeln_list_field(f, "parameters", &self.params)?; + writeln_opt_field(f, "return_type", self.return_type.as_ref())?; + write_field(f, "body", &self.body) } } @@ -1528,8 +1332,8 @@ pub enum Operand { impl Display for Operand { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { match self { - Operand::Classical(arg) => write!(f, "Operand {arg}"), - Operand::Quantum(arg) => write!(f, "Operand {arg}"), + Operand::Classical(arg) => write!(f, "{arg}"), + Operand::Quantum(arg) => write!(f, "{arg}"), } } } @@ -1542,11 +1346,8 @@ pub struct ReturnStmt { impl Display for ReturnStmt { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { - if let Some(expr) = &self.expr { - write!(f, "ReturnStmt {}: {}", self.span, expr) - } else { - write!(f, "ReturnStmt {}: ", self.span) - } + writeln_header(f, "ReturnStmt", self.span)?; + write_opt_field(f, "expr", self.expr.as_ref()) } } @@ -1559,12 +1360,9 @@ pub struct WhileLoop { impl Display for WhileLoop { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { - let mut indent = set_indentation(indented(f), 0); - write!(indent, "WhileLoop {}: {}", self.span, self.while_condition)?; - for stmt in &self.block { - write!(indent, "\n{stmt}")?; - } - Ok(()) + writeln_header(f, "WhileLoop", self.span)?; + writeln_field(f, "condition", &self.while_condition)?; + write_list_field(f, "block", &self.block) } } @@ -1579,16 +1377,11 @@ pub struct ForStmt { impl Display for ForStmt { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { - let mut indent = set_indentation(indented(f), 0); - write!( - indent, - "ForStmt {}: {}, {}, {}", - self.span, self.r#type, self.identifier, self.set_declaration - )?; - for stmt in &self.block { - write!(indent, "\n{stmt}")?; - } - Ok(()) + writeln_header(f, "ForStmt", self.span)?; + writeln_field(f, "variable_type", &self.r#type)?; + writeln_field(f, "variable_name", &self.identifier)?; + writeln_field(f, "iterable", &self.set_declaration)?; + write_list_field(f, "block", &self.block) } } @@ -1603,10 +1396,7 @@ impl Display for EnumerableSet { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { match self { EnumerableSet::DiscreteSet(set) => write!(f, "{set}"), - EnumerableSet::RangeDefinition(range) => { - let indent = set_indentation(indented(f), 0); - display_range(indent, range) - } + EnumerableSet::RangeDefinition(range) => write!(f, "{range}"), EnumerableSet::Expr(expr) => write!(f, "{expr}"), } } @@ -1616,7 +1406,7 @@ impl Display for EnumerableSet { pub struct SwitchStmt { pub span: Span, pub target: Expr, - pub cases: List<(List, Block)>, + pub cases: List, /// Note that `None` is quite different to `[]` in this case; the latter is /// an explicitly empty body, whereas the absence of a default might mean /// that the switch is inexhaustive, and a linter might want to complain. @@ -1625,63 +1415,25 @@ pub struct SwitchStmt { impl Display for SwitchStmt { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { - write!(f, "SwitchStmt {}:", self.span)?; - let mut indent = set_indentation(indented(f), 1); - write!(indent, "\nTarget: {}", self.target)?; - if self.cases.is_empty() { - write!(indent, "\n")?; - } else { - write!(indent, "\nCases:")?; - for elt in &self.cases { - let (labels, block) = &**elt; - indent = display_switch_case(indent, labels, block)?; - } - } - if let Some(default) = &self.default { - write!(indent, "\nDefault Case:")?; - indent = set_indentation(indent, 2); - write!(indent, "\n{default}")?; - } else { - write!(indent, "\n")?; - } - Ok(()) - } -} - -fn display_switch_case<'a, 'b>( - mut indent: Indented<'a, Formatter<'b>>, - labels: &List, - block: &Block, -) -> Result>, core::fmt::Error> { - indent = set_indentation(indent, 2); - if labels.is_empty() { - write!(indent, "\n")?; - } else { - write!(indent, "\nLabels:")?; - indent = set_indentation(indent, 3); - for label in labels { - write!(indent, "\n{label}")?; - } + writeln_header(f, "SwitchStmt", self.span)?; + writeln_field(f, "target", &self.target)?; + writeln_list_field(f, "cases", &self.cases)?; + write_opt_field(f, "default_case", self.default.as_ref()) } - indent = set_indentation(indent, 2); - write!(indent, "\n{block}")?; - Ok(indent) } #[derive(Clone, Debug)] -pub struct ClassicalAssignment { +pub struct SwitchCase { pub span: Span, - pub lvalue: Identifier, - pub op: AssignmentOp, + pub labels: List, + pub block: Block, } -impl Display for ClassicalAssignment { +impl Display for SwitchCase { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { - write!( - f, - "ClassicalAssignment {}: {}, {}", - self.span, self.lvalue, self.op - ) + writeln_header(f, "SwitchCase", self.span)?; + writeln_list_field(f, "labels", &self.labels)?; + write_field(f, "block", &self.block) } } @@ -1704,19 +1456,18 @@ pub enum ExprKind { impl Display for ExprKind { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { - let indent = set_indentation(indented(f), 0); match self { ExprKind::Err => write!(f, "Err"), ExprKind::Ident(id) => write!(f, "{id}"), ExprKind::UnaryOp(expr) => write!(f, "{expr}"), - ExprKind::BinaryOp(expr) => display_bin_op(indent, expr), + ExprKind::BinaryOp(expr) => write!(f, "{expr}"), ExprKind::Lit(lit) => write!(f, "{lit}"), ExprKind::FunctionCall(call) => write!(f, "{call}"), - ExprKind::Cast(cast) => display_cast(indent, cast), - ExprKind::IndexExpr(index) => write!(f, "{index}"), + ExprKind::Cast(expr) => write!(f, "{expr}"), + ExprKind::IndexExpr(expr) => write!(f, "{expr}"), ExprKind::Assign(expr) => write!(f, "{expr}"), ExprKind::AssignOp(expr) => write!(f, "{expr}"), - ExprKind::Paren(expr) => display_paren(indent, expr), + ExprKind::Paren(expr) => write!(f, "Paren {expr}"), } } } @@ -1729,8 +1480,9 @@ pub struct AssignExpr { impl Display for AssignExpr { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { - let indent = set_indentation(indented(f), 0); - display_assign(indent, &self.lhs, &self.rhs) + writeln!(f, "AssignExpr:")?; + writeln_field(f, "lhs", &self.lhs)?; + write_field(f, "rhs", &self.rhs) } } @@ -1743,8 +1495,10 @@ pub struct AssignOpExpr { impl Display for AssignOpExpr { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { - let indent = set_indentation(indented(f), 0); - display_assign_op(indent, self.op, &self.lhs, &self.rhs) + writeln!(f, "AssignOpExpr:")?; + writeln_field(f, "op", &self.op)?; + writeln_field(f, "lhs", &self.lhs)?; + write_field(f, "rhs", &self.rhs) } } @@ -1756,8 +1510,9 @@ pub struct UnaryOpExpr { impl Display for UnaryOpExpr { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { - let indent = set_indentation(indented(f), 0); - display_un_op(indent, self.op, &self.expr) + writeln!(f, "UnaryOpExpr:")?; + writeln_field(f, "op", &self.op)?; + write_field(f, "expr", &self.expr) } } @@ -1770,8 +1525,10 @@ pub struct BinaryOpExpr { impl Display for BinaryOpExpr { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { - let indent = set_indentation(indented(f), 0); - display_bin_op(indent, self) + writeln!(f, "BinaryOpExpr:")?; + writeln_field(f, "op", &self.op)?; + writeln_field(f, "lhs", &self.lhs)?; + write_field(f, "rhs", &self.rhs) } } @@ -1784,13 +1541,9 @@ pub struct FunctionCall { impl Display for FunctionCall { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { - let mut indent = set_indentation(indented(f), 0); - write!(indent, "FunctionCall {}: {}", self.span, self.name)?; - indent = set_indentation(indent, 1); - for arg in &self.args { - write!(indent, "\n{arg}")?; - } - Ok(()) + writeln_header(f, "FunctionCall", self.span)?; + writeln_field(f, "name", &self.name)?; + write_list_field(f, "args", &self.args) } } @@ -1803,7 +1556,9 @@ pub struct Cast { impl Display for Cast { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { - write!(f, "Cast {}: {}, {}", self.span, self.r#type, self.arg) + writeln_header(f, "Cast", self.span)?; + writeln_field(f, "type", &self.r#type)?; + write_field(f, "arg", &self.arg) } } @@ -1816,11 +1571,9 @@ pub struct IndexExpr { impl Display for IndexExpr { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { - write!( - f, - "IndexExpr {}: {}, {}", - self.span, self.collection, self.index - ) + writeln_header(f, "IndexExpr", self.span)?; + writeln_field(f, "collection", &self.collection)?; + write_field(f, "index", &self.index) } } @@ -1839,7 +1592,7 @@ impl Display for Lit { #[derive(Clone, Debug)] pub enum LiteralKind { Array(List), - Bitstring(BigInt, usize), + Bitstring { value: BigInt, width: usize }, Bool(bool), Duration { value: f64, unit: TimeUnit }, Float(f64), @@ -1852,16 +1605,8 @@ pub enum LiteralKind { impl Display for LiteralKind { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { match self { - LiteralKind::Array(exprs) => { - let mut indent = set_indentation(indented(f), 0); - write!(indent, "Array:")?; - indent = set_indentation(indent, 1); - for expr in exprs { - write!(indent, "\n{expr:?}")?; - } - Ok(()) - } - LiteralKind::Bitstring(value, width) => { + LiteralKind::Array(exprs) => write_list_field(f, "Array", exprs), + LiteralKind::Bitstring { value, width } => { write!(f, "Bitstring(\"{:0>width$}\")", value.to_str_radix(2)) } LiteralKind::Bool(b) => write!(f, "Bool({b:?})"), @@ -1896,22 +1641,14 @@ impl fmt::Display for Version { #[derive(Clone, Debug)] pub enum IndexElement { DiscreteSet(DiscreteSet), - IndexSet(List), + IndexSet(IndexSet), } impl Display for IndexElement { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { match self { - IndexElement::DiscreteSet(set) => write!(f, "IndexElement {set}"), - IndexElement::IndexSet(items) => { - let mut indent = set_indentation(indented(f), 0); - write!(indent, "IndexElement:")?; - indent = set_indentation(indent, 1); - for item in items { - write!(indent, "\n{item}")?; - } - Ok(()) - } + IndexElement::DiscreteSet(set) => write!(f, "{set}"), + IndexElement::IndexSet(set) => write!(f, "{set}"), } } } @@ -1933,34 +1670,14 @@ impl WithSpan for IndexSetItem { impl Display for IndexSetItem { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { - let indent = set_indentation(indented(f), 0); match self { - IndexSetItem::RangeDefinition(range) => display_range(indent, range), - IndexSetItem::Expr(expr) => write!(f, "IndexSetItem {expr}"), + IndexSetItem::RangeDefinition(range) => write!(f, "{range}"), + IndexSetItem::Expr(expr) => write!(f, "{expr}"), IndexSetItem::Err => write!(f, "Err"), } } } -#[derive(Clone, Debug)] -pub enum AssignmentOp { - BinaryOp(BinOp), - /// `OpenQASM3` has the `~=` assignment operator. - /// This enum variant is meant to capture that. - UnaryOp(UnaryOp), - Assign, -} - -impl Display for AssignmentOp { - fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { - match self { - AssignmentOp::BinaryOp(op) => write!(f, "AssignmentOp ({op:?})"), - AssignmentOp::UnaryOp(op) => write!(f, "AssignmentOp ({op:?})"), - AssignmentOp::Assign => write!(f, "AssignmentOp (Assign)"), - } - } -} - #[derive(Clone, Debug)] pub enum IOKeyword { Input, @@ -2008,7 +1725,7 @@ pub struct BreakStmt { impl Display for BreakStmt { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { - write!(f, "Break {}", self.span) + write!(f, "BreakStmt {}", self.span) } } @@ -2019,7 +1736,7 @@ pub struct ContinueStmt { impl Display for ContinueStmt { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { - write!(f, "Continue {}", self.span) + write!(f, "ContinueStmt {}", self.span) } } @@ -2030,82 +1747,6 @@ pub struct EndStmt { impl Display for EndStmt { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { - write!(f, "End {}", self.span) - } -} - -fn display_assign(mut indent: Indented, lhs: &Expr, rhs: &Expr) -> fmt::Result { - write!(indent, "Assign:")?; - indent = set_indentation(indent, 1); - write!(indent, "\n{lhs}")?; - write!(indent, "\n{rhs}")?; - Ok(()) -} - -fn display_assign_op( - mut indent: Indented, - op: BinOp, - lhs: &Expr, - rhs: &Expr, -) -> fmt::Result { - write!(indent, "AssignOp ({op:?}):")?; - indent = set_indentation(indent, 1); - write!(indent, "\n{lhs}")?; - write!(indent, "\n{rhs}")?; - Ok(()) -} - -fn display_bin_op(mut indent: Indented, expr: &BinaryOpExpr) -> fmt::Result { - write!(indent, "BinOp ({:?}):", expr.op)?; - indent = set_indentation(indent, 1); - write!(indent, "\n{}", expr.lhs)?; - write!(indent, "\n{}", expr.rhs)?; - Ok(()) -} - -fn display_un_op(mut indent: Indented, op: UnaryOp, expr: &Expr) -> fmt::Result { - write!(indent, "UnOp ({op}):")?; - indent = set_indentation(indent, 1); - write!(indent, "\n{expr}")?; - Ok(()) -} - -fn display_paren(mut indent: Indented, expr: &Expr) -> fmt::Result { - write!(indent, "Paren:")?; - indent = set_indentation(indent, 1); - write!(indent, "\n{expr}")?; - Ok(()) -} -fn display_cast(mut indent: Indented, cast: &Cast) -> fmt::Result { - let Cast { span, r#type, arg } = cast; - write!(indent, "Cast {span}:")?; - indent = set_indentation(indent, 1); - write!(indent, "\n{type}\n{arg}")?; - Ok(()) -} - -fn display_while(mut indent: Indented, cond: &Expr, block: &Block) -> fmt::Result { - write!(indent, "While:")?; - indent = set_indentation(indent, 1); - write!(indent, "\n{cond}")?; - write!(indent, "\n{block}")?; - Ok(()) -} - -fn display_range(mut indent: Indented, range: &RangeDefinition) -> fmt::Result { - write!(indent, "Range: {}", range.span)?; - indent = set_indentation(indent, 1); - match &range.start { - Some(e) => write!(indent, "\nstart: {e}")?, - None => write!(indent, "\n")?, - } - match &range.step { - Some(e) => write!(indent, "\nstep: {e}")?, - None => write!(indent, "\n")?, - } - match &range.end { - Some(e) => write!(indent, "\nend: {e}")?, - None => write!(indent, "\n")?, + write!(f, "EndStmt {}", self.span) } - Ok(()) } diff --git a/compiler/qsc_qasm3/src/ast/display_utils.rs b/compiler/qsc_qasm3/src/ast/display_utils.rs new file mode 100644 index 0000000000..9088590bcf --- /dev/null +++ b/compiler/qsc_qasm3/src/ast/display_utils.rs @@ -0,0 +1,206 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +use std::fmt::{self, Display, Write}; + +/// Takes a unicode buffer or stream and wraps it with +/// `indenter::Idented`. Which applies an indentation of 1 +/// each time you insert a new line. +fn with_indentation(f: &mut T) -> indenter::Indented<'_, T> +where + T: fmt::Write, +{ + let indent = indenter::indented(f); + set_indentation(indent, 1) +} + +/// Takes an `indenter::Idented` and changes its indentation level. +fn set_indentation(indent: indenter::Indented<'_, T>, level: usize) -> indenter::Indented<'_, T> +where + T: fmt::Write, +{ + match level { + 0 => indent.with_str(""), + 1 => indent.with_str(" "), + 2 => indent.with_str(" "), + 3 => indent.with_str(" "), + _ => unimplemented!("indentation level not supported"), + } +} + +/// Writes a list of elements to the given buffer or stream. +fn write_list<'write, 'itemref, 'item, T, I>(f: &'write mut impl Write, vals: I) -> fmt::Result +where + 'item: 'itemref, + T: Display + 'item, + I: IntoIterator, +{ + let mut iter = vals.into_iter().peekable(); + if iter.peek().is_none() { + write!(f, " ") + } else { + for elt in iter { + write!(f, "\n{elt}")?; + } + Ok(()) + } +} + +/// Writes a list of elements to the given buffer or stream +/// with an additional indentation level. +pub(super) fn write_indented_list<'write, 'itemref, 'item, T, I>( + f: &'write mut impl Write, + vals: I, +) -> fmt::Result +where + 'item: 'itemref, + T: Display + 'item, + I: IntoIterator, +{ + let mut iter = vals.into_iter().peekable(); + if iter.peek().is_none() { + write!(f, " ") + } else { + let mut indent = with_indentation(f); + for elt in iter { + write!(indent, "\n{elt}")?; + } + Ok(()) + } +} + +/// Writes the name and span of a structure to the given buffer or stream. +pub(super) fn write_header(f: &mut impl Write, name: &str, span: super::Span) -> fmt::Result { + write!(f, "{name} {span}:") +} + +/// Writes the name and span of a structure to the given buffer or stream. +/// Inserts a newline afterwards. +pub(super) fn writeln_header(f: &mut impl Write, name: &str, span: super::Span) -> fmt::Result { + writeln!(f, "{name} {span}:") +} + +/// Writes a field of a structure to the given buffer +/// or stream with an additional indententation level. +pub(super) fn write_field( + f: &mut impl Write, + field_name: &str, + val: &T, +) -> fmt::Result { + let mut indent = with_indentation(f); + write!(indent, "{field_name}: {val}") +} + +/// Writes a field of a structure to the given buffer +/// or stream with an additional indententation level. +/// Inserts a newline afterwards. +pub(super) fn writeln_field( + f: &mut impl Write, + field_name: &str, + val: &T, +) -> fmt::Result { + write_field(f, field_name, val)?; + writeln!(f) +} + +/// Writes an optional field of a structure to the given buffer +/// or stream with an additional indententation level. +pub(super) fn write_opt_field( + f: &mut impl Write, + field_name: &str, + opt_val: Option<&T>, +) -> fmt::Result { + if let Some(val) = opt_val { + write_field(f, field_name, val) + } else { + write_field(f, field_name, &"") + } +} + +/// Writes an optional field of a structure to the given buffer +/// or stream with an additional indententation level. +/// Inserts a newline afterwards. +pub(super) fn writeln_opt_field( + f: &mut impl Write, + field_name: &str, + opt_val: Option<&T>, +) -> fmt::Result { + write_opt_field(f, field_name, opt_val)?; + writeln!(f) +} + +/// Writes an field of a structure to the given buffer +/// or stream with an additional indententation level. +/// The field must be an iterable. +pub(super) fn write_list_field<'write, 'itemref, 'item, T, I>( + f: &mut impl Write, + field_name: &str, + vals: I, +) -> fmt::Result +where + 'item: 'itemref, + T: Display + 'item, + I: IntoIterator, +{ + let mut indent = with_indentation(f); + write!(indent, "{field_name}:")?; + let mut indent = set_indentation(indent, 2); + write_list(&mut indent, vals) +} + +/// Writes an field of a structure to the given buffer +/// or stream with an additional indententation level. +/// The field must be an iterable. +/// Inserts a newline afterwards. +pub(super) fn writeln_list_field<'write, 'itemref, 'item, T, I>( + f: &mut impl Write, + field_name: &str, + vals: I, +) -> fmt::Result +where + 'item: 'itemref, + T: Display + 'item, + I: IntoIterator, +{ + write_list_field(f, field_name, vals)?; + writeln!(f) +} + +/// Writes an optional field of a structure to the given buffer +/// or stream with an additional indententation level. +/// The field must be an iterable. +pub(super) fn write_opt_list_field<'write, 'itemref, 'item, T, I>( + f: &mut impl Write, + field_name: &str, + opt_vals: Option, +) -> fmt::Result +where + 'item: 'itemref, + T: Display + 'item, + I: IntoIterator, +{ + if let Some(vals) = opt_vals { + write_list_field(f, field_name, vals) + } else { + let mut indent = with_indentation(f); + write!(indent, "{field_name}: ") + } +} + +/// Writes an optional field of a structure to the given buffer +/// or stream with an additional indententation level. +/// The field must be an iterable. +/// Inserts a newline afterwards. +pub(super) fn writeln_opt_list_field<'write, 'itemref, 'item, T, I>( + f: &mut impl Write, + field_name: &str, + opt_vals: Option, +) -> fmt::Result +where + 'item: 'itemref, + T: Display + 'item, + I: IntoIterator, +{ + write_opt_list_field(f, field_name, opt_vals)?; + writeln!(f) +} diff --git a/compiler/qsc_qasm3/src/parser/expr.rs b/compiler/qsc_qasm3/src/parser/expr.rs index 586951d57a..3e19032516 100644 --- a/compiler/qsc_qasm3/src/parser/expr.rs +++ b/compiler/qsc_qasm3/src/parser/expr.rs @@ -18,8 +18,8 @@ use crate::{ ast::{ self, list_from_iter, AssignExpr, AssignOpExpr, BinOp, BinaryOpExpr, Cast, DiscreteSet, Expr, ExprKind, FunctionCall, GateOperand, HardwareQubit, Ident, IndexElement, IndexExpr, - IndexSetItem, IndexedIdent, List, Lit, LiteralKind, MeasureExpr, RangeDefinition, TimeUnit, - TypeDef, UnaryOp, ValueExpression, Version, + IndexSet, IndexSetItem, IndexedIdent, List, Lit, LiteralKind, MeasureExpr, RangeDefinition, + TimeUnit, TypeDef, UnaryOp, ValueExpression, Version, }, keyword::Keyword, lex::{ @@ -291,7 +291,7 @@ fn lit_token(lexeme: &str, token: Token) -> Result> { Ok(Some(Lit { span: token.span, - kind: LiteralKind::Bitstring(value, width), + kind: LiteralKind::Bitstring { value, width }, })) } Literal::Imaginary => { @@ -492,13 +492,13 @@ fn index_element(s: &mut ParserContext) -> Result { Ok(Some(v)) => IndexElement::DiscreteSet(v), Err(err) => return Err(err), Ok(None) => { + let lo = s.peek().span.lo; let (exprs, _) = seq(s, index_set_item)?; - let exprs = exprs - .into_iter() - .map(Box::new) - .collect::>() - .into_boxed_slice(); - IndexElement::IndexSet(exprs) + let exprs = list_from_iter(exprs); + IndexElement::IndexSet(IndexSet { + span: s.span(lo), + values: exprs, + }) } }; Ok(index) @@ -556,7 +556,7 @@ pub(crate) fn set_expr(s: &mut ParserContext) -> Result { recovering_token(s, TokenKind::Close(Delim::Brace)); Ok(DiscreteSet { span: s.span(lo), - values: exprs.into_boxed_slice(), + values: list_from_iter(exprs), }) } diff --git a/compiler/qsc_qasm3/src/parser/expr/tests.rs b/compiler/qsc_qasm3/src/parser/expr/tests.rs index 455a3bcdff..1e8cec4ac5 100644 --- a/compiler/qsc_qasm3/src/parser/expr/tests.rs +++ b/compiler/qsc_qasm3/src/parser/expr/tests.rs @@ -117,8 +117,9 @@ fn lit_int_min() { check_expr( "-9_223_372_036_854_775_808", &expect![[r#" - Expr [0-26]: UnOp (Neg): - Expr [1-26]: Lit: Int(-9223372036854775808)"#]], + Expr [0-26]: UnaryOpExpr: + op: Neg + expr: Expr [1-26]: Lit: Int(-9223372036854775808)"#]], ); } @@ -540,9 +541,10 @@ fn pratt_parsing_binary_expr() { check_expr( "1 + 2", &expect![[r#" - Expr [0-5]: BinOp (Add): - Expr [0-1]: Lit: Int(1) - Expr [4-5]: Lit: Int(2)"#]], + Expr [0-5]: BinaryOpExpr: + op: Add + lhs: Expr [0-1]: Lit: Int(1) + rhs: Expr [4-5]: Lit: Int(2)"#]], ); } @@ -551,11 +553,13 @@ fn pratt_parsing_mul_add() { check_expr( "1 + 2 * 3", &expect![[r#" - Expr [0-9]: BinOp (Add): - Expr [0-1]: Lit: Int(1) - Expr [4-9]: BinOp (Mul): - Expr [4-5]: Lit: Int(2) - Expr [8-9]: Lit: Int(3)"#]], + Expr [0-9]: BinaryOpExpr: + op: Add + lhs: Expr [0-1]: Lit: Int(1) + rhs: Expr [4-9]: BinaryOpExpr: + op: Mul + lhs: Expr [4-5]: Lit: Int(2) + rhs: Expr [8-9]: Lit: Int(3)"#]], ); } @@ -564,12 +568,13 @@ fn pratt_parsing_parens() { check_expr( "(1 + 2) * 3", &expect![[r#" - Expr [0-11]: BinOp (Mul): - Expr [0-7]: Paren: - Expr [1-6]: BinOp (Add): - Expr [1-2]: Lit: Int(1) - Expr [5-6]: Lit: Int(2) - Expr [10-11]: Lit: Int(3)"#]], + Expr [0-11]: BinaryOpExpr: + op: Mul + lhs: Expr [0-7]: Paren Expr [1-6]: BinaryOpExpr: + op: Add + lhs: Expr [1-2]: Lit: Int(1) + rhs: Expr [5-6]: Lit: Int(2) + rhs: Expr [10-11]: Lit: Int(3)"#]], ); } @@ -578,10 +583,12 @@ fn prat_parsing_mul_unary() { check_expr( "2 * -3", &expect![[r#" - Expr [0-6]: BinOp (Mul): - Expr [0-1]: Lit: Int(2) - Expr [4-6]: UnOp (Neg): - Expr [5-6]: Lit: Int(3)"#]], + Expr [0-6]: BinaryOpExpr: + op: Mul + lhs: Expr [0-1]: Lit: Int(2) + rhs: Expr [4-6]: UnaryOpExpr: + op: Neg + expr: Expr [5-6]: Lit: Int(3)"#]], ); } @@ -590,10 +597,12 @@ fn prat_parsing_unary_mul() { check_expr( "-2 * 3", &expect![[r#" - Expr [0-6]: BinOp (Mul): - Expr [0-2]: UnOp (Neg): - Expr [1-2]: Lit: Int(2) - Expr [5-6]: Lit: Int(3)"#]], + Expr [0-6]: BinaryOpExpr: + op: Mul + lhs: Expr [0-2]: UnaryOpExpr: + op: Neg + expr: Expr [1-2]: Lit: Int(2) + rhs: Expr [5-6]: Lit: Int(3)"#]], ); } @@ -602,10 +611,13 @@ fn prat_parsing_exp_funcall() { check_expr( "2 ** square(3)", &expect![[r#" - Expr [0-14]: BinOp (Exp): - Expr [0-1]: Lit: Int(2) - Expr [5-14]: FunctionCall [5-14]: Ident [5-11] "square" - Expr [12-13]: Lit: Int(3)"#]], + Expr [0-14]: BinaryOpExpr: + op: Exp + lhs: Expr [0-1]: Lit: Int(2) + rhs: Expr [5-14]: FunctionCall [5-14]: + name: Ident [5-11] "square" + args: + Expr [12-13]: Lit: Int(3)"#]], ); } @@ -614,10 +626,13 @@ fn prat_parsing_funcall_exp() { check_expr( "square(2) ** 3", &expect![[r#" - Expr [0-14]: BinOp (Exp): - Expr [0-9]: FunctionCall [0-9]: Ident [0-6] "square" - Expr [7-8]: Lit: Int(2) - Expr [13-14]: Lit: Int(3)"#]], + Expr [0-14]: BinaryOpExpr: + op: Exp + lhs: Expr [0-9]: FunctionCall [0-9]: + name: Ident [0-6] "square" + args: + Expr [7-8]: Lit: Int(2) + rhs: Expr [13-14]: Lit: Int(3)"#]], ); } @@ -626,10 +641,13 @@ fn prat_parsing_funcall_exp_arg() { check_expr( "square(2 ** 3)", &expect![[r#" - Expr [0-14]: FunctionCall [0-14]: Ident [0-6] "square" - Expr [7-13]: BinOp (Exp): - Expr [7-8]: Lit: Int(2) - Expr [12-13]: Lit: Int(3)"#]], + Expr [0-14]: FunctionCall [0-14]: + name: Ident [0-6] "square" + args: + Expr [7-13]: BinaryOpExpr: + op: Exp + lhs: Expr [7-8]: Lit: Int(2) + rhs: Expr [12-13]: Lit: Int(3)"#]], ); } @@ -638,8 +656,10 @@ fn funcall() { check_expr( "square(2)", &expect![[r#" - Expr [0-9]: FunctionCall [0-9]: Ident [0-6] "square" - Expr [7-8]: Lit: Int(2)"#]], + Expr [0-9]: FunctionCall [0-9]: + name: Ident [0-6] "square" + args: + Expr [7-8]: Lit: Int(2)"#]], ); } @@ -648,9 +668,11 @@ fn funcall_multiple_args() { check_expr( "square(2, 3)", &expect![[r#" - Expr [0-12]: FunctionCall [0-12]: Ident [0-6] "square" - Expr [7-8]: Lit: Int(2) - Expr [10-11]: Lit: Int(3)"#]], + Expr [0-12]: FunctionCall [0-12]: + name: Ident [0-6] "square" + args: + Expr [7-8]: Lit: Int(2) + Expr [10-11]: Lit: Int(3)"#]], ); } @@ -659,9 +681,11 @@ fn funcall_multiple_args_trailing_comma() { check_expr( "square(2, 3,)", &expect![[r#" - Expr [0-13]: FunctionCall [0-13]: Ident [0-6] "square" - Expr [7-8]: Lit: Int(2) - Expr [10-11]: Lit: Int(3)"#]], + Expr [0-13]: FunctionCall [0-13]: + name: Ident [0-6] "square" + args: + Expr [7-8]: Lit: Int(2) + Expr [10-11]: Lit: Int(3)"#]], ); } @@ -671,8 +695,9 @@ fn cast_to_bit() { "bit(0)", &expect![[r#" Expr [0-6]: Cast [0-6]: - ClassicalType [0-3]: BitType - Expr [4-5]: Lit: Int(0)"#]], + type: ScalarType [0-3]: BitType [0-3]: + size: + arg: Expr [4-5]: Lit: Int(0)"#]], ); } @@ -682,8 +707,9 @@ fn cast_to_bit_with_designator() { "bit[4](0)", &expect![[r#" Expr [0-9]: Cast [0-9]: - ClassicalType [0-6]: BitType [0-6]: Expr [4-5]: Lit: Int(4) - Expr [7-8]: Lit: Int(0)"#]], + type: ScalarType [0-6]: BitType [0-6]: + size: Expr [4-5]: Lit: Int(4) + arg: Expr [7-8]: Lit: Int(0)"#]], ); } @@ -693,8 +719,9 @@ fn cast_to_int() { "int(0)", &expect![[r#" Expr [0-6]: Cast [0-6]: - ClassicalType [0-3]: IntType [0-3] - Expr [4-5]: Lit: Int(0)"#]], + type: ScalarType [0-3]: IntType [0-3]: + size: + arg: Expr [4-5]: Lit: Int(0)"#]], ); } @@ -704,8 +731,9 @@ fn cast_to_int_with_designator() { "int[64](0)", &expect![[r#" Expr [0-10]: Cast [0-10]: - ClassicalType [0-7]: IntType[Expr [4-6]: Lit: Int(64)]: [0-7] - Expr [8-9]: Lit: Int(0)"#]], + type: ScalarType [0-7]: IntType [0-7]: + size: Expr [4-6]: Lit: Int(64) + arg: Expr [8-9]: Lit: Int(0)"#]], ); } @@ -715,8 +743,9 @@ fn cast_to_uint() { "uint(0)", &expect![[r#" Expr [0-7]: Cast [0-7]: - ClassicalType [0-4]: UIntType [0-4] - Expr [5-6]: Lit: Int(0)"#]], + type: ScalarType [0-4]: UIntType [0-4]: + size: + arg: Expr [5-6]: Lit: Int(0)"#]], ); } @@ -726,8 +755,9 @@ fn cast_to_uint_with_designator() { "uint[64](0)", &expect![[r#" Expr [0-11]: Cast [0-11]: - ClassicalType [0-8]: UIntType[Expr [5-7]: Lit: Int(64)]: [0-8] - Expr [9-10]: Lit: Int(0)"#]], + type: ScalarType [0-8]: UIntType [0-8]: + size: Expr [5-7]: Lit: Int(64) + arg: Expr [9-10]: Lit: Int(0)"#]], ); } @@ -737,8 +767,9 @@ fn cast_to_float() { "float(0)", &expect![[r#" Expr [0-8]: Cast [0-8]: - ClassicalType [0-5]: FloatType [0-5] - Expr [6-7]: Lit: Int(0)"#]], + type: ScalarType [0-5]: FloatType [0-5]: + size: + arg: Expr [6-7]: Lit: Int(0)"#]], ); } @@ -748,8 +779,9 @@ fn cast_to_float_with_designator() { "float[64](0)", &expect![[r#" Expr [0-12]: Cast [0-12]: - ClassicalType [0-9]: FloatType[Expr [6-8]: Lit: Int(64)]: [0-9] - Expr [10-11]: Lit: Int(0)"#]], + type: ScalarType [0-9]: FloatType [0-9]: + size: Expr [6-8]: Lit: Int(64) + arg: Expr [10-11]: Lit: Int(0)"#]], ); } @@ -759,8 +791,10 @@ fn cast_to_complex() { "complex[float](0)", &expect![[r#" Expr [0-17]: Cast [0-17]: - ClassicalType [0-14]: ComplexType[float[FloatType [8-13]]]: [0-14] - Expr [15-16]: Lit: Int(0)"#]], + type: ScalarType [0-14]: ComplexType [0-14]: + base_size: FloatType [8-13]: + size: + arg: Expr [15-16]: Lit: Int(0)"#]], ); } @@ -770,8 +804,10 @@ fn cast_to_complex_with_designator() { "complex[float[64]](0)", &expect![[r#" Expr [0-21]: Cast [0-21]: - ClassicalType [0-18]: ComplexType[float[FloatType[Expr [14-16]: Lit: Int(64)]: [8-17]]]: [0-18] - Expr [19-20]: Lit: Int(0)"#]], + type: ScalarType [0-18]: ComplexType [0-18]: + base_size: FloatType [8-17]: + size: Expr [14-16]: Lit: Int(64) + arg: Expr [19-20]: Lit: Int(0)"#]], ); } @@ -781,8 +817,8 @@ fn cast_to_bool() { "bool(0)", &expect![[r#" Expr [0-7]: Cast [0-7]: - ClassicalType [0-4]: BoolType - Expr [5-6]: Lit: Int(0)"#]], + type: ScalarType [0-4]: BoolType + arg: Expr [5-6]: Lit: Int(0)"#]], ); } @@ -792,8 +828,8 @@ fn cast_to_duration() { "duration(0)", &expect![[r#" Expr [0-11]: Cast [0-11]: - ClassicalType [0-8]: Duration - Expr [9-10]: Lit: Int(0)"#]], + type: ScalarType [0-8]: Duration + arg: Expr [9-10]: Lit: Int(0)"#]], ); } @@ -803,8 +839,8 @@ fn cast_to_stretch() { "stretch(0)", &expect![[r#" Expr [0-10]: Cast [0-10]: - ClassicalType [0-7]: Stretch - Expr [8-9]: Lit: Int(0)"#]], + type: ScalarType [0-7]: Stretch + arg: Expr [8-9]: Lit: Int(0)"#]], ); } @@ -814,9 +850,12 @@ fn cast_to_int_array() { "array[int[64], 4](0)", &expect![[r#" Expr [0-20]: Cast [0-20]: - ArrayType [0-17]: ArrayBaseTypeKind IntType[Expr [10-12]: Lit: Int(64)]: [6-13] - Expr [15-16]: Lit: Int(4) - Expr [18-19]: Lit: Int(0)"#]], + type: ArrayType [0-17]: + base_type: ArrayBaseTypeKind IntType [6-13]: + size: Expr [10-12]: Lit: Int(64) + dimensions: + Expr [15-16]: Lit: Int(4) + arg: Expr [18-19]: Lit: Int(0)"#]], ); } @@ -826,9 +865,12 @@ fn cast_to_uint_array() { "array[uint[64], 4](0)", &expect![[r#" Expr [0-21]: Cast [0-21]: - ArrayType [0-18]: ArrayBaseTypeKind UIntType[Expr [11-13]: Lit: Int(64)]: [6-14] - Expr [16-17]: Lit: Int(4) - Expr [19-20]: Lit: Int(0)"#]], + type: ArrayType [0-18]: + base_type: ArrayBaseTypeKind UIntType [6-14]: + size: Expr [11-13]: Lit: Int(64) + dimensions: + Expr [16-17]: Lit: Int(4) + arg: Expr [19-20]: Lit: Int(0)"#]], ); } @@ -838,9 +880,12 @@ fn cast_to_float_array() { "array[float[64], 4](0)", &expect![[r#" Expr [0-22]: Cast [0-22]: - ArrayType [0-19]: ArrayBaseTypeKind FloatType[Expr [12-14]: Lit: Int(64)]: [6-15] - Expr [17-18]: Lit: Int(4) - Expr [20-21]: Lit: Int(0)"#]], + type: ArrayType [0-19]: + base_type: ArrayBaseTypeKind FloatType [6-15]: + size: Expr [12-14]: Lit: Int(64) + dimensions: + Expr [17-18]: Lit: Int(4) + arg: Expr [20-21]: Lit: Int(0)"#]], ); } @@ -850,9 +895,12 @@ fn cast_to_angle_array() { "array[angle[64], 4](0)", &expect![[r#" Expr [0-22]: Cast [0-22]: - ArrayType [0-19]: ArrayBaseTypeKind AngleType [6-15]: Expr [12-14]: Lit: Int(64) - Expr [17-18]: Lit: Int(4) - Expr [20-21]: Lit: Int(0)"#]], + type: ArrayType [0-19]: + base_type: ArrayBaseTypeKind AngleType [6-15]: + size: Expr [12-14]: Lit: Int(64) + dimensions: + Expr [17-18]: Lit: Int(4) + arg: Expr [20-21]: Lit: Int(0)"#]], ); } @@ -862,9 +910,11 @@ fn cast_to_bool_array() { "array[bool, 4](0)", &expect![[r#" Expr [0-17]: Cast [0-17]: - ArrayType [0-14]: ArrayBaseTypeKind BoolType - Expr [12-13]: Lit: Int(4) - Expr [15-16]: Lit: Int(0)"#]], + type: ArrayType [0-14]: + base_type: ArrayBaseTypeKind BoolType + dimensions: + Expr [12-13]: Lit: Int(4) + arg: Expr [15-16]: Lit: Int(0)"#]], ); } @@ -874,9 +924,11 @@ fn cast_to_duration_array() { "array[duration, 4](0)", &expect![[r#" Expr [0-21]: Cast [0-21]: - ArrayType [0-18]: ArrayBaseTypeKind DurationType - Expr [16-17]: Lit: Int(4) - Expr [19-20]: Lit: Int(0)"#]], + type: ArrayType [0-18]: + base_type: ArrayBaseTypeKind DurationType + dimensions: + Expr [16-17]: Lit: Int(4) + arg: Expr [19-20]: Lit: Int(0)"#]], ); } @@ -886,9 +938,13 @@ fn cast_to_complex_array() { "array[complex[float[32]], 4](0)", &expect![[r#" Expr [0-31]: Cast [0-31]: - ArrayType [0-28]: ArrayBaseTypeKind ComplexType[float[FloatType[Expr [20-22]: Lit: Int(32)]: [14-23]]]: [6-24] - Expr [26-27]: Lit: Int(4) - Expr [29-30]: Lit: Int(0)"#]], + type: ArrayType [0-28]: + base_type: ArrayBaseTypeKind ComplexType [6-24]: + base_size: FloatType [14-23]: + size: Expr [20-22]: Lit: Int(32) + dimensions: + Expr [26-27]: Lit: Int(4) + arg: Expr [29-30]: Lit: Int(0)"#]], ); } @@ -897,8 +953,11 @@ fn index_expr() { check_expr( "foo[1]", &expect![[r#" - Expr [0-6]: IndexExpr [3-6]: Expr [0-3]: Ident [0-3] "foo", IndexElement: - IndexSetItem Expr [4-5]: Lit: Int(1)"#]], + Expr [0-6]: IndexExpr [3-6]: + collection: Expr [0-3]: Ident [0-3] "foo" + index: IndexSet [4-5]: + values: + Expr [4-5]: Lit: Int(1)"#]], ); } @@ -907,10 +966,13 @@ fn index_set() { check_expr( "foo[{1, 4, 5}]", &expect![[r#" - Expr [0-14]: IndexExpr [3-14]: Expr [0-3]: Ident [0-3] "foo", IndexElement DiscreteSet [4-13]: - Expr [5-6]: Lit: Int(1) - Expr [8-9]: Lit: Int(4) - Expr [11-12]: Lit: Int(5)"#]], + Expr [0-14]: IndexExpr [3-14]: + collection: Expr [0-3]: Ident [0-3] "foo" + index: DiscreteSet [4-13]: + values: + Expr [5-6]: Lit: Int(1) + Expr [8-9]: Lit: Int(4) + Expr [11-12]: Lit: Int(5)"#]], ); } @@ -919,19 +981,22 @@ fn index_multiple_ranges() { check_expr( "foo[1:5, 3:7, 4:8]", &expect![[r#" - Expr [0-18]: IndexExpr [3-18]: Expr [0-3]: Ident [0-3] "foo", IndexElement: - Range: [4-7] - start: Expr [4-5]: Lit: Int(1) - - end: Expr [6-7]: Lit: Int(5) - Range: [9-12] - start: Expr [9-10]: Lit: Int(3) - - end: Expr [11-12]: Lit: Int(7) - Range: [14-17] - start: Expr [14-15]: Lit: Int(4) - - end: Expr [16-17]: Lit: Int(8)"#]], + Expr [0-18]: IndexExpr [3-18]: + collection: Expr [0-3]: Ident [0-3] "foo" + index: IndexSet [4-17]: + values: + RangeDefinition [4-7]: + start: Expr [4-5]: Lit: Int(1) + step: + end: Expr [6-7]: Lit: Int(5) + RangeDefinition [9-12]: + start: Expr [9-10]: Lit: Int(3) + step: + end: Expr [11-12]: Lit: Int(7) + RangeDefinition [14-17]: + start: Expr [14-15]: Lit: Int(4) + step: + end: Expr [16-17]: Lit: Int(8)"#]], ); } @@ -940,11 +1005,14 @@ fn index_range() { check_expr( "foo[1:5:2]", &expect![[r#" - Expr [0-10]: IndexExpr [3-10]: Expr [0-3]: Ident [0-3] "foo", IndexElement: - Range: [4-9] - start: Expr [4-5]: Lit: Int(1) - step: Expr [6-7]: Lit: Int(5) - end: Expr [8-9]: Lit: Int(2)"#]], + Expr [0-10]: IndexExpr [3-10]: + collection: Expr [0-3]: Ident [0-3] "foo" + index: IndexSet [4-9]: + values: + RangeDefinition [4-9]: + start: Expr [4-5]: Lit: Int(1) + step: Expr [6-7]: Lit: Int(5) + end: Expr [8-9]: Lit: Int(2)"#]], ); } @@ -953,11 +1021,14 @@ fn index_full_range() { check_expr( "foo[:]", &expect![[r#" - Expr [0-6]: IndexExpr [3-6]: Expr [0-3]: Ident [0-3] "foo", IndexElement: - Range: [4-5] - - - "#]], + Expr [0-6]: IndexExpr [3-6]: + collection: Expr [0-3]: Ident [0-3] "foo" + index: IndexSet [4-5]: + values: + RangeDefinition [4-5]: + start: + step: + end: "#]], ); } @@ -966,11 +1037,14 @@ fn index_range_start() { check_expr( "foo[1:]", &expect![[r#" - Expr [0-7]: IndexExpr [3-7]: Expr [0-3]: Ident [0-3] "foo", IndexElement: - Range: [4-6] - start: Expr [4-5]: Lit: Int(1) - - "#]], + Expr [0-7]: IndexExpr [3-7]: + collection: Expr [0-3]: Ident [0-3] "foo" + index: IndexSet [4-6]: + values: + RangeDefinition [4-6]: + start: Expr [4-5]: Lit: Int(1) + step: + end: "#]], ); } @@ -979,11 +1053,14 @@ fn index_range_end() { check_expr( "foo[:5]", &expect![[r#" - Expr [0-7]: IndexExpr [3-7]: Expr [0-3]: Ident [0-3] "foo", IndexElement: - Range: [4-6] - - - end: Expr [5-6]: Lit: Int(5)"#]], + Expr [0-7]: IndexExpr [3-7]: + collection: Expr [0-3]: Ident [0-3] "foo" + index: IndexSet [4-6]: + values: + RangeDefinition [4-6]: + start: + step: + end: Expr [5-6]: Lit: Int(5)"#]], ); } @@ -992,11 +1069,14 @@ fn index_range_step() { check_expr( "foo[:2:]", &expect![[r#" - Expr [0-8]: IndexExpr [3-8]: Expr [0-3]: Ident [0-3] "foo", IndexElement: - Range: [4-7] - - step: Expr [5-6]: Lit: Int(2) - "#]], + Expr [0-8]: IndexExpr [3-8]: + collection: Expr [0-3]: Ident [0-3] "foo" + index: IndexSet [4-7]: + values: + RangeDefinition [4-7]: + start: + step: Expr [5-6]: Lit: Int(2) + end: "#]], ); } @@ -1006,10 +1086,11 @@ fn set_expr() { super::set_expr, "{2, 3, 4}", &expect![[r#" - DiscreteSet [0-9]: - Expr [1-2]: Lit: Int(2) - Expr [4-5]: Lit: Int(3) - Expr [7-8]: Lit: Int(4)"#]], + DiscreteSet [0-9]: + values: + Expr [1-2]: Lit: Int(2) + Expr [4-5]: Lit: Int(3) + Expr [7-8]: Lit: Int(4)"#]], ); } @@ -1019,9 +1100,15 @@ fn lit_array() { super::lit_array, "{{2, {5}}, 1 + z}", &expect![[r#" - Expr [0-17]: Lit: Array: - Expr { span: Span { lo: 1, hi: 9 }, kind: Lit(Lit { span: Span { lo: 1, hi: 9 }, kind: Array([Expr { span: Span { lo: 2, hi: 3 }, kind: Lit(Lit { span: Span { lo: 2, hi: 3 }, kind: Int(2) }) }, Expr { span: Span { lo: 5, hi: 8 }, kind: Lit(Lit { span: Span { lo: 5, hi: 8 }, kind: Array([Expr { span: Span { lo: 6, hi: 7 }, kind: Lit(Lit { span: Span { lo: 6, hi: 7 }, kind: Int(5) }) }]) }) }]) }) } - Expr { span: Span { lo: 11, hi: 16 }, kind: BinaryOp(BinaryOpExpr { op: Add, lhs: Expr { span: Span { lo: 11, hi: 12 }, kind: Lit(Lit { span: Span { lo: 11, hi: 12 }, kind: Int(1) }) }, rhs: Expr { span: Span { lo: 15, hi: 16 }, kind: Ident(Ident { span: Span { lo: 15, hi: 16 }, name: "z" }) } }) }"#]], + Expr [0-17]: Lit: Array: + Expr [1-9]: Lit: Array: + Expr [2-3]: Lit: Int(2) + Expr [5-8]: Lit: Array: + Expr [6-7]: Lit: Int(5) + Expr [11-16]: BinaryOpExpr: + op: Add + lhs: Expr [11-12]: Lit: Int(1) + rhs: Expr [15-16]: Ident [15-16] "z""#]], ); } @@ -1030,12 +1117,14 @@ fn assignment_and_unop() { check_expr( "c = a && !b", &expect![[r#" - Expr [0-11]: Assign: - Expr [0-1]: Ident [0-1] "c" - Expr [4-11]: BinOp (AndL): - Expr [4-5]: Ident [4-5] "a" - Expr [9-11]: UnOp (NotL): - Expr [10-11]: Ident [10-11] "b""#]], + Expr [0-11]: AssignExpr: + lhs: Expr [0-1]: Ident [0-1] "c" + rhs: Expr [4-11]: BinaryOpExpr: + op: AndL + lhs: Expr [4-5]: Ident [4-5] "a" + rhs: Expr [9-11]: UnaryOpExpr: + op: NotL + expr: Expr [10-11]: Ident [10-11] "b""#]], ); } @@ -1044,12 +1133,14 @@ fn assignment_unop_and() { check_expr( "d = !a && b", &expect![[r#" - Expr [0-11]: Assign: - Expr [0-1]: Ident [0-1] "d" - Expr [4-11]: BinOp (AndL): - Expr [4-6]: UnOp (NotL): - Expr [5-6]: Ident [5-6] "a" - Expr [10-11]: Ident [10-11] "b""#]], + Expr [0-11]: AssignExpr: + lhs: Expr [0-1]: Ident [0-1] "d" + rhs: Expr [4-11]: BinaryOpExpr: + op: AndL + lhs: Expr [4-6]: UnaryOpExpr: + op: NotL + expr: Expr [5-6]: Ident [5-6] "a" + rhs: Expr [10-11]: Ident [10-11] "b""#]], ); } @@ -1068,11 +1159,15 @@ fn indexed_identifier() { super::indexed_identifier, "arr[1][2]", &expect![[r#" - IndexedIdent [0-9]: Ident [0-3] "arr"[ - IndexElement: - IndexSetItem Expr [4-5]: Lit: Int(1) - IndexElement: - IndexSetItem Expr [7-8]: Lit: Int(2)]"#]], + IndexedIdent [0-9]: + name: Ident [0-3] "arr" + indices: + IndexSet [4-5]: + values: + Expr [4-5]: Lit: Int(1) + IndexSet [7-8]: + values: + Expr [7-8]: Lit: Int(2)"#]], ); } @@ -1081,7 +1176,9 @@ fn measure_hardware_qubit() { check( super::measure_expr, "measure $12", - &expect!["MeasureExpr [0-7]: GateOperand HardwareQubit [8-11]: 12"], + &expect![[r#" + MeasureExpr [0-7]: + operand: HardwareQubit [8-11]: 12"#]], ); } @@ -1091,11 +1188,16 @@ fn measure_indexed_identifier() { super::measure_expr, "measure qubits[1][2]", &expect![[r#" - MeasureExpr [0-7]: GateOperand IndexedIdent [8-20]: Ident [8-14] "qubits"[ - IndexElement: - IndexSetItem Expr [15-16]: Lit: Int(1) - IndexElement: - IndexSetItem Expr [18-19]: Lit: Int(2)]"#]], + MeasureExpr [0-7]: + operand: IndexedIdent [8-20]: + name: Ident [8-14] "qubits" + indices: + IndexSet [15-16]: + values: + Expr [15-16]: Lit: Int(1) + IndexSet [18-19]: + values: + Expr [18-19]: Lit: Int(2)"#]], ); } @@ -1104,12 +1206,15 @@ fn addition_of_casts() { check_expr( "bit(0) + bit(1)", &expect![[r#" - Expr [0-15]: BinOp (Add): - Expr [0-6]: Cast [0-6]: - ClassicalType [0-3]: BitType - Expr [4-5]: Lit: Int(0) - Expr [9-15]: Cast [9-15]: - ClassicalType [9-12]: BitType - Expr [13-14]: Lit: Int(1)"#]], + Expr [0-15]: BinaryOpExpr: + op: Add + lhs: Expr [0-6]: Cast [0-6]: + type: ScalarType [0-3]: BitType [0-3]: + size: + arg: Expr [4-5]: Lit: Int(0) + rhs: Expr [9-15]: Cast [9-15]: + type: ScalarType [9-12]: BitType [9-12]: + size: + arg: Expr [13-14]: Lit: Int(1)"#]], ); } diff --git a/compiler/qsc_qasm3/src/parser/prim/tests.rs b/compiler/qsc_qasm3/src/parser/prim/tests.rs index d21861f1cd..a86993fa83 100644 --- a/compiler/qsc_qasm3/src/parser/prim/tests.rs +++ b/compiler/qsc_qasm3/src/parser/prim/tests.rs @@ -87,7 +87,14 @@ fn ident_keyword() { #[test] fn path_single() { - check(path, "Foo", &expect![[r#"Path [0-3] (Ident [0-3] "Foo")"#]]); + check( + path, + "Foo", + &expect![[r#" + Path [0-3]: + name: Ident [0-3] "Foo" + segments: "#]], + ); } #[test] @@ -97,8 +104,9 @@ fn path_double() { "Foo.Bar", &expect![[r#" Path [0-7]: - Ident [0-3] "Foo" - Ident [4-7] "Bar""#]], + name: Ident [4-7] "Bar" + segments: + Ident [0-3] "Foo""#]], ); } @@ -109,9 +117,10 @@ fn path_triple() { "Foo.Bar.Baz", &expect![[r#" Path [0-11]: - Ident [0-3] "Foo" - Ident [4-7] "Bar" - Ident [8-11] "Baz""#]], + name: Ident [8-11] "Baz" + segments: + Ident [0-3] "Foo" + Ident [4-7] "Bar""#]], ); } @@ -121,9 +130,9 @@ fn path_trailing_dot() { path, "Foo.Bar.", &expect![[r#" - Err IncompletePath [0-8]: - Ident [0-3] "Foo" - Ident [4-7] "Bar" + Err IncompletePath [0-8]: segments: + Ident [0-3] "Foo" + Ident [4-7] "Bar" [ Error( @@ -146,9 +155,9 @@ fn path_followed_by_keyword() { path, "Foo.Bar.in", &expect![[r#" - Err IncompletePath [0-10]: - Ident [0-3] "Foo" - Ident [4-7] "Bar" + Err IncompletePath [0-10]: segments: + Ident [0-3] "Foo" + Ident [4-7] "Bar" [ Error( @@ -174,8 +183,9 @@ fn opt_succeed() { "Foo.Bar", &expect![[r#" Path [0-7]: - Ident [0-3] "Foo" - Ident [4-7] "Bar""#]], + name: Ident [4-7] "Bar" + segments: + Ident [0-3] "Foo""#]], ); } @@ -190,8 +200,8 @@ fn opt_fail_consume() { |s| opt(s, path), "Foo.$", &expect![[r#" - Err IncompletePath [0-5]: - Ident [0-3] "Foo" + Err IncompletePath [0-5]: segments: + Ident [0-3] "Foo" [ Error( diff --git a/compiler/qsc_qasm3/src/parser/stmt.rs b/compiler/qsc_qasm3/src/parser/stmt.rs index a18ac394ff..2dfa716727 100644 --- a/compiler/qsc_qasm3/src/parser/stmt.rs +++ b/compiler/qsc_qasm3/src/parser/stmt.rs @@ -17,15 +17,16 @@ use super::{ use crate::{ ast::{ list_from_iter, AccessControl, AliasDeclStmt, AngleType, Annotation, ArrayBaseTypeKind, - ArrayReferenceType, ArrayType, BarrierStmt, BitType, Block, BoxStmt, BreakStmt, - CalibrationGrammarStmt, CalibrationStmt, Cast, ClassicalDeclarationStmt, ComplexType, - ConstantDeclStmt, ContinueStmt, DefCalStmt, DefStmt, DelayStmt, EndStmt, EnumerableSet, - Expr, ExprKind, ExprStmt, ExternDecl, ExternParameter, FloatType, ForStmt, FunctionCall, - GPhase, GateCall, GateModifierKind, GateOperand, IODeclaration, IOKeyword, Ident, - Identifier, IfStmt, IncludeStmt, IndexElement, IndexExpr, IndexSetItem, IntType, List, - LiteralKind, MeasureStmt, Pragma, QuantumGateDefinition, QuantumGateModifier, - QubitDeclaration, RangeDefinition, ResetStmt, ReturnStmt, ScalarType, ScalarTypeKind, Stmt, - StmtKind, SwitchStmt, TypeDef, TypedParameter, UIntType, WhileLoop, + ArrayReferenceType, ArrayType, ArrayTypedParameter, BarrierStmt, BitType, Block, BoxStmt, + BreakStmt, CalibrationGrammarStmt, CalibrationStmt, Cast, ClassicalDeclarationStmt, + ComplexType, ConstantDeclStmt, ContinueStmt, DefCalStmt, DefStmt, DelayStmt, EndStmt, + EnumerableSet, Expr, ExprKind, ExprStmt, ExternDecl, ExternParameter, FloatType, ForStmt, + FunctionCall, GPhase, GateCall, GateModifierKind, GateOperand, IODeclaration, IOKeyword, + Ident, Identifier, IfStmt, IncludeStmt, IndexElement, IndexExpr, IndexSetItem, IntType, + List, LiteralKind, MeasureStmt, Pragma, QuantumGateDefinition, QuantumGateModifier, + QuantumTypedParameter, QubitDeclaration, RangeDefinition, ResetStmt, ReturnStmt, + ScalarType, ScalarTypeKind, ScalarTypedParameter, Stmt, StmtKind, SwitchCase, SwitchStmt, + TypeDef, TypedParameter, UIntType, WhileLoop, }, keyword::Keyword, lex::{cooked::Type, Delim, TokenKind}, @@ -359,10 +360,18 @@ fn arg_def(s: &mut ParserContext) -> Result { let kind = if let Ok(ty) = scalar_type(s) { let ident = prim::ident(s)?; - TypedParameter::Scalar(ty, Box::new(ident), s.span(lo)) + TypedParameter::Scalar(ScalarTypedParameter { + span: s.span(lo), + r#type: Box::new(ty), + ident, + }) } else if let Ok(size) = qubit_type(s) { let ident = prim::ident(s)?; - TypedParameter::Quantum(size, Box::new(ident), s.span(lo)) + TypedParameter::Quantum(QuantumTypedParameter { + span: s.span(lo), + size, + ident, + }) } else if let Ok((ident, size)) = creg_type(s) { let ty = ScalarType { span: s.span(lo), @@ -371,12 +380,24 @@ fn arg_def(s: &mut ParserContext) -> Result { span: s.span(lo), }), }; - TypedParameter::Scalar(ty, ident, s.span(lo)) + TypedParameter::Scalar(ScalarTypedParameter { + span: s.span(lo), + r#type: Box::new(ty), + ident, + }) } else if let Ok((ident, size)) = qreg_type(s) { - TypedParameter::Quantum(size, ident, s.span(lo)) + TypedParameter::Quantum(QuantumTypedParameter { + span: s.span(lo), + size, + ident, + }) } else if let Ok(ty) = array_reference_ty(s) { let ident = prim::ident(s)?; - TypedParameter::ArrayReference(ty, Box::new(ident), s.span(lo)) + TypedParameter::ArrayReference(ArrayTypedParameter { + span: s.span(lo), + r#type: Box::new(ty), + ident, + }) } else { return Err(Error::new(ErrorKind::Rule( "argument definition", @@ -472,7 +493,7 @@ fn parse_quantum_decl(s: &mut ParserContext) -> Result { recovering_semi(s); Ok(StmtKind::QuantumDecl(QubitDeclaration { span: s.span(lo), - qubit: Box::new(ident), + qubit: ident, size, })) } @@ -531,7 +552,7 @@ fn parse_non_constant_classical_decl( ty: TypeDef, lo: u32, ) -> Result { - let identifier = Box::new(prim::ident(s)?); + let identifier = prim::ident(s)?; let init_expr = if s.peek().kind == TokenKind::Eq { s.advance(); Some(expr::value_expr(s)?) @@ -541,7 +562,7 @@ fn parse_non_constant_classical_decl( recovering_semi(s); let decl = ClassicalDeclarationStmt { span: s.span(lo), - r#type: ty, + r#type: Box::new(ty), identifier, init_expr, }; @@ -654,13 +675,13 @@ fn creg_decl(s: &mut ParserContext) -> Result { recovering_semi(s); Ok(StmtKind::ClassicalDecl(ClassicalDeclarationStmt { span: s.span(lo), - r#type: TypeDef::Scalar(ScalarType { + r#type: Box::new(TypeDef::Scalar(ScalarType { span: s.span(lo), kind: ScalarTypeKind::Bit(BitType { size, span: s.span(lo), }), - }), + })), identifier, init_expr: None, })) @@ -683,16 +704,16 @@ fn extern_creg_type(s: &mut ParserContext) -> Result> { Ok(size) } -fn creg_type(s: &mut ParserContext) -> Result<(Box, Option)> { +fn creg_type(s: &mut ParserContext) -> Result<(Ident, Option)> { token(s, TokenKind::Keyword(crate::keyword::Keyword::CReg))?; - let name = Box::new(prim::ident(s)?); + let name = prim::ident(s)?; let size = opt(s, designator)?; Ok((name, size)) } -fn qreg_type(s: &mut ParserContext) -> Result<(Box, Option)> { +fn qreg_type(s: &mut ParserContext) -> Result<(Ident, Option)> { token(s, TokenKind::Keyword(crate::keyword::Keyword::QReg))?; - let name = Box::new(prim::ident(s)?); + let name = prim::ident(s)?; let size = opt(s, designator)?; Ok((name, size)) } @@ -911,7 +932,7 @@ pub fn parse_switch_stmt(s: &mut ParserContext) -> Result { }) } -fn case_stmt(s: &mut ParserContext) -> Result<(List, Block)> { +fn case_stmt(s: &mut ParserContext) -> Result { let lo = s.peek().span.lo; token(s, TokenKind::Keyword(Keyword::Case))?; @@ -921,7 +942,12 @@ fn case_stmt(s: &mut ParserContext) -> Result<(List, Block)> { } let block = parse_block(s).map(|block| *block)?; - Ok((list_from_iter(controlling_label), block)) + + Ok(SwitchCase { + span: s.span(lo), + labels: list_from_iter(controlling_label), + block, + }) } fn default_case_stmt(s: &mut ParserContext) -> Result { @@ -1266,8 +1292,8 @@ fn reinterpret_index_expr( } = index_expr; if let IndexElement::IndexSet(set) = index { - if set.len() == 1 { - let first_elt: IndexSetItem = (*set[0]).clone(); + if set.values.len() == 1 { + let first_elt: IndexSetItem = (*set.values[0]).clone(); if let IndexSetItem::Expr(expr) = first_elt { if duration.is_none() { match *collection.kind { diff --git a/compiler/qsc_qasm3/src/parser/stmt/tests/alias.rs b/compiler/qsc_qasm3/src/parser/stmt/tests/alias.rs index 65d53fc6c7..9a918a3296 100644 --- a/compiler/qsc_qasm3/src/parser/stmt/tests/alias.rs +++ b/compiler/qsc_qasm3/src/parser/stmt/tests/alias.rs @@ -6,23 +6,47 @@ use crate::parser::tests::check; use expect_test::expect; #[test] -fn alias_decl_stmt() { +fn simple_alias() { + check( + parse, + "let x = a;", + &expect![[r#" + Stmt [0-10]: + annotations: + kind: AliasDeclStmt [0-10]: + ident: Ident [4-5] "x" + exprs: + Expr [8-9]: Ident [8-9] "a""#]], + ); +} + +#[test] +fn concatenation_alias() { check( parse, "let x = a[1:2] ++ b ++ c[1:2:3];", &expect![[r#" - Stmt [0-32] - StmtKind: Alias [0-32]: Ident [4-5] "x" - Expr [8-14]: IndexExpr [9-14]: Expr [8-9]: Ident [8-9] "a", IndexElement: - Range: [10-13] - start: Expr [10-11]: Lit: Int(1) - - end: Expr [12-13]: Lit: Int(2) - Expr [18-19]: Ident [18-19] "b" - Expr [23-31]: IndexExpr [24-31]: Expr [23-24]: Ident [23-24] "c", IndexElement: - Range: [25-30] - start: Expr [25-26]: Lit: Int(1) - step: Expr [27-28]: Lit: Int(2) - end: Expr [29-30]: Lit: Int(3)"#]], + Stmt [0-32]: + annotations: + kind: AliasDeclStmt [0-32]: + ident: Ident [4-5] "x" + exprs: + Expr [8-14]: IndexExpr [9-14]: + collection: Expr [8-9]: Ident [8-9] "a" + index: IndexSet [10-13]: + values: + RangeDefinition [10-13]: + start: Expr [10-11]: Lit: Int(1) + step: + end: Expr [12-13]: Lit: Int(2) + Expr [18-19]: Ident [18-19] "b" + Expr [23-31]: IndexExpr [24-31]: + collection: Expr [23-24]: Ident [23-24] "c" + index: IndexSet [25-30]: + values: + RangeDefinition [25-30]: + start: Expr [25-26]: Lit: Int(1) + step: Expr [27-28]: Lit: Int(2) + end: Expr [29-30]: Lit: Int(3)"#]], ); } diff --git a/compiler/qsc_qasm3/src/parser/stmt/tests/annotation.rs b/compiler/qsc_qasm3/src/parser/stmt/tests/annotation.rs index d2f5eb4942..a37d4e11ff 100644 --- a/compiler/qsc_qasm3/src/parser/stmt/tests/annotation.rs +++ b/compiler/qsc_qasm3/src/parser/stmt/tests/annotation.rs @@ -1,18 +1,19 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. -use expect_test::expect; - -use crate::parser::tests::check; - use crate::parser::stmt::parse_annotation; +use crate::parser::tests::check; +use expect_test::expect; #[test] fn annotation() { check( parse_annotation, "@a.b.d 23", - &expect!["Annotation [0-9]: (a.b.d, 23)"], + &expect![[r#" + Annotation [0-9]: + identifier: "a.b.d" + value: "23""#]], ); } @@ -21,7 +22,10 @@ fn annotation_ident_only() { check( parse_annotation, "@a.b.d", - &expect!["Annotation [0-6]: (a.b.d)"], + &expect![[r#" + Annotation [0-6]: + identifier: "a.b.d" + value: "#]], ); } @@ -31,7 +35,9 @@ fn annotation_missing_ident() { parse_annotation, "@", &expect![[r#" - Annotation [0-1]: () + Annotation [0-1]: + identifier: "" + value: [ Error( diff --git a/compiler/qsc_qasm3/src/parser/stmt/tests/barrier.rs b/compiler/qsc_qasm3/src/parser/stmt/tests/barrier.rs index c925c89a8c..4bcd0c5776 100644 --- a/compiler/qsc_qasm3/src/parser/stmt/tests/barrier.rs +++ b/compiler/qsc_qasm3/src/parser/stmt/tests/barrier.rs @@ -11,13 +11,20 @@ fn barrier() { parse, "barrier r, q[0], $2;", &expect![[r#" - Stmt [0-20] - StmtKind: Barrier [0-20]: [ - GateOperand IndexedIdent [8-9]: Ident [8-9] "r"[] - GateOperand IndexedIdent [11-15]: Ident [11-12] "q"[ - IndexElement: - IndexSetItem Expr [13-14]: Lit: Int(0)] - GateOperand HardwareQubit [17-19]: 2]"#]], + Stmt [0-20]: + annotations: + kind: BarrierStmt [0-20]: + operands: + IndexedIdent [8-9]: + name: Ident [8-9] "r" + indices: + IndexedIdent [11-15]: + name: Ident [11-12] "q" + indices: + IndexSet [13-14]: + values: + Expr [13-14]: Lit: Int(0) + HardwareQubit [17-19]: 2"#]], ); } @@ -27,7 +34,9 @@ fn barrier_no_args() { parse, "barrier;", &expect![[r#" - Stmt [0-8] - StmtKind: Barrier [0-8]: []"#]], + Stmt [0-8]: + annotations: + kind: BarrierStmt [0-8]: + operands: "#]], ); } diff --git a/compiler/qsc_qasm3/src/parser/stmt/tests/box_stmt.rs b/compiler/qsc_qasm3/src/parser/stmt/tests/box_stmt.rs index 463f5fa21c..6e500319fa 100644 --- a/compiler/qsc_qasm3/src/parser/stmt/tests/box_stmt.rs +++ b/compiler/qsc_qasm3/src/parser/stmt/tests/box_stmt.rs @@ -15,15 +15,34 @@ fn box_stmt() { Rx(2.4) q1; }", &expect![[r#" - Stmt [5-50] - StmtKind: BoxStmt [5-50]: - Stmt [19-24] - StmtKind: GateCall [19-24]: Ident [19-20] "H" - GateOperand IndexedIdent [21-23]: Ident [21-23] "q0"[] - Stmt [33-44] - StmtKind: GateCall [33-44]: Ident [33-35] "Rx" - Expr [36-39]: Lit: Float(2.4) - GateOperand IndexedIdent [41-43]: Ident [41-43] "q1"[]"#]], + Stmt [5-50]: + annotations: + kind: BoxStmt [5-50]: + duration: + body: + Stmt [19-24]: + annotations: + kind: GateCall [19-24]: + modifiers: + name: Ident [19-20] "H" + args: + duration: + qubits: + IndexedIdent [21-23]: + name: Ident [21-23] "q0" + indices: + Stmt [33-44]: + annotations: + kind: GateCall [33-44]: + modifiers: + name: Ident [33-35] "Rx" + args: + Expr [36-39]: Lit: Float(2.4) + duration: + qubits: + IndexedIdent [41-43]: + name: Ident [41-43] "q1" + indices: "#]], ); } @@ -37,15 +56,34 @@ fn box_stmt_with_designator() { Rx(2.4) q1; }", &expect![[r#" - Stmt [5-55] - StmtKind: BoxStmt [5-55]: Expr [9-12]: Lit: Duration(4.0, Us) - Stmt [24-29] - StmtKind: GateCall [24-29]: Ident [24-25] "H" - GateOperand IndexedIdent [26-28]: Ident [26-28] "q0"[] - Stmt [38-49] - StmtKind: GateCall [38-49]: Ident [38-40] "Rx" - Expr [41-44]: Lit: Float(2.4) - GateOperand IndexedIdent [46-48]: Ident [46-48] "q1"[]"#]], + Stmt [5-55]: + annotations: + kind: BoxStmt [5-55]: + duration: Expr [9-12]: Lit: Duration(4.0, Us) + body: + Stmt [24-29]: + annotations: + kind: GateCall [24-29]: + modifiers: + name: Ident [24-25] "H" + args: + duration: + qubits: + IndexedIdent [26-28]: + name: Ident [26-28] "q0" + indices: + Stmt [38-49]: + annotations: + kind: GateCall [38-49]: + modifiers: + name: Ident [38-40] "Rx" + args: + Expr [41-44]: Lit: Float(2.4) + duration: + qubits: + IndexedIdent [46-48]: + name: Ident [46-48] "q1" + indices: "#]], ); } @@ -59,16 +97,36 @@ fn box_stmt_with_invalid_instruction() { X q1; }", &expect![[r#" - Stmt [0-54] - StmtKind: BoxStmt [0-54]: - Stmt [14-19] - StmtKind: GateCall [14-19]: Ident [14-15] "H" - GateOperand IndexedIdent [16-18]: Ident [16-18] "q0"[] - Stmt [28-34] - StmtKind: Err - Stmt [43-48] - StmtKind: GateCall [43-48]: Ident [43-44] "X" - GateOperand IndexedIdent [45-47]: Ident [45-47] "q1"[] + Stmt [0-54]: + annotations: + kind: BoxStmt [0-54]: + duration: + body: + Stmt [14-19]: + annotations: + kind: GateCall [14-19]: + modifiers: + name: Ident [14-15] "H" + args: + duration: + qubits: + IndexedIdent [16-18]: + name: Ident [16-18] "q0" + indices: + Stmt [28-34]: + annotations: + kind: Err + Stmt [43-48]: + annotations: + kind: GateCall [43-48]: + modifiers: + name: Ident [43-44] "X" + args: + duration: + qubits: + IndexedIdent [45-47]: + name: Ident [45-47] "q1" + indices: [ Error( diff --git a/compiler/qsc_qasm3/src/parser/stmt/tests/cal.rs b/compiler/qsc_qasm3/src/parser/stmt/tests/cal.rs index da1ce82d60..13633b5175 100644 --- a/compiler/qsc_qasm3/src/parser/stmt/tests/cal.rs +++ b/compiler/qsc_qasm3/src/parser/stmt/tests/cal.rs @@ -38,7 +38,8 @@ fn cal_block_accept_any_tokens_inside() { .314 }", &expect![[r#" - Stmt [5-69] - StmtKind: CalibrationStmt [5-69]"#]], + Stmt [5-69]: + annotations: + kind: CalibrationStmt [5-69]"#]], ); } diff --git a/compiler/qsc_qasm3/src/parser/stmt/tests/cal_grammar.rs b/compiler/qsc_qasm3/src/parser/stmt/tests/cal_grammar.rs index f5ea7b2380..da777fc9fa 100644 --- a/compiler/qsc_qasm3/src/parser/stmt/tests/cal_grammar.rs +++ b/compiler/qsc_qasm3/src/parser/stmt/tests/cal_grammar.rs @@ -11,8 +11,10 @@ fn defcalgrammar() { parse, r#"defcalgrammar "openpulse";"#, &expect![[r#" - Stmt [0-26] - StmtKind: CalibrationGrammarStmt [0-26]: openpulse"#]], + Stmt [0-26]: + annotations: + kind: CalibrationGrammarStmt [0-26]: + name: openpulse"#]], ); } diff --git a/compiler/qsc_qasm3/src/parser/stmt/tests/classical_decl.rs b/compiler/qsc_qasm3/src/parser/stmt/tests/classical_decl.rs index 1a6dd141fe..e9292638af 100644 --- a/compiler/qsc_qasm3/src/parser/stmt/tests/classical_decl.rs +++ b/compiler/qsc_qasm3/src/parser/stmt/tests/classical_decl.rs @@ -13,8 +13,13 @@ fn bit_decl() { parse, "bit b;", &expect![[r#" - Stmt [0-6] - StmtKind: ClassicalDeclarationStmt [0-6]: ClassicalType [0-3]: BitType, Ident [4-5] "b""#]], + Stmt [0-6]: + annotations: + kind: ClassicalDeclarationStmt [0-6]: + type: ScalarType [0-3]: BitType [0-3]: + size: + ident: Ident [4-5] "b" + init_expr: "#]], ); } @@ -24,8 +29,13 @@ fn bit_decl_bit_lit() { parse, "bit b = 1;", &expect![[r#" - Stmt [0-10] - StmtKind: ClassicalDeclarationStmt [0-10]: ClassicalType [0-3]: BitType, Ident [4-5] "b", ValueExpression Expr [8-9]: Lit: Int(1)"#]], + Stmt [0-10]: + annotations: + kind: ClassicalDeclarationStmt [0-10]: + type: ScalarType [0-3]: BitType [0-3]: + size: + ident: Ident [4-5] "b" + init_expr: Expr [8-9]: Lit: Int(1)"#]], ); } @@ -35,8 +45,13 @@ fn const_bit_decl_bit_lit() { parse, "const bit b = 1;", &expect![[r#" - Stmt [0-16] - StmtKind: ConstantDeclaration [0-16]: ClassicalType [6-9]: BitType, Ident [10-11] "b", Expr [14-15]: Lit: Int(1)"#]], + Stmt [0-16]: + annotations: + kind: ConstantDeclStmt [0-16]: + type: ScalarType [6-9]: BitType [6-9]: + size: + ident: Ident [10-11] "b" + init_expr: Expr [14-15]: Lit: Int(1)"#]], ); } @@ -46,8 +61,13 @@ fn bit_array_decl() { parse, "bit[2] b;", &expect![[r#" - Stmt [0-9] - StmtKind: ClassicalDeclarationStmt [0-9]: ClassicalType [0-6]: BitType [0-6]: Expr [4-5]: Lit: Int(2), Ident [7-8] "b""#]], + Stmt [0-9]: + annotations: + kind: ClassicalDeclarationStmt [0-9]: + type: ScalarType [0-6]: BitType [0-6]: + size: Expr [4-5]: Lit: Int(2) + ident: Ident [7-8] "b" + init_expr: "#]], ); } @@ -57,8 +77,13 @@ fn bit_array_decl_bit_lit() { parse, "bit[2] b = \"11\";", &expect![[r#" - Stmt [0-16] - StmtKind: ClassicalDeclarationStmt [0-16]: ClassicalType [0-6]: BitType [0-6]: Expr [4-5]: Lit: Int(2), Ident [7-8] "b", ValueExpression Expr [11-15]: Lit: Bitstring("11")"#]], + Stmt [0-16]: + annotations: + kind: ClassicalDeclarationStmt [0-16]: + type: ScalarType [0-6]: BitType [0-6]: + size: Expr [4-5]: Lit: Int(2) + ident: Ident [7-8] "b" + init_expr: Expr [11-15]: Lit: Bitstring("11")"#]], ); } @@ -68,8 +93,13 @@ fn const_bit_array_decl_bit_lit() { parse, "const bit[2] b = \"11\";", &expect![[r#" - Stmt [0-22] - StmtKind: ConstantDeclaration [0-22]: ClassicalType [6-12]: BitType [6-12]: Expr [10-11]: Lit: Int(2), Ident [13-14] "b", Expr [17-21]: Lit: Bitstring("11")"#]], + Stmt [0-22]: + annotations: + kind: ConstantDeclStmt [0-22]: + type: ScalarType [6-12]: BitType [6-12]: + size: Expr [10-11]: Lit: Int(2) + ident: Ident [13-14] "b" + init_expr: Expr [17-21]: Lit: Bitstring("11")"#]], ); } @@ -79,8 +109,12 @@ fn bool_decl() { parse, "bool b;", &expect![[r#" - Stmt [0-7] - StmtKind: ClassicalDeclarationStmt [0-7]: ClassicalType [0-4]: BoolType, Ident [5-6] "b""#]], + Stmt [0-7]: + annotations: + kind: ClassicalDeclarationStmt [0-7]: + type: ScalarType [0-4]: BoolType + ident: Ident [5-6] "b" + init_expr: "#]], ); } @@ -90,8 +124,12 @@ fn bool_decl_int_lit() { parse, "bool b = 1;", &expect![[r#" - Stmt [0-11] - StmtKind: ClassicalDeclarationStmt [0-11]: ClassicalType [0-4]: BoolType, Ident [5-6] "b", ValueExpression Expr [9-10]: Lit: Int(1)"#]], + Stmt [0-11]: + annotations: + kind: ClassicalDeclarationStmt [0-11]: + type: ScalarType [0-4]: BoolType + ident: Ident [5-6] "b" + init_expr: Expr [9-10]: Lit: Int(1)"#]], ); } @@ -101,8 +139,12 @@ fn const_bool_decl_bool_lit() { parse, "const bool b = true;", &expect![[r#" - Stmt [0-20] - StmtKind: ConstantDeclaration [0-20]: ClassicalType [6-10]: BoolType, Ident [11-12] "b", Expr [15-19]: Lit: Bool(true)"#]], + Stmt [0-20]: + annotations: + kind: ConstantDeclStmt [0-20]: + type: ScalarType [6-10]: BoolType + ident: Ident [11-12] "b" + init_expr: Expr [15-19]: Lit: Bool(true)"#]], ); } @@ -112,8 +154,13 @@ fn complex_decl() { parse, "complex c;", &expect![[r#" - Stmt [0-10] - StmtKind: ClassicalDeclarationStmt [0-10]: ClassicalType [0-7]: ComplexType [0-7], Ident [8-9] "c""#]], + Stmt [0-10]: + annotations: + kind: ClassicalDeclarationStmt [0-10]: + type: ScalarType [0-7]: ComplexType [0-7]: + base_size: + ident: Ident [8-9] "c" + init_expr: "#]], ); } @@ -123,8 +170,13 @@ fn complex_decl_complex_lit() { parse, "complex c = 1im;", &expect![[r#" - Stmt [0-16] - StmtKind: ClassicalDeclarationStmt [0-16]: ClassicalType [0-7]: ComplexType [0-7], Ident [8-9] "c", ValueExpression Expr [12-15]: Lit: Imaginary(1.0)"#]], + Stmt [0-16]: + annotations: + kind: ClassicalDeclarationStmt [0-16]: + type: ScalarType [0-7]: ComplexType [0-7]: + base_size: + ident: Ident [8-9] "c" + init_expr: Expr [12-15]: Lit: Imaginary(1.0)"#]], ); } @@ -134,8 +186,13 @@ fn const_complex_decl_complex_lit() { parse, "const complex c = 1im;", &expect![[r#" - Stmt [0-22] - StmtKind: ConstantDeclaration [0-22]: ClassicalType [6-13]: ComplexType [6-13], Ident [14-15] "c", Expr [18-21]: Lit: Imaginary(1.0)"#]], + Stmt [0-22]: + annotations: + kind: ConstantDeclStmt [0-22]: + type: ScalarType [6-13]: ComplexType [6-13]: + base_size: + ident: Ident [14-15] "c" + init_expr: Expr [18-21]: Lit: Imaginary(1.0)"#]], ); } @@ -145,10 +202,16 @@ fn const_complex_decl_complex_binary_lit() { parse, "const complex c = 23.5 + 1.7im;", &expect![[r#" - Stmt [0-31] - StmtKind: ConstantDeclaration [0-31]: ClassicalType [6-13]: ComplexType [6-13], Ident [14-15] "c", Expr [18-30]: BinOp (Add): - Expr [18-22]: Lit: Float(23.5) - Expr [25-30]: Lit: Imaginary(1.7)"#]], + Stmt [0-31]: + annotations: + kind: ConstantDeclStmt [0-31]: + type: ScalarType [6-13]: ComplexType [6-13]: + base_size: + ident: Ident [14-15] "c" + init_expr: Expr [18-30]: BinaryOpExpr: + op: Add + lhs: Expr [18-22]: Lit: Float(23.5) + rhs: Expr [25-30]: Lit: Imaginary(1.7)"#]], ); } @@ -158,8 +221,14 @@ fn complex_sized_decl() { parse, "complex[float[32]] c;", &expect![[r#" - Stmt [0-21] - StmtKind: ClassicalDeclarationStmt [0-21]: ClassicalType [0-18]: ComplexType[float[FloatType[Expr [14-16]: Lit: Int(32)]: [8-17]]]: [0-18], Ident [19-20] "c""#]], + Stmt [0-21]: + annotations: + kind: ClassicalDeclarationStmt [0-21]: + type: ScalarType [0-18]: ComplexType [0-18]: + base_size: FloatType [8-17]: + size: Expr [14-16]: Lit: Int(32) + ident: Ident [19-20] "c" + init_expr: "#]], ); } @@ -191,8 +260,14 @@ fn complex_sized_decl_complex_lit() { parse, "complex[float[32]] c = 1im;", &expect![[r#" - Stmt [0-27] - StmtKind: ClassicalDeclarationStmt [0-27]: ClassicalType [0-18]: ComplexType[float[FloatType[Expr [14-16]: Lit: Int(32)]: [8-17]]]: [0-18], Ident [19-20] "c", ValueExpression Expr [23-26]: Lit: Imaginary(1.0)"#]], + Stmt [0-27]: + annotations: + kind: ClassicalDeclarationStmt [0-27]: + type: ScalarType [0-18]: ComplexType [0-18]: + base_size: FloatType [8-17]: + size: Expr [14-16]: Lit: Int(32) + ident: Ident [19-20] "c" + init_expr: Expr [23-26]: Lit: Imaginary(1.0)"#]], ); } @@ -202,8 +277,14 @@ fn const_complex_sized_decl_complex_lit() { parse, "const complex[float[32]] c = 1im;", &expect![[r#" - Stmt [0-33] - StmtKind: ConstantDeclaration [0-33]: ClassicalType [6-24]: ComplexType[float[FloatType[Expr [20-22]: Lit: Int(32)]: [14-23]]]: [6-24], Ident [25-26] "c", Expr [29-32]: Lit: Imaginary(1.0)"#]], + Stmt [0-33]: + annotations: + kind: ConstantDeclStmt [0-33]: + type: ScalarType [6-24]: ComplexType [6-24]: + base_size: FloatType [14-23]: + size: Expr [20-22]: Lit: Int(32) + ident: Ident [25-26] "c" + init_expr: Expr [29-32]: Lit: Imaginary(1.0)"#]], ); } @@ -213,8 +294,13 @@ fn int_decl() { parse, "int i;", &expect![[r#" - Stmt [0-6] - StmtKind: ClassicalDeclarationStmt [0-6]: ClassicalType [0-3]: IntType [0-3], Ident [4-5] "i""#]], + Stmt [0-6]: + annotations: + kind: ClassicalDeclarationStmt [0-6]: + type: ScalarType [0-3]: IntType [0-3]: + size: + ident: Ident [4-5] "i" + init_expr: "#]], ); } @@ -224,8 +310,13 @@ fn int_decl_int_lit() { parse, "int i = 1;", &expect![[r#" - Stmt [0-10] - StmtKind: ClassicalDeclarationStmt [0-10]: ClassicalType [0-3]: IntType [0-3], Ident [4-5] "i", ValueExpression Expr [8-9]: Lit: Int(1)"#]], + Stmt [0-10]: + annotations: + kind: ClassicalDeclarationStmt [0-10]: + type: ScalarType [0-3]: IntType [0-3]: + size: + ident: Ident [4-5] "i" + init_expr: Expr [8-9]: Lit: Int(1)"#]], ); } @@ -235,8 +326,13 @@ fn const_int_decl_int_lit() { parse, "const int i = 1;", &expect![[r#" - Stmt [0-16] - StmtKind: ConstantDeclaration [0-16]: ClassicalType [6-9]: IntType [6-9], Ident [10-11] "i", Expr [14-15]: Lit: Int(1)"#]], + Stmt [0-16]: + annotations: + kind: ConstantDeclStmt [0-16]: + type: ScalarType [6-9]: IntType [6-9]: + size: + ident: Ident [10-11] "i" + init_expr: Expr [14-15]: Lit: Int(1)"#]], ); } @@ -246,8 +342,13 @@ fn int_sized_decl() { parse, "int[32] i;", &expect![[r#" - Stmt [0-10] - StmtKind: ClassicalDeclarationStmt [0-10]: ClassicalType [0-7]: IntType[Expr [4-6]: Lit: Int(32)]: [0-7], Ident [8-9] "i""#]], + Stmt [0-10]: + annotations: + kind: ClassicalDeclarationStmt [0-10]: + type: ScalarType [0-7]: IntType [0-7]: + size: Expr [4-6]: Lit: Int(32) + ident: Ident [8-9] "i" + init_expr: "#]], ); } @@ -257,8 +358,13 @@ fn int_sized_decl_int_lit() { parse, "int[32] i = 1;", &expect![[r#" - Stmt [0-14] - StmtKind: ClassicalDeclarationStmt [0-14]: ClassicalType [0-7]: IntType[Expr [4-6]: Lit: Int(32)]: [0-7], Ident [8-9] "i", ValueExpression Expr [12-13]: Lit: Int(1)"#]], + Stmt [0-14]: + annotations: + kind: ClassicalDeclarationStmt [0-14]: + type: ScalarType [0-7]: IntType [0-7]: + size: Expr [4-6]: Lit: Int(32) + ident: Ident [8-9] "i" + init_expr: Expr [12-13]: Lit: Int(1)"#]], ); } @@ -268,8 +374,13 @@ fn const_int_sized_decl_int_lit() { parse, "const int[32] i = 1;", &expect![[r#" - Stmt [0-20] - StmtKind: ConstantDeclaration [0-20]: ClassicalType [6-13]: IntType[Expr [10-12]: Lit: Int(32)]: [6-13], Ident [14-15] "i", Expr [18-19]: Lit: Int(1)"#]], + Stmt [0-20]: + annotations: + kind: ConstantDeclStmt [0-20]: + type: ScalarType [6-13]: IntType [6-13]: + size: Expr [10-12]: Lit: Int(32) + ident: Ident [14-15] "i" + init_expr: Expr [18-19]: Lit: Int(1)"#]], ); } @@ -279,8 +390,13 @@ fn uint_decl() { parse, "uint i;", &expect![[r#" - Stmt [0-7] - StmtKind: ClassicalDeclarationStmt [0-7]: ClassicalType [0-4]: UIntType [0-4], Ident [5-6] "i""#]], + Stmt [0-7]: + annotations: + kind: ClassicalDeclarationStmt [0-7]: + type: ScalarType [0-4]: UIntType [0-4]: + size: + ident: Ident [5-6] "i" + init_expr: "#]], ); } @@ -290,8 +406,13 @@ fn uint_decl_uint_lit() { parse, "uint i = 1;", &expect![[r#" - Stmt [0-11] - StmtKind: ClassicalDeclarationStmt [0-11]: ClassicalType [0-4]: UIntType [0-4], Ident [5-6] "i", ValueExpression Expr [9-10]: Lit: Int(1)"#]], + Stmt [0-11]: + annotations: + kind: ClassicalDeclarationStmt [0-11]: + type: ScalarType [0-4]: UIntType [0-4]: + size: + ident: Ident [5-6] "i" + init_expr: Expr [9-10]: Lit: Int(1)"#]], ); } @@ -301,8 +422,13 @@ fn const_uint_decl_uint_lit() { parse, "const uint i = 1;", &expect![[r#" - Stmt [0-17] - StmtKind: ConstantDeclaration [0-17]: ClassicalType [6-10]: UIntType [6-10], Ident [11-12] "i", Expr [15-16]: Lit: Int(1)"#]], + Stmt [0-17]: + annotations: + kind: ConstantDeclStmt [0-17]: + type: ScalarType [6-10]: UIntType [6-10]: + size: + ident: Ident [11-12] "i" + init_expr: Expr [15-16]: Lit: Int(1)"#]], ); } @@ -312,8 +438,13 @@ fn uint_sized_decl() { parse, "uint[32] i;", &expect![[r#" - Stmt [0-11] - StmtKind: ClassicalDeclarationStmt [0-11]: ClassicalType [0-8]: UIntType[Expr [5-7]: Lit: Int(32)]: [0-8], Ident [9-10] "i""#]], + Stmt [0-11]: + annotations: + kind: ClassicalDeclarationStmt [0-11]: + type: ScalarType [0-8]: UIntType [0-8]: + size: Expr [5-7]: Lit: Int(32) + ident: Ident [9-10] "i" + init_expr: "#]], ); } @@ -323,8 +454,13 @@ fn uint_sized_decl_uint_lit() { parse, "uint[32] i = 1;", &expect![[r#" - Stmt [0-15] - StmtKind: ClassicalDeclarationStmt [0-15]: ClassicalType [0-8]: UIntType[Expr [5-7]: Lit: Int(32)]: [0-8], Ident [9-10] "i", ValueExpression Expr [13-14]: Lit: Int(1)"#]], + Stmt [0-15]: + annotations: + kind: ClassicalDeclarationStmt [0-15]: + type: ScalarType [0-8]: UIntType [0-8]: + size: Expr [5-7]: Lit: Int(32) + ident: Ident [9-10] "i" + init_expr: Expr [13-14]: Lit: Int(1)"#]], ); } @@ -334,8 +470,13 @@ fn const_uint_sized_decl_uint_lit() { parse, "const uint[32] i = 1;", &expect![[r#" - Stmt [0-21] - StmtKind: ConstantDeclaration [0-21]: ClassicalType [6-14]: UIntType[Expr [11-13]: Lit: Int(32)]: [6-14], Ident [15-16] "i", Expr [19-20]: Lit: Int(1)"#]], + Stmt [0-21]: + annotations: + kind: ConstantDeclStmt [0-21]: + type: ScalarType [6-14]: UIntType [6-14]: + size: Expr [11-13]: Lit: Int(32) + ident: Ident [15-16] "i" + init_expr: Expr [19-20]: Lit: Int(1)"#]], ); } @@ -345,8 +486,13 @@ fn float_decl() { parse, "float f;", &expect![[r#" - Stmt [0-8] - StmtKind: ClassicalDeclarationStmt [0-8]: ClassicalType [0-5]: FloatType [0-5], Ident [6-7] "f""#]], + Stmt [0-8]: + annotations: + kind: ClassicalDeclarationStmt [0-8]: + type: ScalarType [0-5]: FloatType [0-5]: + size: + ident: Ident [6-7] "f" + init_expr: "#]], ); } @@ -356,8 +502,13 @@ fn float_decl_float_lit() { parse, "float f = 1;", &expect![[r#" - Stmt [0-12] - StmtKind: ClassicalDeclarationStmt [0-12]: ClassicalType [0-5]: FloatType [0-5], Ident [6-7] "f", ValueExpression Expr [10-11]: Lit: Int(1)"#]], + Stmt [0-12]: + annotations: + kind: ClassicalDeclarationStmt [0-12]: + type: ScalarType [0-5]: FloatType [0-5]: + size: + ident: Ident [6-7] "f" + init_expr: Expr [10-11]: Lit: Int(1)"#]], ); } @@ -367,8 +518,13 @@ fn const_float_decl_float_lit() { parse, "const float f = 1.0;", &expect![[r#" - Stmt [0-20] - StmtKind: ConstantDeclaration [0-20]: ClassicalType [6-11]: FloatType [6-11], Ident [12-13] "f", Expr [16-19]: Lit: Float(1.0)"#]], + Stmt [0-20]: + annotations: + kind: ConstantDeclStmt [0-20]: + type: ScalarType [6-11]: FloatType [6-11]: + size: + ident: Ident [12-13] "f" + init_expr: Expr [16-19]: Lit: Float(1.0)"#]], ); } @@ -378,8 +534,13 @@ fn float_sized_decl() { parse, "float[32] f;", &expect![[r#" - Stmt [0-12] - StmtKind: ClassicalDeclarationStmt [0-12]: ClassicalType [0-9]: FloatType[Expr [6-8]: Lit: Int(32)]: [0-9], Ident [10-11] "f""#]], + Stmt [0-12]: + annotations: + kind: ClassicalDeclarationStmt [0-12]: + type: ScalarType [0-9]: FloatType [0-9]: + size: Expr [6-8]: Lit: Int(32) + ident: Ident [10-11] "f" + init_expr: "#]], ); } @@ -389,8 +550,13 @@ fn float_sized_decl_float_lit() { parse, "float[32] f = 1.0;", &expect![[r#" - Stmt [0-18] - StmtKind: ClassicalDeclarationStmt [0-18]: ClassicalType [0-9]: FloatType[Expr [6-8]: Lit: Int(32)]: [0-9], Ident [10-11] "f", ValueExpression Expr [14-17]: Lit: Float(1.0)"#]], + Stmt [0-18]: + annotations: + kind: ClassicalDeclarationStmt [0-18]: + type: ScalarType [0-9]: FloatType [0-9]: + size: Expr [6-8]: Lit: Int(32) + ident: Ident [10-11] "f" + init_expr: Expr [14-17]: Lit: Float(1.0)"#]], ); } @@ -400,8 +566,13 @@ fn const_float_sized_decl_float_lit() { parse, "const float[32] f = 1;", &expect![[r#" - Stmt [0-22] - StmtKind: ConstantDeclaration [0-22]: ClassicalType [6-15]: FloatType[Expr [12-14]: Lit: Int(32)]: [6-15], Ident [16-17] "f", Expr [20-21]: Lit: Int(1)"#]], + Stmt [0-22]: + annotations: + kind: ConstantDeclStmt [0-22]: + type: ScalarType [6-15]: FloatType [6-15]: + size: Expr [12-14]: Lit: Int(32) + ident: Ident [16-17] "f" + init_expr: Expr [20-21]: Lit: Int(1)"#]], ); } @@ -411,8 +582,13 @@ fn angle_decl() { parse, "angle a;", &expect![[r#" - Stmt [0-8] - StmtKind: ClassicalDeclarationStmt [0-8]: ClassicalType [0-5]: AngleType [0-5], Ident [6-7] "a""#]], + Stmt [0-8]: + annotations: + kind: ClassicalDeclarationStmt [0-8]: + type: ScalarType [0-5]: AngleType [0-5]: + size: + ident: Ident [6-7] "a" + init_expr: "#]], ); } @@ -422,8 +598,13 @@ fn angle_decl_angle_lit() { parse, "angle a = 1.0;", &expect![[r#" - Stmt [0-14] - StmtKind: ClassicalDeclarationStmt [0-14]: ClassicalType [0-5]: AngleType [0-5], Ident [6-7] "a", ValueExpression Expr [10-13]: Lit: Float(1.0)"#]], + Stmt [0-14]: + annotations: + kind: ClassicalDeclarationStmt [0-14]: + type: ScalarType [0-5]: AngleType [0-5]: + size: + ident: Ident [6-7] "a" + init_expr: Expr [10-13]: Lit: Float(1.0)"#]], ); } @@ -453,8 +634,13 @@ fn const_angle_decl_angle_lit() { parse, "const angle a = 1.0;", &expect![[r#" - Stmt [0-20] - StmtKind: ConstantDeclaration [0-20]: ClassicalType [6-11]: AngleType [6-11], Ident [12-13] "a", Expr [16-19]: Lit: Float(1.0)"#]], + Stmt [0-20]: + annotations: + kind: ConstantDeclStmt [0-20]: + type: ScalarType [6-11]: AngleType [6-11]: + size: + ident: Ident [12-13] "a" + init_expr: Expr [16-19]: Lit: Float(1.0)"#]], ); } @@ -464,8 +650,13 @@ fn angle_sized_decl() { parse, "angle[32] a;", &expect![[r#" - Stmt [0-12] - StmtKind: ClassicalDeclarationStmt [0-12]: ClassicalType [0-9]: AngleType [0-9]: Expr [6-8]: Lit: Int(32), Ident [10-11] "a""#]], + Stmt [0-12]: + annotations: + kind: ClassicalDeclarationStmt [0-12]: + type: ScalarType [0-9]: AngleType [0-9]: + size: Expr [6-8]: Lit: Int(32) + ident: Ident [10-11] "a" + init_expr: "#]], ); } @@ -475,8 +666,13 @@ fn angle_sized_decl_angle_lit() { parse, "angle[32] a = 1.0;", &expect![[r#" - Stmt [0-18] - StmtKind: ClassicalDeclarationStmt [0-18]: ClassicalType [0-9]: AngleType [0-9]: Expr [6-8]: Lit: Int(32), Ident [10-11] "a", ValueExpression Expr [14-17]: Lit: Float(1.0)"#]], + Stmt [0-18]: + annotations: + kind: ClassicalDeclarationStmt [0-18]: + type: ScalarType [0-9]: AngleType [0-9]: + size: Expr [6-8]: Lit: Int(32) + ident: Ident [10-11] "a" + init_expr: Expr [14-17]: Lit: Float(1.0)"#]], ); } @@ -486,8 +682,13 @@ fn const_angle_sized_decl_angle_lit() { parse, "const angle[32] a = 1.0;", &expect![[r#" - Stmt [0-24] - StmtKind: ConstantDeclaration [0-24]: ClassicalType [6-15]: AngleType [6-15]: Expr [12-14]: Lit: Int(32), Ident [16-17] "a", Expr [20-23]: Lit: Float(1.0)"#]], + Stmt [0-24]: + annotations: + kind: ConstantDeclStmt [0-24]: + type: ScalarType [6-15]: AngleType [6-15]: + size: Expr [12-14]: Lit: Int(32) + ident: Ident [16-17] "a" + init_expr: Expr [20-23]: Lit: Float(1.0)"#]], ); } @@ -497,8 +698,12 @@ fn duration_decl() { parse, "duration d;", &expect![[r#" - Stmt [0-11] - StmtKind: ClassicalDeclarationStmt [0-11]: ClassicalType [0-8]: Duration, Ident [9-10] "d""#]], + Stmt [0-11]: + annotations: + kind: ClassicalDeclarationStmt [0-11]: + type: ScalarType [0-8]: Duration + ident: Ident [9-10] "d" + init_expr: "#]], ); } @@ -508,8 +713,12 @@ fn duration_decl_ns_lit() { parse, "duration d = 1000ns;", &expect![[r#" - Stmt [0-20] - StmtKind: ClassicalDeclarationStmt [0-20]: ClassicalType [0-8]: Duration, Ident [9-10] "d", ValueExpression Expr [13-19]: Lit: Duration(1000.0, Ns)"#]], + Stmt [0-20]: + annotations: + kind: ClassicalDeclarationStmt [0-20]: + type: ScalarType [0-8]: Duration + ident: Ident [9-10] "d" + init_expr: Expr [13-19]: Lit: Duration(1000.0, Ns)"#]], ); } @@ -519,8 +728,12 @@ fn duration_decl_us_lit() { parse, "duration d = 1000us;", &expect![[r#" - Stmt [0-20] - StmtKind: ClassicalDeclarationStmt [0-20]: ClassicalType [0-8]: Duration, Ident [9-10] "d", ValueExpression Expr [13-19]: Lit: Duration(1000.0, Us)"#]], + Stmt [0-20]: + annotations: + kind: ClassicalDeclarationStmt [0-20]: + type: ScalarType [0-8]: Duration + ident: Ident [9-10] "d" + init_expr: Expr [13-19]: Lit: Duration(1000.0, Us)"#]], ); } @@ -532,8 +745,12 @@ fn duration_decl_uus_lit() { parse, "duration d = 1000µs;", &expect![[r#" - Stmt [0-21] - StmtKind: ClassicalDeclarationStmt [0-21]: ClassicalType [0-8]: Duration, Ident [9-10] "d", ValueExpression Expr [13-20]: Lit: Duration(1000.0, Us)"#]], + Stmt [0-21]: + annotations: + kind: ClassicalDeclarationStmt [0-21]: + type: ScalarType [0-8]: Duration + ident: Ident [9-10] "d" + init_expr: Expr [13-20]: Lit: Duration(1000.0, Us)"#]], ); } @@ -543,8 +760,12 @@ fn duration_decl_ms_lit() { parse, "duration d = 1000ms;", &expect![[r#" - Stmt [0-20] - StmtKind: ClassicalDeclarationStmt [0-20]: ClassicalType [0-8]: Duration, Ident [9-10] "d", ValueExpression Expr [13-19]: Lit: Duration(1000.0, Ms)"#]], + Stmt [0-20]: + annotations: + kind: ClassicalDeclarationStmt [0-20]: + type: ScalarType [0-8]: Duration + ident: Ident [9-10] "d" + init_expr: Expr [13-19]: Lit: Duration(1000.0, Ms)"#]], ); } @@ -554,8 +775,12 @@ fn duration_decl_s_lit() { parse, "duration d = 1000s;", &expect![[r#" - Stmt [0-19] - StmtKind: ClassicalDeclarationStmt [0-19]: ClassicalType [0-8]: Duration, Ident [9-10] "d", ValueExpression Expr [13-18]: Lit: Duration(1000.0, S)"#]], + Stmt [0-19]: + annotations: + kind: ClassicalDeclarationStmt [0-19]: + type: ScalarType [0-8]: Duration + ident: Ident [9-10] "d" + init_expr: Expr [13-18]: Lit: Duration(1000.0, S)"#]], ); } @@ -565,8 +790,12 @@ fn duration_decl_dt_lit() { parse, "duration d = 1000dt;", &expect![[r#" - Stmt [0-20] - StmtKind: ClassicalDeclarationStmt [0-20]: ClassicalType [0-8]: Duration, Ident [9-10] "d", ValueExpression Expr [13-19]: Lit: Duration(1000.0, Dt)"#]], + Stmt [0-20]: + annotations: + kind: ClassicalDeclarationStmt [0-20]: + type: ScalarType [0-8]: Duration + ident: Ident [9-10] "d" + init_expr: Expr [13-19]: Lit: Duration(1000.0, Dt)"#]], ); } @@ -576,8 +805,12 @@ fn const_duration_decl_dt_lit() { parse, "const duration d = 10dt;", &expect![[r#" - Stmt [0-24] - StmtKind: ConstantDeclaration [0-24]: ClassicalType [6-14]: Duration, Ident [15-16] "d", Expr [19-23]: Lit: Duration(10.0, Dt)"#]], + Stmt [0-24]: + annotations: + kind: ConstantDeclStmt [0-24]: + type: ScalarType [6-14]: Duration + ident: Ident [15-16] "d" + init_expr: Expr [19-23]: Lit: Duration(10.0, Dt)"#]], ); } @@ -587,8 +820,12 @@ fn stretch_decl() { parse, "stretch s;", &expect![[r#" - Stmt [0-10] - StmtKind: ClassicalDeclarationStmt [0-10]: ClassicalType [0-7]: Stretch, Ident [8-9] "s""#]], + Stmt [0-10]: + annotations: + kind: ClassicalDeclarationStmt [0-10]: + type: ScalarType [0-7]: Stretch + ident: Ident [8-9] "s" + init_expr: "#]], ); } @@ -598,9 +835,16 @@ fn empty_array_decl() { parse, "array[int, 0] arr = {};", &expect![[r#" - Stmt [0-23] - StmtKind: ClassicalDeclarationStmt [0-23]: ArrayType [0-13]: ArrayBaseTypeKind IntType [6-9] - Expr [11-12]: Lit: Int(0), Ident [14-17] "arr", ValueExpression Expr [20-22]: Lit: Array:"#]], + Stmt [0-23]: + annotations: + kind: ClassicalDeclarationStmt [0-23]: + type: ArrayType [0-13]: + base_type: ArrayBaseTypeKind IntType [6-9]: + size: + dimensions: + Expr [11-12]: Lit: Int(0) + ident: Ident [14-17] "arr" + init_expr: Expr [20-22]: Lit: Array: "#]], ); } @@ -610,12 +854,19 @@ fn simple_array_decl() { parse, "array[int[32], 3] arr = {1, 2, 3};", &expect![[r#" - Stmt [0-34] - StmtKind: ClassicalDeclarationStmt [0-34]: ArrayType [0-17]: ArrayBaseTypeKind IntType[Expr [10-12]: Lit: Int(32)]: [6-13] - Expr [15-16]: Lit: Int(3), Ident [18-21] "arr", ValueExpression Expr [24-33]: Lit: Array: - Expr { span: Span { lo: 25, hi: 26 }, kind: Lit(Lit { span: Span { lo: 25, hi: 26 }, kind: Int(1) }) } - Expr { span: Span { lo: 28, hi: 29 }, kind: Lit(Lit { span: Span { lo: 28, hi: 29 }, kind: Int(2) }) } - Expr { span: Span { lo: 31, hi: 32 }, kind: Lit(Lit { span: Span { lo: 31, hi: 32 }, kind: Int(3) }) }"#]], + Stmt [0-34]: + annotations: + kind: ClassicalDeclarationStmt [0-34]: + type: ArrayType [0-17]: + base_type: ArrayBaseTypeKind IntType [6-13]: + size: Expr [10-12]: Lit: Int(32) + dimensions: + Expr [15-16]: Lit: Int(3) + ident: Ident [18-21] "arr" + init_expr: Expr [24-33]: Lit: Array: + Expr [25-26]: Lit: Int(1) + Expr [28-29]: Lit: Int(2) + Expr [31-32]: Lit: Int(3)"#]], ); } @@ -625,13 +876,26 @@ fn nested_array_decl() { parse, "array[int[32], 3, 2] arr = {{1, 2}, {3, 4}, {5, 6}};", &expect![[r#" - Stmt [0-52] - StmtKind: ClassicalDeclarationStmt [0-52]: ArrayType [0-20]: ArrayBaseTypeKind IntType[Expr [10-12]: Lit: Int(32)]: [6-13] - Expr [15-16]: Lit: Int(3) - Expr [18-19]: Lit: Int(2), Ident [21-24] "arr", ValueExpression Expr [27-51]: Lit: Array: - Expr { span: Span { lo: 28, hi: 34 }, kind: Lit(Lit { span: Span { lo: 28, hi: 34 }, kind: Array([Expr { span: Span { lo: 29, hi: 30 }, kind: Lit(Lit { span: Span { lo: 29, hi: 30 }, kind: Int(1) }) }, Expr { span: Span { lo: 32, hi: 33 }, kind: Lit(Lit { span: Span { lo: 32, hi: 33 }, kind: Int(2) }) }]) }) } - Expr { span: Span { lo: 36, hi: 42 }, kind: Lit(Lit { span: Span { lo: 36, hi: 42 }, kind: Array([Expr { span: Span { lo: 37, hi: 38 }, kind: Lit(Lit { span: Span { lo: 37, hi: 38 }, kind: Int(3) }) }, Expr { span: Span { lo: 40, hi: 41 }, kind: Lit(Lit { span: Span { lo: 40, hi: 41 }, kind: Int(4) }) }]) }) } - Expr { span: Span { lo: 44, hi: 50 }, kind: Lit(Lit { span: Span { lo: 44, hi: 50 }, kind: Array([Expr { span: Span { lo: 45, hi: 46 }, kind: Lit(Lit { span: Span { lo: 45, hi: 46 }, kind: Int(5) }) }, Expr { span: Span { lo: 48, hi: 49 }, kind: Lit(Lit { span: Span { lo: 48, hi: 49 }, kind: Int(6) }) }]) }) }"#]], + Stmt [0-52]: + annotations: + kind: ClassicalDeclarationStmt [0-52]: + type: ArrayType [0-20]: + base_type: ArrayBaseTypeKind IntType [6-13]: + size: Expr [10-12]: Lit: Int(32) + dimensions: + Expr [15-16]: Lit: Int(3) + Expr [18-19]: Lit: Int(2) + ident: Ident [21-24] "arr" + init_expr: Expr [27-51]: Lit: Array: + Expr [28-34]: Lit: Array: + Expr [29-30]: Lit: Int(1) + Expr [32-33]: Lit: Int(2) + Expr [36-42]: Lit: Array: + Expr [37-38]: Lit: Int(3) + Expr [40-41]: Lit: Int(4) + Expr [44-50]: Lit: Array: + Expr [45-46]: Lit: Int(5) + Expr [48-49]: Lit: Int(6)"#]], ); } @@ -641,8 +905,14 @@ fn measure_hardware_qubit_decl() { parse, "bit res = measure $12;", &expect![[r#" - Stmt [0-22] - StmtKind: ClassicalDeclarationStmt [0-22]: ClassicalType [0-3]: BitType, Ident [4-7] "res", ValueExpression MeasureExpr [10-17]: GateOperand HardwareQubit [18-21]: 12"#]], + Stmt [0-22]: + annotations: + kind: ClassicalDeclarationStmt [0-22]: + type: ScalarType [0-3]: BitType [0-3]: + size: + ident: Ident [4-7] "res" + init_expr: MeasureExpr [10-17]: + operand: HardwareQubit [18-21]: 12"#]], ); } @@ -652,11 +922,21 @@ fn measure_register_decl() { parse, "bit res = measure qubits[2][3];", &expect![[r#" - Stmt [0-31] - StmtKind: ClassicalDeclarationStmt [0-31]: ClassicalType [0-3]: BitType, Ident [4-7] "res", ValueExpression MeasureExpr [10-17]: GateOperand IndexedIdent [18-30]: Ident [18-24] "qubits"[ - IndexElement: - IndexSetItem Expr [25-26]: Lit: Int(2) - IndexElement: - IndexSetItem Expr [28-29]: Lit: Int(3)]"#]], + Stmt [0-31]: + annotations: + kind: ClassicalDeclarationStmt [0-31]: + type: ScalarType [0-3]: BitType [0-3]: + size: + ident: Ident [4-7] "res" + init_expr: MeasureExpr [10-17]: + operand: IndexedIdent [18-30]: + name: Ident [18-24] "qubits" + indices: + IndexSet [25-26]: + values: + Expr [25-26]: Lit: Int(2) + IndexSet [28-29]: + values: + Expr [28-29]: Lit: Int(3)"#]], ); } diff --git a/compiler/qsc_qasm3/src/parser/stmt/tests/def.rs b/compiler/qsc_qasm3/src/parser/stmt/tests/def.rs index d9f82d4b77..d257821433 100644 --- a/compiler/qsc_qasm3/src/parser/stmt/tests/def.rs +++ b/compiler/qsc_qasm3/src/parser/stmt/tests/def.rs @@ -13,8 +13,13 @@ fn minimal() { parse, "def x() { }", &expect![[r#" - Stmt [0-11] - StmtKind: DefStmt [0-11]: Ident [4-5] "x"() "#]], + Stmt [0-11]: + annotations: + kind: DefStmt [0-11]: + ident: Ident [4-5] "x" + parameters: + return_type: + body: Block [8-11]: "#]], ); } @@ -46,8 +51,16 @@ fn missing_args_with_delim_error() { parse, "def x(,) { }", &expect![[r#" - Stmt [0-12] - StmtKind: DefStmt [0-12]: Ident [4-5] "x"([6-6] Ident [0-0] "": ClassicalType [0-0]: Err) + Stmt [0-12]: + annotations: + kind: DefStmt [0-12]: + ident: Ident [4-5] "x" + parameters: + ScalarTypedParameter [6-6]: + type: ScalarType [0-0]: Err + ident: Ident [0-0] "" + return_type: + body: Block [9-12]: [ Error( @@ -68,8 +81,24 @@ fn args_with_extra_delim_err_ty() { parse, "def x(int a,,int b) { }", &expect![[r#" - Stmt [0-23] - StmtKind: DefStmt [0-23]: Ident [4-5] "x"([6-11] Ident [10-11] "a": ClassicalType [6-9]: IntType [6-9], [12-12] Ident [0-0] "": ClassicalType [0-0]: Err, [13-18] Ident [17-18] "b": ClassicalType [13-16]: IntType [13-16]) + Stmt [0-23]: + annotations: + kind: DefStmt [0-23]: + ident: Ident [4-5] "x" + parameters: + ScalarTypedParameter [6-11]: + type: ScalarType [6-9]: IntType [6-9]: + size: + ident: Ident [10-11] "a" + ScalarTypedParameter [12-12]: + type: ScalarType [0-0]: Err + ident: Ident [0-0] "" + ScalarTypedParameter [13-18]: + type: ScalarType [13-16]: IntType [13-16]: + size: + ident: Ident [17-18] "b" + return_type: + body: Block [20-23]: [ Error( @@ -90,13 +119,25 @@ fn classical_subroutine() { parse, "def square(int[32] x) -> int { return x ** 2; }", &expect![[r#" - Stmt [0-47] - StmtKind: DefStmt [0-47]: Ident [4-10] "square"([11-20] Ident [19-20] "x": ClassicalType [11-18]: IntType[Expr [15-17]: Lit: Int(32)]: [11-18]) - Stmt [31-45] - StmtKind: ReturnStmt [31-45]: ValueExpression Expr [38-44]: BinOp (Exp): - Expr [38-39]: Ident [38-39] "x" - Expr [43-44]: Lit: Int(2) - ClassicalType [25-28]: IntType [25-28]"#]], + Stmt [0-47]: + annotations: + kind: DefStmt [0-47]: + ident: Ident [4-10] "square" + parameters: + ScalarTypedParameter [11-20]: + type: ScalarType [11-18]: IntType [11-18]: + size: Expr [15-17]: Lit: Int(32) + ident: Ident [19-20] "x" + return_type: ScalarType [25-28]: IntType [25-28]: + size: + body: Block [29-47]: + Stmt [31-45]: + annotations: + kind: ReturnStmt [31-45]: + expr: Expr [38-44]: BinaryOpExpr: + op: Exp + lhs: Expr [38-39]: Ident [38-39] "x" + rhs: Expr [43-44]: Lit: Int(2)"#]], ); } @@ -106,8 +147,19 @@ fn quantum_args() { parse, "def x(qubit q, qubit[n] qubits) { }", &expect![[r#" - Stmt [0-35] - StmtKind: DefStmt [0-35]: Ident [4-5] "x"([6-13] Ident [12-13] "q": qubit, [15-30] Ident [24-30] "qubits": qubit[Expr [21-22]: Ident [21-22] "n"]) "#]], + Stmt [0-35]: + annotations: + kind: DefStmt [0-35]: + ident: Ident [4-5] "x" + parameters: + QuantumTypedParameter [6-13]: + size: + ident: Ident [12-13] "q" + QuantumTypedParameter [15-30]: + size: Expr [21-22]: Ident [21-22] "n" + ident: Ident [24-30] "qubits" + return_type: + body: Block [32-35]: "#]], ); } @@ -117,13 +169,35 @@ fn old_style_args() { parse, "def test(creg c, qreg q, creg c2[2], qreg q4[4]) -> int { return x ** 2; }", &expect![[r#" - Stmt [0-74] - StmtKind: DefStmt [0-74]: Ident [4-8] "test"([9-15] Ident [14-15] "c": ClassicalType [9-15]: BitType, [17-23] Ident [22-23] "q": qubit, [25-35] Ident [30-32] "c2": ClassicalType [25-35]: BitType [25-35]: Expr [33-34]: Lit: Int(2), [37-47] Ident [42-44] "q4": qubit[Expr [45-46]: Lit: Int(4)]) - Stmt [58-72] - StmtKind: ReturnStmt [58-72]: ValueExpression Expr [65-71]: BinOp (Exp): - Expr [65-66]: Ident [65-66] "x" - Expr [70-71]: Lit: Int(2) - ClassicalType [52-55]: IntType [52-55]"#]], + Stmt [0-74]: + annotations: + kind: DefStmt [0-74]: + ident: Ident [4-8] "test" + parameters: + ScalarTypedParameter [9-15]: + type: ScalarType [9-15]: BitType [9-15]: + size: + ident: Ident [14-15] "c" + QuantumTypedParameter [17-23]: + size: + ident: Ident [22-23] "q" + ScalarTypedParameter [25-35]: + type: ScalarType [25-35]: BitType [25-35]: + size: Expr [33-34]: Lit: Int(2) + ident: Ident [30-32] "c2" + QuantumTypedParameter [37-47]: + size: Expr [45-46]: Lit: Int(4) + ident: Ident [42-44] "q4" + return_type: ScalarType [52-55]: IntType [52-55]: + size: + body: Block [56-74]: + Stmt [58-72]: + annotations: + kind: ReturnStmt [58-72]: + expr: Expr [65-71]: BinaryOpExpr: + op: Exp + lhs: Expr [65-66]: Ident [65-66] "x" + rhs: Expr [70-71]: Lit: Int(2)"#]], ); } @@ -133,10 +207,23 @@ fn readonly_array_arg_with_int_dims() { parse, "def specified_sub(readonly array[int[8], 2, 10] arr_arg) {}", &expect![[r#" - Stmt [0-59] - StmtKind: DefStmt [0-59]: Ident [4-17] "specified_sub"([18-55] Ident [48-55] "arr_arg": ArrayReferenceType [18-47]: ArrayBaseTypeKind IntType[Expr [37-38]: Lit: Int(8)]: [33-39] - Expr [41-42]: Lit: Int(2) - Expr [44-46]: Lit: Int(10)) "#]], + Stmt [0-59]: + annotations: + kind: DefStmt [0-59]: + ident: Ident [4-17] "specified_sub" + parameters: + ArrayTypedParameter [18-55]: + type: ArrayReferenceType [18-47]: + mutability: ReadOnly + base_type: ArrayBaseTypeKind IntType [33-39]: + size: Expr [37-38]: Lit: Int(8) + dimensions: + Expr [41-42]: Lit: Int(2) + Expr [44-46]: Lit: Int(10) + + ident: Ident [48-55] "arr_arg" + return_type: + body: Block [57-59]: "#]], ); } @@ -146,9 +233,22 @@ fn readonly_array_arg_with_dim() { parse, "def arr_subroutine(readonly array[int[8], #dim = 1] arr_arg) {}", &expect![[r#" - Stmt [0-63] - StmtKind: DefStmt [0-63]: Ident [4-18] "arr_subroutine"([19-59] Ident [52-59] "arr_arg": ArrayReferenceType [19-51]: ArrayBaseTypeKind IntType[Expr [38-39]: Lit: Int(8)]: [34-40] - Expr [49-50]: Lit: Int(1)) "#]], + Stmt [0-63]: + annotations: + kind: DefStmt [0-63]: + ident: Ident [4-18] "arr_subroutine" + parameters: + ArrayTypedParameter [19-59]: + type: ArrayReferenceType [19-51]: + mutability: ReadOnly + base_type: ArrayBaseTypeKind IntType [34-40]: + size: Expr [38-39]: Lit: Int(8) + dimensions: + Expr [49-50]: Lit: Int(1) + + ident: Ident [52-59] "arr_arg" + return_type: + body: Block [61-63]: "#]], ); } @@ -158,8 +258,21 @@ fn mutable_array_arg() { parse, "def mut_subroutine(mutable array[int[8], #dim = 1] arr_arg) {}", &expect![[r#" - Stmt [0-62] - StmtKind: DefStmt [0-62]: Ident [4-18] "mut_subroutine"([19-58] Ident [51-58] "arr_arg": ArrayReferenceType [19-50]: ArrayBaseTypeKind IntType[Expr [37-38]: Lit: Int(8)]: [33-39] - Expr [48-49]: Lit: Int(1)) "#]], + Stmt [0-62]: + annotations: + kind: DefStmt [0-62]: + ident: Ident [4-18] "mut_subroutine" + parameters: + ArrayTypedParameter [19-58]: + type: ArrayReferenceType [19-50]: + mutability: Mutable + base_type: ArrayBaseTypeKind IntType [33-39]: + size: Expr [37-38]: Lit: Int(8) + dimensions: + Expr [48-49]: Lit: Int(1) + + ident: Ident [51-58] "arr_arg" + return_type: + body: Block [60-62]: "#]], ); } diff --git a/compiler/qsc_qasm3/src/parser/stmt/tests/defcal.rs b/compiler/qsc_qasm3/src/parser/stmt/tests/defcal.rs index 986f833810..b086ea2e68 100644 --- a/compiler/qsc_qasm3/src/parser/stmt/tests/defcal.rs +++ b/compiler/qsc_qasm3/src/parser/stmt/tests/defcal.rs @@ -38,7 +38,8 @@ fn cal_block_accept_any_tokens_inside() { .314 }", &expect![[r#" - Stmt [5-88] - StmtKind: DefCalStmt [5-88]"#]], + Stmt [5-88]: + annotations: + kind: DefCalStmt [5-88]"#]], ); } diff --git a/compiler/qsc_qasm3/src/parser/stmt/tests/delay.rs b/compiler/qsc_qasm3/src/parser/stmt/tests/delay.rs index 463ba062f0..2fb62a4e1f 100644 --- a/compiler/qsc_qasm3/src/parser/stmt/tests/delay.rs +++ b/compiler/qsc_qasm3/src/parser/stmt/tests/delay.rs @@ -11,13 +11,22 @@ fn delay() { parse, "delay[a] q[0], q[1];", &expect![[r#" - Stmt [0-20] - StmtKind: DelayInstruction [0-20]: Expr [6-7]: Ident [6-7] "a" - GateOperand IndexedIdent [9-13]: Ident [9-10] "q"[ - IndexElement: - IndexSetItem Expr [11-12]: Lit: Int(0)] - GateOperand IndexedIdent [15-19]: Ident [15-16] "q"[ - IndexElement: - IndexSetItem Expr [17-18]: Lit: Int(1)]"#]], + Stmt [0-20]: + annotations: + kind: DelayStmt [0-20]: + duration: Expr [6-7]: Ident [6-7] "a" + qubits: + IndexedIdent [9-13]: + name: Ident [9-10] "q" + indices: + IndexSet [11-12]: + values: + Expr [11-12]: Lit: Int(0) + IndexedIdent [15-19]: + name: Ident [15-16] "q" + indices: + IndexSet [17-18]: + values: + Expr [17-18]: Lit: Int(1)"#]], ); } diff --git a/compiler/qsc_qasm3/src/parser/stmt/tests/expr_stmt.rs b/compiler/qsc_qasm3/src/parser/stmt/tests/expr_stmt.rs index f5a5082ad7..d5f55416e4 100644 --- a/compiler/qsc_qasm3/src/parser/stmt/tests/expr_stmt.rs +++ b/compiler/qsc_qasm3/src/parser/stmt/tests/expr_stmt.rs @@ -11,8 +11,10 @@ fn identifier() { parse, "H;", &expect![[r#" - Stmt [0-2] - StmtKind: ExprStmt [0-2]: Expr [0-1]: Ident [0-1] "H""#]], + Stmt [0-2]: + annotations: + kind: ExprStmt [0-2]: + expr: Expr [0-1]: Ident [0-1] "H""#]], ); } @@ -22,10 +24,13 @@ fn identifier_plus_number() { parse, "H + 2;", &expect![[r#" - Stmt [0-6] - StmtKind: ExprStmt [0-6]: Expr [0-5]: BinOp (Add): - Expr [0-1]: Ident [0-1] "H" - Expr [4-5]: Lit: Int(2)"#]], + Stmt [0-6]: + annotations: + kind: ExprStmt [0-6]: + expr: Expr [0-5]: BinaryOpExpr: + op: Add + lhs: Expr [0-1]: Ident [0-1] "H" + rhs: Expr [4-5]: Lit: Int(2)"#]], ); } @@ -37,12 +42,17 @@ fn function_call_plus_ident() { parse, "Name(2, 3) + a;", &expect![[r#" - Stmt [0-15] - StmtKind: ExprStmt [0-15]: Expr [0-14]: BinOp (Add): - Expr [0-10]: FunctionCall [0-10]: Ident [0-4] "Name" - Expr [5-6]: Lit: Int(2) - Expr [8-9]: Lit: Int(3) - Expr [13-14]: Ident [13-14] "a""#]], + Stmt [0-15]: + annotations: + kind: ExprStmt [0-15]: + expr: Expr [0-14]: BinaryOpExpr: + op: Add + lhs: Expr [0-10]: FunctionCall [0-10]: + name: Ident [0-4] "Name" + args: + Expr [5-6]: Lit: Int(2) + Expr [8-9]: Lit: Int(3) + rhs: Expr [13-14]: Ident [13-14] "a""#]], ); } @@ -52,10 +62,14 @@ fn function_call() { parse, "Name(2, 3);", &expect![[r#" - Stmt [0-11] - StmtKind: ExprStmt [0-11]: Expr [0-10]: FunctionCall [0-10]: Ident [0-4] "Name" - Expr [5-6]: Lit: Int(2) - Expr [8-9]: Lit: Int(3)"#]], + Stmt [0-11]: + annotations: + kind: ExprStmt [0-11]: + expr: Expr [0-10]: FunctionCall [0-10]: + name: Ident [0-4] "Name" + args: + Expr [5-6]: Lit: Int(2) + Expr [8-9]: Lit: Int(3)"#]], ); } @@ -65,11 +79,18 @@ fn indexed_function_call() { parse, "Name(2, 3)[1];", &expect![[r#" - Stmt [0-14] - StmtKind: ExprStmt [0-14]: Expr [0-13]: IndexExpr [10-13]: Expr [0-10]: FunctionCall [0-10]: Ident [0-4] "Name" - Expr [5-6]: Lit: Int(2) - Expr [8-9]: Lit: Int(3), IndexElement: - IndexSetItem Expr [11-12]: Lit: Int(1)"#]], + Stmt [0-14]: + annotations: + kind: ExprStmt [0-14]: + expr: Expr [0-13]: IndexExpr [10-13]: + collection: Expr [0-10]: FunctionCall [0-10]: + name: Ident [0-4] "Name" + args: + Expr [5-6]: Lit: Int(2) + Expr [8-9]: Lit: Int(3) + index: IndexSet [11-12]: + values: + Expr [11-12]: Lit: Int(1)"#]], ); } @@ -79,12 +100,19 @@ fn multi_indexed_function_call() { parse, "Name(2, 3)[1, 0];", &expect![[r#" - Stmt [0-17] - StmtKind: ExprStmt [0-17]: Expr [0-16]: IndexExpr [10-16]: Expr [0-10]: FunctionCall [0-10]: Ident [0-4] "Name" - Expr [5-6]: Lit: Int(2) - Expr [8-9]: Lit: Int(3), IndexElement: - IndexSetItem Expr [11-12]: Lit: Int(1) - IndexSetItem Expr [14-15]: Lit: Int(0)"#]], + Stmt [0-17]: + annotations: + kind: ExprStmt [0-17]: + expr: Expr [0-16]: IndexExpr [10-16]: + collection: Expr [0-10]: FunctionCall [0-10]: + name: Ident [0-4] "Name" + args: + Expr [5-6]: Lit: Int(2) + Expr [8-9]: Lit: Int(3) + index: IndexSet [11-15]: + values: + Expr [11-12]: Lit: Int(1) + Expr [14-15]: Lit: Int(0)"#]], ); } @@ -94,8 +122,10 @@ fn ident() { parse, "Name;", &expect![[r#" - Stmt [0-5] - StmtKind: ExprStmt [0-5]: Expr [0-4]: Ident [0-4] "Name""#]], + Stmt [0-5]: + annotations: + kind: ExprStmt [0-5]: + expr: Expr [0-4]: Ident [0-4] "Name""#]], ); } @@ -105,9 +135,14 @@ fn index_expr() { parse, "Name[1];", &expect![[r#" - Stmt [0-8] - StmtKind: ExprStmt [0-8]: Expr [0-7]: IndexExpr [4-7]: Expr [0-4]: Ident [0-4] "Name", IndexElement: - IndexSetItem Expr [5-6]: Lit: Int(1)"#]], + Stmt [0-8]: + annotations: + kind: ExprStmt [0-8]: + expr: Expr [0-7]: IndexExpr [4-7]: + collection: Expr [0-4]: Ident [0-4] "Name" + index: IndexSet [5-6]: + values: + Expr [5-6]: Lit: Int(1)"#]], ); } @@ -117,10 +152,13 @@ fn cast_expr() { parse, "bit(0);", &expect![[r#" - Stmt [0-7] - StmtKind: ExprStmt [0-7]: Expr [0-6]: Cast [0-6]: - ClassicalType [0-3]: BitType - Expr [4-5]: Lit: Int(0)"#]], + Stmt [0-7]: + annotations: + kind: ExprStmt [0-7]: + expr: Expr [0-6]: Cast [0-6]: + type: ScalarType [0-3]: BitType [0-3]: + size: + arg: Expr [4-5]: Lit: Int(0)"#]], ); } @@ -130,9 +168,12 @@ fn cast_expr_with_designator() { parse, "bit[45](0);", &expect![[r#" - Stmt [0-11] - StmtKind: ExprStmt [0-11]: Expr [0-10]: Cast [0-10]: - ClassicalType [0-7]: BitType [0-7]: Expr [4-6]: Lit: Int(45) - Expr [8-9]: Lit: Int(0)"#]], + Stmt [0-11]: + annotations: + kind: ExprStmt [0-11]: + expr: Expr [0-10]: Cast [0-10]: + type: ScalarType [0-7]: BitType [0-7]: + size: Expr [4-6]: Lit: Int(45) + arg: Expr [8-9]: Lit: Int(0)"#]], ); } diff --git a/compiler/qsc_qasm3/src/parser/stmt/tests/extern_decl.rs b/compiler/qsc_qasm3/src/parser/stmt/tests/extern_decl.rs index 2771da1428..6022cf83f1 100644 --- a/compiler/qsc_qasm3/src/parser/stmt/tests/extern_decl.rs +++ b/compiler/qsc_qasm3/src/parser/stmt/tests/extern_decl.rs @@ -13,8 +13,12 @@ fn missing_semicolon_err() { parse, "extern x()", &expect![[r#" - Stmt [0-10] - StmtKind: ExternDecl [0-10]: Ident [7-8] "x" + Stmt [0-10]: + annotations: + kind: ExternDecl [0-10]: + ident: Ident [7-8] "x" + parameters: + return_type: [ Error( @@ -37,10 +41,15 @@ fn bit_param_bit_ret_decl() { parse, "extern x(bit) -> bit;", &expect![[r#" - Stmt [0-21] - StmtKind: ExternDecl [0-21]: Ident [7-8] "x" - [9-12]: ClassicalType [9-12]: BitType - ClassicalType [17-20]: BitType"#]], + Stmt [0-21]: + annotations: + kind: ExternDecl [0-21]: + ident: Ident [7-8] "x" + parameters: + [9-12]: ScalarType [9-12]: BitType [9-12]: + size: + return_type: ScalarType [17-20]: BitType [17-20]: + size: "#]], ); } @@ -50,10 +59,15 @@ fn sized_bit_param_bit_ret_decl() { parse, "extern x(bit[n]) -> bit;", &expect![[r#" - Stmt [0-24] - StmtKind: ExternDecl [0-24]: Ident [7-8] "x" - [9-15]: ClassicalType [9-15]: BitType [9-15]: Expr [13-14]: Ident [13-14] "n" - ClassicalType [20-23]: BitType"#]], + Stmt [0-24]: + annotations: + kind: ExternDecl [0-24]: + ident: Ident [7-8] "x" + parameters: + [9-15]: ScalarType [9-15]: BitType [9-15]: + size: Expr [13-14]: Ident [13-14] "n" + return_type: ScalarType [20-23]: BitType [20-23]: + size: "#]], ); } @@ -63,10 +77,15 @@ fn sized_creg_param_bit_ret_decl() { parse, "extern x(creg[n]) -> bit;", &expect![[r#" - Stmt [0-25] - StmtKind: ExternDecl [0-25]: Ident [7-8] "x" - [9-16]: ClassicalType [9-16]: BitType [9-16]: Expr [14-15]: Ident [14-15] "n" - ClassicalType [21-24]: BitType"#]], + Stmt [0-25]: + annotations: + kind: ExternDecl [0-25]: + ident: Ident [7-8] "x" + parameters: + [9-16]: ScalarType [9-16]: BitType [9-16]: + size: Expr [14-15]: Ident [14-15] "n" + return_type: ScalarType [21-24]: BitType [21-24]: + size: "#]], ); } @@ -76,10 +95,15 @@ fn creg_param_bit_ret_decl() { parse, "extern x(creg) -> bit;", &expect![[r#" - Stmt [0-22] - StmtKind: ExternDecl [0-22]: Ident [7-8] "x" - [9-13]: ClassicalType [9-13]: BitType - ClassicalType [18-21]: BitType"#]], + Stmt [0-22]: + annotations: + kind: ExternDecl [0-22]: + ident: Ident [7-8] "x" + parameters: + [9-13]: ScalarType [9-13]: BitType [9-13]: + size: + return_type: ScalarType [18-21]: BitType [18-21]: + size: "#]], ); } @@ -89,11 +113,20 @@ fn readonly_array_arg_with_int_dims() { parse, "extern x(readonly array[int[8], 2, 10]);", &expect![[r#" - Stmt [0-40] - StmtKind: ExternDecl [0-40]: Ident [7-8] "x" - [9-38]: ArrayReferenceType [9-38]: ArrayBaseTypeKind IntType[Expr [28-29]: Lit: Int(8)]: [24-30] - Expr [32-33]: Lit: Int(2) - Expr [35-37]: Lit: Int(10)"#]], + Stmt [0-40]: + annotations: + kind: ExternDecl [0-40]: + ident: Ident [7-8] "x" + parameters: + [9-38]: ArrayReferenceType [9-38]: + mutability: ReadOnly + base_type: ArrayBaseTypeKind IntType [24-30]: + size: Expr [28-29]: Lit: Int(8) + dimensions: + Expr [32-33]: Lit: Int(2) + Expr [35-37]: Lit: Int(10) + + return_type: "#]], ); } @@ -103,10 +136,19 @@ fn readonly_array_arg_with_dim() { parse, "extern x(readonly array[int[8], #dim = 1]);", &expect![[r#" - Stmt [0-43] - StmtKind: ExternDecl [0-43]: Ident [7-8] "x" - [9-41]: ArrayReferenceType [9-41]: ArrayBaseTypeKind IntType[Expr [28-29]: Lit: Int(8)]: [24-30] - Expr [39-40]: Lit: Int(1)"#]], + Stmt [0-43]: + annotations: + kind: ExternDecl [0-43]: + ident: Ident [7-8] "x" + parameters: + [9-41]: ArrayReferenceType [9-41]: + mutability: ReadOnly + base_type: ArrayBaseTypeKind IntType [24-30]: + size: Expr [28-29]: Lit: Int(8) + dimensions: + Expr [39-40]: Lit: Int(1) + + return_type: "#]], ); } @@ -116,10 +158,19 @@ fn mutable_array_arg() { parse, "extern x(mutable array[int[8], #dim = 1]);", &expect![[r#" - Stmt [0-42] - StmtKind: ExternDecl [0-42]: Ident [7-8] "x" - [9-40]: ArrayReferenceType [9-40]: ArrayBaseTypeKind IntType[Expr [27-28]: Lit: Int(8)]: [23-29] - Expr [38-39]: Lit: Int(1)"#]], + Stmt [0-42]: + annotations: + kind: ExternDecl [0-42]: + ident: Ident [7-8] "x" + parameters: + [9-40]: ArrayReferenceType [9-40]: + mutability: Mutable + base_type: ArrayBaseTypeKind IntType [23-29]: + size: Expr [27-28]: Lit: Int(8) + dimensions: + Expr [38-39]: Lit: Int(1) + + return_type: "#]], ); } @@ -152,11 +203,18 @@ fn annotation() { r#"@test.annotation extern x(creg) -> bit;"#, &expect![[r#" - Stmt [0-47] - Annotation [0-16]: (test.annotation) - StmtKind: ExternDecl [25-47]: Ident [32-33] "x" - [34-38]: ClassicalType [34-38]: BitType - ClassicalType [43-46]: BitType"#]], + Stmt [0-47]: + annotations: + Annotation [0-16]: + identifier: "test.annotation" + value: + kind: ExternDecl [25-47]: + ident: Ident [32-33] "x" + parameters: + [34-38]: ScalarType [34-38]: BitType [34-38]: + size: + return_type: ScalarType [43-46]: BitType [43-46]: + size: "#]], ); } @@ -186,9 +244,13 @@ fn missing_args_with_delim_error() { parse, "extern x(,);", &expect![[r#" - Stmt [0-12] - StmtKind: ExternDecl [0-12]: Ident [7-8] "x" - [9-9]: ClassicalType [0-0]: Err + Stmt [0-12]: + annotations: + kind: ExternDecl [0-12]: + ident: Ident [7-8] "x" + parameters: + [9-9]: ScalarType [0-0]: Err + return_type: [ Error( @@ -209,11 +271,17 @@ fn args_with_extra_delim_err_ty() { parse, "extern x(int,,int);", &expect![[r#" - Stmt [0-19] - StmtKind: ExternDecl [0-19]: Ident [7-8] "x" - [9-12]: ClassicalType [9-12]: IntType [9-12] - [13-13]: ClassicalType [0-0]: Err - [14-17]: ClassicalType [14-17]: IntType [14-17] + Stmt [0-19]: + annotations: + kind: ExternDecl [0-19]: + ident: Ident [7-8] "x" + parameters: + [9-12]: ScalarType [9-12]: IntType [9-12]: + size: + [13-13]: ScalarType [0-0]: Err + [14-17]: ScalarType [14-17]: IntType [14-17]: + size: + return_type: [ Error( diff --git a/compiler/qsc_qasm3/src/parser/stmt/tests/for_loops.rs b/compiler/qsc_qasm3/src/parser/stmt/tests/for_loops.rs index 8ce396ef6b..8d6bf920b7 100644 --- a/compiler/qsc_qasm3/src/parser/stmt/tests/for_loops.rs +++ b/compiler/qsc_qasm3/src/parser/stmt/tests/for_loops.rs @@ -13,15 +13,42 @@ fn simple_for_loop() { a = 0; }", &expect![[r#" - Stmt [5-50] - StmtKind: ForStmt [5-50]: ClassicalType [9-12]: IntType [9-12], Ident [13-14] "x", DiscreteSet [18-27]: - Expr [19-20]: Lit: Int(1) - Expr [22-23]: Lit: Int(2) - Expr [25-26]: Lit: Int(3) - Stmt [38-44] - StmtKind: ExprStmt [38-44]: Expr [38-43]: Assign: - Expr [38-39]: Ident [38-39] "a" - Expr [42-43]: Lit: Int(0)"#]], + Stmt [5-50]: + annotations: + kind: ForStmt [5-50]: + variable_type: ScalarType [9-12]: IntType [9-12]: + size: + variable_name: Ident [13-14] "x" + iterable: DiscreteSet [18-27]: + values: + Expr [19-20]: Lit: Int(1) + Expr [22-23]: Lit: Int(2) + Expr [25-26]: Lit: Int(3) + block: + Stmt [38-44]: + annotations: + kind: ExprStmt [38-44]: + expr: Expr [38-43]: AssignExpr: + lhs: Expr [38-39]: Ident [38-39] "a" + rhs: Expr [42-43]: Lit: Int(0)"#]], + ); +} + +#[test] +fn empty_for_loop() { + check( + parse, + "for int x in {} {}", + &expect![[r#" + Stmt [0-18]: + annotations: + kind: ForStmt [0-18]: + variable_type: ScalarType [4-7]: IntType [4-7]: + size: + variable_name: Ident [8-9] "x" + iterable: DiscreteSet [13-15]: + values: + block: "#]], ); } @@ -34,15 +61,24 @@ fn simple_for_loop_stmt_body() { a = 0; ", &expect![[r#" - Stmt [5-42] - StmtKind: ForStmt [5-42]: ClassicalType [9-12]: IntType [9-12], Ident [13-14] "x", DiscreteSet [18-27]: - Expr [19-20]: Lit: Int(1) - Expr [22-23]: Lit: Int(2) - Expr [25-26]: Lit: Int(3) - Stmt [36-42] - StmtKind: ExprStmt [36-42]: Expr [36-41]: Assign: - Expr [36-37]: Ident [36-37] "a" - Expr [40-41]: Lit: Int(0)"#]], + Stmt [5-42]: + annotations: + kind: ForStmt [5-42]: + variable_type: ScalarType [9-12]: IntType [9-12]: + size: + variable_name: Ident [13-14] "x" + iterable: DiscreteSet [18-27]: + values: + Expr [19-20]: Lit: Int(1) + Expr [22-23]: Lit: Int(2) + Expr [25-26]: Lit: Int(3) + block: + Stmt [36-42]: + annotations: + kind: ExprStmt [36-42]: + expr: Expr [36-41]: AssignExpr: + lhs: Expr [36-37]: Ident [36-37] "a" + rhs: Expr [40-41]: Lit: Int(0)"#]], ); } @@ -55,15 +91,23 @@ fn for_loop_range() { a = 0; }", &expect![[r#" - Stmt [5-48] - StmtKind: ForStmt [5-48]: ClassicalType [9-12]: IntType [9-12], Ident [13-14] "x", Range: [18-25] - start: Expr [19-20]: Lit: Int(0) - step: Expr [21-22]: Lit: Int(2) - end: Expr [23-24]: Lit: Int(7) - Stmt [36-42] - StmtKind: ExprStmt [36-42]: Expr [36-41]: Assign: - Expr [36-37]: Ident [36-37] "a" - Expr [40-41]: Lit: Int(0)"#]], + Stmt [5-48]: + annotations: + kind: ForStmt [5-48]: + variable_type: ScalarType [9-12]: IntType [9-12]: + size: + variable_name: Ident [13-14] "x" + iterable: RangeDefinition [18-25]: + start: Expr [19-20]: Lit: Int(0) + step: Expr [21-22]: Lit: Int(2) + end: Expr [23-24]: Lit: Int(7) + block: + Stmt [36-42]: + annotations: + kind: ExprStmt [36-42]: + expr: Expr [36-41]: AssignExpr: + lhs: Expr [36-37]: Ident [36-37] "a" + rhs: Expr [40-41]: Lit: Int(0)"#]], ); } @@ -76,15 +120,23 @@ fn for_loop_range_no_step() { a = 0; }", &expect![[r#" - Stmt [5-46] - StmtKind: ForStmt [5-46]: ClassicalType [9-12]: IntType [9-12], Ident [13-14] "x", Range: [18-23] - start: Expr [19-20]: Lit: Int(0) - - end: Expr [21-22]: Lit: Int(7) - Stmt [34-40] - StmtKind: ExprStmt [34-40]: Expr [34-39]: Assign: - Expr [34-35]: Ident [34-35] "a" - Expr [38-39]: Lit: Int(0)"#]], + Stmt [5-46]: + annotations: + kind: ForStmt [5-46]: + variable_type: ScalarType [9-12]: IntType [9-12]: + size: + variable_name: Ident [13-14] "x" + iterable: RangeDefinition [18-23]: + start: Expr [19-20]: Lit: Int(0) + step: + end: Expr [21-22]: Lit: Int(7) + block: + Stmt [34-40]: + annotations: + kind: ExprStmt [34-40]: + expr: Expr [34-39]: AssignExpr: + lhs: Expr [34-35]: Ident [34-35] "a" + rhs: Expr [38-39]: Lit: Int(0)"#]], ); } @@ -97,12 +149,20 @@ fn for_loop_expr() { a = 0; }", &expect![[r#" - Stmt [5-43] - StmtKind: ForStmt [5-43]: ClassicalType [9-12]: IntType [9-12], Ident [13-14] "x", Expr [18-20]: Ident [18-20] "xs" - Stmt [31-37] - StmtKind: ExprStmt [31-37]: Expr [31-36]: Assign: - Expr [31-32]: Ident [31-32] "a" - Expr [35-36]: Lit: Int(0)"#]], + Stmt [5-43]: + annotations: + kind: ForStmt [5-43]: + variable_type: ScalarType [9-12]: IntType [9-12]: + size: + variable_name: Ident [13-14] "x" + iterable: Expr [18-20]: Ident [18-20] "xs" + block: + Stmt [31-37]: + annotations: + kind: ExprStmt [31-37]: + expr: Expr [31-36]: AssignExpr: + lhs: Expr [31-32]: Ident [31-32] "a" + rhs: Expr [35-36]: Lit: Int(0)"#]], ); } @@ -116,17 +176,27 @@ fn for_loop_with_continue_stmt() { continue; }", &expect![[r#" - Stmt [5-68] - StmtKind: ForStmt [5-68]: ClassicalType [9-12]: IntType [9-12], Ident [13-14] "x", DiscreteSet [18-27]: - Expr [19-20]: Lit: Int(1) - Expr [22-23]: Lit: Int(2) - Expr [25-26]: Lit: Int(3) - Stmt [38-44] - StmtKind: ExprStmt [38-44]: Expr [38-43]: Assign: - Expr [38-39]: Ident [38-39] "a" - Expr [42-43]: Lit: Int(0) - Stmt [53-62] - StmtKind: Continue [53-62]"#]], + Stmt [5-68]: + annotations: + kind: ForStmt [5-68]: + variable_type: ScalarType [9-12]: IntType [9-12]: + size: + variable_name: Ident [13-14] "x" + iterable: DiscreteSet [18-27]: + values: + Expr [19-20]: Lit: Int(1) + Expr [22-23]: Lit: Int(2) + Expr [25-26]: Lit: Int(3) + block: + Stmt [38-44]: + annotations: + kind: ExprStmt [38-44]: + expr: Expr [38-43]: AssignExpr: + lhs: Expr [38-39]: Ident [38-39] "a" + rhs: Expr [42-43]: Lit: Int(0) + Stmt [53-62]: + annotations: + kind: ContinueStmt [53-62]"#]], ); } @@ -140,16 +210,26 @@ fn for_loop_with_break_stmt() { break; }", &expect![[r#" - Stmt [5-65] - StmtKind: ForStmt [5-65]: ClassicalType [9-12]: IntType [9-12], Ident [13-14] "x", DiscreteSet [18-27]: - Expr [19-20]: Lit: Int(1) - Expr [22-23]: Lit: Int(2) - Expr [25-26]: Lit: Int(3) - Stmt [38-44] - StmtKind: ExprStmt [38-44]: Expr [38-43]: Assign: - Expr [38-39]: Ident [38-39] "a" - Expr [42-43]: Lit: Int(0) - Stmt [53-59] - StmtKind: Break [53-59]"#]], + Stmt [5-65]: + annotations: + kind: ForStmt [5-65]: + variable_type: ScalarType [9-12]: IntType [9-12]: + size: + variable_name: Ident [13-14] "x" + iterable: DiscreteSet [18-27]: + values: + Expr [19-20]: Lit: Int(1) + Expr [22-23]: Lit: Int(2) + Expr [25-26]: Lit: Int(3) + block: + Stmt [38-44]: + annotations: + kind: ExprStmt [38-44]: + expr: Expr [38-43]: AssignExpr: + lhs: Expr [38-39]: Ident [38-39] "a" + rhs: Expr [42-43]: Lit: Int(0) + Stmt [53-59]: + annotations: + kind: BreakStmt [53-59]"#]], ); } diff --git a/compiler/qsc_qasm3/src/parser/stmt/tests/gate_call.rs b/compiler/qsc_qasm3/src/parser/stmt/tests/gate_call.rs index 98af770cd0..03e4a43334 100644 --- a/compiler/qsc_qasm3/src/parser/stmt/tests/gate_call.rs +++ b/compiler/qsc_qasm3/src/parser/stmt/tests/gate_call.rs @@ -11,9 +11,17 @@ fn gate_call() { parse, "H q0;", &expect![[r#" - Stmt [0-5] - StmtKind: GateCall [0-5]: Ident [0-1] "H" - GateOperand IndexedIdent [2-4]: Ident [2-4] "q0"[]"#]], + Stmt [0-5]: + annotations: + kind: GateCall [0-5]: + modifiers: + name: Ident [0-1] "H" + args: + duration: + qubits: + IndexedIdent [2-4]: + name: Ident [2-4] "q0" + indices: "#]], ); } @@ -23,11 +31,20 @@ fn gate_call_qubit_register() { parse, "H q[2];", &expect![[r#" - Stmt [0-7] - StmtKind: GateCall [0-7]: Ident [0-1] "H" - GateOperand IndexedIdent [2-6]: Ident [2-3] "q"[ - IndexElement: - IndexSetItem Expr [4-5]: Lit: Int(2)]"#]], + Stmt [0-7]: + annotations: + kind: GateCall [0-7]: + modifiers: + name: Ident [0-1] "H" + args: + duration: + qubits: + IndexedIdent [2-6]: + name: Ident [2-3] "q" + indices: + IndexSet [4-5]: + values: + Expr [4-5]: Lit: Int(2)"#]], ); } @@ -37,12 +54,23 @@ fn gate_multiple_qubits() { parse, "CNOT q0, q[4];", &expect![[r#" - Stmt [0-14] - StmtKind: GateCall [0-14]: Ident [0-4] "CNOT" - GateOperand IndexedIdent [5-7]: Ident [5-7] "q0"[] - GateOperand IndexedIdent [9-13]: Ident [9-10] "q"[ - IndexElement: - IndexSetItem Expr [11-12]: Lit: Int(4)]"#]], + Stmt [0-14]: + annotations: + kind: GateCall [0-14]: + modifiers: + name: Ident [0-4] "CNOT" + args: + duration: + qubits: + IndexedIdent [5-7]: + name: Ident [5-7] "q0" + indices: + IndexedIdent [9-13]: + name: Ident [9-10] "q" + indices: + IndexSet [11-12]: + values: + Expr [11-12]: Lit: Int(4)"#]], ); } @@ -52,8 +80,15 @@ fn gate_with_no_qubits() { parse, "inv @ H;", &expect![[r#" - Stmt [0-8] - StmtKind: GateCall [0-8]: Ident [6-7] "H" + Stmt [0-8]: + annotations: + kind: GateCall [0-8]: + modifiers: + QuantumGateModifier [0-5]: Inv + name: Ident [6-7] "H" + args: + duration: + qubits: [ Error( @@ -74,12 +109,21 @@ fn gate_call_with_parameters() { parse, "Rx(pi / 2) q0;", &expect![[r#" - Stmt [0-14] - StmtKind: GateCall [0-14]: Ident [0-2] "Rx" - Expr [3-9]: BinOp (Div): - Expr [3-5]: Ident [3-5] "pi" - Expr [8-9]: Lit: Int(2) - GateOperand IndexedIdent [11-13]: Ident [11-13] "q0"[]"#]], + Stmt [0-14]: + annotations: + kind: GateCall [0-14]: + modifiers: + name: Ident [0-2] "Rx" + args: + Expr [3-9]: BinaryOpExpr: + op: Div + lhs: Expr [3-5]: Ident [3-5] "pi" + rhs: Expr [8-9]: Lit: Int(2) + duration: + qubits: + IndexedIdent [11-13]: + name: Ident [11-13] "q0" + indices: "#]], ); } @@ -89,9 +133,18 @@ fn gate_call_inv_modifier() { parse, "inv @ H q0;", &expect![[r#" - Stmt [0-11] - StmtKind: GateCall [0-11]: Ident [6-7] "H" - GateOperand IndexedIdent [8-10]: Ident [8-10] "q0"[]"#]], + Stmt [0-11]: + annotations: + kind: GateCall [0-11]: + modifiers: + QuantumGateModifier [0-5]: Inv + name: Ident [6-7] "H" + args: + duration: + qubits: + IndexedIdent [8-10]: + name: Ident [8-10] "q0" + indices: "#]], ); } @@ -101,14 +154,29 @@ fn gate_call_ctrl_inv_modifiers() { parse, "ctrl(2) @ inv @ Rx(pi / 2) c1, c2, q0;", &expect![[r#" - Stmt [0-38] - StmtKind: GateCall [0-38]: Ident [16-18] "Rx" - Expr [19-25]: BinOp (Div): - Expr [19-21]: Ident [19-21] "pi" - Expr [24-25]: Lit: Int(2) - GateOperand IndexedIdent [27-29]: Ident [27-29] "c1"[] - GateOperand IndexedIdent [31-33]: Ident [31-33] "c2"[] - GateOperand IndexedIdent [35-37]: Ident [35-37] "q0"[]"#]], + Stmt [0-38]: + annotations: + kind: GateCall [0-38]: + modifiers: + QuantumGateModifier [0-9]: Ctrl Some(Expr { span: Span { lo: 5, hi: 6 }, kind: Lit(Lit { span: Span { lo: 5, hi: 6 }, kind: Int(2) }) }) + QuantumGateModifier [10-15]: Inv + name: Ident [16-18] "Rx" + args: + Expr [19-25]: BinaryOpExpr: + op: Div + lhs: Expr [19-21]: Ident [19-21] "pi" + rhs: Expr [24-25]: Lit: Int(2) + duration: + qubits: + IndexedIdent [27-29]: + name: Ident [27-29] "c1" + indices: + IndexedIdent [31-33]: + name: Ident [31-33] "c2" + indices: + IndexedIdent [35-37]: + name: Ident [35-37] "q0" + indices: "#]], ); } @@ -137,11 +205,19 @@ fn parametrized_gate_call() { parse, "Name(2, 3) q;", &expect![[r#" - Stmt [0-13] - StmtKind: GateCall [0-13]: Ident [0-4] "Name" - Expr [5-6]: Lit: Int(2) - Expr [8-9]: Lit: Int(3) - GateOperand IndexedIdent [11-12]: Ident [11-12] "q"[]"#]], + Stmt [0-13]: + annotations: + kind: GateCall [0-13]: + modifiers: + name: Ident [0-4] "Name" + args: + Expr [5-6]: Lit: Int(2) + Expr [8-9]: Lit: Int(3) + duration: + qubits: + IndexedIdent [11-12]: + name: Ident [11-12] "q" + indices: "#]], ); } @@ -151,12 +227,19 @@ fn parametrized_gate_call_with_designator() { parse, "Name(2, 3)[1] q;", &expect![[r#" - Stmt [0-16] - StmtKind: GateCall [0-16]: Ident [0-4] "Name" - Expr [5-6]: Lit: Int(2) - Expr [8-9]: Lit: Int(3) - GateOperand IndexedIdent [14-15]: Ident [14-15] "q"[] - Expr [11-12]: Lit: Int(1)"#]], + Stmt [0-16]: + annotations: + kind: GateCall [0-16]: + modifiers: + name: Ident [0-4] "Name" + args: + Expr [5-6]: Lit: Int(2) + Expr [8-9]: Lit: Int(3) + duration: Expr [11-12]: Lit: Int(1) + qubits: + IndexedIdent [14-15]: + name: Ident [14-15] "q" + indices: "#]], ); } @@ -184,10 +267,17 @@ fn gate_call_with_designator() { parse, "H[2us] q;", &expect![[r#" - Stmt [0-9] - StmtKind: GateCall [0-9]: Ident [0-1] "H" - GateOperand IndexedIdent [7-8]: Ident [7-8] "q"[] - Expr [2-5]: Lit: Duration(2.0, Us)"#]], + Stmt [0-9]: + annotations: + kind: GateCall [0-9]: + modifiers: + name: Ident [0-1] "H" + args: + duration: Expr [2-5]: Lit: Duration(2.0, Us) + qubits: + IndexedIdent [7-8]: + name: Ident [7-8] "q" + indices: "#]], ); } diff --git a/compiler/qsc_qasm3/src/parser/stmt/tests/gate_def.rs b/compiler/qsc_qasm3/src/parser/stmt/tests/gate_def.rs index a45c07effa..d54e31d86a 100644 --- a/compiler/qsc_qasm3/src/parser/stmt/tests/gate_def.rs +++ b/compiler/qsc_qasm3/src/parser/stmt/tests/gate_def.rs @@ -13,9 +13,13 @@ fn no_qubits_no_classical() { parse, "gate c0q0 {}", &expect![[r#" - Stmt [0-12] - StmtKind: Gate [0-12]: Ident [5-9] "c0q0"() - "#]], + Stmt [0-12]: + annotations: + kind: Gate [0-12]: + ident: Ident [5-9] "c0q0" + parameters: + qubits: + body: Block [10-12]: "#]], ); } @@ -25,9 +29,13 @@ fn no_qubits_no_classical_with_parens() { parse, "gate c0q0() {}", &expect![[r#" - Stmt [0-14] - StmtKind: Gate [0-14]: Ident [5-9] "c0q0"() - "#]], + Stmt [0-14]: + annotations: + kind: Gate [0-14]: + ident: Ident [5-9] "c0q0" + parameters: + qubits: + body: Block [12-14]: "#]], ); } @@ -37,9 +45,14 @@ fn one_qubit_no_classical() { parse, "gate c0q1 a {}", &expect![[r#" - Stmt [0-14] - StmtKind: Gate [0-14]: Ident [5-9] "c0q1"() Ident [10-11] "a" - "#]], + Stmt [0-14]: + annotations: + kind: Gate [0-14]: + ident: Ident [5-9] "c0q1" + parameters: + qubits: + Ident [10-11] "a" + body: Block [12-14]: "#]], ); } @@ -49,9 +62,15 @@ fn two_qubits_no_classical() { parse, "gate c0q2 a, b {}", &expect![[r#" - Stmt [0-17] - StmtKind: Gate [0-17]: Ident [5-9] "c0q2"() Ident [10-11] "a", Ident [13-14] "b" - "#]], + Stmt [0-17]: + annotations: + kind: Gate [0-17]: + ident: Ident [5-9] "c0q2" + parameters: + qubits: + Ident [10-11] "a" + Ident [13-14] "b" + body: Block [15-17]: "#]], ); } @@ -61,9 +80,16 @@ fn three_qubits_trailing_comma_no_classical() { parse, "gate c0q3 a, b, c, {}", &expect![[r#" - Stmt [0-21] - StmtKind: Gate [0-21]: Ident [5-9] "c0q3"() Ident [10-11] "a", Ident [13-14] "b", Ident [16-17] "c" - "#]], + Stmt [0-21]: + annotations: + kind: Gate [0-21]: + ident: Ident [5-9] "c0q3" + parameters: + qubits: + Ident [10-11] "a" + Ident [13-14] "b" + Ident [16-17] "c" + body: Block [19-21]: "#]], ); } @@ -73,9 +99,14 @@ fn no_qubits_one_classical() { parse, "gate c1q0(a) {}", &expect![[r#" - Stmt [0-15] - StmtKind: Gate [0-15]: Ident [5-9] "c1q0"(Ident [10-11] "a") - "#]], + Stmt [0-15]: + annotations: + kind: Gate [0-15]: + ident: Ident [5-9] "c1q0" + parameters: + Ident [10-11] "a" + qubits: + body: Block [13-15]: "#]], ); } @@ -85,9 +116,15 @@ fn no_qubits_two_classical() { parse, "gate c2q0(a, b) {}", &expect![[r#" - Stmt [0-18] - StmtKind: Gate [0-18]: Ident [5-9] "c2q0"(Ident [10-11] "a", Ident [13-14] "b") - "#]], + Stmt [0-18]: + annotations: + kind: Gate [0-18]: + ident: Ident [5-9] "c2q0" + parameters: + Ident [10-11] "a" + Ident [13-14] "b" + qubits: + body: Block [16-18]: "#]], ); } @@ -97,9 +134,16 @@ fn no_qubits_three_classical() { parse, "gate c3q0(a, b, c) {}", &expect![[r#" - Stmt [0-21] - StmtKind: Gate [0-21]: Ident [5-9] "c3q0"(Ident [10-11] "a", Ident [13-14] "b", Ident [16-17] "c") - "#]], + Stmt [0-21]: + annotations: + kind: Gate [0-21]: + ident: Ident [5-9] "c3q0" + parameters: + Ident [10-11] "a" + Ident [13-14] "b" + Ident [16-17] "c" + qubits: + body: Block [19-21]: "#]], ); } @@ -109,9 +153,15 @@ fn one_qubit_one_classical() { parse, "gate c1q1(a) b {}", &expect![[r#" - Stmt [0-17] - StmtKind: Gate [0-17]: Ident [5-9] "c1q1"(Ident [10-11] "a") Ident [13-14] "b" - "#]], + Stmt [0-17]: + annotations: + kind: Gate [0-17]: + ident: Ident [5-9] "c1q1" + parameters: + Ident [10-11] "a" + qubits: + Ident [13-14] "b" + body: Block [15-17]: "#]], ); } @@ -121,9 +171,17 @@ fn two_qubits_two_classical() { parse, "gate c2q2(a, b) c, d {}", &expect![[r#" - Stmt [0-23] - StmtKind: Gate [0-23]: Ident [5-9] "c2q2"(Ident [10-11] "a", Ident [13-14] "b") Ident [16-17] "c", Ident [19-20] "d" - "#]], + Stmt [0-23]: + annotations: + kind: Gate [0-23]: + ident: Ident [5-9] "c2q2" + parameters: + Ident [10-11] "a" + Ident [13-14] "b" + qubits: + Ident [16-17] "c" + Ident [19-20] "d" + body: Block [21-23]: "#]], ); } @@ -133,12 +191,26 @@ fn two_qubits_two_classical_with_body() { parse, "gate c2q2(a, b) c, d { float[32] x = a - b; }", &expect![[r#" - Stmt [0-45] - StmtKind: Gate [0-45]: Ident [5-9] "c2q2"(Ident [10-11] "a", Ident [13-14] "b") Ident [16-17] "c", Ident [19-20] "d" - - Stmt [23-43] - StmtKind: ClassicalDeclarationStmt [23-43]: ClassicalType [23-32]: FloatType[Expr [29-31]: Lit: Int(32)]: [23-32], Ident [33-34] "x", ValueExpression Expr [37-42]: BinOp (Sub): - Expr [37-38]: Ident [37-38] "a" - Expr [41-42]: Ident [41-42] "b""#]], + Stmt [0-45]: + annotations: + kind: Gate [0-45]: + ident: Ident [5-9] "c2q2" + parameters: + Ident [10-11] "a" + Ident [13-14] "b" + qubits: + Ident [16-17] "c" + Ident [19-20] "d" + body: Block [21-45]: + Stmt [23-43]: + annotations: + kind: ClassicalDeclarationStmt [23-43]: + type: ScalarType [23-32]: FloatType [23-32]: + size: Expr [29-31]: Lit: Int(32) + ident: Ident [33-34] "x" + init_expr: Expr [37-42]: BinaryOpExpr: + op: Sub + lhs: Expr [37-38]: Ident [37-38] "a" + rhs: Expr [41-42]: Ident [41-42] "b""#]], ); } diff --git a/compiler/qsc_qasm3/src/parser/stmt/tests/gphase.rs b/compiler/qsc_qasm3/src/parser/stmt/tests/gphase.rs index 773611b83c..6b3278918d 100644 --- a/compiler/qsc_qasm3/src/parser/stmt/tests/gphase.rs +++ b/compiler/qsc_qasm3/src/parser/stmt/tests/gphase.rs @@ -11,11 +11,17 @@ fn gphase() { parse, "gphase(pi / 2);", &expect![[r#" - Stmt [0-15] - StmtKind: GPhase [0-15]: - Expr [7-13]: BinOp (Div): - Expr [7-9]: Ident [7-9] "pi" - Expr [12-13]: Lit: Int(2)"#]], + Stmt [0-15]: + annotations: + kind: GPhase [0-15]: + modifiers: + args: + Expr [7-13]: BinaryOpExpr: + op: Div + lhs: Expr [7-9]: Ident [7-9] "pi" + rhs: Expr [12-13]: Lit: Int(2) + duration: + qubits: "#]], ); } @@ -25,10 +31,17 @@ fn gphase_qubit_ident() { parse, "gphase(a) q0;", &expect![[r#" - Stmt [0-13] - StmtKind: GPhase [0-13]: - Expr [7-8]: Ident [7-8] "a" - GateOperand IndexedIdent [10-12]: Ident [10-12] "q0"[]"#]], + Stmt [0-13]: + annotations: + kind: GPhase [0-13]: + modifiers: + args: + Expr [7-8]: Ident [7-8] "a" + duration: + qubits: + IndexedIdent [10-12]: + name: Ident [10-12] "q0" + indices: "#]], ); } @@ -38,12 +51,20 @@ fn gphase_qubit_register() { parse, "gphase(a) q[2];", &expect![[r#" - Stmt [0-15] - StmtKind: GPhase [0-15]: - Expr [7-8]: Ident [7-8] "a" - GateOperand IndexedIdent [10-14]: Ident [10-11] "q"[ - IndexElement: - IndexSetItem Expr [12-13]: Lit: Int(2)]"#]], + Stmt [0-15]: + annotations: + kind: GPhase [0-15]: + modifiers: + args: + Expr [7-8]: Ident [7-8] "a" + duration: + qubits: + IndexedIdent [10-14]: + name: Ident [10-11] "q" + indices: + IndexSet [12-13]: + values: + Expr [12-13]: Lit: Int(2)"#]], ); } @@ -53,13 +74,23 @@ fn gphase_multiple_qubits() { parse, "gphase(a) q0, q[4];", &expect![[r#" - Stmt [0-19] - StmtKind: GPhase [0-19]: - Expr [7-8]: Ident [7-8] "a" - GateOperand IndexedIdent [10-12]: Ident [10-12] "q0"[] - GateOperand IndexedIdent [14-18]: Ident [14-15] "q"[ - IndexElement: - IndexSetItem Expr [16-17]: Lit: Int(4)]"#]], + Stmt [0-19]: + annotations: + kind: GPhase [0-19]: + modifiers: + args: + Expr [7-8]: Ident [7-8] "a" + duration: + qubits: + IndexedIdent [10-12]: + name: Ident [10-12] "q0" + indices: + IndexedIdent [14-18]: + name: Ident [14-15] "q" + indices: + IndexSet [16-17]: + values: + Expr [16-17]: Lit: Int(4)"#]], ); } @@ -69,8 +100,13 @@ fn gphase_no_arguments() { parse, "gphase;", &expect![[r#" - Stmt [0-7] - StmtKind: GPhase [0-7]: + Stmt [0-7]: + annotations: + kind: GPhase [0-7]: + modifiers: + args: + duration: + qubits: [ Error( @@ -91,11 +127,17 @@ fn gphase_with_parameters() { parse, "gphase(pi / 2);", &expect![[r#" - Stmt [0-15] - StmtKind: GPhase [0-15]: - Expr [7-13]: BinOp (Div): - Expr [7-9]: Ident [7-9] "pi" - Expr [12-13]: Lit: Int(2)"#]], + Stmt [0-15]: + annotations: + kind: GPhase [0-15]: + modifiers: + args: + Expr [7-13]: BinaryOpExpr: + op: Div + lhs: Expr [7-9]: Ident [7-9] "pi" + rhs: Expr [12-13]: Lit: Int(2) + duration: + qubits: "#]], ); } @@ -105,9 +147,15 @@ fn gphase_inv_modifier() { parse, "inv @ gphase(a);", &expect![[r#" - Stmt [0-16] - StmtKind: GPhase [0-16]: - Expr [13-14]: Ident [13-14] "a""#]], + Stmt [0-16]: + annotations: + kind: GPhase [0-16]: + modifiers: + QuantumGateModifier [0-5]: Inv + args: + Expr [13-14]: Ident [13-14] "a" + duration: + qubits: "#]], ); } @@ -117,11 +165,21 @@ fn gphase_ctrl_inv_modifiers() { parse, "ctrl @ inv @ gphase(pi / 2) q0;", &expect![[r#" - Stmt [0-31] - StmtKind: GPhase [0-31]: - Expr [20-26]: BinOp (Div): - Expr [20-22]: Ident [20-22] "pi" - Expr [25-26]: Lit: Int(2) - GateOperand IndexedIdent [28-30]: Ident [28-30] "q0"[]"#]], + Stmt [0-31]: + annotations: + kind: GPhase [0-31]: + modifiers: + QuantumGateModifier [0-6]: Ctrl None + QuantumGateModifier [7-12]: Inv + args: + Expr [20-26]: BinaryOpExpr: + op: Div + lhs: Expr [20-22]: Ident [20-22] "pi" + rhs: Expr [25-26]: Lit: Int(2) + duration: + qubits: + IndexedIdent [28-30]: + name: Ident [28-30] "q0" + indices: "#]], ); } diff --git a/compiler/qsc_qasm3/src/parser/stmt/tests/if_stmt.rs b/compiler/qsc_qasm3/src/parser/stmt/tests/if_stmt.rs index 4d99ccfda8..29e8f0136d 100644 --- a/compiler/qsc_qasm3/src/parser/stmt/tests/if_stmt.rs +++ b/compiler/qsc_qasm3/src/parser/stmt/tests/if_stmt.rs @@ -16,19 +16,27 @@ fn simple_if_stmt() { } ", &expect![[r#" - Stmt [5-67] - StmtKind: IfStmt [5-67]: Expr [9-15]: BinOp (Eq): - Expr [9-10]: Ident [9-10] "x" - Expr [14-15]: Ident [14-15] "y" - Stmt [27-33] - StmtKind: ExprStmt [27-33]: Expr [27-32]: Assign: - Expr [27-28]: Ident [27-28] "a" - Expr [31-32]: Lit: Int(0) - Else: - Stmt [55-61] - StmtKind: ExprStmt [55-61]: Expr [55-60]: Assign: - Expr [55-56]: Ident [55-56] "a" - Expr [59-60]: Lit: Int(1)"#]], + Stmt [5-67]: + annotations: + kind: IfStmt [5-67]: + condition: Expr [9-15]: BinaryOpExpr: + op: Eq + lhs: Expr [9-10]: Ident [9-10] "x" + rhs: Expr [14-15]: Ident [14-15] "y" + if_block: + Stmt [27-33]: + annotations: + kind: ExprStmt [27-33]: + expr: Expr [27-32]: AssignExpr: + lhs: Expr [27-28]: Ident [27-28] "a" + rhs: Expr [31-32]: Lit: Int(0) + else_block: + Stmt [55-61]: + annotations: + kind: ExprStmt [55-61]: + expr: Expr [55-60]: AssignExpr: + lhs: Expr [55-56]: Ident [55-56] "a" + rhs: Expr [59-60]: Lit: Int(1)"#]], ); } @@ -42,14 +50,21 @@ fn if_stmt_missing_else() { } ", &expect![[r#" - Stmt [5-39] - StmtKind: IfStmt [5-39]: Expr [9-15]: BinOp (Eq): - Expr [9-10]: Ident [9-10] "x" - Expr [14-15]: Ident [14-15] "y" - Stmt [27-33] - StmtKind: ExprStmt [27-33]: Expr [27-32]: Assign: - Expr [27-28]: Ident [27-28] "a" - Expr [31-32]: Lit: Int(0)"#]], + Stmt [5-39]: + annotations: + kind: IfStmt [5-39]: + condition: Expr [9-15]: BinaryOpExpr: + op: Eq + lhs: Expr [9-10]: Ident [9-10] "x" + rhs: Expr [14-15]: Ident [14-15] "y" + if_block: + Stmt [27-33]: + annotations: + kind: ExprStmt [27-33]: + expr: Expr [27-32]: AssignExpr: + lhs: Expr [27-28]: Ident [27-28] "a" + rhs: Expr [31-32]: Lit: Int(0) + else_block: "#]], ); } @@ -73,36 +88,56 @@ fn nested_if_stmts() { } ", &expect![[r#" - Stmt [5-215] - StmtKind: IfStmt [5-215]: Expr [9-15]: BinOp (Eq): - Expr [9-10]: Ident [9-10] "x" - Expr [14-15]: Ident [14-15] "y" - Stmt [27-107] - StmtKind: IfStmt [27-107]: Expr [31-39]: BinOp (Eq): - Expr [31-33]: Ident [31-33] "x1" - Expr [37-39]: Ident [37-39] "y1" - Stmt [55-61] - StmtKind: ExprStmt [55-61]: Expr [55-60]: Assign: - Expr [55-56]: Ident [55-56] "a" - Expr [59-60]: Lit: Int(0) - Else: - Stmt [91-97] - StmtKind: ExprStmt [91-97]: Expr [91-96]: Assign: - Expr [91-92]: Ident [91-92] "a" - Expr [95-96]: Lit: Int(1) - Else: - Stmt [129-209] - StmtKind: IfStmt [129-209]: Expr [133-141]: BinOp (Eq): - Expr [133-135]: Ident [133-135] "x2" - Expr [139-141]: Ident [139-141] "y2" - Stmt [157-163] - StmtKind: ExprStmt [157-163]: Expr [157-162]: Assign: - Expr [157-158]: Ident [157-158] "a" - Expr [161-162]: Lit: Int(2) - Else: - Stmt [193-199] - StmtKind: ExprStmt [193-199]: Expr [193-198]: Assign: - Expr [193-194]: Ident [193-194] "a" - Expr [197-198]: Lit: Int(3)"#]], + Stmt [5-215]: + annotations: + kind: IfStmt [5-215]: + condition: Expr [9-15]: BinaryOpExpr: + op: Eq + lhs: Expr [9-10]: Ident [9-10] "x" + rhs: Expr [14-15]: Ident [14-15] "y" + if_block: + Stmt [27-107]: + annotations: + kind: IfStmt [27-107]: + condition: Expr [31-39]: BinaryOpExpr: + op: Eq + lhs: Expr [31-33]: Ident [31-33] "x1" + rhs: Expr [37-39]: Ident [37-39] "y1" + if_block: + Stmt [55-61]: + annotations: + kind: ExprStmt [55-61]: + expr: Expr [55-60]: AssignExpr: + lhs: Expr [55-56]: Ident [55-56] "a" + rhs: Expr [59-60]: Lit: Int(0) + else_block: + Stmt [91-97]: + annotations: + kind: ExprStmt [91-97]: + expr: Expr [91-96]: AssignExpr: + lhs: Expr [91-92]: Ident [91-92] "a" + rhs: Expr [95-96]: Lit: Int(1) + else_block: + Stmt [129-209]: + annotations: + kind: IfStmt [129-209]: + condition: Expr [133-141]: BinaryOpExpr: + op: Eq + lhs: Expr [133-135]: Ident [133-135] "x2" + rhs: Expr [139-141]: Ident [139-141] "y2" + if_block: + Stmt [157-163]: + annotations: + kind: ExprStmt [157-163]: + expr: Expr [157-162]: AssignExpr: + lhs: Expr [157-158]: Ident [157-158] "a" + rhs: Expr [161-162]: Lit: Int(2) + else_block: + Stmt [193-199]: + annotations: + kind: ExprStmt [193-199]: + expr: Expr [193-198]: AssignExpr: + lhs: Expr [193-194]: Ident [193-194] "a" + rhs: Expr [197-198]: Lit: Int(3)"#]], ); } diff --git a/compiler/qsc_qasm3/src/parser/stmt/tests/io_decl.rs b/compiler/qsc_qasm3/src/parser/stmt/tests/io_decl.rs index cfcae5cb3e..aa9c48b864 100644 --- a/compiler/qsc_qasm3/src/parser/stmt/tests/io_decl.rs +++ b/compiler/qsc_qasm3/src/parser/stmt/tests/io_decl.rs @@ -13,8 +13,13 @@ fn input_bit_decl() { parse, "input bit b;", &expect![[r#" - Stmt [0-12] - StmtKind: IODeclaration [0-12]: input, ClassicalType [6-9]: BitType, Ident [10-11] "b""#]], + Stmt [0-12]: + annotations: + kind: IODeclaration [0-12]: + io_keyword: input + type: ScalarType [6-9]: BitType [6-9]: + size: + ident: Ident [10-11] "b""#]], ); } @@ -24,8 +29,13 @@ fn output_bit_decl() { parse, "output bit b;", &expect![[r#" - Stmt [0-13] - StmtKind: IODeclaration [0-13]: output, ClassicalType [7-10]: BitType, Ident [11-12] "b""#]], + Stmt [0-13]: + annotations: + kind: IODeclaration [0-13]: + io_keyword: output + type: ScalarType [7-10]: BitType [7-10]: + size: + ident: Ident [11-12] "b""#]], ); } @@ -35,8 +45,13 @@ fn input_bit_array_decl() { parse, "input bit[2] b;", &expect![[r#" - Stmt [0-15] - StmtKind: IODeclaration [0-15]: input, ClassicalType [6-12]: BitType [6-12]: Expr [10-11]: Lit: Int(2), Ident [13-14] "b""#]], + Stmt [0-15]: + annotations: + kind: IODeclaration [0-15]: + io_keyword: input + type: ScalarType [6-12]: BitType [6-12]: + size: Expr [10-11]: Lit: Int(2) + ident: Ident [13-14] "b""#]], ); } @@ -46,8 +61,13 @@ fn output_bit_array_decl() { parse, "output bit[2] b;", &expect![[r#" - Stmt [0-16] - StmtKind: IODeclaration [0-16]: output, ClassicalType [7-13]: BitType [7-13]: Expr [11-12]: Lit: Int(2), Ident [14-15] "b""#]], + Stmt [0-16]: + annotations: + kind: IODeclaration [0-16]: + io_keyword: output + type: ScalarType [7-13]: BitType [7-13]: + size: Expr [11-12]: Lit: Int(2) + ident: Ident [14-15] "b""#]], ); } @@ -57,8 +77,12 @@ fn intput_bool_decl() { parse, "input bool b;", &expect![[r#" - Stmt [0-13] - StmtKind: IODeclaration [0-13]: input, ClassicalType [6-10]: BoolType, Ident [11-12] "b""#]], + Stmt [0-13]: + annotations: + kind: IODeclaration [0-13]: + io_keyword: input + type: ScalarType [6-10]: BoolType + ident: Ident [11-12] "b""#]], ); } @@ -68,8 +92,12 @@ fn output_bool_decl() { parse, "output bool b;", &expect![[r#" - Stmt [0-14] - StmtKind: IODeclaration [0-14]: output, ClassicalType [7-11]: BoolType, Ident [12-13] "b""#]], + Stmt [0-14]: + annotations: + kind: IODeclaration [0-14]: + io_keyword: output + type: ScalarType [7-11]: BoolType + ident: Ident [12-13] "b""#]], ); } @@ -79,8 +107,13 @@ fn input_complex_decl() { parse, "input complex c;", &expect![[r#" - Stmt [0-16] - StmtKind: IODeclaration [0-16]: input, ClassicalType [6-13]: ComplexType [6-13], Ident [14-15] "c""#]], + Stmt [0-16]: + annotations: + kind: IODeclaration [0-16]: + io_keyword: input + type: ScalarType [6-13]: ComplexType [6-13]: + base_size: + ident: Ident [14-15] "c""#]], ); } @@ -90,8 +123,13 @@ fn output_complex_decl() { parse, "output complex c;", &expect![[r#" - Stmt [0-17] - StmtKind: IODeclaration [0-17]: output, ClassicalType [7-14]: ComplexType [7-14], Ident [15-16] "c""#]], + Stmt [0-17]: + annotations: + kind: IODeclaration [0-17]: + io_keyword: output + type: ScalarType [7-14]: ComplexType [7-14]: + base_size: + ident: Ident [15-16] "c""#]], ); } @@ -101,8 +139,14 @@ fn input_complex_sized_decl() { parse, "input complex[float[32]] c;", &expect![[r#" - Stmt [0-27] - StmtKind: IODeclaration [0-27]: input, ClassicalType [6-24]: ComplexType[float[FloatType[Expr [20-22]: Lit: Int(32)]: [14-23]]]: [6-24], Ident [25-26] "c""#]], + Stmt [0-27]: + annotations: + kind: IODeclaration [0-27]: + io_keyword: input + type: ScalarType [6-24]: ComplexType [6-24]: + base_size: FloatType [14-23]: + size: Expr [20-22]: Lit: Int(32) + ident: Ident [25-26] "c""#]], ); } @@ -112,8 +156,14 @@ fn output_complex_sized_decl() { parse, "output complex[float[32]] c;", &expect![[r#" - Stmt [0-28] - StmtKind: IODeclaration [0-28]: output, ClassicalType [7-25]: ComplexType[float[FloatType[Expr [21-23]: Lit: Int(32)]: [15-24]]]: [7-25], Ident [26-27] "c""#]], + Stmt [0-28]: + annotations: + kind: IODeclaration [0-28]: + io_keyword: output + type: ScalarType [7-25]: ComplexType [7-25]: + base_size: FloatType [15-24]: + size: Expr [21-23]: Lit: Int(32) + ident: Ident [26-27] "c""#]], ); } @@ -123,8 +173,13 @@ fn input_int_decl() { parse, "input int i;", &expect![[r#" - Stmt [0-12] - StmtKind: IODeclaration [0-12]: input, ClassicalType [6-9]: IntType [6-9], Ident [10-11] "i""#]], + Stmt [0-12]: + annotations: + kind: IODeclaration [0-12]: + io_keyword: input + type: ScalarType [6-9]: IntType [6-9]: + size: + ident: Ident [10-11] "i""#]], ); } @@ -134,8 +189,13 @@ fn output_int_decl() { parse, "output int i;", &expect![[r#" - Stmt [0-13] - StmtKind: IODeclaration [0-13]: output, ClassicalType [7-10]: IntType [7-10], Ident [11-12] "i""#]], + Stmt [0-13]: + annotations: + kind: IODeclaration [0-13]: + io_keyword: output + type: ScalarType [7-10]: IntType [7-10]: + size: + ident: Ident [11-12] "i""#]], ); } @@ -145,8 +205,13 @@ fn input_int_sized_decl() { parse, "input int[32] i;", &expect![[r#" - Stmt [0-16] - StmtKind: IODeclaration [0-16]: input, ClassicalType [6-13]: IntType[Expr [10-12]: Lit: Int(32)]: [6-13], Ident [14-15] "i""#]], + Stmt [0-16]: + annotations: + kind: IODeclaration [0-16]: + io_keyword: input + type: ScalarType [6-13]: IntType [6-13]: + size: Expr [10-12]: Lit: Int(32) + ident: Ident [14-15] "i""#]], ); } @@ -156,8 +221,13 @@ fn output_int_sized_decl() { parse, "output int[32] i;", &expect![[r#" - Stmt [0-17] - StmtKind: IODeclaration [0-17]: output, ClassicalType [7-14]: IntType[Expr [11-13]: Lit: Int(32)]: [7-14], Ident [15-16] "i""#]], + Stmt [0-17]: + annotations: + kind: IODeclaration [0-17]: + io_keyword: output + type: ScalarType [7-14]: IntType [7-14]: + size: Expr [11-13]: Lit: Int(32) + ident: Ident [15-16] "i""#]], ); } @@ -167,8 +237,13 @@ fn input_uint_decl() { parse, "input uint i;", &expect![[r#" - Stmt [0-13] - StmtKind: IODeclaration [0-13]: input, ClassicalType [6-10]: UIntType [6-10], Ident [11-12] "i""#]], + Stmt [0-13]: + annotations: + kind: IODeclaration [0-13]: + io_keyword: input + type: ScalarType [6-10]: UIntType [6-10]: + size: + ident: Ident [11-12] "i""#]], ); } @@ -178,8 +253,13 @@ fn output_uint_decl() { parse, "output uint i;", &expect![[r#" - Stmt [0-14] - StmtKind: IODeclaration [0-14]: output, ClassicalType [7-11]: UIntType [7-11], Ident [12-13] "i""#]], + Stmt [0-14]: + annotations: + kind: IODeclaration [0-14]: + io_keyword: output + type: ScalarType [7-11]: UIntType [7-11]: + size: + ident: Ident [12-13] "i""#]], ); } @@ -189,8 +269,13 @@ fn input_uint_sized_decl() { parse, "input uint[32] i;", &expect![[r#" - Stmt [0-17] - StmtKind: IODeclaration [0-17]: input, ClassicalType [6-14]: UIntType[Expr [11-13]: Lit: Int(32)]: [6-14], Ident [15-16] "i""#]], + Stmt [0-17]: + annotations: + kind: IODeclaration [0-17]: + io_keyword: input + type: ScalarType [6-14]: UIntType [6-14]: + size: Expr [11-13]: Lit: Int(32) + ident: Ident [15-16] "i""#]], ); } @@ -200,8 +285,13 @@ fn output_uint_sized_decl() { parse, "output uint[32] i;", &expect![[r#" - Stmt [0-18] - StmtKind: IODeclaration [0-18]: output, ClassicalType [7-15]: UIntType[Expr [12-14]: Lit: Int(32)]: [7-15], Ident [16-17] "i""#]], + Stmt [0-18]: + annotations: + kind: IODeclaration [0-18]: + io_keyword: output + type: ScalarType [7-15]: UIntType [7-15]: + size: Expr [12-14]: Lit: Int(32) + ident: Ident [16-17] "i""#]], ); } @@ -211,8 +301,13 @@ fn input_float_decl() { parse, "input float f;", &expect![[r#" - Stmt [0-14] - StmtKind: IODeclaration [0-14]: input, ClassicalType [6-11]: FloatType [6-11], Ident [12-13] "f""#]], + Stmt [0-14]: + annotations: + kind: IODeclaration [0-14]: + io_keyword: input + type: ScalarType [6-11]: FloatType [6-11]: + size: + ident: Ident [12-13] "f""#]], ); } @@ -222,8 +317,13 @@ fn output_float_decl() { parse, "output float f;", &expect![[r#" - Stmt [0-15] - StmtKind: IODeclaration [0-15]: output, ClassicalType [7-12]: FloatType [7-12], Ident [13-14] "f""#]], + Stmt [0-15]: + annotations: + kind: IODeclaration [0-15]: + io_keyword: output + type: ScalarType [7-12]: FloatType [7-12]: + size: + ident: Ident [13-14] "f""#]], ); } @@ -233,8 +333,13 @@ fn input_float_sized_decl() { parse, "input float[32] f;", &expect![[r#" - Stmt [0-18] - StmtKind: IODeclaration [0-18]: input, ClassicalType [6-15]: FloatType[Expr [12-14]: Lit: Int(32)]: [6-15], Ident [16-17] "f""#]], + Stmt [0-18]: + annotations: + kind: IODeclaration [0-18]: + io_keyword: input + type: ScalarType [6-15]: FloatType [6-15]: + size: Expr [12-14]: Lit: Int(32) + ident: Ident [16-17] "f""#]], ); } @@ -244,8 +349,13 @@ fn output_float_sized_decl() { parse, "output float[32] f;", &expect![[r#" - Stmt [0-19] - StmtKind: IODeclaration [0-19]: output, ClassicalType [7-16]: FloatType[Expr [13-15]: Lit: Int(32)]: [7-16], Ident [17-18] "f""#]], + Stmt [0-19]: + annotations: + kind: IODeclaration [0-19]: + io_keyword: output + type: ScalarType [7-16]: FloatType [7-16]: + size: Expr [13-15]: Lit: Int(32) + ident: Ident [17-18] "f""#]], ); } @@ -255,8 +365,13 @@ fn input_angle_decl() { parse, "input angle a;", &expect![[r#" - Stmt [0-14] - StmtKind: IODeclaration [0-14]: input, ClassicalType [6-11]: AngleType [6-11], Ident [12-13] "a""#]], + Stmt [0-14]: + annotations: + kind: IODeclaration [0-14]: + io_keyword: input + type: ScalarType [6-11]: AngleType [6-11]: + size: + ident: Ident [12-13] "a""#]], ); } @@ -266,8 +381,13 @@ fn output_angle_decl() { parse, "output angle a;", &expect![[r#" - Stmt [0-15] - StmtKind: IODeclaration [0-15]: output, ClassicalType [7-12]: AngleType [7-12], Ident [13-14] "a""#]], + Stmt [0-15]: + annotations: + kind: IODeclaration [0-15]: + io_keyword: output + type: ScalarType [7-12]: AngleType [7-12]: + size: + ident: Ident [13-14] "a""#]], ); } @@ -277,8 +397,13 @@ fn input_angle_sized_decl() { parse, "input angle[32] a;", &expect![[r#" - Stmt [0-18] - StmtKind: IODeclaration [0-18]: input, ClassicalType [6-15]: AngleType [6-15]: Expr [12-14]: Lit: Int(32), Ident [16-17] "a""#]], + Stmt [0-18]: + annotations: + kind: IODeclaration [0-18]: + io_keyword: input + type: ScalarType [6-15]: AngleType [6-15]: + size: Expr [12-14]: Lit: Int(32) + ident: Ident [16-17] "a""#]], ); } @@ -288,8 +413,13 @@ fn output_angle_sized_decl() { parse, "output angle[32] a;", &expect![[r#" - Stmt [0-19] - StmtKind: IODeclaration [0-19]: output, ClassicalType [7-16]: AngleType [7-16]: Expr [13-15]: Lit: Int(32), Ident [17-18] "a""#]], + Stmt [0-19]: + annotations: + kind: IODeclaration [0-19]: + io_keyword: output + type: ScalarType [7-16]: AngleType [7-16]: + size: Expr [13-15]: Lit: Int(32) + ident: Ident [17-18] "a""#]], ); } @@ -299,8 +429,12 @@ fn input_duration_decl() { parse, "input duration d;", &expect![[r#" - Stmt [0-17] - StmtKind: IODeclaration [0-17]: input, ClassicalType [6-14]: Duration, Ident [15-16] "d""#]], + Stmt [0-17]: + annotations: + kind: IODeclaration [0-17]: + io_keyword: input + type: ScalarType [6-14]: Duration + ident: Ident [15-16] "d""#]], ); } @@ -310,8 +444,12 @@ fn output_duration_decl() { parse, "output duration d;", &expect![[r#" - Stmt [0-18] - StmtKind: IODeclaration [0-18]: output, ClassicalType [7-15]: Duration, Ident [16-17] "d""#]], + Stmt [0-18]: + annotations: + kind: IODeclaration [0-18]: + io_keyword: output + type: ScalarType [7-15]: Duration + ident: Ident [16-17] "d""#]], ); } @@ -321,8 +459,12 @@ fn input_stretch_decl() { parse, "input stretch s;", &expect![[r#" - Stmt [0-16] - StmtKind: IODeclaration [0-16]: input, ClassicalType [6-13]: Stretch, Ident [14-15] "s""#]], + Stmt [0-16]: + annotations: + kind: IODeclaration [0-16]: + io_keyword: input + type: ScalarType [6-13]: Stretch + ident: Ident [14-15] "s""#]], ); } @@ -332,7 +474,11 @@ fn output_stretch_decl() { parse, "output stretch s;", &expect![[r#" - Stmt [0-17] - StmtKind: IODeclaration [0-17]: output, ClassicalType [7-14]: Stretch, Ident [15-16] "s""#]], + Stmt [0-17]: + annotations: + kind: IODeclaration [0-17]: + io_keyword: output + type: ScalarType [7-14]: Stretch + ident: Ident [15-16] "s""#]], ); } diff --git a/compiler/qsc_qasm3/src/parser/stmt/tests/measure.rs b/compiler/qsc_qasm3/src/parser/stmt/tests/measure.rs index ffb621efa9..dbf71b9d03 100644 --- a/compiler/qsc_qasm3/src/parser/stmt/tests/measure.rs +++ b/compiler/qsc_qasm3/src/parser/stmt/tests/measure.rs @@ -11,8 +11,14 @@ fn measure_identifier() { parse, "measure q;", &expect![[r#" - Stmt [0-10] - StmtKind: MeasureStmt [0-10]: MeasureExpr [0-7]: GateOperand IndexedIdent [8-9]: Ident [8-9] "q"[]"#]], + Stmt [0-10]: + annotations: + kind: MeasureStmt [0-10]: + measurement: MeasureExpr [0-7]: + operand: IndexedIdent [8-9]: + name: Ident [8-9] "q" + indices: + target: "#]], ); } @@ -22,10 +28,17 @@ fn measure_indented_ident() { parse, "measure q[2];", &expect![[r#" - Stmt [0-13] - StmtKind: MeasureStmt [0-13]: MeasureExpr [0-7]: GateOperand IndexedIdent [8-12]: Ident [8-9] "q"[ - IndexElement: - IndexSetItem Expr [10-11]: Lit: Int(2)]"#]], + Stmt [0-13]: + annotations: + kind: MeasureStmt [0-13]: + measurement: MeasureExpr [0-7]: + operand: IndexedIdent [8-12]: + name: Ident [8-9] "q" + indices: + IndexSet [10-11]: + values: + Expr [10-11]: Lit: Int(2) + target: "#]], ); } @@ -35,8 +48,12 @@ fn measure_hardware_qubit() { parse, "measure $42;", &expect![[r#" - Stmt [0-12] - StmtKind: MeasureStmt [0-12]: MeasureExpr [0-7]: GateOperand HardwareQubit [8-11]: 42"#]], + Stmt [0-12]: + annotations: + kind: MeasureStmt [0-12]: + measurement: MeasureExpr [0-7]: + operand: HardwareQubit [8-11]: 42 + target: "#]], ); } @@ -46,8 +63,16 @@ fn measure_arrow_into_ident() { parse, "measure q -> a;", &expect![[r#" - Stmt [0-15] - StmtKind: MeasureStmt [0-15]: MeasureExpr [0-7]: GateOperand IndexedIdent [8-9]: Ident [8-9] "q"[], IndexedIdent [13-14]: Ident [13-14] "a"[]"#]], + Stmt [0-15]: + annotations: + kind: MeasureStmt [0-15]: + measurement: MeasureExpr [0-7]: + operand: IndexedIdent [8-9]: + name: Ident [8-9] "q" + indices: + target: IndexedIdent [13-14]: + name: Ident [13-14] "a" + indices: "#]], ); } @@ -57,9 +82,18 @@ fn measure_arrow_into_indented_ident() { parse, "measure q -> a[1];", &expect![[r#" - Stmt [0-18] - StmtKind: MeasureStmt [0-18]: MeasureExpr [0-7]: GateOperand IndexedIdent [8-9]: Ident [8-9] "q"[], IndexedIdent [13-17]: Ident [13-14] "a"[ - IndexElement: - IndexSetItem Expr [15-16]: Lit: Int(1)]"#]], + Stmt [0-18]: + annotations: + kind: MeasureStmt [0-18]: + measurement: MeasureExpr [0-7]: + operand: IndexedIdent [8-9]: + name: Ident [8-9] "q" + indices: + target: IndexedIdent [13-17]: + name: Ident [13-14] "a" + indices: + IndexSet [15-16]: + values: + Expr [15-16]: Lit: Int(1)"#]], ); } diff --git a/compiler/qsc_qasm3/src/parser/stmt/tests/old_style_decl.rs b/compiler/qsc_qasm3/src/parser/stmt/tests/old_style_decl.rs index 69152c07ff..ad0d3b7e1c 100644 --- a/compiler/qsc_qasm3/src/parser/stmt/tests/old_style_decl.rs +++ b/compiler/qsc_qasm3/src/parser/stmt/tests/old_style_decl.rs @@ -13,8 +13,13 @@ fn creg_decl() { parse, "creg c;", &expect![[r#" - Stmt [0-7] - StmtKind: ClassicalDeclarationStmt [0-7]: ClassicalType [0-7]: BitType, Ident [5-6] "c""#]], + Stmt [0-7]: + annotations: + kind: ClassicalDeclarationStmt [0-7]: + type: ScalarType [0-7]: BitType [0-7]: + size: + ident: Ident [5-6] "c" + init_expr: "#]], ); } @@ -24,8 +29,13 @@ fn creg_array_decl() { parse, "creg c[n];", &expect![[r#" - Stmt [0-10] - StmtKind: ClassicalDeclarationStmt [0-10]: ClassicalType [0-10]: BitType [0-10]: Expr [7-8]: Ident [7-8] "n", Ident [5-6] "c""#]], + Stmt [0-10]: + annotations: + kind: ClassicalDeclarationStmt [0-10]: + type: ScalarType [0-10]: BitType [0-10]: + size: Expr [7-8]: Ident [7-8] "n" + ident: Ident [5-6] "c" + init_expr: "#]], ); } @@ -35,8 +45,11 @@ fn qreg_decl() { parse, "qreg q;", &expect![[r#" - Stmt [0-7] - StmtKind: QubitDeclaration [0-7]: Ident [5-6] "q""#]], + Stmt [0-7]: + annotations: + kind: QubitDeclaration [0-7]: + ident: Ident [5-6] "q" + size: "#]], ); } @@ -46,7 +59,10 @@ fn qreg_array_decl() { parse, "qreg q[n];", &expect![[r#" - Stmt [0-10] - StmtKind: QubitDeclaration [0-10]: Ident [5-6] "q", Expr [7-8]: Ident [7-8] "n""#]], + Stmt [0-10]: + annotations: + kind: QubitDeclaration [0-10]: + ident: Ident [5-6] "q" + size: Expr [7-8]: Ident [7-8] "n""#]], ); } diff --git a/compiler/qsc_qasm3/src/parser/stmt/tests/pragma.rs b/compiler/qsc_qasm3/src/parser/stmt/tests/pragma.rs index 83319befae..42abef06cb 100644 --- a/compiler/qsc_qasm3/src/parser/stmt/tests/pragma.rs +++ b/compiler/qsc_qasm3/src/parser/stmt/tests/pragma.rs @@ -13,8 +13,11 @@ fn pragma_decl() { parse, "pragma a.b.d 23", &expect![[r#" - Stmt [0-15] - StmtKind: Pragma [0-15]: (a.b.d, 23)"#]], + Stmt [0-15]: + annotations: + kind: Pragma [0-15]: + identifier: "a.b.d" + value: "23""#]], ); } @@ -24,8 +27,11 @@ fn pragma_decl_ident_only() { parse, "pragma a.b.d", &expect![[r#" - Stmt [0-12] - StmtKind: Pragma [0-12]: (a.b.d)"#]], + Stmt [0-12]: + annotations: + kind: Pragma [0-12]: + identifier: "a.b.d" + value: "#]], ); } @@ -35,8 +41,11 @@ fn pragma_decl_missing_ident() { parse, "pragma ", &expect![[r#" - Stmt [0-7] - StmtKind: Pragma [0-7]: () + Stmt [0-7]: + annotations: + kind: Pragma [0-7]: + identifier: "" + value: [ Error( @@ -59,8 +68,11 @@ fn legacy_pragma_decl() { parse, "#pragma a.b.d 23", &expect![[r#" - Stmt [0-16] - StmtKind: Pragma [0-16]: (a, a.b.d 23)"#]], + Stmt [0-16]: + annotations: + kind: Pragma [0-16]: + identifier: "a" + value: "a.b.d 23""#]], ); } @@ -70,8 +82,11 @@ fn legacy_pragma_decl_ident_only() { parse, "#pragma a.b.d", &expect![[r#" - Stmt [0-13] - StmtKind: Pragma [0-13]: (a, a.b.d)"#]], + Stmt [0-13]: + annotations: + kind: Pragma [0-13]: + identifier: "a" + value: "a.b.d""#]], ); } @@ -81,8 +96,11 @@ fn legacy_pragma_ws_after_hash() { parse, "# pragma a.b.d", &expect![[r#" - Stmt [2-14] - StmtKind: Pragma [2-14]: (a.b.d) + Stmt [2-14]: + annotations: + kind: Pragma [2-14]: + identifier: "a.b.d" + value: [ Error( @@ -108,7 +126,10 @@ fn legacy_pragma_decl_missing_ident() { parse, "#pragma ", &expect![[r#" - Stmt [0-8] - StmtKind: Pragma [0-8]: (a, )"#]], + Stmt [0-8]: + annotations: + kind: Pragma [0-8]: + identifier: "a" + value: """#]], ); } diff --git a/compiler/qsc_qasm3/src/parser/stmt/tests/quantum_decl.rs b/compiler/qsc_qasm3/src/parser/stmt/tests/quantum_decl.rs index 93d18cec15..1bf02de48d 100644 --- a/compiler/qsc_qasm3/src/parser/stmt/tests/quantum_decl.rs +++ b/compiler/qsc_qasm3/src/parser/stmt/tests/quantum_decl.rs @@ -13,8 +13,11 @@ fn quantum_decl() { parse, "qubit q;", &expect![[r#" - Stmt [0-8] - StmtKind: QubitDeclaration [0-8]: Ident [6-7] "q""#]], + Stmt [0-8]: + annotations: + kind: QubitDeclaration [0-8]: + ident: Ident [6-7] "q" + size: "#]], ); } @@ -26,9 +29,14 @@ fn annotated_quantum_decl() { @a.b.c 123 qubit q;"#, &expect![[r#" - Stmt [9-36] - Annotation [9-19]: (a.b.c, 123) - StmtKind: QubitDeclaration [28-36]: Ident [34-35] "q""#]], + Stmt [9-36]: + annotations: + Annotation [9-19]: + identifier: "a.b.c" + value: "123" + kind: QubitDeclaration [28-36]: + ident: Ident [34-35] "q" + size: "#]], ); } @@ -42,11 +50,20 @@ fn multi_annotated_quantum_decl() { @a.b.c 123 qubit q;"#, &expect![[r#" - Stmt [9-108] - Annotation [9-57]: (g.h, dolor sit amet, consectetur adipiscing elit) - Annotation [66-72]: (d.e.f) - Annotation [81-91]: (a.b.c, 123) - StmtKind: QubitDeclaration [100-108]: Ident [106-107] "q""#]], + Stmt [9-108]: + annotations: + Annotation [9-57]: + identifier: "g.h" + value: "dolor sit amet, consectetur adipiscing elit" + Annotation [66-72]: + identifier: "d.e.f" + value: + Annotation [81-91]: + identifier: "a.b.c" + value: "123" + kind: QubitDeclaration [100-108]: + ident: Ident [106-107] "q" + size: "#]], ); } @@ -76,8 +93,11 @@ fn quantum_decl_with_designator() { parse, "qubit[5] qubits;", &expect![[r#" - Stmt [0-16] - StmtKind: QubitDeclaration [0-16]: Ident [9-15] "qubits", Expr [6-7]: Lit: Int(5)"#]], + Stmt [0-16]: + annotations: + kind: QubitDeclaration [0-16]: + ident: Ident [9-15] "qubits" + size: Expr [6-7]: Lit: Int(5)"#]], ); } diff --git a/compiler/qsc_qasm3/src/parser/stmt/tests/reset.rs b/compiler/qsc_qasm3/src/parser/stmt/tests/reset.rs index 863df34196..0b144e90b4 100644 --- a/compiler/qsc_qasm3/src/parser/stmt/tests/reset.rs +++ b/compiler/qsc_qasm3/src/parser/stmt/tests/reset.rs @@ -11,8 +11,12 @@ fn reset_ident() { parse, "reset a;", &expect![[r#" - Stmt [0-8] - StmtKind: ResetStmt [0-8]: GateOperand IndexedIdent [6-7]: Ident [6-7] "a"[]"#]], + Stmt [0-8]: + annotations: + kind: ResetStmt [0-8]: + operand: IndexedIdent [6-7]: + name: Ident [6-7] "a" + indices: "#]], ); } @@ -22,10 +26,15 @@ fn reset_indexed_ident() { parse, "reset a[1];", &expect![[r#" - Stmt [0-11] - StmtKind: ResetStmt [0-11]: GateOperand IndexedIdent [6-10]: Ident [6-7] "a"[ - IndexElement: - IndexSetItem Expr [8-9]: Lit: Int(1)]"#]], + Stmt [0-11]: + annotations: + kind: ResetStmt [0-11]: + operand: IndexedIdent [6-10]: + name: Ident [6-7] "a" + indices: + IndexSet [8-9]: + values: + Expr [8-9]: Lit: Int(1)"#]], ); } @@ -35,7 +44,9 @@ fn reset_hardware_qubit() { parse, "reset $42;", &expect![[r#" - Stmt [0-10] - StmtKind: ResetStmt [0-10]: GateOperand HardwareQubit [6-9]: 42"#]], + Stmt [0-10]: + annotations: + kind: ResetStmt [0-10]: + operand: HardwareQubit [6-9]: 42"#]], ); } diff --git a/compiler/qsc_qasm3/src/parser/stmt/tests/switch_stmt.rs b/compiler/qsc_qasm3/src/parser/stmt/tests/switch_stmt.rs index cc6cfdd706..2ecdea6fd4 100644 --- a/compiler/qsc_qasm3/src/parser/stmt/tests/switch_stmt.rs +++ b/compiler/qsc_qasm3/src/parser/stmt/tests/switch_stmt.rs @@ -16,13 +16,13 @@ fn simple_switch() { ", &expect![[r#" SwitchStmt [9-72]: - Target: Expr [17-18]: Ident [17-18] "x" - Cases: - Labels: - Expr [37-38]: Lit: Int(1) - Block [39-41]: - Default Case: - Block [60-62]: "#]], + target: Expr [17-18]: Ident [17-18] "x" + cases: + SwitchCase [32-41]: + labels: + Expr [37-38]: Lit: Int(1) + block: Block [39-41]: + default_case: Block [60-62]: "#]], ); } @@ -35,9 +35,9 @@ fn no_cases_no_default() { ", &expect![[r#" SwitchStmt [9-22]: - Target: Expr [17-18]: Ident [17-18] "x" - - + target: Expr [17-18]: Ident [17-18] "x" + cases: + default_case: [ Error( @@ -63,10 +63,9 @@ fn no_cases() { ", &expect![[r#" SwitchStmt [9-52]: - Target: Expr [17-18]: Ident [17-18] "x" - - Default Case: - Block [40-42]: + target: Expr [17-18]: Ident [17-18] "x" + cases: + default_case: Block [40-42]: [ Error( @@ -92,13 +91,14 @@ fn no_default() { ", &expect![[r#" SwitchStmt [9-54]: - Target: Expr [17-18]: Ident [17-18] "x" - Cases: - Labels: - Expr [37-38]: Lit: Int(0) - Expr [40-41]: Lit: Int(1) - Block [42-44]: - "#]], + target: Expr [17-18]: Ident [17-18] "x" + cases: + SwitchCase [32-44]: + labels: + Expr [37-38]: Lit: Int(0) + Expr [40-41]: Lit: Int(1) + block: Block [42-44]: + default_case: "#]], ); } @@ -113,11 +113,12 @@ fn case_with_no_labels() { ", &expect![[r#" SwitchStmt [9-49]: - Target: Expr [17-18]: Ident [17-18] "x" - Cases: - - Block [37-39]: - + target: Expr [17-18]: Ident [17-18] "x" + cases: + SwitchCase [32-39]: + labels: + block: Block [37-39]: + default_case: [ Error( @@ -144,18 +145,30 @@ fn multiple_cases() { ", &expect![[r#" SwitchStmt [9-95]: - Target: Expr [17-18]: Ident [17-18] "x" - Cases: - Labels: - Expr [37-38]: Lit: Int(0) - Block [39-53]: - Stmt [41-51] - StmtKind: ClassicalDeclarationStmt [41-51]: ClassicalType [41-44]: IntType [41-44], Ident [45-46] "x", ValueExpression Expr [49-50]: Lit: Int(0) - Labels: - Expr [69-70]: Lit: Int(1) - Block [71-85]: - Stmt [73-83] - StmtKind: ClassicalDeclarationStmt [73-83]: ClassicalType [73-76]: IntType [73-76], Ident [77-78] "y", ValueExpression Expr [81-82]: Lit: Int(1) - "#]], + target: Expr [17-18]: Ident [17-18] "x" + cases: + SwitchCase [32-53]: + labels: + Expr [37-38]: Lit: Int(0) + block: Block [39-53]: + Stmt [41-51]: + annotations: + kind: ClassicalDeclarationStmt [41-51]: + type: ScalarType [41-44]: IntType [41-44]: + size: + ident: Ident [45-46] "x" + init_expr: Expr [49-50]: Lit: Int(0) + SwitchCase [64-85]: + labels: + Expr [69-70]: Lit: Int(1) + block: Block [71-85]: + Stmt [73-83]: + annotations: + kind: ClassicalDeclarationStmt [73-83]: + type: ScalarType [73-76]: IntType [73-76]: + size: + ident: Ident [77-78] "y" + init_expr: Expr [81-82]: Lit: Int(1) + default_case: "#]], ); } diff --git a/compiler/qsc_qasm3/src/parser/stmt/tests/while_loops.rs b/compiler/qsc_qasm3/src/parser/stmt/tests/while_loops.rs index fe7576bbb7..6a7cff7fde 100644 --- a/compiler/qsc_qasm3/src/parser/stmt/tests/while_loops.rs +++ b/compiler/qsc_qasm3/src/parser/stmt/tests/while_loops.rs @@ -13,14 +13,34 @@ fn simple_while() { a = 0; }", &expect![[r#" - Stmt [5-42] - StmtKind: WhileLoop [5-42]: Expr [12-18]: BinOp (Neq): - Expr [12-13]: Ident [12-13] "x" - Expr [17-18]: Lit: Int(2) - Stmt [30-36] - StmtKind: ExprStmt [30-36]: Expr [30-35]: Assign: - Expr [30-31]: Ident [30-31] "a" - Expr [34-35]: Lit: Int(0)"#]], + Stmt [5-42]: + annotations: + kind: WhileLoop [5-42]: + condition: Expr [12-18]: BinaryOpExpr: + op: Neq + lhs: Expr [12-13]: Ident [12-13] "x" + rhs: Expr [17-18]: Lit: Int(2) + block: + Stmt [30-36]: + annotations: + kind: ExprStmt [30-36]: + expr: Expr [30-35]: AssignExpr: + lhs: Expr [30-31]: Ident [30-31] "a" + rhs: Expr [34-35]: Lit: Int(0)"#]], + ); +} + +#[test] +fn empty_while() { + check( + parse, + "while (true) {}", + &expect![[r#" + Stmt [0-15]: + annotations: + kind: WhileLoop [0-15]: + condition: Expr [7-11]: Lit: Bool(true) + block: "#]], ); } @@ -32,14 +52,20 @@ fn while_stmt_body() { while (x != 2) a = 0;", &expect![[r#" - Stmt [5-34] - StmtKind: WhileLoop [5-34]: Expr [12-18]: BinOp (Neq): - Expr [12-13]: Ident [12-13] "x" - Expr [17-18]: Lit: Int(2) - Stmt [28-34] - StmtKind: ExprStmt [28-34]: Expr [28-33]: Assign: - Expr [28-29]: Ident [28-29] "a" - Expr [32-33]: Lit: Int(0)"#]], + Stmt [5-34]: + annotations: + kind: WhileLoop [5-34]: + condition: Expr [12-18]: BinaryOpExpr: + op: Neq + lhs: Expr [12-13]: Ident [12-13] "x" + rhs: Expr [17-18]: Lit: Int(2) + block: + Stmt [28-34]: + annotations: + kind: ExprStmt [28-34]: + expr: Expr [28-33]: AssignExpr: + lhs: Expr [28-29]: Ident [28-29] "a" + rhs: Expr [32-33]: Lit: Int(0)"#]], ); } @@ -53,16 +79,23 @@ fn while_loop_with_continue_stmt() { continue; }", &expect![[r#" - Stmt [5-60] - StmtKind: WhileLoop [5-60]: Expr [12-18]: BinOp (Neq): - Expr [12-13]: Ident [12-13] "x" - Expr [17-18]: Lit: Int(2) - Stmt [30-36] - StmtKind: ExprStmt [30-36]: Expr [30-35]: Assign: - Expr [30-31]: Ident [30-31] "a" - Expr [34-35]: Lit: Int(0) - Stmt [45-54] - StmtKind: Continue [45-54]"#]], + Stmt [5-60]: + annotations: + kind: WhileLoop [5-60]: + condition: Expr [12-18]: BinaryOpExpr: + op: Neq + lhs: Expr [12-13]: Ident [12-13] "x" + rhs: Expr [17-18]: Lit: Int(2) + block: + Stmt [30-36]: + annotations: + kind: ExprStmt [30-36]: + expr: Expr [30-35]: AssignExpr: + lhs: Expr [30-31]: Ident [30-31] "a" + rhs: Expr [34-35]: Lit: Int(0) + Stmt [45-54]: + annotations: + kind: ContinueStmt [45-54]"#]], ); } @@ -76,15 +109,22 @@ fn while_loop_with_break_stmt() { break; }", &expect![[r#" - Stmt [5-57] - StmtKind: WhileLoop [5-57]: Expr [12-18]: BinOp (Neq): - Expr [12-13]: Ident [12-13] "x" - Expr [17-18]: Lit: Int(2) - Stmt [30-36] - StmtKind: ExprStmt [30-36]: Expr [30-35]: Assign: - Expr [30-31]: Ident [30-31] "a" - Expr [34-35]: Lit: Int(0) - Stmt [45-51] - StmtKind: Break [45-51]"#]], + Stmt [5-57]: + annotations: + kind: WhileLoop [5-57]: + condition: Expr [12-18]: BinaryOpExpr: + op: Neq + lhs: Expr [12-13]: Ident [12-13] "x" + rhs: Expr [17-18]: Lit: Int(2) + block: + Stmt [30-36]: + annotations: + kind: ExprStmt [30-36]: + expr: Expr [30-35]: AssignExpr: + lhs: Expr [30-31]: Ident [30-31] "a" + rhs: Expr [34-35]: Lit: Int(0) + Stmt [45-51]: + annotations: + kind: BreakStmt [45-51]"#]], ); } From 7860600dae7b419de85c0ffe904c668d1001b9b6 Mon Sep 17 00:00:00 2001 From: Ian Davis Date: Fri, 7 Mar 2025 14:34:38 -0800 Subject: [PATCH 054/108] Add initial semantic analysis/transform (#2214) --- compiler/qsc_qasm3/src/ast.rs | 144 +- compiler/qsc_qasm3/src/ast/display_utils.rs | 22 +- compiler/qsc_qasm3/src/ast_builder.rs | 1 + compiler/qsc_qasm3/src/compile.rs | 44 +- compiler/qsc_qasm3/src/lib.rs | 305 +-- compiler/qsc_qasm3/src/oqasm_helpers.rs | 11 +- compiler/qsc_qasm3/src/parser.rs | 36 +- compiler/qsc_qasm3/src/parser/completion.rs | 3 - compiler/qsc_qasm3/src/parser/expr.rs | 23 +- compiler/qsc_qasm3/src/parser/prim.rs | 3 - compiler/qsc_qasm3/src/parser/scan.rs | 4 +- compiler/qsc_qasm3/src/parser/stmt.rs | 20 +- compiler/qsc_qasm3/src/runtime.rs | 70 +- compiler/qsc_qasm3/src/semantic.rs | 131 ++ compiler/qsc_qasm3/src/semantic/ast.rs | 1655 +++++++++++++++ compiler/qsc_qasm3/src/semantic/error.rs | 330 +++ compiler/qsc_qasm3/src/semantic/lowerer.rs | 1788 +++++++++++++++++ compiler/qsc_qasm3/src/semantic/symbols.rs | 451 +++++ compiler/qsc_qasm3/src/semantic/tests.rs | 152 ++ .../qsc_qasm3/src/semantic/tests/decls.rs | 97 + .../qsc_qasm3/src/semantic/tests/decls/bit.rs | 123 ++ .../src/semantic/tests/decls/bool.rs | 123 ++ .../src/semantic/tests/decls/complex.rs | 393 ++++ .../src/semantic/tests/decls/creg.rs | 47 + .../src/semantic/tests/decls/duration.rs | 26 + .../src/semantic/tests/decls/float.rs | 499 +++++ .../qsc_qasm3/src/semantic/tests/decls/int.rs | 423 ++++ .../src/semantic/tests/decls/qreg.rs | 28 + .../src/semantic/tests/decls/stretch.rs | 26 + .../src/semantic/tests/decls/uint.rs | 427 ++++ compiler/qsc_qasm3/src/semantic/types.rs | 675 +++++++ .../src/tests/declaration/integer.rs | 45 - .../src/tests/declaration/unsigned_integer.rs | 45 + compiler/qsc_qasm3/src/types.rs | 27 +- 34 files changed, 7700 insertions(+), 497 deletions(-) create mode 100644 compiler/qsc_qasm3/src/semantic.rs create mode 100644 compiler/qsc_qasm3/src/semantic/ast.rs create mode 100644 compiler/qsc_qasm3/src/semantic/error.rs create mode 100644 compiler/qsc_qasm3/src/semantic/lowerer.rs create mode 100644 compiler/qsc_qasm3/src/semantic/symbols.rs create mode 100644 compiler/qsc_qasm3/src/semantic/tests.rs create mode 100644 compiler/qsc_qasm3/src/semantic/tests/decls.rs create mode 100644 compiler/qsc_qasm3/src/semantic/tests/decls/bit.rs create mode 100644 compiler/qsc_qasm3/src/semantic/tests/decls/bool.rs create mode 100644 compiler/qsc_qasm3/src/semantic/tests/decls/complex.rs create mode 100644 compiler/qsc_qasm3/src/semantic/tests/decls/creg.rs create mode 100644 compiler/qsc_qasm3/src/semantic/tests/decls/duration.rs create mode 100644 compiler/qsc_qasm3/src/semantic/tests/decls/float.rs create mode 100644 compiler/qsc_qasm3/src/semantic/tests/decls/int.rs create mode 100644 compiler/qsc_qasm3/src/semantic/tests/decls/qreg.rs create mode 100644 compiler/qsc_qasm3/src/semantic/tests/decls/stretch.rs create mode 100644 compiler/qsc_qasm3/src/semantic/tests/decls/uint.rs create mode 100644 compiler/qsc_qasm3/src/semantic/types.rs diff --git a/compiler/qsc_qasm3/src/ast.rs b/compiler/qsc_qasm3/src/ast.rs index 24ca614a38..d53088a362 100644 --- a/compiler/qsc_qasm3/src/ast.rs +++ b/compiler/qsc_qasm3/src/ast.rs @@ -1,15 +1,13 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. -// while we work through the conversion, allow dead code to avoid warnings -#![allow(dead_code)] - -mod display_utils; +pub(crate) mod display_utils; use display_utils::{ write_field, write_header, write_indented_list, write_list_field, write_opt_field, write_opt_list_field, writeln_field, writeln_header, writeln_list_field, writeln_opt_field, }; + use num_bigint::BigInt; use qsc_data_structures::span::{Span, WithSpan}; use std::{ @@ -66,6 +64,7 @@ pub struct Annotation { pub identifier: Rc, pub value: Option>, } + impl Display for Annotation { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { let identifier = format!("\"{}\"", self.identifier); @@ -145,21 +144,6 @@ impl WithSpan for Path { } } -#[derive(Clone, Debug)] -pub enum AssignmentExpr { - Expr(Expr), - Measurement(MeasureExpr), -} - -impl Display for AssignmentExpr { - fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { - match self { - AssignmentExpr::Expr(expr) => write!(f, "AssignmentExpr {expr}"), - AssignmentExpr::Measurement(measure) => write!(f, "AssignmentExpr {measure}"), - } - } -} - #[derive(Clone, Debug)] pub struct MeasureExpr { pub span: Span, @@ -282,8 +266,12 @@ impl Display for GateOperand { } impl WithSpan for GateOperand { - fn with_span(self, _span: Span) -> Self { - self + fn with_span(self, span: Span) -> Self { + match self { + GateOperand::IndexedIdent(ident) => GateOperand::IndexedIdent(ident.with_span(span)), + GateOperand::HardwareQubit(qubit) => GateOperand::HardwareQubit(qubit.with_span(span)), + GateOperand::Err => GateOperand::Err, + } } } @@ -299,6 +287,12 @@ impl Display for HardwareQubit { } } +impl WithSpan for HardwareQubit { + fn with_span(self, span: Span) -> Self { + Self { span, ..self } + } +} + #[derive(Clone, Debug)] pub struct AliasDeclStmt { pub span: Span, @@ -482,6 +476,15 @@ pub enum Identifier { IndexedIdent(Box), } +impl Identifier { + pub fn span(&self) -> Span { + match self { + Identifier::Ident(ident) => ident.span, + Identifier::IndexedIdent(ident) => ident.span, + } + } +} + impl Display for Identifier { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { match self { @@ -533,6 +536,12 @@ impl Display for IndexedIdent { } } +impl WithSpan for IndexedIdent { + fn with_span(self, span: Span) -> Self { + Self { span, ..self } + } +} + #[derive(Clone, Debug)] pub struct ExprStmt { pub span: Span, @@ -598,6 +607,12 @@ pub struct RangeDefinition { pub step: Option, } +impl WithSpan for RangeDefinition { + fn with_span(self, span: Span) -> Self { + Self { span, ..self } + } +} + impl Display for RangeDefinition { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { writeln_header(f, "RangeDefinition", self.span)?; @@ -641,7 +656,7 @@ impl Display for GateModifierKind { #[derive(Clone, Debug)] pub struct ClassicalArgument { pub span: Span, - pub r#type: ScalarType, + pub ty: ScalarType, pub name: Identifier, pub access: Option, } @@ -652,13 +667,13 @@ impl Display for ClassicalArgument { write!( f, "ClassicalArgument {}: {}, {}, {}", - self.span, self.r#type, self.name, access + self.span, self.ty, self.name, access ) } else { write!( f, "ClassicalArgument {}: {}, {}", - self.span, self.r#type, self.name + self.span, self.ty, self.name ) } } @@ -726,6 +741,7 @@ pub enum ScalarTypeKind { BoolType, Duration, Stretch, + // Any usage of Err should have pushed a parse error #[default] Err, } @@ -1106,7 +1122,7 @@ impl Display for MeasureStmt { #[derive(Clone, Debug)] pub struct ClassicalDeclarationStmt { pub span: Span, - pub r#type: Box, + pub ty: Box, pub identifier: Ident, pub init_expr: Option>, } @@ -1114,7 +1130,7 @@ pub struct ClassicalDeclarationStmt { impl Display for ClassicalDeclarationStmt { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { writeln_header(f, "ClassicalDeclarationStmt", self.span)?; - writeln_field(f, "type", &self.r#type)?; + writeln_field(f, "type", &self.ty)?; writeln_field(f, "ident", &self.identifier)?; write_opt_field(f, "init_expr", self.init_expr.as_ref()) } @@ -1139,7 +1155,7 @@ impl Display for ValueExpression { pub struct IODeclaration { pub span: Span, pub io_identifier: IOKeyword, - pub r#type: TypeDef, + pub ty: TypeDef, pub ident: Box, } @@ -1147,7 +1163,7 @@ impl Display for IODeclaration { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { writeln_header(f, "IODeclaration", self.span)?; writeln_field(f, "io_keyword", &self.io_identifier)?; - writeln_field(f, "type", &self.r#type)?; + writeln_field(f, "type", &self.ty)?; write_field(f, "ident", &self.ident) } } @@ -1155,7 +1171,7 @@ impl Display for IODeclaration { #[derive(Clone, Debug)] pub struct ConstantDeclStmt { pub span: Span, - pub r#type: TypeDef, + pub ty: TypeDef, pub identifier: Box, pub init_expr: Expr, } @@ -1163,7 +1179,7 @@ pub struct ConstantDeclStmt { impl Display for ConstantDeclStmt { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { writeln_header(f, "ConstantDeclStmt", self.span)?; - writeln_field(f, "type", &self.r#type)?; + writeln_field(f, "type", &self.ty)?; writeln_field(f, "ident", &self.identifier)?; write_field(f, "init_expr", &self.init_expr) } @@ -1225,7 +1241,7 @@ impl Default for TypedParameter { Self::Scalar(ScalarTypedParameter { span: Span::default(), ident: Ident::default(), - r#type: Box::default(), + ty: Box::default(), }) } } @@ -1233,26 +1249,22 @@ impl Default for TypedParameter { #[derive(Clone, Debug)] pub struct ScalarTypedParameter { pub span: Span, - pub r#type: Box, + pub ty: Box, pub ident: Ident, } impl Display for ScalarTypedParameter { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { writeln_header(f, "ScalarTypedParameter", self.span)?; - writeln_field(f, "type", &self.r#type)?; + writeln_field(f, "type", &self.ty)?; write_field(f, "ident", &self.ident) } } impl WithSpan for ScalarTypedParameter { fn with_span(self, span: Span) -> Self { - let Self { r#type, ident, .. } = self; - Self { - span, - r#type, - ident, - } + let Self { ty, ident, .. } = self; + Self { span, ty, ident } } } @@ -1281,26 +1293,22 @@ impl WithSpan for QuantumTypedParameter { #[derive(Clone, Debug)] pub struct ArrayTypedParameter { pub span: Span, - pub r#type: Box, + pub ty: Box, pub ident: Ident, } impl Display for ArrayTypedParameter { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { writeln_header(f, "ArrayTypedParameter", self.span)?; - writeln_field(f, "type", &self.r#type)?; + writeln_field(f, "type", &self.ty)?; write_field(f, "ident", &self.ident) } } impl WithSpan for ArrayTypedParameter { fn with_span(self, span: Span) -> Self { - let Self { r#type, ident, .. } = self; - Self { - span, - r#type, - ident, - } + let Self { ty, ident, .. } = self; + Self { span, ty, ident } } } @@ -1323,21 +1331,6 @@ impl Display for DefStmt { } } -#[derive(Clone, Debug)] -pub enum Operand { - Classical(ClassicalArgument), - Quantum(QuantumArgument), -} - -impl Display for Operand { - fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { - match self { - Operand::Classical(arg) => write!(f, "{arg}"), - Operand::Quantum(arg) => write!(f, "{arg}"), - } - } -} - #[derive(Clone, Debug)] pub struct ReturnStmt { pub span: Span, @@ -1369,7 +1362,7 @@ impl Display for WhileLoop { #[derive(Clone, Debug)] pub struct ForStmt { pub span: Span, - pub r#type: ScalarType, + pub ty: ScalarType, pub identifier: Identifier, pub set_declaration: Box, pub block: List, @@ -1378,7 +1371,7 @@ pub struct ForStmt { impl Display for ForStmt { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { writeln_header(f, "ForStmt", self.span)?; - writeln_field(f, "variable_type", &self.r#type)?; + writeln_field(f, "variable_type", &self.ty)?; writeln_field(f, "variable_name", &self.identifier)?; writeln_field(f, "iterable", &self.set_declaration)?; write_list_field(f, "block", &self.block) @@ -1550,14 +1543,14 @@ impl Display for FunctionCall { #[derive(Clone, Debug)] pub struct Cast { pub span: Span, - pub r#type: TypeDef, + pub ty: TypeDef, pub arg: Expr, } impl Display for Cast { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { writeln_header(f, "Cast", self.span)?; - writeln_field(f, "type", &self.r#type)?; + writeln_field(f, "type", &self.ty)?; write_field(f, "arg", &self.arg) } } @@ -1592,9 +1585,9 @@ impl Display for Lit { #[derive(Clone, Debug)] pub enum LiteralKind { Array(List), - Bitstring { value: BigInt, width: usize }, + Bitstring(BigInt, u32), Bool(bool), - Duration { value: f64, unit: TimeUnit }, + Duration(f64, TimeUnit), Float(f64), Imaginary(f64), Int(i64), @@ -1606,11 +1599,12 @@ impl Display for LiteralKind { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { match self { LiteralKind::Array(exprs) => write_list_field(f, "Array", exprs), - LiteralKind::Bitstring { value, width } => { + LiteralKind::Bitstring(value, width) => { + let width = *width as usize; write!(f, "Bitstring(\"{:0>width$}\")", value.to_str_radix(2)) } LiteralKind::Bool(b) => write!(f, "Bool({b:?})"), - LiteralKind::Duration { value, unit } => { + LiteralKind::Duration(value, unit) => { write!(f, "Duration({value:?}, {unit:?})") } LiteralKind::Float(value) => write!(f, "Float({value:?})"), @@ -1663,8 +1657,14 @@ pub enum IndexSetItem { /// This is needed to able to use `IndexSetItem` in the `seq` combinator. impl WithSpan for IndexSetItem { - fn with_span(self, _span: Span) -> Self { - self + fn with_span(self, span: Span) -> Self { + match self { + IndexSetItem::RangeDefinition(range) => { + IndexSetItem::RangeDefinition(range.with_span(span)) + } + IndexSetItem::Expr(expr) => IndexSetItem::Expr(expr.with_span(span)), + IndexSetItem::Err => IndexSetItem::Err, + } } } diff --git a/compiler/qsc_qasm3/src/ast/display_utils.rs b/compiler/qsc_qasm3/src/ast/display_utils.rs index 9088590bcf..37b29d3198 100644 --- a/compiler/qsc_qasm3/src/ast/display_utils.rs +++ b/compiler/qsc_qasm3/src/ast/display_utils.rs @@ -48,7 +48,7 @@ where /// Writes a list of elements to the given buffer or stream /// with an additional indentation level. -pub(super) fn write_indented_list<'write, 'itemref, 'item, T, I>( +pub(crate) fn write_indented_list<'write, 'itemref, 'item, T, I>( f: &'write mut impl Write, vals: I, ) -> fmt::Result @@ -70,19 +70,19 @@ where } /// Writes the name and span of a structure to the given buffer or stream. -pub(super) fn write_header(f: &mut impl Write, name: &str, span: super::Span) -> fmt::Result { +pub(crate) fn write_header(f: &mut impl Write, name: &str, span: super::Span) -> fmt::Result { write!(f, "{name} {span}:") } /// Writes the name and span of a structure to the given buffer or stream. /// Inserts a newline afterwards. -pub(super) fn writeln_header(f: &mut impl Write, name: &str, span: super::Span) -> fmt::Result { +pub(crate) fn writeln_header(f: &mut impl Write, name: &str, span: super::Span) -> fmt::Result { writeln!(f, "{name} {span}:") } /// Writes a field of a structure to the given buffer /// or stream with an additional indententation level. -pub(super) fn write_field( +pub(crate) fn write_field( f: &mut impl Write, field_name: &str, val: &T, @@ -94,7 +94,7 @@ pub(super) fn write_field( /// Writes a field of a structure to the given buffer /// or stream with an additional indententation level. /// Inserts a newline afterwards. -pub(super) fn writeln_field( +pub(crate) fn writeln_field( f: &mut impl Write, field_name: &str, val: &T, @@ -105,7 +105,7 @@ pub(super) fn writeln_field( /// Writes an optional field of a structure to the given buffer /// or stream with an additional indententation level. -pub(super) fn write_opt_field( +pub(crate) fn write_opt_field( f: &mut impl Write, field_name: &str, opt_val: Option<&T>, @@ -120,7 +120,7 @@ pub(super) fn write_opt_field( /// Writes an optional field of a structure to the given buffer /// or stream with an additional indententation level. /// Inserts a newline afterwards. -pub(super) fn writeln_opt_field( +pub(crate) fn writeln_opt_field( f: &mut impl Write, field_name: &str, opt_val: Option<&T>, @@ -132,7 +132,7 @@ pub(super) fn writeln_opt_field( /// Writes an field of a structure to the given buffer /// or stream with an additional indententation level. /// The field must be an iterable. -pub(super) fn write_list_field<'write, 'itemref, 'item, T, I>( +pub(crate) fn write_list_field<'write, 'itemref, 'item, T, I>( f: &mut impl Write, field_name: &str, vals: I, @@ -152,7 +152,7 @@ where /// or stream with an additional indententation level. /// The field must be an iterable. /// Inserts a newline afterwards. -pub(super) fn writeln_list_field<'write, 'itemref, 'item, T, I>( +pub(crate) fn writeln_list_field<'write, 'itemref, 'item, T, I>( f: &mut impl Write, field_name: &str, vals: I, @@ -169,7 +169,7 @@ where /// Writes an optional field of a structure to the given buffer /// or stream with an additional indententation level. /// The field must be an iterable. -pub(super) fn write_opt_list_field<'write, 'itemref, 'item, T, I>( +pub(crate) fn write_opt_list_field<'write, 'itemref, 'item, T, I>( f: &mut impl Write, field_name: &str, opt_vals: Option, @@ -191,7 +191,7 @@ where /// or stream with an additional indententation level. /// The field must be an iterable. /// Inserts a newline afterwards. -pub(super) fn writeln_opt_list_field<'write, 'itemref, 'item, T, I>( +pub(crate) fn writeln_opt_list_field<'write, 'itemref, 'item, T, I>( f: &mut impl Write, field_name: &str, opt_vals: Option, diff --git a/compiler/qsc_qasm3/src/ast_builder.rs b/compiler/qsc_qasm3/src/ast_builder.rs index 4269334548..5f63aef4bb 100644 --- a/compiler/qsc_qasm3/src/ast_builder.rs +++ b/compiler/qsc_qasm3/src/ast_builder.rs @@ -1151,6 +1151,7 @@ pub(crate) fn map_qsharp_type_to_ast_ty(output_ty: &crate::types::Type) -> Ty { let ty = map_qsharp_type_to_ast_ty(&crate::types::Type::Tuple(tys.clone())); wrap_array_ty_by_dims(dims, ty) } + crate::types::Type::Err => Ty::default(), } } diff --git a/compiler/qsc_qasm3/src/compile.rs b/compiler/qsc_qasm3/src/compile.rs index 0be78c1467..ca87db03fc 100644 --- a/compiler/qsc_qasm3/src/compile.rs +++ b/compiler/qsc_qasm3/src/compile.rs @@ -36,8 +36,8 @@ use crate::symbols::Symbol; use crate::symbols::SymbolTable; use crate::types::{get_indexed_type, get_qsharp_gate_name, GateModifier, QasmTypedExpr}; use crate::{ - CompilerConfig, OperationSignature, OutputSemantics, ProgramType, QubitSemantics, - SemanticError, SemanticErrorKind, + semantic::SemanticErrorKind, CompilerConfig, OperationSignature, OutputSemantics, ProgramType, + QubitSemantics, }; use ast::NodeId; @@ -2914,11 +2914,21 @@ impl QasmCompiler { Type::AngleArray(_) => todo!("AngleArray to Q# type"), Type::ComplexArray(_) => todo!("ComplexArray to Q# type"), Type::BoolArray(dims) => Some(crate::types::Type::BoolArray(dims.into(), is_const)), - Type::Gate(cargs, qargs) => Some(crate::types::Type::Callable( - crate::types::CallableKind::Operation, - *cargs, - *qargs, - )), + Type::Gate(cargs, qargs) => { + if let (Ok(cargs), Ok(qargs)) = (u32::try_from(*cargs), u32::try_from(*qargs)) { + Some(crate::types::Type::Callable( + crate::types::CallableKind::Operation, + cargs, + qargs, + )) + } else { + let message = format!( + "Gate with {cargs} control and {qargs} qubits has too many arguments" + ); + self.push_unsupported_error_message(message, node); + None + } + } Type::Range => Some(crate::types::Type::Range), Type::Set => todo!("Set to Q# type"), Type::Void => Some(crate::types::Type::Tuple(vec![])), @@ -3086,7 +3096,7 @@ impl QasmCompiler { }, Some(expr) => { let span = span_for_syntax_node(expr.syntax()); - let kind = SemanticErrorKind::DesignatorMustBeIntLiteral(span); + let kind = SemanticErrorKind::DesignatorMustBePositiveIntLiteral(span); self.push_semantic_error(kind); return None; } @@ -3297,7 +3307,7 @@ impl QasmCompiler { if let Ok(value) = value.try_into() { let value: i64 = value; let expr = build_lit_int_expr(value, span); - let ty = Type::Int(None, IsConst::True); + let ty = Type::UInt(None, IsConst::True); return Some(QasmTypedExpr { ty, expr }); } } @@ -3913,7 +3923,9 @@ impl QasmCompiler { node: &SyntaxNode, ) { let span = span_for_syntax_node(node); - let kind = crate::ErrorKind::Unimplemented(message.as_ref().to_string(), span); + let kind = crate::ErrorKind::Semantic(crate::semantic::Error( + SemanticErrorKind::Unimplemented(message.as_ref().to_string(), span), + )); let error = self.create_err(kind); self.errors.push(error); } @@ -3923,7 +3935,7 @@ impl QasmCompiler { pub fn push_missing_symbol_error>(&mut self, name: S, node: &SyntaxNode) { let span = span_for_syntax_node(node); let kind = SemanticErrorKind::UndefinedSymbol(name.as_ref().to_string(), span); - let kind = crate::ErrorKind::Semantic(SemanticError(kind)); + let kind = crate::ErrorKind::Semantic(crate::semantic::Error(kind)); let error = self.create_err(kind); self.errors.push(error); } @@ -3937,7 +3949,7 @@ impl QasmCompiler { /// Pushes a semantic error with the given kind. pub fn push_semantic_error(&mut self, kind: SemanticErrorKind) { - let kind = crate::ErrorKind::Semantic(SemanticError(kind)); + let kind = crate::ErrorKind::Semantic(crate::semantic::Error(kind)); let error = self.create_err(kind); self.errors.push(error); } @@ -3945,7 +3957,9 @@ impl QasmCompiler { /// Pushes an unsupported error with the supplied message. pub fn push_unsupported_error_message>(&mut self, message: S, node: &SyntaxNode) { let span = span_for_syntax_node(node); - let kind = crate::ErrorKind::NotSupported(message.as_ref().to_string(), span); + let kind = crate::ErrorKind::Semantic(crate::semantic::Error( + SemanticErrorKind::NotSupported(message.as_ref().to_string(), span), + )); let error = self.create_err(kind); self.errors.push(error); } @@ -3954,7 +3968,9 @@ impl QasmCompiler { pub fn push_calibration_error(&mut self, node: &SyntaxNode) { let span = span_for_syntax_node(node); let text = node.text().to_string(); - let kind = crate::ErrorKind::CalibrationsNotSupported(text, span); + let kind = crate::ErrorKind::Semantic(crate::semantic::Error( + SemanticErrorKind::CalibrationsNotSupported(text, span), + )); let error = self.create_err(kind); self.errors.push(error); } diff --git a/compiler/qsc_qasm3/src/lib.rs b/compiler/qsc_qasm3/src/lib.rs index 362a969a76..bc4e58ce4f 100644 --- a/compiler/qsc_qasm3/src/lib.rs +++ b/compiler/qsc_qasm3/src/lib.rs @@ -1,6 +1,9 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. +// while we work through the conversion, allow dead code to avoid warnings +#![allow(dead_code)] + mod angle; mod ast; mod ast_builder; @@ -14,6 +17,7 @@ mod oqasm_types; pub mod parse; pub mod parser; mod runtime; +pub mod semantic; mod symbols; mod types; @@ -58,18 +62,16 @@ impl Error { /// - Semantic errors /// - IO errors #[derive(Clone, Debug, Diagnostic, Eq, Error, PartialEq)] +#[error(transparent)] pub enum ErrorKind { - #[error("this statement is not yet handled during OpenQASM 3 import: {0}")] - Unimplemented(String, #[label] Span), - #[error("calibration statements are not supported: {0}")] - CalibrationsNotSupported(String, #[label] Span), - #[error("{0} are not supported.")] - NotSupported(String, #[label] Span), #[error("QASM3 Parse Error: {0}")] Parse(String, #[label] Span), #[error(transparent)] #[diagnostic(transparent)] - Semantic(#[from] crate::SemanticError), + Parser(#[from] crate::parser::Error), + #[error(transparent)] + #[diagnostic(transparent)] + Semantic(#[from] crate::semantic::Error), #[error("QASM3 Parse Error: Not Found {0}")] NotFound(String), #[error("IO Error: {0}")] @@ -79,300 +81,15 @@ pub enum ErrorKind { impl ErrorKind { fn with_offset(self, offset: u32) -> Self { match self { - ErrorKind::Unimplemented(error, span) => Self::Unimplemented(error, span + offset), - ErrorKind::CalibrationsNotSupported(error, span) => { - Self::CalibrationsNotSupported(error, span + offset) - } - ErrorKind::NotSupported(error, span) => Self::NotSupported(error, span + offset), ErrorKind::Parse(error, span) => Self::Parse(error, span + offset), - ErrorKind::Semantic(error) => ErrorKind::Semantic(error.with_offset(offset)), + ErrorKind::Parser(error) => Self::Parser(error.with_offset(offset)), + ErrorKind::Semantic(error) => Self::Semantic(error.with_offset(offset)), ErrorKind::NotFound(error) => Self::NotFound(error), ErrorKind::IO(error) => Self::IO(error), } } } -#[derive(Clone, Debug, Diagnostic, Eq, Error, PartialEq)] -#[error(transparent)] -#[diagnostic(transparent)] -pub struct SemanticError(SemanticErrorKind); - -impl SemanticError { - #[must_use] - pub fn with_offset(self, offset: u32) -> Self { - Self(self.0.with_offset(offset)) - } -} - -/// Represents the kind of semantic error that occurred during compilation of a QASM file(s). -/// For the most part, these errors are fatal and prevent compilation and are -/// safety checks to ensure that the QASM code is valid. -/// -/// We can't use the semantics library for this: -/// - it is unsafe to use (heavy use of panic and unwrap) -/// - it is missing many language features -#[derive(Clone, Debug, Diagnostic, Eq, Error, PartialEq)] -enum SemanticErrorKind { - #[error("Annotation missing target statement.")] - #[diagnostic(code("Qsc.Qasm3.Compile.AnnotationWithoutStatement"))] - AnnotationWithoutStatement(#[label] Span), - #[error("Cannot alias type {0}. Only qubit and qubit[] can be aliased.")] - #[diagnostic(code("Qsc.Qasm3.Compile.CannotAliasType"))] - CannotAliasType(String, Span), - #[error("Cannot apply operator {0} to types {1} and {2}.")] - #[diagnostic(code("Qsc.Qasm3.Compile.CannotApplyOperatorToTypes"))] - CannotApplyOperatorToTypes(String, String, String, #[label] Span), - #[error("Cannot assign a value of {0} type to a classical variable of {1} type.")] - #[diagnostic(code("Qsc.Qasm3.Compile.CannotAssignToType"))] - CannotAssignToType(String, String, #[label] Span), - #[error("Cannot call a gate that is not a gate.")] - #[diagnostic(code("Qsc.Qasm3.Compile.CannotCallNonGate"))] - CannotCallNonGate(#[label] Span), - #[error("Cannot cast expression of type {0} to type {1}")] - #[diagnostic(code("Qsc.Qasm3.Compile.CannotCast"))] - CannotCast(String, String, #[label] Span), - #[error("Cannot index variables of type {0}")] - #[diagnostic(code("Qsc.Qasm3.Compile.CannotIndexType"))] - CannotIndexType(String, #[label] Span), - #[error("Cannot update const variable {0}")] - #[diagnostic(help("mutable variables must be declared without the keyword `const`."))] - #[diagnostic(code("Qsc.Qasm3.Compile.CannotUpdateConstVariable"))] - CannotUpdateConstVariable(String, #[label] Span), - #[error("Cannot cast expression of type {0} to type {1} as it would cause truncation.")] - #[diagnostic(code("Qsc.Qasm3.Compile.CastWouldCauseTruncation"))] - CastWouldCauseTruncation(String, String, #[label] Span), - #[error("Complex numbers in assignment binary expressions are not yet supported.")] - #[diagnostic(code("Qsc.Qasm3.Compile.ComplexBinaryAssignment"))] - ComplexBinaryAssignment(#[label] Span), - #[error("Designator must be a literal integer.")] - #[diagnostic(code("Qsc.Qasm3.Compile.DesignatorMustBeIntLiteral"))] - DesignatorMustBeIntLiteral(#[label] Span), - #[error("Failed to compile all expressions in expression list.")] - #[diagnostic(code("Qsc.Qasm3.Compile.FailedToCompileExpressionList"))] - FailedToCompileExpressionList(#[label] Span), - #[error("For iterable must have a set expression, range expression, or iterable expression.")] - #[diagnostic(code("Qsc.Qasm3.Compile.ForIterableInvalidExpression"))] - ForIterableInvalidExpression(#[label] Span), - #[error("For statements must have a body or statement.")] - #[diagnostic(code("Qsc.Qasm3.Compile.ForStatementsMustHaveABodyOrStatement"))] - ForStatementsMustHaveABodyOrStatement(#[label] Span), - #[error("If statement missing {0} expression.")] - #[diagnostic(code("Qsc.Qasm3.Compile.IfStmtMissingExpression"))] - IfStmtMissingExpression(String, #[label] Span), - #[error("include {0} could not be found.")] - #[diagnostic(code("Qsc.Qasm3.Compile.IncludeNotFound"))] - IncludeNotFound(String, #[label] Span), - #[error("include {0} must be declared in global scope.")] - #[diagnostic(code("Qsc.Qasm3.Compile.IncludeNotInGlobalScope"))] - IncludeNotInGlobalScope(String, #[label] Span), - #[error("include {0} must be declared in global scope.")] - #[diagnostic(code("Qsc.Qasm3.Compile.IncludeStatementMissingPath"))] - IncludeStatementMissingPath(#[label] Span), - #[error("Indexed must be a single expression.")] - #[diagnostic(code("Qsc.Qasm3.Compile.IndexMustBeSingleExpr"))] - IndexMustBeSingleExpr(#[label] Span), - #[error("Annotations only valid on gate definitions.")] - #[diagnostic(code("Qsc.Qasm3.Compile.InvalidAnnotationTarget"))] - InvalidAnnotationTarget(Span), - #[error("Assigning {0} values to {1} must be in a range that be converted to {1}.")] - InvalidCastValueRange(String, String, #[label] Span), - #[error("Gate operands other than qubits or qubit arrays are not supported.")] - #[diagnostic(code("Qsc.Qasm3.Compile.InvalidGateOperand"))] - InvalidGateOperand(#[label] Span), - #[error("Control counts must be integer literals.")] - #[diagnostic(code("Qsc.Qasm3.Compile.InvalidControlCount"))] - InvalidControlCount(#[label] Span), - #[error("Gate operands other than qubit arrays are not supported.")] - #[diagnostic(code("Qsc.Qasm3.Compile.InvalidIndexedGateOperand"))] - InvalidIndexedGateOperand(#[label] Span), - #[error("Gate expects {0} classical arguments, but {1} were provided.")] - #[diagnostic(code("Qsc.Qasm3.Compile.InvalidNumberOfClassicalArgs"))] - InvalidNumberOfClassicalArgs(usize, usize, #[label] Span), - #[error("Gate expects {0} qubit arguments, but {1} were provided.")] - #[diagnostic(code("Qsc.Qasm3.Compile.InvalidNumberOfQubitArgs"))] - InvalidNumberOfQubitArgs(usize, usize, #[label] Span), - #[error("Measure statements must have a name.")] - #[diagnostic(code("Qsc.Qasm3.Compile.MeasureExpressionsMustHaveName"))] - MeasureExpressionsMustHaveName(#[label] Span), - #[error("Measure statements must have a gate operand name.")] - #[diagnostic(code("Qsc.Qasm3.Compile.MeasureExpressionsMustHaveGateOperand"))] - MeasureExpressionsMustHaveGateOperand(#[label] Span), - #[error("Control counts must be postitive integers.")] - #[diagnostic(code("Qsc.Qasm3.Compile.NegativeControlCount"))] - NegativeControlCount(#[label] Span), - #[error("The operator {0} is not valid with lhs {1} and rhs {2}.")] - #[diagnostic(code("Qsc.Qasm3.Compile.OperatorNotSupportedForTypes"))] - OperatorNotSupportedForTypes(String, String, String, #[label] Span), - #[error("Pow gate modifiers must have an exponent.")] - #[diagnostic(code("Qsc.Qasm3.Compile.PowModifierMustHaveExponent"))] - PowModifierMustHaveExponent(#[label] Span), - #[error("Qiskit circuits must have output registers.")] - #[diagnostic(code("Qsc.Qasm3.Compile.QiskitEntryPointMissingOutput"))] - QiskitEntryPointMissingOutput(#[label] Span), - #[error("Quantum declarations must be done in global scope.")] - #[diagnostic(code("Qsc.Qasm3.Compile.QuantumDeclarationInNonGlobalScope"))] - QuantumDeclarationInNonGlobalScope(#[label] Span), - #[error("Quantum typed values cannot be used in binary expressions.")] - #[diagnostic(code("Qsc.Qasm3.Compile.QuantumTypesInBinaryExpression"))] - QuantumTypesInBinaryExpression(#[label] Span), - #[error("Range expressions must have a start.")] - #[diagnostic(code("Qsc.Qasm3.Compile.RangeExpressionsMustHaveStart"))] - RangeExpressionsMustHaveStart(#[label] Span), - #[error("Range expressions must have a stop.")] - #[diagnostic(code("Qsc.Qasm3.Compile.RangeExpressionsMustHaveStop"))] - RangeExpressionsMustHaveStop(#[label] Span), - #[error("Redefined symbol: {0}.")] - #[diagnostic(code("Qsc.Qasm3.Compile.RedefinedSymbol"))] - RedefinedSymbol(String, #[label] Span), - #[error("Reset expression must have a gate operand.")] - #[diagnostic(code("Qsc.Qasm3.Compile.ResetExpressionMustHaveGateOperand"))] - ResetExpressionMustHaveGateOperand(#[label] Span), - #[error("Reset expression must have a name.")] - #[diagnostic(code("Qsc.Qasm3.Compile.ResetExpressionMustHaveName"))] - ResetExpressionMustHaveName(#[label] Span), - #[error("Return statements are only allowed within subroutines.")] - #[diagnostic(code("Qsc.Qasm3.Compile.ReturnNotInSubroutine"))] - ReturnNotInSubroutine(#[label] Span), - #[error("Too many controls specified.")] - #[diagnostic(code("Qsc.Qasm3.Compile.TooManyControls"))] - TooManyControls(#[label] Span), - #[error("Types differ by dimensions and are incompatible.")] - #[diagnostic(code("Qsc.Qasm3.Compile.TypeRankError"))] - TypeRankError(#[label] Span), - #[error("Undefined symbol: {0}.")] - #[diagnostic(code("Qsc.Qasm3.Compile.UndefinedSymbol"))] - UndefinedSymbol(String, #[label] Span), - #[error("Unexpected parser error: {0}.")] - #[diagnostic(code("Qsc.Qasm3.Compile.UnexpectedParserError"))] - UnexpectedParserError(String, #[label] Span), - #[error("Unexpected annotation: {0}.")] - #[diagnostic(code("Qsc.Qasm3.Compile.UnknownAnnotation"))] - UnknownAnnotation(String, #[label] Span), - #[error("Undefined symbol: {0}.")] - #[diagnostic(code("Qsc.Qasm3.Compile.UnknownIndexedOperatorKind"))] - UnknownIndexedOperatorKind(#[label] Span), - #[error("While statement missing {0} expression.")] - #[diagnostic(code("Qsc.Qasm3.Compile.WhileStmtMissingExpression"))] - WhileStmtMissingExpression(String, Span), -} - -impl SemanticErrorKind { - /// The semantic errors are reported with the span of the syntax that caused the error. - /// This offset is relative to the start of the file in which the error occurred. - /// This method is used to adjust the span of the error to be relative to where the - /// error was reported in the entire compilation unit as part of the source map. - #[allow(clippy::too_many_lines)] - fn with_offset(self, offset: u32) -> Self { - match self { - Self::AnnotationWithoutStatement(span) => { - Self::AnnotationWithoutStatement(span + offset) - } - Self::CannotCast(lhs, rhs, span) => Self::CannotCast(lhs, rhs, span + offset), - Self::CastWouldCauseTruncation(lhs, rhs, span) => { - Self::CastWouldCauseTruncation(lhs, rhs, span + offset) - } - Self::CannotAliasType(name, span) => Self::CannotAliasType(name, span + offset), - Self::CannotApplyOperatorToTypes(op, lhs, rhs, span) => { - Self::CannotApplyOperatorToTypes(op, lhs, rhs, span + offset) - } - Self::CannotAssignToType(lhs, rhs, span) => { - Self::CannotAssignToType(lhs, rhs, span + offset) - } - Self::CannotCallNonGate(span) => Self::CannotCallNonGate(span + offset), - Self::CannotIndexType(name, span) => Self::CannotIndexType(name, span + offset), - Self::CannotUpdateConstVariable(name, span) => { - Self::CannotUpdateConstVariable(name, span + offset) - } - Self::ComplexBinaryAssignment(span) => Self::ComplexBinaryAssignment(span + offset), - Self::DesignatorMustBeIntLiteral(span) => { - Self::DesignatorMustBeIntLiteral(span + offset) - } - Self::FailedToCompileExpressionList(span) => { - Self::FailedToCompileExpressionList(span + offset) - } - Self::ForIterableInvalidExpression(span) => { - Self::ForIterableInvalidExpression(span + offset) - } - Self::ForStatementsMustHaveABodyOrStatement(span) => { - Self::ForStatementsMustHaveABodyOrStatement(span + offset) - } - Self::IfStmtMissingExpression(name, span) => { - Self::IfStmtMissingExpression(name, span + offset) - } - Self::IncludeNotFound(name, span) => Self::IncludeNotFound(name, span + offset), - Self::IncludeNotInGlobalScope(name, span) => { - Self::IncludeNotInGlobalScope(name, span + offset) - } - Self::IncludeStatementMissingPath(span) => { - Self::IncludeStatementMissingPath(span + offset) - } - Self::IndexMustBeSingleExpr(span) => Self::IndexMustBeSingleExpr(span + offset), - Self::InvalidAnnotationTarget(span) => Self::InvalidAnnotationTarget(span + offset), - Self::InvalidControlCount(span) => Self::InvalidControlCount(span + offset), - Self::InvalidNumberOfClassicalArgs(expected, actual, span) => { - Self::InvalidNumberOfClassicalArgs(expected, actual, span + offset) - } - Self::InvalidNumberOfQubitArgs(expected, actual, span) => { - Self::InvalidNumberOfQubitArgs(expected, actual, span + offset) - } - Self::InvalidCastValueRange(lhs, rhs, span) => { - Self::InvalidCastValueRange(lhs, rhs, span + offset) - } - Self::InvalidGateOperand(span) => Self::InvalidGateOperand(span + offset), - Self::InvalidIndexedGateOperand(span) => Self::InvalidIndexedGateOperand(span + offset), - Self::MeasureExpressionsMustHaveGateOperand(span) => { - Self::MeasureExpressionsMustHaveGateOperand(span + offset) - } - Self::MeasureExpressionsMustHaveName(span) => { - Self::MeasureExpressionsMustHaveName(span + offset) - } - Self::NegativeControlCount(span) => Self::NegativeControlCount(span + offset), - Self::OperatorNotSupportedForTypes(op, lhs, rhs, span) => { - Self::OperatorNotSupportedForTypes(op, lhs, rhs, span + offset) - } - Self::PowModifierMustHaveExponent(span) => { - Self::PowModifierMustHaveExponent(span + offset) - } - Self::QiskitEntryPointMissingOutput(span) => { - Self::QiskitEntryPointMissingOutput(span + offset) - } - Self::QuantumDeclarationInNonGlobalScope(span) => { - Self::QuantumDeclarationInNonGlobalScope(span + offset) - } - Self::QuantumTypesInBinaryExpression(span) => { - Self::QuantumTypesInBinaryExpression(span + offset) - } - Self::RangeExpressionsMustHaveStart(span) => { - Self::RangeExpressionsMustHaveStart(span + offset) - } - Self::RangeExpressionsMustHaveStop(span) => { - Self::RangeExpressionsMustHaveStop(span + offset) - } - Self::RedefinedSymbol(name, span) => Self::RedefinedSymbol(name, span + offset), - Self::ResetExpressionMustHaveGateOperand(span) => { - Self::ResetExpressionMustHaveGateOperand(span + offset) - } - Self::ResetExpressionMustHaveName(span) => { - Self::ResetExpressionMustHaveName(span + offset) - } - Self::ReturnNotInSubroutine(span) => Self::ReturnNotInSubroutine(span + offset), - Self::TooManyControls(span) => Self::TooManyControls(span + offset), - Self::TypeRankError(span) => Self::TypeRankError(span + offset), - Self::UndefinedSymbol(name, span) => Self::UndefinedSymbol(name, span + offset), - Self::UnexpectedParserError(error, span) => { - Self::UnexpectedParserError(error, span + offset) - } - Self::UnknownAnnotation(name, span) => Self::UnknownAnnotation(name, span + offset), - Self::UnknownIndexedOperatorKind(span) => { - Self::UnknownIndexedOperatorKind(span + offset) - } - Self::WhileStmtMissingExpression(name, span) => { - Self::WhileStmtMissingExpression(name, span + offset) - } - } - } -} - /// Qubit semantics differ between Q# and Qiskit. This enum is used to /// specify which semantics to use when compiling QASM to Q#. /// diff --git a/compiler/qsc_qasm3/src/oqasm_helpers.rs b/compiler/qsc_qasm3/src/oqasm_helpers.rs index bbac93ad6e..344139d49a 100644 --- a/compiler/qsc_qasm3/src/oqasm_helpers.rs +++ b/compiler/qsc_qasm3/src/oqasm_helpers.rs @@ -50,9 +50,11 @@ pub(crate) fn safe_u128_to_f64(value: u128) -> Option { } } +/// `i64` is 64 bits wide, but `f64`'s mantissa is only 53 bits wide pub(crate) fn safe_i64_to_f64(value: i64) -> Option { - #[allow(clippy::cast_possible_truncation)] - if value <= f64::MAX as i64 { + const MAX_EXACT_INT: i64 = 2i64.pow(f64::MANTISSA_DIGITS); + const MAX_EXACT_NEG_INT: i64 = -(2i64.pow(f64::MANTISSA_DIGITS)); + if (MAX_EXACT_NEG_INT..=MAX_EXACT_INT).contains(&value) { #[allow(clippy::cast_precision_loss)] Some(value as f64) } else { @@ -61,9 +63,8 @@ pub(crate) fn safe_i64_to_f64(value: i64) -> Option { } pub(crate) fn safe_u64_to_f64(value: u64) -> Option { - #[allow(clippy::cast_possible_truncation)] - #[allow(clippy::cast_sign_loss)] - if value <= f64::MAX as u64 { + const MAX_EXACT_UINT: u64 = 2u64.pow(f64::MANTISSA_DIGITS); + if value <= MAX_EXACT_UINT { #[allow(clippy::cast_precision_loss)] Some(value as f64) } else { diff --git a/compiler/qsc_qasm3/src/parser.rs b/compiler/qsc_qasm3/src/parser.rs index 9c74e68e40..bf9c2a83aa 100644 --- a/compiler/qsc_qasm3/src/parser.rs +++ b/compiler/qsc_qasm3/src/parser.rs @@ -14,6 +14,7 @@ pub(crate) mod tests; mod completion; mod error; +pub use error::Error; mod expr; mod prgm; mod prim; @@ -37,39 +38,40 @@ impl QasmParseResult { self.source.has_errors() } - pub fn all_errors(&self) -> Vec> { + pub fn all_errors(&self) -> Vec> { let mut self_errors = self.errors(); let include_errors = self .source .includes() .iter() .flat_map(QasmSource::all_errors) - .map(|e| self.map_error(e)); + .map(|e| self.map_error(e)) + .collect::>(); self_errors.extend(include_errors); self_errors } #[must_use] - pub fn errors(&self) -> Vec> { + pub fn errors(&self) -> Vec> { self.source .errors() .iter() .map(|e| self.map_error(e.clone())) - .collect() + .collect::>() } - fn map_error( - &self, - error: crate::parser::error::Error, - ) -> WithSource { + fn map_error(&self, error: Error) -> WithSource { let path = self.source.path().display().to_string(); let source = self.source_map.find_by_name(&path); let offset = source.map_or(0, |source| source.offset); let offset_error = error.with_offset(offset); - WithSource::from_map(&self.source_map, offset_error) + WithSource::from_map( + &self.source_map, + crate::Error(crate::ErrorKind::Parser(offset_error)), + ) } } @@ -101,9 +103,7 @@ fn create_source_map(source: &QasmSource) -> SourceMap { for include in source.includes() { collect_source_files(include, &mut files); } - // Map the main source file to the entry point expression - // This may be incorrect, but it's the best we can do for now. - SourceMap::new(files, Some(Arc::from(source.source()))) + SourceMap::new(files, None) } /// Recursively collect all source files from the includes @@ -128,7 +128,7 @@ pub struct QasmSource { /// The parsed AST of the source file or any parse errors. program: Program, /// Any parse errors that occurred. - errors: Vec, + errors: Vec, /// Any included files that were resolved. /// Note that this is a recursive structure. included: Vec, @@ -139,7 +139,7 @@ impl QasmSource { source: T, file_path: P, program: Program, - errors: Vec, + errors: Vec, included: Vec, ) -> QasmSource { QasmSource { @@ -160,7 +160,7 @@ impl QasmSource { } #[must_use] - pub fn all_errors(&self) -> Vec { + pub fn all_errors(&self) -> Vec { let mut self_errors = self.errors(); let include_errors = self.includes().iter().flat_map(QasmSource::all_errors); self_errors.extend(include_errors); @@ -183,7 +183,7 @@ impl QasmSource { } #[must_use] - pub fn errors(&self) -> Vec { + pub fn errors(&self) -> Vec { self.errors.clone() } @@ -222,7 +222,7 @@ where fn parse_source_and_includes, R>( source: P, resolver: &R, -) -> miette::Result<(Program, Vec, Vec)> +) -> miette::Result<(Program, Vec, Vec)> where R: SourceResolver, { @@ -258,7 +258,7 @@ pub(crate) trait Parser: FnMut(&mut ParserContext) -> Result {} impl Result> Parser for F {} -pub fn parse(input: &str) -> Result<(Program, Vec)> { +pub fn parse(input: &str) -> Result<(Program, Vec)> { let mut scanner = ParserContext::new(input); let program = prgm::parse(&mut scanner)?; Ok((program, scanner.into_errors())) diff --git a/compiler/qsc_qasm3/src/parser/completion.rs b/compiler/qsc_qasm3/src/parser/completion.rs index 17b37d8507..ae8f8e57e9 100644 --- a/compiler/qsc_qasm3/src/parser/completion.rs +++ b/compiler/qsc_qasm3/src/parser/completion.rs @@ -1,9 +1,6 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. -// while we work through the conversion, allow dead code to avoid warnings -#![allow(dead_code)] - pub(crate) mod collector; #[cfg(test)] mod tests; diff --git a/compiler/qsc_qasm3/src/parser/expr.rs b/compiler/qsc_qasm3/src/parser/expr.rs index 3e19032516..1cc3bc7b4b 100644 --- a/compiler/qsc_qasm3/src/parser/expr.rs +++ b/compiler/qsc_qasm3/src/parser/expr.rs @@ -1,9 +1,6 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. -// while we work through the conversion, allow dead code to avoid warnings -#![allow(dead_code)] - //! Expression parsing makes use of Pratt parsing (or “top-down operator-precedence parsing”) to handle //! relative precedence of operators. @@ -280,18 +277,22 @@ fn lit_token(lexeme: &str, token: Token) -> Result> { } Literal::Bitstring => { let lexeme = shorten(1, 1, lexeme); - let width = lexeme - .to_string() - .chars() - .filter(|c| *c == '0' || *c == '1') - .count(); + let width = u32::try_from( + lexeme + .to_string() + .chars() + .filter(|c| *c == '0' || *c == '1') + .count(), + ) + .map_err(|_| Error::new(ErrorKind::Lit("bitstring", token.span)))?; + // parse it to validate the bitstring let value = BigInt::from_str_radix(lexeme, 2) .map_err(|_| Error::new(ErrorKind::Lit("bitstring", token.span)))?; Ok(Some(Lit { span: token.span, - kind: LiteralKind::Bitstring { value, width }, + kind: LiteralKind::Bitstring(value, width), })) } Literal::Imaginary => { @@ -429,7 +430,7 @@ fn timing_literal(lexeme: &str, token: Token, kind: TimingLiteralKind) -> Result Ok(Some(Lit { span: token.span, - kind: LiteralKind::Duration { value, unit }, + kind: LiteralKind::Duration(value, unit), })) } @@ -471,7 +472,7 @@ fn cast_op(s: &mut ParserContext, r#type: TypeDef) -> Result { recovering_token(s, TokenKind::Close(Delim::Paren)); Ok(ExprKind::Cast(Cast { span: s.span(lo), - r#type, + ty: r#type, arg, })) } diff --git a/compiler/qsc_qasm3/src/parser/prim.rs b/compiler/qsc_qasm3/src/parser/prim.rs index c3062c9bf4..44e3ef913b 100644 --- a/compiler/qsc_qasm3/src/parser/prim.rs +++ b/compiler/qsc_qasm3/src/parser/prim.rs @@ -1,9 +1,6 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. -// while we work through the conversion, allow dead code to avoid warnings -#![allow(dead_code)] - #[cfg(test)] pub(crate) mod tests; diff --git a/compiler/qsc_qasm3/src/parser/scan.rs b/compiler/qsc_qasm3/src/parser/scan.rs index 775c20373f..50fbd3b45f 100644 --- a/compiler/qsc_qasm3/src/parser/scan.rs +++ b/compiler/qsc_qasm3/src/parser/scan.rs @@ -7,8 +7,8 @@ use crate::{ }; use qsc_data_structures::span::Span; -use super::error::Error; use super::error::ErrorKind; +use super::Error; #[derive(Debug)] pub(super) struct NoBarrierError; @@ -106,7 +106,7 @@ impl<'a> ParserContext<'a> { } } - pub(super) fn into_errors(self) -> Vec { + pub(super) fn into_errors(self) -> Vec { self.scanner.into_errors() } diff --git a/compiler/qsc_qasm3/src/parser/stmt.rs b/compiler/qsc_qasm3/src/parser/stmt.rs index 2dfa716727..680c192d73 100644 --- a/compiler/qsc_qasm3/src/parser/stmt.rs +++ b/compiler/qsc_qasm3/src/parser/stmt.rs @@ -70,7 +70,7 @@ pub(super) fn parse(s: &mut ParserContext) -> Result> { span: s.span(ty.span().lo), kind: Box::new(ExprKind::Cast(Cast { span: s.span(ty.span().lo), - r#type: ty, + ty, arg, })), }; @@ -362,7 +362,7 @@ fn arg_def(s: &mut ParserContext) -> Result { let ident = prim::ident(s)?; TypedParameter::Scalar(ScalarTypedParameter { span: s.span(lo), - r#type: Box::new(ty), + ty: Box::new(ty), ident, }) } else if let Ok(size) = qubit_type(s) { @@ -382,7 +382,7 @@ fn arg_def(s: &mut ParserContext) -> Result { }; TypedParameter::Scalar(ScalarTypedParameter { span: s.span(lo), - r#type: Box::new(ty), + ty: Box::new(ty), ident, }) } else if let Ok((ident, size)) = qreg_type(s) { @@ -395,7 +395,7 @@ fn arg_def(s: &mut ParserContext) -> Result { let ident = prim::ident(s)?; TypedParameter::ArrayReference(ArrayTypedParameter { span: s.span(lo), - r#type: Box::new(ty), + ty: Box::new(ty), ident, }) } else { @@ -527,7 +527,7 @@ fn parse_io_decl(s: &mut ParserContext) -> Result { let decl = IODeclaration { span: s.span(lo), io_identifier: kind, - r#type: ty, + ty, ident, }; Ok(StmtKind::IODeclaration(decl)) @@ -562,7 +562,7 @@ fn parse_non_constant_classical_decl( recovering_semi(s); let decl = ClassicalDeclarationStmt { span: s.span(lo), - r#type: Box::new(ty), + ty: Box::new(ty), identifier, init_expr, }; @@ -580,7 +580,7 @@ fn parse_constant_classical_decl(s: &mut ParserContext) -> Result { recovering_semi(s); let decl = ConstantDeclStmt { span: s.span(lo), - r#type: ty, + ty, identifier, init_expr, }; @@ -675,7 +675,7 @@ fn creg_decl(s: &mut ParserContext) -> Result { recovering_semi(s); Ok(StmtKind::ClassicalDecl(ClassicalDeclarationStmt { span: s.span(lo), - r#type: Box::new(TypeDef::Scalar(ScalarType { + ty: Box::new(TypeDef::Scalar(ScalarType { span: s.span(lo), kind: ScalarTypeKind::Bit(BitType { size, @@ -1039,7 +1039,7 @@ fn for_loop_iterable_expr(s: &mut ParserContext) -> Result { pub fn parse_for_loop(s: &mut ParserContext) -> Result { let lo = s.peek().span.lo; token(s, TokenKind::Keyword(Keyword::For))?; - let r#type = scalar_type(s)?; + let ty = scalar_type(s)?; let identifier = Identifier::Ident(Box::new(prim::ident(s)?)); token(s, TokenKind::Keyword(Keyword::In))?; let set_declaration = Box::new(for_loop_iterable_expr(s)?); @@ -1047,7 +1047,7 @@ pub fn parse_for_loop(s: &mut ParserContext) -> Result { Ok(ForStmt { span: s.span(lo), - r#type, + ty, identifier, set_declaration, block, diff --git a/compiler/qsc_qasm3/src/runtime.rs b/compiler/qsc_qasm3/src/runtime.rs index 2b0080d5fd..0f1b0a4541 100644 --- a/compiler/qsc_qasm3/src/runtime.rs +++ b/compiler/qsc_qasm3/src/runtime.rs @@ -1,16 +1,54 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. +//! Q# doesn't support gphase and U gates which are used in QASM3. +//! We provide the implementation of these gates here so that QASM3 +//! users can still define custom gates in terms of these operations. +//! +//! We also provide runtime functions that are used in the generated AST. +//! These functions are not part of the QASM3 standard, but are used to implement +//! utility fuctions that would be cumbersome to implement building the AST +//! directly. +//! +//! Finally, we provide QASM3 runtime functions mapped to their Q# counterparts. + use bitflags::bitflags; use qsc_ast::ast::{Stmt, TopLevelNode}; use qsc_data_structures::language_features::LanguageFeatures; -/// Runtime functions that are used in the generated AST. -/// These functions are not part of the QASM3 standard, but are used to implement -/// utility fuctions that would be cumbersome to implement building the AST -/// directly. -/// +/// Implement the `gphase` operation for QASM3. +const GPHASE_GATE: &str = " +operation gphase(theta : Double) : Unit is Adj + Ctl { + body ... { + Exp([], theta, []) + } + adjoint auto; + controlled auto; + controlled adjoint auto; +} +"; + +/// Implement the `U` operation for QASM3. +/// We need to apply a global phase, but rather than require gphase to be called, +/// we can use the `R` gate since we have a qubit parameter (though it is ignored). +/// `R(PauliI, 4. * PI() - (lambda + phi + theta), qubit);` +/// Since `U` is periodic to `2pi`, we can use the following: +/// `R(PauliI, -(lambda + phi + theta), qubit);` +const U_GATE: &str = " +operation U(theta : Double, phi : Double, lambda : Double, qubit : Qubit) : Unit is Adj + Ctl { + body ... { + Rz(lambda, qubit); + Ry(theta, qubit); + Rz(phi, qubit); + R(PauliI, -lambda - phi - theta, qubit); + } + adjoint auto; + controlled auto; + controlled adjoint auto; +} +"; + /// The POW function is used to implement the `pow` modifier in QASM3 for integers. const POW: &str = " operation __Pow__<'T>(N: Int, op: ('T => Unit is Adj), target : 'T) : Unit is Adj { @@ -144,6 +182,8 @@ bitflags! { /// IntAsResultArray requires BoolAsResult to be included. const IntAsResultArrayBE = 0b1_000_000_000 | 0b100; const ResultArrayAsIntBE = 0b10_000_000_000; + const Gphase = 0b100_000_000_000; + const U = 0b1_000_000_000_000; } } @@ -197,6 +237,17 @@ pub(crate) fn get_result_array_as_int_be_decl() -> Stmt { parse_stmt(RESULT_ARRAY_AS_INT_BE) } +pub(crate) fn get_gphase_decl() -> Stmt { + parse_stmt(GPHASE_GATE) +} + +pub(crate) fn get_u_decl() -> Stmt { + parse_stmt(U_GATE) +} + +/// As we are trying to add statements to the AST, we parse the Q# implementations +/// of the runtime functions and return the AST nodes. This saves us a lot of time +/// in writing the AST nodes manually. fn parse_stmt(name: &str) -> Stmt { let (nodes, errors) = qsc_parse::top_level_nodes(name, LanguageFeatures::default()); assert!(errors.is_empty(), "Failed to parse POW: {errors:?}"); @@ -213,6 +264,7 @@ fn parse_stmt(name: &str) -> Stmt { } } +/// Get the runtime function declarations for the given runtime functions. pub(crate) fn get_runtime_function_decls(runtime: RuntimeFunctions) -> Vec { let mut stmts = vec![]; if runtime.contains(RuntimeFunctions::Pow) { @@ -259,5 +311,13 @@ pub(crate) fn get_runtime_function_decls(runtime: RuntimeFunctions) -> Vec let stmt = crate::runtime::get_result_array_as_int_be_decl(); stmts.push(stmt); } + if runtime.contains(RuntimeFunctions::Gphase) { + let stmt = crate::runtime::get_gphase_decl(); + stmts.push(stmt); + } + if runtime.contains(RuntimeFunctions::U) { + let stmt = crate::runtime::get_u_decl(); + stmts.push(stmt); + } stmts } diff --git a/compiler/qsc_qasm3/src/semantic.rs b/compiler/qsc_qasm3/src/semantic.rs new file mode 100644 index 0000000000..1f676049b2 --- /dev/null +++ b/compiler/qsc_qasm3/src/semantic.rs @@ -0,0 +1,131 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +use crate::io::SourceResolver; +use crate::parser::QasmSource; + +use qsc_frontend::compile::SourceMap; +use qsc_frontend::error::WithSource; +use symbols::SymbolTable; + +use std::path::Path; + +mod ast; +pub mod error; +mod lowerer; +pub use error::Error; +pub use error::SemanticErrorKind; +pub mod symbols; +pub mod types; + +#[cfg(test)] +pub(crate) mod tests; + +pub struct QasmSemanticParseResult { + pub source: QasmSource, + pub source_map: SourceMap, + pub symbols: self::symbols::SymbolTable, + pub program: self::ast::Program, + pub errors: Vec>, +} + +impl QasmSemanticParseResult { + #[must_use] + pub fn has_errors(&self) -> bool { + self.has_syntax_errors() || self.has_semantic_errors() + } + + #[must_use] + pub fn has_syntax_errors(&self) -> bool { + self.source.has_errors() + } + + #[must_use] + pub fn has_semantic_errors(&self) -> bool { + !self.errors.is_empty() + } + + pub fn parse_errors(&self) -> Vec> { + let mut self_errors = self + .source + .errors() + .iter() + .map(|e| self.map_parse_error(e.clone())) + .collect::>(); + let include_errors = self + .source + .includes() + .iter() + .flat_map(QasmSource::all_errors) + .map(|e| self.map_parse_error(e)) + .collect::>(); + + self_errors.extend(include_errors); + self_errors + } + + #[must_use] + pub fn semantic_errors(&self) -> Vec> { + self.errors().clone() + } + + #[must_use] + pub fn all_errors(&self) -> Vec> { + let mut parse_errors = self.parse_errors(); + let sem_errors = self.semantic_errors(); + parse_errors.extend(sem_errors); + parse_errors + } + + #[must_use] + pub fn errors(&self) -> Vec> { + self.errors.clone() + } + + fn map_parse_error(&self, error: crate::parser::Error) -> WithSource { + let path = self.source.path().display().to_string(); + let source = self.source_map.find_by_name(&path); + let offset = source.map_or(0, |source| source.offset); + + let offset_error = error.with_offset(offset); + + WithSource::from_map( + &self.source_map, + crate::Error(crate::ErrorKind::Parser(offset_error)), + ) + } +} + +/// Parse a QASM file and return the parse result. +/// This function will resolve includes using the provided resolver. +/// If an include file cannot be resolved, an error will be returned. +/// If a file is included recursively, a stack overflow occurs. +pub fn parse_source( + source: S, + path: P, + resolver: &R, +) -> miette::Result +where + S: AsRef, + P: AsRef, + R: SourceResolver, +{ + let res = crate::parser::parse_source(source, path, resolver)?; + let analyzer = crate::semantic::lowerer::Lowerer { + source: res.source, + source_map: res.source_map, + errors: vec![], + file_stack: vec![], + symbols: SymbolTable::default(), + version: None, + stmts: vec![], + }; + let sem_res = analyzer.lower(); + Ok(QasmSemanticParseResult { + source: sem_res.source, + source_map: sem_res.source_map, + symbols: sem_res.symbols, + program: sem_res.program, + errors: sem_res.errors, + }) +} diff --git a/compiler/qsc_qasm3/src/semantic/ast.rs b/compiler/qsc_qasm3/src/semantic/ast.rs new file mode 100644 index 0000000000..7b5d6bd8d2 --- /dev/null +++ b/compiler/qsc_qasm3/src/semantic/ast.rs @@ -0,0 +1,1655 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +use num_bigint::BigInt; +use qsc_data_structures::span::{Span, WithSpan}; +use std::{ + fmt::{self, Display, Formatter}, + hash::Hash, + rc::Rc, +}; + +use crate::{ + ast::{ + display_utils::{ + write_field, write_header, write_indented_list, write_list_field, write_opt_field, + write_opt_list_field, writeln_field, writeln_header, writeln_list_field, + writeln_opt_field, + }, + List, + }, + semantic::symbols::SymbolId, +}; + +#[derive(Clone, Debug)] +pub struct Program { + pub statements: List, + pub version: Option, +} + +impl Display for Program { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + writeln!(f, "Program:")?; + writeln_opt_field(f, "version", self.version.as_ref())?; + write_list_field(f, "statements", &self.statements) + } +} + +#[derive(Clone, Debug)] +pub struct Stmt { + pub span: Span, + pub annotations: List, + pub kind: Box, +} + +impl Display for Stmt { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + writeln_header(f, "Stmt", self.span)?; + writeln_list_field(f, "annotations", &self.annotations)?; + write_field(f, "kind", &self.kind) + } +} + +#[derive(Clone, Debug)] +pub struct Annotation { + pub span: Span, + pub identifier: Rc, + pub value: Option>, +} + +impl Display for Annotation { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + let identifier = format!("\"{}\"", self.identifier); + let value = self.value.as_ref().map(|val| format!("\"{val}\"")); + writeln_header(f, "Annotation", self.span)?; + writeln_field(f, "identifier", &identifier)?; + write_opt_field(f, "value", value.as_ref()) + } +} + +/// A path that may or may not have been successfully parsed. +#[derive(Clone, Debug, Hash, PartialEq, Eq)] +pub enum PathKind { + /// A successfully parsed path. + Ok(Box), + /// An invalid path. + Err(Option>), +} + +impl Default for PathKind { + fn default() -> Self { + PathKind::Err(None) + } +} + +impl Display for PathKind { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + match self { + PathKind::Ok(path) => write!(f, "{path}"), + PathKind::Err(Some(incomplete_path)) => { + write!(f, "Err IncompletePath {}:", incomplete_path.span)?; + write_list_field(f, "segments", &incomplete_path.segments) + } + PathKind::Err(None) => write!(f, "Err",), + } + } +} + +/// A path that was successfully parsed up to a certain `.`, +/// but is missing its final identifier. +#[derive(Clone, Debug, PartialEq, Eq, Hash)] +pub struct IncompletePath { + /// The whole span of the incomplete path, + /// including the final `.` and any whitespace or keyword + /// that follows it. + pub span: Span, + /// Any segments that were successfully parsed before the final `.`. + pub segments: Box<[Ident]>, + /// Whether a keyword exists after the final `.`. + /// This keyword can be presumed to be a partially typed identifier. + pub keyword: bool, +} + +/// A path to a declaration or a field access expression, +/// to be disambiguated during name resolution. +#[derive(Clone, Debug, Hash, PartialEq, Eq)] +pub struct Path { + /// The span. + pub span: Span, + /// The segments that make up the front of the path before the final `.`. + pub segments: Option>, + /// The declaration or field name. + pub name: Box, +} + +impl Display for Path { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + writeln_header(f, "Path", self.span)?; + writeln_field(f, "name", &self.name)?; + write_opt_list_field(f, "segments", self.segments.as_ref()) + } +} + +impl WithSpan for Path { + fn with_span(self, span: Span) -> Self { + Self { span, ..self } + } +} + +#[derive(Clone, Debug)] +pub struct MeasureExpr { + pub span: Span, + pub operand: GateOperand, +} + +impl Display for MeasureExpr { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + writeln_header(f, "MeasureExpr", self.span)?; + write_field(f, "operand", &self.operand) + } +} + +/// A binary operator. +#[derive(Clone, Copy, Debug)] +pub enum BinOp { + /// Addition: `+`. + Add, + /// Bitwise AND: `&`. + AndB, + /// Logical AND: `&&`. + AndL, + /// Division: `/`. + Div, + /// Equality: `==`. + Eq, + /// Exponentiation: `**`. + Exp, + /// Greater than: `>`. + Gt, + /// Greater than or equal: `>=`. + Gte, + /// Less than: `<`. + Lt, + /// Less than or equal: `<=`. + Lte, + /// Modulus: `%`. + Mod, + /// Multiplication: `*`. + Mul, + /// Inequality: `!=`. + Neq, + /// Bitwise OR: `|`. + OrB, + /// Logical OR: `||`. + OrL, + /// Shift left: `<<`. + Shl, + /// Shift right: `>>`. + Shr, + /// Subtraction: `-`. + Sub, + /// Bitwise XOR: `^`. + XorB, +} + +impl Display for BinOp { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + match self { + BinOp::Add => write!(f, "Add"), + BinOp::AndB => write!(f, "AndB"), + BinOp::AndL => write!(f, "AndL"), + BinOp::Div => write!(f, "Div"), + BinOp::Eq => write!(f, "Eq"), + BinOp::Exp => write!(f, "Exp"), + BinOp::Gt => write!(f, "Gt"), + BinOp::Gte => write!(f, "Gte"), + BinOp::Lt => write!(f, "Lt"), + BinOp::Lte => write!(f, "Lte"), + BinOp::Mod => write!(f, "Mod"), + BinOp::Mul => write!(f, "Mul"), + BinOp::Neq => write!(f, "Neq"), + BinOp::OrB => write!(f, "OrB"), + BinOp::OrL => write!(f, "OrL"), + BinOp::Shl => write!(f, "Shl"), + BinOp::Shr => write!(f, "Shr"), + BinOp::Sub => write!(f, "Sub"), + BinOp::XorB => write!(f, "XorB"), + } + } +} + +/// A unary operator. +#[derive(Clone, Copy, Debug)] +pub enum UnaryOp { + /// Negation: `-`. + Neg, + /// Bitwise NOT: `~`. + NotB, + /// Logical NOT: `!`. + NotL, +} + +impl Display for UnaryOp { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + match self { + UnaryOp::Neg => write!(f, "Neg"), + UnaryOp::NotB => write!(f, "NotB"), + UnaryOp::NotL => write!(f, "NotL"), + } + } +} + +#[derive(Clone, Debug, Default)] +pub enum GateOperand { + IndexedIdent(Box), + HardwareQubit(Box), + #[default] + Err, +} + +impl Display for GateOperand { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + match self { + GateOperand::IndexedIdent(ident) => write!(f, "{ident}"), + GateOperand::HardwareQubit(qubit) => write!(f, "{qubit}"), + GateOperand::Err => write!(f, "Error"), + } + } +} + +impl WithSpan for GateOperand { + fn with_span(self, span: Span) -> Self { + match self { + GateOperand::IndexedIdent(ident) => GateOperand::IndexedIdent(ident.with_span(span)), + GateOperand::HardwareQubit(qubit) => GateOperand::HardwareQubit(qubit.with_span(span)), + GateOperand::Err => GateOperand::Err, + } + } +} + +#[derive(Clone, Debug)] +pub struct HardwareQubit { + pub span: Span, + pub name: Rc, +} + +impl Display for HardwareQubit { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + write!(f, "HardwareQubit {}: {}", self.span, self.name) + } +} + +impl WithSpan for HardwareQubit { + fn with_span(self, span: Span) -> Self { + Self { span, ..self } + } +} + +#[derive(Clone, Debug)] +pub struct AliasDeclStmt { + pub symbol_id: SymbolId, + pub exprs: List, + pub span: Span, +} + +impl Display for AliasDeclStmt { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + writeln_header(f, "AliasDeclStmt", self.span)?; + writeln_field(f, "symbol_id", &self.symbol_id)?; + write_list_field(f, "exprs", &self.exprs) + } +} + +/// A statement kind. +#[derive(Clone, Debug, Default)] +pub enum StmtKind { + Alias(AliasDeclStmt), + Barrier(BarrierStmt), + Box(BoxStmt), + Block(Box), + CalibrationGrammar(CalibrationGrammarStmt), + ClassicalDecl(ClassicalDeclarationStmt), + Def(DefStmt), + DefCal(DefCalStmt), + Delay(DelayStmt), + /// An empty statement. + Empty, + End(EndStmt), + ExprStmt(ExprStmt), + ExternDecl(ExternDecl), + For(ForStmt), + If(IfStmt), + GateCall(GateCall), + GPhase(GPhase), + Include(IncludeStmt), + IODeclaration(IODeclaration), + Measure(MeasureStmt), + Pragma(Pragma), + QuantumGateDefinition(QuantumGateDefinition), + QuantumDecl(QubitDeclaration), + Reset(ResetStmt), + Return(ReturnStmt), + Switch(SwitchStmt), + WhileLoop(WhileLoop), + /// An invalid statement. + #[default] + Err, +} + +impl Display for StmtKind { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + match self { + StmtKind::Alias(alias) => write!(f, "{alias}"), + StmtKind::Barrier(barrier) => write!(f, "{barrier}"), + StmtKind::Box(box_stmt) => write!(f, "{box_stmt}"), + StmtKind::Block(block) => write!(f, "{block}"), + StmtKind::CalibrationGrammar(grammar) => write!(f, "{grammar}"), + StmtKind::ClassicalDecl(decl) => write!(f, "{decl}"), + StmtKind::Def(def) => write!(f, "{def}"), + StmtKind::DefCal(defcal) => write!(f, "{defcal}"), + StmtKind::Delay(delay) => write!(f, "{delay}"), + StmtKind::Empty => write!(f, "Empty"), + StmtKind::End(end_stmt) => write!(f, "{end_stmt}"), + StmtKind::ExprStmt(expr) => write!(f, "{expr}"), + StmtKind::ExternDecl(decl) => write!(f, "{decl}"), + StmtKind::For(for_stmt) => write!(f, "{for_stmt}"), + StmtKind::GateCall(gate_call) => write!(f, "{gate_call}"), + StmtKind::GPhase(gphase) => write!(f, "{gphase}"), + StmtKind::If(if_stmt) => write!(f, "{if_stmt}"), + StmtKind::Include(include) => write!(f, "{include}"), + StmtKind::IODeclaration(io) => write!(f, "{io}"), + StmtKind::Measure(measure) => write!(f, "{measure}"), + StmtKind::Pragma(pragma) => write!(f, "{pragma}"), + StmtKind::QuantumGateDefinition(gate) => write!(f, "{gate}"), + StmtKind::QuantumDecl(decl) => write!(f, "{decl}"), + StmtKind::Reset(reset_stmt) => write!(f, "{reset_stmt}"), + StmtKind::Return(return_stmt) => write!(f, "{return_stmt}"), + StmtKind::Switch(switch_stmt) => write!(f, "{switch_stmt}"), + StmtKind::WhileLoop(while_loop) => write!(f, "{while_loop}"), + StmtKind::Err => write!(f, "Err"), + } + } +} + +#[derive(Clone, Debug)] +pub struct CalibrationGrammarStmt { + pub span: Span, + pub name: String, +} + +impl Display for CalibrationGrammarStmt { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + writeln_header(f, "CalibrationGrammarStmt", self.span)?; + write_field(f, "name", &self.name) + } +} + +#[derive(Clone, Debug)] +pub struct DefCalStmt { + pub span: Span, +} + +impl Display for DefCalStmt { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + write!(f, "DefCalStmt {}", self.span) + } +} + +#[derive(Clone, Debug)] +pub struct IfStmt { + pub span: Span, + pub condition: Expr, + pub if_block: List, + pub else_block: Option>, +} + +impl Display for IfStmt { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + writeln_header(f, "IfStmt", self.span)?; + writeln_field(f, "condition", &self.condition)?; + writeln_list_field(f, "if_block", &self.if_block)?; + write_opt_list_field(f, "else_block", self.else_block.as_ref()) + } +} + +#[derive(Clone, Debug)] +pub struct BarrierStmt { + pub span: Span, + pub qubits: List, +} + +impl Display for BarrierStmt { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + writeln_header(f, "BarrierStmt", self.span)?; + write_list_field(f, "operands", &self.qubits) + } +} + +#[derive(Clone, Debug)] +pub struct ResetStmt { + pub span: Span, + pub operand: Box, +} + +impl Display for ResetStmt { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + writeln_header(f, "ResetStmt", self.span)?; + write_field(f, "operand", &self.operand) + } +} + +/// A sequenced block of statements. +#[derive(Clone, Debug, Default)] +pub struct Block { + /// The span. + pub span: Span, + /// The statements in the block. + pub stmts: List, +} + +impl Display for Block { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + write_header(f, "Block", self.span)?; + write_indented_list(f, &self.stmts) + } +} + +#[derive(Clone, Debug)] +pub enum Identifier { + Ident(Box), + IndexedIdent(Box), +} + +impl Display for Identifier { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + match self { + Identifier::Ident(ident) => write!(f, "{ident}"), + Identifier::IndexedIdent(ident) => write!(f, "{ident}"), + } + } +} + +#[derive(Clone, Debug, Hash, PartialEq, Eq)] +pub struct Ident { + pub span: Span, + pub name: Rc, +} + +impl Default for Ident { + fn default() -> Self { + Ident { + span: Span::default(), + name: "".into(), + } + } +} + +impl Display for Ident { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + write!(f, "Ident {} \"{}\"", self.span, self.name) + } +} + +impl WithSpan for Ident { + fn with_span(self, span: Span) -> Self { + Self { span, ..self } + } +} + +#[derive(Clone, Debug)] +pub struct IndexedIdent { + pub span: Span, + pub name: Ident, + pub indices: List, +} + +impl Display for IndexedIdent { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + writeln_header(f, "IndexedIdent", self.span)?; + writeln_field(f, "name", &self.name)?; + write_list_field(f, "indices", &self.indices) + } +} + +impl WithSpan for IndexedIdent { + fn with_span(self, span: Span) -> Self { + Self { span, ..self } + } +} + +#[derive(Clone, Debug)] +pub struct ExprStmt { + pub span: Span, + pub expr: Expr, +} + +impl Display for ExprStmt { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + writeln_header(f, "ExprStmt", self.span)?; + write_field(f, "expr", &self.expr) + } +} + +#[derive(Clone, Debug, Default)] +pub struct Expr { + pub span: Span, + pub kind: Box, + pub ty: super::types::Type, +} + +impl WithSpan for Expr { + fn with_span(self, span: Span) -> Self { + Self { span, ..self } + } +} + +impl Display for Expr { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + writeln_header(f, "Expr", self.span)?; + writeln_field(f, "ty", &self.ty)?; + write_field(f, "kind", &self.kind) + } +} + +#[derive(Clone, Debug)] +pub struct DiscreteSet { + pub span: Span, + pub values: List, +} + +impl Display for DiscreteSet { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + writeln_header(f, "DiscreteSet", self.span)?; + write_list_field(f, "values", &self.values) + } +} + +#[derive(Clone, Debug)] +pub struct IndexSet { + pub span: Span, + pub values: List, +} + +impl Display for IndexSet { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + writeln_header(f, "IndexSet", self.span)?; + write_list_field(f, "values", &self.values) + } +} + +#[derive(Clone, Debug)] +pub struct RangeDefinition { + pub span: Span, + pub start: Option, + pub end: Option, + pub step: Option, +} + +impl WithSpan for RangeDefinition { + fn with_span(self, span: Span) -> Self { + Self { span, ..self } + } +} + +impl Display for RangeDefinition { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + writeln_header(f, "RangeDefinition", self.span)?; + writeln_opt_field(f, "start", self.start.as_ref())?; + writeln_opt_field(f, "step", self.step.as_ref())?; + write_opt_field(f, "end", self.end.as_ref()) + } +} + +#[derive(Clone, Debug)] +pub struct QuantumGateModifier { + pub span: Span, + pub kind: GateModifierKind, +} + +impl Display for QuantumGateModifier { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + write!(f, "QuantumGateModifier {}: {}", self.span, self.kind) + } +} + +#[derive(Clone, Debug)] +pub enum GateModifierKind { + Inv, + Pow(Expr), + Ctrl(Option), + NegCtrl(Option), +} + +impl Display for GateModifierKind { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + match self { + GateModifierKind::Inv => write!(f, "Inv"), + GateModifierKind::Pow(expr) => write!(f, "Pow {expr}"), + GateModifierKind::Ctrl(expr) => write!(f, "Ctrl {expr:?}"), + GateModifierKind::NegCtrl(expr) => write!(f, "NegCtrl {expr:?}"), + } + } +} + +#[derive(Clone, Debug)] +pub struct ClassicalArgument { + pub span: Span, + pub ty: ScalarType, + pub name: Identifier, + pub access: Option, +} + +impl Display for ClassicalArgument { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + if let Some(access) = &self.access { + write!( + f, + "ClassicalArgument {}: {}, {}, {}", + self.span, self.ty, self.name, access + ) + } else { + write!( + f, + "ClassicalArgument {}: {}, {}", + self.span, self.ty, self.name + ) + } + } +} + +#[derive(Clone, Debug)] +pub enum ExternParameter { + Scalar(ScalarType, Span), + Quantum(Option, Span), + ArrayReference(ArrayReferenceType, Span), +} + +impl Display for ExternParameter { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + match self { + ExternParameter::Scalar(ty, span) => { + write!(f, "{span}: {ty}") + } + ExternParameter::Quantum(expr, span) => { + write!(f, "{span}: {expr:?}") + } + ExternParameter::ArrayReference(ty, span) => { + write!(f, "{span}: {ty}") + } + } + } +} + +impl Default for ExternParameter { + fn default() -> Self { + ExternParameter::Scalar(ScalarType::default(), Span::default()) + } +} + +impl WithSpan for ExternParameter { + fn with_span(self, span: Span) -> Self { + match self { + ExternParameter::Scalar(ty, _) => ExternParameter::Scalar(ty, span), + ExternParameter::Quantum(expr, _) => ExternParameter::Quantum(expr, span), + ExternParameter::ArrayReference(ty, _) => ExternParameter::ArrayReference(ty, span), + } + } +} + +#[derive(Clone, Debug, Default)] +pub struct ScalarType { + pub span: Span, + pub kind: ScalarTypeKind, +} + +impl Display for ScalarType { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + write!(f, "ScalarType {}: {}", self.span, self.kind) + } +} + +#[derive(Clone, Debug, Default)] +pub enum ScalarTypeKind { + Bit(BitType), + Int(IntType), + UInt(UIntType), + Float(FloatType), + Complex(ComplexType), + Angle(AngleType), + BoolType, + Duration, + Stretch, + // Any usage of Err should have pushed a parse error + #[default] + Err, +} + +impl Display for ScalarTypeKind { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + match self { + ScalarTypeKind::Int(int) => write!(f, "{int}"), + ScalarTypeKind::UInt(uint) => write!(f, "{uint}"), + ScalarTypeKind::Float(float) => write!(f, "{float}"), + ScalarTypeKind::Complex(complex) => write!(f, "{complex}"), + ScalarTypeKind::Angle(angle) => write!(f, "{angle}"), + ScalarTypeKind::Bit(bit) => write!(f, "{bit}"), + ScalarTypeKind::BoolType => write!(f, "BoolType"), + ScalarTypeKind::Duration => write!(f, "Duration"), + ScalarTypeKind::Stretch => write!(f, "Stretch"), + ScalarTypeKind::Err => write!(f, "Err"), + } + } +} + +#[derive(Clone, Debug)] +pub enum ArrayBaseTypeKind { + Int(IntType), + UInt(UIntType), + Float(FloatType), + Complex(ComplexType), + Angle(AngleType), + BoolType, + Duration, +} + +impl Display for ArrayBaseTypeKind { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + match self { + ArrayBaseTypeKind::Int(int) => write!(f, "ArrayBaseTypeKind {int}"), + ArrayBaseTypeKind::UInt(uint) => write!(f, "ArrayBaseTypeKind {uint}"), + ArrayBaseTypeKind::Float(float) => write!(f, "ArrayBaseTypeKind {float}"), + ArrayBaseTypeKind::Complex(complex) => write!(f, "ArrayBaseTypeKind {complex}"), + ArrayBaseTypeKind::Angle(angle) => write!(f, "ArrayBaseTypeKind {angle}"), + ArrayBaseTypeKind::Duration => write!(f, "ArrayBaseTypeKind DurationType"), + ArrayBaseTypeKind::BoolType => write!(f, "ArrayBaseTypeKind BoolType"), + } + } +} + +#[derive(Clone, Debug)] +pub struct IntType { + pub span: Span, + pub size: Option, +} + +impl Display for IntType { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + writeln_header(f, "IntType", self.span)?; + write_opt_field(f, "size", self.size.as_ref()) + } +} + +#[derive(Clone, Debug)] +pub struct UIntType { + pub span: Span, + pub size: Option, +} + +impl Display for UIntType { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + writeln_header(f, "UIntType", self.span)?; + write_opt_field(f, "size", self.size.as_ref()) + } +} + +#[derive(Clone, Debug)] +pub struct FloatType { + pub span: Span, + pub size: Option, +} + +impl Display for FloatType { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + writeln_header(f, "FloatType", self.span)?; + write_opt_field(f, "size", self.size.as_ref()) + } +} + +#[derive(Clone, Debug)] +pub struct ComplexType { + pub span: Span, + pub base_size: Option, +} + +impl Display for ComplexType { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + writeln_header(f, "ComplexType", self.span)?; + write_opt_field(f, "base_size", self.base_size.as_ref()) + } +} + +#[derive(Clone, Debug)] +pub struct AngleType { + pub span: Span, + pub size: Option, +} + +impl Display for AngleType { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + writeln_header(f, "AngleType", self.span)?; + write_opt_field(f, "size", self.size.as_ref()) + } +} + +#[derive(Clone, Debug)] +pub struct BitType { + pub span: Span, + pub size: Option, +} + +impl Display for BitType { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + writeln_header(f, "BitType", self.span)?; + write_opt_field(f, "size", self.size.as_ref()) + } +} + +#[derive(Clone, Debug)] +pub enum TypeDef { + Scalar(ScalarType), + Array(ArrayType), + ArrayReference(ArrayReferenceType), +} + +impl TypeDef { + pub fn span(&self) -> Span { + match self { + TypeDef::Scalar(ident) => ident.span, + TypeDef::Array(array) => array.span, + TypeDef::ArrayReference(array) => array.span, + } + } +} + +impl Display for TypeDef { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + match self { + TypeDef::Scalar(scalar) => write!(f, "{scalar}"), + TypeDef::Array(array) => write!(f, "{array}"), + TypeDef::ArrayReference(array) => write!(f, "{array}"), + } + } +} + +#[derive(Clone, Debug)] +pub struct ArrayType { + pub span: Span, + pub base_type: ArrayBaseTypeKind, + pub dimensions: List, +} + +impl Display for ArrayType { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + writeln_header(f, "ArrayType", self.span)?; + writeln_field(f, "base_type", &self.base_type)?; + write_list_field(f, "dimensions", &self.dimensions) + } +} + +#[derive(Clone, Debug)] +pub struct ArrayReferenceType { + pub span: Span, + pub mutability: AccessControl, + pub base_type: ArrayBaseTypeKind, + pub dimensions: List, +} + +impl Display for ArrayReferenceType { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + writeln_header(f, "ArrayReferenceType", self.span)?; + writeln_field(f, "mutability", &self.mutability)?; + writeln_field(f, "base_type", &self.base_type)?; + writeln_list_field(f, "dimensions", &self.dimensions) + } +} + +#[derive(Clone, Debug)] +pub enum AccessControl { + ReadOnly, + Mutable, +} + +impl Display for AccessControl { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + match self { + AccessControl::ReadOnly => write!(f, "ReadOnly"), + AccessControl::Mutable => write!(f, "Mutable"), + } + } +} + +#[derive(Clone, Debug)] +pub struct QuantumArgument { + pub span: Span, + pub expr: Option, +} + +impl Display for QuantumArgument { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + writeln_header(f, "QuantumArgument", self.span)?; + write_opt_field(f, "expr", self.expr.as_ref()) + } +} + +#[derive(Clone, Debug)] +pub struct Pragma { + pub span: Span, + pub identifier: Rc, + pub value: Option>, +} + +impl Display for Pragma { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + let identifier = format!("\"{}\"", self.identifier); + let value = self.value.as_ref().map(|val| format!("\"{val}\"")); + writeln_header(f, "Pragma", self.span)?; + writeln_field(f, "identifier", &identifier)?; + write_opt_field(f, "value", value.as_ref()) + } +} + +#[derive(Clone, Debug)] +pub struct IncludeStmt { + pub span: Span, + pub filename: String, +} + +impl Display for IncludeStmt { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + writeln_header(f, "IncludeStmt", self.span)?; + write_field(f, "filename", &self.filename) + } +} + +#[derive(Clone, Debug)] +pub struct QubitDeclaration { + pub span: Span, + pub qubit: Box, + pub size: Option, +} + +impl Display for QubitDeclaration { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + writeln_header(f, "QubitDeclaration", self.span)?; + writeln_field(f, "ident", &self.qubit)?; + write_opt_field(f, "size", self.size.as_ref()) + } +} + +#[derive(Clone, Debug)] +pub struct QuantumGateDefinition { + pub span: Span, + pub ident: Box, + pub params: List, + pub qubits: List, + pub body: Box, +} + +impl Display for QuantumGateDefinition { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + writeln_header(f, "Gate", self.span)?; + writeln_field(f, "ident", &self.ident)?; + writeln_list_field(f, "parameters", &self.params)?; + writeln_list_field(f, "qubits", &self.qubits)?; + write_field(f, "body", &self.body) + } +} + +#[derive(Clone, Debug)] +pub struct ExternDecl { + pub span: Span, + pub ident: Box, + pub params: List, + pub return_type: Option, +} + +impl Display for ExternDecl { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + writeln_header(f, "ExternDecl", self.span)?; + writeln_field(f, "ident", &self.ident)?; + writeln_list_field(f, "parameters", &self.params)?; + write_opt_field(f, "return_type", self.return_type.as_ref()) + } +} + +#[derive(Clone, Debug)] +pub struct GateCall { + pub span: Span, + pub modifiers: List, + pub name: Ident, + pub args: List, + pub qubits: List, + pub duration: Option, +} + +impl Display for GateCall { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + writeln_header(f, "GateCall", self.span)?; + writeln_list_field(f, "modifiers", &self.modifiers)?; + writeln_field(f, "name", &self.name)?; + writeln_list_field(f, "args", &self.args)?; + writeln_opt_field(f, "duration", self.duration.as_ref())?; + write_list_field(f, "qubits", &self.qubits) + } +} + +#[derive(Clone, Debug)] +pub struct GPhase { + pub span: Span, + pub modifiers: List, + pub args: List, + pub qubits: List, + pub duration: Option, +} + +impl Display for GPhase { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + writeln_header(f, "GPhase", self.span)?; + writeln_list_field(f, "modifiers", &self.modifiers)?; + writeln_list_field(f, "args", &self.args)?; + writeln_opt_field(f, "duration", self.duration.as_ref())?; + write_list_field(f, "qubits", &self.qubits) + } +} + +#[derive(Clone, Debug)] +pub struct DelayStmt { + pub span: Span, + pub duration: Expr, + pub qubits: List, +} + +impl Display for DelayStmt { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + writeln_header(f, "DelayStmt", self.span)?; + writeln_field(f, "duration", &self.duration)?; + write_list_field(f, "qubits", &self.qubits) + } +} + +#[derive(Clone, Debug)] +pub struct BoxStmt { + pub span: Span, + pub duration: Option, + pub body: List, +} + +impl Display for BoxStmt { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + writeln_header(f, "BoxStmt", self.span)?; + writeln_opt_field(f, "duration", self.duration.as_ref())?; + write_list_field(f, "body", &self.body) + } +} + +#[derive(Clone, Debug)] +pub struct MeasureStmt { + pub span: Span, + pub measurement: MeasureExpr, + pub target: Option>, +} + +impl Display for MeasureStmt { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + writeln_header(f, "MeasureStmt", self.span)?; + writeln_field(f, "measurement", &self.measurement)?; + write_opt_field(f, "target", self.target.as_ref()) + } +} + +#[derive(Clone, Debug)] +pub struct ClassicalDeclarationStmt { + pub span: Span, + pub ty_span: Span, + pub symbol_id: SymbolId, + pub init_expr: Box, +} + +impl Display for ClassicalDeclarationStmt { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + writeln_header(f, "ClassicalDeclarationStmt", self.span)?; + writeln_field(f, "symbol_id", &self.symbol_id)?; + writeln_field(f, "ty_span", &self.ty_span)?; + write_field(f, "init_expr", self.init_expr.as_ref()) + } +} + +#[derive(Clone, Debug)] +pub enum ValueExpression { + Expr(Expr), + Measurement(MeasureExpr), +} + +impl Display for ValueExpression { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + match self { + ValueExpression::Expr(expr) => write!(f, "{expr}"), + ValueExpression::Measurement(measure) => write!(f, "{measure}"), + } + } +} + +#[derive(Clone, Debug)] +pub struct IODeclaration { + pub span: Span, + pub io_identifier: IOKeyword, + pub ty: TypeDef, + pub ident: Box, +} + +impl Display for IODeclaration { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + writeln_header(f, "IODeclaration", self.span)?; + writeln_field(f, "io_keyword", &self.io_identifier)?; + writeln_field(f, "type", &self.ty)?; + write_field(f, "ident", &self.ident) + } +} + +#[derive(Clone, Debug)] +pub enum TypedParameter { + Scalar(ScalarTypedParameter), + Quantum(QuantumTypedParameter), + ArrayReference(ArrayTypedParameter), +} + +impl WithSpan for TypedParameter { + fn with_span(self, span: Span) -> Self { + match self { + Self::Scalar(param) => Self::Scalar(param.with_span(span)), + Self::Quantum(param) => Self::Quantum(param.with_span(span)), + Self::ArrayReference(param) => Self::ArrayReference(param.with_span(span)), + } + } +} + +impl Display for TypedParameter { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + match self { + Self::Scalar(param) => write!(f, "{param}"), + Self::Quantum(param) => write!(f, "{param}"), + Self::ArrayReference(param) => write!(f, "{param}"), + } + } +} +impl Default for TypedParameter { + fn default() -> Self { + Self::Scalar(ScalarTypedParameter { + span: Span::default(), + ident: Ident::default(), + ty: Box::default(), + }) + } +} + +#[derive(Clone, Debug)] +pub struct ScalarTypedParameter { + pub span: Span, + pub ty: Box, + pub ident: Ident, +} + +impl Display for ScalarTypedParameter { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + writeln_header(f, "ScalarTypedParameter", self.span)?; + writeln_field(f, "type", &self.ty)?; + write_field(f, "ident", &self.ident) + } +} + +impl WithSpan for ScalarTypedParameter { + fn with_span(self, span: Span) -> Self { + let Self { ty, ident, .. } = self; + Self { span, ty, ident } + } +} + +#[derive(Clone, Debug)] +pub struct QuantumTypedParameter { + pub span: Span, + pub size: Option, + pub ident: Ident, +} + +impl Display for QuantumTypedParameter { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + writeln_header(f, "QuantumTypedParameter", self.span)?; + writeln_opt_field(f, "size", self.size.as_ref())?; + write_field(f, "ident", &self.ident) + } +} + +impl WithSpan for QuantumTypedParameter { + fn with_span(self, span: Span) -> Self { + let Self { size, ident, .. } = self; + Self { span, size, ident } + } +} + +#[derive(Clone, Debug)] +pub struct ArrayTypedParameter { + pub span: Span, + pub ty: Box, + pub ident: Ident, +} + +impl Display for ArrayTypedParameter { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + writeln_header(f, "ArrayTypedParameter", self.span)?; + writeln_field(f, "type", &self.ty)?; + write_field(f, "ident", &self.ident) + } +} + +impl WithSpan for ArrayTypedParameter { + fn with_span(self, span: Span) -> Self { + let Self { ty, ident, .. } = self; + Self { span, ty, ident } + } +} + +#[derive(Clone, Debug)] +pub struct DefStmt { + pub span: Span, + pub name: Box, + pub params: List, + pub body: Box, + pub return_type: Option, +} + +impl Display for DefStmt { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + writeln_header(f, "DefStmt", self.span)?; + writeln_field(f, "ident", &self.name)?; + writeln_list_field(f, "parameters", &self.params)?; + writeln_opt_field(f, "return_type", self.return_type.as_ref())?; + write_field(f, "body", &self.body) + } +} + +#[derive(Clone, Debug)] +pub struct ReturnStmt { + pub span: Span, + pub expr: Option>, +} + +impl Display for ReturnStmt { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + writeln_header(f, "ReturnStmt", self.span)?; + write_opt_field(f, "expr", self.expr.as_ref()) + } +} + +#[derive(Clone, Debug)] +pub struct WhileLoop { + pub span: Span, + pub while_condition: Expr, + pub block: List, +} + +impl Display for WhileLoop { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + writeln_header(f, "WhileLoop", self.span)?; + writeln_field(f, "condition", &self.while_condition)?; + write_list_field(f, "block", &self.block) + } +} + +#[derive(Clone, Debug)] +pub struct ForStmt { + pub span: Span, + pub ty: ScalarType, + pub identifier: Identifier, + pub set_declaration: Box, + pub block: List, +} + +impl Display for ForStmt { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + writeln_header(f, "ForStmt", self.span)?; + writeln_field(f, "variable_type", &self.ty)?; + writeln_field(f, "variable_name", &self.identifier)?; + writeln_field(f, "iterable", &self.set_declaration)?; + write_list_field(f, "block", &self.block) + } +} + +#[derive(Clone, Debug)] +pub enum EnumerableSet { + DiscreteSet(DiscreteSet), + RangeDefinition(RangeDefinition), + Expr(Expr), +} + +impl Display for EnumerableSet { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + match self { + EnumerableSet::DiscreteSet(set) => write!(f, "{set}"), + EnumerableSet::RangeDefinition(range) => write!(f, "{range}"), + EnumerableSet::Expr(expr) => write!(f, "{expr}"), + } + } +} + +#[derive(Clone, Debug)] +pub struct SwitchStmt { + pub span: Span, + pub target: Expr, + pub cases: List, + /// Note that `None` is quite different to `[]` in this case; the latter is + /// an explicitly empty body, whereas the absence of a default might mean + /// that the switch is inexhaustive, and a linter might want to complain. + pub default: Option, +} + +impl Display for SwitchStmt { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + writeln_header(f, "SwitchStmt", self.span)?; + writeln_field(f, "target", &self.target)?; + writeln_list_field(f, "cases", &self.cases)?; + write_opt_field(f, "default_case", self.default.as_ref()) + } +} + +#[derive(Clone, Debug)] +pub struct SwitchCase { + pub span: Span, + pub labels: List, + pub block: Block, +} + +impl Display for SwitchCase { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + writeln_header(f, "SwitchCase", self.span)?; + writeln_list_field(f, "labels", &self.labels)?; + write_field(f, "block", &self.block) + } +} + +#[derive(Clone, Debug, Default)] +pub enum ExprKind { + Assign(AssignExpr), + AssignOp(AssignOpExpr), + /// An expression with invalid syntax that can't be parsed. + #[default] + Err, + Ident(SymbolId), + UnaryOp(UnaryOpExpr), + BinaryOp(BinaryOpExpr), + Lit(LiteralKind), + FunctionCall(FunctionCall), + Cast(Cast), + IndexExpr(IndexExpr), + Paren(Expr), +} + +impl Display for ExprKind { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + match self { + ExprKind::Err => write!(f, "Err"), + ExprKind::Ident(id) => write!(f, "{id}"), + ExprKind::UnaryOp(expr) => write!(f, "{expr}"), + ExprKind::BinaryOp(expr) => write!(f, "{expr}"), + ExprKind::Lit(lit) => write!(f, "Lit: {lit}"), + ExprKind::FunctionCall(call) => write!(f, "{call}"), + ExprKind::Cast(expr) => write!(f, "{expr}"), + ExprKind::IndexExpr(expr) => write!(f, "{expr}"), + ExprKind::Assign(expr) => write!(f, "{expr}"), + ExprKind::AssignOp(expr) => write!(f, "{expr}"), + ExprKind::Paren(expr) => write!(f, "Paren {expr}"), + } + } +} + +#[derive(Clone, Debug)] +pub struct AssignExpr { + pub lhs: Expr, + pub rhs: Expr, +} + +impl Display for AssignExpr { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + writeln!(f, "AssignExpr:")?; + writeln_field(f, "lhs", &self.lhs)?; + write_field(f, "rhs", &self.rhs) + } +} + +#[derive(Clone, Debug)] +pub struct AssignOpExpr { + pub op: BinOp, + pub lhs: Expr, + pub rhs: Expr, +} + +impl Display for AssignOpExpr { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + writeln!(f, "AssignOpExpr:")?; + writeln_field(f, "op", &self.op)?; + writeln_field(f, "lhs", &self.lhs)?; + write_field(f, "rhs", &self.rhs) + } +} + +#[derive(Clone, Debug)] +pub struct UnaryOpExpr { + pub op: UnaryOp, + pub expr: Expr, +} + +impl Display for UnaryOpExpr { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + writeln!(f, "UnaryOpExpr:")?; + writeln_field(f, "op", &self.op)?; + write_field(f, "expr", &self.expr) + } +} + +#[derive(Clone, Debug)] +pub struct BinaryOpExpr { + pub op: BinOp, + pub lhs: Expr, + pub rhs: Expr, +} + +impl Display for BinaryOpExpr { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + writeln!(f, "BinaryOpExpr:")?; + writeln_field(f, "op", &self.op)?; + writeln_field(f, "lhs", &self.lhs)?; + write_field(f, "rhs", &self.rhs) + } +} + +#[derive(Clone, Debug)] +pub struct FunctionCall { + pub span: Span, + pub name: Ident, + pub args: List, +} + +impl Display for FunctionCall { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + writeln_header(f, "FunctionCall", self.span)?; + writeln_field(f, "name", &self.name)?; + write_list_field(f, "args", &self.args) + } +} + +#[derive(Clone, Debug)] +pub struct Cast { + pub span: Span, + pub ty: crate::semantic::types::Type, + pub expr: Expr, +} + +impl Display for Cast { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + writeln_header(f, "Cast", self.span)?; + writeln_field(f, "type", &self.ty)?; + write_field(f, "expr", &self.expr) + } +} + +#[derive(Clone, Debug)] +pub struct IndexExpr { + pub span: Span, + pub collection: Expr, + pub index: IndexElement, +} + +impl Display for IndexExpr { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + writeln_header(f, "IndexExpr", self.span)?; + writeln_field(f, "collection", &self.collection)?; + write_field(f, "index", &self.index) + } +} + +#[derive(Clone, Debug)] +pub enum LiteralKind { + Array(List), + Bitstring(BigInt, u32), + Bool(bool), + Duration(f64, TimeUnit), + Float(f64), + Complex(f64, f64), + Int(i64), + BigInt(BigInt), + String(Rc), +} + +impl Display for LiteralKind { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + match self { + LiteralKind::Array(exprs) => write_list_field(f, "Array", exprs), + LiteralKind::Bitstring(value, width) => { + let width = *width as usize; + write!(f, "Bitstring(\"{:0>width$}\")", value.to_str_radix(2)) + } + LiteralKind::Bool(b) => write!(f, "Bool({b:?})"), + LiteralKind::Complex(real, imag) => write!(f, "Complex({real:?}, {imag:?})"), + LiteralKind::Duration(value, unit) => { + write!(f, "Duration({value:?}, {unit:?})") + } + LiteralKind::Float(value) => write!(f, "Float({value:?})"), + + LiteralKind::Int(i) => write!(f, "Int({i:?})"), + LiteralKind::BigInt(i) => write!(f, "BigInt({i:?})"), + LiteralKind::String(s) => write!(f, "String({s:?})"), + } + } +} + +#[derive(Debug, Clone, Copy, PartialEq)] +pub struct Version { + pub major: u32, + pub minor: Option, + pub span: Span, +} + +impl fmt::Display for Version { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self.minor { + Some(minor) => write!(f, "{}.{}", self.major, minor), + None => write!(f, "{}", self.major), + } + } +} + +#[derive(Clone, Debug)] +pub enum IndexElement { + DiscreteSet(DiscreteSet), + IndexSet(IndexSet), +} + +impl Display for IndexElement { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + match self { + IndexElement::DiscreteSet(set) => write!(f, "{set}"), + IndexElement::IndexSet(set) => write!(f, "{set}"), + } + } +} + +#[derive(Clone, Debug, Default)] +pub enum IndexSetItem { + RangeDefinition(RangeDefinition), + Expr(Expr), + #[default] + Err, +} + +/// This is needed to able to use `IndexSetItem` in the `seq` combinator. +impl WithSpan for IndexSetItem { + fn with_span(self, span: Span) -> Self { + match self { + IndexSetItem::RangeDefinition(range) => { + IndexSetItem::RangeDefinition(range.with_span(span)) + } + IndexSetItem::Expr(expr) => IndexSetItem::Expr(expr.with_span(span)), + IndexSetItem::Err => IndexSetItem::Err, + } + } +} + +impl Display for IndexSetItem { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + match self { + IndexSetItem::RangeDefinition(range) => write!(f, "{range}"), + IndexSetItem::Expr(expr) => write!(f, "{expr}"), + IndexSetItem::Err => write!(f, "Err"), + } + } +} + +#[derive(Clone, Debug)] +pub enum IOKeyword { + Input, + Output, +} + +impl Display for IOKeyword { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + match self { + IOKeyword::Input => write!(f, "input"), + IOKeyword::Output => write!(f, "output"), + } + } +} + +#[derive(Clone, Debug)] +pub enum TimeUnit { + Dt, + /// Nanoseconds. + Ns, + /// Microseconds. + Us, + /// Milliseconds. + Ms, + /// Seconds. + S, +} + +impl Display for TimeUnit { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + match self { + TimeUnit::Dt => write!(f, "dt"), + TimeUnit::Ns => write!(f, "ns"), + TimeUnit::Us => write!(f, "us"), + TimeUnit::Ms => write!(f, "ms"), + TimeUnit::S => write!(f, "s"), + } + } +} + +#[derive(Clone, Debug)] +pub struct EndStmt { + pub span: Span, +} + +impl Display for EndStmt { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + write!(f, "End {}", self.span) + } +} diff --git a/compiler/qsc_qasm3/src/semantic/error.rs b/compiler/qsc_qasm3/src/semantic/error.rs new file mode 100644 index 0000000000..c637443765 --- /dev/null +++ b/compiler/qsc_qasm3/src/semantic/error.rs @@ -0,0 +1,330 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +use miette::Diagnostic; +use qsc_data_structures::span::Span; +use thiserror::Error; + +#[derive(Clone, Debug, Diagnostic, Eq, Error, PartialEq)] +#[error(transparent)] +#[diagnostic(transparent)] +pub struct Error(pub SemanticErrorKind); + +impl Error { + #[must_use] + pub fn with_offset(self, offset: u32) -> Self { + Self(self.0.with_offset(offset)) + } +} + +/// Represents the kind of semantic error that occurred during compilation of a QASM file(s). +/// For the most part, these errors are fatal and prevent compilation and are +/// safety checks to ensure that the QASM code is valid. +/// +/// We can't use the semantics library for this: +/// - it is unsafe to use (heavy use of panic and unwrap) +/// - it is missing many language features +#[derive(Clone, Debug, Diagnostic, Eq, Error, PartialEq)] +pub enum SemanticErrorKind { + #[error("Array literals are only allowed in classical declarations.")] + #[diagnostic(code("Qsc.Qasm3.Compile.ArrayLiteralInNonClassicalDecl"))] + ArrayLiteralInNonClassicalDecl(#[label] Span), + #[error("Annotation missing target statement.")] + #[diagnostic(code("Qsc.Qasm3.Compile.AnnotationWithoutStatement"))] + AnnotationWithoutStatement(#[label] Span), + #[error("calibration statements are not supported: {0}")] + #[diagnostic(code("Qsc.Qasm3.Compile.CalibrationsNotSupported"))] + CalibrationsNotSupported(String, #[label] Span), + #[error("Cannot alias type {0}. Only qubit and qubit[] can be aliased.")] + #[diagnostic(code("Qsc.Qasm3.Compile.CannotAliasType"))] + CannotAliasType(String, #[label] Span), + #[error("Cannot apply operator {0} to types {1} and {2}.")] + #[diagnostic(code("Qsc.Qasm3.Compile.CannotApplyOperatorToTypes"))] + CannotApplyOperatorToTypes(String, String, String, #[label] Span), + #[error("Cannot assign a value of {0} type to a classical variable of {1} type.")] + #[diagnostic(code("Qsc.Qasm3.Compile.CannotAssignToType"))] + CannotAssignToType(String, String, #[label] Span), + #[error("Cannot call a gate that is not a gate.")] + #[diagnostic(code("Qsc.Qasm3.Compile.CannotCallNonGate"))] + CannotCallNonGate(#[label] Span), + #[error("Cannot cast expression of type {0} to type {1}")] + #[diagnostic(code("Qsc.Qasm3.Compile.CannotCast"))] + CannotCast(String, String, #[label] Span), + #[error("Cannot index variables of type {0}")] + #[diagnostic(code("Qsc.Qasm3.Compile.CannotIndexType"))] + CannotIndexType(String, #[label] Span), + #[error("Cannot update const variable {0}")] + #[diagnostic(help("mutable variables must be declared without the keyword `const`."))] + #[diagnostic(code("Qsc.Qasm3.Compile.CannotUpdateConstVariable"))] + CannotUpdateConstVariable(String, #[label] Span), + #[error("Cannot cast expression of type {0} to type {1} as it would cause truncation.")] + #[diagnostic(code("Qsc.Qasm3.Compile.CastWouldCauseTruncation"))] + CastWouldCauseTruncation(String, String, #[label] Span), + #[error("Complex numbers in assignment binary expressions are not yet supported.")] + #[diagnostic(code("Qsc.Qasm3.Compile.ComplexBinaryAssignment"))] + ComplexBinaryAssignment(#[label] Span), + #[error("Designator must be a positive literal integer.")] + #[diagnostic(code("Qsc.Qasm3.Compile.DesignatorMustBePositiveIntLiteral"))] + DesignatorMustBePositiveIntLiteral(#[label] Span), + #[error("Designator is too large.")] + #[diagnostic(code("Qsc.Qasm3.Compile.DesignatorTooLarge"))] + DesignatorTooLarge(#[label] Span), + #[error("Failed to compile all expressions in expression list.")] + #[diagnostic(code("Qsc.Qasm3.Compile.FailedToCompileExpressionList"))] + FailedToCompileExpressionList(#[label] Span), + #[error("For iterable must have a set expression, range expression, or iterable expression.")] + #[diagnostic(code("Qsc.Qasm3.Compile.ForIterableInvalidExpression"))] + ForIterableInvalidExpression(#[label] Span), + #[error("For statements must have a body or statement.")] + #[diagnostic(code("Qsc.Qasm3.Compile.ForStatementsMustHaveABodyOrStatement"))] + ForStatementsMustHaveABodyOrStatement(#[label] Span), + #[error("If statement missing {0} expression.")] + #[diagnostic(code("Qsc.Qasm3.Compile.IfStmtMissingExpression"))] + IfStmtMissingExpression(String, #[label] Span), + #[error("include {0} could not be found.")] + #[diagnostic(code("Qsc.Qasm3.Compile.IncludeNotFound"))] + IncludeNotFound(String, #[label] Span), + #[error("include {0} must be declared in global scope.")] + #[diagnostic(code("Qsc.Qasm3.Compile.IncludeNotInGlobalScope"))] + IncludeNotInGlobalScope(String, #[label] Span), + #[error("include {0} must be declared in global scope.")] + #[diagnostic(code("Qsc.Qasm3.Compile.IncludeStatementMissingPath"))] + IncludeStatementMissingPath(#[label] Span), + #[error("Indexed must be a single expression.")] + #[diagnostic(code("Qsc.Qasm3.Compile.IndexMustBeSingleExpr"))] + IndexMustBeSingleExpr(#[label] Span), + #[error("Annotations only valid on gate definitions.")] + #[diagnostic(code("Qsc.Qasm3.Compile.InvalidAnnotationTarget"))] + InvalidAnnotationTarget(#[label] Span), + #[error("Assigning {0} values to {1} must be in a range that be converted to {1}.")] + #[diagnostic(code("Qsc.Qasm3.Compile.InvalidCastValueRange"))] + InvalidCastValueRange(String, String, #[label] Span), + #[error("Gate operands other than qubits or qubit arrays are not supported.")] + #[diagnostic(code("Qsc.Qasm3.Compile.InvalidGateOperand"))] + InvalidGateOperand(#[label] Span), + #[error("Control counts must be integer literals.")] + #[diagnostic(code("Qsc.Qasm3.Compile.InvalidControlCount"))] + InvalidControlCount(#[label] Span), + #[error("Gate operands other than qubit arrays are not supported.")] + #[diagnostic(code("Qsc.Qasm3.Compile.InvalidIndexedGateOperand"))] + InvalidIndexedGateOperand(#[label] Span), + #[error("Gate expects {0} classical arguments, but {1} were provided.")] + #[diagnostic(code("Qsc.Qasm3.Compile.InvalidNumberOfClassicalArgs"))] + InvalidNumberOfClassicalArgs(usize, usize, #[label] Span), + #[error("Gate expects {0} qubit arguments, but {1} were provided.")] + #[diagnostic(code("Qsc.Qasm3.Compile.InvalidNumberOfQubitArgs"))] + InvalidNumberOfQubitArgs(usize, usize, #[label] Span), + #[error("Measure statements must have a name.")] + #[diagnostic(code("Qsc.Qasm3.Compile.MeasureExpressionsMustHaveName"))] + MeasureExpressionsMustHaveName(#[label] Span), + #[error("Measure statements must have a gate operand name.")] + #[diagnostic(code("Qsc.Qasm3.Compile.MeasureExpressionsMustHaveGateOperand"))] + MeasureExpressionsMustHaveGateOperand(#[label] Span), + #[error("Control counts must be postitive integers.")] + #[diagnostic(code("Qsc.Qasm3.Compile.NegativeControlCount"))] + NegativeControlCount(#[label] Span), + #[error("{0} are not supported.")] + #[diagnostic(code("Qsc.Qasm3.Compile.NotSupported"))] + NotSupported(String, #[label] Span), + #[error("The operator {0} is not valid with lhs {1} and rhs {2}.")] + #[diagnostic(code("Qsc.Qasm3.Compile.OperatorNotSupportedForTypes"))] + OperatorNotSupportedForTypes(String, String, String, #[label] Span), + #[error("Pow gate modifiers must have an exponent.")] + #[diagnostic(code("Qsc.Qasm3.Compile.PowModifierMustHaveExponent"))] + PowModifierMustHaveExponent(#[label] Span), + #[error("Qiskit circuits must have output registers.")] + #[diagnostic(code("Qsc.Qasm3.Compile.QiskitEntryPointMissingOutput"))] + QiskitEntryPointMissingOutput(#[label] Span), + #[error("Quantum declarations must be done in global scope.")] + #[diagnostic(code("Qsc.Qasm3.Compile.QuantumDeclarationInNonGlobalScope"))] + QuantumDeclarationInNonGlobalScope(#[label] Span), + #[error("Quantum typed values cannot be used in binary expressions.")] + #[diagnostic(code("Qsc.Qasm3.Compile.QuantumTypesInBinaryExpression"))] + QuantumTypesInBinaryExpression(#[label] Span), + #[error("Range expressions must have a start.")] + #[diagnostic(code("Qsc.Qasm3.Compile.RangeExpressionsMustHaveStart"))] + RangeExpressionsMustHaveStart(#[label] Span), + #[error("Range expressions must have a stop.")] + #[diagnostic(code("Qsc.Qasm3.Compile.RangeExpressionsMustHaveStop"))] + RangeExpressionsMustHaveStop(#[label] Span), + #[error("Redefined symbol: {0}.")] + #[diagnostic(code("Qsc.Qasm3.Compile.RedefinedSymbol"))] + RedefinedSymbol(String, #[label] Span), + #[error("Reset expression must have a gate operand.")] + #[diagnostic(code("Qsc.Qasm3.Compile.ResetExpressionMustHaveGateOperand"))] + ResetExpressionMustHaveGateOperand(#[label] Span), + #[error("Reset expression must have a name.")] + #[diagnostic(code("Qsc.Qasm3.Compile.ResetExpressionMustHaveName"))] + ResetExpressionMustHaveName(#[label] Span), + #[error("Return statements are only allowed within subroutines.")] + #[diagnostic(code("Qsc.Qasm3.Compile.ReturnNotInSubroutine"))] + ReturnNotInSubroutine(#[label] Span), + #[error("Too many controls specified.")] + #[diagnostic(code("Qsc.Qasm3.Compile.TooManyControls"))] + TooManyControls(#[label] Span), + #[error("Bitwise not `~` is not allowed for instances of {0}.")] + #[diagnostic(code("Qsc.Qasm3.Compile.TypeDoesNotSupportBitwiseNot"))] + TypeDoesNotSupportBitwiseNot(String, #[label] Span), + #[error("Unary negation is not allowed for instances of {0}.")] + #[diagnostic(code("Qsc.Qasm3.Compile.TypeDoesNotSupportedUnaryNegation"))] + TypeDoesNotSupportedUnaryNegation(String, #[label] Span), + #[error("Types differ by dimensions and are incompatible.")] + #[diagnostic(code("Qsc.Qasm3.Compile.TypeRankError"))] + TypeRankError(#[label] Span), + #[error("Undefined symbol: {0}.")] + #[diagnostic(code("Qsc.Qasm3.Compile.UndefinedSymbol"))] + UndefinedSymbol(String, #[label] Span), + #[error("Unexpected parser error: {0}.")] + #[diagnostic(code("Qsc.Qasm3.Compile.UnexpectedParserError"))] + UnexpectedParserError(String, #[label] Span), + #[error("this statement is not yet handled during OpenQASM 3 import: {0}")] + #[diagnostic(code("Qsc.Qasm3.Compile.Unimplemented"))] + Unimplemented(String, #[label] Span), + #[error("Unexpected annotation: {0}.")] + #[diagnostic(code("Qsc.Qasm3.Compile.UnknownAnnotation"))] + UnknownAnnotation(String, #[label] Span), + #[error("Unknown index operation kind.")] + #[diagnostic(code("Qsc.Qasm3.Compile.UnknownIndexedOperatorKind"))] + UnknownIndexedOperatorKind(#[label] Span), + #[error("Unsupported version: '{0}'.")] + #[diagnostic(code("Qsc.Qasm3.Compile.UnsupportedVersion"))] + UnsupportedVersion(String, #[label] Span), + #[error("While statement missing {0} expression.")] + #[diagnostic(code("Qsc.Qasm3.Compile.WhileStmtMissingExpression"))] + WhileStmtMissingExpression(String, #[label] Span), +} + +impl SemanticErrorKind { + /// The semantic errors are reported with the span of the syntax that caused the error. + /// This offset is relative to the start of the file in which the error occurred. + /// This method is used to adjust the span of the error to be relative to where the + /// error was reported in the entire compilation unit as part of the source map. + #[allow(clippy::too_many_lines)] + fn with_offset(self, offset: u32) -> Self { + match self { + Self::ArrayLiteralInNonClassicalDecl(span) => { + Self::ArrayLiteralInNonClassicalDecl(span + offset) + } + Self::AnnotationWithoutStatement(span) => { + Self::AnnotationWithoutStatement(span + offset) + } + Self::CannotCast(lhs, rhs, span) => Self::CannotCast(lhs, rhs, span + offset), + Self::CastWouldCauseTruncation(lhs, rhs, span) => { + Self::CastWouldCauseTruncation(lhs, rhs, span + offset) + } + Self::CalibrationsNotSupported(name, span) => { + Self::CalibrationsNotSupported(name, span + offset) + } + Self::CannotAliasType(name, span) => Self::CannotAliasType(name, span + offset), + Self::CannotApplyOperatorToTypes(op, lhs, rhs, span) => { + Self::CannotApplyOperatorToTypes(op, lhs, rhs, span + offset) + } + Self::CannotAssignToType(lhs, rhs, span) => { + Self::CannotAssignToType(lhs, rhs, span + offset) + } + Self::CannotCallNonGate(span) => Self::CannotCallNonGate(span + offset), + Self::CannotIndexType(name, span) => Self::CannotIndexType(name, span + offset), + Self::CannotUpdateConstVariable(name, span) => { + Self::CannotUpdateConstVariable(name, span + offset) + } + Self::ComplexBinaryAssignment(span) => Self::ComplexBinaryAssignment(span + offset), + Self::DesignatorMustBePositiveIntLiteral(span) => { + Self::DesignatorMustBePositiveIntLiteral(span + offset) + } + Self::DesignatorTooLarge(span) => Self::DesignatorTooLarge(span + offset), + Self::FailedToCompileExpressionList(span) => { + Self::FailedToCompileExpressionList(span + offset) + } + Self::ForIterableInvalidExpression(span) => { + Self::ForIterableInvalidExpression(span + offset) + } + Self::ForStatementsMustHaveABodyOrStatement(span) => { + Self::ForStatementsMustHaveABodyOrStatement(span + offset) + } + Self::IfStmtMissingExpression(name, span) => { + Self::IfStmtMissingExpression(name, span + offset) + } + Self::IncludeNotFound(name, span) => Self::IncludeNotFound(name, span + offset), + Self::IncludeNotInGlobalScope(name, span) => { + Self::IncludeNotInGlobalScope(name, span + offset) + } + Self::IncludeStatementMissingPath(span) => { + Self::IncludeStatementMissingPath(span + offset) + } + Self::IndexMustBeSingleExpr(span) => Self::IndexMustBeSingleExpr(span + offset), + Self::InvalidAnnotationTarget(span) => Self::InvalidAnnotationTarget(span + offset), + Self::InvalidControlCount(span) => Self::InvalidControlCount(span + offset), + Self::InvalidNumberOfClassicalArgs(expected, actual, span) => { + Self::InvalidNumberOfClassicalArgs(expected, actual, span + offset) + } + Self::InvalidNumberOfQubitArgs(expected, actual, span) => { + Self::InvalidNumberOfQubitArgs(expected, actual, span + offset) + } + Self::InvalidCastValueRange(lhs, rhs, span) => { + Self::InvalidCastValueRange(lhs, rhs, span + offset) + } + Self::InvalidGateOperand(span) => Self::InvalidGateOperand(span + offset), + Self::InvalidIndexedGateOperand(span) => Self::InvalidIndexedGateOperand(span + offset), + Self::MeasureExpressionsMustHaveGateOperand(span) => { + Self::MeasureExpressionsMustHaveGateOperand(span + offset) + } + Self::MeasureExpressionsMustHaveName(span) => { + Self::MeasureExpressionsMustHaveName(span + offset) + } + Self::NegativeControlCount(span) => Self::NegativeControlCount(span + offset), + Self::NotSupported(name, span) => Self::NotSupported(name, span + offset), + Self::OperatorNotSupportedForTypes(op, lhs, rhs, span) => { + Self::OperatorNotSupportedForTypes(op, lhs, rhs, span + offset) + } + Self::PowModifierMustHaveExponent(span) => { + Self::PowModifierMustHaveExponent(span + offset) + } + Self::QiskitEntryPointMissingOutput(span) => { + Self::QiskitEntryPointMissingOutput(span + offset) + } + Self::QuantumDeclarationInNonGlobalScope(span) => { + Self::QuantumDeclarationInNonGlobalScope(span + offset) + } + Self::QuantumTypesInBinaryExpression(span) => { + Self::QuantumTypesInBinaryExpression(span + offset) + } + Self::RangeExpressionsMustHaveStart(span) => { + Self::RangeExpressionsMustHaveStart(span + offset) + } + Self::RangeExpressionsMustHaveStop(span) => { + Self::RangeExpressionsMustHaveStop(span + offset) + } + Self::RedefinedSymbol(name, span) => Self::RedefinedSymbol(name, span + offset), + Self::ResetExpressionMustHaveGateOperand(span) => { + Self::ResetExpressionMustHaveGateOperand(span + offset) + } + Self::ResetExpressionMustHaveName(span) => { + Self::ResetExpressionMustHaveName(span + offset) + } + Self::ReturnNotInSubroutine(span) => Self::ReturnNotInSubroutine(span + offset), + Self::TooManyControls(span) => Self::TooManyControls(span + offset), + Self::TypeDoesNotSupportBitwiseNot(name, span) => { + Self::TypeDoesNotSupportBitwiseNot(name, span + offset) + } + Self::TypeDoesNotSupportedUnaryNegation(name, span) => { + Self::TypeDoesNotSupportedUnaryNegation(name, span + offset) + } + Self::TypeRankError(span) => Self::TypeRankError(span + offset), + Self::UndefinedSymbol(name, span) => Self::UndefinedSymbol(name, span + offset), + Self::UnexpectedParserError(error, span) => { + Self::UnexpectedParserError(error, span + offset) + } + Self::Unimplemented(name, span) => Self::Unimplemented(name, span + offset), + Self::UnknownAnnotation(name, span) => Self::UnknownAnnotation(name, span + offset), + Self::UnknownIndexedOperatorKind(span) => { + Self::UnknownIndexedOperatorKind(span + offset) + } + Self::UnsupportedVersion(version, span) => { + Self::UnsupportedVersion(version, span + offset) + } + Self::WhileStmtMissingExpression(name, span) => { + Self::WhileStmtMissingExpression(name, span + offset) + } + } + } +} diff --git a/compiler/qsc_qasm3/src/semantic/lowerer.rs b/compiler/qsc_qasm3/src/semantic/lowerer.rs new file mode 100644 index 0000000000..b15e0a7c9d --- /dev/null +++ b/compiler/qsc_qasm3/src/semantic/lowerer.rs @@ -0,0 +1,1788 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +use std::ops::ShlAssign; +use std::path::PathBuf; + +use super::types::types_equal_except_const; +use super::types::unary_op_can_be_applied_to_type; +use super::types::Type; +use num_bigint::BigInt; +use num_traits::FromPrimitive; +use num_traits::Num; +use qsc_data_structures::span::Span; +use qsc_frontend::{compile::SourceMap, error::WithSource}; + +use super::symbols::{IOKind, Symbol, SymbolTable}; + +use crate::ast::list_from_iter; +use crate::oqasm_helpers::safe_i64_to_f64; +use crate::parser::QasmSource; +use crate::semantic::types::can_cast_literal; +use crate::semantic::types::can_cast_literal_with_value_knowledge; +use crate::semantic::types::ArrayDimensions; + +use super::{ + ast::{Stmt, Version}, + SemanticErrorKind, +}; + +pub(super) struct Lowerer { + /// The root QASM source to compile. + pub source: QasmSource, + /// The source map of QASM sources for error reporting. + pub source_map: SourceMap, + pub errors: Vec>, + /// The file stack is used to track the current file for error reporting. + /// When we include a file, we push the file path to the stack and pop it + /// when we are done with the file. + /// This allows us to report errors with the correct file path. + pub file_stack: Vec, + pub symbols: SymbolTable, + pub version: Option, + pub stmts: Vec, +} +impl Lowerer { + pub fn lower(mut self) -> crate::semantic::QasmSemanticParseResult { + // Should we fail if we see a version in included files? + let source = &self.source.clone(); + self.version = self.lower_version(source.program().version); + + self.lower_source(source); + + let program = super::ast::Program { + version: self.version, + statements: list_from_iter(self.stmts), + }; + + super::QasmSemanticParseResult { + source: self.source, + source_map: self.source_map, + symbols: self.symbols, + program, + errors: self.errors, + } + } + + fn lower_version(&mut self, version: Option) -> Option { + if let Some(version) = version { + if version.major != 3 { + self.push_semantic_error(SemanticErrorKind::UnsupportedVersion( + format!("{version}"), + version.span, + )); + } else if let Some(minor) = version.minor { + if minor != 0 && minor != 1 { + self.push_semantic_error(SemanticErrorKind::UnsupportedVersion( + format!("{version}"), + version.span, + )); + } + } + return Some(crate::semantic::ast::Version { + span: version.span, + major: version.major, + minor: version.minor, + }); + } + None + } + + /// Root recursive function for lowering the source. + fn lower_source(&mut self, source: &QasmSource) { + // we push the file path to the stack so we can track the current file + // for reporting errors. This saves us from having to pass around + // the current QasmSource value. + self.file_stack.push(source.path()); + + // we keep an iterator of the includes so we can match them with the + // source includes. The include statements only have the path, but + // we have already loaded all of source files in the + // `source.includes()` + let mut includes = source.includes().iter(); + + for stmt in &source.program().statements { + match &*stmt.kind { + crate::ast::StmtKind::Include(include) => { + // if we are not in the root we should not be able to include + // as this is a limitation of the QASM3 language + if !self.symbols.is_current_scope_global() { + let kind = SemanticErrorKind::IncludeNotInGlobalScope( + include.filename.to_string(), + include.span, + ); + self.push_semantic_error(kind); + continue; + } + + // special case for stdgates.inc + // it won't be in the includes list + if include.filename.to_lowercase() == "stdgates.inc" { + self.define_stdgates(include); + continue; + } + + let include = includes.next().expect("missing include"); + self.lower_source(include); + } + _ => { + if let Some(stmt) = self.lower_stmt(stmt) { + self.stmts.push(stmt); + } + } + } + } + + // Finally we pop the file path from the stack so that we + // can return to the previous file for error handling. + self.file_stack.pop(); + } + + #[allow(clippy::too_many_lines)] + fn lower_stmt(&mut self, stmt: &crate::ast::Stmt) -> Option { + let kind = match &*stmt.kind { + crate::ast::StmtKind::Alias(stmt) => { + super::ast::StmtKind::Alias(self.lower_alias(stmt)?) + } + crate::ast::StmtKind::Barrier(stmt) => { + super::ast::StmtKind::Barrier(self.lower_barrier(stmt)?) + } + crate::ast::StmtKind::Box(stmt) => super::ast::StmtKind::Box(self.lower_box(stmt)?), + crate::ast::StmtKind::Break(stmt) => self.lower_break(stmt)?, + crate::ast::StmtKind::Block(stmt) => { + super::ast::StmtKind::Block(Box::new(self.lower_block(stmt)?)) + } + crate::ast::StmtKind::Cal(stmt) => self.lower_calibration(stmt)?, + crate::ast::StmtKind::CalibrationGrammar(stmt) => { + super::ast::StmtKind::CalibrationGrammar(self.lower_calibration_grammar(stmt)?) + } + crate::ast::StmtKind::ClassicalDecl(stmt) => { + super::ast::StmtKind::ClassicalDecl(self.lower_classical_decl(stmt)?) + } + crate::ast::StmtKind::ConstDecl(stmt) => { + super::ast::StmtKind::ClassicalDecl(self.lower_const_decl(stmt)?) + } + crate::ast::StmtKind::Continue(stmt) => self.lower_continue_stmt(stmt)?, + crate::ast::StmtKind::Def(stmt) => super::ast::StmtKind::Def(self.lower_def(stmt)?), + crate::ast::StmtKind::DefCal(stmt) => { + super::ast::StmtKind::DefCal(self.lower_def_cal(stmt)?) + } + crate::ast::StmtKind::Delay(stmt) => { + super::ast::StmtKind::Delay(self.lower_delay(stmt)?) + } + crate::ast::StmtKind::Empty => { + // we ignore empty statements + None? + } + crate::ast::StmtKind::End(stmt) => { + super::ast::StmtKind::End(self.lower_end_stmt(stmt)?) + } + crate::ast::StmtKind::ExprStmt(stmt) => { + super::ast::StmtKind::ExprStmt(self.lower_expr_stmt(stmt)?) + } + crate::ast::StmtKind::ExternDecl(extern_decl) => { + super::ast::StmtKind::ExternDecl(self.lower_extern(extern_decl)?) + } + crate::ast::StmtKind::For(stmt) => { + super::ast::StmtKind::For(self.lower_for_stmt(stmt)?) + } + crate::ast::StmtKind::If(stmt) => super::ast::StmtKind::If(self.lower_if_stmt(stmt)?), + crate::ast::StmtKind::GateCall(stmt) => { + super::ast::StmtKind::GateCall(self.lower_gate_call(stmt)?) + } + crate::ast::StmtKind::GPhase(stmt) => { + super::ast::StmtKind::GPhase(self.lower_gphase(stmt)?) + } + crate::ast::StmtKind::Include(stmt) => { + super::ast::StmtKind::Include(self.lower_include(stmt)?) + } + crate::ast::StmtKind::IODeclaration(stmt) => { + super::ast::StmtKind::IODeclaration(self.lower_io_decl(stmt)?) + } + crate::ast::StmtKind::Measure(stmt) => { + super::ast::StmtKind::Measure(self.lower_measure(stmt)?) + } + crate::ast::StmtKind::Pragma(stmt) => { + super::ast::StmtKind::Pragma(self.lower_pragma(stmt)?) + } + crate::ast::StmtKind::QuantumGateDefinition(stmt) => { + super::ast::StmtKind::QuantumGateDefinition(self.lower_gate_def(stmt)?) + } + crate::ast::StmtKind::QuantumDecl(stmt) => { + super::ast::StmtKind::QuantumDecl(self.lower_quantum_decl(stmt)?) + } + crate::ast::StmtKind::Reset(stmt) => { + super::ast::StmtKind::Reset(self.lower_reset(stmt)?) + } + crate::ast::StmtKind::Return(stmt) => { + super::ast::StmtKind::Return(self.lower_return(stmt)?) + } + crate::ast::StmtKind::Switch(stmt) => { + super::ast::StmtKind::Switch(self.lower_switch(stmt)?) + } + crate::ast::StmtKind::WhileLoop(stmt) => { + super::ast::StmtKind::WhileLoop(self.lower_while_loop(stmt)?) + } + crate::ast::StmtKind::Err => { + self.push_semantic_error(SemanticErrorKind::UnexpectedParserError( + "Unexpected error".to_string(), + stmt.span, + )); + return None; + } + }; + let annotations = self.lower_annotations(&stmt.annotations, &stmt.kind); + Some(super::ast::Stmt { + span: stmt.span, + annotations: list_from_iter(annotations), + kind: Box::new(kind), + }) + } + + /// Define the standard gates in the symbol table. + /// The sdg, tdg, crx, cry, crz, and ch are defined + /// as their bare gates, and modifiers are applied + /// when calling them. + fn define_stdgates(&mut self, include: &crate::ast::IncludeStmt) { + fn gate_symbol(name: &str, cargs: u32, qargs: u32) -> Symbol { + Symbol { + name: name.to_string(), + ty: Type::Gate(cargs, qargs), + ..Default::default() + } + } + let gates = vec![ + gate_symbol("X", 0, 1), + gate_symbol("Y", 0, 1), + gate_symbol("Z", 0, 1), + gate_symbol("H", 0, 1), + gate_symbol("S", 0, 1), + gate_symbol("T", 0, 1), + gate_symbol("Rx", 1, 1), + gate_symbol("Rxx", 1, 2), + gate_symbol("Ry", 1, 1), + gate_symbol("Ryy", 1, 2), + gate_symbol("Rz", 1, 1), + gate_symbol("Rzz", 1, 2), + gate_symbol("CNOT", 0, 2), + gate_symbol("CY", 0, 2), + gate_symbol("CZ", 0, 2), + gate_symbol("I", 0, 1), + gate_symbol("SWAP", 0, 2), + gate_symbol("CCNOT", 0, 3), + ]; + for gate in gates { + let name = gate.name.clone(); + if self.symbols.insert_symbol(gate).is_err() { + self.push_redefined_symbol_error(name.as_str(), include.span); + } + } + } + + /// Pushes a missing symbol error with the given name + /// This is a convenience method for pushing a `SemanticErrorKind::UndefinedSymbol` error. + pub fn push_missing_symbol_error>(&mut self, name: S, span: Span) { + let kind = SemanticErrorKind::UndefinedSymbol(name.as_ref().to_string(), span); + self.push_semantic_error(kind); + } + + /// Pushes a redefined symbol error with the given name and span. + /// This is a convenience method for pushing a `SemanticErrorKind::RedefinedSymbol` error. + pub fn push_redefined_symbol_error>(&mut self, name: S, span: Span) { + let kind = SemanticErrorKind::RedefinedSymbol(name.as_ref().to_string(), span); + self.push_semantic_error(kind); + } + + /// Pushes an unsupported error with the supplied message. + pub fn push_unsupported_error_message>(&mut self, message: S, span: Span) { + let kind = SemanticErrorKind::NotSupported(message.as_ref().to_string(), span); + self.push_semantic_error(kind); + } + + /// Pushes an unimplemented error with the supplied message. + pub fn push_unimplemented_error_message>(&mut self, message: S, span: Span) { + let kind = SemanticErrorKind::Unimplemented(message.as_ref().to_string(), span); + self.push_semantic_error(kind); + } + + /// Pushes a semantic error with the given kind. + pub fn push_semantic_error(&mut self, kind: SemanticErrorKind) { + let kind = crate::ErrorKind::Semantic(crate::semantic::Error(kind)); + let error = self.create_err(kind); + self.errors.push(error); + } + + /// Creates an error from the given kind with the current source mapping. + fn create_err(&self, kind: crate::ErrorKind) -> WithSource { + let error = crate::Error(kind); + let path = self.file_stack.last().map_or("", |p| { + p.to_str().expect("expected source mapping to exist.") + }); + let source = self.source_map.find_by_name(path); + let offset = source.map_or(0, |x| x.offset); + let offset_error = error.with_offset(offset); + WithSource::from_map(&self.source_map, offset_error) + } + + fn lower_alias( + &mut self, + alias: &crate::ast::AliasDeclStmt, + ) -> Option { + let name = get_identifier_name(&alias.ident); + // alias statements do their types backwards, you read the right side + // and assign it to the left side. + // the types of the rhs should be in the symbol table. + let rhs = alias + .exprs + .iter() + .filter_map(|expr| self.lower_expr(expr)) + .collect::>(); + // TODO: handle multiple rhs + // TODO: validate consistency of rhs types + let first = rhs.first().expect("missing rhs"); + let symbol = Symbol { + name: name.to_string(), + ty: first.ty.clone(), + qsharp_ty: self.convert_semantic_type_to_qsharp_type(&first.ty, alias.ident.span())?, + span: alias.ident.span(), + io_kind: IOKind::Default, + }; + let Ok(symbol_id) = self.symbols.insert_symbol(symbol) else { + self.push_redefined_symbol_error(name, alias.span); + return None; + }; + + if rhs.len() != alias.exprs.len() { + // we failed + return None; + } + Some(super::ast::AliasDeclStmt { + span: alias.span, + symbol_id, + exprs: list_from_iter(rhs), + }) + } + + fn lower_expr(&mut self, expr: &crate::ast::Expr) -> Option { + match &*expr.kind { + crate::ast::ExprKind::Assign(_) => { + self.push_unimplemented_error_message("assign expr", expr.span); + None + } + crate::ast::ExprKind::AssignOp(_) => { + self.push_unimplemented_error_message("assignop expr", expr.span); + None + } + crate::ast::ExprKind::BinaryOp(_) => { + self.push_unimplemented_error_message("binary op expr", expr.span); + None + } + crate::ast::ExprKind::Cast(_) => { + self.push_unimplemented_error_message("cast expr", expr.span); + None + } + crate::ast::ExprKind::Err => { + unreachable!("Err expr should not be lowered"); + } + crate::ast::ExprKind::FunctionCall(_) => { + self.push_unimplemented_error_message("function call expr", expr.span); + None + } + crate::ast::ExprKind::Ident(ident) => self.lower_ident_expr(ident), + crate::ast::ExprKind::IndexExpr(_) => { + self.push_unimplemented_error_message("index expr", expr.span); + None + } + + crate::ast::ExprKind::Lit(lit) => self.lower_lit_expr(lit), + + crate::ast::ExprKind::Paren(expr) => self.lower_paren_expr(expr), + crate::ast::ExprKind::UnaryOp(expr) => self.lower_unary_op_expr(expr), + } + } + + fn lower_ident_expr(&mut self, ident: &crate::ast::Ident) -> Option { + let name = ident.name.clone(); + let Some((symbol_id, symbol)) = self.symbols.get_symbol_by_name(&name) else { + self.push_missing_symbol_error(&name, ident.span); + return None; + }; + + let kind = super::ast::ExprKind::Ident(symbol_id); + Some(super::ast::Expr { + span: ident.span, + kind: Box::new(kind), + ty: symbol.ty.clone(), + }) + } + + fn lower_lit_expr(&mut self, expr: &crate::ast::Lit) -> Option { + let (kind, ty) = match &expr.kind { + crate::ast::LiteralKind::BigInt(value) => { + // todo: this case is only valid when there is an integer literal + // that requires more than 64 bits to represent. We should probably + // introduce a new type for this as openqasm promotion rules don't + // cover this case as far as I know. + (super::ast::LiteralKind::BigInt(value.clone()), Type::Err) + } + crate::ast::LiteralKind::Bitstring(value, size) => ( + super::ast::LiteralKind::Bitstring(value.clone(), *size), + Type::BitArray(super::types::ArrayDimensions::One(*size), true), + ), + crate::ast::LiteralKind::Bool(value) => { + (super::ast::LiteralKind::Bool(*value), Type::Bool(true)) + } + crate::ast::LiteralKind::Int(value) => { + (super::ast::LiteralKind::Int(*value), Type::Int(None, true)) + } + crate::ast::LiteralKind::Float(value) => ( + super::ast::LiteralKind::Float(*value), + Type::Float(None, true), + ), + crate::ast::LiteralKind::Imaginary(value) => ( + super::ast::LiteralKind::Complex(0.0, *value), + Type::Complex(None, true), + ), + crate::ast::LiteralKind::String(_) => { + self.push_unsupported_error_message("String literals", expr.span); + return None; + } + crate::ast::LiteralKind::Duration(_, _) => { + self.push_unsupported_error_message("Duration literals", expr.span); + return None; + } + crate::ast::LiteralKind::Array(exprs) => { + // array literals are only valid in classical decals (const and mut) + // and we have to know the expected type of the array in order to lower it + // So we can't lower array literals in general. + self.push_semantic_error(SemanticErrorKind::ArrayLiteralInNonClassicalDecl( + expr.span, + )); + // place holder for now, this code will need to move to the correct place when we + // add support for classical decls + let texprs = exprs + .iter() + .filter_map(|expr| self.lower_expr(expr)) + .collect::>(); + if texprs.len() != exprs.len() { + // we failed to lower all the entries and an error was pushed + return None; + } + + ( + super::ast::LiteralKind::Array(list_from_iter(texprs)), + Type::Err, + ) + } + }; + Some(super::ast::Expr { + span: expr.span, + kind: Box::new(super::ast::ExprKind::Lit(kind)), + ty, + }) + } + + fn lower_paren_expr(&mut self, expr: &crate::ast::Expr) -> Option { + let expr = self.lower_expr(expr)?; + let span = expr.span; + let ty = expr.ty.clone(); + let kind = super::ast::ExprKind::Paren(expr); + Some(super::ast::Expr { + span, + kind: Box::new(kind), + ty, + }) + } + + fn lower_unary_op_expr(&mut self, expr: &crate::ast::UnaryOpExpr) -> Option { + match expr.op { + crate::ast::UnaryOp::Neg => { + if let crate::ast::ExprKind::Lit(lit) = expr.expr.kind.as_ref() { + self.lower_negated_literal_as_ty(lit, None, expr.expr.span) + } else { + let expr = self.lower_expr(&expr.expr)?; + let ty = expr.ty.clone(); + if unary_op_can_be_applied_to_type(crate::ast::UnaryOp::Neg, &ty) { + let span = expr.span; + let unary = super::ast::UnaryOpExpr { + op: super::ast::UnaryOp::Neg, + expr, + }; + Some(super::ast::Expr { + span, + kind: Box::new(super::ast::ExprKind::UnaryOp(unary)), + ty, + }) + } else { + let kind = SemanticErrorKind::TypeDoesNotSupportedUnaryNegation( + expr.ty.to_string(), + expr.span, + ); + self.push_semantic_error(kind); + None + } + } + } + crate::ast::UnaryOp::NotB => { + let expr = self.lower_expr(&expr.expr)?; + let ty = expr.ty.clone(); + if unary_op_can_be_applied_to_type(crate::ast::UnaryOp::NotB, &ty) { + let span = expr.span; + let unary = super::ast::UnaryOpExpr { + op: super::ast::UnaryOp::NotB, + expr, + }; + Some(super::ast::Expr { + span, + kind: Box::new(super::ast::ExprKind::UnaryOp(unary)), + ty, + }) + } else { + let kind = SemanticErrorKind::TypeDoesNotSupportedUnaryNegation( + expr.ty.to_string(), + expr.span, + ); + self.push_semantic_error(kind); + None + } + } + crate::ast::UnaryOp::NotL => { + // this is the only unary operator that tries to coerce the type + // I can't find it in the spec, but when looking at existing code + // it seems that the ! operator coerces to a bool if possible + let target_ty = Type::Bool(false); + let expr = + self.lower_expr_with_target_type(Some(&expr.expr), &target_ty, expr.expr.span)?; + + let ty = expr.ty.clone(); + + Some(super::ast::Expr { + span: expr.span, + kind: Box::new(super::ast::ExprKind::UnaryOp(super::ast::UnaryOpExpr { + op: super::ast::UnaryOp::NotL, + expr, + })), + ty, + }) + } + } + } + + fn lower_annotations( + &mut self, + annotations: &[Box], + kind: &crate::ast::StmtKind, + ) -> Vec { + annotations + .iter() + .map(|annotation| self.lower_annotation(annotation, kind)) + .collect::>() + } + + fn lower_annotation( + &mut self, + annotation: &crate::ast::Annotation, + kind: &crate::ast::StmtKind, + ) -> super::ast::Annotation { + if !matches!( + annotation.identifier.to_string().as_str(), + "SimulatableIntrinsic" | "Config" + ) { + self.push_unsupported_error_message( + format!("Annotation {}.", annotation.identifier), + annotation.span, + ); + } + + if let crate::ast::StmtKind::GateCall(_) = &kind { + self.push_unsupported_error_message( + format!( + "Annotation {} is only allowed on gate definitions.", + annotation.identifier + ), + annotation.span, + ); + } + + super::ast::Annotation { + span: annotation.span, + identifier: annotation.identifier.clone(), + value: annotation.value.as_ref().map(Clone::clone), + } + } + + fn convert_semantic_type_to_qsharp_type( + &mut self, + ty: &super::types::Type, + span: Span, + ) -> Option { + let is_const = ty.is_const(); + match ty { + Type::Bit(_) => Some(crate::types::Type::Result(is_const)), + Type::Qubit => Some(crate::types::Type::Qubit), + Type::HardwareQubit => { + let message = "HardwareQubit to Q# type"; + self.push_unsupported_error_message(message, span); + None + } + Type::Int(width, _) | Type::UInt(width, _) => { + if let Some(width) = width { + if *width > 64 { + Some(crate::types::Type::BigInt(is_const)) + } else { + Some(crate::types::Type::Int(is_const)) + } + } else { + Some(crate::types::Type::Int(is_const)) + } + } + Type::Float(_, _) | Type::Angle(_, _) => Some(crate::types::Type::Double(is_const)), + Type::Complex(_, _) => Some(crate::types::Type::Complex(is_const)), + Type::Bool(_) => Some(crate::types::Type::Bool(is_const)), + Type::Duration(_) => { + self.push_unsupported_error_message("Duration type values", span); + None + } + Type::Stretch(_) => { + self.push_unsupported_error_message("Stretch type values", span); + None + } + Type::BitArray(dims, _) => Some(crate::types::Type::ResultArray(dims.into(), is_const)), + Type::QubitArray(dims) => Some(crate::types::Type::QubitArray(dims.into())), + Type::IntArray(size, dims) | Type::UIntArray(size, dims) => { + if let Some(size) = size { + if *size > 64 { + Some(crate::types::Type::BigIntArray(dims.into(), is_const)) + } else { + Some(crate::types::Type::IntArray(dims.into(), is_const)) + } + } else { + Some(crate::types::Type::IntArray(dims.into(), is_const)) + } + } + Type::FloatArray(_, dims) => Some(crate::types::Type::DoubleArray(dims.into())), + Type::AngleArray(_, _) => todo!("AngleArray to Q# type"), + Type::ComplexArray(_, _) => todo!("ComplexArray to Q# type"), + Type::BoolArray(dims) => Some(crate::types::Type::BoolArray(dims.into(), is_const)), + Type::Gate(cargs, qargs) => Some(crate::types::Type::Callable( + crate::types::CallableKind::Operation, + *cargs, + *qargs, + )), + Type::Range => Some(crate::types::Type::Range), + Type::Set => todo!("Set to Q# type"), + Type::Void => Some(crate::types::Type::Tuple(vec![])), + _ => { + let msg = format!("Converting {ty:?} to Q# type"); + self.push_unimplemented_error_message(msg, span); + None + } + } + } + + fn lower_barrier(&mut self, stmt: &crate::ast::BarrierStmt) -> Option { + self.push_unimplemented_error_message("barrier stmt", stmt.span); + None + } + + fn lower_box(&mut self, stmt: &crate::ast::BoxStmt) -> Option { + self.push_unimplemented_error_message("box stmt", stmt.span); + None + } + + fn lower_break(&mut self, stmt: &crate::ast::BreakStmt) -> Option { + self.push_unimplemented_error_message("break stmt", stmt.span); + None + } + + fn lower_block(&mut self, stmt: &crate::ast::Block) -> Option { + self.push_unimplemented_error_message("block stmt", stmt.span); + None + } + + fn lower_calibration( + &mut self, + stmt: &crate::ast::CalibrationStmt, + ) -> Option { + self.push_unimplemented_error_message("calibration stmt", stmt.span); + None + } + + fn lower_calibration_grammar( + &mut self, + stmt: &crate::ast::CalibrationGrammarStmt, + ) -> Option { + self.push_unimplemented_error_message("calibration stmt", stmt.span); + None + } + + fn lower_classical_decl( + &mut self, + stmt: &crate::ast::ClassicalDeclarationStmt, + ) -> Option { + let is_const = false; // const decls are handled separately + let ty = self.get_semantic_type_from_tydef(&stmt.ty, is_const)?; + + let init_expr = stmt.init_expr.as_deref(); + let ty_span = stmt.ty.span(); + let stmt_span = stmt.span; + let name = stmt.identifier.name.clone(); + let qsharp_ty = self.convert_semantic_type_to_qsharp_type(&ty.clone(), ty_span)?; + let symbol = Symbol { + name: name.to_string(), + ty: ty.clone(), + qsharp_ty, + span: stmt.identifier.span, + io_kind: IOKind::Default, + }; + + // process the symbol and init_expr gathering any errors + let init_expr = match init_expr { + Some(expr) => match expr { + crate::ast::ValueExpression::Expr(expr) => self + .lower_expr_with_target_type(Some(expr), &ty, stmt_span) + .map(super::ast::ValueExpression::Expr), + crate::ast::ValueExpression::Measurement(measure_expr) => self + .lower_measure_expr_with_target_type(measure_expr, &ty, stmt_span) + .map(super::ast::ValueExpression::Measurement), + }, + None => self + .lower_expr_with_target_type(None, &ty, stmt_span) + .map(super::ast::ValueExpression::Expr), + }; + + let Ok(symbol_id) = self.symbols.insert_symbol(symbol) else { + self.push_redefined_symbol_error(&name, stmt.identifier.span); + return None; + }; + + // even if init_expr was None, Q# semantics require that we have an initial value + // for classical declarations. So if None is returned we hit an error with the expression. + let init_expr = init_expr?; + + Some(super::ast::ClassicalDeclarationStmt { + span: stmt_span, + ty_span, + symbol_id, + init_expr: Box::new(init_expr), + }) + } + + fn lower_const_decl( + &mut self, + stmt: &crate::ast::ConstantDeclStmt, + ) -> Option { + let is_const = true; + let ty = self.get_semantic_type_from_tydef(&stmt.ty, is_const)?; + let ty_span = stmt.ty.span(); + let name = stmt.identifier.name.clone(); + let qsharp_ty = self.convert_semantic_type_to_qsharp_type(&ty.clone(), stmt.ty.span())?; + let symbol = Symbol { + name: name.to_string(), + ty: ty.clone(), + qsharp_ty, + span: stmt.identifier.span, + io_kind: IOKind::Default, + }; + + // process the symbol and init_expr gathering any errors + let init_expr = self.lower_expr_with_target_type(Some(&stmt.init_expr), &ty, stmt.span); + + let Ok(symbol_id) = self.symbols.insert_symbol(symbol) else { + self.push_redefined_symbol_error(&name, stmt.identifier.span); + return None; + }; + + // even if init_expr was None, Q# semantics require that we have an initial value + // for classical declarations. So if None is returned we hit an error with the expression. + let init_expr = init_expr?; + + Some(super::ast::ClassicalDeclarationStmt { + span: stmt.span, + ty_span, + symbol_id, + init_expr: Box::new(super::ast::ValueExpression::Expr(init_expr)), + }) + } + + fn lower_continue_stmt( + &mut self, + stmt: &crate::ast::ContinueStmt, + ) -> Option { + self.push_unimplemented_error_message("continue stmt", stmt.span); + None + } + + fn lower_def(&mut self, stmt: &crate::ast::DefStmt) -> Option { + self.push_unimplemented_error_message("def stmt", stmt.span); + None + } + + fn lower_def_cal(&mut self, stmt: &crate::ast::DefCalStmt) -> Option { + self.push_unimplemented_error_message("def cal stmt", stmt.span); + None + } + + fn lower_delay(&mut self, stmt: &crate::ast::DelayStmt) -> Option { + self.push_unimplemented_error_message("delay stmt", stmt.span); + None + } + + fn lower_end_stmt(&mut self, stmt: &crate::ast::EndStmt) -> Option { + self.push_unimplemented_error_message("end stmt", stmt.span); + None + } + + fn lower_expr_stmt(&mut self, stmt: &crate::ast::ExprStmt) -> Option { + self.push_unimplemented_error_message("expr stmt", stmt.span); + None + } + + fn lower_extern(&mut self, stmt: &crate::ast::ExternDecl) -> Option { + self.push_unimplemented_error_message("extern stmt", stmt.span); + None + } + + fn lower_for_stmt(&mut self, stmt: &crate::ast::ForStmt) -> Option { + self.push_unimplemented_error_message("for stmt", stmt.span); + None + } + + fn lower_if_stmt(&mut self, stmt: &crate::ast::IfStmt) -> Option { + self.push_unimplemented_error_message("if stmt", stmt.span); + None + } + + fn lower_gate_call(&mut self, stmt: &crate::ast::GateCall) -> Option { + self.push_unimplemented_error_message("gate call stmt", stmt.span); + None + } + + fn lower_gphase(&mut self, stmt: &crate::ast::GPhase) -> Option { + self.push_unimplemented_error_message("gphase stmt", stmt.span); + None + } + + fn lower_include(&mut self, stmt: &crate::ast::IncludeStmt) -> Option { + self.push_unimplemented_error_message("include stmt", stmt.span); + None + } + + fn lower_io_decl( + &mut self, + stmt: &crate::ast::IODeclaration, + ) -> Option { + self.push_unimplemented_error_message("io decl stmt", stmt.span); + None + } + + fn lower_measure(&mut self, stmt: &crate::ast::MeasureStmt) -> Option { + self.push_unimplemented_error_message("measure stmt", stmt.span); + None + } + + fn lower_pragma(&mut self, stmt: &crate::ast::Pragma) -> Option { + self.push_unimplemented_error_message("pragma stmt", stmt.span); + None + } + + fn lower_gate_def( + &mut self, + stmt: &crate::ast::QuantumGateDefinition, + ) -> Option { + self.push_unimplemented_error_message("gate def stmt", stmt.span); + None + } + + fn lower_quantum_decl( + &mut self, + stmt: &crate::ast::QubitDeclaration, + ) -> Option { + self.push_unimplemented_error_message("qubit decl stmt", stmt.span); + None + } + + fn lower_reset(&mut self, stmt: &crate::ast::ResetStmt) -> Option { + self.push_unimplemented_error_message("reset stmt", stmt.span); + None + } + + fn lower_return(&mut self, stmt: &crate::ast::ReturnStmt) -> Option { + self.push_unimplemented_error_message("return stmt", stmt.span); + None + } + + fn lower_switch(&mut self, stmt: &crate::ast::SwitchStmt) -> Option { + self.push_unimplemented_error_message("switch stmt", stmt.span); + None + } + + fn lower_while_loop(&mut self, stmt: &crate::ast::WhileLoop) -> Option { + self.push_unimplemented_error_message("while loop stmt", stmt.span); + None + } + + fn get_semantic_type_from_tydef( + &mut self, + scalar_ty: &crate::ast::TypeDef, + is_const: bool, + ) -> Option { + match scalar_ty { + crate::ast::TypeDef::Scalar(scalar_type) => { + self.get_semantic_type_from_scalar_ty(scalar_type, is_const) + } + crate::ast::TypeDef::Array(array_type) => { + self.get_semantic_type_from_array_ty(array_type, is_const) + } + crate::ast::TypeDef::ArrayReference(array_reference_type) => { + self.get_semantic_type_from_array_reference_ty(array_reference_type, is_const) + } + } + } + + /// designators are positive integer literals when used + /// in the context of a type definition. + fn get_size_designator_from_expr(&mut self, expr: &crate::ast::Expr) -> Option { + if let crate::ast::ExprKind::Lit(lit) = expr.kind.as_ref() { + if let crate::ast::LiteralKind::Int(value) = lit.kind { + if value > 0 { + if let Ok(value) = u32::try_from(value) { + Some(value) + } else { + self.push_semantic_error(SemanticErrorKind::DesignatorTooLarge(lit.span)); + None + } + } else { + self.push_semantic_error( + SemanticErrorKind::DesignatorMustBePositiveIntLiteral(lit.span), + ); + None + } + } else { + self.push_semantic_error(SemanticErrorKind::DesignatorMustBePositiveIntLiteral( + lit.span, + )); + None + } + } else { + self.push_semantic_error(SemanticErrorKind::DesignatorMustBePositiveIntLiteral( + expr.span, + )); + None + } + } + + fn get_semantic_type_from_scalar_ty( + &mut self, + scalar_ty: &crate::ast::ScalarType, + is_const: bool, + ) -> Option { + match &scalar_ty.kind { + crate::ast::ScalarTypeKind::Bit(bit_type) => match &bit_type.size { + Some(size) => Some(crate::semantic::types::Type::BitArray( + super::types::ArrayDimensions::One(self.get_size_designator_from_expr(size)?), + is_const, + )), + None => Some(crate::semantic::types::Type::Bit(is_const)), + }, + crate::ast::ScalarTypeKind::Int(int_type) => match &int_type.size { + Some(size) => Some(crate::semantic::types::Type::Int( + Some(self.get_size_designator_from_expr(size)?), + is_const, + )), + None => Some(crate::semantic::types::Type::Int(None, is_const)), + }, + crate::ast::ScalarTypeKind::UInt(uint_type) => match &uint_type.size { + Some(size) => Some(crate::semantic::types::Type::UInt( + Some(self.get_size_designator_from_expr(size)?), + is_const, + )), + None => Some(crate::semantic::types::Type::UInt(None, is_const)), + }, + crate::ast::ScalarTypeKind::Float(float_type) => match &float_type.size { + Some(size) => Some(crate::semantic::types::Type::Float( + Some(self.get_size_designator_from_expr(size)?), + is_const, + )), + None => Some(crate::semantic::types::Type::Float(None, is_const)), + }, + crate::ast::ScalarTypeKind::Complex(complex_type) => match &complex_type.base_size { + Some(float_type) => match &float_type.size { + Some(size) => Some(crate::semantic::types::Type::Complex( + Some(self.get_size_designator_from_expr(size)?), + is_const, + )), + None => Some(crate::semantic::types::Type::Complex(None, is_const)), + }, + None => Some(crate::semantic::types::Type::Complex(None, is_const)), + }, + crate::ast::ScalarTypeKind::Angle(angle_type) => match &angle_type.size { + Some(size) => Some(crate::semantic::types::Type::Angle( + Some(self.get_size_designator_from_expr(size)?), + is_const, + )), + None => Some(crate::semantic::types::Type::Angle(None, is_const)), + }, + crate::ast::ScalarTypeKind::BoolType => { + Some(crate::semantic::types::Type::Bool(is_const)) + } + crate::ast::ScalarTypeKind::Duration => { + Some(crate::semantic::types::Type::Duration(is_const)) + } + crate::ast::ScalarTypeKind::Stretch => { + Some(crate::semantic::types::Type::Stretch(is_const)) + } + crate::ast::ScalarTypeKind::Err => Some(crate::semantic::types::Type::Err), + } + } + + fn get_semantic_type_from_array_ty( + &mut self, + array_ty: &crate::ast::ArrayType, + _is_const: bool, + ) -> Option { + self.push_unimplemented_error_message("semantic type from array type", array_ty.span); + None + } + + fn get_semantic_type_from_array_reference_ty( + &mut self, + array_ref_ty: &crate::ast::ArrayReferenceType, + _is_const: bool, + ) -> Option { + self.push_unimplemented_error_message( + "semantic type from array refence type", + array_ref_ty.span, + ); + None + } + fn lower_expr_with_target_type( + &mut self, + expr: Option<&crate::ast::Expr>, + ty: &Type, + span: Span, + ) -> Option { + let Some(expr) = expr else { + // In OpenQASM, classical variables may be uninitialized, but in Q#, + // they must be initialized. We will use the default value for the type + // to initialize the variable. + return self.get_default_value(ty, span); + }; + let rhs = self.lower_expr(expr)?; + let rhs_ty = rhs.ty.clone(); + // if we have an exact type match, we can use the rhs as is + if types_equal_except_const(ty, &rhs_ty) { + return Some(rhs); + } + + // if the rhs is a literal, we can try to cast it to the target type + // if they share the same base type. + if let super::ast::ExprKind::Lit(lit) = &*rhs.kind { + // if the rhs is a literal, we can try to coerce it to the lhs type + // we can do better than just types given we have a literal value + if can_cast_literal(ty, &rhs_ty) || can_cast_literal_with_value_knowledge(ty, lit) { + return self.coerce_literal_expr_to_type(ty, &rhs, lit); + } + // if we can't cast the literal, we can't proceed + // create a semantic error and return + let kind = SemanticErrorKind::CannotAssignToType( + format!("{:?}", rhs.ty), + format!("{ty:?}"), + span, + ); + self.push_semantic_error(kind); + return None; + } + // the lhs has a type, but the rhs may be of a different type with + // implicit and explicit conversions. We need to cast the rhs to the + // lhs type, but if that cast fails, we will have already pushed an error + // and we can't proceed + self.cast_expr_to_type(ty, &rhs, span) + } + + fn lower_measure_expr_with_target_type( + &mut self, + _expr: &crate::ast::MeasureExpr, + _ty: &Type, + span: Span, + ) -> Option { + self.push_unimplemented_error_message("measure expr with target type", span); + None + } + + fn get_default_value(&mut self, ty: &Type, span: Span) -> Option { + use super::ast::Expr; + use super::ast::ExprKind; + use super::ast::LiteralKind; + let from_lit_kind = |kind| -> Expr { + Expr { + span: Span::default(), + kind: Box::new(ExprKind::Lit(kind)), + ty: ty.as_const(), + } + }; + match ty { + Type::Bit(_) | Type::Int(_, _) | Type::UInt(_, _) => { + Some(from_lit_kind(LiteralKind::Int(0))) + } + Type::Bool(_) => Some(from_lit_kind(LiteralKind::Bool(false))), + Type::Angle(_, _) | Type::Float(_, _) => Some(from_lit_kind(LiteralKind::Float(0.0))), + Type::Complex(_, _) => Some(from_lit_kind(LiteralKind::Complex(0.0, 0.0))), + Type::Stretch(_) => { + let message = "Stretch default values"; + self.push_unsupported_error_message(message, span); + None + } + Type::Qubit => { + let message = "Qubit default values"; + self.push_unsupported_error_message(message, span); + None + } + Type::HardwareQubit => { + let message = "HardwareQubit default values"; + self.push_unsupported_error_message(message, span); + None + } + Type::QubitArray(_) => { + let message = "QubitArray default values"; + self.push_unsupported_error_message(message, span); + None + } + Type::BitArray(_, _) => { + self.push_unimplemented_error_message("bit array default value", span); + None + } + Type::BoolArray(_) => { + self.push_unimplemented_error_message("bool array default value", span); + None + } + Type::DurationArray(_) => { + self.push_unimplemented_error_message("duration array default value", span); + None + } + Type::AngleArray(_, _) => { + self.push_unimplemented_error_message("angle array default value", span); + None + } + Type::ComplexArray(_, _) => { + self.push_unimplemented_error_message("complex array default value", span); + None + } + Type::FloatArray(_, _) => { + self.push_unimplemented_error_message("float array default value", span); + None + } + Type::IntArray(_, _) => { + self.push_unimplemented_error_message("int array default value", span); + None + } + Type::UIntArray(_, _) => { + self.push_unimplemented_error_message("uint array default value", span); + None + } + Type::Duration(_) + | Type::Err + | Type::Gate(_, _) + | Type::Range + | Type::Set + | Type::Void => { + let message = format!("Default values for {ty:?} are unsupported."); + self.push_unsupported_error_message(message, span); + None + } + } + } + + #[allow(clippy::too_many_lines)] + fn coerce_literal_expr_to_type( + &mut self, + ty: &Type, + rhs: &super::ast::Expr, + kind: &super::ast::LiteralKind, + ) -> Option { + if *ty == rhs.ty { + // Base case, we shouldn't have gotten here + // but if we did, we can just return the rhs + return Some(rhs.clone()); + } + if types_equal_except_const(ty, &rhs.ty) { + // literals are always const, so we can safely return + // the const ty + return Some(super::ast::Expr { + span: rhs.span, + kind: rhs.kind.clone(), + ty: ty.as_const(), + }); + } + assert!(can_cast_literal(ty, &rhs.ty) || can_cast_literal_with_value_knowledge(ty, kind)); + let lhs_ty = ty.clone(); + let rhs_ty = rhs.ty.clone(); + let span = rhs.span; + + if matches!(lhs_ty, Type::Bit(..)) { + if let super::ast::LiteralKind::Int(value) = kind { + // can_cast_literal_with_value_knowledge guarantees that value is 0 or 1 + return Some(super::ast::Expr { + span, + kind: Box::new(super::ast::ExprKind::Lit(super::ast::LiteralKind::Int( + *value, + ))), + ty: lhs_ty.as_const(), + }); + } else if let super::ast::LiteralKind::Bool(value) = kind { + return Some(super::ast::Expr { + span, + kind: Box::new(super::ast::ExprKind::Lit(super::ast::LiteralKind::Int( + i64::from(*value), + ))), + ty: lhs_ty.as_const(), + }); + } + } + // if lhs_ty is 1 dim bitarray and rhs is int/uint, we can cast + let (is_int_to_bit_array, size) = match &lhs_ty { + Type::BitArray(dims, _) => { + if matches!(rhs.ty, Type::Int(..) | Type::UInt(..)) { + match dims { + &ArrayDimensions::One(size) => (true, size), + _ => (false, 0), + } + } else { + (false, 0) + } + } + _ => (false, 0), + }; + if is_int_to_bit_array { + if let super::ast::LiteralKind::Int(value) = kind { + if *value < 0 || *value >= (1 << size) { + // todo: error message + return None; + } + + let u_size = size as usize; + let bitstring = format!("{value:0u_size$b}"); + let Ok(value) = BigInt::from_str_radix(&bitstring, 2) else { + // todo: error message + return None; + }; + + return Some(super::ast::Expr { + span, + kind: Box::new(super::ast::ExprKind::Lit( + super::ast::LiteralKind::Bitstring(value, size), + )), + ty: lhs_ty.as_const(), + }); + } + } + if matches!(lhs_ty, Type::UInt(..)) { + if let super::ast::LiteralKind::Int(value) = kind { + // this should have been validated by can_cast_literal_with_value_knowledge + return Some(super::ast::Expr { + span, + kind: Box::new(super::ast::ExprKind::Lit(super::ast::LiteralKind::Int( + *value, + ))), + ty: lhs_ty.as_const(), + }); + } + } + let result = match (&lhs_ty, &rhs_ty) { + (Type::Float(..), Type::Int(..) | Type::UInt(..)) => { + if let super::ast::LiteralKind::Int(value) = kind { + if let Some(value) = safe_i64_to_f64(*value) { + return Some(super::ast::Expr { + span, + kind: Box::new(super::ast::ExprKind::Lit( + super::ast::LiteralKind::Float(value), + )), + ty: lhs_ty.as_const(), + }); + } + self.push_semantic_error(SemanticErrorKind::InvalidCastValueRange( + rhs_ty.to_string(), + lhs_ty.to_string(), + span, + )); + return None?; + } + None + } + (Type::Float(..), Type::Float(..)) => { + if let super::ast::LiteralKind::Float(value) = kind { + return Some(super::ast::Expr { + span, + kind: Box::new(super::ast::ExprKind::Lit(super::ast::LiteralKind::Float( + *value, + ))), + ty: lhs_ty.as_const(), + }); + } + None + } + (Type::Complex(..), Type::Complex(..)) => { + if let super::ast::LiteralKind::Complex(real, imag) = kind { + return Some(super::ast::Expr { + span, + kind: Box::new(super::ast::ExprKind::Lit( + super::ast::LiteralKind::Complex(*real, *imag), + )), + ty: lhs_ty.as_const(), + }); + } + None + } + (Type::Complex(..), Type::Float(..)) => { + if let super::ast::LiteralKind::Float(value) = kind { + return Some(super::ast::Expr { + span, + kind: Box::new(super::ast::ExprKind::Lit( + super::ast::LiteralKind::Complex(*value, 0.0), + )), + ty: lhs_ty.as_const(), + }); + } + None + } + (Type::Complex(..), Type::Int(..) | Type::UInt(..)) => { + // complex requires a double as input, so we need to + // convert the int to a double, then create the complex + if let super::ast::LiteralKind::Int(value) = kind { + if let Some(value) = safe_i64_to_f64(*value) { + return Some(super::ast::Expr { + span, + kind: Box::new(super::ast::ExprKind::Lit( + super::ast::LiteralKind::Complex(value, 0.0), + )), + ty: lhs_ty.as_const(), + }); + } + let kind = SemanticErrorKind::InvalidCastValueRange( + "Integer".to_string(), + "Double".to_string(), + span, + ); + self.push_semantic_error(kind); + return None?; + } + None + } + (Type::Bit(..), Type::Int(..) | Type::UInt(..)) => { + // we've already checked that the value is 0 or 1 + if let super::ast::LiteralKind::Int(value) = kind { + if *value == 0 || *value == 1 { + return Some(super::ast::Expr { + span, + kind: Box::new(super::ast::ExprKind::Lit( + super::ast::LiteralKind::Int(*value), + )), + ty: lhs_ty.as_const(), + }); + } + panic!("Value must be 0 or 1"); + } else { + panic!("Literal must be an IntNumber"); + } + } + (Type::Int(width, _), Type::Int(_, _) | Type::UInt(_, _)) => { + // we've already checked that this conversion can happen from a signed to unsigned int + match kind { + super::ast::LiteralKind::Int(value) => { + return Some(super::ast::Expr { + span, + kind: Box::new(super::ast::ExprKind::Lit( + super::ast::LiteralKind::Int(*value), + )), + ty: lhs_ty.as_const(), + }); + } + super::ast::LiteralKind::BigInt(value) => { + if let Some(width) = width { + let mut cap = BigInt::from_i64(1).expect("1 is a valid i64"); + BigInt::shl_assign(&mut cap, width); + if *value >= cap { + self.push_semantic_error(SemanticErrorKind::InvalidCastValueRange( + "BigInt".to_string(), + "Int".to_string(), + span, + )); + return None; + } + } + return Some(super::ast::Expr { + span, + kind: Box::new(super::ast::ExprKind::Lit( + super::ast::LiteralKind::BigInt(value.clone()), + )), + ty: lhs_ty.as_const(), + }); + } + _ => panic!("Literal must be an IntNumber or BigInt"), + } + } + _ => None, + }; + if result.is_none() { + // we assert that the type can be casted + // but we didn't cast it, so this is a bug + panic!("Literal type cast failed lhs: {:?}, rhs: {:?}", ty, rhs.ty); + } else { + result + } + } + + // Rules for negating literals are different than that of expressions + // What those rules are is not clear from the spec, so this is a best guess + // based on other qasm implementations. + fn lower_negated_literal_as_ty( + &mut self, + lit: &crate::ast::Lit, + target_ty: Option, + span: Span, + ) -> Option { + let (kind, ty) = (match &lit.kind { + crate::ast::LiteralKind::Float(value) => Some(( + super::ast::LiteralKind::Float(-value), + Type::Float(None, true), + )), + crate::ast::LiteralKind::Imaginary(value) => Some(( + super::ast::LiteralKind::Complex(0.0, -value), + Type::Complex(None, true), + )), + crate::ast::LiteralKind::Int(value) => { + Some((super::ast::LiteralKind::Int(-value), Type::Int(None, true))) + } + crate::ast::LiteralKind::BigInt(value) => { + let value = BigInt::from(-1) * value; + Some(( + super::ast::LiteralKind::BigInt(value), + Type::Int(None, true), + )) + } + crate::ast::LiteralKind::Duration(value, time_unit) => { + let unit = match time_unit { + crate::ast::TimeUnit::Dt => super::ast::TimeUnit::Dt, + crate::ast::TimeUnit::Ms => super::ast::TimeUnit::Ms, + crate::ast::TimeUnit::Ns => super::ast::TimeUnit::Ns, + crate::ast::TimeUnit::S => super::ast::TimeUnit::S, + crate::ast::TimeUnit::Us => super::ast::TimeUnit::Us, + }; + Some(( + super::ast::LiteralKind::Duration(-value, unit), + Type::Duration(true), + )) + } + crate::ast::LiteralKind::Array(_) => { + self.push_unsupported_error_message("negated array literal expressions", span); + None + } + crate::ast::LiteralKind::Bitstring(_, _) => { + self.push_unsupported_error_message("negated bitstring literal expressions", span); + None + } + crate::ast::LiteralKind::Bool(_) => { + self.push_unsupported_error_message("negated bool literal expressions", span); + None + } + crate::ast::LiteralKind::String(_) => { + self.push_unsupported_error_message("negated string literal expressions", span); + None + } + })?; + + let expr = super::ast::Expr { + span, + kind: Box::new(super::ast::ExprKind::Lit(kind.clone())), + ty, + }; + if let Some(target_ty) = target_ty { + return self.coerce_literal_expr_to_type(&target_ty, &expr, &kind); + } + Some(expr) + } + + fn cast_expr_to_type( + &mut self, + ty: &Type, + rhs: &super::ast::Expr, + span: Span, + ) -> Option { + let cast_expr = self.try_cast_expr_to_type(ty, rhs, span); + if cast_expr.is_none() { + let rhs_ty_name = format!("{:?}", rhs.ty); + let lhs_ty_name = format!("{ty:?}"); + let kind = SemanticErrorKind::CannotCast(rhs_ty_name, lhs_ty_name, span); + self.push_semantic_error(kind); + } + cast_expr + } + + fn try_cast_expr_to_type( + &mut self, + ty: &Type, + rhs: &super::ast::Expr, + span: Span, + ) -> Option { + if *ty == rhs.ty { + // Base case, we shouldn't have gotten here + // but if we did, we can just return the rhs + return Some(rhs.clone()); + } + if types_equal_except_const(ty, &rhs.ty) { + if rhs.ty.is_const() { + // lhs isn't const, but rhs is, we can just return the rhs + return Some(rhs.clone()); + } + // the lsh is supposed to be const but is being initialized + // to a non-const value, we can't allow this + return None; + } + // if the target type is wider, we can try to relax the rhs type + // We only do this for float and complex. Int types + // require extra handling for BigInts + match (ty, &rhs.ty) { + (Type::Float(w1, _), Type::Float(w2, _)) + | (Type::Complex(w1, _), Type::Complex(w2, _)) => { + if w1.is_none() && w2.is_some() { + return Some(super::ast::Expr { + span: rhs.span, + kind: rhs.kind.clone(), + ty: ty.clone(), + }); + } + + if *w1 >= *w2 { + return Some(super::ast::Expr { + span: rhs.span, + kind: rhs.kind.clone(), + ty: ty.clone(), + }); + } + } + _ => {} + } + // Casting of literals is handled elsewhere. This is for casting expressions + // which cannot be bypassed and must be handled by built-in Q# casts from + // the standard library. + match &rhs.ty { + Type::Angle(_, _) => self.cast_angle_expr_to_type(ty, rhs, span), + Type::Bit(_) => self.cast_bit_expr_to_type(ty, rhs, span), + Type::Bool(_) => Self::cast_bool_expr_to_type(ty, rhs), + Type::Complex(_, _) => cast_complex_expr_to_type(ty, rhs), + Type::Float(_, _) => self.cast_float_expr_to_type(ty, rhs, span), + Type::Int(_, _) | Type::UInt(_, _) => Self::cast_int_expr_to_type(ty, rhs), + Type::BitArray(dims, _) => Self::cast_bitarray_expr_to_type(dims, ty, rhs), + _ => None, + } + } + + /// +----------------+-------------------------------------------------------------+ + /// | Allowed casts | Casting To | + /// +----------------+-------+-----+------+-------+-------+-----+----------+-------+ + /// | Casting From | bool | int | uint | float | angle | bit | duration | qubit | + /// +----------------+-------+-----+------+-------+-------+-----+----------+-------+ + /// | angle | Yes | No | No | No | - | Yes | No | No | + /// +----------------+-------+-----+------+-------+-------+-----+----------+-------+ + fn cast_angle_expr_to_type( + &mut self, + ty: &Type, + rhs: &super::ast::Expr, + span: Span, + ) -> Option { + assert!(matches!(rhs.ty, Type::Bit(..))); + match ty { + Type::Bit(..) => { + let msg = "Cast angle to bit"; + self.push_unimplemented_error_message(msg, span); + None + } + Type::Bool(..) => { + let msg = "Cast angle to bool"; + self.push_unimplemented_error_message(msg, span); + None + } + + _ => None, + } + } + + /// +----------------+-------------------------------------------------------------+ + /// | Allowed casts | Casting To | + /// +----------------+-------+-----+------+-------+-------+-----+----------+-------+ + /// | Casting From | bool | int | uint | float | angle | bit | duration | qubit | + /// +----------------+-------+-----+------+-------+-------+-----+----------+-------+ + /// | bit | Yes | Yes | Yes | No | Yes | - | No | No | + /// +----------------+-------+-----+------+-------+-------+-----+----------+-------+ + fn cast_bit_expr_to_type( + &mut self, + ty: &Type, + rhs: &super::ast::Expr, + span: Span, + ) -> Option { + assert!(matches!(rhs.ty, Type::Bit(..))); + // There is no operand, choosing the span of the node + // but we could use the expr span as well. + match ty { + &Type::Angle(..) => { + let msg = "Cast bit to angle"; + self.push_unimplemented_error_message(msg, span); + None + } + &Type::Float(..) => { + // The spec says that this cast isn't supported, but it + // casts to other types that case to float. For now, we'll + // say it is invalid like the spec. + None + } + &Type::Bool(_) | &Type::Int(_, _) | &Type::UInt(_, _) => { + Some(wrap_expr_in_implicit_cast_expr(ty.clone(), rhs.clone())) + } + + _ => None, + } + } + + /// +----------------+-------------------------------------------------------------+ + /// | Allowed casts | Casting To | + /// +----------------+-------+-----+------+-------+-------+-----+----------+-------+ + /// | Casting From | bool | int | uint | float | angle | bit | duration | qubit | + /// +----------------+-------+-----+------+-------+-------+-----+----------+-------+ + /// | float | Yes | Yes | Yes | - | Yes | No | No | No | + /// +----------------+-------+-----+------+-------+-------+-----+----------+-------+ + /// + /// Additional cast to complex + fn cast_float_expr_to_type( + &mut self, + ty: &Type, + rhs: &super::ast::Expr, + span: Span, + ) -> Option { + assert!(matches!(rhs.ty, Type::Float(..))); + match ty { + &Type::Complex(_, _) | &Type::Int(_, _) | &Type::UInt(_, _) | &Type::Bool(_) => { + // this will eventually be a call into Complex(expr, 0.0) + Some(wrap_expr_in_implicit_cast_expr(ty.clone(), rhs.clone())) + } + &Type::Angle(..) => { + let msg = "Cast float to angle"; + self.push_unimplemented_error_message(msg, span); + None + } + _ => None, + } + } + + /// +----------------+-------------------------------------------------------------+ + /// | Allowed casts | Casting To | + /// +----------------+-------+-----+------+-------+-------+-----+----------+-------+ + /// | Casting From | bool | int | uint | float | angle | bit | duration | qubit | + /// +----------------+-------+-----+------+-------+-------+-----+----------+-------+ + /// | bool | - | Yes | Yes | Yes | No | Yes | No | No | + /// +----------------+-------+-----+------+-------+-------+-----+----------+-------+ + fn cast_bool_expr_to_type(ty: &Type, rhs: &super::ast::Expr) -> Option { + assert!(matches!(rhs.ty, Type::Bool(..))); + match ty { + &Type::Bit(_) | &Type::Float(_, _) | &Type::Int(_, _) | &Type::UInt(_, _) => { + Some(wrap_expr_in_implicit_cast_expr(ty.clone(), rhs.clone())) + } + _ => None, + } + } + + /// +----------------+-------------------------------------------------------------+ + /// | Allowed casts | Casting To | + /// +----------------+-------+-----+------+-------+-------+-----+----------+-------+ + /// | Casting From | bool | int | uint | float | angle | bit | duration | qubit | + /// +----------------+-------+-----+------+-------+-------+-----+----------+-------+ + /// | int | Yes | - | Yes | Yes | No | Yes | No | No | + /// +----------------+-------+-----+------+-------+-------+-----+----------+-------+ + /// | uint | Yes | Yes | - | Yes | No | Yes | No | No | + /// +----------------+-------+-----+------+-------+-------+-----+----------+-------+ + /// + /// Additional cast to ``BigInt`` + #[allow(clippy::too_many_lines)] + fn cast_int_expr_to_type(ty: &Type, rhs: &super::ast::Expr) -> Option { + assert!(matches!(rhs.ty, Type::Int(..) | Type::UInt(..))); + + match ty { + Type::BitArray(_, _) + | Type::Float(_, _) + | Type::Int(_, _) + | Type::UInt(_, _) + | Type::Bool(..) + | Type::Bit(..) + | Type::Complex(..) => Some(wrap_expr_in_implicit_cast_expr(ty.clone(), rhs.clone())), + _ => None, + } + } + + fn cast_bitarray_expr_to_type( + dims: &ArrayDimensions, + ty: &Type, + rhs: &super::ast::Expr, + ) -> Option { + let ArrayDimensions::One(array_width) = dims else { + return None; + }; + if !matches!(ty, Type::Int(..) | Type::UInt(..)) { + return None; + } + // we know we have a bit array being cast to an int/uint + // verfiy widths + let int_width = ty.width(); + + if int_width.is_none() || (int_width == Some(*array_width)) { + Some(wrap_expr_in_implicit_cast_expr(ty.clone(), rhs.clone())) + } else { + None + } + } +} + +fn wrap_expr_in_implicit_cast_expr(ty: Type, rhs: super::ast::Expr) -> super::ast::Expr { + super::ast::Expr { + span: Span::default(), + kind: Box::new(super::ast::ExprKind::Cast(super::ast::Cast { + span: Span::default(), + expr: rhs, + ty: ty.clone(), + })), + ty, + } +} + +/// +----------------+-------------------------------------------------------------+ +/// | Allowed casts | Casting To | +/// +----------------+-------+-----+------+-------+-------+-----+----------+-------+ +/// | Casting From | bool | int | uint | float | angle | bit | duration | qubit | +/// +----------------+-------+-----+------+-------+-------+-----+----------+-------+ +/// | complex | ?? | ?? | ?? | ?? | No | ?? | No | No | +/// +----------------+-------+-----+------+-------+-------+-----+----------+-------+ +fn cast_complex_expr_to_type(ty: &Type, rhs: &super::ast::Expr) -> Option { + assert!(matches!(rhs.ty, Type::Complex(..))); + + if matches!((ty, &rhs.ty), (Type::Complex(..), Type::Complex(..))) { + // we are both complex, but our widths are different. If both + // had implicit widths, we would have already matched for the + // (None, None). If the rhs width is bigger, we will return + // None to let the cast fail + + // Here, we can safely cast the rhs to the lhs type if the + // lhs width can hold the rhs's width + if ty.width().is_none() && rhs.ty.width().is_some() { + return Some(wrap_expr_in_implicit_cast_expr(ty.clone(), rhs.clone())); + } + if ty.width() >= rhs.ty.width() { + return Some(wrap_expr_in_implicit_cast_expr(ty.clone(), rhs.clone())); + } + } + None +} + +fn get_identifier_name(identifier: &crate::ast::Identifier) -> std::rc::Rc { + match identifier { + crate::ast::Identifier::Ident(ident) => ident.name.clone(), + crate::ast::Identifier::IndexedIdent(ident) => ident.name.name.clone(), + } +} diff --git a/compiler/qsc_qasm3/src/semantic/symbols.rs b/compiler/qsc_qasm3/src/semantic/symbols.rs new file mode 100644 index 0000000000..a9ebfe99a2 --- /dev/null +++ b/compiler/qsc_qasm3/src/semantic/symbols.rs @@ -0,0 +1,451 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +use qsc_data_structures::span::Span; +use rustc_hash::FxHashMap; + +use super::types::Type; + +/// We need a symbol table to keep track of the symbols in the program. +/// The scoping rules for QASM3 are slightly different from Q#. This also +/// means that we need to keep track of the input and output symbols in the +/// program. Additionally, we need to keep track of the types of the symbols +/// in the program for type checking. +/// Q# and QASM have different type systems, so we track the QASM semantic. +/// +/// A symbol ID is a unique identifier for a symbol in the symbol table. +/// This is used to look up symbols in the symbol table. +/// Every symbol in the symbol table has a unique ID. +#[derive(Debug, Default, Clone, Copy)] +pub struct SymbolId(pub u32); + +impl SymbolId { + /// The successor of this ID. + #[must_use] + pub fn successor(self) -> Self { + Self(self.0 + 1) + } +} + +impl From for SymbolId { + fn from(val: u32) -> Self { + SymbolId(val) + } +} + +impl From for u32 { + fn from(id: SymbolId) -> Self { + id.0 + } +} + +impl From for usize { + fn from(value: SymbolId) -> Self { + value.0 as usize + } +} + +impl From for SymbolId { + fn from(value: usize) -> Self { + SymbolId( + value + .try_into() + .unwrap_or_else(|_| panic!("Value, {value}, does not fit into SymbolId")), + ) + } +} + +impl PartialEq for SymbolId { + fn eq(&self, other: &Self) -> bool { + self.0 == other.0 + } +} + +impl Eq for SymbolId {} + +impl PartialOrd for SymbolId { + fn partial_cmp(&self, other: &Self) -> Option { + Some(self.cmp(other)) + } +} + +impl Ord for SymbolId { + fn cmp(&self, other: &Self) -> std::cmp::Ordering { + self.0.cmp(&other.0) + } +} + +impl std::hash::Hash for SymbolId { + fn hash(&self, state: &mut H) { + self.0.hash(state); + } +} + +impl std::fmt::Display for SymbolId { + fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + std::fmt::Display::fmt(&self.0, f) + } +} + +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct Symbol { + pub name: String, + pub span: Span, + pub ty: Type, + pub qsharp_ty: crate::types::Type, + pub io_kind: IOKind, +} + +impl std::fmt::Display for Symbol { + fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + use crate::ast::display_utils; + display_utils::writeln_header(f, "Symbol", self.span)?; + display_utils::writeln_field(f, "name", &self.name)?; + display_utils::writeln_field(f, "type", &self.ty)?; + display_utils::writeln_field(f, "qsharp_type", &self.qsharp_ty)?; + display_utils::write_field(f, "io_kind", &self.io_kind) + } +} + +/// A symbol in the symbol table. +/// Default Q# type is Unit +impl Default for Symbol { + fn default() -> Self { + Self { + name: String::default(), + span: Span::default(), + ty: Type::Err, + qsharp_ty: crate::types::Type::Tuple(vec![]), + io_kind: IOKind::default(), + } + } +} + +#[derive(Debug, Clone, PartialEq, Eq)] +pub enum SymbolError { + /// The symbol already exists in the symbol table, at the current scope. + AlreadyExists, +} + +/// Symbols have a an I/O kind that determines if they are input or output, or unspecified. +/// The default I/O kind means no explicit kind was part of the decl. +/// There is a specific statement for io decls which sets the I/O kind appropriately. +/// This is used to determine the input and output symbols in the program. +#[derive(Debug, Clone, PartialEq, Eq)] +pub enum IOKind { + Default, + Input, + Output, +} + +impl Default for IOKind { + fn default() -> Self { + Self::Default + } +} + +impl std::fmt::Display for IOKind { + fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + match self { + IOKind::Default => write!(f, "Default"), + IOKind::Input => write!(f, "Input"), + IOKind::Output => write!(f, "Output"), + } + } +} + +/// A scope is a collection of symbols and a kind. The kind determines semantic +/// rules for compliation as shadowing and decl rules vary by scope kind. +pub(crate) struct Scope { + /// A map from symbol name to symbol ID. + name_to_id: FxHashMap, + /// A map from symbol ID to symbol. + id_to_symbol: FxHashMap, + /// The order in which symbols were inserted into the scope. + /// This is used to determine the order of symbols in the output. + order: Vec, + /// The kind of the scope. + kind: ScopeKind, +} + +impl Scope { + pub fn new(kind: ScopeKind) -> Self { + Self { + name_to_id: FxHashMap::default(), + id_to_symbol: FxHashMap::default(), + order: vec![], + kind, + } + } + + /// Inserts the symbol into the current scope. + /// Returns the ID of the symbol. + /// + /// # Errors + /// + /// This function will return an error if a symbol of the same name has already + /// been declared in this scope. + pub fn insert_symbol(&mut self, id: SymbolId, symbol: Symbol) -> Result<(), SymbolError> { + if self.name_to_id.contains_key(&symbol.name) { + return Err(SymbolError::AlreadyExists); + } + self.name_to_id.insert(symbol.name.clone(), id); + self.id_to_symbol.insert(id, symbol); + self.order.push(id); + Ok(()) + } + + pub fn get_symbol_by_name(&self, name: &str) -> Option<(SymbolId, &Symbol)> { + self.name_to_id + .get(name) + .and_then(|id| self.id_to_symbol.get(id).map(|s| (*id, s))) + } + + fn get_ordered_symbols(&self) -> Vec { + self.order + .iter() + .map(|id| self.id_to_symbol.get(id).expect("ID should exist").clone()) + .collect() + } +} + +/// A symbol table is a collection of scopes and manages the symbol ids. +pub struct SymbolTable { + scopes: Vec, + current_id: SymbolId, +} + +#[derive(Debug, Clone, PartialEq, Eq)] +pub enum ScopeKind { + /// Global scope, which is the current scope only when no other scopes are active. + /// This is the only scope where gates, qubits, and arrays can be declared. + Global, + Function, + Gate, + Block, +} + +const BUILTIN_SYMBOLS: [&str; 6] = ["pi", "π", "tau", "τ", "euler", "ℇ"]; + +impl Default for SymbolTable { + fn default() -> Self { + let global = Scope::new(ScopeKind::Global); + + let mut slf = Self { + scopes: vec![global], + current_id: SymbolId::default(), + }; + + // Define global constants + for symbol in BUILTIN_SYMBOLS { + slf.insert_symbol(Symbol { + name: symbol.to_string(), + span: Span::default(), + ty: Type::Float(None, true), + qsharp_ty: crate::types::Type::Double(true), + io_kind: IOKind::Default, + }) + .unwrap_or_else(|_| panic!("Failed to insert symbol: {symbol}")); + } + + slf + } +} + +impl SymbolTable { + pub fn push_scope(&mut self, kind: ScopeKind) { + assert!(kind != ScopeKind::Global, "Cannot push a global scope"); + self.scopes.push(Scope::new(kind)); + } + + pub fn pop_scope(&mut self) { + assert!(self.scopes.len() != 1, "Cannot pop the global scope"); + self.scopes.pop(); + } + + pub fn insert_symbol(&mut self, symbol: Symbol) -> Result { + let id = self.current_id; + self.current_id = self.current_id.successor(); + self.scopes + .last_mut() + .expect("At least one scope should be available") + .insert_symbol(id, symbol)?; + + Ok(id) + } + + #[must_use] + pub fn get_symbol_by_id(&self, id: SymbolId) -> Option<(SymbolId, &Symbol)> { + for scope in self.scopes.iter().rev() { + if let Some(symbol) = scope.id_to_symbol.get(&id) { + return Some((id, symbol)); + } + } + None + } + + #[must_use] + pub fn get_symbol_by_name(&self, name: &str) -> Option<(SymbolId, &Symbol)> { + let scopes = self.scopes.iter().rev(); + let predicate = |x: &Scope| { + x.kind == ScopeKind::Block || x.kind == ScopeKind::Function || x.kind == ScopeKind::Gate + }; + + // Use scan to track the last item that returned false + let mut last_false = None; + let _ = scopes + .scan(&mut last_false, |state, item| { + if !predicate(item) { + **state = Some(item); + } + Some(predicate(item)) + }) + .take_while(|&x| x) + .last(); + let mut scopes = self.scopes.iter().rev(); + while let Some(scope) = scopes + .by_ref() + .take_while(|arg0: &&Scope| predicate(arg0)) + .next() + { + if let Some((id, symbol)) = scope.get_symbol_by_name(name) { + return Some((id, symbol)); + } + } + + if let Some(scope) = last_false { + if let Some((id, symbol)) = scope.get_symbol_by_name(name) { + if symbol.ty.is_const() + || matches!(symbol.ty, Type::Gate(..) | Type::Void) + || self.is_scope_rooted_in_global() + { + return Some((id, symbol)); + } + } + } + // we should be at the global, function, or gate scope now + for scope in scopes { + if let Some((id, symbol)) = scope.get_symbol_by_name(name) { + if symbol.ty.is_const() || matches!(symbol.ty, Type::Gate(..) | Type::Void) { + return Some((id, symbol)); + } + } + } + + None + } + + #[must_use] + pub fn is_current_scope_global(&self) -> bool { + matches!(self.scopes.last(), Some(scope) if scope.kind == ScopeKind::Global) + } + + #[must_use] + pub fn is_scope_rooted_in_subroutine(&self) -> bool { + self.scopes + .iter() + .rev() + .any(|scope| scope.kind == ScopeKind::Function) + } + + #[must_use] + pub fn is_scope_rooted_in_global(&self) -> bool { + for scope in self.scopes.iter().rev() { + if scope.kind == ScopeKind::Function { + return false; + } + if scope.kind == ScopeKind::Gate { + return false; + } + } + true + } + + /// Get the input symbols in the program. + pub(crate) fn get_input(&self) -> Option> { + let io_input = self.get_io_input(); + if io_input.is_empty() { + None + } else { + Some(io_input) + } + } + + /// Get the output symbols in the program. + /// Output symbols are either inferred or explicitly declared. + /// If there are no explicitly declared output symbols, then the inferred + /// output symbols are returned. + pub(crate) fn get_output(&self) -> Option> { + let io_ouput = self.get_io_output(); + if io_ouput.is_some() { + io_ouput + } else { + self.get_inferred_output() + } + } + + /// Get all symbols in the global scope that are inferred output symbols. + /// Any global symbol that is not a built-in symbol and has a type that is + /// inferred to be an output type is considered an inferred output symbol. + fn get_inferred_output(&self) -> Option> { + let mut symbols = vec![]; + self.scopes + .iter() + .filter(|scope| scope.kind == ScopeKind::Global) + .for_each(|scope| { + for symbol in scope + .get_ordered_symbols() + .into_iter() + .filter(|symbol| !BUILTIN_SYMBOLS.contains(&symbol.name.as_str())) + .filter(|symbol| symbol.io_kind == IOKind::Default) + { + if symbol.ty.is_inferred_output_type() { + symbols.push(symbol); + } + } + }); + if symbols.is_empty() { + None + } else { + Some(symbols) + } + } + + /// Get all symbols in the global scope that are output symbols. + fn get_io_output(&self) -> Option> { + let mut symbols = vec![]; + for scope in self + .scopes + .iter() + .filter(|scope| scope.kind == ScopeKind::Global) + { + for symbol in scope.get_ordered_symbols() { + if symbol.io_kind == IOKind::Output { + symbols.push(symbol); + } + } + } + if symbols.is_empty() { + None + } else { + Some(symbols) + } + } + + /// Get all symbols in the global scope that are input symbols. + fn get_io_input(&self) -> Vec { + let mut symbols = vec![]; + for scope in self + .scopes + .iter() + .filter(|scope| scope.kind == ScopeKind::Global) + { + for symbol in scope.get_ordered_symbols() { + if symbol.io_kind == IOKind::Input { + symbols.push(symbol); + } + } + } + symbols + } +} diff --git a/compiler/qsc_qasm3/src/semantic/tests.rs b/compiler/qsc_qasm3/src/semantic/tests.rs new file mode 100644 index 0000000000..8aa9e00e81 --- /dev/null +++ b/compiler/qsc_qasm3/src/semantic/tests.rs @@ -0,0 +1,152 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +pub mod decls; + +use std::path::Path; +use std::sync::Arc; + +use crate::io::InMemorySourceResolver; +use crate::io::SourceResolver; + +use super::parse_source; + +use super::QasmSemanticParseResult; + +use miette::Report; + +use expect_test::Expect; + +pub(crate) fn parse_all

( + path: P, + sources: impl IntoIterator, Arc)>, +) -> miette::Result> +where + P: AsRef, +{ + let resolver = InMemorySourceResolver::from_iter(sources); + let source = resolver.resolve(path.as_ref()).map_err(|e| vec![e])?.1; + let res = parse_source(source, path, &resolver).map_err(|e| vec![e])?; + if res.source.has_errors() { + let errors = res + .errors() + .into_iter() + .map(|e| Report::new(e.clone())) + .collect(); + Err(errors) + } else { + Ok(res) + } +} + +pub(crate) fn parse(source: S) -> miette::Result> +where + S: AsRef, +{ + let resolver = InMemorySourceResolver::from_iter([("test".into(), source.as_ref().into())]); + let res = parse_source(source, "test", &resolver).map_err(|e| vec![e])?; + if res.source.has_errors() { + let errors = res + .errors() + .into_iter() + .map(|e| Report::new(e.clone())) + .collect(); + return Err(errors); + } + Ok(res) +} + +pub(super) fn check(input: &str, expect: &Expect) { + check_map(input, expect, |p, _| p.to_string()); +} + +pub(super) fn check_classical_decl(input: &str, expect: &Expect) { + check_map(input, expect, |p, s| { + let kind = p + .statements + .first() + .expect("reading first statement") + .kind + .clone(); + let super::ast::StmtKind::ClassicalDecl(decl) = kind.as_ref() else { + panic!("expected classical declaration statement"); + }; + let mut value = decl.to_string(); + value.push('\n'); + let symbol = s + .get_symbol_by_id(decl.symbol_id) + .expect("getting symbol by id"); + value.push_str(&format!("[{}] {}", symbol.0, symbol.1)); + value + }); +} + +pub(super) fn check_classical_decls(input: &str, expect: &Expect) { + check_map(input, expect, |p, s| { + let kinds = p + .statements + .iter() + .map(|stmt| stmt.kind.as_ref().clone()) + .collect::>(); + let mut value = String::new(); + for kind in kinds { + let super::ast::StmtKind::ClassicalDecl(decl) = kind else { + panic!("expected classical declaration statement"); + }; + value.push_str(&decl.to_string()); + value.push('\n'); + let symbol = s + .get_symbol_by_id(decl.symbol_id) + .expect("getting symbol by id"); + value.push_str(&format!("[{}] {}", symbol.0, symbol.1)); + value.push('\n'); + } + + value + }); +} + +pub(super) fn check_stmt_kind(input: &str, expect: &Expect) { + check_map(input, expect, |p, _| { + p.statements + .first() + .expect("reading first statement") + .kind + .to_string() + }); +} + +pub(super) fn check_stmt_kinds(input: &str, expect: &Expect) { + check_map(input, expect, |p, _| { + p.statements + .iter() + .fold(String::new(), |acc, x| format!("{acc}{}\n", x.kind)) + }); +} + +fn check_map( + input: S, + expect: &Expect, + selector: impl FnOnce(&super::ast::Program, &super::SymbolTable) -> String, +) where + S: AsRef, +{ + let resolver = InMemorySourceResolver::from_iter([("test".into(), input.as_ref().into())]); + let res = parse_source(input, "test", &resolver) + .map_err(|e| vec![e]) + .expect("failed to parse"); + let errors = res.all_errors(); + + if errors.is_empty() { + expect.assert_eq(&selector(&res.program, &res.symbols)); + } else { + expect.assert_eq(&format!( + "{}\n\n{:?}", + res.program, + errors + .iter() + .map(|e| Report::new(e.clone())) + .collect::>() + )); + } +} diff --git a/compiler/qsc_qasm3/src/semantic/tests/decls.rs b/compiler/qsc_qasm3/src/semantic/tests/decls.rs new file mode 100644 index 0000000000..f5f926e956 --- /dev/null +++ b/compiler/qsc_qasm3/src/semantic/tests/decls.rs @@ -0,0 +1,97 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +mod bit; +mod bool; +mod complex; +mod creg; +mod duration; +mod float; +mod int; +mod qreg; +mod stretch; +mod uint; + +use expect_test::expect; + +use super::check; + +#[test] +#[ignore = "Not yet implemented"] +fn duration_and_stretch_types_without_init_exprs() { + check( + r#" + duration i; + stretch n; + "#, + &expect![[r#" + + + [Qsc.Qasm3.Compile.NotSupported + + x Duration type values are not supported. + ,-[test:2:9] + 1 | + 2 | duration i; + : ^^^^^^^^ + 3 | stretch n; + `---- + , Qsc.Qasm3.Compile.NotSupported + + x Stretch type values are not supported. + ,-[test:3:9] + 2 | duration i; + 3 | stretch n; + : ^^^^^^^ + 4 | + `---- + ]"#]], + ); +} + +#[test] +fn scalar_ty_designator_must_be_positive() { + check( + "int[-5] i;", + &expect![[r#" + Program: + version: + statements: + + [Qsc.Qasm3.Compile.DesignatorMustBePositiveIntLiteral + + x Designator must be a positive literal integer. + ,-[test:1:5] + 1 | int[-5] i; + : ^^ + `---- + ]"#]], + ); +} + +#[test] +fn scalar_ty_designator_must_be_int_literal() { + check( + r#"int[size] i; float[0.0] j;"#, + &expect![[r#" + Program: + version: + statements: + + [Qsc.Qasm3.Compile.DesignatorMustBePositiveIntLiteral + + x Designator must be a positive literal integer. + ,-[test:1:5] + 1 | int[size] i; float[0.0] j; + : ^^^^ + `---- + , Qsc.Qasm3.Compile.DesignatorMustBePositiveIntLiteral + + x Designator must be a positive literal integer. + ,-[test:1:20] + 1 | int[size] i; float[0.0] j; + : ^^^ + `---- + ]"#]], + ); +} diff --git a/compiler/qsc_qasm3/src/semantic/tests/decls/bit.rs b/compiler/qsc_qasm3/src/semantic/tests/decls/bit.rs new file mode 100644 index 0000000000..dc29049bf5 --- /dev/null +++ b/compiler/qsc_qasm3/src/semantic/tests/decls/bit.rs @@ -0,0 +1,123 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +use expect_test::expect; + +use crate::semantic::tests::check_classical_decl; + +#[test] +fn with_no_init_expr_has_generated_lit_expr() { + check_classical_decl( + "bit a;", + &expect![[r#" + ClassicalDeclarationStmt [0-6]: + symbol_id: 6 + ty_span: [0-3] + init_expr: Expr [0-0]: + ty: Bit(true) + kind: Lit: Int(0) + [6] Symbol [4-5]: + name: a + type: Bit(false) + qsharp_type: Result + io_kind: Default"#]], + ); +} + +#[test] +#[ignore = "Unimplemented"] +fn array_with_no_init_expr_has_generated_lit_expr() { + check_classical_decl( + "bit[4] a;", + &expect![[r#" + Program: + version: + statements: + + [Qsc.Qasm3.Compile.Unimplemented + + x this statement is not yet handled during OpenQASM 3 import: bit array + | default value + ,-[test:1:1] + 1 | bit[4] a; + : ^^^^^^^^^ + `---- + ]"#]], + ); +} + +#[test] +fn decl_with_lit_0_init_expr() { + check_classical_decl( + "bit a = 0;", + &expect![[r#" + ClassicalDeclarationStmt [0-10]: + symbol_id: 6 + ty_span: [0-3] + init_expr: Expr [8-9]: + ty: Bit(true) + kind: Lit: Int(0) + [6] Symbol [4-5]: + name: a + type: Bit(false) + qsharp_type: Result + io_kind: Default"#]], + ); +} + +#[test] +fn decl_with_lit_1_init_expr() { + check_classical_decl( + "bit a = 1;", + &expect![[r#" + ClassicalDeclarationStmt [0-10]: + symbol_id: 6 + ty_span: [0-3] + init_expr: Expr [8-9]: + ty: Bit(true) + kind: Lit: Int(1) + [6] Symbol [4-5]: + name: a + type: Bit(false) + qsharp_type: Result + io_kind: Default"#]], + ); +} + +#[test] +fn const_decl_with_lit_0_init_expr() { + check_classical_decl( + "const bit a = 0;", + &expect![[r#" + ClassicalDeclarationStmt [0-16]: + symbol_id: 6 + ty_span: [6-9] + init_expr: Expr [14-15]: + ty: Bit(true) + kind: Lit: Int(0) + [6] Symbol [10-11]: + name: a + type: Bit(true) + qsharp_type: Result + io_kind: Default"#]], + ); +} + +#[test] +fn const_decl_with_lit_1_init_expr() { + check_classical_decl( + "const bit a = 1;", + &expect![[r#" + ClassicalDeclarationStmt [0-16]: + symbol_id: 6 + ty_span: [6-9] + init_expr: Expr [14-15]: + ty: Bit(true) + kind: Lit: Int(1) + [6] Symbol [10-11]: + name: a + type: Bit(true) + qsharp_type: Result + io_kind: Default"#]], + ); +} diff --git a/compiler/qsc_qasm3/src/semantic/tests/decls/bool.rs b/compiler/qsc_qasm3/src/semantic/tests/decls/bool.rs new file mode 100644 index 0000000000..d6b7dc7ea3 --- /dev/null +++ b/compiler/qsc_qasm3/src/semantic/tests/decls/bool.rs @@ -0,0 +1,123 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +use expect_test::expect; + +use crate::semantic::tests::check_classical_decl; + +#[test] +fn with_no_init_expr_has_generated_lit_expr() { + check_classical_decl( + "bool a;", + &expect![[r#" + ClassicalDeclarationStmt [0-7]: + symbol_id: 6 + ty_span: [0-4] + init_expr: Expr [0-0]: + ty: Bool(true) + kind: Lit: Bool(false) + [6] Symbol [5-6]: + name: a + type: Bool(false) + qsharp_type: bool + io_kind: Default"#]], + ); +} + +#[test] +#[ignore = "Unimplemented"] +fn array_with_no_init_expr_has_generated_lit_expr() { + check_classical_decl( + "bool[4] a;", + &expect![[r#" + Program: + version: + statements: + + [Qsc.Qasm3.Compile.Unimplemented + + x this statement is not yet handled during OpenQASM 3 import: bool array + | default value + ,-[test:1:1] + 1 | bool[4] a; + : ^^^^^^^^^ + `---- + ]"#]], + ); +} + +#[test] +fn decl_with_lit_false_init_expr() { + check_classical_decl( + "bool a = false;", + &expect![[r#" + ClassicalDeclarationStmt [0-15]: + symbol_id: 6 + ty_span: [0-4] + init_expr: Expr [9-14]: + ty: Bool(true) + kind: Lit: Bool(false) + [6] Symbol [5-6]: + name: a + type: Bool(false) + qsharp_type: bool + io_kind: Default"#]], + ); +} + +#[test] +fn decl_with_lit_true_init_expr() { + check_classical_decl( + "bool a = true;", + &expect![[r#" + ClassicalDeclarationStmt [0-14]: + symbol_id: 6 + ty_span: [0-4] + init_expr: Expr [9-13]: + ty: Bool(true) + kind: Lit: Bool(true) + [6] Symbol [5-6]: + name: a + type: Bool(false) + qsharp_type: bool + io_kind: Default"#]], + ); +} + +#[test] +fn const_decl_with_lit_false_init_expr() { + check_classical_decl( + "const bool a = false;", + &expect![[r#" + ClassicalDeclarationStmt [0-21]: + symbol_id: 6 + ty_span: [6-10] + init_expr: Expr [15-20]: + ty: Bool(true) + kind: Lit: Bool(false) + [6] Symbol [11-12]: + name: a + type: Bool(true) + qsharp_type: bool + io_kind: Default"#]], + ); +} + +#[test] +fn const_decl_with_lit_true_init_expr() { + check_classical_decl( + "const bool a = true;", + &expect![[r#" + ClassicalDeclarationStmt [0-20]: + symbol_id: 6 + ty_span: [6-10] + init_expr: Expr [15-19]: + ty: Bool(true) + kind: Lit: Bool(true) + [6] Symbol [11-12]: + name: a + type: Bool(true) + qsharp_type: bool + io_kind: Default"#]], + ); +} diff --git a/compiler/qsc_qasm3/src/semantic/tests/decls/complex.rs b/compiler/qsc_qasm3/src/semantic/tests/decls/complex.rs new file mode 100644 index 0000000000..62c3a38ad8 --- /dev/null +++ b/compiler/qsc_qasm3/src/semantic/tests/decls/complex.rs @@ -0,0 +1,393 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +use expect_test::expect; + +use crate::semantic::tests::check_classical_decl; + +#[test] +fn implicit_bitness_default() { + check_classical_decl( + "complex[float] x;", + &expect![[r#" + ClassicalDeclarationStmt [0-17]: + symbol_id: 6 + ty_span: [0-14] + init_expr: Expr [0-0]: + ty: Complex(None, true) + kind: Lit: Complex(0.0, 0.0) + [6] Symbol [15-16]: + name: x + type: Complex(None, false) + qsharp_type: Complex + io_kind: Default"#]], + ); +} + +#[test] +fn const_implicit_bitness_default() { + check_classical_decl( + "const complex[float] x;", + &expect![[r#" + Program: + version: + statements: + + [Qasm3.Parse.Token + + x expected `=`, found `;` + ,-[test:1:23] + 1 | const complex[float] x; + : ^ + `---- + , Qsc.Qasm3.Compile.UnexpectedParserError + + x Unexpected parser error: Unexpected error. + ,-[test:1:1] + 1 | const complex[float] x; + : ^^^^^^^^^^^^^^^^^^^^^^^ + `---- + ]"#]], + ); +} + +#[test] +fn explicit_bitness_default() { + check_classical_decl( + "complex[float[42]] x;", + &expect![[r#" + ClassicalDeclarationStmt [0-21]: + symbol_id: 6 + ty_span: [0-18] + init_expr: Expr [0-0]: + ty: Complex(Some(42), true) + kind: Lit: Complex(0.0, 0.0) + [6] Symbol [19-20]: + name: x + type: Complex(Some(42), false) + qsharp_type: Complex + io_kind: Default"#]], + ); +} + +#[test] +fn const_explicit_bitness_default() { + check_classical_decl( + "const complex[float[42]] x;", + &expect![[r#" + Program: + version: + statements: + + [Qasm3.Parse.Token + + x expected `=`, found `;` + ,-[test:1:27] + 1 | const complex[float[42]] x; + : ^ + `---- + , Qsc.Qasm3.Compile.UnexpectedParserError + + x Unexpected parser error: Unexpected error. + ,-[test:1:1] + 1 | const complex[float[42]] x; + : ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + `---- + ]"#]], + ); +} + +#[test] +fn const_implicit_bitness_double_img_only() { + check_classical_decl( + "const complex[float] x = 1.01im;", + &expect![[r#" + ClassicalDeclarationStmt [0-32]: + symbol_id: 6 + ty_span: [6-20] + init_expr: Expr [25-31]: + ty: Complex(None, true) + kind: Lit: Complex(0.0, 1.01) + [6] Symbol [21-22]: + name: x + type: Complex(None, true) + qsharp_type: Complex + io_kind: Default"#]], + ); +} + +#[test] +fn const_implicit_bitness_int_img_only() { + check_classical_decl( + "const complex[float] x = 1im;", + &expect![[r#" + ClassicalDeclarationStmt [0-29]: + symbol_id: 6 + ty_span: [6-20] + init_expr: Expr [25-28]: + ty: Complex(None, true) + kind: Lit: Complex(0.0, 1.0) + [6] Symbol [21-22]: + name: x + type: Complex(None, true) + qsharp_type: Complex + io_kind: Default"#]], + ); +} + +#[test] +fn const_explicit_bitness_double_img_only() { + check_classical_decl( + "const complex[float[42]] x = 1.01im;", + &expect![[r#" + ClassicalDeclarationStmt [0-36]: + symbol_id: 6 + ty_span: [6-24] + init_expr: Expr [29-35]: + ty: Complex(Some(42), true) + kind: Lit: Complex(0.0, 1.01) + [6] Symbol [25-26]: + name: x + type: Complex(Some(42), true) + qsharp_type: Complex + io_kind: Default"#]], + ); +} + +#[test] +fn const_explicit_bitness_int_img_only() { + check_classical_decl( + "const complex[float[42]] x = 1im;", + &expect![[r#" + ClassicalDeclarationStmt [0-33]: + symbol_id: 6 + ty_span: [6-24] + init_expr: Expr [29-32]: + ty: Complex(Some(42), true) + kind: Lit: Complex(0.0, 1.0) + [6] Symbol [25-26]: + name: x + type: Complex(Some(42), true) + qsharp_type: Complex + io_kind: Default"#]], + ); +} + +#[test] +fn implicit_bitness_double_img_only() { + check_classical_decl( + "complex[float] x = 1.01im;", + &expect![[r#" + ClassicalDeclarationStmt [0-26]: + symbol_id: 6 + ty_span: [0-14] + init_expr: Expr [19-25]: + ty: Complex(None, true) + kind: Lit: Complex(0.0, 1.01) + [6] Symbol [15-16]: + name: x + type: Complex(None, false) + qsharp_type: Complex + io_kind: Default"#]], + ); +} + +#[test] +fn implicit_bitness_int_img_only() { + check_classical_decl( + "complex[float] x = 1im;", + &expect![[r#" + ClassicalDeclarationStmt [0-23]: + symbol_id: 6 + ty_span: [0-14] + init_expr: Expr [19-22]: + ty: Complex(None, true) + kind: Lit: Complex(0.0, 1.0) + [6] Symbol [15-16]: + name: x + type: Complex(None, false) + qsharp_type: Complex + io_kind: Default"#]], + ); +} + +#[test] +fn const_implicit_bitness_double_real_only() { + check_classical_decl( + "const complex[float] x = 1.01;", + &expect![[r#" + ClassicalDeclarationStmt [0-30]: + symbol_id: 6 + ty_span: [6-20] + init_expr: Expr [25-29]: + ty: Complex(None, true) + kind: Lit: Complex(1.01, 0.0) + [6] Symbol [21-22]: + name: x + type: Complex(None, true) + qsharp_type: Complex + io_kind: Default"#]], + ); +} + +#[test] +fn const_implicit_bitness_int_real_only() { + check_classical_decl( + "const complex[float] x = 1;", + &expect![[r#" + ClassicalDeclarationStmt [0-27]: + symbol_id: 6 + ty_span: [6-20] + init_expr: Expr [25-26]: + ty: Complex(None, true) + kind: Lit: Complex(1.0, 0.0) + [6] Symbol [21-22]: + name: x + type: Complex(None, true) + qsharp_type: Complex + io_kind: Default"#]], + ); +} + +#[test] +fn implicit_bitness_double_real_only() { + check_classical_decl( + "complex[float] x = 1.01;", + &expect![[r#" + ClassicalDeclarationStmt [0-24]: + symbol_id: 6 + ty_span: [0-14] + init_expr: Expr [19-23]: + ty: Complex(None, true) + kind: Lit: Complex(1.01, 0.0) + [6] Symbol [15-16]: + name: x + type: Complex(None, false) + qsharp_type: Complex + io_kind: Default"#]], + ); +} + +#[test] +fn implicit_bitness_int_real_only() { + check_classical_decl( + "complex[float] x = 1;", + &expect![[r#" + ClassicalDeclarationStmt [0-21]: + symbol_id: 6 + ty_span: [0-14] + init_expr: Expr [19-20]: + ty: Complex(None, true) + kind: Lit: Complex(1.0, 0.0) + [6] Symbol [15-16]: + name: x + type: Complex(None, false) + qsharp_type: Complex + io_kind: Default"#]], + ); +} + +#[test] +#[ignore = "Requires support for binary operators"] +fn implicit_bitness_simple_double_pos_im() { + check_classical_decl( + "complex[float] x = 1.1 + 2.2im;", + &expect![[r#" + Program: + version: + statements: + + [Qsc.Qasm3.Compile.Unimplemented + + x this statement is not yet handled during OpenQASM 3 import: binary op expr + ,-[test:1:20] + 1 | complex[float] x = 1.1 + 2.2im; + : ^^^^^^^^^^^ + `---- + ]"#]], + ); +} + +#[test] +#[ignore = "Requires support for binary operators"] +fn implicit_bitness_simple_double_neg_im() { + check_classical_decl( + "complex[float] x = 1.1 - 2.2im;", + &expect![[r#" + Program: + version: + statements: + + [Qsc.Qasm3.Compile.Unimplemented + + x this statement is not yet handled during OpenQASM 3 import: binary op expr + ,-[test:1:20] + 1 | complex[float] x = 1.1 - 2.2im; + : ^^^^^^^^^^^ + `---- + ]"#]], + ); +} + +#[test] +#[ignore = "Requires support for binary operators"] +fn const_implicit_bitness_simple_double_neg_im() { + check_classical_decl( + "const complex[float] x = 1.1 - 2.2im;", + &expect![[r#" + Program: + version: + statements: + + [Qsc.Qasm3.Compile.Unimplemented + + x this statement is not yet handled during OpenQASM 3 import: binary op expr + ,-[test:1:26] + 1 | const complex[float] x = 1.1 - 2.2im; + : ^^^^^^^^^^^ + `---- + ]"#]], + ); +} + +#[test] +#[ignore = "Requires support for binary operators"] +fn implicit_bitness_simple_double_neg_real() { + check_classical_decl( + "complex[float] x = -1.1 + 2.2im;", + &expect![[r#" + Program: + version: + statements: + + [Qsc.Qasm3.Compile.Unimplemented + + x this statement is not yet handled during OpenQASM 3 import: binary op expr + ,-[test:1:20] + 1 | complex[float] x = -1.1 + 2.2im; + : ^^^^^^^^^^^^ + `---- + ]"#]], + ); +} + +#[test] +#[ignore = "Requires support for binary operators"] +fn const_implicit_bitness_simple_double_neg_real() { + check_classical_decl( + "const complex[float] x = -1.1 + 2.2im;", + &expect![[r#" + Program: + version: + statements: + + [Qsc.Qasm3.Compile.Unimplemented + + x this statement is not yet handled during OpenQASM 3 import: binary op expr + ,-[test:1:26] + 1 | const complex[float] x = -1.1 + 2.2im; + : ^^^^^^^^^^^^ + `---- + ]"#]], + ); +} diff --git a/compiler/qsc_qasm3/src/semantic/tests/decls/creg.rs b/compiler/qsc_qasm3/src/semantic/tests/decls/creg.rs new file mode 100644 index 0000000000..1f7014b74e --- /dev/null +++ b/compiler/qsc_qasm3/src/semantic/tests/decls/creg.rs @@ -0,0 +1,47 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +use expect_test::expect; + +use crate::semantic::tests::check_classical_decl; + +#[test] +fn with_no_init_expr_has_generated_lit_expr() { + check_classical_decl( + "creg a;", + &expect![[r#" + ClassicalDeclarationStmt [0-7]: + symbol_id: 6 + ty_span: [0-7] + init_expr: Expr [0-0]: + ty: Bit(true) + kind: Lit: Int(0) + [6] Symbol [5-6]: + name: a + type: Bit(false) + qsharp_type: Result + io_kind: Default"#]], + ); +} + +#[test] +#[ignore = "Unimplemented"] +fn array_with_no_init_expr_has_generated_lit_expr() { + check_classical_decl( + "creg a[4];", + &expect![[r#" + Program: + version: + statements: + + [Qsc.Qasm3.Compile.Unimplemented + + x this statement is not yet handled during OpenQASM 3 import: bit array + | default value + ,-[test:1:1] + 1 | creg a[4]; + : ^^^^^^^^^^ + `---- + ]"#]], + ); +} diff --git a/compiler/qsc_qasm3/src/semantic/tests/decls/duration.rs b/compiler/qsc_qasm3/src/semantic/tests/decls/duration.rs new file mode 100644 index 0000000000..a28b2a4110 --- /dev/null +++ b/compiler/qsc_qasm3/src/semantic/tests/decls/duration.rs @@ -0,0 +1,26 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +use expect_test::expect; + +use crate::semantic::tests::check_classical_decl; + +#[test] +fn with_no_init_expr_has_generated_lit_expr() { + check_classical_decl( + "duration a;", + &expect![[r#" + Program: + version: + statements: + + [Qsc.Qasm3.Compile.NotSupported + + x Duration type values are not supported. + ,-[test:1:1] + 1 | duration a; + : ^^^^^^^^ + `---- + ]"#]], + ); +} diff --git a/compiler/qsc_qasm3/src/semantic/tests/decls/float.rs b/compiler/qsc_qasm3/src/semantic/tests/decls/float.rs new file mode 100644 index 0000000000..7975ff3a9e --- /dev/null +++ b/compiler/qsc_qasm3/src/semantic/tests/decls/float.rs @@ -0,0 +1,499 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +use expect_test::expect; + +use crate::semantic::tests::check_classical_decl; + +#[test] +fn implicit_bitness_default() { + check_classical_decl( + "float x;", + &expect![[r#" + ClassicalDeclarationStmt [0-8]: + symbol_id: 6 + ty_span: [0-5] + init_expr: Expr [0-0]: + ty: Float(None, true) + kind: Lit: Float(0.0) + [6] Symbol [6-7]: + name: x + type: Float(None, false) + qsharp_type: Double + io_kind: Default"#]], + ); +} + +#[test] +fn const_default() { + check_classical_decl( + "const float x;", + &expect![[r#" + Program: + version: + statements: + + [Qasm3.Parse.Token + + x expected `=`, found `;` + ,-[test:1:14] + 1 | const float x; + : ^ + `---- + , Qsc.Qasm3.Compile.UnexpectedParserError + + x Unexpected parser error: Unexpected error. + ,-[test:1:1] + 1 | const float x; + : ^^^^^^^^^^^^^^ + `---- + ]"#]], + ); +} + +#[test] +fn lit() { + check_classical_decl( + "float x = 42.1;", + &expect![[r#" + ClassicalDeclarationStmt [0-15]: + symbol_id: 6 + ty_span: [0-5] + init_expr: Expr [10-14]: + ty: Float(None, true) + kind: Lit: Float(42.1) + [6] Symbol [6-7]: + name: x + type: Float(None, false) + qsharp_type: Double + io_kind: Default"#]], + ); +} + +#[test] +fn const_lit() { + check_classical_decl( + "const float x = 42.1;", + &expect![[r#" + ClassicalDeclarationStmt [0-21]: + symbol_id: 6 + ty_span: [6-11] + init_expr: Expr [16-20]: + ty: Float(None, true) + kind: Lit: Float(42.1) + [6] Symbol [12-13]: + name: x + type: Float(None, true) + qsharp_type: Double + io_kind: Default"#]], + ); +} + +#[test] +fn lit_explicit_width() { + check_classical_decl( + "float[64] x = 42.1;", + &expect![[r#" + ClassicalDeclarationStmt [0-19]: + symbol_id: 6 + ty_span: [0-9] + init_expr: Expr [14-18]: + ty: Float(Some(64), true) + kind: Lit: Float(42.1) + [6] Symbol [10-11]: + name: x + type: Float(Some(64), false) + qsharp_type: Double + io_kind: Default"#]], + ); +} + +#[test] +fn const_explicit_width_lit() { + check_classical_decl( + "const float[64] x = 42.1;", + &expect![[r#" + ClassicalDeclarationStmt [0-25]: + symbol_id: 6 + ty_span: [6-15] + init_expr: Expr [20-24]: + ty: Float(Some(64), true) + kind: Lit: Float(42.1) + [6] Symbol [16-17]: + name: x + type: Float(Some(64), true) + qsharp_type: Double + io_kind: Default"#]], + ); +} + +#[test] +fn lit_decl_leading_dot() { + check_classical_decl( + "float x = .421;", + &expect![[r#" + ClassicalDeclarationStmt [0-15]: + symbol_id: 6 + ty_span: [0-5] + init_expr: Expr [10-14]: + ty: Float(None, true) + kind: Lit: Float(0.421) + [6] Symbol [6-7]: + name: x + type: Float(None, false) + qsharp_type: Double + io_kind: Default"#]], + ); +} + +#[test] +fn const_lit_decl_leading_dot() { + check_classical_decl( + "const float x = .421;", + &expect![[r#" + ClassicalDeclarationStmt [0-21]: + symbol_id: 6 + ty_span: [6-11] + init_expr: Expr [16-20]: + ty: Float(None, true) + kind: Lit: Float(0.421) + [6] Symbol [12-13]: + name: x + type: Float(None, true) + qsharp_type: Double + io_kind: Default"#]], + ); +} + +#[test] +fn const_lit_decl_leading_dot_scientific() { + check_classical_decl( + "const float x = .421e2;", + &expect![[r#" + ClassicalDeclarationStmt [0-23]: + symbol_id: 6 + ty_span: [6-11] + init_expr: Expr [16-22]: + ty: Float(None, true) + kind: Lit: Float(42.1) + [6] Symbol [12-13]: + name: x + type: Float(None, true) + qsharp_type: Double + io_kind: Default"#]], + ); +} + +#[test] +fn lit_decl_trailing_dot() { + check_classical_decl( + "float x = 421.;", + &expect![[r#" + ClassicalDeclarationStmt [0-15]: + symbol_id: 6 + ty_span: [0-5] + init_expr: Expr [10-14]: + ty: Float(None, true) + kind: Lit: Float(421.0) + [6] Symbol [6-7]: + name: x + type: Float(None, false) + qsharp_type: Double + io_kind: Default"#]], + ); +} + +#[test] +fn const_lit_decl_trailing_dot() { + check_classical_decl( + "const float x = 421.;", + &expect![[r#" + ClassicalDeclarationStmt [0-21]: + symbol_id: 6 + ty_span: [6-11] + init_expr: Expr [16-20]: + ty: Float(None, true) + kind: Lit: Float(421.0) + [6] Symbol [12-13]: + name: x + type: Float(None, true) + qsharp_type: Double + io_kind: Default"#]], + ); +} + +#[test] +fn lit_decl_scientific() { + check_classical_decl( + "float x = 4.21e1;", + &expect![[r#" + ClassicalDeclarationStmt [0-17]: + symbol_id: 6 + ty_span: [0-5] + init_expr: Expr [10-16]: + ty: Float(None, true) + kind: Lit: Float(42.1) + [6] Symbol [6-7]: + name: x + type: Float(None, false) + qsharp_type: Double + io_kind: Default"#]], + ); +} + +#[test] +fn const_lit_decl_scientific() { + check_classical_decl( + "const float x = 4.21e1;", + &expect![[r#" + ClassicalDeclarationStmt [0-23]: + symbol_id: 6 + ty_span: [6-11] + init_expr: Expr [16-22]: + ty: Float(None, true) + kind: Lit: Float(42.1) + [6] Symbol [12-13]: + name: x + type: Float(None, true) + qsharp_type: Double + io_kind: Default"#]], + ); +} + +#[test] +fn lit_decl_scientific_signed_pos() { + check_classical_decl( + "float x = 4.21e+1;", + &expect![[r#" + ClassicalDeclarationStmt [0-18]: + symbol_id: 6 + ty_span: [0-5] + init_expr: Expr [10-17]: + ty: Float(None, true) + kind: Lit: Float(42.1) + [6] Symbol [6-7]: + name: x + type: Float(None, false) + qsharp_type: Double + io_kind: Default"#]], + ); +} + +#[test] +fn const_lit_decl_scientific_signed_pos() { + check_classical_decl( + "const float x = 4.21e+1;", + &expect![[r#" + ClassicalDeclarationStmt [0-24]: + symbol_id: 6 + ty_span: [6-11] + init_expr: Expr [16-23]: + ty: Float(None, true) + kind: Lit: Float(42.1) + [6] Symbol [12-13]: + name: x + type: Float(None, true) + qsharp_type: Double + io_kind: Default"#]], + ); +} + +#[test] +fn lit_decl_scientific_cap_e() { + check_classical_decl( + "float x = 4.21E1;", + &expect![[r#" + ClassicalDeclarationStmt [0-17]: + symbol_id: 6 + ty_span: [0-5] + init_expr: Expr [10-16]: + ty: Float(None, true) + kind: Lit: Float(42.1) + [6] Symbol [6-7]: + name: x + type: Float(None, false) + qsharp_type: Double + io_kind: Default"#]], + ); +} + +#[test] +fn const_lit_decl_scientific_cap_e() { + check_classical_decl( + "const float x = 4.21E1;", + &expect![[r#" + ClassicalDeclarationStmt [0-23]: + symbol_id: 6 + ty_span: [6-11] + init_expr: Expr [16-22]: + ty: Float(None, true) + kind: Lit: Float(42.1) + [6] Symbol [12-13]: + name: x + type: Float(None, true) + qsharp_type: Double + io_kind: Default"#]], + ); +} + +#[test] +fn lit_decl_scientific_signed_neg() { + check_classical_decl( + "float x = 421.0e-1;", + &expect![[r#" + ClassicalDeclarationStmt [0-19]: + symbol_id: 6 + ty_span: [0-5] + init_expr: Expr [10-18]: + ty: Float(None, true) + kind: Lit: Float(42.1) + [6] Symbol [6-7]: + name: x + type: Float(None, false) + qsharp_type: Double + io_kind: Default"#]], + ); +} + +#[test] +fn const_lit_decl_scientific_signed_neg() { + check_classical_decl( + "const float x = 421.0e-1;", + &expect![[r#" + ClassicalDeclarationStmt [0-25]: + symbol_id: 6 + ty_span: [6-11] + init_expr: Expr [16-24]: + ty: Float(None, true) + kind: Lit: Float(42.1) + [6] Symbol [12-13]: + name: x + type: Float(None, true) + qsharp_type: Double + io_kind: Default"#]], + ); +} + +#[test] +fn const_lit_decl_signed_float_lit_cast_neg() { + check_classical_decl( + "const float x = -7.;", + &expect![[r#" + ClassicalDeclarationStmt [0-20]: + symbol_id: 6 + ty_span: [6-11] + init_expr: Expr [17-19]: + ty: Float(None, true) + kind: Lit: Float(-7.0) + [6] Symbol [12-13]: + name: x + type: Float(None, true) + qsharp_type: Double + io_kind: Default"#]], + ); +} + +#[test] +fn const_lit_decl_signed_int_lit_cast_neg() { + check_classical_decl( + "const float x = -7;", + &expect![[r#" + ClassicalDeclarationStmt [0-19]: + symbol_id: 6 + ty_span: [6-11] + init_expr: Expr [17-18]: + ty: Float(None, true) + kind: Lit: Float(-7.0) + [6] Symbol [12-13]: + name: x + type: Float(None, true) + qsharp_type: Double + io_kind: Default"#]], + ); +} + +#[test] +fn init_float_with_int_value_equal_max_safely_representable_values() { + let max_exact_int = 2i64.pow(f64::MANTISSA_DIGITS); + check_classical_decl( + &format!("float a = {max_exact_int};"), + &expect![[r#" + ClassicalDeclarationStmt [0-27]: + symbol_id: 6 + ty_span: [0-5] + init_expr: Expr [10-26]: + ty: Float(None, true) + kind: Lit: Float(9007199254740992.0) + [6] Symbol [6-7]: + name: a + type: Float(None, false) + qsharp_type: Double + io_kind: Default"#]], + ); +} + +#[test] +fn init_float_with_int_value_greater_than_safely_representable_values() { + let max_exact_int = 2i64.pow(f64::MANTISSA_DIGITS); + let next = max_exact_int + 1; + check_classical_decl( + &format!("float a = {next};"), + &expect![[r#" + Program: + version: + statements: + + [Qsc.Qasm3.Compile.InvalidCastValueRange + + x Assigning Int(None, true) values to Float(None, false) must be in a range + | that be converted to Float(None, false). + ,-[test:1:11] + 1 | float a = 9007199254740993; + : ^^^^^^^^^^^^^^^^ + `---- + ]"#]], + ); +} + +#[test] +fn init_float_with_int_value_equal_min_safely_representable_values() { + let min_exact_int = -(2i64.pow(f64::MANTISSA_DIGITS)); + check_classical_decl( + &format!("float a = {min_exact_int};"), + &expect![[r#" + ClassicalDeclarationStmt [0-28]: + symbol_id: 6 + ty_span: [0-5] + init_expr: Expr [11-27]: + ty: Float(None, true) + kind: Lit: Float(-9007199254740992.0) + [6] Symbol [6-7]: + name: a + type: Float(None, false) + qsharp_type: Double + io_kind: Default"#]], + ); +} + +#[test] +fn init_float_with_int_value_less_than_safely_representable_values() { + let min_exact_int = -(2i64.pow(f64::MANTISSA_DIGITS)); + let next = min_exact_int - 1; + check_classical_decl( + &format!("float a = {next};"), + &expect![[r#" + Program: + version: + statements: + + [Qsc.Qasm3.Compile.InvalidCastValueRange + + x Assigning Int(None, true) values to Float(None, false) must be in a range + | that be converted to Float(None, false). + ,-[test:1:12] + 1 | float a = -9007199254740993; + : ^^^^^^^^^^^^^^^^ + `---- + ]"#]], + ); +} diff --git a/compiler/qsc_qasm3/src/semantic/tests/decls/int.rs b/compiler/qsc_qasm3/src/semantic/tests/decls/int.rs new file mode 100644 index 0000000000..7a0f38a21c --- /dev/null +++ b/compiler/qsc_qasm3/src/semantic/tests/decls/int.rs @@ -0,0 +1,423 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +use expect_test::expect; + +use crate::semantic::tests::check_classical_decl; + +#[test] +fn implicit_bitness_int_negative() { + check_classical_decl( + "int x = -42;", + &expect![[r#" + ClassicalDeclarationStmt [0-12]: + symbol_id: 6 + ty_span: [0-3] + init_expr: Expr [9-11]: + ty: Int(None, true) + kind: Lit: Int(-42) + [6] Symbol [4-5]: + name: x + type: Int(None, false) + qsharp_type: Int + io_kind: Default"#]], + ); +} + +#[test] +fn implicit_bitness_int_const_negative() { + check_classical_decl( + "const int x = -42;", + &expect![[r#" + ClassicalDeclarationStmt [0-18]: + symbol_id: 6 + ty_span: [6-9] + init_expr: Expr [15-17]: + ty: Int(None, true) + kind: Lit: Int(-42) + [6] Symbol [10-11]: + name: x + type: Int(None, true) + qsharp_type: Int + io_kind: Default"#]], + ); +} + +#[test] +fn implicit_bitness_int_default() { + check_classical_decl( + "int x;", + &expect![[r#" + ClassicalDeclarationStmt [0-6]: + symbol_id: 6 + ty_span: [0-3] + init_expr: Expr [0-0]: + ty: Int(None, true) + kind: Lit: Int(0) + [6] Symbol [4-5]: + name: x + type: Int(None, false) + qsharp_type: Int + io_kind: Default"#]], + ); +} + +#[test] +fn const_implicit_bitness_int_default() { + check_classical_decl( + "const int x;", + &expect![[r#" + Program: + version: + statements: + + [Qasm3.Parse.Token + + x expected `=`, found `;` + ,-[test:1:12] + 1 | const int x; + : ^ + `---- + , Qsc.Qasm3.Compile.UnexpectedParserError + + x Unexpected parser error: Unexpected error. + ,-[test:1:1] + 1 | const int x; + : ^^^^^^^^^^^^ + `---- + ]"#]], + ); +} + +#[test] +fn const_implicit_bitness_int_lit() { + check_classical_decl( + "const int x = 42;", + &expect![[r#" + ClassicalDeclarationStmt [0-17]: + symbol_id: 6 + ty_span: [6-9] + init_expr: Expr [14-16]: + ty: Int(None, true) + kind: Lit: Int(42) + [6] Symbol [10-11]: + name: x + type: Int(None, true) + qsharp_type: Int + io_kind: Default"#]], + ); +} + +#[test] +fn implicit_bitness_int_hex_cap() { + check_classical_decl( + "int x = 0XFa_1F;", + &expect![[r#" + ClassicalDeclarationStmt [0-16]: + symbol_id: 6 + ty_span: [0-3] + init_expr: Expr [8-15]: + ty: Int(None, true) + kind: Lit: Int(64031) + [6] Symbol [4-5]: + name: x + type: Int(None, false) + qsharp_type: Int + io_kind: Default"#]], + ); +} + +#[test] +fn const_implicit_bitness_int_hex_cap() { + check_classical_decl( + "const int y = 0XFa_1F;", + &expect![[r#" + ClassicalDeclarationStmt [0-22]: + symbol_id: 6 + ty_span: [6-9] + init_expr: Expr [14-21]: + ty: Int(None, true) + kind: Lit: Int(64031) + [6] Symbol [10-11]: + name: y + type: Int(None, true) + qsharp_type: Int + io_kind: Default"#]], + ); +} + +#[test] +fn implicit_bitness_int_octal() { + check_classical_decl( + "int x = 0o42;", + &expect![[r#" + ClassicalDeclarationStmt [0-13]: + symbol_id: 6 + ty_span: [0-3] + init_expr: Expr [8-12]: + ty: Int(None, true) + kind: Lit: Int(34) + [6] Symbol [4-5]: + name: x + type: Int(None, false) + qsharp_type: Int + io_kind: Default"#]], + ); +} + +#[test] +fn const_implicit_bitness_int_octal() { + check_classical_decl( + "const int x = 0o42;", + &expect![[r#" + ClassicalDeclarationStmt [0-19]: + symbol_id: 6 + ty_span: [6-9] + init_expr: Expr [14-18]: + ty: Int(None, true) + kind: Lit: Int(34) + [6] Symbol [10-11]: + name: x + type: Int(None, true) + qsharp_type: Int + io_kind: Default"#]], + ); +} + +#[test] +fn const_implicit_bitness_int_octal_cap() { + check_classical_decl( + "const int x = 0O42;", + &expect![[r#" + ClassicalDeclarationStmt [0-19]: + symbol_id: 6 + ty_span: [6-9] + init_expr: Expr [14-18]: + ty: Int(None, true) + kind: Lit: Int(34) + [6] Symbol [10-11]: + name: x + type: Int(None, true) + qsharp_type: Int + io_kind: Default"#]], + ); +} + +#[test] +fn implicit_bitness_int_binary_low() { + check_classical_decl( + "int x = 0b1001_1001;", + &expect![[r#" + ClassicalDeclarationStmt [0-20]: + symbol_id: 6 + ty_span: [0-3] + init_expr: Expr [8-19]: + ty: Int(None, true) + kind: Lit: Int(153) + [6] Symbol [4-5]: + name: x + type: Int(None, false) + qsharp_type: Int + io_kind: Default"#]], + ); +} + +#[test] +fn implicit_bitness_int_binary_cap() { + check_classical_decl( + "int x = 0B1010;", + &expect![[r#" + ClassicalDeclarationStmt [0-15]: + symbol_id: 6 + ty_span: [0-3] + init_expr: Expr [8-14]: + ty: Int(None, true) + kind: Lit: Int(10) + [6] Symbol [4-5]: + name: x + type: Int(None, false) + qsharp_type: Int + io_kind: Default"#]], + ); +} + +#[test] +fn const_implicit_bitness_int_binary_low() { + check_classical_decl( + "const int x = 0b1001_1001;", + &expect![[r#" + ClassicalDeclarationStmt [0-26]: + symbol_id: 6 + ty_span: [6-9] + init_expr: Expr [14-25]: + ty: Int(None, true) + kind: Lit: Int(153) + [6] Symbol [10-11]: + name: x + type: Int(None, true) + qsharp_type: Int + io_kind: Default"#]], + ); +} + +#[test] +fn const_implicit_bitness_int_binary_cap() { + check_classical_decl( + "const int x = 0B1010;", + &expect![[r#" + ClassicalDeclarationStmt [0-21]: + symbol_id: 6 + ty_span: [6-9] + init_expr: Expr [14-20]: + ty: Int(None, true) + kind: Lit: Int(10) + [6] Symbol [10-11]: + name: x + type: Int(None, true) + qsharp_type: Int + io_kind: Default"#]], + ); +} + +#[test] +fn implicit_bitness_int_formatted() { + check_classical_decl( + "int x = 2_0_00;", + &expect![[r#" + ClassicalDeclarationStmt [0-15]: + symbol_id: 6 + ty_span: [0-3] + init_expr: Expr [8-14]: + ty: Int(None, true) + kind: Lit: Int(2000) + [6] Symbol [4-5]: + name: x + type: Int(None, false) + qsharp_type: Int + io_kind: Default"#]], + ); +} + +#[test] +fn const_implicit_bitness_int_formatted() { + check_classical_decl( + "const int x = 2_0_00;", + &expect![[r#" + ClassicalDeclarationStmt [0-21]: + symbol_id: 6 + ty_span: [6-9] + init_expr: Expr [14-20]: + ty: Int(None, true) + kind: Lit: Int(2000) + [6] Symbol [10-11]: + name: x + type: Int(None, true) + qsharp_type: Int + io_kind: Default"#]], + ); +} + +#[test] +fn explicit_bitness_int_default() { + check_classical_decl( + "int[10] x;", + &expect![[r#" + ClassicalDeclarationStmt [0-10]: + symbol_id: 6 + ty_span: [0-7] + init_expr: Expr [0-0]: + ty: Int(Some(10), true) + kind: Lit: Int(0) + [6] Symbol [8-9]: + name: x + type: Int(Some(10), false) + qsharp_type: Int + io_kind: Default"#]], + ); +} + +#[test] +fn const_explicit_bitness_int_default() { + check_classical_decl( + "const int[10] x;", + &expect![[r#" + Program: + version: + statements: + + [Qasm3.Parse.Token + + x expected `=`, found `;` + ,-[test:1:16] + 1 | const int[10] x; + : ^ + `---- + , Qsc.Qasm3.Compile.UnexpectedParserError + + x Unexpected parser error: Unexpected error. + ,-[test:1:1] + 1 | const int[10] x; + : ^^^^^^^^^^^^^^^^ + `---- + ]"#]], + ); +} + +#[test] +fn explicit_bitness_int() { + check_classical_decl( + "int[10] x = 42;", + &expect![[r#" + ClassicalDeclarationStmt [0-15]: + symbol_id: 6 + ty_span: [0-7] + init_expr: Expr [12-14]: + ty: Int(Some(10), true) + kind: Lit: Int(42) + [6] Symbol [8-9]: + name: x + type: Int(Some(10), false) + qsharp_type: Int + io_kind: Default"#]], + ); +} + +#[test] +fn const_explicit_bitness_int() { + check_classical_decl( + "const int[10] x = 42;", + &expect![[r#" + ClassicalDeclarationStmt [0-21]: + symbol_id: 6 + ty_span: [6-13] + init_expr: Expr [18-20]: + ty: Int(Some(10), true) + kind: Lit: Int(42) + [6] Symbol [14-15]: + name: x + type: Int(Some(10), true) + qsharp_type: Int + io_kind: Default"#]], + ); +} + +#[test] +fn implicit_bitness_int_negative_float_decl_causes_semantic_error() { + check_classical_decl( + "int x = -42.;", + &expect![[r#" + Program: + version: + statements: + + [Qsc.Qasm3.Compile.CannotAssignToType + + x Cannot assign a value of Float(None, true) type to a classical variable of + | Int(None, false) type. + ,-[test:1:1] + 1 | int x = -42.; + : ^^^^^^^^^^^^^ + `---- + ]"#]], + ); +} diff --git a/compiler/qsc_qasm3/src/semantic/tests/decls/qreg.rs b/compiler/qsc_qasm3/src/semantic/tests/decls/qreg.rs new file mode 100644 index 0000000000..37fb09e2a4 --- /dev/null +++ b/compiler/qsc_qasm3/src/semantic/tests/decls/qreg.rs @@ -0,0 +1,28 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +use expect_test::expect; + +use crate::semantic::tests::check_stmt_kind; + +#[test] +#[ignore = "unimplemented"] +fn with_no_init_expr_has_generated_lit_expr() { + check_stmt_kind( + "qreg a;", + &expect![[r#" + Program: + version: + statements: + + [Qsc.Qasm3.Compile.Unimplemented + + x this statement is not yet handled during OpenQASM 3 import: qubit decl + | stmt + ,-[test:1:1] + 1 | qreg a; + : ^^^^^^^ + `---- + ]"#]], + ); +} diff --git a/compiler/qsc_qasm3/src/semantic/tests/decls/stretch.rs b/compiler/qsc_qasm3/src/semantic/tests/decls/stretch.rs new file mode 100644 index 0000000000..488931a1d5 --- /dev/null +++ b/compiler/qsc_qasm3/src/semantic/tests/decls/stretch.rs @@ -0,0 +1,26 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +use expect_test::expect; + +use crate::semantic::tests::check_classical_decl; + +#[test] +fn with_no_init_expr_has_generated_lit_expr() { + check_classical_decl( + "stretch a;", + &expect![[r#" + Program: + version: + statements: + + [Qsc.Qasm3.Compile.NotSupported + + x Stretch type values are not supported. + ,-[test:1:1] + 1 | stretch a; + : ^^^^^^^ + `---- + ]"#]], + ); +} diff --git a/compiler/qsc_qasm3/src/semantic/tests/decls/uint.rs b/compiler/qsc_qasm3/src/semantic/tests/decls/uint.rs new file mode 100644 index 0000000000..78ffe2cc58 --- /dev/null +++ b/compiler/qsc_qasm3/src/semantic/tests/decls/uint.rs @@ -0,0 +1,427 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +use expect_test::expect; + +use crate::semantic::tests::check_classical_decl; + +#[test] +fn implicit_bitness_int_default() { + check_classical_decl( + "uint x;", + &expect![[r#" + ClassicalDeclarationStmt [0-7]: + symbol_id: 6 + ty_span: [0-4] + init_expr: Expr [0-0]: + ty: UInt(None, true) + kind: Lit: Int(0) + [6] Symbol [5-6]: + name: x + type: UInt(None, false) + qsharp_type: Int + io_kind: Default"#]], + ); +} + +#[test] +fn const_implicit_bitness_int_default() { + check_classical_decl( + "const uint x;", + &expect![[r#" + Program: + version: + statements: + + [Qasm3.Parse.Token + + x expected `=`, found `;` + ,-[test:1:13] + 1 | const uint x; + : ^ + `---- + , Qsc.Qasm3.Compile.UnexpectedParserError + + x Unexpected parser error: Unexpected error. + ,-[test:1:1] + 1 | const uint x; + : ^^^^^^^^^^^^^ + `---- + ]"#]], + ); +} + +#[test] +fn const_implicit_bitness_int_lit() { + check_classical_decl( + "const uint x = 42;", + &expect![[r#" + ClassicalDeclarationStmt [0-18]: + symbol_id: 6 + ty_span: [6-10] + init_expr: Expr [15-17]: + ty: UInt(None, true) + kind: Lit: Int(42) + [6] Symbol [11-12]: + name: x + type: UInt(None, true) + qsharp_type: Int + io_kind: Default"#]], + ); +} + +#[test] +fn implicit_bitness_int_hex_cap() { + check_classical_decl( + "uint x = 0XFa_1F;", + &expect![[r#" + ClassicalDeclarationStmt [0-17]: + symbol_id: 6 + ty_span: [0-4] + init_expr: Expr [9-16]: + ty: UInt(None, true) + kind: Lit: Int(64031) + [6] Symbol [5-6]: + name: x + type: UInt(None, false) + qsharp_type: Int + io_kind: Default"#]], + ); +} + +#[test] +fn const_implicit_bitness_int_hex_low() { + check_classical_decl( + "const uint x = 0xFa_1F;", + &expect![[r#" + ClassicalDeclarationStmt [0-23]: + symbol_id: 6 + ty_span: [6-10] + init_expr: Expr [15-22]: + ty: UInt(None, true) + kind: Lit: Int(64031) + [6] Symbol [11-12]: + name: x + type: UInt(None, true) + qsharp_type: Int + io_kind: Default"#]], + ); +} + +#[test] +fn const_implicit_bitness_int_hex_cap() { + check_classical_decl( + "const uint y = 0XFa_1F;", + &expect![[r#" + ClassicalDeclarationStmt [0-23]: + symbol_id: 6 + ty_span: [6-10] + init_expr: Expr [15-22]: + ty: UInt(None, true) + kind: Lit: Int(64031) + [6] Symbol [11-12]: + name: y + type: UInt(None, true) + qsharp_type: Int + io_kind: Default"#]], + ); +} + +#[test] +fn implicit_bitness_int_octal_low() { + check_classical_decl( + "uint x = 0o42;", + &expect![[r#" + ClassicalDeclarationStmt [0-14]: + symbol_id: 6 + ty_span: [0-4] + init_expr: Expr [9-13]: + ty: UInt(None, true) + kind: Lit: Int(34) + [6] Symbol [5-6]: + name: x + type: UInt(None, false) + qsharp_type: Int + io_kind: Default"#]], + ); +} + +#[test] +fn implicit_bitness_int_octal_cap() { + check_classical_decl( + "uint x = 0O42;", + &expect![[r#" + ClassicalDeclarationStmt [0-14]: + symbol_id: 6 + ty_span: [0-4] + init_expr: Expr [9-13]: + ty: UInt(None, true) + kind: Lit: Int(34) + [6] Symbol [5-6]: + name: x + type: UInt(None, false) + qsharp_type: Int + io_kind: Default"#]], + ); +} + +#[test] +fn const_implicit_bitness_int_octal_low() { + check_classical_decl( + "const uint x = 0o42;", + &expect![[r#" + ClassicalDeclarationStmt [0-20]: + symbol_id: 6 + ty_span: [6-10] + init_expr: Expr [15-19]: + ty: UInt(None, true) + kind: Lit: Int(34) + [6] Symbol [11-12]: + name: x + type: UInt(None, true) + qsharp_type: Int + io_kind: Default"#]], + ); +} + +#[test] +fn const_implicit_bitness_int_octal_cap() { + check_classical_decl( + "const uint x = 0O42;", + &expect![[r#" + ClassicalDeclarationStmt [0-20]: + symbol_id: 6 + ty_span: [6-10] + init_expr: Expr [15-19]: + ty: UInt(None, true) + kind: Lit: Int(34) + [6] Symbol [11-12]: + name: x + type: UInt(None, true) + qsharp_type: Int + io_kind: Default"#]], + ); +} + +#[test] +fn implicit_bitness_int_binary_low() { + check_classical_decl( + "uint x = 0b1001_1001;", + &expect![[r#" + ClassicalDeclarationStmt [0-21]: + symbol_id: 6 + ty_span: [0-4] + init_expr: Expr [9-20]: + ty: UInt(None, true) + kind: Lit: Int(153) + [6] Symbol [5-6]: + name: x + type: UInt(None, false) + qsharp_type: Int + io_kind: Default"#]], + ); +} + +#[test] +fn implicit_bitness_int_binary_cap() { + check_classical_decl( + "uint x = 0B1010;", + &expect![[r#" + ClassicalDeclarationStmt [0-16]: + symbol_id: 6 + ty_span: [0-4] + init_expr: Expr [9-15]: + ty: UInt(None, true) + kind: Lit: Int(10) + [6] Symbol [5-6]: + name: x + type: UInt(None, false) + qsharp_type: Int + io_kind: Default"#]], + ); +} + +#[test] +fn const_implicit_bitness_int_binary_low() { + check_classical_decl( + "const uint x = 0b1001_1001;", + &expect![[r#" + ClassicalDeclarationStmt [0-27]: + symbol_id: 6 + ty_span: [6-10] + init_expr: Expr [15-26]: + ty: UInt(None, true) + kind: Lit: Int(153) + [6] Symbol [11-12]: + name: x + type: UInt(None, true) + qsharp_type: Int + io_kind: Default"#]], + ); +} + +#[test] +fn const_implicit_bitness_int_binary_cap() { + check_classical_decl( + "const uint x = 0B1010;", + &expect![[r#" + ClassicalDeclarationStmt [0-22]: + symbol_id: 6 + ty_span: [6-10] + init_expr: Expr [15-21]: + ty: UInt(None, true) + kind: Lit: Int(10) + [6] Symbol [11-12]: + name: x + type: UInt(None, true) + qsharp_type: Int + io_kind: Default"#]], + ); +} + +#[test] +fn implicit_bitness_int_formatted() { + check_classical_decl( + "uint x = 2_0_00;", + &expect![[r#" + ClassicalDeclarationStmt [0-16]: + symbol_id: 6 + ty_span: [0-4] + init_expr: Expr [9-15]: + ty: UInt(None, true) + kind: Lit: Int(2000) + [6] Symbol [5-6]: + name: x + type: UInt(None, false) + qsharp_type: Int + io_kind: Default"#]], + ); +} + +#[test] +fn const_implicit_bitness_int_formatted() { + check_classical_decl( + "const uint x = 2_0_00;", + &expect![[r#" + ClassicalDeclarationStmt [0-22]: + symbol_id: 6 + ty_span: [6-10] + init_expr: Expr [15-21]: + ty: UInt(None, true) + kind: Lit: Int(2000) + [6] Symbol [11-12]: + name: x + type: UInt(None, true) + qsharp_type: Int + io_kind: Default"#]], + ); +} + +#[test] +fn const_explicit_bitness_int() { + check_classical_decl( + "uint[10] x;", + &expect![[r#" + ClassicalDeclarationStmt [0-11]: + symbol_id: 6 + ty_span: [0-8] + init_expr: Expr [0-0]: + ty: UInt(Some(10), true) + kind: Lit: Int(0) + [6] Symbol [9-10]: + name: x + type: UInt(Some(10), false) + qsharp_type: Int + io_kind: Default"#]], + ); +} + +#[test] +fn explicit_bitness_int() { + check_classical_decl( + "const uint[10] x;", + &expect![[r#" + Program: + version: + statements: + + [Qasm3.Parse.Token + + x expected `=`, found `;` + ,-[test:1:17] + 1 | const uint[10] x; + : ^ + `---- + , Qsc.Qasm3.Compile.UnexpectedParserError + + x Unexpected parser error: Unexpected error. + ,-[test:1:1] + 1 | const uint[10] x; + : ^^^^^^^^^^^^^^^^^ + `---- + ]"#]], + ); +} + +#[test] +fn assigning_uint_to_negative_lit_results_in_semantic_error() { + check_classical_decl( + "const uint[10] x = -42;", + &expect![[r#" + Program: + version: + statements: + + [Qsc.Qasm3.Compile.CannotAssignToType + + x Cannot assign a value of Int(None, true) type to a classical variable of + | UInt(Some(10), true) type. + ,-[test:1:1] + 1 | const uint[10] x = -42; + : ^^^^^^^^^^^^^^^^^^^^^^^ + `---- + ]"#]], + ); +} + +#[test] +fn implicit_bitness_uint_const_negative_decl_raises_semantic_error() { + check_classical_decl( + "const uint x = -42;", + &expect![[r#" + Program: + version: + statements: + + [Qsc.Qasm3.Compile.CannotAssignToType + + x Cannot assign a value of Int(None, true) type to a classical variable of + | UInt(None, true) type. + ,-[test:1:1] + 1 | const uint x = -42; + : ^^^^^^^^^^^^^^^^^^^ + `---- + ]"#]], + ); +} + +#[test] +fn explicit_bitness_uint_const_negative_decl_raises_semantic_error() { + check_classical_decl( + "const uint[32] x = -42;", + &expect![[r#" + Program: + version: + statements: + + [Qsc.Qasm3.Compile.CannotAssignToType + + x Cannot assign a value of Int(None, true) type to a classical variable of + | UInt(Some(32), true) type. + ,-[test:1:1] + 1 | const uint[32] x = -42; + : ^^^^^^^^^^^^^^^^^^^^^^^ + `---- + ]"#]], + ); +} diff --git a/compiler/qsc_qasm3/src/semantic/types.rs b/compiler/qsc_qasm3/src/semantic/types.rs new file mode 100644 index 0000000000..f111bef11d --- /dev/null +++ b/compiler/qsc_qasm3/src/semantic/types.rs @@ -0,0 +1,675 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +use std::cmp::max; + +use core::fmt; +use std::fmt::{Display, Formatter}; + +use crate::ast::UnaryOp::NotL; + +use super::ast::LiteralKind; + +#[derive(Clone, Debug, Default, Eq, Hash, PartialEq)] +pub enum Type { + // scalar types + Bit(bool), + Bool(bool), + Duration(bool), + Stretch(bool), + + Angle(Option, bool), + Complex(Option, bool), + Float(Option, bool), + Int(Option, bool), + UInt(Option, bool), + + // quantum + Qubit, + HardwareQubit, + + // magic arrays + BitArray(ArrayDimensions, bool), + QubitArray(ArrayDimensions), + + // proper arrays + BoolArray(ArrayDimensions), + DurationArray(ArrayDimensions), + AngleArray(Option, ArrayDimensions), + ComplexArray(Option, ArrayDimensions), + FloatArray(Option, ArrayDimensions), + IntArray(Option, ArrayDimensions), + UIntArray(Option, ArrayDimensions), + + // realistically the sizes could be u3 + Gate(u32, u32), + Range, + Set, + Void, + #[default] + Err, +} + +impl Display for Type { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + match self { + Type::Bit(is_const) => write!(f, "Bit({is_const})"), + Type::Bool(is_const) => write!(f, "Bool({is_const})"), + Type::Duration(is_const) => write!(f, "Duration({is_const})"), + Type::Stretch(is_const) => write!(f, "Stretch({is_const})"), + Type::Angle(width, is_const) => write!(f, "Angle({width:?}, {is_const})"), + Type::Complex(width, is_const) => write!(f, "Complex({width:?}, {is_const})"), + Type::Float(width, is_const) => write!(f, "Float({width:?}, {is_const})"), + Type::Int(width, is_const) => write!(f, "Int({width:?}, {is_const})"), + Type::UInt(width, is_const) => write!(f, "UInt({width:?}, {is_const})"), + Type::Qubit => write!(f, "Qubit"), + Type::HardwareQubit => write!(f, "HardwareQubit"), + Type::BitArray(dims, is_const) => write!(f, "BitArray({dims:?}, {is_const})"), + Type::QubitArray(dims) => write!(f, "QubitArray({dims:?})"), + Type::BoolArray(dims) => write!(f, "BoolArray({dims:?})"), + Type::DurationArray(dims) => write!(f, "DurationArray({dims:?})"), + Type::AngleArray(width, dims) => write!(f, "AngleArray({width:?}, {dims:?})"), + Type::ComplexArray(width, dims) => write!(f, "ComplexArray({width:?}, {dims:?})"), + Type::FloatArray(width, dims) => write!(f, "FloatArray({width:?}, {dims:?})"), + Type::IntArray(width, dims) => write!(f, "IntArray({width:?}, {dims:?})"), + Type::UIntArray(width, dims) => write!(f, "UIntArray({width:?}, {dims:?})"), + Type::Gate(cargs, qargs) => write!(f, "Gate({cargs}, {qargs})"), + Type::Range => write!(f, "Range"), + Type::Set => write!(f, "Set"), + Type::Void => write!(f, "Void"), + Type::Err => write!(f, "Err"), + } + } +} + +impl Type { + #[must_use] + pub fn is_array(&self) -> bool { + matches!( + self, + Type::BitArray(..) + | Type::QubitArray(..) + | Type::AngleArray(..) + | Type::BoolArray(..) + | Type::ComplexArray(..) + | Type::DurationArray(..) + | Type::FloatArray(..) + | Type::IntArray(..) + | Type::UIntArray(..) + ) + } + + #[must_use] + pub fn is_const(&self) -> bool { + match self { + Type::BitArray(_, is_const) + | Type::Bit(is_const) + | Type::Bool(is_const) + | Type::Duration(is_const) + | Type::Stretch(is_const) + | Type::Angle(_, is_const) + | Type::Complex(_, is_const) + | Type::Float(_, is_const) + | Type::Int(_, is_const) + | Type::UInt(_, is_const) => *is_const, + _ => false, + } + } + + #[must_use] + pub fn width(&self) -> Option { + match self { + Type::Angle(w, _) + | Type::Complex(w, _) + | Type::Float(w, _) + | Type::Int(w, _) + | Type::UInt(w, _) => *w, + _ => None, + } + } + + #[must_use] + pub fn is_inferred_output_type(&self) -> bool { + matches!( + self, + Type::Bit(_) + | Type::Int(_, _) + | Type::UInt(_, _) + | Type::Float(_, _) + | Type::Angle(_, _) + | Type::Complex(_, _) + | Type::Bool(_) + | Type::BitArray(_, _) + | Type::IntArray(_, _) + | Type::UIntArray(_, _) + | Type::FloatArray(_, _) + | Type::AngleArray(_, _) + | Type::ComplexArray(_, _) + | Type::BoolArray(_) + | Type::Range + | Type::Set + ) + } + + /// Get the indexed type of a given type. + /// For example, if the type is `Int[2][3]`, the indexed type is `Int[2]`. + /// If the type is `Int[2]`, the indexed type is `Int`. + /// If the type is `Int`, the indexed type is `None`. + /// + /// This is useful for determining the type of an array element. + #[allow(clippy::too_many_lines)] + #[must_use] + pub fn get_indexed_type(&self) -> Option { + let ty = match self { + Type::BitArray(dims, is_const) => match dims { + ArrayDimensions::One(_) => Type::Bit(*is_const), + ArrayDimensions::Two(d1, _) => Type::BitArray(ArrayDimensions::One(*d1), *is_const), + ArrayDimensions::Three(d1, d2, _) => { + Type::BitArray(ArrayDimensions::Two(*d1, *d2), *is_const) + } + ArrayDimensions::Four(d1, d2, d3, _) => { + Type::BitArray(ArrayDimensions::Three(*d1, *d2, *d3), *is_const) + } + ArrayDimensions::Five(d1, d2, d3, d4, _) => { + Type::BitArray(ArrayDimensions::Four(*d1, *d2, *d3, *d4), *is_const) + } + ArrayDimensions::Six(d1, d2, d3, d4, d5, _) => { + Type::BitArray(ArrayDimensions::Five(*d1, *d2, *d3, *d4, *d5), *is_const) + } + ArrayDimensions::Seven(d1, d2, d3, d4, d5, d6, _) => Type::BitArray( + ArrayDimensions::Six(*d1, *d2, *d3, *d4, *d5, *d6), + *is_const, + ), + ArrayDimensions::Err => Type::Err, + }, + Type::QubitArray(dims) => match dims { + ArrayDimensions::One(_) => Type::Qubit, + ArrayDimensions::Two(d1, _) => Type::QubitArray(ArrayDimensions::One(*d1)), + ArrayDimensions::Three(d1, d2, _) => { + Type::QubitArray(ArrayDimensions::Two(*d1, *d2)) + } + ArrayDimensions::Four(d1, d2, d3, _) => { + Type::QubitArray(ArrayDimensions::Three(*d1, *d2, *d3)) + } + ArrayDimensions::Five(d1, d2, d3, d4, _) => { + Type::QubitArray(ArrayDimensions::Four(*d1, *d2, *d3, *d4)) + } + ArrayDimensions::Six(d1, d2, d3, d4, d5, _) => { + Type::QubitArray(ArrayDimensions::Five(*d1, *d2, *d3, *d4, *d5)) + } + ArrayDimensions::Seven(d1, d2, d3, d4, d5, d6, _) => { + Type::QubitArray(ArrayDimensions::Six(*d1, *d2, *d3, *d4, *d5, *d6)) + } + ArrayDimensions::Err => Type::Err, + }, + Type::BoolArray(dims) => match dims { + ArrayDimensions::One(_) => Type::Bool(false), + ArrayDimensions::Two(d1, _) => Type::BoolArray(ArrayDimensions::One(*d1)), + ArrayDimensions::Three(d1, d2, _) => { + Type::BoolArray(ArrayDimensions::Two(*d1, *d2)) + } + ArrayDimensions::Four(d1, d2, d3, _) => { + Type::BoolArray(ArrayDimensions::Three(*d1, *d2, *d3)) + } + ArrayDimensions::Five(d1, d2, d3, d4, _) => { + Type::BoolArray(ArrayDimensions::Four(*d1, *d2, *d3, *d4)) + } + ArrayDimensions::Six(d1, d2, d3, d4, d5, _) => { + Type::BoolArray(ArrayDimensions::Five(*d1, *d2, *d3, *d4, *d5)) + } + ArrayDimensions::Seven(d1, d2, d3, d4, d5, d6, _) => { + Type::BoolArray(ArrayDimensions::Six(*d1, *d2, *d3, *d4, *d5, *d6)) + } + ArrayDimensions::Err => Type::Err, + }, + Type::AngleArray(size, dims) => match dims { + ArrayDimensions::One(_) => Type::Angle(*size, false), + ArrayDimensions::Two(d1, _) => Type::AngleArray(*size, ArrayDimensions::One(*d1)), + ArrayDimensions::Three(d1, d2, _) => { + Type::AngleArray(*size, ArrayDimensions::Two(*d1, *d2)) + } + ArrayDimensions::Four(d1, d2, d3, _) => { + Type::AngleArray(*size, ArrayDimensions::Three(*d1, *d2, *d3)) + } + ArrayDimensions::Five(d1, d2, d3, d4, _) => { + Type::AngleArray(*size, ArrayDimensions::Four(*d1, *d2, *d3, *d4)) + } + ArrayDimensions::Six(d1, d2, d3, d4, d5, _) => { + Type::AngleArray(*size, ArrayDimensions::Five(*d1, *d2, *d3, *d4, *d5)) + } + ArrayDimensions::Seven(d1, d2, d3, d4, d5, d6, _) => { + Type::AngleArray(*size, ArrayDimensions::Six(*d1, *d2, *d3, *d4, *d5, *d6)) + } + ArrayDimensions::Err => Type::Err, + }, + Type::ComplexArray(size, dims) => match dims { + ArrayDimensions::One(_) => Type::Complex(*size, false), + ArrayDimensions::Two(d1, _) => Type::ComplexArray(*size, ArrayDimensions::One(*d1)), + ArrayDimensions::Three(d1, d2, _) => { + Type::ComplexArray(*size, ArrayDimensions::Two(*d1, *d2)) + } + ArrayDimensions::Four(d1, d2, d3, _) => { + Type::ComplexArray(*size, ArrayDimensions::Three(*d1, *d2, *d3)) + } + ArrayDimensions::Five(d1, d2, d3, d4, _) => { + Type::ComplexArray(*size, ArrayDimensions::Four(*d1, *d2, *d3, *d4)) + } + ArrayDimensions::Six(d1, d2, d3, d4, d5, _) => { + Type::ComplexArray(*size, ArrayDimensions::Five(*d1, *d2, *d3, *d4, *d5)) + } + ArrayDimensions::Seven(d1, d2, d3, d4, d5, d6, _) => { + Type::ComplexArray(*size, ArrayDimensions::Six(*d1, *d2, *d3, *d4, *d5, *d6)) + } + ArrayDimensions::Err => Type::Err, + }, + Type::DurationArray(dims) => match dims { + ArrayDimensions::One(_) => Type::Duration(false), + ArrayDimensions::Two(d1, _) => Type::DurationArray(ArrayDimensions::One(*d1)), + ArrayDimensions::Three(d1, d2, _) => { + Type::DurationArray(ArrayDimensions::Two(*d1, *d2)) + } + ArrayDimensions::Four(d1, d2, d3, _) => { + Type::DurationArray(ArrayDimensions::Three(*d1, *d2, *d3)) + } + ArrayDimensions::Five(d1, d2, d3, d4, _) => { + Type::DurationArray(ArrayDimensions::Four(*d1, *d2, *d3, *d4)) + } + ArrayDimensions::Six(d1, d2, d3, d4, d5, _) => { + Type::DurationArray(ArrayDimensions::Five(*d1, *d2, *d3, *d4, *d5)) + } + ArrayDimensions::Seven(d1, d2, d3, d4, d5, d6, _) => { + Type::DurationArray(ArrayDimensions::Six(*d1, *d2, *d3, *d4, *d5, *d6)) + } + ArrayDimensions::Err => Type::Err, + }, + Type::FloatArray(size, dims) => match dims { + ArrayDimensions::One(_) => Type::Float(*size, false), + ArrayDimensions::Two(d1, _) => Type::FloatArray(*size, ArrayDimensions::One(*d1)), + ArrayDimensions::Three(d1, d2, _) => { + Type::FloatArray(*size, ArrayDimensions::Two(*d1, *d2)) + } + ArrayDimensions::Four(d1, d2, d3, _) => { + Type::FloatArray(*size, ArrayDimensions::Three(*d1, *d2, *d3)) + } + ArrayDimensions::Five(d1, d2, d3, d4, _) => { + Type::FloatArray(*size, ArrayDimensions::Four(*d1, *d2, *d3, *d4)) + } + ArrayDimensions::Six(d1, d2, d3, d4, d5, _) => { + Type::FloatArray(*size, ArrayDimensions::Five(*d1, *d2, *d3, *d4, *d5)) + } + ArrayDimensions::Seven(d1, d2, d3, d4, d5, d6, _) => { + Type::FloatArray(*size, ArrayDimensions::Six(*d1, *d2, *d3, *d4, *d5, *d6)) + } + ArrayDimensions::Err => Type::Err, + }, + Type::IntArray(size, dims) => match dims { + ArrayDimensions::One(_) => Type::Int(*size, false), + ArrayDimensions::Two(d1, _) => Type::IntArray(*size, ArrayDimensions::One(*d1)), + ArrayDimensions::Three(d1, d2, _) => { + Type::IntArray(*size, ArrayDimensions::Two(*d1, *d2)) + } + ArrayDimensions::Four(d1, d2, d3, _) => { + Type::IntArray(*size, ArrayDimensions::Three(*d1, *d2, *d3)) + } + ArrayDimensions::Five(d1, d2, d3, d4, _) => { + Type::IntArray(*size, ArrayDimensions::Four(*d1, *d2, *d3, *d4)) + } + ArrayDimensions::Six(d1, d2, d3, d4, d5, _) => { + Type::IntArray(*size, ArrayDimensions::Five(*d1, *d2, *d3, *d4, *d5)) + } + ArrayDimensions::Seven(d1, d2, d3, d4, d5, d6, _) => { + Type::IntArray(*size, ArrayDimensions::Six(*d1, *d2, *d3, *d4, *d5, *d6)) + } + ArrayDimensions::Err => Type::Err, + }, + Type::UIntArray(size, dims) => match dims { + ArrayDimensions::One(_) => Type::UInt(*size, false), + ArrayDimensions::Two(d1, _) => Type::UIntArray(*size, ArrayDimensions::One(*d1)), + ArrayDimensions::Three(d1, d2, _) => { + Type::UIntArray(*size, ArrayDimensions::Two(*d1, *d2)) + } + ArrayDimensions::Four(d1, d2, d3, _) => { + Type::UIntArray(*size, ArrayDimensions::Three(*d1, *d2, *d3)) + } + ArrayDimensions::Five(d1, d2, d3, d4, _) => { + Type::UIntArray(*size, ArrayDimensions::Four(*d1, *d2, *d3, *d4)) + } + ArrayDimensions::Six(d1, d2, d3, d4, d5, _) => { + Type::UIntArray(*size, ArrayDimensions::Five(*d1, *d2, *d3, *d4, *d5)) + } + ArrayDimensions::Seven(d1, d2, d3, d4, d5, d6, _) => { + Type::UIntArray(*size, ArrayDimensions::Six(*d1, *d2, *d3, *d4, *d5, *d6)) + } + ArrayDimensions::Err => Type::Err, + }, + _ => return None, + }; + Some(ty) + } + + pub(crate) fn as_const(&self) -> Type { + match self { + Type::Bit(_) => Self::Bit(true), + Type::Bool(_) => Self::Bool(true), + Type::Duration(_) => Self::Duration(true), + Type::Stretch(_) => Self::Stretch(true), + Type::Angle(w, _) => Self::Angle(*w, true), + Type::Complex(w, _) => Self::Complex(*w, true), + Type::Float(w, _) => Self::Float(*w, true), + Type::Int(w, _) => Self::Int(*w, true), + Type::UInt(w, _) => Self::UInt(*w, true), + Type::BitArray(dims, _) => Self::BitArray(dims.clone(), true), + _ => self.clone(), + } + } +} + +#[derive(Debug, Clone, PartialEq, Eq, Hash, Default)] +pub enum ArrayDimensions { + One(u32), + Two(u32, u32), + Three(u32, u32, u32), + Four(u32, u32, u32, u32), + Five(u32, u32, u32, u32, u32), + Six(u32, u32, u32, u32, u32, u32), + Seven(u32, u32, u32, u32, u32, u32, u32), + #[default] + Err, +} + +impl Display for ArrayDimensions { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + match self { + ArrayDimensions::One(..) => write!(f, "[]"), + ArrayDimensions::Two(..) => write!(f, "[][]"), + ArrayDimensions::Three(..) => write!(f, "[][][]"), + ArrayDimensions::Four(..) => write!(f, "[][][][]"), + ArrayDimensions::Five(..) => write!(f, "[][][][][]"), + ArrayDimensions::Six(..) => write!(f, "[][][][][][]"), + ArrayDimensions::Seven(..) => write!(f, "[][][][][][][]"), + ArrayDimensions::Err => write!(f, "Invalid array dimensions"), + } + } +} + +/// When two types are combined, the result is a type that can represent both. +/// For constness, the result is const iff both types are const. +#[must_use] +pub fn relax_constness(lhs_ty: &Type, rhs_ty: &Type) -> bool { + lhs_ty.is_const() && rhs_ty.is_const() +} + +/// Having no width means that the type is not a fixed-width type +/// and can hold any explicit width. If both types have a width, +/// the result is the maximum of the two. Otherwise, the result +/// is a type without a width. +#[must_use] +pub fn promote_width(lhs_ty: &Type, rhs_ty: &Type) -> Option { + match (lhs_ty.width(), rhs_ty.width()) { + (Some(w1), Some(w2)) => Some(max(w1, w2)), + (Some(_) | None, None) | (None, Some(_)) => None, + } +} + +fn get_effective_width(lhs_ty: &Type, rhs_ty: &Type) -> Option { + match (lhs_ty.width(), rhs_ty.width()) { + (Some(w1), Some(w2)) => Some(max(w1, w2)), + (Some(w), None) | (None, Some(w)) => Some(w), + (None, None) => None, + } +} + +/// If both can be promoted to a common type, the result is that type. +/// If the types are not compatible, the result is `Type::Void`. +#[must_use] +pub fn promote_types(lhs_ty: &Type, rhs_ty: &Type) -> Type { + if types_equal_except_const(lhs_ty, rhs_ty) { + return lhs_ty.clone(); + } + let ty = promote_types_symmetric(lhs_ty, rhs_ty); + if ty != Type::Void { + return ty; + } + let ty = promote_types_asymmetric(lhs_ty, rhs_ty); + if ty == Type::Void { + return promote_types_asymmetric(rhs_ty, lhs_ty); + } + ty +} + +pub(crate) fn promote_to_uint_ty( + lhs_ty: &Type, + rhs_ty: &Type, +) -> (Option, Option, Option) { + let is_const = relax_constness(lhs_ty, rhs_ty); + let lhs_ty = get_uint_ty(lhs_ty); + let rhs_ty = get_uint_ty(rhs_ty); + match (lhs_ty, rhs_ty) { + (Some(lhs_ty), Some(rhs_ty)) => { + let width = get_effective_width(&lhs_ty, &rhs_ty); + ( + Some(Type::UInt(width, is_const)), + Some(lhs_ty), + Some(rhs_ty), + ) + } + (Some(lhs_ty), None) => (None, Some(lhs_ty), None), + (None, Some(rhs_ty)) => (None, None, Some(rhs_ty)), + (None, None) => (None, None, None), + } +} + +fn get_uint_ty(ty: &Type) -> Option { + if matches!(ty, Type::UInt(..) | Type::Angle(..)) { + Some(Type::UInt(ty.width(), ty.is_const())) + } else if let Type::BitArray(dims, _) = ty { + match dims { + ArrayDimensions::One(d) => Some(Type::UInt(Some(*d), ty.is_const())), + _ => None, + } + } else { + None + } +} + +/// Promotes two types if they share a common base type with +/// their constness relaxed, and their width promoted. +/// If the types are not compatible, the result is `Type::Void`. +fn promote_types_symmetric(lhs_ty: &Type, rhs_ty: &Type) -> Type { + let is_const = relax_constness(lhs_ty, rhs_ty); + match (lhs_ty, rhs_ty) { + (Type::Bit(..), Type::Bit(..)) => Type::Bit(is_const), + (Type::Bool(..), Type::Bool(..)) => Type::Bool(is_const), + (Type::Int(..), Type::Int(..)) => Type::Int(promote_width(lhs_ty, rhs_ty), is_const), + (Type::UInt(..), Type::UInt(..)) => Type::UInt(promote_width(lhs_ty, rhs_ty), is_const), + (Type::Angle(..), Type::Angle(..)) => Type::Angle(promote_width(lhs_ty, rhs_ty), is_const), + (Type::Float(..), Type::Float(..)) => Type::Float(promote_width(lhs_ty, rhs_ty), is_const), + (Type::Complex(..), Type::Complex(..)) => { + Type::Complex(promote_width(lhs_ty, rhs_ty), is_const) + } + _ => Type::Void, + } +} + +/// Promotion follows casting rules. We only match one way, as the +/// both combinations are covered by calling this function twice +/// with the arguments swapped. +/// +/// If the types are not compatible, the result is `Type::Void`. +/// +/// The left-hand side is the type to promote from, and the right-hand +/// side is the type to promote to. So any promotion goes from lesser +/// type to greater type. +/// +/// This is more complicated as we have C99 promotion for simple types, +/// but complex types like `Complex`, and `Angle` don't follow those rules. +fn promote_types_asymmetric(lhs_ty: &Type, rhs_ty: &Type) -> Type { + let is_const = relax_constness(lhs_ty, rhs_ty); + #[allow(clippy::match_same_arms)] + match (lhs_ty, rhs_ty) { + (Type::Bit(..), Type::Bool(..)) => Type::Bool(is_const), + (Type::Bit(..), Type::Int(w, _)) => Type::Int(*w, is_const), + (Type::Bit(..), Type::UInt(w, _)) => Type::UInt(*w, is_const), + + (Type::Bit(..), Type::Angle(w, _)) => Type::Angle(*w, is_const), + + (Type::Bool(..), Type::Int(w, _)) => Type::Int(*w, is_const), + (Type::Bool(..), Type::UInt(w, _)) => Type::UInt(*w, is_const), + (Type::Bool(..), Type::Float(w, _)) => Type::Float(*w, is_const), + (Type::Bool(..), Type::Complex(w, _)) => Type::Complex(*w, is_const), + + (Type::UInt(..), Type::Int(..)) => Type::Int(promote_width(lhs_ty, rhs_ty), is_const), + (Type::UInt(..), Type::Float(..)) => Type::Float(promote_width(lhs_ty, rhs_ty), is_const), + (Type::UInt(..), Type::Complex(..)) => { + Type::Complex(promote_width(lhs_ty, rhs_ty), is_const) + } + + (Type::Int(..), Type::Float(..)) => Type::Float(promote_width(lhs_ty, rhs_ty), is_const), + (Type::Int(..), Type::Complex(..)) => { + Type::Complex(promote_width(lhs_ty, rhs_ty), is_const) + } + (Type::Angle(..), Type::Float(..)) => Type::Float(promote_width(lhs_ty, rhs_ty), is_const), + (Type::Float(..), Type::Complex(..)) => { + Type::Complex(promote_width(lhs_ty, rhs_ty), is_const) + } + _ => Type::Void, + } +} + +/// Compares two types for equality, ignoring constness. +pub(crate) fn types_equal_except_const(lhs: &Type, rhs: &Type) -> bool { + match (lhs, rhs) { + (Type::Bit(_), Type::Bit(_)) + | (Type::Qubit, Type::Qubit) + | (Type::HardwareQubit, Type::HardwareQubit) + | (Type::Bool(_), Type::Bool(_)) + | (Type::Duration(_), Type::Duration(_)) + | (Type::Stretch(_), Type::Stretch(_)) + | (Type::Range, Type::Range) + | (Type::Set, Type::Set) + | (Type::Void, Type::Void) + | (Type::Err, Type::Err) => true, + (Type::Int(lhs_width, _), Type::Int(rhs_width, _)) + | (Type::UInt(lhs_width, _), Type::UInt(rhs_width, _)) + | (Type::Float(lhs_width, _), Type::Float(rhs_width, _)) + | (Type::Angle(lhs_width, _), Type::Angle(rhs_width, _)) + | (Type::Complex(lhs_width, _), Type::Complex(rhs_width, _)) => lhs_width == rhs_width, + (Type::BitArray(lhs_dims, _), Type::BitArray(rhs_dims, _)) + | (Type::BoolArray(lhs_dims), Type::BoolArray(rhs_dims)) + | (Type::QubitArray(lhs_dims), Type::QubitArray(rhs_dims)) => lhs_dims == rhs_dims, + (Type::IntArray(lhs_width, lhs_dims), Type::IntArray(rhs_width, rhs_dims)) + | (Type::UIntArray(lhs_width, lhs_dims), Type::UIntArray(rhs_width, rhs_dims)) + | (Type::FloatArray(lhs_width, lhs_dims), Type::FloatArray(rhs_width, rhs_dims)) + | (Type::AngleArray(lhs_width, lhs_dims), Type::AngleArray(rhs_width, rhs_dims)) + | (Type::ComplexArray(lhs_width, lhs_dims), Type::ComplexArray(rhs_width, rhs_dims)) => { + lhs_width == rhs_width && lhs_dims == rhs_dims + } + (Type::Gate(lhs_cargs, lhs_qargs), Type::Gate(rhs_cargs, rhs_qargs)) => { + lhs_cargs == rhs_cargs && lhs_qargs == rhs_qargs + } + _ => false, + } +} + +/// Compares two types for equality, ignoring constness and width. +/// arrays are equal if their dimensions are equal. +pub(crate) fn base_types_equal(lhs: &Type, rhs: &Type) -> bool { + match (lhs, rhs) { + (Type::Bit(_), Type::Bit(_)) + | (Type::Qubit, Type::Qubit) + | (Type::HardwareQubit, Type::HardwareQubit) + | (Type::Bool(_), Type::Bool(_)) + | (Type::Duration(_), Type::Duration(_)) + | (Type::Stretch(_), Type::Stretch(_)) + | (Type::Range, Type::Range) + | (Type::Set, Type::Set) + | (Type::Void, Type::Void) + | (Type::Err, Type::Err) + | (Type::Int(_, _), Type::Int(_, _)) + | (Type::UInt(_, _), Type::UInt(_, _)) + | (Type::Float(_, _), Type::Float(_, _)) + | (Type::Angle(_, _), Type::Angle(_, _)) + | (Type::Complex(_, _), Type::Complex(_, _)) + | (Type::Gate(_, _), Type::Gate(_, _)) => true, + (Type::BitArray(lhs_dims, _), Type::BitArray(rhs_dims, _)) + | (Type::BoolArray(lhs_dims), Type::BoolArray(rhs_dims)) + | (Type::QubitArray(lhs_dims), Type::QubitArray(rhs_dims)) => lhs_dims == rhs_dims, + (Type::IntArray(_, lhs_dims), Type::IntArray(_, rhs_dims)) + | (Type::UIntArray(_, lhs_dims), Type::UIntArray(_, rhs_dims)) + | (Type::FloatArray(_, lhs_dims), Type::FloatArray(_, rhs_dims)) + | (Type::AngleArray(_, lhs_dims), Type::AngleArray(_, rhs_dims)) + | (Type::ComplexArray(_, lhs_dims), Type::ComplexArray(_, rhs_dims)) => { + lhs_dims == rhs_dims + } + _ => false, + } +} + +#[must_use] +pub fn can_cast_literal(lhs_ty: &Type, ty_lit: &Type) -> bool { + // todo: not sure if this top case is still needed after parser changes + if matches!(lhs_ty, Type::Int(..)) && matches!(ty_lit, Type::UInt(..)) { + return true; + } + // todo: not sure if this case is still needed after parser changes + if matches!(lhs_ty, Type::UInt(..)) { + return matches!(ty_lit, Type::Complex(..)); + } + + base_types_equal(lhs_ty, ty_lit) + || matches!( + (lhs_ty, ty_lit), + ( + Type::Float(_, _) | Type::Complex(_, _), + Type::Int(_, _) | Type::UInt(_, _) + ) | (Type::Complex(_, _), Type::Float(_, _)) + ) + || { + matches!(lhs_ty, Type::Bit(..) | Type::Bool(..)) + && matches!(ty_lit, Type::Bit(..) | Type::Bool(..)) + } + || { + match lhs_ty { + Type::BitArray(dims, _) => { + matches!(dims, ArrayDimensions::One(_)) + && matches!(ty_lit, Type::Int(_, _) | Type::UInt(_, _)) + } + _ => false, + } + } +} + +/// some literals can be cast to a specific type if the value is known +/// This is useful to avoid generating a cast expression in the AST +pub(crate) fn can_cast_literal_with_value_knowledge(lhs_ty: &Type, kind: &LiteralKind) -> bool { + if matches!(lhs_ty, &Type::Bit(_)) { + if let LiteralKind::Int(value) = kind { + return *value == 0 || *value == 1; + } + } + if matches!(lhs_ty, &Type::UInt(..)) { + if let LiteralKind::Int(value) = kind { + return *value >= 0; + } + } + false +} + +// https://openqasm.com/language/classical.html +pub(crate) fn unary_op_can_be_applied_to_type(op: crate::ast::UnaryOp, ty: &Type) -> bool { + match op { + crate::ast::UnaryOp::NotB => match ty { + Type::Bit(_) | Type::UInt(_, _) | Type::Angle(_, _) => true, + Type::BitArray(dims, _) | Type::UIntArray(_, dims) | Type::AngleArray(_, dims) => { + // the spe says "registers of the same size" which is a bit ambiguous + // but we can assume that it means that the array is a single dim + matches!(dims, ArrayDimensions::One(_)) + } + _ => false, + }, + NotL => matches!(ty, Type::Bool(_)), + crate::ast::UnaryOp::Neg => { + matches!(ty, Type::Int(_, _) | Type::Float(_, _) | Type::Angle(_, _)) + } + } +} diff --git a/compiler/qsc_qasm3/src/tests/declaration/integer.rs b/compiler/qsc_qasm3/src/tests/declaration/integer.rs index e4d9e1f9a0..529b9b1231 100644 --- a/compiler/qsc_qasm3/src/tests/declaration/integer.rs +++ b/compiler/qsc_qasm3/src/tests/declaration/integer.rs @@ -330,51 +330,6 @@ fn const_explicit_bitness_int_decl() -> miette::Result<(), Vec> { Ok(()) } -#[test] -fn assigning_uint_to_negative_lit_results_in_semantic_error() { - let source = " - const uint[10] x = -42; - "; - - let Err(errors) = compile_qasm_stmt_to_qsharp(source) else { - panic!("Expected error"); - }; - expect![ - r#"Cannot assign a value of Negative Int type to a classical variable of UInt(Some(10), True) type."# - ] - .assert_eq(&errors[0].to_string()); -} - -#[test] -fn implicit_bitness_uint_const_negative_decl_raises_semantic_error() { - let source = " - const uint x = -42; - "; - - let Err(errors) = compile_qasm_stmt_to_qsharp(source) else { - panic!("Expected error"); - }; - expect![ - r#"Cannot assign a value of Negative Int type to a classical variable of UInt(None, True) type."# - ] - .assert_eq(&errors[0].to_string()); -} - -#[test] -fn explicit_bitness_uint_const_negative_decl_raises_semantic_error() { - let source = " - const uint[32] x = -42; - "; - - let Err(errors) = compile_qasm_stmt_to_qsharp(source) else { - panic!("Expected error"); - }; - expect![ - r#"Cannot assign a value of Negative Int type to a classical variable of UInt(Some(32), True) type."# - ] - .assert_eq(&errors[0].to_string()); -} - #[test] fn implicit_bitness_int_negative_float_decl_causes_semantic_error() { let source = " diff --git a/compiler/qsc_qasm3/src/tests/declaration/unsigned_integer.rs b/compiler/qsc_qasm3/src/tests/declaration/unsigned_integer.rs index f5e1695c35..736ccb6bda 100644 --- a/compiler/qsc_qasm3/src/tests/declaration/unsigned_integer.rs +++ b/compiler/qsc_qasm3/src/tests/declaration/unsigned_integer.rs @@ -265,3 +265,48 @@ fn const_explicit_bitness_int_decl() -> miette::Result<(), Vec> { .assert_eq(&qsharp); Ok(()) } + +#[test] +fn assigning_uint_to_negative_lit_results_in_semantic_error() { + let source = " + const uint[10] x = -42; + "; + + let Err(errors) = compile_qasm_stmt_to_qsharp(source) else { + panic!("Expected error"); + }; + expect![ + r#"Cannot assign a value of Negative Int type to a classical variable of UInt(Some(10), True) type."# + ] + .assert_eq(&errors[0].to_string()); +} + +#[test] +fn implicit_bitness_uint_const_negative_decl_raises_semantic_error() { + let source = " + const uint x = -42; + "; + + let Err(errors) = compile_qasm_stmt_to_qsharp(source) else { + panic!("Expected error"); + }; + expect![ + r#"Cannot assign a value of Negative Int type to a classical variable of UInt(None, True) type."# + ] + .assert_eq(&errors[0].to_string()); +} + +#[test] +fn explicit_bitness_uint_const_negative_decl_raises_semantic_error() { + let source = " + const uint[32] x = -42; + "; + + let Err(errors) = compile_qasm_stmt_to_qsharp(source) else { + panic!("Expected error"); + }; + expect![ + r#"Cannot assign a value of Negative Int type to a classical variable of UInt(Some(32), True) type."# + ] + .assert_eq(&errors[0].to_string()); +} diff --git a/compiler/qsc_qasm3/src/types.rs b/compiler/qsc_qasm3/src/types.rs index 6b24bfd83a..046ba921d0 100644 --- a/compiler/qsc_qasm3/src/types.rs +++ b/compiler/qsc_qasm3/src/types.rs @@ -97,9 +97,8 @@ impl Complex { } } -#[allow(dead_code)] -#[derive(Debug, Clone, PartialEq, Eq)] -pub(crate) enum Type { +#[derive(Debug, Clone, Default, PartialEq, Eq)] +pub enum Type { Bool(bool), BigInt(bool), Complex(bool), @@ -117,7 +116,9 @@ pub(crate) enum Type { ResultArray(ArrayDimensions, bool), TupleArray(ArrayDimensions, Vec), /// Function or operation, with the number of classical parameters and qubits. - Callable(CallableKind, usize, usize), + Callable(CallableKind, u32, u32), + #[default] + Err, } #[derive(Debug, Clone, PartialEq, Eq)] @@ -156,6 +157,23 @@ impl From<&ArrayDims> for ArrayDimensions { } } +impl From<&crate::semantic::types::ArrayDimensions> for ArrayDimensions { + fn from(value: &crate::semantic::types::ArrayDimensions) -> Self { + match value { + crate::semantic::types::ArrayDimensions::One(dim) => { + ArrayDimensions::One(*dim as usize) + } + crate::semantic::types::ArrayDimensions::Two(dim1, dim2) => { + ArrayDimensions::Two(*dim1 as usize, *dim2 as usize) + } + crate::semantic::types::ArrayDimensions::Three(dim1, dim2, dim3) => { + ArrayDimensions::Three(*dim1 as usize, *dim2 as usize, *dim3 as usize) + } + _ => unimplemented!("Array dimensions greater than three are not supported."), + } + } +} + impl Display for Type { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { match self { @@ -196,6 +214,7 @@ impl Display for Type { Type::Callable(kind, num_classical, num_qubits) => { write!(f, "Callable({kind}, {num_classical}, {num_qubits})") } + Type::Err => write!(f, "Err"), } } } From 9d646be953efbc9ea4d2d57a8dd9e588c1c49f5f Mon Sep 17 00:00:00 2001 From: orpuente-MS <156957451+orpuente-MS@users.noreply.github.com> Date: Sat, 8 Mar 2025 09:24:06 -0800 Subject: [PATCH 055/108] Make Assign and AssignOp valid only in expression statements (#2215) Previously assignments and binary assignments were valid in any part of the expression tree. This PR makes Assign and AssignOp valid only in expression statements. --- compiler/qsc_qasm3/src/parser/expr.rs | 24 ++----- compiler/qsc_qasm3/src/parser/expr/tests.rs | 32 --------- compiler/qsc_qasm3/src/parser/stmt.rs | 56 ++++++++++----- compiler/qsc_qasm3/src/parser/stmt/tests.rs | 1 + .../src/parser/stmt/tests/expr_stmt.rs | 71 ++++++++++++++++++ .../src/parser/stmt/tests/invalid_stmts.rs | 72 +++++++++++++++++++ 6 files changed, 188 insertions(+), 68 deletions(-) create mode 100644 compiler/qsc_qasm3/src/parser/stmt/tests/invalid_stmts.rs diff --git a/compiler/qsc_qasm3/src/parser/expr.rs b/compiler/qsc_qasm3/src/parser/expr.rs index 1cc3bc7b4b..902bad0eaa 100644 --- a/compiler/qsc_qasm3/src/parser/expr.rs +++ b/compiler/qsc_qasm3/src/parser/expr.rs @@ -13,10 +13,10 @@ use qsc_data_structures::span::Span; use crate::{ ast::{ - self, list_from_iter, AssignExpr, AssignOpExpr, BinOp, BinaryOpExpr, Cast, DiscreteSet, - Expr, ExprKind, FunctionCall, GateOperand, HardwareQubit, Ident, IndexElement, IndexExpr, - IndexSet, IndexSetItem, IndexedIdent, List, Lit, LiteralKind, MeasureExpr, RangeDefinition, - TimeUnit, TypeDef, UnaryOp, ValueExpression, Version, + self, list_from_iter, BinOp, BinaryOpExpr, Cast, DiscreteSet, Expr, ExprKind, FunctionCall, + GateOperand, HardwareQubit, Ident, IndexElement, IndexExpr, IndexSet, IndexSetItem, + IndexedIdent, List, Lit, LiteralKind, MeasureExpr, RangeDefinition, TimeUnit, TypeDef, + UnaryOp, ValueExpression, Version, }, keyword::Keyword, lex::{ @@ -49,8 +49,6 @@ struct InfixOp { } enum OpKind { - Assign, - AssignBinary(BinOp), Binary(BinOp, Assoc), Funcall, Index, @@ -120,14 +118,6 @@ fn expr_op_with_lhs(s: &mut ParserContext, context: OpContext, mut lhs: Expr) -> s.advance(); let kind = match op.kind { - OpKind::Assign => { - let rhs = expr_op(s, OpContext::Precedence(op.precedence))?; - Box::new(ExprKind::Assign(AssignExpr { lhs, rhs })) - } - OpKind::AssignBinary(kind) => { - let rhs = expr_op(s, OpContext::Precedence(op.precedence))?; - Box::new(ExprKind::AssignOp(AssignOpExpr { op: kind, lhs, rhs })) - } OpKind::Binary(kind, assoc) => { let precedence = next_precedence(op.precedence, assoc); let rhs = expr_op(s, OpContext::Precedence(precedence))?; @@ -645,15 +635,11 @@ fn infix_op(name: OpName) -> Option { kind: OpKind::Index, precedence: 13, }), - TokenKind::Eq => Some(InfixOp { - kind: OpKind::Assign, - precedence: 0, - }), _ => None, } } -fn closed_bin_op(op: ClosedBinOp) -> BinOp { +pub(crate) fn closed_bin_op(op: ClosedBinOp) -> BinOp { match op { ClosedBinOp::Amp => BinOp::AndB, ClosedBinOp::AmpAmp => BinOp::AndL, diff --git a/compiler/qsc_qasm3/src/parser/expr/tests.rs b/compiler/qsc_qasm3/src/parser/expr/tests.rs index 1e8cec4ac5..64c050a3b1 100644 --- a/compiler/qsc_qasm3/src/parser/expr/tests.rs +++ b/compiler/qsc_qasm3/src/parser/expr/tests.rs @@ -1112,38 +1112,6 @@ fn lit_array() { ); } -#[test] -fn assignment_and_unop() { - check_expr( - "c = a && !b", - &expect![[r#" - Expr [0-11]: AssignExpr: - lhs: Expr [0-1]: Ident [0-1] "c" - rhs: Expr [4-11]: BinaryOpExpr: - op: AndL - lhs: Expr [4-5]: Ident [4-5] "a" - rhs: Expr [9-11]: UnaryOpExpr: - op: NotL - expr: Expr [10-11]: Ident [10-11] "b""#]], - ); -} - -#[test] -fn assignment_unop_and() { - check_expr( - "d = !a && b", - &expect![[r#" - Expr [0-11]: AssignExpr: - lhs: Expr [0-1]: Ident [0-1] "d" - rhs: Expr [4-11]: BinaryOpExpr: - op: AndL - lhs: Expr [4-6]: UnaryOpExpr: - op: NotL - expr: Expr [5-6]: Ident [5-6] "a" - rhs: Expr [10-11]: Ident [10-11] "b""#]], - ); -} - #[test] fn hardware_qubit() { check( diff --git a/compiler/qsc_qasm3/src/parser/stmt.rs b/compiler/qsc_qasm3/src/parser/stmt.rs index 680c192d73..6a79e964c7 100644 --- a/compiler/qsc_qasm3/src/parser/stmt.rs +++ b/compiler/qsc_qasm3/src/parser/stmt.rs @@ -17,16 +17,16 @@ use super::{ use crate::{ ast::{ list_from_iter, AccessControl, AliasDeclStmt, AngleType, Annotation, ArrayBaseTypeKind, - ArrayReferenceType, ArrayType, ArrayTypedParameter, BarrierStmt, BitType, Block, BoxStmt, - BreakStmt, CalibrationGrammarStmt, CalibrationStmt, Cast, ClassicalDeclarationStmt, - ComplexType, ConstantDeclStmt, ContinueStmt, DefCalStmt, DefStmt, DelayStmt, EndStmt, - EnumerableSet, Expr, ExprKind, ExprStmt, ExternDecl, ExternParameter, FloatType, ForStmt, - FunctionCall, GPhase, GateCall, GateModifierKind, GateOperand, IODeclaration, IOKeyword, - Ident, Identifier, IfStmt, IncludeStmt, IndexElement, IndexExpr, IndexSetItem, IntType, - List, LiteralKind, MeasureStmt, Pragma, QuantumGateDefinition, QuantumGateModifier, - QuantumTypedParameter, QubitDeclaration, RangeDefinition, ResetStmt, ReturnStmt, - ScalarType, ScalarTypeKind, ScalarTypedParameter, Stmt, StmtKind, SwitchCase, SwitchStmt, - TypeDef, TypedParameter, UIntType, WhileLoop, + ArrayReferenceType, ArrayType, ArrayTypedParameter, AssignExpr, BarrierStmt, BitType, + Block, BoxStmt, BreakStmt, CalibrationGrammarStmt, CalibrationStmt, Cast, + ClassicalDeclarationStmt, ComplexType, ConstantDeclStmt, ContinueStmt, DefCalStmt, DefStmt, + DelayStmt, EndStmt, EnumerableSet, Expr, ExprKind, ExprStmt, ExternDecl, ExternParameter, + FloatType, ForStmt, FunctionCall, GPhase, GateCall, GateModifierKind, GateOperand, + IODeclaration, IOKeyword, Ident, Identifier, IfStmt, IncludeStmt, IndexElement, IndexExpr, + IndexSetItem, IntType, List, LiteralKind, MeasureStmt, Pragma, QuantumGateDefinition, + QuantumGateModifier, QuantumTypedParameter, QubitDeclaration, RangeDefinition, ResetStmt, + ReturnStmt, ScalarType, ScalarTypeKind, ScalarTypedParameter, Stmt, StmtKind, SwitchCase, + SwitchStmt, TypeDef, TypedParameter, UIntType, WhileLoop, }, keyword::Keyword, lex::{cooked::Type, Delim, TokenKind}, @@ -1095,10 +1095,30 @@ fn parse_end_stmt(s: &mut ParserContext) -> Result { Ok(EndStmt { span: s.span(lo) }) } -/// Grammar: `expression SEMICOLON`. +/// Grammar: `(expression | assignExpr | AssignOpExpr) SEMICOLON`. fn parse_expression_stmt(s: &mut ParserContext, lhs: Option) -> Result { let expr = if let Some(lhs) = lhs { - expr::expr_with_lhs(s, lhs)? + if opt(s, |s| token(s, TokenKind::Eq))?.is_some() { + let rhs = expr::expr(s)?; + Expr { + span: s.span(lhs.span.lo), + kind: Box::new(ExprKind::Assign(AssignExpr { lhs, rhs })), + } + } else if let TokenKind::BinOpEq(op) = s.peek().kind { + s.advance(); + let op = expr::closed_bin_op(op); + let rhs = expr::expr(s)?; + Expr { + span: s.span(lhs.span.lo), + kind: Box::new(ExprKind::AssignOp(crate::ast::AssignOpExpr { + op, + lhs, + rhs, + })), + } + } else { + expr::expr_with_lhs(s, lhs)? + } } else { expr::expr(s)? }; @@ -1237,16 +1257,18 @@ fn parse_gate_call_stmt(s: &mut ParserContext) -> Result { let mut duration = opt(s, designator)?; let qubits = gate_operand_list(s)?; - recovering_semi(s); // If didn't parse modifiers, a duration, nor qubit args then this is an expr, not a gate call. if modifiers.is_empty() && duration.is_none() && qubits.is_empty() { - return Ok(StmtKind::ExprStmt(ExprStmt { - span: s.span(lo), - expr: gate_or_expr, - })); + return Ok(StmtKind::ExprStmt(parse_expression_stmt( + s, + Some(gate_or_expr), + )?)); } + // We parse the recovering semi after we call parse_expr_stmt. + recovering_semi(s); + // Reinterpret the function call or ident as a gate call. let (name, args) = match *gate_or_expr.kind { ExprKind::FunctionCall(FunctionCall { name, args, .. }) => (name, args), diff --git a/compiler/qsc_qasm3/src/parser/stmt/tests.rs b/compiler/qsc_qasm3/src/parser/stmt/tests.rs index 0e3a2f74a9..5b6e25c336 100644 --- a/compiler/qsc_qasm3/src/parser/stmt/tests.rs +++ b/compiler/qsc_qasm3/src/parser/stmt/tests.rs @@ -19,6 +19,7 @@ mod gate_def; mod gphase; mod if_stmt; mod include; +mod invalid_stmts; mod io_decl; mod measure; mod old_style_decl; diff --git a/compiler/qsc_qasm3/src/parser/stmt/tests/expr_stmt.rs b/compiler/qsc_qasm3/src/parser/stmt/tests/expr_stmt.rs index d5f55416e4..a039a335b7 100644 --- a/compiler/qsc_qasm3/src/parser/stmt/tests/expr_stmt.rs +++ b/compiler/qsc_qasm3/src/parser/stmt/tests/expr_stmt.rs @@ -34,6 +34,77 @@ fn identifier_plus_number() { ); } +#[test] +fn assignment() { + check( + parse, + "a = 1;", + &expect![[r#" + Stmt [0-6]: + annotations: + kind: ExprStmt [0-6]: + expr: Expr [0-5]: AssignExpr: + lhs: Expr [0-1]: Ident [0-1] "a" + rhs: Expr [4-5]: Lit: Int(1)"#]], + ); +} + +#[test] +fn binary_assignment() { + check( + parse, + "a += 1;", + &expect![[r#" + Stmt [0-7]: + annotations: + kind: ExprStmt [0-7]: + expr: Expr [0-6]: AssignOpExpr: + op: Add + lhs: Expr [0-1]: Ident [0-1] "a" + rhs: Expr [5-6]: Lit: Int(1)"#]], + ); +} + +#[test] +fn assignment_and_unop() { + check( + parse, + "c = a && !b;", + &expect![[r#" + Stmt [0-12]: + annotations: + kind: ExprStmt [0-12]: + expr: Expr [0-11]: AssignExpr: + lhs: Expr [0-1]: Ident [0-1] "c" + rhs: Expr [4-11]: BinaryOpExpr: + op: AndL + lhs: Expr [4-5]: Ident [4-5] "a" + rhs: Expr [9-11]: UnaryOpExpr: + op: NotL + expr: Expr [10-11]: Ident [10-11] "b""#]], + ); +} + +#[test] +fn assignment_unop_and() { + check( + parse, + "d = !a && b;", + &expect![[r#" + Stmt [0-12]: + annotations: + kind: ExprStmt [0-12]: + expr: Expr [0-11]: AssignExpr: + lhs: Expr [0-1]: Ident [0-1] "d" + rhs: Expr [4-11]: BinaryOpExpr: + op: AndL + lhs: Expr [4-6]: UnaryOpExpr: + op: NotL + expr: Expr [5-6]: Ident [5-6] "a" + rhs: Expr [10-11]: Ident [10-11] "b""#]], + ); +} + // These are negative unit tests for gate calls: #[test] diff --git a/compiler/qsc_qasm3/src/parser/stmt/tests/invalid_stmts.rs b/compiler/qsc_qasm3/src/parser/stmt/tests/invalid_stmts.rs new file mode 100644 index 0000000000..db14c8a438 --- /dev/null +++ b/compiler/qsc_qasm3/src/parser/stmt/tests/invalid_stmts.rs @@ -0,0 +1,72 @@ +use crate::parser::{stmt::parse, tests::check}; +use expect_test::expect; + +#[test] +fn assignment_in_if_condition() { + check( + parse, + "if (x = 2) { 3; }", + &expect![[r#" + Stmt [0-17]: + annotations: + kind: IfStmt [0-17]: + condition: Expr [4-5]: Ident [4-5] "x" + if_block: + Stmt [13-15]: + annotations: + kind: ExprStmt [13-15]: + expr: Expr [13-14]: Lit: Int(3) + else_block: + + [ + Error( + Token( + Close( + Paren, + ), + Eq, + Span { + lo: 6, + hi: 7, + }, + ), + ), + ]"#]], + ); +} + +#[test] +fn binary_op_assignment_in_if_condition() { + check( + parse, + "if (x += 2) { 3; }", + &expect![[r#" + Stmt [0-18]: + annotations: + kind: IfStmt [0-18]: + condition: Expr [4-5]: Ident [4-5] "x" + if_block: + Stmt [14-16]: + annotations: + kind: ExprStmt [14-16]: + expr: Expr [14-15]: Lit: Int(3) + else_block: + + [ + Error( + Token( + Close( + Paren, + ), + BinOpEq( + Plus, + ), + Span { + lo: 6, + hi: 8, + }, + ), + ), + ]"#]], + ); +} From 09ff45e6172d41600f7bf29277a06fef6bd38248 Mon Sep 17 00:00:00 2001 From: orpuente-MS <156957451+orpuente-MS@users.noreply.github.com> Date: Mon, 10 Mar 2025 22:01:24 -0700 Subject: [PATCH 056/108] Make assignment a StmtKind (#2216) This PR turns assignment into a StmtKind (it was a ExprKind before). --- compiler/qsc_qasm3/src/ast.rs | 26 +-- compiler/qsc_qasm3/src/parser/error.rs | 4 + compiler/qsc_qasm3/src/parser/expr.rs | 2 +- compiler/qsc_qasm3/src/parser/expr/tests.rs | 16 +- compiler/qsc_qasm3/src/parser/stmt.rs | 146 +++++++++++--- .../qsc_qasm3/src/parser/stmt/tests/alias.rs | 4 +- .../src/parser/stmt/tests/expr_stmt.rs | 180 ++++++++++++++---- .../src/parser/stmt/tests/for_loops.rs | 63 +++--- .../src/parser/stmt/tests/gate_call.rs | 31 ++- .../src/parser/stmt/tests/if_stmt.rs | 63 +++--- .../src/parser/stmt/tests/while_loops.rs | 36 ++-- compiler/qsc_qasm3/src/semantic/ast.rs | 22 ++- compiler/qsc_qasm3/src/semantic/lowerer.rs | 27 ++- 13 files changed, 443 insertions(+), 177 deletions(-) diff --git a/compiler/qsc_qasm3/src/ast.rs b/compiler/qsc_qasm3/src/ast.rs index d53088a362..c45bc40bae 100644 --- a/compiler/qsc_qasm3/src/ast.rs +++ b/compiler/qsc_qasm3/src/ast.rs @@ -312,6 +312,8 @@ impl Display for AliasDeclStmt { #[derive(Clone, Debug, Default)] pub enum StmtKind { Alias(AliasDeclStmt), + Assign(AssignStmt), + AssignOp(AssignOpStmt), Barrier(BarrierStmt), Box(BoxStmt), Break(BreakStmt), @@ -351,6 +353,8 @@ pub enum StmtKind { impl Display for StmtKind { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { match self { + StmtKind::Assign(stmt) => write!(f, "{stmt}"), + StmtKind::AssignOp(stmt) => write!(f, "{stmt}"), StmtKind::Alias(alias) => write!(f, "{alias}"), StmtKind::Barrier(barrier) => write!(f, "{barrier}"), StmtKind::Box(box_stmt) => write!(f, "{box_stmt}"), @@ -1432,8 +1436,6 @@ impl Display for SwitchCase { #[derive(Clone, Debug, Default)] pub enum ExprKind { - Assign(AssignExpr), - AssignOp(AssignOpExpr), /// An expression with invalid syntax that can't be parsed. #[default] Err, @@ -1458,37 +1460,37 @@ impl Display for ExprKind { ExprKind::FunctionCall(call) => write!(f, "{call}"), ExprKind::Cast(expr) => write!(f, "{expr}"), ExprKind::IndexExpr(expr) => write!(f, "{expr}"), - ExprKind::Assign(expr) => write!(f, "{expr}"), - ExprKind::AssignOp(expr) => write!(f, "{expr}"), ExprKind::Paren(expr) => write!(f, "Paren {expr}"), } } } #[derive(Clone, Debug)] -pub struct AssignExpr { - pub lhs: Expr, +pub struct AssignStmt { + pub span: Span, + pub lhs: IndexedIdent, pub rhs: Expr, } -impl Display for AssignExpr { +impl Display for AssignStmt { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { - writeln!(f, "AssignExpr:")?; + writeln_header(f, "AssignStmt", self.span)?; writeln_field(f, "lhs", &self.lhs)?; write_field(f, "rhs", &self.rhs) } } #[derive(Clone, Debug)] -pub struct AssignOpExpr { +pub struct AssignOpStmt { + pub span: Span, pub op: BinOp, - pub lhs: Expr, + pub lhs: IndexedIdent, pub rhs: Expr, } -impl Display for AssignOpExpr { +impl Display for AssignOpStmt { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { - writeln!(f, "AssignOpExpr:")?; + writeln_header(f, "AssignOpStmt", self.span)?; writeln_field(f, "op", &self.op)?; writeln_field(f, "lhs", &self.lhs)?; write_field(f, "rhs", &self.rhs) diff --git a/compiler/qsc_qasm3/src/parser/error.rs b/compiler/qsc_qasm3/src/parser/error.rs index ce4d35dfe6..fbba90d248 100644 --- a/compiler/qsc_qasm3/src/parser/error.rs +++ b/compiler/qsc_qasm3/src/parser/error.rs @@ -134,6 +134,9 @@ pub enum ErrorKind { #[error("invalid gate call designator")] #[diagnostic(code("Qasm3.Parse.InvalidGateCallDesignator"))] InvalidGateCallDesignator(#[label] Span), + #[error("multiple index operators are only allowed in assignments")] + #[diagnostic(code("Qasm3.Parse.MultipleIndexOperators"))] + MultipleIndexOperators(#[label] Span), } impl ErrorKind { @@ -156,6 +159,7 @@ impl ErrorKind { Self::ExpectedItem(token, span) => Self::ExpectedItem(token, span + offset), Self::GPhaseInvalidArguments(span) => Self::GPhaseInvalidArguments(span + offset), Self::InvalidGateCallDesignator(span) => Self::InvalidGateCallDesignator(span + offset), + Self::MultipleIndexOperators(span) => Self::MultipleIndexOperators(span + offset), } } } diff --git a/compiler/qsc_qasm3/src/parser/expr.rs b/compiler/qsc_qasm3/src/parser/expr.rs index 902bad0eaa..b9b082310f 100644 --- a/compiler/qsc_qasm3/src/parser/expr.rs +++ b/compiler/qsc_qasm3/src/parser/expr.rs @@ -468,7 +468,7 @@ fn cast_op(s: &mut ParserContext, r#type: TypeDef) -> Result { } fn index_expr(s: &mut ParserContext, lhs: Expr) -> Result { - let lo = s.span(0).hi - 1; + let lo = lhs.span.lo; let index = index_element(s)?; recovering_token(s, TokenKind::Close(Delim::Bracket)); Ok(ExprKind::IndexExpr(IndexExpr { diff --git a/compiler/qsc_qasm3/src/parser/expr/tests.rs b/compiler/qsc_qasm3/src/parser/expr/tests.rs index 64c050a3b1..a14716f10c 100644 --- a/compiler/qsc_qasm3/src/parser/expr/tests.rs +++ b/compiler/qsc_qasm3/src/parser/expr/tests.rs @@ -953,7 +953,7 @@ fn index_expr() { check_expr( "foo[1]", &expect![[r#" - Expr [0-6]: IndexExpr [3-6]: + Expr [0-6]: IndexExpr [0-6]: collection: Expr [0-3]: Ident [0-3] "foo" index: IndexSet [4-5]: values: @@ -966,7 +966,7 @@ fn index_set() { check_expr( "foo[{1, 4, 5}]", &expect![[r#" - Expr [0-14]: IndexExpr [3-14]: + Expr [0-14]: IndexExpr [0-14]: collection: Expr [0-3]: Ident [0-3] "foo" index: DiscreteSet [4-13]: values: @@ -981,7 +981,7 @@ fn index_multiple_ranges() { check_expr( "foo[1:5, 3:7, 4:8]", &expect![[r#" - Expr [0-18]: IndexExpr [3-18]: + Expr [0-18]: IndexExpr [0-18]: collection: Expr [0-3]: Ident [0-3] "foo" index: IndexSet [4-17]: values: @@ -1005,7 +1005,7 @@ fn index_range() { check_expr( "foo[1:5:2]", &expect![[r#" - Expr [0-10]: IndexExpr [3-10]: + Expr [0-10]: IndexExpr [0-10]: collection: Expr [0-3]: Ident [0-3] "foo" index: IndexSet [4-9]: values: @@ -1021,7 +1021,7 @@ fn index_full_range() { check_expr( "foo[:]", &expect![[r#" - Expr [0-6]: IndexExpr [3-6]: + Expr [0-6]: IndexExpr [0-6]: collection: Expr [0-3]: Ident [0-3] "foo" index: IndexSet [4-5]: values: @@ -1037,7 +1037,7 @@ fn index_range_start() { check_expr( "foo[1:]", &expect![[r#" - Expr [0-7]: IndexExpr [3-7]: + Expr [0-7]: IndexExpr [0-7]: collection: Expr [0-3]: Ident [0-3] "foo" index: IndexSet [4-6]: values: @@ -1053,7 +1053,7 @@ fn index_range_end() { check_expr( "foo[:5]", &expect![[r#" - Expr [0-7]: IndexExpr [3-7]: + Expr [0-7]: IndexExpr [0-7]: collection: Expr [0-3]: Ident [0-3] "foo" index: IndexSet [4-6]: values: @@ -1069,7 +1069,7 @@ fn index_range_step() { check_expr( "foo[:2:]", &expect![[r#" - Expr [0-8]: IndexExpr [3-8]: + Expr [0-8]: IndexExpr [0-8]: collection: Expr [0-3]: Ident [0-3] "foo" index: IndexSet [4-7]: values: diff --git a/compiler/qsc_qasm3/src/parser/stmt.rs b/compiler/qsc_qasm3/src/parser/stmt.rs index 6a79e964c7..5cd51becaa 100644 --- a/compiler/qsc_qasm3/src/parser/stmt.rs +++ b/compiler/qsc_qasm3/src/parser/stmt.rs @@ -17,8 +17,8 @@ use super::{ use crate::{ ast::{ list_from_iter, AccessControl, AliasDeclStmt, AngleType, Annotation, ArrayBaseTypeKind, - ArrayReferenceType, ArrayType, ArrayTypedParameter, AssignExpr, BarrierStmt, BitType, - Block, BoxStmt, BreakStmt, CalibrationGrammarStmt, CalibrationStmt, Cast, + ArrayReferenceType, ArrayType, ArrayTypedParameter, AssignOpStmt, AssignStmt, BarrierStmt, + BitType, Block, BoxStmt, BreakStmt, CalibrationGrammarStmt, CalibrationStmt, Cast, ClassicalDeclarationStmt, ComplexType, ConstantDeclStmt, ContinueStmt, DefCalStmt, DefStmt, DelayStmt, EndStmt, EnumerableSet, Expr, ExprKind, ExprStmt, ExternDecl, ExternParameter, FloatType, ForStmt, FunctionCall, GPhase, GateCall, GateModifierKind, GateOperand, @@ -107,6 +107,81 @@ pub(super) fn parse(s: &mut ParserContext) -> Result> { Box::new(StmtKind::Break(stmt)) } else if let Some(stmt) = opt(s, parse_end_stmt)? { Box::new(StmtKind::End(stmt)) + } else if let Some(indexed_ident) = opt(s, indexed_identifier)? { + if s.peek().kind == TokenKind::Eq { + s.advance(); + let expr = expr::expr(s)?; + recovering_semi(s); + Box::new(StmtKind::Assign(AssignStmt { + span: s.span(lo), + lhs: indexed_ident, + rhs: expr, + })) + } else if let TokenKind::BinOpEq(op) = s.peek().kind { + s.advance(); + let op = expr::closed_bin_op(op); + let expr = expr::expr(s)?; + recovering_semi(s); + Box::new(StmtKind::AssignOp(AssignOpStmt { + span: s.span(lo), + op, + lhs: indexed_ident, + rhs: expr, + })) + } else if s.peek().kind == TokenKind::Open(Delim::Paren) { + if !indexed_ident.indices.is_empty() { + s.push_error(Error::new(ErrorKind::Convert( + "Ident", + "IndexedIdent", + indexed_ident.span, + ))); + } + + let ident = indexed_ident.name; + + s.advance(); + let (args, _) = seq(s, expr::expr)?; + token(s, TokenKind::Close(Delim::Paren))?; + + let funcall = Expr { + span: s.span(lo), + kind: Box::new(ExprKind::FunctionCall(FunctionCall { + span: s.span(lo), + name: ident, + args: args.into_iter().map(Box::new).collect(), + })), + }; + + let expr = expr::expr_with_lhs(s, funcall)?; + + Box::new(parse_gate_call_with_expr(s, expr)?) + } else { + let kind = if indexed_ident.indices.is_empty() { + ExprKind::Ident(indexed_ident.name) + } else { + if indexed_ident.indices.len() > 1 { + s.push_error(Error::new(ErrorKind::MultipleIndexOperators( + indexed_ident.span, + ))); + } + + ExprKind::IndexExpr(IndexExpr { + span: indexed_ident.span, + collection: Expr { + span: indexed_ident.name.span, + kind: Box::new(ExprKind::Ident(indexed_ident.name)), + }, + index: *indexed_ident.indices[0].clone(), + }) + }; + + let expr = Expr { + span: indexed_ident.span, + kind: Box::new(kind), + }; + + Box::new(parse_gate_call_with_expr(s, expr)?) + } } else if let Some(stmt_kind) = opt(s, parse_gate_call_stmt)? { Box::new(stmt_kind) } else if let Some(stmt) = opt(s, |s| parse_expression_stmt(s, None))? { @@ -1098,27 +1173,7 @@ fn parse_end_stmt(s: &mut ParserContext) -> Result { /// Grammar: `(expression | assignExpr | AssignOpExpr) SEMICOLON`. fn parse_expression_stmt(s: &mut ParserContext, lhs: Option) -> Result { let expr = if let Some(lhs) = lhs { - if opt(s, |s| token(s, TokenKind::Eq))?.is_some() { - let rhs = expr::expr(s)?; - Expr { - span: s.span(lhs.span.lo), - kind: Box::new(ExprKind::Assign(AssignExpr { lhs, rhs })), - } - } else if let TokenKind::BinOpEq(op) = s.peek().kind { - s.advance(); - let op = expr::closed_bin_op(op); - let rhs = expr::expr(s)?; - Expr { - span: s.span(lhs.span.lo), - kind: Box::new(ExprKind::AssignOp(crate::ast::AssignOpExpr { - op, - lhs, - rhs, - })), - } - } else { - expr::expr_with_lhs(s, lhs)? - } + expr::expr_with_lhs(s, lhs)? } else { expr::expr(s)? }; @@ -1296,6 +1351,51 @@ fn parse_gate_call_stmt(s: &mut ParserContext) -> Result { })) } +/// This parser is used to disambiguate statements starting with an index +/// identifier. It is a simplified version of `parse_gate_call_stmt`. +fn parse_gate_call_with_expr(s: &mut ParserContext, gate_or_expr: Expr) -> Result { + let lo = gate_or_expr.span.lo; + let mut duration = opt(s, designator)?; + let qubits = gate_operand_list(s)?; + + // If didn't parse modifiers, a duration, nor qubit args then this is an expr, not a gate call. + if duration.is_none() && qubits.is_empty() { + return Ok(StmtKind::ExprStmt(parse_expression_stmt( + s, + Some(gate_or_expr), + )?)); + } + + // We parse the recovering semi after we call parse_expr_stmt. + recovering_semi(s); + + // Reinterpret the function call or ident as a gate call. + let (name, args) = match *gate_or_expr.kind { + ExprKind::FunctionCall(FunctionCall { name, args, .. }) => (name, args), + ExprKind::Ident(ident) => (ident, Default::default()), + ExprKind::IndexExpr(index_expr) => reinterpret_index_expr(index_expr, &mut duration)?, + _ => { + return Err(Error::new(ErrorKind::ExpectedItem( + TokenKind::Identifier, + gate_or_expr.span, + ))) + } + }; + + if qubits.is_empty() { + s.push_error(Error::new(ErrorKind::MissingGateCallOperands(s.span(lo)))); + } + + Ok(StmtKind::GateCall(GateCall { + span: s.span(lo), + modifiers: Default::default(), + name, + args, + qubits, + duration, + })) +} + /// This helper function reinterprets an indexed expression as /// a gate call. There are two valid cases we are interested in: /// 1. Ident[4] diff --git a/compiler/qsc_qasm3/src/parser/stmt/tests/alias.rs b/compiler/qsc_qasm3/src/parser/stmt/tests/alias.rs index 9a918a3296..3c6d47ec8a 100644 --- a/compiler/qsc_qasm3/src/parser/stmt/tests/alias.rs +++ b/compiler/qsc_qasm3/src/parser/stmt/tests/alias.rs @@ -31,7 +31,7 @@ fn concatenation_alias() { kind: AliasDeclStmt [0-32]: ident: Ident [4-5] "x" exprs: - Expr [8-14]: IndexExpr [9-14]: + Expr [8-14]: IndexExpr [8-14]: collection: Expr [8-9]: Ident [8-9] "a" index: IndexSet [10-13]: values: @@ -40,7 +40,7 @@ fn concatenation_alias() { step: end: Expr [12-13]: Lit: Int(2) Expr [18-19]: Ident [18-19] "b" - Expr [23-31]: IndexExpr [24-31]: + Expr [23-31]: IndexExpr [23-31]: collection: Expr [23-24]: Ident [23-24] "c" index: IndexSet [25-30]: values: diff --git a/compiler/qsc_qasm3/src/parser/stmt/tests/expr_stmt.rs b/compiler/qsc_qasm3/src/parser/stmt/tests/expr_stmt.rs index a039a335b7..5fd49612b6 100644 --- a/compiler/qsc_qasm3/src/parser/stmt/tests/expr_stmt.rs +++ b/compiler/qsc_qasm3/src/parser/stmt/tests/expr_stmt.rs @@ -40,31 +40,117 @@ fn assignment() { parse, "a = 1;", &expect![[r#" - Stmt [0-6]: - annotations: - kind: ExprStmt [0-6]: - expr: Expr [0-5]: AssignExpr: - lhs: Expr [0-1]: Ident [0-1] "a" + Stmt [0-6]: + annotations: + kind: AssignStmt [0-6]: + lhs: IndexedIdent [0-1]: + name: Ident [0-1] "a" + indices: rhs: Expr [4-5]: Lit: Int(1)"#]], ); } #[test] -fn binary_assignment() { +fn index_assignment() { + check( + parse, + "a[0] = 1;", + &expect![[r#" + Stmt [0-9]: + annotations: + kind: AssignStmt [0-9]: + lhs: IndexedIdent [0-4]: + name: Ident [0-1] "a" + indices: + IndexSet [2-3]: + values: + Expr [2-3]: Lit: Int(0) + rhs: Expr [7-8]: Lit: Int(1)"#]], + ); +} + +#[test] +fn multi_index_assignment() { + check( + parse, + "a[0][1] = 1;", + &expect![[r#" + Stmt [0-12]: + annotations: + kind: AssignStmt [0-12]: + lhs: IndexedIdent [0-7]: + name: Ident [0-1] "a" + indices: + IndexSet [2-3]: + values: + Expr [2-3]: Lit: Int(0) + IndexSet [5-6]: + values: + Expr [5-6]: Lit: Int(1) + rhs: Expr [10-11]: Lit: Int(1)"#]], + ); +} + +#[test] +fn assignment_op() { check( parse, "a += 1;", &expect![[r#" - Stmt [0-7]: - annotations: - kind: ExprStmt [0-7]: - expr: Expr [0-6]: AssignOpExpr: + Stmt [0-7]: + annotations: + kind: AssignOpStmt [0-7]: op: Add - lhs: Expr [0-1]: Ident [0-1] "a" + lhs: IndexedIdent [0-1]: + name: Ident [0-1] "a" + indices: rhs: Expr [5-6]: Lit: Int(1)"#]], ); } +#[test] +fn index_assignment_op() { + check( + parse, + "a[0] += 1;", + &expect![[r#" + Stmt [0-10]: + annotations: + kind: AssignOpStmt [0-10]: + op: Add + lhs: IndexedIdent [0-4]: + name: Ident [0-1] "a" + indices: + IndexSet [2-3]: + values: + Expr [2-3]: Lit: Int(0) + rhs: Expr [8-9]: Lit: Int(1)"#]], + ); +} + +#[test] +fn multi_index_assignment_op() { + check( + parse, + "a[0][1] += 1;", + &expect![[r#" + Stmt [0-13]: + annotations: + kind: AssignOpStmt [0-13]: + op: Add + lhs: IndexedIdent [0-7]: + name: Ident [0-1] "a" + indices: + IndexSet [2-3]: + values: + Expr [2-3]: Lit: Int(0) + IndexSet [5-6]: + values: + Expr [5-6]: Lit: Int(1) + rhs: Expr [11-12]: Lit: Int(1)"#]], + ); +} + #[test] fn assignment_and_unop() { check( @@ -73,15 +159,16 @@ fn assignment_and_unop() { &expect![[r#" Stmt [0-12]: annotations: - kind: ExprStmt [0-12]: - expr: Expr [0-11]: AssignExpr: - lhs: Expr [0-1]: Ident [0-1] "c" - rhs: Expr [4-11]: BinaryOpExpr: - op: AndL - lhs: Expr [4-5]: Ident [4-5] "a" - rhs: Expr [9-11]: UnaryOpExpr: - op: NotL - expr: Expr [10-11]: Ident [10-11] "b""#]], + kind: AssignStmt [0-12]: + lhs: IndexedIdent [0-1]: + name: Ident [0-1] "c" + indices: + rhs: Expr [4-11]: BinaryOpExpr: + op: AndL + lhs: Expr [4-5]: Ident [4-5] "a" + rhs: Expr [9-11]: UnaryOpExpr: + op: NotL + expr: Expr [10-11]: Ident [10-11] "b""#]], ); } @@ -93,15 +180,16 @@ fn assignment_unop_and() { &expect![[r#" Stmt [0-12]: annotations: - kind: ExprStmt [0-12]: - expr: Expr [0-11]: AssignExpr: - lhs: Expr [0-1]: Ident [0-1] "d" - rhs: Expr [4-11]: BinaryOpExpr: - op: AndL - lhs: Expr [4-6]: UnaryOpExpr: - op: NotL - expr: Expr [5-6]: Ident [5-6] "a" - rhs: Expr [10-11]: Ident [10-11] "b""#]], + kind: AssignStmt [0-12]: + lhs: IndexedIdent [0-1]: + name: Ident [0-1] "d" + indices: + rhs: Expr [4-11]: BinaryOpExpr: + op: AndL + lhs: Expr [4-6]: UnaryOpExpr: + op: NotL + expr: Expr [5-6]: Ident [5-6] "a" + rhs: Expr [10-11]: Ident [10-11] "b""#]], ); } @@ -153,7 +241,7 @@ fn indexed_function_call() { Stmt [0-14]: annotations: kind: ExprStmt [0-14]: - expr: Expr [0-13]: IndexExpr [10-13]: + expr: Expr [0-13]: IndexExpr [0-13]: collection: Expr [0-10]: FunctionCall [0-10]: name: Ident [0-4] "Name" args: @@ -174,7 +262,7 @@ fn multi_indexed_function_call() { Stmt [0-17]: annotations: kind: ExprStmt [0-17]: - expr: Expr [0-16]: IndexExpr [10-16]: + expr: Expr [0-16]: IndexExpr [0-16]: collection: Expr [0-10]: FunctionCall [0-10]: name: Ident [0-4] "Name" args: @@ -209,7 +297,7 @@ fn index_expr() { Stmt [0-8]: annotations: kind: ExprStmt [0-8]: - expr: Expr [0-7]: IndexExpr [4-7]: + expr: Expr [0-7]: IndexExpr [0-7]: collection: Expr [0-4]: Ident [0-4] "Name" index: IndexSet [5-6]: values: @@ -217,6 +305,34 @@ fn index_expr() { ); } +#[test] +fn index_expr_with_multiple_index_operators_errors() { + check( + parse, + "Name[1][2];", + &expect![[r#" + Stmt [0-11]: + annotations: + kind: ExprStmt [0-11]: + expr: Expr [0-10]: IndexExpr [0-10]: + collection: Expr [0-4]: Ident [0-4] "Name" + index: IndexSet [5-6]: + values: + Expr [5-6]: Lit: Int(1) + + [ + Error( + MultipleIndexOperators( + Span { + lo: 0, + hi: 10, + }, + ), + ), + ]"#]], + ); +} + #[test] fn cast_expr() { check( diff --git a/compiler/qsc_qasm3/src/parser/stmt/tests/for_loops.rs b/compiler/qsc_qasm3/src/parser/stmt/tests/for_loops.rs index 8d6bf920b7..30239971b5 100644 --- a/compiler/qsc_qasm3/src/parser/stmt/tests/for_loops.rs +++ b/compiler/qsc_qasm3/src/parser/stmt/tests/for_loops.rs @@ -27,10 +27,11 @@ fn simple_for_loop() { block: Stmt [38-44]: annotations: - kind: ExprStmt [38-44]: - expr: Expr [38-43]: AssignExpr: - lhs: Expr [38-39]: Ident [38-39] "a" - rhs: Expr [42-43]: Lit: Int(0)"#]], + kind: AssignStmt [38-44]: + lhs: IndexedIdent [38-39]: + name: Ident [38-39] "a" + indices: + rhs: Expr [42-43]: Lit: Int(0)"#]], ); } @@ -75,10 +76,11 @@ fn simple_for_loop_stmt_body() { block: Stmt [36-42]: annotations: - kind: ExprStmt [36-42]: - expr: Expr [36-41]: AssignExpr: - lhs: Expr [36-37]: Ident [36-37] "a" - rhs: Expr [40-41]: Lit: Int(0)"#]], + kind: AssignStmt [36-42]: + lhs: IndexedIdent [36-37]: + name: Ident [36-37] "a" + indices: + rhs: Expr [40-41]: Lit: Int(0)"#]], ); } @@ -104,10 +106,11 @@ fn for_loop_range() { block: Stmt [36-42]: annotations: - kind: ExprStmt [36-42]: - expr: Expr [36-41]: AssignExpr: - lhs: Expr [36-37]: Ident [36-37] "a" - rhs: Expr [40-41]: Lit: Int(0)"#]], + kind: AssignStmt [36-42]: + lhs: IndexedIdent [36-37]: + name: Ident [36-37] "a" + indices: + rhs: Expr [40-41]: Lit: Int(0)"#]], ); } @@ -133,10 +136,11 @@ fn for_loop_range_no_step() { block: Stmt [34-40]: annotations: - kind: ExprStmt [34-40]: - expr: Expr [34-39]: AssignExpr: - lhs: Expr [34-35]: Ident [34-35] "a" - rhs: Expr [38-39]: Lit: Int(0)"#]], + kind: AssignStmt [34-40]: + lhs: IndexedIdent [34-35]: + name: Ident [34-35] "a" + indices: + rhs: Expr [38-39]: Lit: Int(0)"#]], ); } @@ -159,10 +163,11 @@ fn for_loop_expr() { block: Stmt [31-37]: annotations: - kind: ExprStmt [31-37]: - expr: Expr [31-36]: AssignExpr: - lhs: Expr [31-32]: Ident [31-32] "a" - rhs: Expr [35-36]: Lit: Int(0)"#]], + kind: AssignStmt [31-37]: + lhs: IndexedIdent [31-32]: + name: Ident [31-32] "a" + indices: + rhs: Expr [35-36]: Lit: Int(0)"#]], ); } @@ -190,10 +195,11 @@ fn for_loop_with_continue_stmt() { block: Stmt [38-44]: annotations: - kind: ExprStmt [38-44]: - expr: Expr [38-43]: AssignExpr: - lhs: Expr [38-39]: Ident [38-39] "a" - rhs: Expr [42-43]: Lit: Int(0) + kind: AssignStmt [38-44]: + lhs: IndexedIdent [38-39]: + name: Ident [38-39] "a" + indices: + rhs: Expr [42-43]: Lit: Int(0) Stmt [53-62]: annotations: kind: ContinueStmt [53-62]"#]], @@ -224,10 +230,11 @@ fn for_loop_with_break_stmt() { block: Stmt [38-44]: annotations: - kind: ExprStmt [38-44]: - expr: Expr [38-43]: AssignExpr: - lhs: Expr [38-39]: Ident [38-39] "a" - rhs: Expr [42-43]: Lit: Int(0) + kind: AssignStmt [38-44]: + lhs: IndexedIdent [38-39]: + name: Ident [38-39] "a" + indices: + rhs: Expr [42-43]: Lit: Int(0) Stmt [53-59]: annotations: kind: BreakStmt [53-59]"#]], diff --git a/compiler/qsc_qasm3/src/parser/stmt/tests/gate_call.rs b/compiler/qsc_qasm3/src/parser/stmt/tests/gate_call.rs index 03e4a43334..78de9c02c0 100644 --- a/compiler/qsc_qasm3/src/parser/stmt/tests/gate_call.rs +++ b/compiler/qsc_qasm3/src/parser/stmt/tests/gate_call.rs @@ -252,7 +252,7 @@ fn multi_indexed_gate_call() { Error( InvalidGateCallDesignator( Span { - lo: 10, + lo: 0, hi: 16, }, ), @@ -287,14 +287,27 @@ fn gate_call_with_invalid_designator() { parse, "H[2us][3] q;", &expect![[r#" - Error( - InvalidGateCallDesignator( - Span { - lo: 6, - hi: 9, - }, + Stmt [0-12]: + annotations: + kind: GateCall [0-12]: + modifiers: + name: Ident [0-1] "H" + args: + duration: Expr [2-5]: Lit: Duration(2.0, Us) + qubits: + IndexedIdent [10-11]: + name: Ident [10-11] "q" + indices: + + [ + Error( + MultipleIndexOperators( + Span { + lo: 0, + hi: 9, + }, + ), ), - ) - "#]], + ]"#]], ); } diff --git a/compiler/qsc_qasm3/src/parser/stmt/tests/if_stmt.rs b/compiler/qsc_qasm3/src/parser/stmt/tests/if_stmt.rs index 29e8f0136d..e2c242942a 100644 --- a/compiler/qsc_qasm3/src/parser/stmt/tests/if_stmt.rs +++ b/compiler/qsc_qasm3/src/parser/stmt/tests/if_stmt.rs @@ -26,17 +26,19 @@ fn simple_if_stmt() { if_block: Stmt [27-33]: annotations: - kind: ExprStmt [27-33]: - expr: Expr [27-32]: AssignExpr: - lhs: Expr [27-28]: Ident [27-28] "a" - rhs: Expr [31-32]: Lit: Int(0) + kind: AssignStmt [27-33]: + lhs: IndexedIdent [27-28]: + name: Ident [27-28] "a" + indices: + rhs: Expr [31-32]: Lit: Int(0) else_block: Stmt [55-61]: annotations: - kind: ExprStmt [55-61]: - expr: Expr [55-60]: AssignExpr: - lhs: Expr [55-56]: Ident [55-56] "a" - rhs: Expr [59-60]: Lit: Int(1)"#]], + kind: AssignStmt [55-61]: + lhs: IndexedIdent [55-56]: + name: Ident [55-56] "a" + indices: + rhs: Expr [59-60]: Lit: Int(1)"#]], ); } @@ -60,10 +62,11 @@ fn if_stmt_missing_else() { if_block: Stmt [27-33]: annotations: - kind: ExprStmt [27-33]: - expr: Expr [27-32]: AssignExpr: - lhs: Expr [27-28]: Ident [27-28] "a" - rhs: Expr [31-32]: Lit: Int(0) + kind: AssignStmt [27-33]: + lhs: IndexedIdent [27-28]: + name: Ident [27-28] "a" + indices: + rhs: Expr [31-32]: Lit: Int(0) else_block: "#]], ); } @@ -106,17 +109,19 @@ fn nested_if_stmts() { if_block: Stmt [55-61]: annotations: - kind: ExprStmt [55-61]: - expr: Expr [55-60]: AssignExpr: - lhs: Expr [55-56]: Ident [55-56] "a" - rhs: Expr [59-60]: Lit: Int(0) + kind: AssignStmt [55-61]: + lhs: IndexedIdent [55-56]: + name: Ident [55-56] "a" + indices: + rhs: Expr [59-60]: Lit: Int(0) else_block: Stmt [91-97]: annotations: - kind: ExprStmt [91-97]: - expr: Expr [91-96]: AssignExpr: - lhs: Expr [91-92]: Ident [91-92] "a" - rhs: Expr [95-96]: Lit: Int(1) + kind: AssignStmt [91-97]: + lhs: IndexedIdent [91-92]: + name: Ident [91-92] "a" + indices: + rhs: Expr [95-96]: Lit: Int(1) else_block: Stmt [129-209]: annotations: @@ -128,16 +133,18 @@ fn nested_if_stmts() { if_block: Stmt [157-163]: annotations: - kind: ExprStmt [157-163]: - expr: Expr [157-162]: AssignExpr: - lhs: Expr [157-158]: Ident [157-158] "a" - rhs: Expr [161-162]: Lit: Int(2) + kind: AssignStmt [157-163]: + lhs: IndexedIdent [157-158]: + name: Ident [157-158] "a" + indices: + rhs: Expr [161-162]: Lit: Int(2) else_block: Stmt [193-199]: annotations: - kind: ExprStmt [193-199]: - expr: Expr [193-198]: AssignExpr: - lhs: Expr [193-194]: Ident [193-194] "a" - rhs: Expr [197-198]: Lit: Int(3)"#]], + kind: AssignStmt [193-199]: + lhs: IndexedIdent [193-194]: + name: Ident [193-194] "a" + indices: + rhs: Expr [197-198]: Lit: Int(3)"#]], ); } diff --git a/compiler/qsc_qasm3/src/parser/stmt/tests/while_loops.rs b/compiler/qsc_qasm3/src/parser/stmt/tests/while_loops.rs index 6a7cff7fde..4c5a1e0af1 100644 --- a/compiler/qsc_qasm3/src/parser/stmt/tests/while_loops.rs +++ b/compiler/qsc_qasm3/src/parser/stmt/tests/while_loops.rs @@ -23,10 +23,11 @@ fn simple_while() { block: Stmt [30-36]: annotations: - kind: ExprStmt [30-36]: - expr: Expr [30-35]: AssignExpr: - lhs: Expr [30-31]: Ident [30-31] "a" - rhs: Expr [34-35]: Lit: Int(0)"#]], + kind: AssignStmt [30-36]: + lhs: IndexedIdent [30-31]: + name: Ident [30-31] "a" + indices: + rhs: Expr [34-35]: Lit: Int(0)"#]], ); } @@ -62,10 +63,11 @@ fn while_stmt_body() { block: Stmt [28-34]: annotations: - kind: ExprStmt [28-34]: - expr: Expr [28-33]: AssignExpr: - lhs: Expr [28-29]: Ident [28-29] "a" - rhs: Expr [32-33]: Lit: Int(0)"#]], + kind: AssignStmt [28-34]: + lhs: IndexedIdent [28-29]: + name: Ident [28-29] "a" + indices: + rhs: Expr [32-33]: Lit: Int(0)"#]], ); } @@ -89,10 +91,11 @@ fn while_loop_with_continue_stmt() { block: Stmt [30-36]: annotations: - kind: ExprStmt [30-36]: - expr: Expr [30-35]: AssignExpr: - lhs: Expr [30-31]: Ident [30-31] "a" - rhs: Expr [34-35]: Lit: Int(0) + kind: AssignStmt [30-36]: + lhs: IndexedIdent [30-31]: + name: Ident [30-31] "a" + indices: + rhs: Expr [34-35]: Lit: Int(0) Stmt [45-54]: annotations: kind: ContinueStmt [45-54]"#]], @@ -119,10 +122,11 @@ fn while_loop_with_break_stmt() { block: Stmt [30-36]: annotations: - kind: ExprStmt [30-36]: - expr: Expr [30-35]: AssignExpr: - lhs: Expr [30-31]: Ident [30-31] "a" - rhs: Expr [34-35]: Lit: Int(0) + kind: AssignStmt [30-36]: + lhs: IndexedIdent [30-31]: + name: Ident [30-31] "a" + indices: + rhs: Expr [34-35]: Lit: Int(0) Stmt [45-51]: annotations: kind: BreakStmt [45-51]"#]], diff --git a/compiler/qsc_qasm3/src/semantic/ast.rs b/compiler/qsc_qasm3/src/semantic/ast.rs index 7b5d6bd8d2..dcbebbaee9 100644 --- a/compiler/qsc_qasm3/src/semantic/ast.rs +++ b/compiler/qsc_qasm3/src/semantic/ast.rs @@ -304,6 +304,8 @@ impl Display for AliasDeclStmt { #[derive(Clone, Debug, Default)] pub enum StmtKind { Alias(AliasDeclStmt), + Assign(AssignStmt), + AssignOp(AssignOpStmt), Barrier(BarrierStmt), Box(BoxStmt), Block(Box), @@ -340,6 +342,8 @@ impl Display for StmtKind { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { match self { StmtKind::Alias(alias) => write!(f, "{alias}"), + StmtKind::Assign(stmt) => write!(f, "{stmt}"), + StmtKind::AssignOp(stmt) => write!(f, "{stmt}"), StmtKind::Barrier(barrier) => write!(f, "{barrier}"), StmtKind::Box(box_stmt) => write!(f, "{box_stmt}"), StmtKind::Block(block) => write!(f, "{block}"), @@ -1368,8 +1372,6 @@ impl Display for SwitchCase { #[derive(Clone, Debug, Default)] pub enum ExprKind { - Assign(AssignExpr), - AssignOp(AssignOpExpr), /// An expression with invalid syntax that can't be parsed. #[default] Err, @@ -1394,37 +1396,37 @@ impl Display for ExprKind { ExprKind::FunctionCall(call) => write!(f, "{call}"), ExprKind::Cast(expr) => write!(f, "{expr}"), ExprKind::IndexExpr(expr) => write!(f, "{expr}"), - ExprKind::Assign(expr) => write!(f, "{expr}"), - ExprKind::AssignOp(expr) => write!(f, "{expr}"), ExprKind::Paren(expr) => write!(f, "Paren {expr}"), } } } #[derive(Clone, Debug)] -pub struct AssignExpr { +pub struct AssignStmt { + pub span: Span, pub lhs: Expr, pub rhs: Expr, } -impl Display for AssignExpr { +impl Display for AssignStmt { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { - writeln!(f, "AssignExpr:")?; + writeln_header(f, "AssignStmt", self.span)?; writeln_field(f, "lhs", &self.lhs)?; write_field(f, "rhs", &self.rhs) } } #[derive(Clone, Debug)] -pub struct AssignOpExpr { +pub struct AssignOpStmt { + pub span: Span, pub op: BinOp, pub lhs: Expr, pub rhs: Expr, } -impl Display for AssignOpExpr { +impl Display for AssignOpStmt { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { - writeln!(f, "AssignOpExpr:")?; + writeln_header(f, "AssignOpStmt", self.span)?; writeln_field(f, "op", &self.op)?; writeln_field(f, "lhs", &self.lhs)?; write_field(f, "rhs", &self.rhs) diff --git a/compiler/qsc_qasm3/src/semantic/lowerer.rs b/compiler/qsc_qasm3/src/semantic/lowerer.rs index b15e0a7c9d..e0976acccc 100644 --- a/compiler/qsc_qasm3/src/semantic/lowerer.rs +++ b/compiler/qsc_qasm3/src/semantic/lowerer.rs @@ -144,6 +144,12 @@ impl Lowerer { crate::ast::StmtKind::Alias(stmt) => { super::ast::StmtKind::Alias(self.lower_alias(stmt)?) } + crate::ast::StmtKind::Assign(stmt) => { + super::ast::StmtKind::Assign(self.lower_assign(stmt)?) + } + crate::ast::StmtKind::AssignOp(stmt) => { + super::ast::StmtKind::AssignOp(self.lower_assign_op(stmt)?) + } crate::ast::StmtKind::Barrier(stmt) => { super::ast::StmtKind::Barrier(self.lower_barrier(stmt)?) } @@ -363,16 +369,21 @@ impl Lowerer { }) } + fn lower_assign(&mut self, assign: &crate::ast::AssignStmt) -> Option { + self.push_unimplemented_error_message("assign stmt", assign.span); + None + } + + fn lower_assign_op( + &mut self, + assign_op: &crate::ast::AssignOpStmt, + ) -> Option { + self.push_unimplemented_error_message("assign op stmt", assign_op.span); + None + } + fn lower_expr(&mut self, expr: &crate::ast::Expr) -> Option { match &*expr.kind { - crate::ast::ExprKind::Assign(_) => { - self.push_unimplemented_error_message("assign expr", expr.span); - None - } - crate::ast::ExprKind::AssignOp(_) => { - self.push_unimplemented_error_message("assignop expr", expr.span); - None - } crate::ast::ExprKind::BinaryOp(_) => { self.push_unimplemented_error_message("binary op expr", expr.span); None From 687b467a9a4bb34de99c98e97be09db7b9cf6b53 Mon Sep 17 00:00:00 2001 From: orpuente-MS <156957451+orpuente-MS@users.noreply.github.com> Date: Wed, 12 Mar 2025 07:30:32 -0700 Subject: [PATCH 057/108] Add grammar docstrings to QASM3 parser (#2222) This PR: 1. Fixes a bug with block statements 2. Adds grammar docstrings to the QASM3 parser. 3. Adds the invalid tests in the reference parser to make sure they are invalid for our parser as well. --- compiler/qsc_qasm3/src/parser/expr.rs | 11 + compiler/qsc_qasm3/src/parser/prgm.rs | 2 + compiler/qsc_qasm3/src/parser/stmt.rs | 342 ++++-- compiler/qsc_qasm3/src/parser/stmt/tests.rs | 1 + .../qsc_qasm3/src/parser/stmt/tests/block.rs | 44 + .../src/parser/stmt/tests/invalid_stmts.rs | 83 +- .../parser/stmt/tests/invalid_stmts/branch.rs | 205 ++++ .../parser/stmt/tests/invalid_stmts/cal.rs | 69 ++ .../stmt/tests/invalid_stmts/constant.rs | 153 +++ .../parser/stmt/tests/invalid_stmts/decl.rs | 994 ++++++++++++++++++ .../stmt/tests/invalid_stmts/gate_calls.rs | 213 ++++ .../stmt/tests/invalid_stmts/headers.rs | 231 ++++ .../src/parser/stmt/tests/invalid_stmts/io.rs | 187 ++++ .../parser/stmt/tests/invalid_stmts/loops.rs | 289 +++++ .../stmt/tests/invalid_stmts/measure.rs | 180 ++++ .../parser/stmt/tests/invalid_stmts/switch.rs | 193 ++++ .../parser/stmt/tests/invalid_stmts/tokens.rs | 298 ++++++ 17 files changed, 3312 insertions(+), 183 deletions(-) create mode 100644 compiler/qsc_qasm3/src/parser/stmt/tests/block.rs create mode 100644 compiler/qsc_qasm3/src/parser/stmt/tests/invalid_stmts/branch.rs create mode 100644 compiler/qsc_qasm3/src/parser/stmt/tests/invalid_stmts/cal.rs create mode 100644 compiler/qsc_qasm3/src/parser/stmt/tests/invalid_stmts/constant.rs create mode 100644 compiler/qsc_qasm3/src/parser/stmt/tests/invalid_stmts/decl.rs create mode 100644 compiler/qsc_qasm3/src/parser/stmt/tests/invalid_stmts/gate_calls.rs create mode 100644 compiler/qsc_qasm3/src/parser/stmt/tests/invalid_stmts/headers.rs create mode 100644 compiler/qsc_qasm3/src/parser/stmt/tests/invalid_stmts/io.rs create mode 100644 compiler/qsc_qasm3/src/parser/stmt/tests/invalid_stmts/loops.rs create mode 100644 compiler/qsc_qasm3/src/parser/stmt/tests/invalid_stmts/measure.rs create mode 100644 compiler/qsc_qasm3/src/parser/stmt/tests/invalid_stmts/switch.rs create mode 100644 compiler/qsc_qasm3/src/parser/stmt/tests/invalid_stmts/tokens.rs diff --git a/compiler/qsc_qasm3/src/parser/expr.rs b/compiler/qsc_qasm3/src/parser/expr.rs index b9b082310f..5f679cd385 100644 --- a/compiler/qsc_qasm3/src/parser/expr.rs +++ b/compiler/qsc_qasm3/src/parser/expr.rs @@ -680,6 +680,7 @@ fn unescape(s: &str) -> std::result::Result { Ok(buf) } +/// Grammar: `LBRACKET expression RBRACKET`. pub(super) fn designator(s: &mut ParserContext) -> Result { token(s, TokenKind::Open(Delim::Bracket))?; let expr = expr(s)?; @@ -756,6 +757,7 @@ fn hardware_qubit(s: &mut ParserContext) -> Result { }) } +/// Grammar: `Identifier indexOperator*`. pub(crate) fn indexed_identifier(s: &mut ParserContext) -> Result { let lo = s.peek().span.lo; let name: Ident = ident(s)?; @@ -768,6 +770,15 @@ pub(crate) fn indexed_identifier(s: &mut ParserContext) -> Result }) } +/// Grammar: +/// ```g4 +/// LBRACKET +/// ( +/// setExpression +/// | (expression | rangeExpression) (COMMA (expression | rangeExpression))* COMMA? +/// ) +/// RBRACKET +/// ``` fn index_operand(s: &mut ParserContext) -> Result { token(s, TokenKind::Open(Delim::Bracket))?; let index = index_element(s)?; diff --git a/compiler/qsc_qasm3/src/parser/prgm.rs b/compiler/qsc_qasm3/src/parser/prgm.rs index 5de4907612..928b033c8e 100644 --- a/compiler/qsc_qasm3/src/parser/prgm.rs +++ b/compiler/qsc_qasm3/src/parser/prgm.rs @@ -13,6 +13,7 @@ use crate::{ use super::ParserContext; +/// Grammar: `version? statementOrScope* EOF`. pub(super) fn parse(s: &mut ParserContext) -> Result { let lo = s.peek().span.lo; let version = opt(s, parse_version)?; @@ -29,6 +30,7 @@ pub(super) fn parse(s: &mut ParserContext) -> Result { }) } +/// Grammar: `OPENQASM VersionSpecifier SEMICOLON`. fn parse_version(s: &mut ParserContext<'_>) -> Result { s.expect(WordKinds::OpenQASM); token(s, TokenKind::Keyword(crate::keyword::Keyword::OpenQASM))?; diff --git a/compiler/qsc_qasm3/src/parser/stmt.rs b/compiler/qsc_qasm3/src/parser/stmt.rs index 5cd51becaa..a72c0eac9b 100644 --- a/compiler/qsc_qasm3/src/parser/stmt.rs +++ b/compiler/qsc_qasm3/src/parser/stmt.rs @@ -23,10 +23,10 @@ use crate::{ DelayStmt, EndStmt, EnumerableSet, Expr, ExprKind, ExprStmt, ExternDecl, ExternParameter, FloatType, ForStmt, FunctionCall, GPhase, GateCall, GateModifierKind, GateOperand, IODeclaration, IOKeyword, Ident, Identifier, IfStmt, IncludeStmt, IndexElement, IndexExpr, - IndexSetItem, IntType, List, LiteralKind, MeasureStmt, Pragma, QuantumGateDefinition, - QuantumGateModifier, QuantumTypedParameter, QubitDeclaration, RangeDefinition, ResetStmt, - ReturnStmt, ScalarType, ScalarTypeKind, ScalarTypedParameter, Stmt, StmtKind, SwitchCase, - SwitchStmt, TypeDef, TypedParameter, UIntType, WhileLoop, + IndexSetItem, IndexedIdent, IntType, List, LiteralKind, MeasureStmt, Pragma, + QuantumGateDefinition, QuantumGateModifier, QuantumTypedParameter, QubitDeclaration, + RangeDefinition, ResetStmt, ReturnStmt, ScalarType, ScalarTypeKind, ScalarTypedParameter, + Stmt, StmtKind, SwitchCase, SwitchStmt, TypeDef, TypedParameter, UIntType, WhileLoop, }, keyword::Keyword, lex::{cooked::Type, Delim, TokenKind}, @@ -34,7 +34,45 @@ use crate::{ use super::{prim::token, ParserContext}; -#[allow(clippy::too_many_lines)] +/// Our implementation differs slightly from the grammar in +/// that we accumulate annotations and append them to the next +/// consecutive statement. +/// +/// Grammar: +/// ```g4 +/// pragma +/// | annotation* ( +/// aliasDeclarationStatement +/// | assignmentStatement +/// | barrierStatement +/// | boxStatement +/// | breakStatement +/// | calStatement +/// | calibrationGrammarStatement +/// | classicalDeclarationStatement +/// | constDeclarationStatement +/// | continueStatement +/// | defStatement +/// | defcalStatement +/// | delayStatement +/// | endStatement +/// | expressionStatement +/// | externStatement +/// | forStatement +/// | gateCallStatement +/// | gateStatement +/// | ifStatement +/// | includeStatement +/// | ioDeclarationStatement +/// | measureArrowAssignmentStatement +/// | oldStyleDeclarationStatement +/// | quantumDeclarationStatement +/// | resetStatement +/// | returnStatement +/// | switchStatement +/// | whileStatement +/// ) +/// ``` pub(super) fn parse(s: &mut ParserContext) -> Result> { let lo = s.peek().span.lo; if let Some(pragma) = opt(s, parse_pragma)? { @@ -45,6 +83,7 @@ pub(super) fn parse(s: &mut ParserContext) -> Result> { })); } let attrs = many(s, parse_annotation)?; + let kind = if token(s, TokenKind::Semicolon).is_ok() { if attrs.is_empty() { Box::new(StmtKind::Empty) @@ -60,25 +99,7 @@ pub(super) fn parse(s: &mut ParserContext) -> Result> { } else if let Some(include) = opt(s, parse_include)? { Box::new(include) } else if let Some(ty) = opt(s, scalar_or_array_type)? { - if matches!(s.peek().kind, TokenKind::Identifier) { - Box::new(parse_non_constant_classical_decl(s, ty, lo)?) - } else { - token(s, TokenKind::Open(Delim::Paren))?; - let arg = expr::expr(s)?; - recovering_token(s, TokenKind::Close(Delim::Paren)); - let cast_expr = Expr { - span: s.span(ty.span().lo), - kind: Box::new(ExprKind::Cast(Cast { - span: s.span(ty.span().lo), - ty, - arg, - })), - }; - Box::new(StmtKind::ExprStmt(parse_expression_stmt( - s, - Some(cast_expr), - )?)) - } + Box::new(disambiguate_type(s, ty)?) } else if let Some(decl) = opt(s, parse_constant_classical_decl)? { Box::new(decl) } else if let Some(decl) = opt(s, parse_quantum_decl)? { @@ -108,80 +129,7 @@ pub(super) fn parse(s: &mut ParserContext) -> Result> { } else if let Some(stmt) = opt(s, parse_end_stmt)? { Box::new(StmtKind::End(stmt)) } else if let Some(indexed_ident) = opt(s, indexed_identifier)? { - if s.peek().kind == TokenKind::Eq { - s.advance(); - let expr = expr::expr(s)?; - recovering_semi(s); - Box::new(StmtKind::Assign(AssignStmt { - span: s.span(lo), - lhs: indexed_ident, - rhs: expr, - })) - } else if let TokenKind::BinOpEq(op) = s.peek().kind { - s.advance(); - let op = expr::closed_bin_op(op); - let expr = expr::expr(s)?; - recovering_semi(s); - Box::new(StmtKind::AssignOp(AssignOpStmt { - span: s.span(lo), - op, - lhs: indexed_ident, - rhs: expr, - })) - } else if s.peek().kind == TokenKind::Open(Delim::Paren) { - if !indexed_ident.indices.is_empty() { - s.push_error(Error::new(ErrorKind::Convert( - "Ident", - "IndexedIdent", - indexed_ident.span, - ))); - } - - let ident = indexed_ident.name; - - s.advance(); - let (args, _) = seq(s, expr::expr)?; - token(s, TokenKind::Close(Delim::Paren))?; - - let funcall = Expr { - span: s.span(lo), - kind: Box::new(ExprKind::FunctionCall(FunctionCall { - span: s.span(lo), - name: ident, - args: args.into_iter().map(Box::new).collect(), - })), - }; - - let expr = expr::expr_with_lhs(s, funcall)?; - - Box::new(parse_gate_call_with_expr(s, expr)?) - } else { - let kind = if indexed_ident.indices.is_empty() { - ExprKind::Ident(indexed_ident.name) - } else { - if indexed_ident.indices.len() > 1 { - s.push_error(Error::new(ErrorKind::MultipleIndexOperators( - indexed_ident.span, - ))); - } - - ExprKind::IndexExpr(IndexExpr { - span: indexed_ident.span, - collection: Expr { - span: indexed_ident.name.span, - kind: Box::new(ExprKind::Ident(indexed_ident.name)), - }, - index: *indexed_ident.indices[0].clone(), - }) - }; - - let expr = Expr { - span: indexed_ident.span, - kind: Box::new(kind), - }; - - Box::new(parse_gate_call_with_expr(s, expr)?) - } + Box::new(disambiguate_ident(s, indexed_ident)?) } else if let Some(stmt_kind) = opt(s, parse_gate_call_stmt)? { Box::new(stmt_kind) } else if let Some(stmt) = opt(s, |s| parse_expression_stmt(s, None))? { @@ -219,13 +167,122 @@ pub(super) fn parse(s: &mut ParserContext) -> Result> { })) } +/// This helper function allows us to disambiguate between +/// non-constant declarations and cast expressions when +/// reading a `TypeDef`. +fn disambiguate_type(s: &mut ParserContext, ty: TypeDef) -> Result { + let lo = ty.span().lo; + if matches!(s.peek().kind, TokenKind::Identifier) { + Ok(parse_non_constant_classical_decl(s, ty, lo)?) + } else { + token(s, TokenKind::Open(Delim::Paren))?; + let arg = expr::expr(s)?; + recovering_token(s, TokenKind::Close(Delim::Paren)); + let cast_expr = Expr { + span: s.span(ty.span().lo), + kind: Box::new(ExprKind::Cast(Cast { + span: s.span(ty.span().lo), + ty, + arg, + })), + }; + Ok(StmtKind::ExprStmt(parse_expression_stmt( + s, + Some(cast_expr), + )?)) + } +} + +/// This helper function allows us to disambiguate between +/// assignments, assignment operations, gate calls, and +/// `expr_stmts` beginning with an ident or a function call +/// when reading an `Ident`. +fn disambiguate_ident(s: &mut ParserContext, indexed_ident: IndexedIdent) -> Result { + let lo = indexed_ident.span.lo; + if s.peek().kind == TokenKind::Eq { + s.advance(); + let expr = expr::expr(s)?; + recovering_semi(s); + Ok(StmtKind::Assign(AssignStmt { + span: s.span(lo), + lhs: indexed_ident, + rhs: expr, + })) + } else if let TokenKind::BinOpEq(op) = s.peek().kind { + s.advance(); + let op = expr::closed_bin_op(op); + let expr = expr::expr(s)?; + recovering_semi(s); + Ok(StmtKind::AssignOp(AssignOpStmt { + span: s.span(lo), + op, + lhs: indexed_ident, + rhs: expr, + })) + } else if s.peek().kind == TokenKind::Open(Delim::Paren) { + if !indexed_ident.indices.is_empty() { + s.push_error(Error::new(ErrorKind::Convert( + "Ident", + "IndexedIdent", + indexed_ident.span, + ))); + } + + let ident = indexed_ident.name; + + s.advance(); + let (args, _) = seq(s, expr::expr)?; + token(s, TokenKind::Close(Delim::Paren))?; + + let funcall = Expr { + span: s.span(lo), + kind: Box::new(ExprKind::FunctionCall(FunctionCall { + span: s.span(lo), + name: ident, + args: args.into_iter().map(Box::new).collect(), + })), + }; + + let expr = expr::expr_with_lhs(s, funcall)?; + + Ok(parse_gate_call_with_expr(s, expr)?) + } else { + let kind = if indexed_ident.indices.is_empty() { + ExprKind::Ident(indexed_ident.name) + } else { + if indexed_ident.indices.len() > 1 { + s.push_error(Error::new(ErrorKind::MultipleIndexOperators( + indexed_ident.span, + ))); + } + + ExprKind::IndexExpr(IndexExpr { + span: indexed_ident.span, + collection: Expr { + span: indexed_ident.name.span, + kind: Box::new(ExprKind::Ident(indexed_ident.name)), + }, + index: *indexed_ident.indices[0].clone(), + }) + }; + + let expr = Expr { + span: indexed_ident.span, + kind: Box::new(kind), + }; + + Ok(parse_gate_call_with_expr(s, expr)?) + } +} + #[allow(clippy::vec_box)] pub(super) fn parse_many(s: &mut ParserContext) -> Result>> { many(s, |s| { - recovering(s, default, &[TokenKind::Semicolon], parse) + recovering(s, default, &[TokenKind::Semicolon], parse_block_or_stmt) }) } +/// Grammar: `LBRACE statementOrScope* RBRACE`. pub(super) fn parse_block(s: &mut ParserContext) -> Result> { let lo = s.peek().span.lo; token(s, TokenKind::Open(Delim::Brace))?; @@ -246,6 +303,7 @@ fn default(span: Span) -> Box { }) } +/// Grammar: `AnnotationKeyword RemainingLineContent?`. pub fn parse_annotation(s: &mut ParserContext) -> Result> { let lo = s.peek().span.lo; s.expect(WordKinds::Annotation); @@ -297,6 +355,7 @@ pub fn parse_annotation(s: &mut ParserContext) -> Result> { })) } +/// Grammar: `INCLUDE StringLiteral SEMICOLON`. fn parse_include(s: &mut ParserContext) -> Result { let lo = s.peek().span.lo; token(s, TokenKind::Keyword(Keyword::Include))?; @@ -320,6 +379,7 @@ fn parse_include(s: &mut ParserContext) -> Result { ))) } +/// Grammar: `PRAGMA RemainingLineContent`. fn parse_pragma(s: &mut ParserContext) -> Result { let lo = s.peek().span.lo; s.expect(WordKinds::Pragma); @@ -367,6 +427,7 @@ fn parse_pragma(s: &mut ParserContext) -> Result { }) } +/// Grammar: `EXTERN Identifier LPAREN externArgumentList? RPAREN returnSignature? SEMICOLON`. fn parse_extern(s: &mut ParserContext) -> Result { let lo = s.peek().span.lo; token(s, TokenKind::Keyword(crate::keyword::Keyword::Extern))?; @@ -385,6 +446,7 @@ fn parse_extern(s: &mut ParserContext) -> Result { Ok(kind) } +/// Grammar: `DEF Identifier LPAREN argumentDefinitionList? RPAREN returnSignature? scope`. fn parse_def(s: &mut ParserContext) -> Result { let lo = s.peek().span.lo; token(s, TokenKind::Keyword(crate::keyword::Keyword::Def))?; @@ -404,6 +466,7 @@ fn parse_def(s: &mut ParserContext) -> Result { Ok(kind) } +/// Grammar: `scalarType | arrayReferenceType | CREG designator?`. fn extern_arg_def(s: &mut ParserContext) -> Result { let lo = s.peek().span.lo; @@ -430,6 +493,13 @@ fn extern_arg_def(s: &mut ParserContext) -> Result { Ok(kind) } +/// Grammar: +/// ```g4 +/// scalarType Identifier +/// | qubitType Identifier +/// | (CREG | QREG) Identifier designator? +/// | arrayReferenceType Identifier +/// ``` fn arg_def(s: &mut ParserContext) -> Result { let lo = s.peek().span.lo; @@ -483,6 +553,8 @@ fn arg_def(s: &mut ParserContext) -> Result { Ok(kind) } +/// Grammar: +/// `(READONLY | MUTABLE) ARRAY LBRACKET scalarType COMMA (expressionList | DIM EQUALS expression) RBRACKET`. fn array_reference_ty(s: &mut ParserContext) -> Result { let lo = s.peek().span.lo; @@ -519,6 +591,7 @@ fn array_reference_ty(s: &mut ParserContext) -> Result { }) } +/// Grammar: `ARROW scalarType`. fn return_sig(s: &mut ParserContext) -> Result { token(s, TokenKind::Arrow)?; scalar_type(s) @@ -549,6 +622,7 @@ fn gate_params(s: &mut ParserContext<'_>) -> Result> { Ok(params) } +/// Grammar: `RETURN (expression | measureExpression)? SEMICOLON`. fn parse_return(s: &mut ParserContext) -> Result { let lo = s.peek().span.lo; token(s, TokenKind::Keyword(crate::keyword::Keyword::Return))?; @@ -560,6 +634,7 @@ fn parse_return(s: &mut ParserContext) -> Result { })) } +/// Grammar: `qubitType Identifier SEMICOLON`. fn parse_quantum_decl(s: &mut ParserContext) -> Result { let lo = s.peek().span.lo; let size = qubit_type(s)?; @@ -573,12 +648,14 @@ fn parse_quantum_decl(s: &mut ParserContext) -> Result { })) } +/// Grammar: `QUBIT designator?`. fn qubit_type(s: &mut ParserContext<'_>) -> Result> { token(s, TokenKind::Keyword(crate::keyword::Keyword::Qubit))?; let size = opt(s, designator)?; Ok(size) } +/// Grammar: `(INPUT | OUTPUT) (scalarType | arrayType) Identifier SEMICOLON`. fn parse_io_decl(s: &mut ParserContext) -> Result { let lo = s.peek().span.lo; @@ -608,6 +685,7 @@ fn parse_io_decl(s: &mut ParserContext) -> Result { Ok(StmtKind::IODeclaration(decl)) } +/// Grammar `(scalarType | arrayType)`. pub fn scalar_or_array_type(s: &mut ParserContext) -> Result { if let Ok(v) = scalar_type(s) { return Ok(TypeDef::Scalar(v)); @@ -622,6 +700,7 @@ pub fn scalar_or_array_type(s: &mut ParserContext) -> Result { ))) } +/// Grammar: `(scalarType | arrayType) Identifier (EQUALS declarationExpression)? SEMICOLON`. fn parse_non_constant_classical_decl( s: &mut ParserContext, ty: TypeDef, @@ -645,6 +724,7 @@ fn parse_non_constant_classical_decl( Ok(StmtKind::ClassicalDecl(decl)) } +/// Grammar: `CONST scalarType Identifier EQUALS declarationExpression SEMICOLON`. fn parse_constant_classical_decl(s: &mut ParserContext) -> Result { let lo = s.peek().span.lo; token(s, TokenKind::Keyword(Keyword::Const))?; @@ -663,6 +743,8 @@ fn parse_constant_classical_decl(s: &mut ParserContext) -> Result { Ok(StmtKind::ConstDecl(decl)) } +/// The Spec and the grammar differ in the base type for arrays. We followed the Spec. +/// Grammar: `ARRAY LBRACKET arrayBaseType COMMA expressionList RBRACKET`. pub(super) fn array_type(s: &mut ParserContext) -> Result { let lo = s.peek().span.lo; token(s, TokenKind::Type(Type::Array))?; @@ -679,6 +761,17 @@ pub(super) fn array_type(s: &mut ParserContext) -> Result { }) } +/// The Spec for 3.0, main Spec, and the grammar differ in the base type for arrays. +/// We followed the main Spec. +/// Grammar: +/// | INT designator? +/// | UINT designator? +/// | FLOAT designator? +/// | ANGLE designator? +/// | BOOL +/// | DURATION +/// | COMPLEX (LBRACKET scalarType RBRACKET)? +/// Reference: . pub(super) fn array_base_type(s: &mut ParserContext) -> Result { if let Ok(v) = array_angle_type(s) { return Ok(v); @@ -709,6 +802,18 @@ pub(super) fn array_base_type(s: &mut ParserContext) -> Result Result { if let Ok(v) = scalar_bit_type(s) { return Ok(v); @@ -1007,6 +1112,7 @@ pub fn parse_switch_stmt(s: &mut ParserContext) -> Result { }) } +/// Grammar: `CASE expressionList scope`. fn case_stmt(s: &mut ParserContext) -> Result { let lo = s.peek().span.lo; token(s, TokenKind::Keyword(Keyword::Case))?; @@ -1025,19 +1131,30 @@ fn case_stmt(s: &mut ParserContext) -> Result { }) } +/// Grammar: `DEFAULT scope`. fn default_case_stmt(s: &mut ParserContext) -> Result { token(s, TokenKind::Keyword(Keyword::Default))?; parse_block(s).map(|block| *block) } -/// Parses a block or a statement. This is a helper function -/// to be used in loops and if stmts, in which their bodies -/// can be a block expr or a single statement. -fn parse_block_or_stmt(s: &mut ParserContext) -> Result> { +/// Grammar: `statement | scope`. +fn parse_block_or_stmt(s: &mut ParserContext) -> Result> { if let Some(block) = opt(s, parse_block)? { - Ok(block.stmts) + Ok(Box::new(Stmt { + span: block.span, + annotations: Default::default(), + kind: Box::new(StmtKind::Block(block)), + })) } else { - Ok(Box::new([parse(s)?])) + Ok(parse(s)?) + } +} + +fn into_stmt_list(stmt: Stmt) -> List { + if let StmtKind::Block(block) = *stmt.kind { + block.stmts + } else { + Box::new([Box::new(stmt)]) } } @@ -1050,9 +1167,9 @@ pub fn parse_if_stmt(s: &mut ParserContext) -> Result { let condition = expr::expr(s)?; recovering_token(s, TokenKind::Close(Delim::Paren)); - let if_block = parse_block_or_stmt(s)?; + let if_block = into_stmt_list(*parse_block_or_stmt(s)?); let else_block = if opt(s, |s| token(s, TokenKind::Keyword(Keyword::Else)))?.is_some() { - Some(parse_block_or_stmt(s)?) + Some(into_stmt_list(*parse_block_or_stmt(s)?)) } else { None }; @@ -1109,7 +1226,8 @@ fn for_loop_iterable_expr(s: &mut ParserContext) -> Result { } } -/// Grammar: `FOR scalarType Identifier IN (setExpression | LBRACKET rangeExpression RBRACKET | expression) body=statementOrScope`. +/// Grammar: +/// `FOR scalarType Identifier IN (setExpression | LBRACKET rangeExpression RBRACKET | expression) body=statementOrScope`. /// Reference: . pub fn parse_for_loop(s: &mut ParserContext) -> Result { let lo = s.peek().span.lo; @@ -1118,7 +1236,7 @@ pub fn parse_for_loop(s: &mut ParserContext) -> Result { let identifier = Identifier::Ident(Box::new(prim::ident(s)?)); token(s, TokenKind::Keyword(Keyword::In))?; let set_declaration = Box::new(for_loop_iterable_expr(s)?); - let block = parse_block_or_stmt(s)?; + let block = into_stmt_list(*parse_block_or_stmt(s)?); Ok(ForStmt { span: s.span(lo), @@ -1137,7 +1255,7 @@ pub fn parse_while_loop(s: &mut ParserContext) -> Result { token(s, TokenKind::Open(Delim::Paren))?; let while_condition = expr::expr(s)?; recovering_token(s, TokenKind::Close(Delim::Paren)); - let block = parse_block_or_stmt(s)?; + let block = into_stmt_list(*parse_block_or_stmt(s)?); Ok(WhileLoop { span: s.span(lo), @@ -1170,7 +1288,7 @@ fn parse_end_stmt(s: &mut ParserContext) -> Result { Ok(EndStmt { span: s.span(lo) }) } -/// Grammar: `(expression | assignExpr | AssignOpExpr) SEMICOLON`. +/// Grammar: `expression SEMICOLON`. fn parse_expression_stmt(s: &mut ParserContext, lhs: Option) -> Result { let expr = if let Some(lhs) = lhs { expr::expr_with_lhs(s, lhs)? @@ -1439,6 +1557,8 @@ fn reinterpret_index_expr( ))) } +/// Grammar: +/// `gateModifier* GPHASE (LPAREN expressionList? RPAREN)? designator? gateOperandList? SEMICOLON` fn parse_gphase( s: &mut ParserContext, lo: u32, diff --git a/compiler/qsc_qasm3/src/parser/stmt/tests.rs b/compiler/qsc_qasm3/src/parser/stmt/tests.rs index 5b6e25c336..1db174f83a 100644 --- a/compiler/qsc_qasm3/src/parser/stmt/tests.rs +++ b/compiler/qsc_qasm3/src/parser/stmt/tests.rs @@ -4,6 +4,7 @@ mod alias; mod annotation; mod barrier; +mod block; mod box_stmt; mod cal; mod cal_grammar; diff --git a/compiler/qsc_qasm3/src/parser/stmt/tests/block.rs b/compiler/qsc_qasm3/src/parser/stmt/tests/block.rs new file mode 100644 index 0000000000..de70376f6b --- /dev/null +++ b/compiler/qsc_qasm3/src/parser/stmt/tests/block.rs @@ -0,0 +1,44 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +use crate::parser::stmt::parse_block; +use crate::parser::tests::check; +use expect_test::expect; + +#[test] +fn nested_blocks() { + check( + parse_block, + " + { + { + int x = 1; + { + x = 2; + } + } + }", + &expect![[r#" + Block [5-106]: + Stmt [15-100]: + annotations: + kind: Block [15-100]: + Stmt [29-39]: + annotations: + kind: ClassicalDeclarationStmt [29-39]: + type: ScalarType [29-32]: IntType [29-32]: + size: + ident: Ident [33-34] "x" + init_expr: Expr [37-38]: Lit: Int(1) + Stmt [52-90]: + annotations: + kind: Block [52-90]: + Stmt [70-76]: + annotations: + kind: AssignStmt [70-76]: + lhs: IndexedIdent [70-71]: + name: Ident [70-71] "x" + indices: + rhs: Expr [74-75]: Lit: Int(2)"#]], + ); +} diff --git a/compiler/qsc_qasm3/src/parser/stmt/tests/invalid_stmts.rs b/compiler/qsc_qasm3/src/parser/stmt/tests/invalid_stmts.rs index db14c8a438..deff9b0e90 100644 --- a/compiler/qsc_qasm3/src/parser/stmt/tests/invalid_stmts.rs +++ b/compiler/qsc_qasm3/src/parser/stmt/tests/invalid_stmts.rs @@ -1,72 +1,11 @@ -use crate::parser::{stmt::parse, tests::check}; -use expect_test::expect; - -#[test] -fn assignment_in_if_condition() { - check( - parse, - "if (x = 2) { 3; }", - &expect![[r#" - Stmt [0-17]: - annotations: - kind: IfStmt [0-17]: - condition: Expr [4-5]: Ident [4-5] "x" - if_block: - Stmt [13-15]: - annotations: - kind: ExprStmt [13-15]: - expr: Expr [13-14]: Lit: Int(3) - else_block: - - [ - Error( - Token( - Close( - Paren, - ), - Eq, - Span { - lo: 6, - hi: 7, - }, - ), - ), - ]"#]], - ); -} - -#[test] -fn binary_op_assignment_in_if_condition() { - check( - parse, - "if (x += 2) { 3; }", - &expect![[r#" - Stmt [0-18]: - annotations: - kind: IfStmt [0-18]: - condition: Expr [4-5]: Ident [4-5] "x" - if_block: - Stmt [14-16]: - annotations: - kind: ExprStmt [14-16]: - expr: Expr [14-15]: Lit: Int(3) - else_block: - - [ - Error( - Token( - Close( - Paren, - ), - BinOpEq( - Plus, - ), - Span { - lo: 6, - hi: 8, - }, - ), - ), - ]"#]], - ); -} +mod branch; +mod cal; +mod constant; +mod decl; +mod gate_calls; +mod headers; +mod io; +mod loops; +mod measure; +mod switch; +mod tokens; diff --git a/compiler/qsc_qasm3/src/parser/stmt/tests/invalid_stmts/branch.rs b/compiler/qsc_qasm3/src/parser/stmt/tests/invalid_stmts/branch.rs new file mode 100644 index 0000000000..0229f5bbb4 --- /dev/null +++ b/compiler/qsc_qasm3/src/parser/stmt/tests/invalid_stmts/branch.rs @@ -0,0 +1,205 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +use crate::parser::{stmt::parse, tests::check}; +use expect_test::expect; + +#[test] +fn if_condition_missing_parens() { + check( + parse, + "if true 3;", + &expect![[r#" + Error( + Token( + Open( + Paren, + ), + Keyword( + True, + ), + Span { + lo: 3, + hi: 7, + }, + ), + ) + "#]], + ); +} + +#[test] +fn decl_in_if_condition() { + check( + parse, + "if (int[8] myvar = 1) { x $0; }", + &expect![[r#" + Error( + Token( + Open( + Paren, + ), + Identifier, + Span { + lo: 11, + hi: 16, + }, + ), + ) + "#]], + ); +} + +#[test] +fn assignment_in_if_condition() { + check( + parse, + "if (x = 2) { 3; }", + &expect![[r#" + Stmt [0-17]: + annotations: + kind: IfStmt [0-17]: + condition: Expr [4-5]: Ident [4-5] "x" + if_block: + Stmt [13-15]: + annotations: + kind: ExprStmt [13-15]: + expr: Expr [13-14]: Lit: Int(3) + else_block: + + [ + Error( + Token( + Close( + Paren, + ), + Eq, + Span { + lo: 6, + hi: 7, + }, + ), + ), + ]"#]], + ); +} + +#[test] +fn binary_op_assignment_in_if_condition() { + check( + parse, + "if (x += 2) { 3; }", + &expect![[r#" + Stmt [0-18]: + annotations: + kind: IfStmt [0-18]: + condition: Expr [4-5]: Ident [4-5] "x" + if_block: + Stmt [14-16]: + annotations: + kind: ExprStmt [14-16]: + expr: Expr [14-15]: Lit: Int(3) + else_block: + + [ + Error( + Token( + Close( + Paren, + ), + BinOpEq( + Plus, + ), + Span { + lo: 6, + hi: 8, + }, + ), + ), + ]"#]], + ); +} + +#[test] +fn empty_if_block() { + check( + parse, + "if (true);", + &expect![[r#" + Stmt [0-10]: + annotations: + kind: IfStmt [0-10]: + condition: Expr [4-8]: Lit: Bool(true) + if_block: + Stmt [9-10]: + annotations: + kind: Empty + else_block: "#]], + ); +} + +#[test] +fn empty_if_block_else() { + check( + parse, + "if (true) else x $0;", + &expect![[r#" + Error( + Rule( + "statement", + Keyword( + Else, + ), + Span { + lo: 10, + hi: 14, + }, + ), + ) + "#]], + ); +} + +#[test] +fn empty_if_block_else_with_condition() { + check( + parse, + "if (true) else (false) x $0;", + &expect![[r#" + Error( + Rule( + "statement", + Keyword( + Else, + ), + Span { + lo: 10, + hi: 14, + }, + ), + ) + "#]], + ); +} + +#[test] +fn reset_in_if_condition() { + check( + parse, + "if (reset $0) { x $1; }", + &expect![[r#" + Error( + Rule( + "expression", + Keyword( + Reset, + ), + Span { + lo: 4, + hi: 9, + }, + ), + ) + "#]], + ); +} diff --git a/compiler/qsc_qasm3/src/parser/stmt/tests/invalid_stmts/cal.rs b/compiler/qsc_qasm3/src/parser/stmt/tests/invalid_stmts/cal.rs new file mode 100644 index 0000000000..1d50f9d05d --- /dev/null +++ b/compiler/qsc_qasm3/src/parser/stmt/tests/invalid_stmts/cal.rs @@ -0,0 +1,69 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +use crate::parser::{stmt::parse, tests::check}; +use expect_test::expect; + +#[test] +fn multiple_defcalgrammar_in_same_stmt() { + check( + parse, + r#"defcalgrammar "openpulse" defcalgrammar "openpulse";"#, + &expect![[r#" + Stmt [0-25]: + annotations: + kind: CalibrationGrammarStmt [0-25]: + name: openpulse + + [ + Error( + Token( + Semicolon, + Keyword( + DefCalGrammar, + ), + Span { + lo: 26, + hi: 39, + }, + ), + ), + ]"#]], + ); +} + +#[test] +fn defcalgrammar_with_wrong_literal_kind() { + check( + parse, + "defcalgrammar 3;", + &expect![[r#" + Error( + Rule( + "string literal", + Literal( + Integer( + Decimal, + ), + ), + Span { + lo: 14, + hi: 15, + }, + ), + ) + "#]], + ); +} + +#[test] +fn defcal_bad_signature() { + check( + parse, + "defcal x $0 -> int[8] -> int[8] {}", + &expect![[r#" + Stmt [0-34]: + annotations: + kind: DefCalStmt [0-34]"#]], + ); +} diff --git a/compiler/qsc_qasm3/src/parser/stmt/tests/invalid_stmts/constant.rs b/compiler/qsc_qasm3/src/parser/stmt/tests/invalid_stmts/constant.rs new file mode 100644 index 0000000000..571e8341ba --- /dev/null +++ b/compiler/qsc_qasm3/src/parser/stmt/tests/invalid_stmts/constant.rs @@ -0,0 +1,153 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +use crate::parser::{stmt::parse, tests::check}; +use expect_test::expect; + +#[test] +fn const_decl_missing_type_and_init() { + check( + parse, + "const myvar;", + &expect![[r#" + Error( + Rule( + "scalar or array type", + Identifier, + Span { + lo: 6, + hi: 11, + }, + ), + ) + "#]], + ); +} + +#[test] +fn const_decl_eq_missing_type_and_init() { + check( + parse, + "const myvar = ;", + &expect![[r#" + Error( + Rule( + "scalar or array type", + Identifier, + Span { + lo: 6, + hi: 11, + }, + ), + ) + "#]], + ); +} + +#[test] +fn const_decl_missing_type() { + check( + parse, + "const myvar = 8.0;", + &expect![[r#" + Error( + Rule( + "scalar or array type", + Identifier, + Span { + lo: 6, + hi: 11, + }, + ), + ) + "#]], + ); +} + +#[test] +fn invalid_input() { + check( + parse, + "input const myvar = 8;", + &expect![[r#" + Error( + Rule( + "scalar or array type", + Keyword( + Const, + ), + Span { + lo: 6, + hi: 11, + }, + ), + ) + "#]], + ); +} + +#[test] +fn invalid_output() { + check( + parse, + "output const myvar = 8;", + &expect![[r#" + Error( + Rule( + "scalar or array type", + Keyword( + Const, + ), + Span { + lo: 7, + hi: 12, + }, + ), + ) + "#]], + ); +} + +#[test] +fn invalid_const_input() { + check( + parse, + "const input myvar = 8;", + &expect![[r#" + Error( + Rule( + "scalar or array type", + Keyword( + Input, + ), + Span { + lo: 6, + hi: 11, + }, + ), + ) + "#]], + ); +} + +#[test] +fn invalid_const_output() { + check( + parse, + "const output myvar = 8;", + &expect![[r#" + Error( + Rule( + "scalar or array type", + Keyword( + Output, + ), + Span { + lo: 6, + hi: 12, + }, + ), + ) + "#]], + ); +} diff --git a/compiler/qsc_qasm3/src/parser/stmt/tests/invalid_stmts/decl.rs b/compiler/qsc_qasm3/src/parser/stmt/tests/invalid_stmts/decl.rs new file mode 100644 index 0000000000..a653c561ec --- /dev/null +++ b/compiler/qsc_qasm3/src/parser/stmt/tests/invalid_stmts/decl.rs @@ -0,0 +1,994 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +use crate::parser::{stmt::parse, tests::check}; +use expect_test::expect; + +#[test] +fn missing_ident() { + check( + parse, + "float;", + &expect![[r#" + Error( + Token( + Open( + Paren, + ), + Semicolon, + Span { + lo: 5, + hi: 6, + }, + ), + ) + "#]], + ); + check( + parse, + "uint[8];", + &expect![[r#" + Error( + Token( + Open( + Paren, + ), + Semicolon, + Span { + lo: 7, + hi: 8, + }, + ), + ) + "#]], + ); + check( + parse, + "qreg[4];", + &expect![[r#" + Error( + Rule( + "identifier", + Open( + Bracket, + ), + Span { + lo: 4, + hi: 5, + }, + ), + ) + "#]], + ); + check( + parse, + "creg[4];", + &expect![[r#" + Error( + Rule( + "identifier", + Open( + Bracket, + ), + Span { + lo: 4, + hi: 5, + }, + ), + ) + "#]], + ); + check( + parse, + "complex[float[32]];", + &expect![[r#" + Error( + Token( + Open( + Paren, + ), + Semicolon, + Span { + lo: 18, + hi: 19, + }, + ), + ) + "#]], + ); +} + +#[test] +#[allow(clippy::too_many_lines)] +fn incorrect_designators() { + check( + parse, + "int[8, 8] myvar;", + &expect![[r#" + Stmt [0-16]: + annotations: + kind: ClassicalDeclarationStmt [0-16]: + type: ScalarType [0-9]: IntType [0-9]: + size: Expr [4-5]: Lit: Int(8) + ident: Ident [10-15] "myvar" + init_expr: + + [ + Error( + Token( + Close( + Bracket, + ), + Comma, + Span { + lo: 5, + hi: 6, + }, + ), + ), + ]"#]], + ); + check( + parse, + "uint[8, 8] myvar;", + &expect![[r#" + Stmt [0-17]: + annotations: + kind: ClassicalDeclarationStmt [0-17]: + type: ScalarType [0-10]: UIntType [0-10]: + size: Expr [5-6]: Lit: Int(8) + ident: Ident [11-16] "myvar" + init_expr: + + [ + Error( + Token( + Close( + Bracket, + ), + Comma, + Span { + lo: 6, + hi: 7, + }, + ), + ), + ]"#]], + ); + check( + parse, + "float[8, 8] myvar;", + &expect![[r#" + Stmt [0-18]: + annotations: + kind: ClassicalDeclarationStmt [0-18]: + type: ScalarType [0-11]: FloatType [0-11]: + size: Expr [6-7]: Lit: Int(8) + ident: Ident [12-17] "myvar" + init_expr: + + [ + Error( + Token( + Close( + Bracket, + ), + Comma, + Span { + lo: 7, + hi: 8, + }, + ), + ), + ]"#]], + ); + check( + parse, + "angle[8, 8] myvar;", + &expect![[r#" + Stmt [0-18]: + annotations: + kind: ClassicalDeclarationStmt [0-18]: + type: ScalarType [0-11]: AngleType [0-11]: + size: Expr [6-7]: Lit: Int(8) + ident: Ident [12-17] "myvar" + init_expr: + + [ + Error( + Token( + Close( + Bracket, + ), + Comma, + Span { + lo: 7, + hi: 8, + }, + ), + ), + ]"#]], + ); + check( + parse, + "bool[4] myvar;", + &expect![[r#" + Error( + Token( + Open( + Paren, + ), + Open( + Bracket, + ), + Span { + lo: 4, + hi: 5, + }, + ), + ) + "#]], + ); + check( + parse, + "bool[4, 4] myvar;", + &expect![[r#" + Error( + Token( + Open( + Paren, + ), + Open( + Bracket, + ), + Span { + lo: 4, + hi: 5, + }, + ), + ) + "#]], + ); + check( + parse, + "bit[4, 4] myvar;", + &expect![[r#" + Stmt [0-16]: + annotations: + kind: ClassicalDeclarationStmt [0-16]: + type: ScalarType [0-9]: BitType [0-9]: + size: Expr [4-5]: Lit: Int(4) + ident: Ident [10-15] "myvar" + init_expr: + + [ + Error( + Token( + Close( + Bracket, + ), + Comma, + Span { + lo: 5, + hi: 6, + }, + ), + ), + ]"#]], + ); + check( + parse, + "creg[2] myvar;", + &expect![[r#" + Error( + Rule( + "identifier", + Open( + Bracket, + ), + Span { + lo: 4, + hi: 5, + }, + ), + ) + "#]], + ); + check( + parse, + "creg[2, 2] myvar;", + &expect![[r#" + Error( + Rule( + "identifier", + Open( + Bracket, + ), + Span { + lo: 4, + hi: 5, + }, + ), + ) + "#]], + ); + check( + parse, + "qreg[2] myvar;", + &expect![[r#" + Error( + Rule( + "identifier", + Open( + Bracket, + ), + Span { + lo: 4, + hi: 5, + }, + ), + ) + "#]], + ); + check( + parse, + "qreg[2, 2] myvar;", + &expect![[r#" + Error( + Rule( + "identifier", + Open( + Bracket, + ), + Span { + lo: 4, + hi: 5, + }, + ), + ) + "#]], + ); + check( + parse, + "complex[32] myvar;", + &expect![[r#" + Error( + Rule( + "scalar or array type", + Literal( + Integer( + Decimal, + ), + ), + Span { + lo: 8, + hi: 10, + }, + ), + ) + "#]], + ); + check( + parse, + "complex[mytype] myvar;", + &expect![[r#" + Error( + Rule( + "scalar or array type", + Identifier, + Span { + lo: 8, + hi: 14, + }, + ), + ) + "#]], + ); + check( + parse, + "complex[float[32], float[32]] myvar;", + &expect![[r#" + Error( + Rule( + "scalar or array type", + Comma, + Span { + lo: 17, + hi: 18, + }, + ), + ) + "#]], + ); + check( + parse, + "complex[qreg] myvar;", + &expect![[r#" + Error( + Rule( + "scalar or array type", + Keyword( + QReg, + ), + Span { + lo: 8, + hi: 12, + }, + ), + ) + "#]], + ); + check( + parse, + "complex[creg] myvar;", + &expect![[r#" + Error( + Rule( + "scalar or array type", + Keyword( + CReg, + ), + Span { + lo: 8, + hi: 12, + }, + ), + ) + "#]], + ); + check( + parse, + "complex[qreg[8]] myvar;", + &expect![[r#" + Error( + Rule( + "scalar or array type", + Keyword( + QReg, + ), + Span { + lo: 8, + hi: 12, + }, + ), + ) + "#]], + ); + check( + parse, + "complex[creg[8]] myvar;", + &expect![[r#" + Error( + Rule( + "scalar or array type", + Keyword( + CReg, + ), + Span { + lo: 8, + hi: 12, + }, + ), + ) + "#]], + ); +} + +#[test] +fn bad_array_specifiers() { + check( + parse, + "array myvar;", + &expect![[r#" + Error( + Rule( + "scalar or array type", + Identifier, + Span { + lo: 6, + hi: 11, + }, + ), + ) + "#]], + ); + check( + parse, + "array[8] myvar;", + &expect![[r#" + Error( + Rule( + "scalar or array type", + Literal( + Integer( + Decimal, + ), + ), + Span { + lo: 6, + hi: 7, + }, + ), + ) + "#]], + ); + check( + parse, + "array[not_a_type, 4] myvar;", + &expect![[r#" + Error( + Rule( + "scalar or array type", + Identifier, + Span { + lo: 6, + hi: 16, + }, + ), + ) + "#]], + ); + check( + parse, + "array[int[8], int[8], 2] myvar;", + &expect![[r#" + Error( + Rule( + "scalar or array type", + Comma, + Span { + lo: 20, + hi: 21, + }, + ), + ) + "#]], + ); +} + +#[test] +fn invalid_identifiers() { + check( + parse, + "int[8] int;", + &expect![[r#" + Error( + Token( + Open( + Paren, + ), + Type( + Int, + ), + Span { + lo: 7, + hi: 10, + }, + ), + ) + "#]], + ); + check( + parse, + "int[8] def;", + &expect![[r#" + Error( + Token( + Open( + Paren, + ), + Keyword( + Def, + ), + Span { + lo: 7, + hi: 10, + }, + ), + ) + "#]], + ); + check( + parse, + "int[8] 0;", + &expect![[r#" + Error( + Token( + Open( + Paren, + ), + Literal( + Integer( + Decimal, + ), + ), + Span { + lo: 7, + hi: 8, + }, + ), + ) + "#]], + ); + check( + parse, + "int[8] input;", + &expect![[r#" + Error( + Token( + Open( + Paren, + ), + Keyword( + Input, + ), + Span { + lo: 7, + hi: 12, + }, + ), + ) + "#]], + ); +} + +#[test] +fn bad_assignments() { + check( + parse, + "int[8] myvar = end;", + &expect![[r#" + Error( + Token( + Open( + Brace, + ), + Keyword( + End, + ), + Span { + lo: 15, + hi: 18, + }, + ), + ) + "#]], + ); + check( + parse, + "int[8] myvar =;", + &expect![[r#" + Error( + Token( + Open( + Brace, + ), + Semicolon, + Span { + lo: 14, + hi: 15, + }, + ), + ) + "#]], + ); + check( + parse, + "float[32] myvar_f = int[32] myvar_i = 2;", + &expect![[r#" + Error( + Token( + Open( + Paren, + ), + Identifier, + Span { + lo: 28, + hi: 35, + }, + ), + ) + "#]], + ); +} + +#[test] +fn array_initialiser_uses_braces() { + check( + parse, + "array[uint[8], 4] myvar = [4, 5, 6, 7];", + &expect![[r#" + Error( + Token( + Open( + Brace, + ), + Open( + Bracket, + ), + Span { + lo: 26, + hi: 27, + }, + ), + ) + "#]], + ); +} + +#[test] +fn cant_use_arithmetic_on_the_entire_initialiser() { + check( + parse, + "array[uint[8], 4] myvar = 2 * {1, 2, 3, 4};", + &expect![[r#" + Error( + Rule( + "expression", + Open( + Brace, + ), + Span { + lo: 30, + hi: 31, + }, + ), + ) + "#]], + ); +} + +#[test] +fn backed_arrays_cant_use_dim() { + check( + parse, + "array[uint[8], #dim=2] myvar;", + &expect![[r#" + Error( + Rule( + "scalar or array type", + Dim, + Span { + lo: 15, + hi: 19, + }, + ), + ) + "#]], + ); +} + +#[test] +fn cant_have_more_than_one_type_specification() { + check( + parse, + "array[int[8], int[8]] myvar;", + &expect![[r#" + Error( + Rule( + "scalar or array type", + Close( + Bracket, + ), + Span { + lo: 20, + hi: 21, + }, + ), + ) + "#]], + ); +} + +#[test] +#[allow(clippy::too_many_lines)] +fn incorrect_orders() { + check( + parse, + "myvar: int[8];", + &expect![[r#" + Stmt [0-5]: + annotations: + kind: ExprStmt [0-5]: + expr: Expr [0-5]: Ident [0-5] "myvar" + + [ + Error( + Token( + Semicolon, + Colon, + Span { + lo: 5, + hi: 6, + }, + ), + ), + ]"#]], + ); + check( + parse, + "myvar int[8];", + &expect![[r#" + Stmt [0-5]: + annotations: + kind: ExprStmt [0-5]: + expr: Expr [0-5]: Ident [0-5] "myvar" + + [ + Error( + Token( + Semicolon, + Type( + Int, + ), + Span { + lo: 6, + hi: 9, + }, + ), + ), + ]"#]], + ); + check( + parse, + "int myvar[8];", + &expect![[r#" + Stmt [0-9]: + annotations: + kind: ClassicalDeclarationStmt [0-9]: + type: ScalarType [0-3]: IntType [0-3]: + size: + ident: Ident [4-9] "myvar" + init_expr: + + [ + Error( + Token( + Semicolon, + Open( + Bracket, + ), + Span { + lo: 9, + hi: 10, + }, + ), + ), + ]"#]], + ); + check( + parse, + "uint myvar[8];", + &expect![[r#" + Stmt [0-10]: + annotations: + kind: ClassicalDeclarationStmt [0-10]: + type: ScalarType [0-4]: UIntType [0-4]: + size: + ident: Ident [5-10] "myvar" + init_expr: + + [ + Error( + Token( + Semicolon, + Open( + Bracket, + ), + Span { + lo: 10, + hi: 11, + }, + ), + ), + ]"#]], + ); + check( + parse, + "float myvar[32];", + &expect![[r#" + Stmt [0-11]: + annotations: + kind: ClassicalDeclarationStmt [0-11]: + type: ScalarType [0-5]: FloatType [0-5]: + size: + ident: Ident [6-11] "myvar" + init_expr: + + [ + Error( + Token( + Semicolon, + Open( + Bracket, + ), + Span { + lo: 11, + hi: 12, + }, + ), + ), + ]"#]], + ); +} + +#[test] +fn compound_assigments() { + check( + parse, + "int[8] myvar1, myvar2;", + &expect![[r#" + Stmt [0-13]: + annotations: + kind: ClassicalDeclarationStmt [0-13]: + type: ScalarType [0-6]: IntType [0-6]: + size: Expr [4-5]: Lit: Int(8) + ident: Ident [7-13] "myvar1" + init_expr: + + [ + Error( + Token( + Semicolon, + Comma, + Span { + lo: 13, + hi: 14, + }, + ), + ), + ]"#]], + ); + check( + parse, + "int[8] myvari, float[32] myvarf;", + &expect![[r#" + Stmt [0-13]: + annotations: + kind: ClassicalDeclarationStmt [0-13]: + type: ScalarType [0-6]: IntType [0-6]: + size: Expr [4-5]: Lit: Int(8) + ident: Ident [7-13] "myvari" + init_expr: + + [ + Error( + Token( + Semicolon, + Comma, + Span { + lo: 13, + hi: 14, + }, + ), + ), + ]"#]], + ); + check( + parse, + "int[8] myvari float[32] myvarf;", + &expect![[r#" + Stmt [0-13]: + annotations: + kind: ClassicalDeclarationStmt [0-13]: + type: ScalarType [0-6]: IntType [0-6]: + size: Expr [4-5]: Lit: Int(8) + ident: Ident [7-13] "myvari" + init_expr: + + [ + Error( + Token( + Semicolon, + Type( + Float, + ), + Span { + lo: 14, + hi: 19, + }, + ), + ), + ]"#]], + ); +} diff --git a/compiler/qsc_qasm3/src/parser/stmt/tests/invalid_stmts/gate_calls.rs b/compiler/qsc_qasm3/src/parser/stmt/tests/invalid_stmts/gate_calls.rs new file mode 100644 index 0000000000..3268d1ec75 --- /dev/null +++ b/compiler/qsc_qasm3/src/parser/stmt/tests/invalid_stmts/gate_calls.rs @@ -0,0 +1,213 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +use crate::parser::{stmt::parse, tests::check}; +use expect_test::expect; + +#[test] +fn u_gate_with_two_args() { + check( + parse, + "U (1)(2) $0;", + &expect![[r#" + Error( + Convert( + "identifier", + "", + Span { + lo: 0, + hi: 5, + }, + ), + ) + "#]], + ); +} + +#[test] +fn invalid_modifier() { + check( + parse, + "notmodifier @ x $0;", + &expect![[r#" + Stmt [0-11]: + annotations: + kind: ExprStmt [0-11]: + expr: Expr [0-11]: Ident [0-11] "notmodifier" + + [ + Error( + Token( + Semicolon, + At, + Span { + lo: 12, + hi: 13, + }, + ), + ), + ]"#]], + ); +} + +#[test] +fn pow_without_arg() { + check( + parse, + "pow @ x $0;", + &expect![[r#" + Error( + Token( + Open( + Paren, + ), + At, + Span { + lo: 4, + hi: 5, + }, + ), + ) + "#]], + ); +} + +#[test] +fn pow_with_two_args() { + check( + parse, + "pow(2, 3) @ x $0;", + &expect![[r#" + Stmt [0-17]: + annotations: + kind: GateCall [0-17]: + modifiers: + QuantumGateModifier [0-11]: Pow Expr [4-5]: Lit: Int(2) + name: Ident [12-13] "x" + args: + duration: + qubits: + HardwareQubit [14-16]: 0 + + [ + Error( + Token( + Close( + Paren, + ), + Comma, + Span { + lo: 5, + hi: 6, + }, + ), + ), + ]"#]], + ); +} + +#[test] +fn ctrl_with_two_args() { + check( + parse, + "ctrl(2, 3) @ x $0, $1;", + &expect![[r#" + Stmt [0-22]: + annotations: + kind: GateCall [0-22]: + modifiers: + QuantumGateModifier [0-12]: Ctrl Some(Expr { span: Span { lo: 5, hi: 6 }, kind: Lit(Lit { span: Span { lo: 5, hi: 6 }, kind: Int(2) }) }) + name: Ident [13-14] "x" + args: + duration: + qubits: + HardwareQubit [15-17]: 0 + HardwareQubit [19-21]: 1 + + [ + Error( + Token( + Close( + Paren, + ), + Comma, + Span { + lo: 6, + hi: 7, + }, + ), + ), + ]"#]], + ); +} + +#[test] +fn negctrl_with_two_args() { + check( + parse, + "negctrl(2, 3) @ x $0, $1;", + &expect![[r#" + Stmt [0-25]: + annotations: + kind: GateCall [0-25]: + modifiers: + QuantumGateModifier [0-15]: NegCtrl Some(Expr { span: Span { lo: 8, hi: 9 }, kind: Lit(Lit { span: Span { lo: 8, hi: 9 }, kind: Int(2) }) }) + name: Ident [16-17] "x" + args: + duration: + qubits: + HardwareQubit [18-20]: 0 + HardwareQubit [22-24]: 1 + + [ + Error( + Token( + Close( + Paren, + ), + Comma, + Span { + lo: 9, + hi: 10, + }, + ), + ), + ]"#]], + ); +} + +#[test] +fn inv_with_arg() { + check( + parse, + "inv(1) @ ctrl @ x $0, $1;", + &expect![[r#" + Stmt [0-25]: + annotations: + kind: GateCall [0-25]: + modifiers: + QuantumGateModifier [0-8]: Inv + QuantumGateModifier [9-15]: Ctrl None + name: Ident [16-17] "x" + args: + duration: + qubits: + HardwareQubit [18-20]: 0 + HardwareQubit [22-24]: 1 + + [ + Error( + Token( + At, + Open( + Paren, + ), + Span { + lo: 3, + hi: 4, + }, + ), + ), + ]"#]], + ); +} diff --git a/compiler/qsc_qasm3/src/parser/stmt/tests/invalid_stmts/headers.rs b/compiler/qsc_qasm3/src/parser/stmt/tests/invalid_stmts/headers.rs new file mode 100644 index 0000000000..8fa9c878e0 --- /dev/null +++ b/compiler/qsc_qasm3/src/parser/stmt/tests/invalid_stmts/headers.rs @@ -0,0 +1,231 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +use crate::parser::{stmt::parse, tests::check}; +use expect_test::expect; + +#[test] +fn invalid_version_type() { + check( + parse, + "OPENQASM int;", + &expect![[r#" + Error( + Rule( + "statement", + Keyword( + OpenQASM, + ), + Span { + lo: 0, + hi: 8, + }, + ), + ) + "#]], + ); +} + +#[test] +fn invalid_version_literal() { + check( + parse, + "OPENQASM 'hello, world';", + &expect![[r#" + Error( + Rule( + "statement", + Keyword( + OpenQASM, + ), + Span { + lo: 0, + hi: 8, + }, + ), + ) + "#]], + ); +} + +#[test] +fn invalid_version_missing_dot() { + check( + parse, + "OPENQASM 3 3;", + &expect![[r#" + Error( + Rule( + "statement", + Keyword( + OpenQASM, + ), + Span { + lo: 0, + hi: 8, + }, + ), + ) + "#]], + ); +} + +#[test] +fn invalid_version() { + check( + parse, + "OPENQASM 3.x;", + &expect![[r#" + Error( + Rule( + "statement", + Keyword( + OpenQASM, + ), + Span { + lo: 0, + hi: 8, + }, + ), + ) + "#]], + ); +} + +#[test] +fn include_int() { + check( + parse, + "include 3;", + &expect![[r#" + Error( + Rule( + "string literal", + Literal( + Integer( + Decimal, + ), + ), + Span { + lo: 8, + hi: 9, + }, + ), + ) + "#]], + ); +} + +#[test] +fn include_include() { + check( + parse, + "include include;", + &expect![[r#" + Error( + Rule( + "string literal", + Keyword( + Include, + ), + Span { + lo: 8, + hi: 15, + }, + ), + ) + + [ + Error( + Token( + Semicolon, + Keyword( + Include, + ), + Span { + lo: 8, + hi: 15, + }, + ), + ), + ]"#]], + ); +} + +#[test] +fn include_def() { + check( + parse, + "include def;", + &expect![[r#" + Error( + Rule( + "string literal", + Keyword( + Def, + ), + Span { + lo: 8, + hi: 11, + }, + ), + ) + + [ + Error( + Token( + Semicolon, + Keyword( + Def, + ), + Span { + lo: 8, + hi: 11, + }, + ), + ), + ]"#]], + ); +} + +#[test] +fn unclosed_string() { + check( + parse, + r#"include "hello;"#, + &expect![[r#" + Error( + Rule( + "string literal", + Eof, + Span { + lo: 15, + hi: 15, + }, + ), + ) + + [ + Error( + Lex( + UnterminatedString( + Span { + lo: 8, + hi: 8, + }, + ), + ), + ), + Error( + Token( + Semicolon, + Eof, + Span { + lo: 15, + hi: 15, + }, + ), + ), + ]"#]], + ); +} diff --git a/compiler/qsc_qasm3/src/parser/stmt/tests/invalid_stmts/io.rs b/compiler/qsc_qasm3/src/parser/stmt/tests/invalid_stmts/io.rs new file mode 100644 index 0000000000..a6ed43817a --- /dev/null +++ b/compiler/qsc_qasm3/src/parser/stmt/tests/invalid_stmts/io.rs @@ -0,0 +1,187 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +use crate::parser::{stmt::parse, tests::check}; +use expect_test::expect; + +#[test] +fn input_missing_ident() { + check( + parse, + "input int[8];", + &expect![[r#" + Error( + Rule( + "identifier", + Semicolon, + Span { + lo: 12, + hi: 13, + }, + ), + ) + "#]], + ); +} + +#[test] +fn output_missing_ident() { + check( + parse, + "output int[8];", + &expect![[r#" + Error( + Rule( + "identifier", + Semicolon, + Span { + lo: 13, + hi: 14, + }, + ), + ) + "#]], + ); +} + +#[test] +fn input_qreg_missing_ident() { + check( + parse, + "input qreg myvar[4];", + &expect![[r#" + Error( + Rule( + "scalar or array type", + Keyword( + QReg, + ), + Span { + lo: 6, + hi: 10, + }, + ), + ) + "#]], + ); +} + +#[test] +fn output_qreg_missing_ident() { + check( + parse, + "output qreg myvar[4];", + &expect![[r#" + Error( + Rule( + "scalar or array type", + Keyword( + QReg, + ), + Span { + lo: 7, + hi: 11, + }, + ), + ) + "#]], + ); +} + +#[test] +fn initialized_input() { + check( + parse, + "input int[8] myvar = 32;", + &expect![[r#" + Stmt [0-18]: + annotations: + kind: IODeclaration [0-18]: + io_keyword: input + type: ScalarType [6-12]: IntType [6-12]: + size: Expr [10-11]: Lit: Int(8) + ident: Ident [13-18] "myvar" + + [ + Error( + Token( + Semicolon, + Eq, + Span { + lo: 19, + hi: 20, + }, + ), + ), + ]"#]], + ); +} + +#[test] +fn initialized_output() { + check( + parse, + "output int[8] myvar = 32;", + &expect![[r#" + Stmt [0-19]: + annotations: + kind: IODeclaration [0-19]: + io_keyword: output + type: ScalarType [7-13]: IntType [7-13]: + size: Expr [11-12]: Lit: Int(8) + ident: Ident [14-19] "myvar" + + [ + Error( + Token( + Semicolon, + Eq, + Span { + lo: 20, + hi: 21, + }, + ), + ), + ]"#]], + ); +} + +#[test] +fn input_missing_type() { + check( + parse, + "input myvar;", + &expect![[r#" + Error( + Rule( + "scalar or array type", + Identifier, + Span { + lo: 6, + hi: 11, + }, + ), + ) + "#]], + ); +} + +#[test] +fn output_missing_type() { + check( + parse, + "output myvar;", + &expect![[r#" + Error( + Rule( + "scalar or array type", + Identifier, + Span { + lo: 7, + hi: 12, + }, + ), + ) + "#]], + ); +} diff --git a/compiler/qsc_qasm3/src/parser/stmt/tests/invalid_stmts/loops.rs b/compiler/qsc_qasm3/src/parser/stmt/tests/invalid_stmts/loops.rs new file mode 100644 index 0000000000..5258d77869 --- /dev/null +++ b/compiler/qsc_qasm3/src/parser/stmt/tests/invalid_stmts/loops.rs @@ -0,0 +1,289 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +use crate::parser::{stmt::parse, tests::check}; +use expect_test::expect; + +#[test] +fn for_missing_var_type() { + check( + parse, + "for myvar in { 1, 2, 3 };", + &expect![[r#" + Error( + Rule( + "scalar type", + Identifier, + Span { + lo: 4, + hi: 9, + }, + ), + ) + "#]], + ); +} + +#[test] +fn for_multiple_vars() { + check( + parse, + "for myvar1, myvar2 in { 1, 2, 3 } { x $0; }", + &expect![[r#" + Error( + Rule( + "scalar type", + Identifier, + Span { + lo: 4, + hi: 10, + }, + ), + ) + "#]], + ); +} + +#[test] +fn for_missing_var_type_and_invalid_collection() { + check( + parse, + "for myvar in { x $0; } { x $0; }", + &expect![[r#" + Error( + Rule( + "scalar type", + Identifier, + Span { + lo: 4, + hi: 9, + }, + ), + ) + "#]], + ); +} + +#[test] +fn for_missing_var_type_and_keyword_in_collection() { + check( + parse, + "for myvar in for { x $0; }", + &expect![[r#" + Error( + Rule( + "scalar type", + Identifier, + Span { + lo: 4, + hi: 9, + }, + ), + ) + "#]], + ); +} + +#[test] +fn for_bad_syntax() { + check( + parse, + "for myvar { x $0; }", + &expect![[r#" + Error( + Rule( + "scalar type", + Identifier, + Span { + lo: 4, + hi: 9, + }, + ), + ) + "#]], + ); +} + +#[test] +fn for_with_while_syntax() { + check( + parse, + "for (true) { x $0; }", + &expect![[r#" + Error( + Rule( + "scalar type", + Open( + Paren, + ), + Span { + lo: 4, + hi: 5, + }, + ), + ) + "#]], + ); +} + +#[test] +fn for_missing_var_and_collection() { + check( + parse, + "for { x $0; }", + &expect![[r#" + Error( + Rule( + "scalar type", + Open( + Brace, + ), + Span { + lo: 4, + hi: 5, + }, + ), + ) + "#]], + ); +} + +#[test] +fn for_invalid_var_name() { + check( + parse, + "for for in { 1, 2, 3 } { x $0; }", + &expect![[r#" + Error( + Rule( + "scalar type", + Keyword( + For, + ), + Span { + lo: 4, + hi: 7, + }, + ), + ) + "#]], + ); +} + +#[test] +fn for_missing_var() { + check( + parse, + "for in { 1, 2, 3 } { x $0; }", + &expect![[r#" + Error( + Rule( + "scalar type", + Keyword( + In, + ), + Span { + lo: 4, + hi: 6, + }, + ), + ) + "#]], + ); +} + +#[test] +fn while_missing_parens() { + check( + parse, + "while true { x $0; }", + &expect![[r#" + Error( + Token( + Open( + Paren, + ), + Keyword( + True, + ), + Span { + lo: 6, + hi: 10, + }, + ), + ) + "#]], + ); +} + +#[test] +fn while_multi_condition() { + check( + parse, + "while (true) (true) { x $0; }", + &expect![[r#" + Stmt [0-19]: + annotations: + kind: WhileLoop [0-19]: + condition: Expr [7-11]: Lit: Bool(true) + block: + Stmt [13-19]: + annotations: + kind: ExprStmt [13-19]: + expr: Expr [13-19]: Paren Expr [14-18]: Lit: Bool(true) + + [ + Error( + Token( + Semicolon, + Open( + Brace, + ), + Span { + lo: 20, + hi: 21, + }, + ), + ), + ]"#]], + ); +} + +#[test] +fn while_with_for_syntax() { + check( + parse, + "while x in { 1, 2, 3 } { x $0; }", + &expect![[r#" + Error( + Token( + Open( + Paren, + ), + Identifier, + Span { + lo: 6, + hi: 7, + }, + ), + ) + "#]], + ); +} + +#[test] +fn while_missing_body() { + check( + parse, + "while (true);", + &expect![[r#" + Stmt [0-13]: + annotations: + kind: WhileLoop [0-13]: + condition: Expr [7-11]: Lit: Bool(true) + block: + Stmt [12-13]: + annotations: + kind: Empty"#]], + ); +} diff --git a/compiler/qsc_qasm3/src/parser/stmt/tests/invalid_stmts/measure.rs b/compiler/qsc_qasm3/src/parser/stmt/tests/invalid_stmts/measure.rs new file mode 100644 index 0000000000..1a236c2ecb --- /dev/null +++ b/compiler/qsc_qasm3/src/parser/stmt/tests/invalid_stmts/measure.rs @@ -0,0 +1,180 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +use crate::parser::{stmt::parse, tests::check}; +use expect_test::expect; + +#[test] +fn measure_multiple_qubits() { + check( + parse, + "measure $0, $1;", + &expect![[r#" + Stmt [0-10]: + annotations: + kind: MeasureStmt [0-10]: + measurement: MeasureExpr [0-7]: + operand: HardwareQubit [8-10]: 0 + target: + + [ + Error( + Token( + Semicolon, + Comma, + Span { + lo: 10, + hi: 11, + }, + ), + ), + ]"#]], + ); +} + +#[test] +fn assign_measure_multiple_qubits() { + check( + parse, + "a[0:1] = measure $0, $1;", + &expect![[r#" + Error( + Rule( + "expression", + Measure, + Span { + lo: 9, + hi: 16, + }, + ), + ) + "#]], + ); +} + +#[test] +fn assign_arrow() { + check( + parse, + "a = measure $0 -> b;", + &expect![[r#" + Error( + Rule( + "expression", + Measure, + Span { + lo: 4, + hi: 11, + }, + ), + ) + "#]], + ); +} + +#[test] +fn initialized_creg() { + check( + parse, + "creg a[1] = measure $0;", + &expect![[r#" + Stmt [0-9]: + annotations: + kind: ClassicalDeclarationStmt [0-9]: + type: ScalarType [0-9]: BitType [0-9]: + size: Expr [7-8]: Lit: Int(1) + ident: Ident [5-6] "a" + init_expr: + + [ + Error( + Token( + Semicolon, + Eq, + Span { + lo: 10, + hi: 11, + }, + ), + ), + ]"#]], + ); +} + +#[test] +fn invalid_arrow_target() { + check( + parse, + "measure $0 -> creg a[1];", + &expect![[r#" + Error( + Rule( + "identifier", + Keyword( + CReg, + ), + Span { + lo: 14, + hi: 18, + }, + ), + ) + "#]], + ); + check( + parse, + "measure $0 -> bit[1] a;", + &expect![[r#" + Error( + Rule( + "identifier", + Type( + Bit, + ), + Span { + lo: 14, + hi: 17, + }, + ), + ) + "#]], + ); +} + +#[test] +fn measure_cant_be_used_in_sub_expressions() { + check( + parse, + "a = 2 * measure $0;", + &expect![[r#" + Error( + Rule( + "expression", + Measure, + Span { + lo: 8, + hi: 15, + }, + ), + ) + "#]], + ); + check( + parse, + "a = (measure $0) + (measure $1);", + &expect![[r#" + Error( + Token( + Close( + Paren, + ), + Measure, + Span { + lo: 5, + hi: 12, + }, + ), + ) + "#]], + ); +} diff --git a/compiler/qsc_qasm3/src/parser/stmt/tests/invalid_stmts/switch.rs b/compiler/qsc_qasm3/src/parser/stmt/tests/invalid_stmts/switch.rs new file mode 100644 index 0000000000..17a0a7e9d9 --- /dev/null +++ b/compiler/qsc_qasm3/src/parser/stmt/tests/invalid_stmts/switch.rs @@ -0,0 +1,193 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +use crate::parser::{stmt::parse, tests::check}; +use expect_test::expect; + +#[test] +fn missing_target() { + check( + parse, + "switch () {}", + &expect![[r#" + Error( + Rule( + "expression", + Close( + Paren, + ), + Span { + lo: 8, + hi: 9, + }, + ), + ) + "#]], + ); +} + +#[test] +fn missing_cases() { + check( + parse, + "switch (i) { x $0 }", + &expect![[r#" + Stmt [0-19]: + annotations: + kind: SwitchStmt [0-19]: + target: Expr [8-9]: Ident [8-9] "i" + cases: + default_case: + + [ + Error( + MissingSwitchCases( + Span { + lo: 13, + hi: 12, + }, + ), + ), + Error( + Token( + Close( + Brace, + ), + Identifier, + Span { + lo: 13, + hi: 14, + }, + ), + ), + ]"#]], + ); +} + +#[test] +fn missing_case_labels() { + check( + parse, + "switch (i) { case {} }", + &expect![[r#" + Stmt [0-22]: + annotations: + kind: SwitchStmt [0-22]: + target: Expr [8-9]: Ident [8-9] "i" + cases: + SwitchCase [13-20]: + labels: + block: Block [18-20]: + default_case: + + [ + Error( + MissingSwitchCaseLabels( + Span { + lo: 13, + hi: 17, + }, + ), + ), + ]"#]], + ); +} + +#[test] +fn invalid_label_sequence() { + check( + parse, + "switch (i) { case 1,, {} }", + &expect![[r#" + Stmt [0-26]: + annotations: + kind: SwitchStmt [0-26]: + target: Expr [8-9]: Ident [8-9] "i" + cases: + SwitchCase [13-24]: + labels: + Expr [18-19]: Lit: Int(1) + Expr [20-20]: Err + block: Block [22-24]: + default_case: + + [ + Error( + MissingSeqEntry( + Span { + lo: 20, + hi: 20, + }, + ), + ), + ]"#]], + ); +} + +#[test] +fn default_case_with_label() { + check( + parse, + "switch (i) { default 0 {} }", + &expect![[r#" + Error( + Token( + Open( + Brace, + ), + Literal( + Integer( + Decimal, + ), + ), + Span { + lo: 21, + hi: 22, + }, + ), + ) + + [ + Error( + MissingSwitchCases( + Span { + lo: 13, + hi: 12, + }, + ), + ), + ]"#]], + ); +} + +#[test] +fn bad_case_syntax() { + check( + parse, + "switch (i) { default, default {} }", + &expect![[r#" + Error( + Token( + Open( + Brace, + ), + Comma, + Span { + lo: 20, + hi: 21, + }, + ), + ) + + [ + Error( + MissingSwitchCases( + Span { + lo: 13, + hi: 12, + }, + ), + ), + ]"#]], + ); +} diff --git a/compiler/qsc_qasm3/src/parser/stmt/tests/invalid_stmts/tokens.rs b/compiler/qsc_qasm3/src/parser/stmt/tests/invalid_stmts/tokens.rs new file mode 100644 index 0000000000..79c685326c --- /dev/null +++ b/compiler/qsc_qasm3/src/parser/stmt/tests/invalid_stmts/tokens.rs @@ -0,0 +1,298 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +use crate::parser::{stmt::parse, tests::check}; +use expect_test::expect; + +#[test] +fn bad_tokens() { + check( + parse, + "#;", + &expect![[r#" + Stmt [1-2]: + annotations: + kind: Empty + + [ + Error( + Lex( + Incomplete( + Ident, + Identifier, + Single( + Semi, + ), + Span { + lo: 1, + hi: 2, + }, + ), + ), + ), + ]"#]], + ); + + check( + parse, + "3x;", + &expect![[r#" + Error( + ExpectedItem( + Identifier, + Span { + lo: 0, + hi: 1, + }, + ), + ) + "#]], + ); + + check( + parse, + "x@x;", + &expect![[r#" + Stmt [0-1]: + annotations: + kind: ExprStmt [0-1]: + expr: Expr [0-1]: Ident [0-1] "x" + + [ + Error( + Token( + Semicolon, + At, + Span { + lo: 1, + hi: 2, + }, + ), + ), + ]"#]], + ); + + check( + parse, + "3.4.3;", + &expect![[r#" + Stmt [0-3]: + annotations: + kind: ExprStmt [0-3]: + expr: Expr [0-3]: Lit: Float(3.4) + + [ + Error( + Token( + Semicolon, + Literal( + Float, + ), + Span { + lo: 3, + hi: 5, + }, + ), + ), + ]"#]], + ); + + check( + parse, + "3.4e3e3;", + &expect![[r#" + Error( + ExpectedItem( + Identifier, + Span { + lo: 0, + hi: 5, + }, + ), + ) + "#]], + ); +} + +#[test] +#[allow(clippy::too_many_lines)] +fn bad_integer_literals() { + check( + parse, + "3_4_;", + &expect![[r#" + Stmt [4-5]: + annotations: + kind: Empty + + [ + Error( + Lex( + Unknown( + '3', + Span { + lo: 0, + hi: 4, + }, + ), + ), + ), + ]"#]], + ); + + check( + parse, + "0b123;", + &expect![[r#" + Stmt [0-3]: + annotations: + kind: ExprStmt [0-3]: + expr: Expr [0-3]: Lit: Int(1) + + [ + Error( + Token( + Semicolon, + Literal( + Integer( + Decimal, + ), + ), + Span { + lo: 3, + hi: 5, + }, + ), + ), + ]"#]], + ); + + check( + parse, + "0B123;", + &expect![[r#" + Stmt [0-3]: + annotations: + kind: ExprStmt [0-3]: + expr: Expr [0-3]: Lit: Int(1) + + [ + Error( + Token( + Semicolon, + Literal( + Integer( + Decimal, + ), + ), + Span { + lo: 3, + hi: 5, + }, + ), + ), + ]"#]], + ); + + check( + parse, + "0o789;", + &expect![[r#" + Stmt [0-3]: + annotations: + kind: ExprStmt [0-3]: + expr: Expr [0-3]: Lit: Int(7) + + [ + Error( + Token( + Semicolon, + Literal( + Integer( + Decimal, + ), + ), + Span { + lo: 3, + hi: 5, + }, + ), + ), + ]"#]], + ); + + check( + parse, + "0O789;", + &expect![[r#" + Stmt [0-3]: + annotations: + kind: ExprStmt [0-3]: + expr: Expr [0-3]: Lit: Int(7) + + [ + Error( + Token( + Semicolon, + Literal( + Integer( + Decimal, + ), + ), + Span { + lo: 3, + hi: 5, + }, + ), + ), + ]"#]], + ); + + check( + parse, + "0x12g;", + &expect![[r#" + Error( + ExpectedItem( + Identifier, + Span { + lo: 0, + hi: 4, + }, + ), + ) + "#]], + ); + + check( + parse, + "0X12g;", + &expect![[r#" + Error( + ExpectedItem( + Identifier, + Span { + lo: 0, + hi: 4, + }, + ), + ) + "#]], + ); + + check( + parse, + "12af;", + &expect![[r#" + Error( + ExpectedItem( + Identifier, + Span { + lo: 0, + hi: 2, + }, + ), + ) + "#]], + ); +} From 982f84c7434ad009eec7d3aa5c969a995ee00387 Mon Sep 17 00:00:00 2001 From: Ian Davis Date: Wed, 12 Mar 2025 14:07:26 -0700 Subject: [PATCH 058/108] Implement base declarations and binary expression lowering (#2221) --- compiler/qsc_qasm3/src/lib.rs | 1 - compiler/qsc_qasm3/src/parser.rs | 5 +- compiler/qsc_qasm3/src/{ => parser}/ast.rs | 23 +- .../src/{ => parser}/ast/display_utils.rs | 0 compiler/qsc_qasm3/src/parser/expr.rs | 25 +- compiler/qsc_qasm3/src/parser/expr/tests.rs | 2 +- compiler/qsc_qasm3/src/parser/prgm.rs | 3 +- compiler/qsc_qasm3/src/parser/prim.rs | 8 +- compiler/qsc_qasm3/src/parser/prim/tests.rs | 2 +- compiler/qsc_qasm3/src/parser/stmt.rs | 26 +- compiler/qsc_qasm3/src/semantic/ast.rs | 98 +- compiler/qsc_qasm3/src/semantic/error.rs | 10 + compiler/qsc_qasm3/src/semantic/lowerer.rs | 1034 +++++++++++++---- compiler/qsc_qasm3/src/semantic/symbols.rs | 11 +- compiler/qsc_qasm3/src/semantic/tests.rs | 20 +- .../src/semantic/tests/assignment.rs | 18 + .../qsc_qasm3/src/semantic/tests/decls.rs | 1 + .../src/semantic/tests/decls/angle.rs | 415 +++++++ .../src/semantic/tests/expression.rs | 57 + .../src/semantic/tests/expression/binary.rs | 7 + .../binary/arithmetic_conversions.rs | 264 +++++ .../tests/expression/binary/comparison.rs | 53 + .../binary/comparison/bit_to_bit.rs | 542 +++++++++ .../binary/comparison/bool_to_bool.rs | 478 ++++++++ .../binary/comparison/float_to_float.rs | 336 ++++++ .../binary/comparison/int_to_int.rs | 336 ++++++ .../binary/comparison/uint_to_uint.rs | 336 ++++++ .../tests/expression/binary/complex.rs | 66 ++ .../semantic/tests/expression/binary/ident.rs | 161 +++ .../expression/implicit_cast_from_angle.rs | 556 +++++++++ .../expression/implicit_cast_from_bit.rs | 303 +++++ .../expression/implicit_cast_from_bitarray.rs | 97 ++ .../expression/implicit_cast_from_bool.rs | 326 ++++++ .../expression/implicit_cast_from_float.rs | 601 ++++++++++ .../expression/implicit_cast_from_int.rs | 442 +++++++ compiler/qsc_qasm3/src/semantic/types.rs | 199 +++- 36 files changed, 6521 insertions(+), 341 deletions(-) rename compiler/qsc_qasm3/src/{ => parser}/ast.rs (98%) rename compiler/qsc_qasm3/src/{ => parser}/ast/display_utils.rs (100%) create mode 100644 compiler/qsc_qasm3/src/semantic/tests/assignment.rs create mode 100644 compiler/qsc_qasm3/src/semantic/tests/decls/angle.rs create mode 100644 compiler/qsc_qasm3/src/semantic/tests/expression.rs create mode 100644 compiler/qsc_qasm3/src/semantic/tests/expression/binary.rs create mode 100644 compiler/qsc_qasm3/src/semantic/tests/expression/binary/arithmetic_conversions.rs create mode 100644 compiler/qsc_qasm3/src/semantic/tests/expression/binary/comparison.rs create mode 100644 compiler/qsc_qasm3/src/semantic/tests/expression/binary/comparison/bit_to_bit.rs create mode 100644 compiler/qsc_qasm3/src/semantic/tests/expression/binary/comparison/bool_to_bool.rs create mode 100644 compiler/qsc_qasm3/src/semantic/tests/expression/binary/comparison/float_to_float.rs create mode 100644 compiler/qsc_qasm3/src/semantic/tests/expression/binary/comparison/int_to_int.rs create mode 100644 compiler/qsc_qasm3/src/semantic/tests/expression/binary/comparison/uint_to_uint.rs create mode 100644 compiler/qsc_qasm3/src/semantic/tests/expression/binary/complex.rs create mode 100644 compiler/qsc_qasm3/src/semantic/tests/expression/binary/ident.rs create mode 100644 compiler/qsc_qasm3/src/semantic/tests/expression/implicit_cast_from_angle.rs create mode 100644 compiler/qsc_qasm3/src/semantic/tests/expression/implicit_cast_from_bit.rs create mode 100644 compiler/qsc_qasm3/src/semantic/tests/expression/implicit_cast_from_bitarray.rs create mode 100644 compiler/qsc_qasm3/src/semantic/tests/expression/implicit_cast_from_bool.rs create mode 100644 compiler/qsc_qasm3/src/semantic/tests/expression/implicit_cast_from_float.rs create mode 100644 compiler/qsc_qasm3/src/semantic/tests/expression/implicit_cast_from_int.rs diff --git a/compiler/qsc_qasm3/src/lib.rs b/compiler/qsc_qasm3/src/lib.rs index bc4e58ce4f..851252c7ee 100644 --- a/compiler/qsc_qasm3/src/lib.rs +++ b/compiler/qsc_qasm3/src/lib.rs @@ -5,7 +5,6 @@ #![allow(dead_code)] mod angle; -mod ast; mod ast_builder; mod compile; pub use compile::qasm_to_program; diff --git a/compiler/qsc_qasm3/src/parser.rs b/compiler/qsc_qasm3/src/parser.rs index bf9c2a83aa..cdd177cc0f 100644 --- a/compiler/qsc_qasm3/src/parser.rs +++ b/compiler/qsc_qasm3/src/parser.rs @@ -1,8 +1,9 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. -use crate::ast::{Program, StmtKind}; +pub mod ast; use crate::io::SourceResolver; +use ast::{Program, StmtKind}; use qsc_frontend::compile::SourceMap; use qsc_frontend::error::WithSource; use scan::ParserContext; @@ -231,7 +232,7 @@ where Ok((program, errors, included)) } -fn parse_includes(program: &crate::ast::Program, resolver: &R) -> miette::Result> +fn parse_includes(program: &Program, resolver: &R) -> miette::Result> where R: SourceResolver, { diff --git a/compiler/qsc_qasm3/src/ast.rs b/compiler/qsc_qasm3/src/parser/ast.rs similarity index 98% rename from compiler/qsc_qasm3/src/ast.rs rename to compiler/qsc_qasm3/src/parser/ast.rs index c45bc40bae..03193a2d0c 100644 --- a/compiler/qsc_qasm3/src/ast.rs +++ b/compiler/qsc_qasm3/src/parser/ast.rs @@ -481,6 +481,7 @@ pub enum Identifier { } impl Identifier { + #[must_use] pub fn span(&self) -> Span { match self { Identifier::Ident(ident) => ident.span, @@ -878,6 +879,7 @@ pub enum TypeDef { } impl TypeDef { + #[must_use] pub fn span(&self) -> Span { match self { TypeDef::Scalar(ident) => ident.span, @@ -1649,6 +1651,16 @@ impl Display for IndexElement { } } +impl IndexElement { + #[must_use] + pub fn span(&self) -> Span { + match self { + IndexElement::DiscreteSet(set) => set.span, + IndexElement::IndexSet(set) => set.span, + } + } +} + #[derive(Clone, Debug, Default)] pub enum IndexSetItem { RangeDefinition(RangeDefinition), @@ -1680,7 +1692,7 @@ impl Display for IndexSetItem { } } -#[derive(Clone, Debug)] +#[derive(Clone, Copy, Debug, Eq, PartialEq)] pub enum IOKeyword { Input, Output, @@ -1695,6 +1707,15 @@ impl Display for IOKeyword { } } +impl From for crate::semantic::symbols::IOKind { + fn from(value: IOKeyword) -> Self { + match value { + IOKeyword::Input => crate::semantic::symbols::IOKind::Input, + IOKeyword::Output => crate::semantic::symbols::IOKind::Output, + } + } +} + #[derive(Clone, Debug)] pub enum TimeUnit { Dt, diff --git a/compiler/qsc_qasm3/src/ast/display_utils.rs b/compiler/qsc_qasm3/src/parser/ast/display_utils.rs similarity index 100% rename from compiler/qsc_qasm3/src/ast/display_utils.rs rename to compiler/qsc_qasm3/src/parser/ast/display_utils.rs diff --git a/compiler/qsc_qasm3/src/parser/expr.rs b/compiler/qsc_qasm3/src/parser/expr.rs index 5f679cd385..6e775e807a 100644 --- a/compiler/qsc_qasm3/src/parser/expr.rs +++ b/compiler/qsc_qasm3/src/parser/expr.rs @@ -12,29 +12,26 @@ use num_traits::Num; use qsc_data_structures::span::Span; use crate::{ - ast::{ - self, list_from_iter, BinOp, BinaryOpExpr, Cast, DiscreteSet, Expr, ExprKind, FunctionCall, - GateOperand, HardwareQubit, Ident, IndexElement, IndexExpr, IndexSet, IndexSetItem, - IndexedIdent, List, Lit, LiteralKind, MeasureExpr, RangeDefinition, TimeUnit, TypeDef, - UnaryOp, ValueExpression, Version, - }, keyword::Keyword, lex::{ cooked::{ComparisonOp, Literal, TimingLiteralKind}, ClosedBinOp, Delim, Radix, Token, TokenKind, }, - parser::{ - completion::WordKinds, - prim::{shorten, token}, - scan::ParserContext, - }, }; use crate::parser::Result; use super::{ + ast::{ + list_from_iter, BinOp, BinaryOpExpr, Cast, DiscreteSet, Expr, ExprKind, FunctionCall, + GateOperand, HardwareQubit, Ident, IndexElement, IndexExpr, IndexSet, IndexSetItem, + IndexedIdent, List, Lit, LiteralKind, MeasureExpr, RangeDefinition, TimeUnit, TypeDef, + UnaryOp, UnaryOpExpr, ValueExpression, Version, + }, + completion::WordKinds, error::{Error, ErrorKind}, - prim::{ident, many, opt, recovering_token, seq, FinalSep}, + prim::{ident, many, opt, recovering_token, seq, shorten, token, FinalSep}, + scan::ParserContext, stmt::scalar_or_array_type, }; @@ -94,7 +91,7 @@ fn expr_op(s: &mut ParserContext, context: OpContext) -> Result { let rhs = expr_op(s, OpContext::Precedence(op.precedence))?; Expr { span: s.span(lo), - kind: Box::new(ExprKind::UnaryOp(ast::UnaryOpExpr { + kind: Box::new(ExprKind::UnaryOp(UnaryOpExpr { op: op.kind, expr: rhs, })), @@ -444,7 +441,7 @@ pub(crate) fn paren_expr(s: &mut ParserContext, lo: u32) -> Result { }) } -fn funcall(s: &mut ParserContext, ident: ast::Ident) -> Result { +fn funcall(s: &mut ParserContext, ident: Ident) -> Result { let lo = ident.span.lo; let (args, _) = seq(s, expr)?; token(s, TokenKind::Close(Delim::Paren))?; diff --git a/compiler/qsc_qasm3/src/parser/expr/tests.rs b/compiler/qsc_qasm3/src/parser/expr/tests.rs index a14716f10c..893f9db6fa 100644 --- a/compiler/qsc_qasm3/src/parser/expr/tests.rs +++ b/compiler/qsc_qasm3/src/parser/expr/tests.rs @@ -3,7 +3,7 @@ use super::expr; use crate::{ - ast::StmtKind, + parser::ast::StmtKind, parser::{scan::ParserContext, stmt, tests::check}, }; use expect_test::{expect, Expect}; diff --git a/compiler/qsc_qasm3/src/parser/prgm.rs b/compiler/qsc_qasm3/src/parser/prgm.rs index 928b033c8e..52f836ba20 100644 --- a/compiler/qsc_qasm3/src/parser/prgm.rs +++ b/compiler/qsc_qasm3/src/parser/prgm.rs @@ -6,11 +6,12 @@ use super::{ stmt, Result, }; use crate::{ - ast::{Program, Stmt, StmtKind, Version}, lex::{Delim, TokenKind}, parser::{completion::WordKinds, expr}, }; +use super::ast::{Program, Stmt, StmtKind, Version}; + use super::ParserContext; /// Grammar: `version? statementOrScope* EOF`. diff --git a/compiler/qsc_qasm3/src/parser/prim.rs b/compiler/qsc_qasm3/src/parser/prim.rs index 44e3ef913b..7f8d189a10 100644 --- a/compiler/qsc_qasm3/src/parser/prim.rs +++ b/compiler/qsc_qasm3/src/parser/prim.rs @@ -9,11 +9,9 @@ use super::{ scan::ParserContext, Parser, Result, }; -use crate::{ - ast::{Ident, IncompletePath, Path, PathKind}, - lex::TokenKind, - parser::completion::WordKinds, -}; +use crate::{lex::TokenKind, parser::completion::WordKinds}; + +use super::ast::{Ident, IncompletePath, Path, PathKind}; use qsc_data_structures::span::{Span, WithSpan}; diff --git a/compiler/qsc_qasm3/src/parser/prim/tests.rs b/compiler/qsc_qasm3/src/parser/prim/tests.rs index a86993fa83..7b2814b717 100644 --- a/compiler/qsc_qasm3/src/parser/prim/tests.rs +++ b/compiler/qsc_qasm3/src/parser/prim/tests.rs @@ -3,9 +3,9 @@ use super::{ident, opt, seq}; use crate::{ - ast::PathKind, keyword::Keyword, lex::TokenKind, + parser::ast::PathKind, parser::{ completion::WordKinds, error::{Error, ErrorKind}, diff --git a/compiler/qsc_qasm3/src/parser/stmt.rs b/compiler/qsc_qasm3/src/parser/stmt.rs index a72c0eac9b..c8f27caedd 100644 --- a/compiler/qsc_qasm3/src/parser/stmt.rs +++ b/compiler/qsc_qasm3/src/parser/stmt.rs @@ -15,23 +15,23 @@ use super::{ Result, }; use crate::{ - ast::{ - list_from_iter, AccessControl, AliasDeclStmt, AngleType, Annotation, ArrayBaseTypeKind, - ArrayReferenceType, ArrayType, ArrayTypedParameter, AssignOpStmt, AssignStmt, BarrierStmt, - BitType, Block, BoxStmt, BreakStmt, CalibrationGrammarStmt, CalibrationStmt, Cast, - ClassicalDeclarationStmt, ComplexType, ConstantDeclStmt, ContinueStmt, DefCalStmt, DefStmt, - DelayStmt, EndStmt, EnumerableSet, Expr, ExprKind, ExprStmt, ExternDecl, ExternParameter, - FloatType, ForStmt, FunctionCall, GPhase, GateCall, GateModifierKind, GateOperand, - IODeclaration, IOKeyword, Ident, Identifier, IfStmt, IncludeStmt, IndexElement, IndexExpr, - IndexSetItem, IndexedIdent, IntType, List, LiteralKind, MeasureStmt, Pragma, - QuantumGateDefinition, QuantumGateModifier, QuantumTypedParameter, QubitDeclaration, - RangeDefinition, ResetStmt, ReturnStmt, ScalarType, ScalarTypeKind, ScalarTypedParameter, - Stmt, StmtKind, SwitchCase, SwitchStmt, TypeDef, TypedParameter, UIntType, WhileLoop, - }, keyword::Keyword, lex::{cooked::Type, Delim, TokenKind}, }; +use super::ast::{ + list_from_iter, AccessControl, AliasDeclStmt, AngleType, Annotation, ArrayBaseTypeKind, + ArrayReferenceType, ArrayType, ArrayTypedParameter, AssignOpStmt, AssignStmt, BarrierStmt, + BitType, Block, BoxStmt, BreakStmt, CalibrationGrammarStmt, CalibrationStmt, Cast, + ClassicalDeclarationStmt, ComplexType, ConstantDeclStmt, ContinueStmt, DefCalStmt, DefStmt, + DelayStmt, EndStmt, EnumerableSet, Expr, ExprKind, ExprStmt, ExternDecl, ExternParameter, + FloatType, ForStmt, FunctionCall, GPhase, GateCall, GateModifierKind, GateOperand, + IODeclaration, IOKeyword, Ident, Identifier, IfStmt, IncludeStmt, IndexElement, IndexExpr, + IndexSetItem, IndexedIdent, IntType, List, LiteralKind, MeasureStmt, Pragma, + QuantumGateDefinition, QuantumGateModifier, QuantumTypedParameter, QubitDeclaration, + RangeDefinition, ResetStmt, ReturnStmt, ScalarType, ScalarTypeKind, ScalarTypedParameter, Stmt, + StmtKind, SwitchCase, SwitchStmt, TypeDef, TypedParameter, UIntType, WhileLoop, +}; use super::{prim::token, ParserContext}; /// Our implementation differs slightly from the grammar in diff --git a/compiler/qsc_qasm3/src/semantic/ast.rs b/compiler/qsc_qasm3/src/semantic/ast.rs index dcbebbaee9..ecb4d15e2f 100644 --- a/compiler/qsc_qasm3/src/semantic/ast.rs +++ b/compiler/qsc_qasm3/src/semantic/ast.rs @@ -10,7 +10,7 @@ use std::{ }; use crate::{ - ast::{ + parser::ast::{ display_utils::{ write_field, write_header, write_indented_list, write_list_field, write_opt_field, write_opt_list_field, writeln_field, writeln_header, writeln_list_field, @@ -21,6 +21,8 @@ use crate::{ semantic::symbols::SymbolId, }; +use crate::parser::ast as syntax; + #[derive(Clone, Debug)] pub struct Program { pub statements: List, @@ -218,6 +220,32 @@ impl Display for BinOp { } } +impl From for BinOp { + fn from(value: syntax::BinOp) -> Self { + match value { + syntax::BinOp::Add => BinOp::Add, + syntax::BinOp::AndB => BinOp::AndB, + syntax::BinOp::AndL => BinOp::AndL, + syntax::BinOp::Div => BinOp::Div, + syntax::BinOp::Eq => BinOp::Eq, + syntax::BinOp::Exp => BinOp::Exp, + syntax::BinOp::Gt => BinOp::Gt, + syntax::BinOp::Gte => BinOp::Gte, + syntax::BinOp::Lt => BinOp::Lt, + syntax::BinOp::Lte => BinOp::Lte, + syntax::BinOp::Mod => BinOp::Mod, + syntax::BinOp::Mul => BinOp::Mul, + syntax::BinOp::Neq => BinOp::Neq, + syntax::BinOp::OrB => BinOp::OrB, + syntax::BinOp::OrL => BinOp::OrL, + syntax::BinOp::Shl => BinOp::Shl, + syntax::BinOp::Shr => BinOp::Shr, + syntax::BinOp::Sub => BinOp::Sub, + syntax::BinOp::XorB => BinOp::XorB, + } + } +} + /// A unary operator. #[derive(Clone, Copy, Debug)] pub enum UnaryOp { @@ -305,6 +333,7 @@ impl Display for AliasDeclStmt { pub enum StmtKind { Alias(AliasDeclStmt), Assign(AssignStmt), + IndexedAssign(IndexedAssignStmt), AssignOp(AssignOpStmt), Barrier(BarrierStmt), Box(BoxStmt), @@ -361,6 +390,7 @@ impl Display for StmtKind { StmtKind::GPhase(gphase) => write!(f, "{gphase}"), StmtKind::If(if_stmt) => write!(f, "{if_stmt}"), StmtKind::Include(include) => write!(f, "{include}"), + StmtKind::IndexedAssign(assign) => write!(f, "{assign}"), StmtKind::IODeclaration(io) => write!(f, "{io}"), StmtKind::Measure(measure) => write!(f, "{measure}"), StmtKind::Pragma(pragma) => write!(f, "{pragma}"), @@ -503,14 +533,14 @@ impl WithSpan for Ident { #[derive(Clone, Debug)] pub struct IndexedIdent { pub span: Span, - pub name: Ident, + pub symbol_id: SymbolId, pub indices: List, } impl Display for IndexedIdent { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { writeln_header(f, "IndexedIdent", self.span)?; - writeln_field(f, "name", &self.name)?; + writeln_field(f, "symbol_id", &self.symbol_id)?; write_list_field(f, "indices", &self.indices) } } @@ -1136,17 +1166,15 @@ impl Display for ValueExpression { #[derive(Clone, Debug)] pub struct IODeclaration { pub span: Span, - pub io_identifier: IOKeyword, - pub ty: TypeDef, - pub ident: Box, + pub symbol_id: SymbolId, + pub init_expr: Box, } impl Display for IODeclaration { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { writeln_header(f, "IODeclaration", self.span)?; - writeln_field(f, "io_keyword", &self.io_identifier)?; - writeln_field(f, "type", &self.ty)?; - write_field(f, "ident", &self.ident) + writeln_field(f, "symbol_id", &self.symbol_id)?; + write_field(f, "init_expr", &self.init_expr) } } @@ -1196,7 +1224,7 @@ pub struct ScalarTypedParameter { impl Display for ScalarTypedParameter { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { writeln_header(f, "ScalarTypedParameter", self.span)?; - writeln_field(f, "type", &self.ty)?; + writeln_field(f, "ty", &self.ty)?; write_field(f, "ident", &self.ident) } } @@ -1240,7 +1268,7 @@ pub struct ArrayTypedParameter { impl Display for ArrayTypedParameter { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { writeln_header(f, "ArrayTypedParameter", self.span)?; - writeln_field(f, "type", &self.ty)?; + writeln_field(f, "ty", &self.ty)?; write_field(f, "ident", &self.ident) } } @@ -1376,6 +1404,7 @@ pub enum ExprKind { #[default] Err, Ident(SymbolId), + IndexedIdentifier(IndexedIdent), UnaryOp(UnaryOpExpr), BinaryOp(BinaryOpExpr), Lit(LiteralKind), @@ -1389,7 +1418,8 @@ impl Display for ExprKind { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { match self { ExprKind::Err => write!(f, "Err"), - ExprKind::Ident(id) => write!(f, "{id}"), + ExprKind::Ident(id) => write!(f, "SymbolId({id})"), + ExprKind::IndexedIdentifier(id) => write!(f, "{id}"), ExprKind::UnaryOp(expr) => write!(f, "{expr}"), ExprKind::BinaryOp(expr) => write!(f, "{expr}"), ExprKind::Lit(lit) => write!(f, "Lit: {lit}"), @@ -1404,14 +1434,31 @@ impl Display for ExprKind { #[derive(Clone, Debug)] pub struct AssignStmt { pub span: Span, - pub lhs: Expr, + pub symbold_id: SymbolId, pub rhs: Expr, } impl Display for AssignStmt { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { writeln_header(f, "AssignStmt", self.span)?; - writeln_field(f, "lhs", &self.lhs)?; + writeln_field(f, "symbol_id", &self.symbold_id)?; + write_field(f, "rhs", &self.rhs) + } +} + +#[derive(Clone, Debug)] +pub struct IndexedAssignStmt { + pub span: Span, + pub symbold_id: SymbolId, + pub lhs: Expr, + pub rhs: Expr, +} + +impl Display for IndexedAssignStmt { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + writeln_header(f, "AssignStmt", self.span)?; + writeln_field(f, "symbol_id", &self.symbold_id)?; + writeln_field(f, "lhs", &self.rhs)?; write_field(f, "rhs", &self.rhs) } } @@ -1419,7 +1466,7 @@ impl Display for AssignStmt { #[derive(Clone, Debug)] pub struct AssignOpStmt { pub span: Span, - pub op: BinOp, + pub symbold_id: SymbolId, pub lhs: Expr, pub rhs: Expr, } @@ -1427,8 +1474,8 @@ pub struct AssignOpStmt { impl Display for AssignOpStmt { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { writeln_header(f, "AssignOpStmt", self.span)?; - writeln_field(f, "op", &self.op)?; - writeln_field(f, "lhs", &self.lhs)?; + writeln_field(f, "symbol_id", &self.symbold_id)?; + writeln_field(f, "lhs", &self.rhs)?; write_field(f, "rhs", &self.rhs) } } @@ -1454,6 +1501,15 @@ pub struct BinaryOpExpr { pub rhs: Expr, } +impl BinaryOpExpr { + pub fn span(&self) -> Span { + Span { + lo: self.lhs.span.lo, + hi: self.rhs.span.hi, + } + } +} + impl Display for BinaryOpExpr { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { writeln!(f, "BinaryOpExpr:")?; @@ -1488,7 +1544,7 @@ pub struct Cast { impl Display for Cast { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { writeln_header(f, "Cast", self.span)?; - writeln_field(f, "type", &self.ty)?; + writeln_field(f, "ty", &self.ty)?; write_field(f, "expr", &self.expr) } } @@ -1574,12 +1630,10 @@ impl Display for IndexElement { } } -#[derive(Clone, Debug, Default)] +#[derive(Clone, Debug)] pub enum IndexSetItem { RangeDefinition(RangeDefinition), Expr(Expr), - #[default] - Err, } /// This is needed to able to use `IndexSetItem` in the `seq` combinator. @@ -1590,7 +1644,6 @@ impl WithSpan for IndexSetItem { IndexSetItem::RangeDefinition(range.with_span(span)) } IndexSetItem::Expr(expr) => IndexSetItem::Expr(expr.with_span(span)), - IndexSetItem::Err => IndexSetItem::Err, } } } @@ -1600,7 +1653,6 @@ impl Display for IndexSetItem { match self { IndexSetItem::RangeDefinition(range) => write!(f, "{range}"), IndexSetItem::Expr(expr) => write!(f, "{expr}"), - IndexSetItem::Err => write!(f, "Err"), } } } diff --git a/compiler/qsc_qasm3/src/semantic/error.rs b/compiler/qsc_qasm3/src/semantic/error.rs index c637443765..48b7334ff6 100644 --- a/compiler/qsc_qasm3/src/semantic/error.rs +++ b/compiler/qsc_qasm3/src/semantic/error.rs @@ -78,6 +78,9 @@ pub enum SemanticErrorKind { #[error("For statements must have a body or statement.")] #[diagnostic(code("Qsc.Qasm3.Compile.ForStatementsMustHaveABodyOrStatement"))] ForStatementsMustHaveABodyOrStatement(#[label] Span), + #[error("Inconsisten types in alias expression: {0}.")] + #[diagnostic(code("Qsc.Qasm3.Compile.InconsistentTypesInAlias"))] + InconsistentTypesInAlias(String, #[label] Span), #[error("If statement missing {0} expression.")] #[diagnostic(code("Qsc.Qasm3.Compile.IfStmtMissingExpression"))] IfStmtMissingExpression(String, #[label] Span), @@ -162,6 +165,9 @@ pub enum SemanticErrorKind { #[error("Too many controls specified.")] #[diagnostic(code("Qsc.Qasm3.Compile.TooManyControls"))] TooManyControls(#[label] Span), + #[error("Too many indicies specified.")] + #[diagnostic(code("Qsc.Qasm3.Compile.TooManyIndices"))] + TooManyIndices(#[label] Span), #[error("Bitwise not `~` is not allowed for instances of {0}.")] #[diagnostic(code("Qsc.Qasm3.Compile.TypeDoesNotSupportBitwiseNot"))] TypeDoesNotSupportBitwiseNot(String, #[label] Span), @@ -241,6 +247,9 @@ impl SemanticErrorKind { Self::ForStatementsMustHaveABodyOrStatement(span) => { Self::ForStatementsMustHaveABodyOrStatement(span + offset) } + Self::InconsistentTypesInAlias(name, span) => { + Self::InconsistentTypesInAlias(name, span + offset) + } Self::IfStmtMissingExpression(name, span) => { Self::IfStmtMissingExpression(name, span + offset) } @@ -303,6 +312,7 @@ impl SemanticErrorKind { } Self::ReturnNotInSubroutine(span) => Self::ReturnNotInSubroutine(span + offset), Self::TooManyControls(span) => Self::TooManyControls(span + offset), + Self::TooManyIndices(span) => Self::TooManyIndices(span + offset), Self::TypeDoesNotSupportBitwiseNot(name, span) => { Self::TypeDoesNotSupportBitwiseNot(name, span + offset) } diff --git a/compiler/qsc_qasm3/src/semantic/lowerer.rs b/compiler/qsc_qasm3/src/semantic/lowerer.rs index e0976acccc..4656c9a6b3 100644 --- a/compiler/qsc_qasm3/src/semantic/lowerer.rs +++ b/compiler/qsc_qasm3/src/semantic/lowerer.rs @@ -4,6 +4,12 @@ use std::ops::ShlAssign; use std::path::PathBuf; +use super::types::binop_requires_int_conversion_for_type; +use super::types::binop_requires_symmetric_int_conversion; +use super::types::is_complex_binop_supported; +use super::types::promote_to_uint_ty; +use super::types::requires_symmetric_conversion; +use super::types::try_promote_with_casting; use super::types::types_equal_except_const; use super::types::unary_op_can_be_applied_to_type; use super::types::Type; @@ -15,13 +21,14 @@ use qsc_frontend::{compile::SourceMap, error::WithSource}; use super::symbols::{IOKind, Symbol, SymbolTable}; -use crate::ast::list_from_iter; use crate::oqasm_helpers::safe_i64_to_f64; use crate::parser::QasmSource; use crate::semantic::types::can_cast_literal; use crate::semantic::types::can_cast_literal_with_value_knowledge; use crate::semantic::types::ArrayDimensions; +use crate::parser::ast as syntax; + use super::{ ast::{Stmt, Version}, SemanticErrorKind, @@ -52,7 +59,7 @@ impl Lowerer { let program = super::ast::Program { version: self.version, - statements: list_from_iter(self.stmts), + statements: syntax::list_from_iter(self.stmts), }; super::QasmSemanticParseResult { @@ -64,7 +71,7 @@ impl Lowerer { } } - fn lower_version(&mut self, version: Option) -> Option { + fn lower_version(&mut self, version: Option) -> Option { if let Some(version) = version { if version.major != 3 { self.push_semantic_error(SemanticErrorKind::UnsupportedVersion( @@ -103,7 +110,7 @@ impl Lowerer { for stmt in &source.program().statements { match &*stmt.kind { - crate::ast::StmtKind::Include(include) => { + syntax::StmtKind::Include(include) => { // if we are not in the root we should not be able to include // as this is a limitation of the QASM3 language if !self.symbols.is_current_scope_global() { @@ -139,97 +146,85 @@ impl Lowerer { } #[allow(clippy::too_many_lines)] - fn lower_stmt(&mut self, stmt: &crate::ast::Stmt) -> Option { + fn lower_stmt(&mut self, stmt: &syntax::Stmt) -> Option { let kind = match &*stmt.kind { - crate::ast::StmtKind::Alias(stmt) => { - super::ast::StmtKind::Alias(self.lower_alias(stmt)?) - } - crate::ast::StmtKind::Assign(stmt) => { - super::ast::StmtKind::Assign(self.lower_assign(stmt)?) - } - crate::ast::StmtKind::AssignOp(stmt) => { + syntax::StmtKind::Alias(stmt) => super::ast::StmtKind::Alias(self.lower_alias(stmt)?), + syntax::StmtKind::Assign(stmt) => self.lower_assign(stmt)?, + syntax::StmtKind::AssignOp(stmt) => { super::ast::StmtKind::AssignOp(self.lower_assign_op(stmt)?) } - crate::ast::StmtKind::Barrier(stmt) => { + syntax::StmtKind::Barrier(stmt) => { super::ast::StmtKind::Barrier(self.lower_barrier(stmt)?) } - crate::ast::StmtKind::Box(stmt) => super::ast::StmtKind::Box(self.lower_box(stmt)?), - crate::ast::StmtKind::Break(stmt) => self.lower_break(stmt)?, - crate::ast::StmtKind::Block(stmt) => { + syntax::StmtKind::Box(stmt) => super::ast::StmtKind::Box(self.lower_box(stmt)?), + syntax::StmtKind::Break(stmt) => self.lower_break(stmt)?, + syntax::StmtKind::Block(stmt) => { super::ast::StmtKind::Block(Box::new(self.lower_block(stmt)?)) } - crate::ast::StmtKind::Cal(stmt) => self.lower_calibration(stmt)?, - crate::ast::StmtKind::CalibrationGrammar(stmt) => { + syntax::StmtKind::Cal(stmt) => self.lower_calibration(stmt)?, + syntax::StmtKind::CalibrationGrammar(stmt) => { super::ast::StmtKind::CalibrationGrammar(self.lower_calibration_grammar(stmt)?) } - crate::ast::StmtKind::ClassicalDecl(stmt) => { + syntax::StmtKind::ClassicalDecl(stmt) => { super::ast::StmtKind::ClassicalDecl(self.lower_classical_decl(stmt)?) } - crate::ast::StmtKind::ConstDecl(stmt) => { + syntax::StmtKind::ConstDecl(stmt) => { super::ast::StmtKind::ClassicalDecl(self.lower_const_decl(stmt)?) } - crate::ast::StmtKind::Continue(stmt) => self.lower_continue_stmt(stmt)?, - crate::ast::StmtKind::Def(stmt) => super::ast::StmtKind::Def(self.lower_def(stmt)?), - crate::ast::StmtKind::DefCal(stmt) => { + syntax::StmtKind::Continue(stmt) => self.lower_continue_stmt(stmt)?, + syntax::StmtKind::Def(stmt) => super::ast::StmtKind::Def(self.lower_def(stmt)?), + syntax::StmtKind::DefCal(stmt) => { super::ast::StmtKind::DefCal(self.lower_def_cal(stmt)?) } - crate::ast::StmtKind::Delay(stmt) => { - super::ast::StmtKind::Delay(self.lower_delay(stmt)?) - } - crate::ast::StmtKind::Empty => { + syntax::StmtKind::Delay(stmt) => super::ast::StmtKind::Delay(self.lower_delay(stmt)?), + syntax::StmtKind::Empty => { // we ignore empty statements None? } - crate::ast::StmtKind::End(stmt) => { - super::ast::StmtKind::End(self.lower_end_stmt(stmt)?) - } - crate::ast::StmtKind::ExprStmt(stmt) => { + syntax::StmtKind::End(stmt) => super::ast::StmtKind::End(self.lower_end_stmt(stmt)?), + syntax::StmtKind::ExprStmt(stmt) => { super::ast::StmtKind::ExprStmt(self.lower_expr_stmt(stmt)?) } - crate::ast::StmtKind::ExternDecl(extern_decl) => { + syntax::StmtKind::ExternDecl(extern_decl) => { super::ast::StmtKind::ExternDecl(self.lower_extern(extern_decl)?) } - crate::ast::StmtKind::For(stmt) => { - super::ast::StmtKind::For(self.lower_for_stmt(stmt)?) - } - crate::ast::StmtKind::If(stmt) => super::ast::StmtKind::If(self.lower_if_stmt(stmt)?), - crate::ast::StmtKind::GateCall(stmt) => { + syntax::StmtKind::For(stmt) => super::ast::StmtKind::For(self.lower_for_stmt(stmt)?), + syntax::StmtKind::If(stmt) => super::ast::StmtKind::If(self.lower_if_stmt(stmt)?), + syntax::StmtKind::GateCall(stmt) => { super::ast::StmtKind::GateCall(self.lower_gate_call(stmt)?) } - crate::ast::StmtKind::GPhase(stmt) => { + syntax::StmtKind::GPhase(stmt) => { super::ast::StmtKind::GPhase(self.lower_gphase(stmt)?) } - crate::ast::StmtKind::Include(stmt) => { + syntax::StmtKind::Include(stmt) => { super::ast::StmtKind::Include(self.lower_include(stmt)?) } - crate::ast::StmtKind::IODeclaration(stmt) => { + syntax::StmtKind::IODeclaration(stmt) => { super::ast::StmtKind::IODeclaration(self.lower_io_decl(stmt)?) } - crate::ast::StmtKind::Measure(stmt) => { + syntax::StmtKind::Measure(stmt) => { super::ast::StmtKind::Measure(self.lower_measure(stmt)?) } - crate::ast::StmtKind::Pragma(stmt) => { + syntax::StmtKind::Pragma(stmt) => { super::ast::StmtKind::Pragma(self.lower_pragma(stmt)?) } - crate::ast::StmtKind::QuantumGateDefinition(stmt) => { + syntax::StmtKind::QuantumGateDefinition(stmt) => { super::ast::StmtKind::QuantumGateDefinition(self.lower_gate_def(stmt)?) } - crate::ast::StmtKind::QuantumDecl(stmt) => { + syntax::StmtKind::QuantumDecl(stmt) => { super::ast::StmtKind::QuantumDecl(self.lower_quantum_decl(stmt)?) } - crate::ast::StmtKind::Reset(stmt) => { - super::ast::StmtKind::Reset(self.lower_reset(stmt)?) - } - crate::ast::StmtKind::Return(stmt) => { + syntax::StmtKind::Reset(stmt) => super::ast::StmtKind::Reset(self.lower_reset(stmt)?), + syntax::StmtKind::Return(stmt) => { super::ast::StmtKind::Return(self.lower_return(stmt)?) } - crate::ast::StmtKind::Switch(stmt) => { + syntax::StmtKind::Switch(stmt) => { super::ast::StmtKind::Switch(self.lower_switch(stmt)?) } - crate::ast::StmtKind::WhileLoop(stmt) => { + syntax::StmtKind::WhileLoop(stmt) => { super::ast::StmtKind::WhileLoop(self.lower_while_loop(stmt)?) } - crate::ast::StmtKind::Err => { + syntax::StmtKind::Err => { self.push_semantic_error(SemanticErrorKind::UnexpectedParserError( "Unexpected error".to_string(), stmt.span, @@ -240,7 +235,7 @@ impl Lowerer { let annotations = self.lower_annotations(&stmt.annotations, &stmt.kind); Some(super::ast::Stmt { span: stmt.span, - annotations: list_from_iter(annotations), + annotations: syntax::list_from_iter(annotations), kind: Box::new(kind), }) } @@ -249,7 +244,7 @@ impl Lowerer { /// The sdg, tdg, crx, cry, crz, and ch are defined /// as their bare gates, and modifiers are applied /// when calling them. - fn define_stdgates(&mut self, include: &crate::ast::IncludeStmt) { + fn define_stdgates(&mut self, include: &syntax::IncludeStmt) { fn gate_symbol(name: &str, cargs: u32, qargs: u32) -> Symbol { Symbol { name: name.to_string(), @@ -330,10 +325,7 @@ impl Lowerer { WithSource::from_map(&self.source_map, offset_error) } - fn lower_alias( - &mut self, - alias: &crate::ast::AliasDeclStmt, - ) -> Option { + fn lower_alias(&mut self, alias: &syntax::AliasDeclStmt) -> Option { let name = get_identifier_name(&alias.ident); // alias statements do their types backwards, you read the right side // and assign it to the left side. @@ -343,9 +335,8 @@ impl Lowerer { .iter() .filter_map(|expr| self.lower_expr(expr)) .collect::>(); - // TODO: handle multiple rhs - // TODO: validate consistency of rhs types let first = rhs.first().expect("missing rhs"); + let symbol = Symbol { name: name.to_string(), ty: first.ty.clone(), @@ -353,10 +344,24 @@ impl Lowerer { span: alias.ident.span(), io_kind: IOKind::Default, }; - let Ok(symbol_id) = self.symbols.insert_symbol(symbol) else { - self.push_redefined_symbol_error(name, alias.span); + + let symbol_id = self.symbols.insert_symbol(symbol); + if symbol_id.is_err() { + self.push_redefined_symbol_error(name, alias.ident.span()); + } + + if rhs.iter().any(|expr| expr.ty != first.ty) { + let tys = rhs + .iter() + .map(ToString::to_string) + .collect::>() + .join(", "); + let kind = SemanticErrorKind::InconsistentTypesInAlias(tys, alias.span); + self.push_semantic_error(kind); return None; - }; + } + + let symbol_id = symbol_id.ok()?; if rhs.len() != alias.exprs.len() { // we failed @@ -365,54 +370,143 @@ impl Lowerer { Some(super::ast::AliasDeclStmt { span: alias.span, symbol_id, - exprs: list_from_iter(rhs), + exprs: syntax::list_from_iter(rhs), }) } - fn lower_assign(&mut self, assign: &crate::ast::AssignStmt) -> Option { - self.push_unimplemented_error_message("assign stmt", assign.span); - None + fn lower_assign(&mut self, stmt: &syntax::AssignStmt) -> Option { + if stmt.lhs.indices.is_empty() { + Some(super::ast::StmtKind::Assign( + self.lower_simple_assign_expr(&stmt.lhs.name, &stmt.rhs, stmt.span)?, + )) + } else { + Some(super::ast::StmtKind::IndexedAssign( + self.lower_indexed_assign_expr(&stmt.lhs, &stmt.rhs, stmt.span)?, + )) + } } - fn lower_assign_op( + fn lower_simple_assign_expr( &mut self, - assign_op: &crate::ast::AssignOpStmt, - ) -> Option { - self.push_unimplemented_error_message("assign op stmt", assign_op.span); - None + ident: &syntax::Ident, + rhs: &syntax::Expr, + span: Span, + ) -> Option { + let (lhs_symbol_id, lhs_symbol) = self.symbols.get_symbol_by_name(&ident.name)?; + let ty = lhs_symbol.ty.clone(); + if ty.is_const() { + let kind = + SemanticErrorKind::CannotUpdateConstVariable(ident.name.to_string(), ident.span); + self.push_semantic_error(kind); + // usually we'd return None here, but we'll continue to compile the rhs + // looking for more errors. There is nothing in this type of error that + // would prevent us from compiling the rhs. + } + + let rhs = self.lower_expr_with_target_type(Some(rhs), &ty, span)?; + Some(super::ast::AssignStmt { + symbold_id: lhs_symbol_id, + rhs, + span, + }) + } + + fn lower_indexed_assign_expr( + &mut self, + index_expr: &syntax::IndexedIdent, + rhs: &syntax::Expr, + span: Span, + ) -> Option { + let ident = index_expr.name.clone(); + let (lhs_symbol_id, lhs_symbol) = self.symbols.get_symbol_by_name(&ident.name)?; + let ty = lhs_symbol.ty.clone(); + if ty.is_const() { + let kind = + SemanticErrorKind::CannotUpdateConstVariable(ident.name.to_string(), ident.span); + self.push_semantic_error(kind); + // usually we'd return None here, but we'll continue to compile the rhs + // looking for more errors. There is nothing in this type of error that + // would prevent us from compiling the rhs. + return None; + } + + let lhs = self.lower_indexed_ident_expr(index_expr); + let rhs = self.lower_expr_with_target_type(Some(rhs), &ty, span); + let (lhs, rhs) = (lhs?, rhs?); + + Some(super::ast::IndexedAssignStmt { + symbold_id: lhs_symbol_id, + lhs, + rhs, + span, + }) } - fn lower_expr(&mut self, expr: &crate::ast::Expr) -> Option { + fn lower_assign_op(&mut self, stmt: &syntax::AssignOpStmt) -> Option { + let op = stmt.op; + let lhs = &stmt.lhs; + let rhs = &stmt.rhs; + + let ident = lhs.name.clone(); + + let (lhs_symbol_id, lhs_symbol) = self.symbols.get_symbol_by_name(&ident.name)?; + let ty = lhs_symbol.ty.clone(); + if ty.is_const() { + let kind = + SemanticErrorKind::CannotUpdateConstVariable(ident.name.to_string(), ident.span); + self.push_semantic_error(kind); + // usually we'd return None here, but we'll continue to compile the rhs + // looking for more errors. There is nothing in this type of error that + // would prevent us from compiling the rhs. + return None; + } + + let lhs = self.lower_indexed_ident_expr(lhs); + + let rhs = self.lower_expr(rhs); + let (lhs, rhs) = (lhs?, rhs?); + let rhs = self.lower_binary_op_expr(op, lhs.clone(), rhs, stmt.span)?; + let rhs = self.cast_expr_to_type(&ty, &rhs, stmt.span)?; + Some(super::ast::AssignOpStmt { + span: stmt.span, + symbold_id: lhs_symbol_id, + lhs, + rhs, + }) + } + + fn lower_expr(&mut self, expr: &syntax::Expr) -> Option { match &*expr.kind { - crate::ast::ExprKind::BinaryOp(_) => { - self.push_unimplemented_error_message("binary op expr", expr.span); - None - } - crate::ast::ExprKind::Cast(_) => { + syntax::ExprKind::BinaryOp(bin_op_expr) => { + let lhs = self.lower_expr(&bin_op_expr.lhs); + let rhs = self.lower_expr(&bin_op_expr.rhs); + // once we've compiled both sides, we can check if they failed + // and return None if they did so that the error is propagated + let (lhs, rhs) = (lhs?, rhs?); + self.lower_binary_op_expr(bin_op_expr.op, lhs, rhs, expr.span) + } + syntax::ExprKind::Cast(_) => { self.push_unimplemented_error_message("cast expr", expr.span); None } - crate::ast::ExprKind::Err => { + syntax::ExprKind::Err => { unreachable!("Err expr should not be lowered"); } - crate::ast::ExprKind::FunctionCall(_) => { + syntax::ExprKind::FunctionCall(_) => { self.push_unimplemented_error_message("function call expr", expr.span); None } - crate::ast::ExprKind::Ident(ident) => self.lower_ident_expr(ident), - crate::ast::ExprKind::IndexExpr(_) => { - self.push_unimplemented_error_message("index expr", expr.span); - None - } + syntax::ExprKind::Ident(ident) => self.lower_ident_expr(ident), + syntax::ExprKind::IndexExpr(expr) => self.lower_index_expr(expr), - crate::ast::ExprKind::Lit(lit) => self.lower_lit_expr(lit), + syntax::ExprKind::Lit(lit) => self.lower_lit_expr(lit), - crate::ast::ExprKind::Paren(expr) => self.lower_paren_expr(expr), - crate::ast::ExprKind::UnaryOp(expr) => self.lower_unary_op_expr(expr), + syntax::ExprKind::Paren(expr) => self.lower_paren_expr(expr), + syntax::ExprKind::UnaryOp(expr) => self.lower_unary_op_expr(expr), } } - fn lower_ident_expr(&mut self, ident: &crate::ast::Ident) -> Option { + fn lower_ident_expr(&mut self, ident: &syntax::Ident) -> Option { let name = ident.name.clone(); let Some((symbol_id, symbol)) = self.symbols.get_symbol_by_name(&name) else { self.push_missing_symbol_error(&name, ident.span); @@ -427,42 +521,42 @@ impl Lowerer { }) } - fn lower_lit_expr(&mut self, expr: &crate::ast::Lit) -> Option { + fn lower_lit_expr(&mut self, expr: &syntax::Lit) -> Option { let (kind, ty) = match &expr.kind { - crate::ast::LiteralKind::BigInt(value) => { + syntax::LiteralKind::BigInt(value) => { // todo: this case is only valid when there is an integer literal // that requires more than 64 bits to represent. We should probably // introduce a new type for this as openqasm promotion rules don't // cover this case as far as I know. (super::ast::LiteralKind::BigInt(value.clone()), Type::Err) } - crate::ast::LiteralKind::Bitstring(value, size) => ( + syntax::LiteralKind::Bitstring(value, size) => ( super::ast::LiteralKind::Bitstring(value.clone(), *size), Type::BitArray(super::types::ArrayDimensions::One(*size), true), ), - crate::ast::LiteralKind::Bool(value) => { + syntax::LiteralKind::Bool(value) => { (super::ast::LiteralKind::Bool(*value), Type::Bool(true)) } - crate::ast::LiteralKind::Int(value) => { + syntax::LiteralKind::Int(value) => { (super::ast::LiteralKind::Int(*value), Type::Int(None, true)) } - crate::ast::LiteralKind::Float(value) => ( + syntax::LiteralKind::Float(value) => ( super::ast::LiteralKind::Float(*value), Type::Float(None, true), ), - crate::ast::LiteralKind::Imaginary(value) => ( + syntax::LiteralKind::Imaginary(value) => ( super::ast::LiteralKind::Complex(0.0, *value), Type::Complex(None, true), ), - crate::ast::LiteralKind::String(_) => { + syntax::LiteralKind::String(_) => { self.push_unsupported_error_message("String literals", expr.span); return None; } - crate::ast::LiteralKind::Duration(_, _) => { + syntax::LiteralKind::Duration(_, _) => { self.push_unsupported_error_message("Duration literals", expr.span); return None; } - crate::ast::LiteralKind::Array(exprs) => { + syntax::LiteralKind::Array(exprs) => { // array literals are only valid in classical decals (const and mut) // and we have to know the expected type of the array in order to lower it // So we can't lower array literals in general. @@ -481,7 +575,7 @@ impl Lowerer { } ( - super::ast::LiteralKind::Array(list_from_iter(texprs)), + super::ast::LiteralKind::Array(syntax::list_from_iter(texprs)), Type::Err, ) } @@ -493,7 +587,7 @@ impl Lowerer { }) } - fn lower_paren_expr(&mut self, expr: &crate::ast::Expr) -> Option { + fn lower_paren_expr(&mut self, expr: &syntax::Expr) -> Option { let expr = self.lower_expr(expr)?; let span = expr.span; let ty = expr.ty.clone(); @@ -505,15 +599,15 @@ impl Lowerer { }) } - fn lower_unary_op_expr(&mut self, expr: &crate::ast::UnaryOpExpr) -> Option { + fn lower_unary_op_expr(&mut self, expr: &syntax::UnaryOpExpr) -> Option { match expr.op { - crate::ast::UnaryOp::Neg => { - if let crate::ast::ExprKind::Lit(lit) = expr.expr.kind.as_ref() { + syntax::UnaryOp::Neg => { + if let syntax::ExprKind::Lit(lit) = expr.expr.kind.as_ref() { self.lower_negated_literal_as_ty(lit, None, expr.expr.span) } else { let expr = self.lower_expr(&expr.expr)?; let ty = expr.ty.clone(); - if unary_op_can_be_applied_to_type(crate::ast::UnaryOp::Neg, &ty) { + if unary_op_can_be_applied_to_type(syntax::UnaryOp::Neg, &ty) { let span = expr.span; let unary = super::ast::UnaryOpExpr { op: super::ast::UnaryOp::Neg, @@ -534,10 +628,10 @@ impl Lowerer { } } } - crate::ast::UnaryOp::NotB => { + syntax::UnaryOp::NotB => { let expr = self.lower_expr(&expr.expr)?; let ty = expr.ty.clone(); - if unary_op_can_be_applied_to_type(crate::ast::UnaryOp::NotB, &ty) { + if unary_op_can_be_applied_to_type(syntax::UnaryOp::NotB, &ty) { let span = expr.span; let unary = super::ast::UnaryOpExpr { op: super::ast::UnaryOp::NotB, @@ -557,7 +651,7 @@ impl Lowerer { None } } - crate::ast::UnaryOp::NotL => { + syntax::UnaryOp::NotL => { // this is the only unary operator that tries to coerce the type // I can't find it in the spec, but when looking at existing code // it seems that the ! operator coerces to a bool if possible @@ -581,8 +675,8 @@ impl Lowerer { fn lower_annotations( &mut self, - annotations: &[Box], - kind: &crate::ast::StmtKind, + annotations: &[Box], + kind: &syntax::StmtKind, ) -> Vec { annotations .iter() @@ -592,8 +686,8 @@ impl Lowerer { fn lower_annotation( &mut self, - annotation: &crate::ast::Annotation, - kind: &crate::ast::StmtKind, + annotation: &syntax::Annotation, + kind: &syntax::StmtKind, ) -> super::ast::Annotation { if !matches!( annotation.identifier.to_string().as_str(), @@ -605,7 +699,7 @@ impl Lowerer { ); } - if let crate::ast::StmtKind::GateCall(_) = &kind { + if let syntax::StmtKind::GateCall(_) = &kind { self.push_unsupported_error_message( format!( "Annotation {} is only allowed on gate definitions.", @@ -691,29 +785,29 @@ impl Lowerer { } } - fn lower_barrier(&mut self, stmt: &crate::ast::BarrierStmt) -> Option { + fn lower_barrier(&mut self, stmt: &syntax::BarrierStmt) -> Option { self.push_unimplemented_error_message("barrier stmt", stmt.span); None } - fn lower_box(&mut self, stmt: &crate::ast::BoxStmt) -> Option { + fn lower_box(&mut self, stmt: &syntax::BoxStmt) -> Option { self.push_unimplemented_error_message("box stmt", stmt.span); None } - fn lower_break(&mut self, stmt: &crate::ast::BreakStmt) -> Option { + fn lower_break(&mut self, stmt: &syntax::BreakStmt) -> Option { self.push_unimplemented_error_message("break stmt", stmt.span); None } - fn lower_block(&mut self, stmt: &crate::ast::Block) -> Option { + fn lower_block(&mut self, stmt: &syntax::Block) -> Option { self.push_unimplemented_error_message("block stmt", stmt.span); None } fn lower_calibration( &mut self, - stmt: &crate::ast::CalibrationStmt, + stmt: &syntax::CalibrationStmt, ) -> Option { self.push_unimplemented_error_message("calibration stmt", stmt.span); None @@ -721,7 +815,7 @@ impl Lowerer { fn lower_calibration_grammar( &mut self, - stmt: &crate::ast::CalibrationGrammarStmt, + stmt: &syntax::CalibrationGrammarStmt, ) -> Option { self.push_unimplemented_error_message("calibration stmt", stmt.span); None @@ -729,7 +823,7 @@ impl Lowerer { fn lower_classical_decl( &mut self, - stmt: &crate::ast::ClassicalDeclarationStmt, + stmt: &syntax::ClassicalDeclarationStmt, ) -> Option { let is_const = false; // const decls are handled separately let ty = self.get_semantic_type_from_tydef(&stmt.ty, is_const)?; @@ -750,10 +844,10 @@ impl Lowerer { // process the symbol and init_expr gathering any errors let init_expr = match init_expr { Some(expr) => match expr { - crate::ast::ValueExpression::Expr(expr) => self + syntax::ValueExpression::Expr(expr) => self .lower_expr_with_target_type(Some(expr), &ty, stmt_span) .map(super::ast::ValueExpression::Expr), - crate::ast::ValueExpression::Measurement(measure_expr) => self + syntax::ValueExpression::Measurement(measure_expr) => self .lower_measure_expr_with_target_type(measure_expr, &ty, stmt_span) .map(super::ast::ValueExpression::Measurement), }, @@ -781,7 +875,7 @@ impl Lowerer { fn lower_const_decl( &mut self, - stmt: &crate::ast::ConstantDeclStmt, + stmt: &syntax::ConstantDeclStmt, ) -> Option { let is_const = true; let ty = self.get_semantic_type_from_tydef(&stmt.ty, is_const)?; @@ -816,90 +910,130 @@ impl Lowerer { }) } - fn lower_continue_stmt( - &mut self, - stmt: &crate::ast::ContinueStmt, - ) -> Option { + fn lower_continue_stmt(&mut self, stmt: &syntax::ContinueStmt) -> Option { self.push_unimplemented_error_message("continue stmt", stmt.span); None } - fn lower_def(&mut self, stmt: &crate::ast::DefStmt) -> Option { + fn lower_def(&mut self, stmt: &syntax::DefStmt) -> Option { self.push_unimplemented_error_message("def stmt", stmt.span); None } - fn lower_def_cal(&mut self, stmt: &crate::ast::DefCalStmt) -> Option { + fn lower_def_cal(&mut self, stmt: &syntax::DefCalStmt) -> Option { self.push_unimplemented_error_message("def cal stmt", stmt.span); None } - fn lower_delay(&mut self, stmt: &crate::ast::DelayStmt) -> Option { + fn lower_delay(&mut self, stmt: &syntax::DelayStmt) -> Option { self.push_unimplemented_error_message("delay stmt", stmt.span); None } - fn lower_end_stmt(&mut self, stmt: &crate::ast::EndStmt) -> Option { + fn lower_end_stmt(&mut self, stmt: &syntax::EndStmt) -> Option { self.push_unimplemented_error_message("end stmt", stmt.span); None } - fn lower_expr_stmt(&mut self, stmt: &crate::ast::ExprStmt) -> Option { - self.push_unimplemented_error_message("expr stmt", stmt.span); - None + fn lower_expr_stmt(&mut self, stmt: &syntax::ExprStmt) -> Option { + let expr = self.lower_expr(&stmt.expr)?; + Some(super::ast::ExprStmt { + span: stmt.span, + expr, + }) } - fn lower_extern(&mut self, stmt: &crate::ast::ExternDecl) -> Option { + fn lower_extern(&mut self, stmt: &syntax::ExternDecl) -> Option { self.push_unimplemented_error_message("extern stmt", stmt.span); None } - fn lower_for_stmt(&mut self, stmt: &crate::ast::ForStmt) -> Option { + fn lower_for_stmt(&mut self, stmt: &syntax::ForStmt) -> Option { self.push_unimplemented_error_message("for stmt", stmt.span); None } - fn lower_if_stmt(&mut self, stmt: &crate::ast::IfStmt) -> Option { + fn lower_if_stmt(&mut self, stmt: &syntax::IfStmt) -> Option { self.push_unimplemented_error_message("if stmt", stmt.span); None } - fn lower_gate_call(&mut self, stmt: &crate::ast::GateCall) -> Option { + fn lower_gate_call(&mut self, stmt: &syntax::GateCall) -> Option { self.push_unimplemented_error_message("gate call stmt", stmt.span); None } - fn lower_gphase(&mut self, stmt: &crate::ast::GPhase) -> Option { + fn lower_gphase(&mut self, stmt: &syntax::GPhase) -> Option { self.push_unimplemented_error_message("gphase stmt", stmt.span); None } - fn lower_include(&mut self, stmt: &crate::ast::IncludeStmt) -> Option { - self.push_unimplemented_error_message("include stmt", stmt.span); - None + /// This function is always a indication of a error. Either the + /// program is declaring include in a non-global scope or the + /// include is not handled in `self.lower_source` properly. + fn lower_include(&mut self, stmt: &syntax::IncludeStmt) -> Option { + // if we are not in the root we should not be able to include + if !self.symbols.is_current_scope_global() { + let name = stmt.filename.to_string(); + let kind = SemanticErrorKind::IncludeNotInGlobalScope(name, stmt.span); + self.push_semantic_error(kind); + return None; + } + // if we are at the root and we have an include, we should have + // already handled it and we are in an invalid state + panic!("Include should have been handled in lower_source") } - fn lower_io_decl( - &mut self, - stmt: &crate::ast::IODeclaration, - ) -> Option { - self.push_unimplemented_error_message("io decl stmt", stmt.span); - None + fn lower_io_decl(&mut self, stmt: &syntax::IODeclaration) -> Option { + let is_const = false; + let ty = self.get_semantic_type_from_tydef(&stmt.ty, is_const)?; + let io_kind = stmt.io_identifier.into(); + + let ty_span = stmt.ty.span(); + let stmt_span = stmt.span; + let name = stmt.ident.name.clone(); + let qsharp_ty = self.convert_semantic_type_to_qsharp_type(&ty.clone(), ty_span)?; + let symbol = Symbol { + name: name.to_string(), + ty: ty.clone(), + qsharp_ty, + span: stmt.ident.span, + io_kind, + }; + + let Ok(symbol_id) = self.symbols.insert_symbol(symbol) else { + self.push_redefined_symbol_error(&name, stmt.ident.span); + return None; + }; + + // if we have output, we need to assign a default value to declare the variable + // if we have input, we can keep return none as we would promote the variable + // to a parameter in the function signature once we generate the function + if io_kind == IOKind::Output { + let init_expr = self.get_default_value(&ty, stmt_span)?; + Some(super::ast::IODeclaration { + span: stmt_span, + symbol_id, + init_expr: Box::new(init_expr), + }) + } else { + None + } } - fn lower_measure(&mut self, stmt: &crate::ast::MeasureStmt) -> Option { + fn lower_measure(&mut self, stmt: &syntax::MeasureStmt) -> Option { self.push_unimplemented_error_message("measure stmt", stmt.span); None } - fn lower_pragma(&mut self, stmt: &crate::ast::Pragma) -> Option { + fn lower_pragma(&mut self, stmt: &syntax::Pragma) -> Option { self.push_unimplemented_error_message("pragma stmt", stmt.span); None } fn lower_gate_def( &mut self, - stmt: &crate::ast::QuantumGateDefinition, + stmt: &syntax::QuantumGateDefinition, ) -> Option { self.push_unimplemented_error_message("gate def stmt", stmt.span); None @@ -907,45 +1041,45 @@ impl Lowerer { fn lower_quantum_decl( &mut self, - stmt: &crate::ast::QubitDeclaration, + stmt: &syntax::QubitDeclaration, ) -> Option { self.push_unimplemented_error_message("qubit decl stmt", stmt.span); None } - fn lower_reset(&mut self, stmt: &crate::ast::ResetStmt) -> Option { + fn lower_reset(&mut self, stmt: &syntax::ResetStmt) -> Option { self.push_unimplemented_error_message("reset stmt", stmt.span); None } - fn lower_return(&mut self, stmt: &crate::ast::ReturnStmt) -> Option { + fn lower_return(&mut self, stmt: &syntax::ReturnStmt) -> Option { self.push_unimplemented_error_message("return stmt", stmt.span); None } - fn lower_switch(&mut self, stmt: &crate::ast::SwitchStmt) -> Option { + fn lower_switch(&mut self, stmt: &syntax::SwitchStmt) -> Option { self.push_unimplemented_error_message("switch stmt", stmt.span); None } - fn lower_while_loop(&mut self, stmt: &crate::ast::WhileLoop) -> Option { + fn lower_while_loop(&mut self, stmt: &syntax::WhileLoop) -> Option { self.push_unimplemented_error_message("while loop stmt", stmt.span); None } fn get_semantic_type_from_tydef( &mut self, - scalar_ty: &crate::ast::TypeDef, + scalar_ty: &syntax::TypeDef, is_const: bool, ) -> Option { match scalar_ty { - crate::ast::TypeDef::Scalar(scalar_type) => { + syntax::TypeDef::Scalar(scalar_type) => { self.get_semantic_type_from_scalar_ty(scalar_type, is_const) } - crate::ast::TypeDef::Array(array_type) => { + syntax::TypeDef::Array(array_type) => { self.get_semantic_type_from_array_ty(array_type, is_const) } - crate::ast::TypeDef::ArrayReference(array_reference_type) => { + syntax::TypeDef::ArrayReference(array_reference_type) => { self.get_semantic_type_from_array_reference_ty(array_reference_type, is_const) } } @@ -953,9 +1087,9 @@ impl Lowerer { /// designators are positive integer literals when used /// in the context of a type definition. - fn get_size_designator_from_expr(&mut self, expr: &crate::ast::Expr) -> Option { - if let crate::ast::ExprKind::Lit(lit) = expr.kind.as_ref() { - if let crate::ast::LiteralKind::Int(value) = lit.kind { + fn get_size_designator_from_expr(&mut self, expr: &syntax::Expr) -> Option { + if let syntax::ExprKind::Lit(lit) = expr.kind.as_ref() { + if let syntax::LiteralKind::Int(value) = lit.kind { if value > 0 { if let Ok(value) = u32::try_from(value) { Some(value) @@ -985,39 +1119,39 @@ impl Lowerer { fn get_semantic_type_from_scalar_ty( &mut self, - scalar_ty: &crate::ast::ScalarType, + scalar_ty: &syntax::ScalarType, is_const: bool, ) -> Option { match &scalar_ty.kind { - crate::ast::ScalarTypeKind::Bit(bit_type) => match &bit_type.size { + syntax::ScalarTypeKind::Bit(bit_type) => match &bit_type.size { Some(size) => Some(crate::semantic::types::Type::BitArray( super::types::ArrayDimensions::One(self.get_size_designator_from_expr(size)?), is_const, )), None => Some(crate::semantic::types::Type::Bit(is_const)), }, - crate::ast::ScalarTypeKind::Int(int_type) => match &int_type.size { + syntax::ScalarTypeKind::Int(int_type) => match &int_type.size { Some(size) => Some(crate::semantic::types::Type::Int( Some(self.get_size_designator_from_expr(size)?), is_const, )), None => Some(crate::semantic::types::Type::Int(None, is_const)), }, - crate::ast::ScalarTypeKind::UInt(uint_type) => match &uint_type.size { + syntax::ScalarTypeKind::UInt(uint_type) => match &uint_type.size { Some(size) => Some(crate::semantic::types::Type::UInt( Some(self.get_size_designator_from_expr(size)?), is_const, )), None => Some(crate::semantic::types::Type::UInt(None, is_const)), }, - crate::ast::ScalarTypeKind::Float(float_type) => match &float_type.size { + syntax::ScalarTypeKind::Float(float_type) => match &float_type.size { Some(size) => Some(crate::semantic::types::Type::Float( Some(self.get_size_designator_from_expr(size)?), is_const, )), None => Some(crate::semantic::types::Type::Float(None, is_const)), }, - crate::ast::ScalarTypeKind::Complex(complex_type) => match &complex_type.base_size { + syntax::ScalarTypeKind::Complex(complex_type) => match &complex_type.base_size { Some(float_type) => match &float_type.size { Some(size) => Some(crate::semantic::types::Type::Complex( Some(self.get_size_designator_from_expr(size)?), @@ -1027,29 +1161,27 @@ impl Lowerer { }, None => Some(crate::semantic::types::Type::Complex(None, is_const)), }, - crate::ast::ScalarTypeKind::Angle(angle_type) => match &angle_type.size { + syntax::ScalarTypeKind::Angle(angle_type) => match &angle_type.size { Some(size) => Some(crate::semantic::types::Type::Angle( Some(self.get_size_designator_from_expr(size)?), is_const, )), None => Some(crate::semantic::types::Type::Angle(None, is_const)), }, - crate::ast::ScalarTypeKind::BoolType => { - Some(crate::semantic::types::Type::Bool(is_const)) - } - crate::ast::ScalarTypeKind::Duration => { + syntax::ScalarTypeKind::BoolType => Some(crate::semantic::types::Type::Bool(is_const)), + syntax::ScalarTypeKind::Duration => { Some(crate::semantic::types::Type::Duration(is_const)) } - crate::ast::ScalarTypeKind::Stretch => { + syntax::ScalarTypeKind::Stretch => { Some(crate::semantic::types::Type::Stretch(is_const)) } - crate::ast::ScalarTypeKind::Err => Some(crate::semantic::types::Type::Err), + syntax::ScalarTypeKind::Err => Some(crate::semantic::types::Type::Err), } } fn get_semantic_type_from_array_ty( &mut self, - array_ty: &crate::ast::ArrayType, + array_ty: &syntax::ArrayType, _is_const: bool, ) -> Option { self.push_unimplemented_error_message("semantic type from array type", array_ty.span); @@ -1058,7 +1190,7 @@ impl Lowerer { fn get_semantic_type_from_array_reference_ty( &mut self, - array_ref_ty: &crate::ast::ArrayReferenceType, + array_ref_ty: &syntax::ArrayReferenceType, _is_const: bool, ) -> Option { self.push_unimplemented_error_message( @@ -1069,7 +1201,7 @@ impl Lowerer { } fn lower_expr_with_target_type( &mut self, - expr: Option<&crate::ast::Expr>, + expr: Option<&syntax::Expr>, ty: &Type, span: Span, ) -> Option { @@ -1113,7 +1245,7 @@ impl Lowerer { fn lower_measure_expr_with_target_type( &mut self, - _expr: &crate::ast::MeasureExpr, + _expr: &syntax::MeasureExpr, _ty: &Type, span: Span, ) -> Option { @@ -1219,11 +1351,12 @@ impl Lowerer { if types_equal_except_const(ty, &rhs.ty) { // literals are always const, so we can safely return // the const ty - return Some(super::ast::Expr { - span: rhs.span, - kind: rhs.kind.clone(), - ty: ty.as_const(), - }); + if rhs.ty.is_const() { + return Some(rhs.clone()); + } + // the lsh is supposed to be const but is being initialized + // to a non-const value, we can't allow this + return None; } assert!(can_cast_literal(ty, &rhs.ty) || can_cast_literal_with_value_knowledge(ty, kind)); let lhs_ty = ty.clone(); @@ -1320,7 +1453,7 @@ impl Lowerer { } None } - (Type::Float(..), Type::Float(..)) => { + (Type::Angle(..) | Type::Float(..), Type::Float(..)) => { if let super::ast::LiteralKind::Float(value) = kind { return Some(super::ast::Expr { span, @@ -1448,55 +1581,55 @@ impl Lowerer { // based on other qasm implementations. fn lower_negated_literal_as_ty( &mut self, - lit: &crate::ast::Lit, + lit: &syntax::Lit, target_ty: Option, span: Span, ) -> Option { let (kind, ty) = (match &lit.kind { - crate::ast::LiteralKind::Float(value) => Some(( + syntax::LiteralKind::Float(value) => Some(( super::ast::LiteralKind::Float(-value), Type::Float(None, true), )), - crate::ast::LiteralKind::Imaginary(value) => Some(( + syntax::LiteralKind::Imaginary(value) => Some(( super::ast::LiteralKind::Complex(0.0, -value), Type::Complex(None, true), )), - crate::ast::LiteralKind::Int(value) => { + syntax::LiteralKind::Int(value) => { Some((super::ast::LiteralKind::Int(-value), Type::Int(None, true))) } - crate::ast::LiteralKind::BigInt(value) => { + syntax::LiteralKind::BigInt(value) => { let value = BigInt::from(-1) * value; Some(( super::ast::LiteralKind::BigInt(value), Type::Int(None, true), )) } - crate::ast::LiteralKind::Duration(value, time_unit) => { + syntax::LiteralKind::Duration(value, time_unit) => { let unit = match time_unit { - crate::ast::TimeUnit::Dt => super::ast::TimeUnit::Dt, - crate::ast::TimeUnit::Ms => super::ast::TimeUnit::Ms, - crate::ast::TimeUnit::Ns => super::ast::TimeUnit::Ns, - crate::ast::TimeUnit::S => super::ast::TimeUnit::S, - crate::ast::TimeUnit::Us => super::ast::TimeUnit::Us, + syntax::TimeUnit::Dt => super::ast::TimeUnit::Dt, + syntax::TimeUnit::Ms => super::ast::TimeUnit::Ms, + syntax::TimeUnit::Ns => super::ast::TimeUnit::Ns, + syntax::TimeUnit::S => super::ast::TimeUnit::S, + syntax::TimeUnit::Us => super::ast::TimeUnit::Us, }; Some(( super::ast::LiteralKind::Duration(-value, unit), Type::Duration(true), )) } - crate::ast::LiteralKind::Array(_) => { + syntax::LiteralKind::Array(_) => { self.push_unsupported_error_message("negated array literal expressions", span); None } - crate::ast::LiteralKind::Bitstring(_, _) => { + syntax::LiteralKind::Bitstring(_, _) => { self.push_unsupported_error_message("negated bitstring literal expressions", span); None } - crate::ast::LiteralKind::Bool(_) => { + syntax::LiteralKind::Bool(_) => { self.push_unsupported_error_message("negated bool literal expressions", span); None } - crate::ast::LiteralKind::String(_) => { + syntax::LiteralKind::String(_) => { self.push_unsupported_error_message("negated string literal expressions", span); None } @@ -1516,12 +1649,12 @@ impl Lowerer { fn cast_expr_to_type( &mut self, ty: &Type, - rhs: &super::ast::Expr, + expr: &super::ast::Expr, span: Span, ) -> Option { - let cast_expr = self.try_cast_expr_to_type(ty, rhs, span); + let cast_expr = self.try_cast_expr_to_type(ty, expr, span); if cast_expr.is_none() { - let rhs_ty_name = format!("{:?}", rhs.ty); + let rhs_ty_name = format!("{:?}", expr.ty); let lhs_ty_name = format!("{ty:?}"); let kind = SemanticErrorKind::CannotCast(rhs_ty_name, lhs_ty_name, span); self.push_semantic_error(kind); @@ -1532,18 +1665,18 @@ impl Lowerer { fn try_cast_expr_to_type( &mut self, ty: &Type, - rhs: &super::ast::Expr, + expr: &super::ast::Expr, span: Span, ) -> Option { - if *ty == rhs.ty { + if *ty == expr.ty { // Base case, we shouldn't have gotten here // but if we did, we can just return the rhs - return Some(rhs.clone()); + return Some(expr.clone()); } - if types_equal_except_const(ty, &rhs.ty) { - if rhs.ty.is_const() { + if types_equal_except_const(ty, &expr.ty) { + if expr.ty.is_const() { // lhs isn't const, but rhs is, we can just return the rhs - return Some(rhs.clone()); + return Some(expr.clone()); } // the lsh is supposed to be const but is being initialized // to a non-const value, we can't allow this @@ -1552,21 +1685,22 @@ impl Lowerer { // if the target type is wider, we can try to relax the rhs type // We only do this for float and complex. Int types // require extra handling for BigInts - match (ty, &rhs.ty) { - (Type::Float(w1, _), Type::Float(w2, _)) + match (ty, &expr.ty) { + (Type::Angle(w1, _), Type::Angle(w2, _)) + | (Type::Float(w1, _), Type::Float(w2, _)) | (Type::Complex(w1, _), Type::Complex(w2, _)) => { if w1.is_none() && w2.is_some() { return Some(super::ast::Expr { - span: rhs.span, - kind: rhs.kind.clone(), + span: expr.span, + kind: expr.kind.clone(), ty: ty.clone(), }); } if *w1 >= *w2 { return Some(super::ast::Expr { - span: rhs.span, - kind: rhs.kind.clone(), + span: expr.span, + kind: expr.kind.clone(), ty: ty.clone(), }); } @@ -1576,14 +1710,14 @@ impl Lowerer { // Casting of literals is handled elsewhere. This is for casting expressions // which cannot be bypassed and must be handled by built-in Q# casts from // the standard library. - match &rhs.ty { - Type::Angle(_, _) => self.cast_angle_expr_to_type(ty, rhs, span), - Type::Bit(_) => self.cast_bit_expr_to_type(ty, rhs, span), - Type::Bool(_) => Self::cast_bool_expr_to_type(ty, rhs), - Type::Complex(_, _) => cast_complex_expr_to_type(ty, rhs), - Type::Float(_, _) => self.cast_float_expr_to_type(ty, rhs, span), - Type::Int(_, _) | Type::UInt(_, _) => Self::cast_int_expr_to_type(ty, rhs), - Type::BitArray(dims, _) => Self::cast_bitarray_expr_to_type(dims, ty, rhs), + match &expr.ty { + Type::Angle(_, _) => Self::cast_angle_expr_to_type(ty, expr), + Type::Bit(_) => self.cast_bit_expr_to_type(ty, expr, span), + Type::Bool(_) => Self::cast_bool_expr_to_type(ty, expr), + Type::Complex(_, _) => cast_complex_expr_to_type(ty, expr), + Type::Float(_, _) => Self::cast_float_expr_to_type(ty, expr), + Type::Int(_, _) | Type::UInt(_, _) => Self::cast_int_expr_to_type(ty, expr), + Type::BitArray(dims, _) => Self::cast_bitarray_expr_to_type(dims, ty, expr), _ => None, } } @@ -1595,25 +1729,12 @@ impl Lowerer { /// +----------------+-------+-----+------+-------+-------+-----+----------+-------+ /// | angle | Yes | No | No | No | - | Yes | No | No | /// +----------------+-------+-----+------+-------+-------+-----+----------+-------+ - fn cast_angle_expr_to_type( - &mut self, - ty: &Type, - rhs: &super::ast::Expr, - span: Span, - ) -> Option { - assert!(matches!(rhs.ty, Type::Bit(..))); + fn cast_angle_expr_to_type(ty: &Type, rhs: &super::ast::Expr) -> Option { + assert!(matches!(rhs.ty, Type::Angle(..))); match ty { - Type::Bit(..) => { - let msg = "Cast angle to bit"; - self.push_unimplemented_error_message(msg, span); - None - } - Type::Bool(..) => { - let msg = "Cast angle to bool"; - self.push_unimplemented_error_message(msg, span); - None + Type::Bit(..) | Type::Bool(..) => { + Some(wrap_expr_in_implicit_cast_expr(ty.clone(), rhs.clone())) } - _ => None, } } @@ -1663,23 +1784,17 @@ impl Lowerer { /// +----------------+-------+-----+------+-------+-------+-----+----------+-------+ /// /// Additional cast to complex - fn cast_float_expr_to_type( - &mut self, - ty: &Type, - rhs: &super::ast::Expr, - span: Span, - ) -> Option { + fn cast_float_expr_to_type(ty: &Type, rhs: &super::ast::Expr) -> Option { assert!(matches!(rhs.ty, Type::Float(..))); match ty { - &Type::Complex(_, _) | &Type::Int(_, _) | &Type::UInt(_, _) | &Type::Bool(_) => { + &Type::Angle(..) + | &Type::Complex(_, _) + | &Type::Int(_, _) + | &Type::UInt(_, _) + | &Type::Bool(_) => { // this will eventually be a call into Complex(expr, 0.0) Some(wrap_expr_in_implicit_cast_expr(ty.clone(), rhs.clone())) } - &Type::Angle(..) => { - let msg = "Cast float to angle"; - self.push_unimplemented_error_message(msg, span); - None - } _ => None, } } @@ -1749,11 +1864,418 @@ impl Lowerer { None } } + + #[allow(clippy::too_many_lines)] + fn lower_binary_op_expr( + &mut self, + op: syntax::BinOp, + lhs: super::ast::Expr, + rhs: super::ast::Expr, + span: Span, + ) -> Option { + if lhs.ty.is_quantum() { + let kind = SemanticErrorKind::QuantumTypesInBinaryExpression(lhs.span); + self.push_semantic_error(kind); + } + + if rhs.ty.is_quantum() { + let kind = SemanticErrorKind::QuantumTypesInBinaryExpression(rhs.span); + self.push_semantic_error(kind); + } + + let left_type = lhs.ty.clone(); + let right_type = rhs.ty.clone(); + + if Self::binop_requires_bitwise_conversion(op, &left_type) { + // if the operator requires bitwise conversion, we need to determine + // what size of UInt to promote to. If we can't promote to a UInt, we + // push an error and return None. + let (ty, lhs_uint_promotion, rhs_uint_promotion) = + promote_to_uint_ty(&left_type, &right_type); + let Some(ty) = ty else { + let target_ty = Type::UInt(None, false); + if lhs_uint_promotion.is_none() { + let target_str: String = format!("{target_ty:?}"); + let kind = SemanticErrorKind::CannotCast( + format!("{left_type:?}"), + target_str, + lhs.span, + ); + self.push_semantic_error(kind); + } + if rhs_uint_promotion.is_none() { + let target_str: String = format!("{target_ty:?}"); + let kind = SemanticErrorKind::CannotCast( + format!("{right_type:?}"), + target_str, + rhs.span, + ); + self.push_semantic_error(kind); + } + return None; + }; + // Now that we know the effective Uint type, we can cast the lhs and rhs + // so that operations can be performed on them. + let new_lhs = self.cast_expr_to_type(&ty, &lhs, lhs.span)?; + // only cast the rhs if the operator requires symmetric conversion + let new_rhs = if Self::binop_requires_bitwise_symmetric_conversion(op) { + self.cast_expr_to_type(&ty, &rhs, rhs.span)? + } else { + rhs + }; + + let bin_expr = super::ast::BinaryOpExpr { + lhs: new_lhs, + rhs: new_rhs, + op: op.into(), + }; + let kind = super::ast::ExprKind::BinaryOp(bin_expr); + let expr = super::ast::Expr { + span, + kind: Box::new(kind), + ty, + }; + + let final_expr = self.cast_expr_to_type(&left_type, &expr, span)?; + return Some(final_expr); + } + + // for int, uint, float, and complex, the lesser of the two types is cast to + // the greater of the two. Within each level of complex and float, types with + // greater width are greater than types with lower width. + // complex > float > int/uint + // Q# has built-in functions: IntAsDouble, IntAsBigInt to handle two cases. + // If the width of a float is greater than 64, we can't represent it as a double. + + let (lhs, rhs, ty) = if matches!(op, syntax::BinOp::AndL | syntax::BinOp::OrL) { + let ty = Type::Bool(false); + let new_lhs = self.cast_expr_to_type(&ty, &lhs, lhs.span)?; + let new_rhs = self.cast_expr_to_type(&ty, &rhs, rhs.span)?; + (new_lhs, new_rhs, ty) + } else if binop_requires_int_conversion_for_type(op, &left_type, &rhs.ty) { + let ty = Type::Int(None, false); + let new_lhs = self.cast_expr_to_type(&ty, &lhs, lhs.span)?; + let new_rhs = self.cast_expr_to_type(&ty, &rhs, lhs.span)?; + (new_lhs, new_rhs, ty) + } else if requires_symmetric_conversion(op) { + let promoted_type = try_promote_with_casting(&left_type, &right_type); + let new_left = if promoted_type == left_type { + lhs + } else { + match &lhs.kind.as_ref() { + super::ast::ExprKind::Lit(kind) => { + if can_cast_literal(&promoted_type, &left_type) + || can_cast_literal_with_value_knowledge(&promoted_type, kind) + { + self.coerce_literal_expr_to_type(&promoted_type, &lhs, kind)? + } else { + self.cast_expr_to_type(&promoted_type, &lhs, lhs.span)? + } + } + _ => self.cast_expr_to_type(&promoted_type, &lhs, lhs.span)?, + } + }; + let new_right = if promoted_type == right_type { + rhs + } else { + match &rhs.kind.as_ref() { + super::ast::ExprKind::Lit(kind) => { + if can_cast_literal(&promoted_type, &right_type) + || can_cast_literal_with_value_knowledge(&promoted_type, kind) + { + self.coerce_literal_expr_to_type(&promoted_type, &rhs, kind)? + } else { + self.cast_expr_to_type(&promoted_type, &rhs, rhs.span)? + } + } + _ => self.cast_expr_to_type(&promoted_type, &rhs, rhs.span)?, + } + }; + (new_left, new_right, promoted_type) + } else if binop_requires_symmetric_int_conversion(op) { + let ty = Type::Int(None, false); + let new_rhs = self.cast_expr_to_type(&ty, &rhs, rhs.span)?; + (lhs, new_rhs, left_type) + } else { + (lhs, rhs, left_type) + }; + + // now that we have the lhs and rhs expressions, we can create the binary expression + // but we need to check if the chosen operator is supported by the types after + // promotion and conversion. + + let expr = if matches!(ty, Type::Complex(..)) { + if is_complex_binop_supported(op) { + // TODO: How do we handle complex binary expressions? + // this is going to be a call to a built-in function + // that doesn't map to qasm def semantics + self.push_unimplemented_error_message("complex binary exprs", span); + None + } else { + let kind = SemanticErrorKind::OperatorNotSupportedForTypes( + format!("{op:?}"), + format!("{ty:?}"), + format!("{ty:?}"), + span, + ); + self.push_semantic_error(kind); + None + } + } else { + let bin_expr = super::ast::BinaryOpExpr { + op: op.into(), + lhs, + rhs, + }; + let kind = super::ast::ExprKind::BinaryOp(bin_expr); + let expr = super::ast::Expr { + span, + kind: Box::new(kind), + ty: ty.clone(), + }; + Some(expr) + }; + + let ty = match op.into() { + super::ast::BinOp::AndL + | super::ast::BinOp::Eq + | super::ast::BinOp::Gt + | super::ast::BinOp::Gte + | super::ast::BinOp::Lt + | super::ast::BinOp::Lte + | super::ast::BinOp::Neq + | super::ast::BinOp::OrL => Type::Bool(false), + _ => ty, + }; + let mut expr = expr?; + expr.ty = ty; + Some(expr) + } + + fn binop_requires_bitwise_conversion(op: syntax::BinOp, left_type: &Type) -> bool { + if (matches!( + op, + syntax::BinOp::AndB | syntax::BinOp::OrB | syntax::BinOp::XorB + ) && matches!( + left_type, + Type::Bit(..) + | Type::UInt(..) + | Type::Angle(..) + | Type::BitArray(ArrayDimensions::One(_), _) + )) { + return true; + } + if (matches!(op, syntax::BinOp::Shl | syntax::BinOp::Shr) + && matches!( + left_type, + Type::Bit(..) + | Type::UInt(..) + | Type::Angle(..) + | Type::BitArray(ArrayDimensions::One(_), _) + )) + { + return true; + } + false + } + + fn binop_requires_bitwise_symmetric_conversion(op: syntax::BinOp) -> bool { + matches!( + op, + syntax::BinOp::AndB | syntax::BinOp::OrB | syntax::BinOp::XorB + ) + } + + // TODO: which these are parsed as different types, they are effectively the same + fn lower_index_element( + &mut self, + index: &syntax::IndexElement, + ) -> Option { + match index { + syntax::IndexElement::DiscreteSet(set) => { + let items = set + .values + .iter() + .filter_map(|expr| self.lower_expr(expr)) + .collect::>(); + if set.values.len() == items.len() { + return Some(super::ast::IndexElement::DiscreteSet( + super::ast::DiscreteSet { + span: set.span, + values: syntax::list_from_iter(items), + }, + )); + } + let kind = SemanticErrorKind::FailedToCompileExpressionList(set.span); + self.push_semantic_error(kind); + None + } + syntax::IndexElement::IndexSet(set) => { + let items = set + .values + .iter() + .filter_map(|expr| self.lower_index_set_item(expr)) + .collect::>(); + if set.values.len() == items.len() { + return Some(super::ast::IndexElement::IndexSet(super::ast::IndexSet { + span: set.span, + values: syntax::list_from_iter(items), + })); + } + let kind = SemanticErrorKind::FailedToCompileExpressionList(set.span); + self.push_semantic_error(kind); + None + } + } + } + + fn lower_index_set_item( + &mut self, + item: &syntax::IndexSetItem, + ) -> Option { + let item = match item { + syntax::IndexSetItem::RangeDefinition(range_definition) => { + super::ast::IndexSetItem::RangeDefinition( + self.lower_range_definition(range_definition)?, + ) + } + syntax::IndexSetItem::Expr(expr) => { + super::ast::IndexSetItem::Expr(self.lower_expr(expr)?) + } + syntax::IndexSetItem::Err => { + unreachable!("IndexSetItem::Err should have been handled") + } + }; + Some(item) + } + + fn lower_range_definition( + &mut self, + range_definition: &syntax::RangeDefinition, + ) -> Option { + let mut failed = false; + let start = range_definition.start.as_ref().map(|e| { + let expr = self.lower_expr(e); + if expr.is_none() { + failed = true; + } + expr + }); + let step = range_definition.step.as_ref().map(|e| { + let expr = self.lower_expr(e); + if expr.is_none() { + failed = true; + } + expr + }); + let end = range_definition.end.as_ref().map(|e| { + let expr = self.lower_expr(e); + if expr.is_none() { + failed = true; + } + expr + }); + if failed { + return None; + } + Some(super::ast::RangeDefinition { + span: range_definition.span, + start: start.map(|s| s.expect("start should be Some")), + step: step.map(|s| s.expect("step should be Some")), + end: end.map(|e| e.expect("end should be Some")), + }) + } + + fn lower_index_expr(&mut self, expr: &syntax::IndexExpr) -> Option { + let collection = self.lower_expr(&expr.collection); + let index = self.lower_index_element(&expr.index); + let collection = collection?; + let index = index?; + + let indexed_ty = self.get_indexed_type(&collection.ty, expr.span, 1)?; + + Some(super::ast::Expr { + span: expr.span, + kind: Box::new(super::ast::ExprKind::IndexExpr(super::ast::IndexExpr { + span: expr.span, + collection, + index, + })), + ty: indexed_ty, + }) + } + + fn get_indexed_type( + &mut self, + ty: &Type, + span: Span, + num_indices: usize, + ) -> Option { + if !ty.is_array() { + let kind = SemanticErrorKind::CannotIndexType(format!("{ty:?}"), span); + self.push_semantic_error(kind); + return None; + } + + if num_indices > ty.num_dims() { + let kind = SemanticErrorKind::TooManyIndices(span); + self.push_semantic_error(kind); + return None; + } + + let mut indexed_ty = ty.clone(); + for _ in 0..num_indices { + let Some(ty) = indexed_ty.get_indexed_type() else { + // we failed to get the indexed type, unknown error + // we should have caught this earlier with the two checks above + let kind = SemanticErrorKind::CannotIndexType(format!("{ty:?}"), span); + self.push_semantic_error(kind); + return None; + }; + indexed_ty = ty; + } + Some(indexed_ty) + } + + fn lower_indexed_ident_expr( + &mut self, + indexed_ident: &syntax::IndexedIdent, + ) -> Option { + let ident = indexed_ident.name.clone(); + + let indices = indexed_ident + .indices + .iter() + .filter_map(|index| self.lower_index_element(index)) + .collect::>(); + + let (symbol_id, lhs_symbol) = self.symbols.get_symbol_by_name(&ident.name)?; + let ty = lhs_symbol.ty.clone(); + // use the supplied number of indicies rathar than the number of indicies we lowered + let ty = self.get_indexed_type(&ty, indexed_ident.span, indexed_ident.indices.len())?; + + if indices.len() != indexed_ident.indices.len() { + // we failed to lower all the indices, error was already pushed + return None; + } + + Some(super::ast::Expr { + span: indexed_ident.span, + kind: Box::new(super::ast::ExprKind::IndexedIdentifier( + super::ast::IndexedIdent { + span: indexed_ident.span, + symbol_id, + indices: syntax::list_from_iter(indices), + }, + )), + ty, + }) + } } fn wrap_expr_in_implicit_cast_expr(ty: Type, rhs: super::ast::Expr) -> super::ast::Expr { super::ast::Expr { - span: Span::default(), + span: rhs.span, kind: Box::new(super::ast::ExprKind::Cast(super::ast::Cast { span: Span::default(), expr: rhs, @@ -1791,9 +2313,9 @@ fn cast_complex_expr_to_type(ty: &Type, rhs: &super::ast::Expr) -> Option std::rc::Rc { +fn get_identifier_name(identifier: &syntax::Identifier) -> std::rc::Rc { match identifier { - crate::ast::Identifier::Ident(ident) => ident.name.clone(), - crate::ast::Identifier::IndexedIdent(ident) => ident.name.name.clone(), + syntax::Identifier::Ident(ident) => ident.name.clone(), + syntax::Identifier::IndexedIdent(ident) => ident.name.name.clone(), } } diff --git a/compiler/qsc_qasm3/src/semantic/symbols.rs b/compiler/qsc_qasm3/src/semantic/symbols.rs index a9ebfe99a2..da03471c63 100644 --- a/compiler/qsc_qasm3/src/semantic/symbols.rs +++ b/compiler/qsc_qasm3/src/semantic/symbols.rs @@ -98,7 +98,7 @@ pub struct Symbol { impl std::fmt::Display for Symbol { fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { - use crate::ast::display_utils; + use crate::parser::ast::display_utils; display_utils::writeln_header(f, "Symbol", self.span)?; display_utils::writeln_field(f, "name", &self.name)?; display_utils::writeln_field(f, "type", &self.ty)?; @@ -131,19 +131,14 @@ pub enum SymbolError { /// The default I/O kind means no explicit kind was part of the decl. /// There is a specific statement for io decls which sets the I/O kind appropriately. /// This is used to determine the input and output symbols in the program. -#[derive(Debug, Clone, PartialEq, Eq)] +#[derive(Copy, Default, Debug, Clone, PartialEq, Eq)] pub enum IOKind { + #[default] Default, Input, Output, } -impl Default for IOKind { - fn default() -> Self { - Self::Default - } -} - impl std::fmt::Display for IOKind { fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { match self { diff --git a/compiler/qsc_qasm3/src/semantic/tests.rs b/compiler/qsc_qasm3/src/semantic/tests.rs index 8aa9e00e81..490a573561 100644 --- a/compiler/qsc_qasm3/src/semantic/tests.rs +++ b/compiler/qsc_qasm3/src/semantic/tests.rs @@ -1,8 +1,11 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. +pub mod assignment; pub mod decls; +pub mod expression; + use std::path::Path; use std::sync::Arc; @@ -89,15 +92,18 @@ pub(super) fn check_classical_decls(input: &str, expect: &Expect) { .map(|stmt| stmt.kind.as_ref().clone()) .collect::>(); let mut value = String::new(); - for kind in kinds { - let super::ast::StmtKind::ClassicalDecl(decl) = kind else { - panic!("expected classical declaration statement"); + for kind in &kinds { + let (symbol_id, str) = match kind { + super::ast::StmtKind::ClassicalDecl(decl) => (decl.symbol_id, decl.to_string()), + super::ast::StmtKind::IODeclaration(decl) => (decl.symbol_id, decl.to_string()), + super::ast::StmtKind::Assign(stmt) => (stmt.symbold_id, stmt.to_string()), + super::ast::StmtKind::AssignOp(stmt) => (stmt.symbold_id, stmt.to_string()), + _ => panic!("unsupported stmt type {kind}"), }; - value.push_str(&decl.to_string()); + + value.push_str(&str); value.push('\n'); - let symbol = s - .get_symbol_by_id(decl.symbol_id) - .expect("getting symbol by id"); + let symbol = s.get_symbol_by_id(symbol_id).expect("getting symbol by id"); value.push_str(&format!("[{}] {}", symbol.0, symbol.1)); value.push('\n'); } diff --git a/compiler/qsc_qasm3/src/semantic/tests/assignment.rs b/compiler/qsc_qasm3/src/semantic/tests/assignment.rs new file mode 100644 index 0000000000..bdf65084d2 --- /dev/null +++ b/compiler/qsc_qasm3/src/semantic/tests/assignment.rs @@ -0,0 +1,18 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +use expect_test::expect; + +use super::check; + +#[test] +#[ignore = "Not yet implemented"] +fn too_many_indicies_in_indexed_assignment() { + check( + r#" + array[float[32], 3, 2] multiDim = {{1.1, 1.2}, {2.1, 2.2}, {3.1, 3.2}}; + multiDim[1, 1, 3] = 2.3; + "#, + &expect![[r#""#]], + ); +} diff --git a/compiler/qsc_qasm3/src/semantic/tests/decls.rs b/compiler/qsc_qasm3/src/semantic/tests/decls.rs index f5f926e956..b9ed2cc462 100644 --- a/compiler/qsc_qasm3/src/semantic/tests/decls.rs +++ b/compiler/qsc_qasm3/src/semantic/tests/decls.rs @@ -1,6 +1,7 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. +mod angle; mod bit; mod bool; mod complex; diff --git a/compiler/qsc_qasm3/src/semantic/tests/decls/angle.rs b/compiler/qsc_qasm3/src/semantic/tests/decls/angle.rs new file mode 100644 index 0000000000..22759f8ce0 --- /dev/null +++ b/compiler/qsc_qasm3/src/semantic/tests/decls/angle.rs @@ -0,0 +1,415 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +use expect_test::expect; + +use crate::semantic::tests::check_classical_decl; + +#[test] +fn implicit_bitness_default() { + check_classical_decl( + "angle x;", + &expect![[r#" + ClassicalDeclarationStmt [0-8]: + symbol_id: 6 + ty_span: [0-5] + init_expr: Expr [0-0]: + ty: Angle(None, true) + kind: Lit: Float(0.0) + [6] Symbol [6-7]: + name: x + type: Angle(None, false) + qsharp_type: Double + io_kind: Default"#]], + ); +} + +#[test] +fn const_default_fails() { + check_classical_decl( + "const angle x;", + &expect![[r#" + Program: + version: + statements: + + [Qasm3.Parse.Token + + x expected `=`, found `;` + ,-[test:1:14] + 1 | const angle x; + : ^ + `---- + , Qsc.Qasm3.Compile.UnexpectedParserError + + x Unexpected parser error: Unexpected error. + ,-[test:1:1] + 1 | const angle x; + : ^^^^^^^^^^^^^^ + `---- + ]"#]], + ); +} + +#[test] +fn lit() { + check_classical_decl( + "angle x = 42.1;", + &expect![[r#" + ClassicalDeclarationStmt [0-15]: + symbol_id: 6 + ty_span: [0-5] + init_expr: Expr [10-14]: + ty: Angle(None, true) + kind: Lit: Float(42.1) + [6] Symbol [6-7]: + name: x + type: Angle(None, false) + qsharp_type: Double + io_kind: Default"#]], + ); +} + +#[test] +fn const_lit() { + check_classical_decl( + "const angle x = 42.1;", + &expect![[r#" + ClassicalDeclarationStmt [0-21]: + symbol_id: 6 + ty_span: [6-11] + init_expr: Expr [16-20]: + ty: Angle(None, true) + kind: Lit: Float(42.1) + [6] Symbol [12-13]: + name: x + type: Angle(None, true) + qsharp_type: Double + io_kind: Default"#]], + ); +} + +#[test] +fn lit_explicit_width() { + check_classical_decl( + "angle[64] x = 42.1;", + &expect![[r#" + ClassicalDeclarationStmt [0-19]: + symbol_id: 6 + ty_span: [0-9] + init_expr: Expr [14-18]: + ty: Angle(Some(64), true) + kind: Lit: Float(42.1) + [6] Symbol [10-11]: + name: x + type: Angle(Some(64), false) + qsharp_type: Double + io_kind: Default"#]], + ); +} + +#[test] +fn const_explicit_width_lit() { + check_classical_decl( + "const angle[64] x = 42.1;", + &expect![[r#" + ClassicalDeclarationStmt [0-25]: + symbol_id: 6 + ty_span: [6-15] + init_expr: Expr [20-24]: + ty: Angle(Some(64), true) + kind: Lit: Float(42.1) + [6] Symbol [16-17]: + name: x + type: Angle(Some(64), true) + qsharp_type: Double + io_kind: Default"#]], + ); +} + +#[test] +fn lit_decl_leading_dot() { + check_classical_decl( + "angle x = .421;", + &expect![[r#" + ClassicalDeclarationStmt [0-15]: + symbol_id: 6 + ty_span: [0-5] + init_expr: Expr [10-14]: + ty: Angle(None, true) + kind: Lit: Float(0.421) + [6] Symbol [6-7]: + name: x + type: Angle(None, false) + qsharp_type: Double + io_kind: Default"#]], + ); +} + +#[test] +fn const_lit_decl_leading_dot() { + check_classical_decl( + "const angle x = .421;", + &expect![[r#" + ClassicalDeclarationStmt [0-21]: + symbol_id: 6 + ty_span: [6-11] + init_expr: Expr [16-20]: + ty: Angle(None, true) + kind: Lit: Float(0.421) + [6] Symbol [12-13]: + name: x + type: Angle(None, true) + qsharp_type: Double + io_kind: Default"#]], + ); +} + +#[test] +fn const_lit_decl_leading_dot_scientific() { + check_classical_decl( + "const angle x = .421e2;", + &expect![[r#" + ClassicalDeclarationStmt [0-23]: + symbol_id: 6 + ty_span: [6-11] + init_expr: Expr [16-22]: + ty: Angle(None, true) + kind: Lit: Float(42.1) + [6] Symbol [12-13]: + name: x + type: Angle(None, true) + qsharp_type: Double + io_kind: Default"#]], + ); +} + +#[test] +fn lit_decl_trailing_dot() { + check_classical_decl( + "angle x = 421.;", + &expect![[r#" + ClassicalDeclarationStmt [0-15]: + symbol_id: 6 + ty_span: [0-5] + init_expr: Expr [10-14]: + ty: Angle(None, true) + kind: Lit: Float(421.0) + [6] Symbol [6-7]: + name: x + type: Angle(None, false) + qsharp_type: Double + io_kind: Default"#]], + ); +} + +#[test] +fn const_lit_decl_trailing_dot() { + check_classical_decl( + "const angle x = 421.;", + &expect![[r#" + ClassicalDeclarationStmt [0-21]: + symbol_id: 6 + ty_span: [6-11] + init_expr: Expr [16-20]: + ty: Angle(None, true) + kind: Lit: Float(421.0) + [6] Symbol [12-13]: + name: x + type: Angle(None, true) + qsharp_type: Double + io_kind: Default"#]], + ); +} + +#[test] +fn lit_decl_scientific() { + check_classical_decl( + "angle x = 4.21e1;", + &expect![[r#" + ClassicalDeclarationStmt [0-17]: + symbol_id: 6 + ty_span: [0-5] + init_expr: Expr [10-16]: + ty: Angle(None, true) + kind: Lit: Float(42.1) + [6] Symbol [6-7]: + name: x + type: Angle(None, false) + qsharp_type: Double + io_kind: Default"#]], + ); +} + +#[test] +fn const_lit_decl_scientific() { + check_classical_decl( + "const angle x = 4.21e1;", + &expect![[r#" + ClassicalDeclarationStmt [0-23]: + symbol_id: 6 + ty_span: [6-11] + init_expr: Expr [16-22]: + ty: Angle(None, true) + kind: Lit: Float(42.1) + [6] Symbol [12-13]: + name: x + type: Angle(None, true) + qsharp_type: Double + io_kind: Default"#]], + ); +} + +#[test] +fn lit_decl_scientific_signed_pos() { + check_classical_decl( + "angle x = 4.21e+1;", + &expect![[r#" + ClassicalDeclarationStmt [0-18]: + symbol_id: 6 + ty_span: [0-5] + init_expr: Expr [10-17]: + ty: Angle(None, true) + kind: Lit: Float(42.1) + [6] Symbol [6-7]: + name: x + type: Angle(None, false) + qsharp_type: Double + io_kind: Default"#]], + ); +} + +#[test] +fn const_lit_decl_scientific_signed_pos() { + check_classical_decl( + "const angle x = 4.21e+1;", + &expect![[r#" + ClassicalDeclarationStmt [0-24]: + symbol_id: 6 + ty_span: [6-11] + init_expr: Expr [16-23]: + ty: Angle(None, true) + kind: Lit: Float(42.1) + [6] Symbol [12-13]: + name: x + type: Angle(None, true) + qsharp_type: Double + io_kind: Default"#]], + ); +} + +#[test] +fn lit_decl_scientific_cap_e() { + check_classical_decl( + "angle x = 4.21E1;", + &expect![[r#" + ClassicalDeclarationStmt [0-17]: + symbol_id: 6 + ty_span: [0-5] + init_expr: Expr [10-16]: + ty: Angle(None, true) + kind: Lit: Float(42.1) + [6] Symbol [6-7]: + name: x + type: Angle(None, false) + qsharp_type: Double + io_kind: Default"#]], + ); +} + +#[test] +fn const_lit_decl_scientific_cap_e() { + check_classical_decl( + "const angle x = 4.21E1;", + &expect![[r#" + ClassicalDeclarationStmt [0-23]: + symbol_id: 6 + ty_span: [6-11] + init_expr: Expr [16-22]: + ty: Angle(None, true) + kind: Lit: Float(42.1) + [6] Symbol [12-13]: + name: x + type: Angle(None, true) + qsharp_type: Double + io_kind: Default"#]], + ); +} + +#[test] +fn lit_decl_scientific_signed_neg() { + check_classical_decl( + "angle x = 421.0e-1;", + &expect![[r#" + ClassicalDeclarationStmt [0-19]: + symbol_id: 6 + ty_span: [0-5] + init_expr: Expr [10-18]: + ty: Angle(None, true) + kind: Lit: Float(42.1) + [6] Symbol [6-7]: + name: x + type: Angle(None, false) + qsharp_type: Double + io_kind: Default"#]], + ); +} + +#[test] +fn const_lit_decl_scientific_signed_neg() { + check_classical_decl( + "const angle x = 421.0e-1;", + &expect![[r#" + ClassicalDeclarationStmt [0-25]: + symbol_id: 6 + ty_span: [6-11] + init_expr: Expr [16-24]: + ty: Angle(None, true) + kind: Lit: Float(42.1) + [6] Symbol [12-13]: + name: x + type: Angle(None, true) + qsharp_type: Double + io_kind: Default"#]], + ); +} + +#[test] +fn const_lit_decl_signed_float_lit_cast_neg() { + check_classical_decl( + "const angle x = -7.;", + &expect![[r#" + ClassicalDeclarationStmt [0-20]: + symbol_id: 6 + ty_span: [6-11] + init_expr: Expr [17-19]: + ty: Angle(None, true) + kind: Lit: Float(-7.0) + [6] Symbol [12-13]: + name: x + type: Angle(None, true) + qsharp_type: Double + io_kind: Default"#]], + ); +} + +#[test] +fn const_lit_decl_signed_int_lit_cast_neg_fails() { + check_classical_decl( + "const angle x = -7;", + &expect![[r#" + Program: + version: + statements: + + [Qsc.Qasm3.Compile.CannotAssignToType + + x Cannot assign a value of Int(None, true) type to a classical variable of + | Angle(None, true) type. + ,-[test:1:1] + 1 | const angle x = -7; + : ^^^^^^^^^^^^^^^^^^^ + `---- + ]"#]], + ); +} diff --git a/compiler/qsc_qasm3/src/semantic/tests/expression.rs b/compiler/qsc_qasm3/src/semantic/tests/expression.rs new file mode 100644 index 0000000000..783217bc1d --- /dev/null +++ b/compiler/qsc_qasm3/src/semantic/tests/expression.rs @@ -0,0 +1,57 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +mod binary; +mod implicit_cast_from_angle; +mod implicit_cast_from_bit; +mod implicit_cast_from_bitarray; +mod implicit_cast_from_bool; +mod implicit_cast_from_float; +mod implicit_cast_from_int; + +use expect_test::expect; + +use super::check_stmt_kinds; + +#[test] +fn a() { + check_stmt_kinds( + r#" + true && false; + false || true; + !true; + "#, + &expect![[r#" + ExprStmt [9-23]: + expr: Expr [9-22]: + ty: Bool(false) + kind: BinaryOpExpr: + op: AndL + lhs: Expr [9-13]: + ty: Bool(true) + kind: Lit: Bool(true) + rhs: Expr [17-22]: + ty: Bool(true) + kind: Lit: Bool(false) + ExprStmt [32-46]: + expr: Expr [32-45]: + ty: Bool(false) + kind: BinaryOpExpr: + op: OrL + lhs: Expr [32-37]: + ty: Bool(true) + kind: Lit: Bool(false) + rhs: Expr [41-45]: + ty: Bool(true) + kind: Lit: Bool(true) + ExprStmt [55-61]: + expr: Expr [56-60]: + ty: Bool(true) + kind: UnaryOpExpr: + op: NotL + expr: Expr [56-60]: + ty: Bool(true) + kind: Lit: Bool(true) + "#]], + ); +} diff --git a/compiler/qsc_qasm3/src/semantic/tests/expression/binary.rs b/compiler/qsc_qasm3/src/semantic/tests/expression/binary.rs new file mode 100644 index 0000000000..4e43914b2c --- /dev/null +++ b/compiler/qsc_qasm3/src/semantic/tests/expression/binary.rs @@ -0,0 +1,7 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +mod arithmetic_conversions; +mod comparison; +mod complex; +mod ident; diff --git a/compiler/qsc_qasm3/src/semantic/tests/expression/binary/arithmetic_conversions.rs b/compiler/qsc_qasm3/src/semantic/tests/expression/binary/arithmetic_conversions.rs new file mode 100644 index 0000000000..1df284f262 --- /dev/null +++ b/compiler/qsc_qasm3/src/semantic/tests/expression/binary/arithmetic_conversions.rs @@ -0,0 +1,264 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +use expect_test::expect; + +use crate::semantic::tests::check_stmt_kinds; + +#[test] +fn int_idents_without_width_can_be_multiplied() { + let input = " + int x = 5; + int y = 3; + x * y; + "; + + check_stmt_kinds( + input, + &expect![[r#" + ClassicalDeclarationStmt [9-19]: + symbol_id: 6 + ty_span: [9-12] + init_expr: Expr [17-18]: + ty: Int(None, true) + kind: Lit: Int(5) + ClassicalDeclarationStmt [28-38]: + symbol_id: 7 + ty_span: [28-31] + init_expr: Expr [36-37]: + ty: Int(None, true) + kind: Lit: Int(3) + ExprStmt [47-53]: + expr: Expr [47-52]: + ty: Int(None, false) + kind: BinaryOpExpr: + op: Mul + lhs: Expr [47-48]: + ty: Int(None, false) + kind: SymbolId(6) + rhs: Expr [51-52]: + ty: Int(None, false) + kind: SymbolId(7) + "#]], + ); +} + +#[test] +fn int_idents_with_same_width_can_be_multiplied() { + let input = " + int[32] x = 5; + int[32] y = 3; + x * y; + "; + + check_stmt_kinds( + input, + &expect![[r#" + ClassicalDeclarationStmt [9-23]: + symbol_id: 6 + ty_span: [9-16] + init_expr: Expr [21-22]: + ty: Int(Some(32), true) + kind: Lit: Int(5) + ClassicalDeclarationStmt [32-46]: + symbol_id: 7 + ty_span: [32-39] + init_expr: Expr [44-45]: + ty: Int(Some(32), true) + kind: Lit: Int(3) + ExprStmt [55-61]: + expr: Expr [55-60]: + ty: Int(Some(32), false) + kind: BinaryOpExpr: + op: Mul + lhs: Expr [55-56]: + ty: Int(Some(32), false) + kind: SymbolId(6) + rhs: Expr [59-60]: + ty: Int(Some(32), false) + kind: SymbolId(7) + "#]], + ); +} + +#[test] +fn int_idents_with_different_width_can_be_multiplied() { + let input = " + int[32] x = 5; + int[64] y = 3; + x * y; + "; + + check_stmt_kinds( + input, + &expect![[r#" + ClassicalDeclarationStmt [9-23]: + symbol_id: 6 + ty_span: [9-16] + init_expr: Expr [21-22]: + ty: Int(Some(32), true) + kind: Lit: Int(5) + ClassicalDeclarationStmt [32-46]: + symbol_id: 7 + ty_span: [32-39] + init_expr: Expr [44-45]: + ty: Int(Some(64), true) + kind: Lit: Int(3) + ExprStmt [55-61]: + expr: Expr [55-60]: + ty: Int(Some(64), false) + kind: BinaryOpExpr: + op: Mul + lhs: Expr [55-56]: + ty: Int(Some(64), false) + kind: Cast [0-0]: + ty: Int(Some(64), false) + expr: Expr [55-56]: + ty: Int(Some(32), false) + kind: SymbolId(6) + rhs: Expr [59-60]: + ty: Int(Some(64), false) + kind: SymbolId(7) + "#]], + ); +} + +#[test] +fn multiplying_int_idents_with_different_width_result_in_higher_width_result() { + let input = " + int[32] x = 5; + int[64] y = 3; + int[64] z = x * y; + "; + + check_stmt_kinds( + input, + &expect![[r#" + ClassicalDeclarationStmt [9-23]: + symbol_id: 6 + ty_span: [9-16] + init_expr: Expr [21-22]: + ty: Int(Some(32), true) + kind: Lit: Int(5) + ClassicalDeclarationStmt [32-46]: + symbol_id: 7 + ty_span: [32-39] + init_expr: Expr [44-45]: + ty: Int(Some(64), true) + kind: Lit: Int(3) + ClassicalDeclarationStmt [55-73]: + symbol_id: 8 + ty_span: [55-62] + init_expr: Expr [67-72]: + ty: Int(Some(64), false) + kind: BinaryOpExpr: + op: Mul + lhs: Expr [67-68]: + ty: Int(Some(64), false) + kind: Cast [0-0]: + ty: Int(Some(64), false) + expr: Expr [67-68]: + ty: Int(Some(32), false) + kind: SymbolId(6) + rhs: Expr [71-72]: + ty: Int(Some(64), false) + kind: SymbolId(7) + "#]], + ); +} + +#[test] +fn multiplying_int_idents_with_different_width_result_in_no_width_result() { + let input = " + int[32] x = 5; + int[64] y = 3; + int z = x * y; + "; + + check_stmt_kinds( + input, + &expect![[r#" + ClassicalDeclarationStmt [9-23]: + symbol_id: 6 + ty_span: [9-16] + init_expr: Expr [21-22]: + ty: Int(Some(32), true) + kind: Lit: Int(5) + ClassicalDeclarationStmt [32-46]: + symbol_id: 7 + ty_span: [32-39] + init_expr: Expr [44-45]: + ty: Int(Some(64), true) + kind: Lit: Int(3) + ClassicalDeclarationStmt [55-69]: + symbol_id: 8 + ty_span: [55-58] + init_expr: Expr [63-68]: + ty: Int(None, false) + kind: Cast [0-0]: + ty: Int(None, false) + expr: Expr [63-68]: + ty: Int(Some(64), false) + kind: BinaryOpExpr: + op: Mul + lhs: Expr [63-64]: + ty: Int(Some(64), false) + kind: Cast [0-0]: + ty: Int(Some(64), false) + expr: Expr [63-64]: + ty: Int(Some(32), false) + kind: SymbolId(6) + rhs: Expr [67-68]: + ty: Int(Some(64), false) + kind: SymbolId(7) + "#]], + ); +} + +#[test] +fn multiplying_int_idents_with_width_greater_than_64_result_in_bigint_result() { + let input = " + int[32] x = 5; + int[64] y = 3; + int[67] z = x * y; + "; + + check_stmt_kinds( + input, + &expect![[r#" + ClassicalDeclarationStmt [9-23]: + symbol_id: 6 + ty_span: [9-16] + init_expr: Expr [21-22]: + ty: Int(Some(32), true) + kind: Lit: Int(5) + ClassicalDeclarationStmt [32-46]: + symbol_id: 7 + ty_span: [32-39] + init_expr: Expr [44-45]: + ty: Int(Some(64), true) + kind: Lit: Int(3) + ClassicalDeclarationStmt [55-73]: + symbol_id: 8 + ty_span: [55-62] + init_expr: Expr [67-72]: + ty: Int(Some(67), false) + kind: Cast [0-0]: + ty: Int(Some(67), false) + expr: Expr [67-72]: + ty: Int(Some(64), false) + kind: BinaryOpExpr: + op: Mul + lhs: Expr [67-68]: + ty: Int(Some(64), false) + kind: Cast [0-0]: + ty: Int(Some(64), false) + expr: Expr [67-68]: + ty: Int(Some(32), false) + kind: SymbolId(6) + rhs: Expr [71-72]: + ty: Int(Some(64), false) + kind: SymbolId(7) + "#]], + ); +} diff --git a/compiler/qsc_qasm3/src/semantic/tests/expression/binary/comparison.rs b/compiler/qsc_qasm3/src/semantic/tests/expression/binary/comparison.rs new file mode 100644 index 0000000000..1743c4c3ec --- /dev/null +++ b/compiler/qsc_qasm3/src/semantic/tests/expression/binary/comparison.rs @@ -0,0 +1,53 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +mod bit_to_bit; +mod bool_to_bool; +mod float_to_float; +mod int_to_int; + +mod uint_to_uint; + +use expect_test::expect; + +use crate::semantic::tests::check_stmt_kinds; + +#[test] +#[ignore = "not yet implemented"] +fn bitarray_var_comparisons_can_be_translated() { + let input = r#" + bit[1] x = "1"; + bit[1] y = "0"; + bool f = x > y; + bool e = x >= y; + bool a = x < y; + bool c = x <= y; + bool b = x == y; + bool d = x != y; + "#; + + check_stmt_kinds(input, &expect![[r#""#]]); +} + +#[test] +#[ignore = "not yet implemented"] +fn bitarray_var_comparison_to_int_can_be_translated() { + let input = r#" + bit[1] x = "1"; + input int y; + bool a = x > y; + bool b = x >= y; + bool c = x < y; + bool d = x <= y; + bool e = x == y; + bool f = x != y; + bool g = y > x; + bool h = y >= x; + bool i = y < x; + bool j = y <= x; + bool k = y == x; + bool l = y != x; + "#; + + check_stmt_kinds(input, &expect![[r#""#]]); +} diff --git a/compiler/qsc_qasm3/src/semantic/tests/expression/binary/comparison/bit_to_bit.rs b/compiler/qsc_qasm3/src/semantic/tests/expression/binary/comparison/bit_to_bit.rs new file mode 100644 index 0000000000..2aa0c1eec2 --- /dev/null +++ b/compiler/qsc_qasm3/src/semantic/tests/expression/binary/comparison/bit_to_bit.rs @@ -0,0 +1,542 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +use expect_test::expect; + +use crate::semantic::tests::check_classical_decls; + +#[test] +fn logical_and() { + let input = " + bit x = 1; + bit y = 0; + bool a = x && y; + "; + + check_classical_decls( + input, + &expect![[r#" + ClassicalDeclarationStmt [9-19]: + symbol_id: 6 + ty_span: [9-12] + init_expr: Expr [17-18]: + ty: Bit(true) + kind: Lit: Int(1) + [6] Symbol [13-14]: + name: x + type: Bit(false) + qsharp_type: Result + io_kind: Default + ClassicalDeclarationStmt [28-38]: + symbol_id: 7 + ty_span: [28-31] + init_expr: Expr [36-37]: + ty: Bit(true) + kind: Lit: Int(0) + [7] Symbol [32-33]: + name: y + type: Bit(false) + qsharp_type: Result + io_kind: Default + ClassicalDeclarationStmt [47-63]: + symbol_id: 8 + ty_span: [47-51] + init_expr: Expr [56-62]: + ty: Bool(false) + kind: BinaryOpExpr: + op: AndL + lhs: Expr [56-57]: + ty: Bool(false) + kind: Cast [0-0]: + ty: Bool(false) + expr: Expr [56-57]: + ty: Bit(false) + kind: SymbolId(6) + rhs: Expr [61-62]: + ty: Bool(false) + kind: Cast [0-0]: + ty: Bool(false) + expr: Expr [61-62]: + ty: Bit(false) + kind: SymbolId(7) + [8] Symbol [52-53]: + name: a + type: Bool(false) + qsharp_type: bool + io_kind: Default + "#]], + ); +} + +#[test] +fn logical_or() { + let input = " + bit x = 1; + bit y = 0; + bool a = x || y; + "; + + check_classical_decls( + input, + &expect![[r#" + ClassicalDeclarationStmt [9-19]: + symbol_id: 6 + ty_span: [9-12] + init_expr: Expr [17-18]: + ty: Bit(true) + kind: Lit: Int(1) + [6] Symbol [13-14]: + name: x + type: Bit(false) + qsharp_type: Result + io_kind: Default + ClassicalDeclarationStmt [28-38]: + symbol_id: 7 + ty_span: [28-31] + init_expr: Expr [36-37]: + ty: Bit(true) + kind: Lit: Int(0) + [7] Symbol [32-33]: + name: y + type: Bit(false) + qsharp_type: Result + io_kind: Default + ClassicalDeclarationStmt [47-63]: + symbol_id: 8 + ty_span: [47-51] + init_expr: Expr [56-62]: + ty: Bool(false) + kind: BinaryOpExpr: + op: OrL + lhs: Expr [56-57]: + ty: Bool(false) + kind: Cast [0-0]: + ty: Bool(false) + expr: Expr [56-57]: + ty: Bit(false) + kind: SymbolId(6) + rhs: Expr [61-62]: + ty: Bool(false) + kind: Cast [0-0]: + ty: Bool(false) + expr: Expr [61-62]: + ty: Bit(false) + kind: SymbolId(7) + [8] Symbol [52-53]: + name: a + type: Bool(false) + qsharp_type: bool + io_kind: Default + "#]], + ); +} + +#[test] +fn unop_not_logical_and_unop_not() { + let input = " + bit x = 1; + bit y = 0; + bool a = !x && !y; + "; + + check_classical_decls( + input, + &expect![[r#" + ClassicalDeclarationStmt [9-19]: + symbol_id: 6 + ty_span: [9-12] + init_expr: Expr [17-18]: + ty: Bit(true) + kind: Lit: Int(1) + [6] Symbol [13-14]: + name: x + type: Bit(false) + qsharp_type: Result + io_kind: Default + ClassicalDeclarationStmt [28-38]: + symbol_id: 7 + ty_span: [28-31] + init_expr: Expr [36-37]: + ty: Bit(true) + kind: Lit: Int(0) + [7] Symbol [32-33]: + name: y + type: Bit(false) + qsharp_type: Result + io_kind: Default + ClassicalDeclarationStmt [47-65]: + symbol_id: 8 + ty_span: [47-51] + init_expr: Expr [56-64]: + ty: Bool(false) + kind: BinaryOpExpr: + op: AndL + lhs: Expr [57-58]: + ty: Bool(false) + kind: UnaryOpExpr: + op: NotL + expr: Expr [57-58]: + ty: Bool(false) + kind: Cast [0-0]: + ty: Bool(false) + expr: Expr [57-58]: + ty: Bit(false) + kind: SymbolId(6) + rhs: Expr [63-64]: + ty: Bool(false) + kind: UnaryOpExpr: + op: NotL + expr: Expr [63-64]: + ty: Bool(false) + kind: Cast [0-0]: + ty: Bool(false) + expr: Expr [63-64]: + ty: Bit(false) + kind: SymbolId(7) + [8] Symbol [52-53]: + name: a + type: Bool(false) + qsharp_type: bool + io_kind: Default + "#]], + ); +} + +#[test] +fn unop_not_logical_or_unop_not() { + let input = " + bit x = 1; + bit y = 0; + bool a = !x || !y; + "; + + check_classical_decls( + input, + &expect![[r#" + ClassicalDeclarationStmt [9-19]: + symbol_id: 6 + ty_span: [9-12] + init_expr: Expr [17-18]: + ty: Bit(true) + kind: Lit: Int(1) + [6] Symbol [13-14]: + name: x + type: Bit(false) + qsharp_type: Result + io_kind: Default + ClassicalDeclarationStmt [28-38]: + symbol_id: 7 + ty_span: [28-31] + init_expr: Expr [36-37]: + ty: Bit(true) + kind: Lit: Int(0) + [7] Symbol [32-33]: + name: y + type: Bit(false) + qsharp_type: Result + io_kind: Default + ClassicalDeclarationStmt [47-65]: + symbol_id: 8 + ty_span: [47-51] + init_expr: Expr [56-64]: + ty: Bool(false) + kind: BinaryOpExpr: + op: OrL + lhs: Expr [57-58]: + ty: Bool(false) + kind: UnaryOpExpr: + op: NotL + expr: Expr [57-58]: + ty: Bool(false) + kind: Cast [0-0]: + ty: Bool(false) + expr: Expr [57-58]: + ty: Bit(false) + kind: SymbolId(6) + rhs: Expr [63-64]: + ty: Bool(false) + kind: UnaryOpExpr: + op: NotL + expr: Expr [63-64]: + ty: Bool(false) + kind: Cast [0-0]: + ty: Bool(false) + expr: Expr [63-64]: + ty: Bit(false) + kind: SymbolId(7) + [8] Symbol [52-53]: + name: a + type: Bool(false) + qsharp_type: bool + io_kind: Default + "#]], + ); +} + +#[test] +fn unop_not_logical_and() { + let input = " + bit x = 1; + bit y = 0; + bool a = !x && y; + "; + + check_classical_decls( + input, + &expect![[r#" + ClassicalDeclarationStmt [9-19]: + symbol_id: 6 + ty_span: [9-12] + init_expr: Expr [17-18]: + ty: Bit(true) + kind: Lit: Int(1) + [6] Symbol [13-14]: + name: x + type: Bit(false) + qsharp_type: Result + io_kind: Default + ClassicalDeclarationStmt [28-38]: + symbol_id: 7 + ty_span: [28-31] + init_expr: Expr [36-37]: + ty: Bit(true) + kind: Lit: Int(0) + [7] Symbol [32-33]: + name: y + type: Bit(false) + qsharp_type: Result + io_kind: Default + ClassicalDeclarationStmt [47-64]: + symbol_id: 8 + ty_span: [47-51] + init_expr: Expr [56-63]: + ty: Bool(false) + kind: BinaryOpExpr: + op: AndL + lhs: Expr [57-58]: + ty: Bool(false) + kind: UnaryOpExpr: + op: NotL + expr: Expr [57-58]: + ty: Bool(false) + kind: Cast [0-0]: + ty: Bool(false) + expr: Expr [57-58]: + ty: Bit(false) + kind: SymbolId(6) + rhs: Expr [62-63]: + ty: Bool(false) + kind: Cast [0-0]: + ty: Bool(false) + expr: Expr [62-63]: + ty: Bit(false) + kind: SymbolId(7) + [8] Symbol [52-53]: + name: a + type: Bool(false) + qsharp_type: bool + io_kind: Default + "#]], + ); +} + +#[test] +fn unop_not_logical_or() { + let input = " + bit x = 1; + bit y = 0; + bool a = !x || y; + "; + + check_classical_decls( + input, + &expect![[r#" + ClassicalDeclarationStmt [9-19]: + symbol_id: 6 + ty_span: [9-12] + init_expr: Expr [17-18]: + ty: Bit(true) + kind: Lit: Int(1) + [6] Symbol [13-14]: + name: x + type: Bit(false) + qsharp_type: Result + io_kind: Default + ClassicalDeclarationStmt [28-38]: + symbol_id: 7 + ty_span: [28-31] + init_expr: Expr [36-37]: + ty: Bit(true) + kind: Lit: Int(0) + [7] Symbol [32-33]: + name: y + type: Bit(false) + qsharp_type: Result + io_kind: Default + ClassicalDeclarationStmt [47-64]: + symbol_id: 8 + ty_span: [47-51] + init_expr: Expr [56-63]: + ty: Bool(false) + kind: BinaryOpExpr: + op: OrL + lhs: Expr [57-58]: + ty: Bool(false) + kind: UnaryOpExpr: + op: NotL + expr: Expr [57-58]: + ty: Bool(false) + kind: Cast [0-0]: + ty: Bool(false) + expr: Expr [57-58]: + ty: Bit(false) + kind: SymbolId(6) + rhs: Expr [62-63]: + ty: Bool(false) + kind: Cast [0-0]: + ty: Bool(false) + expr: Expr [62-63]: + ty: Bit(false) + kind: SymbolId(7) + [8] Symbol [52-53]: + name: a + type: Bool(false) + qsharp_type: bool + io_kind: Default + "#]], + ); +} + +#[test] +fn logical_and_unop_not() { + let input = " + bit x = 1; + bit y = 0; + bool a = x && !y; + "; + + check_classical_decls( + input, + &expect![[r#" + ClassicalDeclarationStmt [9-19]: + symbol_id: 6 + ty_span: [9-12] + init_expr: Expr [17-18]: + ty: Bit(true) + kind: Lit: Int(1) + [6] Symbol [13-14]: + name: x + type: Bit(false) + qsharp_type: Result + io_kind: Default + ClassicalDeclarationStmt [28-38]: + symbol_id: 7 + ty_span: [28-31] + init_expr: Expr [36-37]: + ty: Bit(true) + kind: Lit: Int(0) + [7] Symbol [32-33]: + name: y + type: Bit(false) + qsharp_type: Result + io_kind: Default + ClassicalDeclarationStmt [47-64]: + symbol_id: 8 + ty_span: [47-51] + init_expr: Expr [56-63]: + ty: Bool(false) + kind: BinaryOpExpr: + op: AndL + lhs: Expr [56-57]: + ty: Bool(false) + kind: Cast [0-0]: + ty: Bool(false) + expr: Expr [56-57]: + ty: Bit(false) + kind: SymbolId(6) + rhs: Expr [62-63]: + ty: Bool(false) + kind: UnaryOpExpr: + op: NotL + expr: Expr [62-63]: + ty: Bool(false) + kind: Cast [0-0]: + ty: Bool(false) + expr: Expr [62-63]: + ty: Bit(false) + kind: SymbolId(7) + [8] Symbol [52-53]: + name: a + type: Bool(false) + qsharp_type: bool + io_kind: Default + "#]], + ); +} + +#[test] +fn logical_or_unop_not() { + let input = " + bit x = 1; + bit y = 0; + bool a = x || !y; + "; + + check_classical_decls( + input, + &expect![[r#" + ClassicalDeclarationStmt [9-19]: + symbol_id: 6 + ty_span: [9-12] + init_expr: Expr [17-18]: + ty: Bit(true) + kind: Lit: Int(1) + [6] Symbol [13-14]: + name: x + type: Bit(false) + qsharp_type: Result + io_kind: Default + ClassicalDeclarationStmt [28-38]: + symbol_id: 7 + ty_span: [28-31] + init_expr: Expr [36-37]: + ty: Bit(true) + kind: Lit: Int(0) + [7] Symbol [32-33]: + name: y + type: Bit(false) + qsharp_type: Result + io_kind: Default + ClassicalDeclarationStmt [47-64]: + symbol_id: 8 + ty_span: [47-51] + init_expr: Expr [56-63]: + ty: Bool(false) + kind: BinaryOpExpr: + op: OrL + lhs: Expr [56-57]: + ty: Bool(false) + kind: Cast [0-0]: + ty: Bool(false) + expr: Expr [56-57]: + ty: Bit(false) + kind: SymbolId(6) + rhs: Expr [62-63]: + ty: Bool(false) + kind: UnaryOpExpr: + op: NotL + expr: Expr [62-63]: + ty: Bool(false) + kind: Cast [0-0]: + ty: Bool(false) + expr: Expr [62-63]: + ty: Bit(false) + kind: SymbolId(7) + [8] Symbol [52-53]: + name: a + type: Bool(false) + qsharp_type: bool + io_kind: Default + "#]], + ); +} diff --git a/compiler/qsc_qasm3/src/semantic/tests/expression/binary/comparison/bool_to_bool.rs b/compiler/qsc_qasm3/src/semantic/tests/expression/binary/comparison/bool_to_bool.rs new file mode 100644 index 0000000000..df79f99889 --- /dev/null +++ b/compiler/qsc_qasm3/src/semantic/tests/expression/binary/comparison/bool_to_bool.rs @@ -0,0 +1,478 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +use expect_test::expect; + +use crate::semantic::tests::check_classical_decls; + +#[test] +fn logical_and() { + let input = " + bool x = true; + bool y = false; + bool a = x && y; + "; + + check_classical_decls( + input, + &expect![[r#" + ClassicalDeclarationStmt [9-23]: + symbol_id: 6 + ty_span: [9-13] + init_expr: Expr [18-22]: + ty: Bool(true) + kind: Lit: Bool(true) + [6] Symbol [14-15]: + name: x + type: Bool(false) + qsharp_type: bool + io_kind: Default + ClassicalDeclarationStmt [32-47]: + symbol_id: 7 + ty_span: [32-36] + init_expr: Expr [41-46]: + ty: Bool(true) + kind: Lit: Bool(false) + [7] Symbol [37-38]: + name: y + type: Bool(false) + qsharp_type: bool + io_kind: Default + ClassicalDeclarationStmt [56-72]: + symbol_id: 8 + ty_span: [56-60] + init_expr: Expr [65-71]: + ty: Bool(false) + kind: BinaryOpExpr: + op: AndL + lhs: Expr [65-66]: + ty: Bool(false) + kind: SymbolId(6) + rhs: Expr [70-71]: + ty: Bool(false) + kind: SymbolId(7) + [8] Symbol [61-62]: + name: a + type: Bool(false) + qsharp_type: bool + io_kind: Default + "#]], + ); +} + +#[test] +fn logical_or() { + let input = " + bool x = true; + bool y = false; + bool a = x || y; + "; + + check_classical_decls( + input, + &expect![[r#" + ClassicalDeclarationStmt [9-23]: + symbol_id: 6 + ty_span: [9-13] + init_expr: Expr [18-22]: + ty: Bool(true) + kind: Lit: Bool(true) + [6] Symbol [14-15]: + name: x + type: Bool(false) + qsharp_type: bool + io_kind: Default + ClassicalDeclarationStmt [32-47]: + symbol_id: 7 + ty_span: [32-36] + init_expr: Expr [41-46]: + ty: Bool(true) + kind: Lit: Bool(false) + [7] Symbol [37-38]: + name: y + type: Bool(false) + qsharp_type: bool + io_kind: Default + ClassicalDeclarationStmt [56-72]: + symbol_id: 8 + ty_span: [56-60] + init_expr: Expr [65-71]: + ty: Bool(false) + kind: BinaryOpExpr: + op: OrL + lhs: Expr [65-66]: + ty: Bool(false) + kind: SymbolId(6) + rhs: Expr [70-71]: + ty: Bool(false) + kind: SymbolId(7) + [8] Symbol [61-62]: + name: a + type: Bool(false) + qsharp_type: bool + io_kind: Default + "#]], + ); +} + +#[test] +fn unop_not_logical_and_unop_not() { + let input = " + bool x = true; + bool y = false; + bool a = !x && !y; + "; + + check_classical_decls( + input, + &expect![[r#" + ClassicalDeclarationStmt [9-23]: + symbol_id: 6 + ty_span: [9-13] + init_expr: Expr [18-22]: + ty: Bool(true) + kind: Lit: Bool(true) + [6] Symbol [14-15]: + name: x + type: Bool(false) + qsharp_type: bool + io_kind: Default + ClassicalDeclarationStmt [32-47]: + symbol_id: 7 + ty_span: [32-36] + init_expr: Expr [41-46]: + ty: Bool(true) + kind: Lit: Bool(false) + [7] Symbol [37-38]: + name: y + type: Bool(false) + qsharp_type: bool + io_kind: Default + ClassicalDeclarationStmt [56-74]: + symbol_id: 8 + ty_span: [56-60] + init_expr: Expr [65-73]: + ty: Bool(false) + kind: BinaryOpExpr: + op: AndL + lhs: Expr [66-67]: + ty: Bool(false) + kind: UnaryOpExpr: + op: NotL + expr: Expr [66-67]: + ty: Bool(false) + kind: SymbolId(6) + rhs: Expr [72-73]: + ty: Bool(false) + kind: UnaryOpExpr: + op: NotL + expr: Expr [72-73]: + ty: Bool(false) + kind: SymbolId(7) + [8] Symbol [61-62]: + name: a + type: Bool(false) + qsharp_type: bool + io_kind: Default + "#]], + ); +} + +#[test] +fn unop_not_logical_or_unop_not() { + let input = " + bool x = true; + bool y = false; + bool a = !x || !y; + "; + + check_classical_decls( + input, + &expect![[r#" + ClassicalDeclarationStmt [9-23]: + symbol_id: 6 + ty_span: [9-13] + init_expr: Expr [18-22]: + ty: Bool(true) + kind: Lit: Bool(true) + [6] Symbol [14-15]: + name: x + type: Bool(false) + qsharp_type: bool + io_kind: Default + ClassicalDeclarationStmt [32-47]: + symbol_id: 7 + ty_span: [32-36] + init_expr: Expr [41-46]: + ty: Bool(true) + kind: Lit: Bool(false) + [7] Symbol [37-38]: + name: y + type: Bool(false) + qsharp_type: bool + io_kind: Default + ClassicalDeclarationStmt [56-74]: + symbol_id: 8 + ty_span: [56-60] + init_expr: Expr [65-73]: + ty: Bool(false) + kind: BinaryOpExpr: + op: OrL + lhs: Expr [66-67]: + ty: Bool(false) + kind: UnaryOpExpr: + op: NotL + expr: Expr [66-67]: + ty: Bool(false) + kind: SymbolId(6) + rhs: Expr [72-73]: + ty: Bool(false) + kind: UnaryOpExpr: + op: NotL + expr: Expr [72-73]: + ty: Bool(false) + kind: SymbolId(7) + [8] Symbol [61-62]: + name: a + type: Bool(false) + qsharp_type: bool + io_kind: Default + "#]], + ); +} + +#[test] +fn unop_not_logical_and() { + let input = " + bool x = true; + bool y = false; + bool a = !x && y; + "; + + check_classical_decls( + input, + &expect![[r#" + ClassicalDeclarationStmt [9-23]: + symbol_id: 6 + ty_span: [9-13] + init_expr: Expr [18-22]: + ty: Bool(true) + kind: Lit: Bool(true) + [6] Symbol [14-15]: + name: x + type: Bool(false) + qsharp_type: bool + io_kind: Default + ClassicalDeclarationStmt [32-47]: + symbol_id: 7 + ty_span: [32-36] + init_expr: Expr [41-46]: + ty: Bool(true) + kind: Lit: Bool(false) + [7] Symbol [37-38]: + name: y + type: Bool(false) + qsharp_type: bool + io_kind: Default + ClassicalDeclarationStmt [56-73]: + symbol_id: 8 + ty_span: [56-60] + init_expr: Expr [65-72]: + ty: Bool(false) + kind: BinaryOpExpr: + op: AndL + lhs: Expr [66-67]: + ty: Bool(false) + kind: UnaryOpExpr: + op: NotL + expr: Expr [66-67]: + ty: Bool(false) + kind: SymbolId(6) + rhs: Expr [71-72]: + ty: Bool(false) + kind: SymbolId(7) + [8] Symbol [61-62]: + name: a + type: Bool(false) + qsharp_type: bool + io_kind: Default + "#]], + ); +} + +#[test] +fn unop_not_logical_or() { + let input = " + bool x = true; + bool y = false; + bool a = !x || y; + "; + + check_classical_decls( + input, + &expect![[r#" + ClassicalDeclarationStmt [9-23]: + symbol_id: 6 + ty_span: [9-13] + init_expr: Expr [18-22]: + ty: Bool(true) + kind: Lit: Bool(true) + [6] Symbol [14-15]: + name: x + type: Bool(false) + qsharp_type: bool + io_kind: Default + ClassicalDeclarationStmt [32-47]: + symbol_id: 7 + ty_span: [32-36] + init_expr: Expr [41-46]: + ty: Bool(true) + kind: Lit: Bool(false) + [7] Symbol [37-38]: + name: y + type: Bool(false) + qsharp_type: bool + io_kind: Default + ClassicalDeclarationStmt [56-73]: + symbol_id: 8 + ty_span: [56-60] + init_expr: Expr [65-72]: + ty: Bool(false) + kind: BinaryOpExpr: + op: OrL + lhs: Expr [66-67]: + ty: Bool(false) + kind: UnaryOpExpr: + op: NotL + expr: Expr [66-67]: + ty: Bool(false) + kind: SymbolId(6) + rhs: Expr [71-72]: + ty: Bool(false) + kind: SymbolId(7) + [8] Symbol [61-62]: + name: a + type: Bool(false) + qsharp_type: bool + io_kind: Default + "#]], + ); +} + +#[test] +fn logical_and_unop_not() { + let input = " + bool x = true; + bool y = false; + bool a = x && !y; + "; + + check_classical_decls( + input, + &expect![[r#" + ClassicalDeclarationStmt [9-23]: + symbol_id: 6 + ty_span: [9-13] + init_expr: Expr [18-22]: + ty: Bool(true) + kind: Lit: Bool(true) + [6] Symbol [14-15]: + name: x + type: Bool(false) + qsharp_type: bool + io_kind: Default + ClassicalDeclarationStmt [32-47]: + symbol_id: 7 + ty_span: [32-36] + init_expr: Expr [41-46]: + ty: Bool(true) + kind: Lit: Bool(false) + [7] Symbol [37-38]: + name: y + type: Bool(false) + qsharp_type: bool + io_kind: Default + ClassicalDeclarationStmt [56-73]: + symbol_id: 8 + ty_span: [56-60] + init_expr: Expr [65-72]: + ty: Bool(false) + kind: BinaryOpExpr: + op: AndL + lhs: Expr [65-66]: + ty: Bool(false) + kind: SymbolId(6) + rhs: Expr [71-72]: + ty: Bool(false) + kind: UnaryOpExpr: + op: NotL + expr: Expr [71-72]: + ty: Bool(false) + kind: SymbolId(7) + [8] Symbol [61-62]: + name: a + type: Bool(false) + qsharp_type: bool + io_kind: Default + "#]], + ); +} + +#[test] +fn logical_or_unop_not() { + let input = " + bool x = true; + bool y = false; + bool a = x || !y; + "; + + check_classical_decls( + input, + &expect![[r#" + ClassicalDeclarationStmt [9-23]: + symbol_id: 6 + ty_span: [9-13] + init_expr: Expr [18-22]: + ty: Bool(true) + kind: Lit: Bool(true) + [6] Symbol [14-15]: + name: x + type: Bool(false) + qsharp_type: bool + io_kind: Default + ClassicalDeclarationStmt [32-47]: + symbol_id: 7 + ty_span: [32-36] + init_expr: Expr [41-46]: + ty: Bool(true) + kind: Lit: Bool(false) + [7] Symbol [37-38]: + name: y + type: Bool(false) + qsharp_type: bool + io_kind: Default + ClassicalDeclarationStmt [56-73]: + symbol_id: 8 + ty_span: [56-60] + init_expr: Expr [65-72]: + ty: Bool(false) + kind: BinaryOpExpr: + op: OrL + lhs: Expr [65-66]: + ty: Bool(false) + kind: SymbolId(6) + rhs: Expr [71-72]: + ty: Bool(false) + kind: UnaryOpExpr: + op: NotL + expr: Expr [71-72]: + ty: Bool(false) + kind: SymbolId(7) + [8] Symbol [61-62]: + name: a + type: Bool(false) + qsharp_type: bool + io_kind: Default + "#]], + ); +} diff --git a/compiler/qsc_qasm3/src/semantic/tests/expression/binary/comparison/float_to_float.rs b/compiler/qsc_qasm3/src/semantic/tests/expression/binary/comparison/float_to_float.rs new file mode 100644 index 0000000000..76cf8683f6 --- /dev/null +++ b/compiler/qsc_qasm3/src/semantic/tests/expression/binary/comparison/float_to_float.rs @@ -0,0 +1,336 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +use expect_test::expect; + +use crate::semantic::tests::check_classical_decls; + +#[test] +fn greater_than() { + let input = " + float x = 5.; + float y = 3.; + bool f = x > y; + "; + + check_classical_decls( + input, + &expect![[r#" + ClassicalDeclarationStmt [9-22]: + symbol_id: 6 + ty_span: [9-14] + init_expr: Expr [19-21]: + ty: Float(None, true) + kind: Lit: Float(5.0) + [6] Symbol [15-16]: + name: x + type: Float(None, false) + qsharp_type: Double + io_kind: Default + ClassicalDeclarationStmt [31-44]: + symbol_id: 7 + ty_span: [31-36] + init_expr: Expr [41-43]: + ty: Float(None, true) + kind: Lit: Float(3.0) + [7] Symbol [37-38]: + name: y + type: Float(None, false) + qsharp_type: Double + io_kind: Default + ClassicalDeclarationStmt [53-68]: + symbol_id: 8 + ty_span: [53-57] + init_expr: Expr [62-67]: + ty: Bool(false) + kind: BinaryOpExpr: + op: Gt + lhs: Expr [62-63]: + ty: Float(None, false) + kind: SymbolId(6) + rhs: Expr [66-67]: + ty: Float(None, false) + kind: SymbolId(7) + [8] Symbol [58-59]: + name: f + type: Bool(false) + qsharp_type: bool + io_kind: Default + "#]], + ); +} + +#[test] +fn greater_than_equals() { + let input = " + float x = 5.; + float y = 3.; + bool e = x >= y; + "; + + check_classical_decls( + input, + &expect![[r#" + ClassicalDeclarationStmt [9-22]: + symbol_id: 6 + ty_span: [9-14] + init_expr: Expr [19-21]: + ty: Float(None, true) + kind: Lit: Float(5.0) + [6] Symbol [15-16]: + name: x + type: Float(None, false) + qsharp_type: Double + io_kind: Default + ClassicalDeclarationStmt [31-44]: + symbol_id: 7 + ty_span: [31-36] + init_expr: Expr [41-43]: + ty: Float(None, true) + kind: Lit: Float(3.0) + [7] Symbol [37-38]: + name: y + type: Float(None, false) + qsharp_type: Double + io_kind: Default + ClassicalDeclarationStmt [53-69]: + symbol_id: 8 + ty_span: [53-57] + init_expr: Expr [62-68]: + ty: Bool(false) + kind: BinaryOpExpr: + op: Gte + lhs: Expr [62-63]: + ty: Float(None, false) + kind: SymbolId(6) + rhs: Expr [67-68]: + ty: Float(None, false) + kind: SymbolId(7) + [8] Symbol [58-59]: + name: e + type: Bool(false) + qsharp_type: bool + io_kind: Default + "#]], + ); +} + +#[test] +fn less_than() { + let input = " + float x = 5.; + float y = 3.; + bool a = x < y; + "; + + check_classical_decls( + input, + &expect![[r#" + ClassicalDeclarationStmt [9-22]: + symbol_id: 6 + ty_span: [9-14] + init_expr: Expr [19-21]: + ty: Float(None, true) + kind: Lit: Float(5.0) + [6] Symbol [15-16]: + name: x + type: Float(None, false) + qsharp_type: Double + io_kind: Default + ClassicalDeclarationStmt [31-44]: + symbol_id: 7 + ty_span: [31-36] + init_expr: Expr [41-43]: + ty: Float(None, true) + kind: Lit: Float(3.0) + [7] Symbol [37-38]: + name: y + type: Float(None, false) + qsharp_type: Double + io_kind: Default + ClassicalDeclarationStmt [53-68]: + symbol_id: 8 + ty_span: [53-57] + init_expr: Expr [62-67]: + ty: Bool(false) + kind: BinaryOpExpr: + op: Lt + lhs: Expr [62-63]: + ty: Float(None, false) + kind: SymbolId(6) + rhs: Expr [66-67]: + ty: Float(None, false) + kind: SymbolId(7) + [8] Symbol [58-59]: + name: a + type: Bool(false) + qsharp_type: bool + io_kind: Default + "#]], + ); +} + +#[test] +fn less_than_equals() { + let input = " + float x = 5.; + float y = 3.; + bool c = x <= y; + "; + + check_classical_decls( + input, + &expect![[r#" + ClassicalDeclarationStmt [9-22]: + symbol_id: 6 + ty_span: [9-14] + init_expr: Expr [19-21]: + ty: Float(None, true) + kind: Lit: Float(5.0) + [6] Symbol [15-16]: + name: x + type: Float(None, false) + qsharp_type: Double + io_kind: Default + ClassicalDeclarationStmt [31-44]: + symbol_id: 7 + ty_span: [31-36] + init_expr: Expr [41-43]: + ty: Float(None, true) + kind: Lit: Float(3.0) + [7] Symbol [37-38]: + name: y + type: Float(None, false) + qsharp_type: Double + io_kind: Default + ClassicalDeclarationStmt [53-69]: + symbol_id: 8 + ty_span: [53-57] + init_expr: Expr [62-68]: + ty: Bool(false) + kind: BinaryOpExpr: + op: Lte + lhs: Expr [62-63]: + ty: Float(None, false) + kind: SymbolId(6) + rhs: Expr [67-68]: + ty: Float(None, false) + kind: SymbolId(7) + [8] Symbol [58-59]: + name: c + type: Bool(false) + qsharp_type: bool + io_kind: Default + "#]], + ); +} + +#[test] +fn equals() { + let input = " + float x = 5.; + float y = 3.; + bool b = x == y; + "; + + check_classical_decls( + input, + &expect![[r#" + ClassicalDeclarationStmt [9-22]: + symbol_id: 6 + ty_span: [9-14] + init_expr: Expr [19-21]: + ty: Float(None, true) + kind: Lit: Float(5.0) + [6] Symbol [15-16]: + name: x + type: Float(None, false) + qsharp_type: Double + io_kind: Default + ClassicalDeclarationStmt [31-44]: + symbol_id: 7 + ty_span: [31-36] + init_expr: Expr [41-43]: + ty: Float(None, true) + kind: Lit: Float(3.0) + [7] Symbol [37-38]: + name: y + type: Float(None, false) + qsharp_type: Double + io_kind: Default + ClassicalDeclarationStmt [53-69]: + symbol_id: 8 + ty_span: [53-57] + init_expr: Expr [62-68]: + ty: Bool(false) + kind: BinaryOpExpr: + op: Eq + lhs: Expr [62-63]: + ty: Float(None, false) + kind: SymbolId(6) + rhs: Expr [67-68]: + ty: Float(None, false) + kind: SymbolId(7) + [8] Symbol [58-59]: + name: b + type: Bool(false) + qsharp_type: bool + io_kind: Default + "#]], + ); +} + +#[test] +fn not_equals() { + let input = " + float x = 5.; + float y = 3.; + bool d = x != y; + "; + + check_classical_decls( + input, + &expect![[r#" + ClassicalDeclarationStmt [9-22]: + symbol_id: 6 + ty_span: [9-14] + init_expr: Expr [19-21]: + ty: Float(None, true) + kind: Lit: Float(5.0) + [6] Symbol [15-16]: + name: x + type: Float(None, false) + qsharp_type: Double + io_kind: Default + ClassicalDeclarationStmt [31-44]: + symbol_id: 7 + ty_span: [31-36] + init_expr: Expr [41-43]: + ty: Float(None, true) + kind: Lit: Float(3.0) + [7] Symbol [37-38]: + name: y + type: Float(None, false) + qsharp_type: Double + io_kind: Default + ClassicalDeclarationStmt [53-69]: + symbol_id: 8 + ty_span: [53-57] + init_expr: Expr [62-68]: + ty: Bool(false) + kind: BinaryOpExpr: + op: Neq + lhs: Expr [62-63]: + ty: Float(None, false) + kind: SymbolId(6) + rhs: Expr [67-68]: + ty: Float(None, false) + kind: SymbolId(7) + [8] Symbol [58-59]: + name: d + type: Bool(false) + qsharp_type: bool + io_kind: Default + "#]], + ); +} diff --git a/compiler/qsc_qasm3/src/semantic/tests/expression/binary/comparison/int_to_int.rs b/compiler/qsc_qasm3/src/semantic/tests/expression/binary/comparison/int_to_int.rs new file mode 100644 index 0000000000..b9a35b85f0 --- /dev/null +++ b/compiler/qsc_qasm3/src/semantic/tests/expression/binary/comparison/int_to_int.rs @@ -0,0 +1,336 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +use expect_test::expect; + +use crate::semantic::tests::check_classical_decls; + +#[test] +fn greater_than() { + let input = " + int x = 5; + int y = 3; + bool f = x > y; + "; + + check_classical_decls( + input, + &expect![[r#" + ClassicalDeclarationStmt [9-19]: + symbol_id: 6 + ty_span: [9-12] + init_expr: Expr [17-18]: + ty: Int(None, true) + kind: Lit: Int(5) + [6] Symbol [13-14]: + name: x + type: Int(None, false) + qsharp_type: Int + io_kind: Default + ClassicalDeclarationStmt [28-38]: + symbol_id: 7 + ty_span: [28-31] + init_expr: Expr [36-37]: + ty: Int(None, true) + kind: Lit: Int(3) + [7] Symbol [32-33]: + name: y + type: Int(None, false) + qsharp_type: Int + io_kind: Default + ClassicalDeclarationStmt [47-62]: + symbol_id: 8 + ty_span: [47-51] + init_expr: Expr [56-61]: + ty: Bool(false) + kind: BinaryOpExpr: + op: Gt + lhs: Expr [56-57]: + ty: Int(None, false) + kind: SymbolId(6) + rhs: Expr [60-61]: + ty: Int(None, false) + kind: SymbolId(7) + [8] Symbol [52-53]: + name: f + type: Bool(false) + qsharp_type: bool + io_kind: Default + "#]], + ); +} + +#[test] +fn greater_than_equals() { + let input = " + int x = 5; + int y = 3; + bool e = x >= y; + "; + + check_classical_decls( + input, + &expect![[r#" + ClassicalDeclarationStmt [9-19]: + symbol_id: 6 + ty_span: [9-12] + init_expr: Expr [17-18]: + ty: Int(None, true) + kind: Lit: Int(5) + [6] Symbol [13-14]: + name: x + type: Int(None, false) + qsharp_type: Int + io_kind: Default + ClassicalDeclarationStmt [28-38]: + symbol_id: 7 + ty_span: [28-31] + init_expr: Expr [36-37]: + ty: Int(None, true) + kind: Lit: Int(3) + [7] Symbol [32-33]: + name: y + type: Int(None, false) + qsharp_type: Int + io_kind: Default + ClassicalDeclarationStmt [47-63]: + symbol_id: 8 + ty_span: [47-51] + init_expr: Expr [56-62]: + ty: Bool(false) + kind: BinaryOpExpr: + op: Gte + lhs: Expr [56-57]: + ty: Int(None, false) + kind: SymbolId(6) + rhs: Expr [61-62]: + ty: Int(None, false) + kind: SymbolId(7) + [8] Symbol [52-53]: + name: e + type: Bool(false) + qsharp_type: bool + io_kind: Default + "#]], + ); +} + +#[test] +fn less_than() { + let input = " + int x = 5; + int y = 3; + bool a = x < y; + "; + + check_classical_decls( + input, + &expect![[r#" + ClassicalDeclarationStmt [9-19]: + symbol_id: 6 + ty_span: [9-12] + init_expr: Expr [17-18]: + ty: Int(None, true) + kind: Lit: Int(5) + [6] Symbol [13-14]: + name: x + type: Int(None, false) + qsharp_type: Int + io_kind: Default + ClassicalDeclarationStmt [28-38]: + symbol_id: 7 + ty_span: [28-31] + init_expr: Expr [36-37]: + ty: Int(None, true) + kind: Lit: Int(3) + [7] Symbol [32-33]: + name: y + type: Int(None, false) + qsharp_type: Int + io_kind: Default + ClassicalDeclarationStmt [47-62]: + symbol_id: 8 + ty_span: [47-51] + init_expr: Expr [56-61]: + ty: Bool(false) + kind: BinaryOpExpr: + op: Lt + lhs: Expr [56-57]: + ty: Int(None, false) + kind: SymbolId(6) + rhs: Expr [60-61]: + ty: Int(None, false) + kind: SymbolId(7) + [8] Symbol [52-53]: + name: a + type: Bool(false) + qsharp_type: bool + io_kind: Default + "#]], + ); +} + +#[test] +fn less_than_equals() { + let input = " + int x = 5; + int y = 3; + bool c = x <= y; + "; + + check_classical_decls( + input, + &expect![[r#" + ClassicalDeclarationStmt [9-19]: + symbol_id: 6 + ty_span: [9-12] + init_expr: Expr [17-18]: + ty: Int(None, true) + kind: Lit: Int(5) + [6] Symbol [13-14]: + name: x + type: Int(None, false) + qsharp_type: Int + io_kind: Default + ClassicalDeclarationStmt [28-38]: + symbol_id: 7 + ty_span: [28-31] + init_expr: Expr [36-37]: + ty: Int(None, true) + kind: Lit: Int(3) + [7] Symbol [32-33]: + name: y + type: Int(None, false) + qsharp_type: Int + io_kind: Default + ClassicalDeclarationStmt [47-63]: + symbol_id: 8 + ty_span: [47-51] + init_expr: Expr [56-62]: + ty: Bool(false) + kind: BinaryOpExpr: + op: Lte + lhs: Expr [56-57]: + ty: Int(None, false) + kind: SymbolId(6) + rhs: Expr [61-62]: + ty: Int(None, false) + kind: SymbolId(7) + [8] Symbol [52-53]: + name: c + type: Bool(false) + qsharp_type: bool + io_kind: Default + "#]], + ); +} + +#[test] +fn equals() { + let input = " + int x = 5; + int y = 3; + bool b = x == y; + "; + + check_classical_decls( + input, + &expect![[r#" + ClassicalDeclarationStmt [9-19]: + symbol_id: 6 + ty_span: [9-12] + init_expr: Expr [17-18]: + ty: Int(None, true) + kind: Lit: Int(5) + [6] Symbol [13-14]: + name: x + type: Int(None, false) + qsharp_type: Int + io_kind: Default + ClassicalDeclarationStmt [28-38]: + symbol_id: 7 + ty_span: [28-31] + init_expr: Expr [36-37]: + ty: Int(None, true) + kind: Lit: Int(3) + [7] Symbol [32-33]: + name: y + type: Int(None, false) + qsharp_type: Int + io_kind: Default + ClassicalDeclarationStmt [47-63]: + symbol_id: 8 + ty_span: [47-51] + init_expr: Expr [56-62]: + ty: Bool(false) + kind: BinaryOpExpr: + op: Eq + lhs: Expr [56-57]: + ty: Int(None, false) + kind: SymbolId(6) + rhs: Expr [61-62]: + ty: Int(None, false) + kind: SymbolId(7) + [8] Symbol [52-53]: + name: b + type: Bool(false) + qsharp_type: bool + io_kind: Default + "#]], + ); +} + +#[test] +fn not_equals() { + let input = " + int x = 5; + int y = 3; + bool d = x != y; + "; + + check_classical_decls( + input, + &expect![[r#" + ClassicalDeclarationStmt [9-19]: + symbol_id: 6 + ty_span: [9-12] + init_expr: Expr [17-18]: + ty: Int(None, true) + kind: Lit: Int(5) + [6] Symbol [13-14]: + name: x + type: Int(None, false) + qsharp_type: Int + io_kind: Default + ClassicalDeclarationStmt [28-38]: + symbol_id: 7 + ty_span: [28-31] + init_expr: Expr [36-37]: + ty: Int(None, true) + kind: Lit: Int(3) + [7] Symbol [32-33]: + name: y + type: Int(None, false) + qsharp_type: Int + io_kind: Default + ClassicalDeclarationStmt [47-63]: + symbol_id: 8 + ty_span: [47-51] + init_expr: Expr [56-62]: + ty: Bool(false) + kind: BinaryOpExpr: + op: Neq + lhs: Expr [56-57]: + ty: Int(None, false) + kind: SymbolId(6) + rhs: Expr [61-62]: + ty: Int(None, false) + kind: SymbolId(7) + [8] Symbol [52-53]: + name: d + type: Bool(false) + qsharp_type: bool + io_kind: Default + "#]], + ); +} diff --git a/compiler/qsc_qasm3/src/semantic/tests/expression/binary/comparison/uint_to_uint.rs b/compiler/qsc_qasm3/src/semantic/tests/expression/binary/comparison/uint_to_uint.rs new file mode 100644 index 0000000000..ba39fdf302 --- /dev/null +++ b/compiler/qsc_qasm3/src/semantic/tests/expression/binary/comparison/uint_to_uint.rs @@ -0,0 +1,336 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +use expect_test::expect; + +use crate::semantic::tests::check_classical_decls; + +#[test] +fn greater_than() { + let input = " + uint x = 5; + uint y = 3; + bool f = x > y; + "; + + check_classical_decls( + input, + &expect![[r#" + ClassicalDeclarationStmt [9-20]: + symbol_id: 6 + ty_span: [9-13] + init_expr: Expr [18-19]: + ty: UInt(None, true) + kind: Lit: Int(5) + [6] Symbol [14-15]: + name: x + type: UInt(None, false) + qsharp_type: Int + io_kind: Default + ClassicalDeclarationStmt [29-40]: + symbol_id: 7 + ty_span: [29-33] + init_expr: Expr [38-39]: + ty: UInt(None, true) + kind: Lit: Int(3) + [7] Symbol [34-35]: + name: y + type: UInt(None, false) + qsharp_type: Int + io_kind: Default + ClassicalDeclarationStmt [49-64]: + symbol_id: 8 + ty_span: [49-53] + init_expr: Expr [58-63]: + ty: Bool(false) + kind: BinaryOpExpr: + op: Gt + lhs: Expr [58-59]: + ty: UInt(None, false) + kind: SymbolId(6) + rhs: Expr [62-63]: + ty: UInt(None, false) + kind: SymbolId(7) + [8] Symbol [54-55]: + name: f + type: Bool(false) + qsharp_type: bool + io_kind: Default + "#]], + ); +} + +#[test] +fn greater_than_equals() { + let input = " + uint x = 5; + uint y = 3; + bool e = x >= y; + "; + + check_classical_decls( + input, + &expect![[r#" + ClassicalDeclarationStmt [9-20]: + symbol_id: 6 + ty_span: [9-13] + init_expr: Expr [18-19]: + ty: UInt(None, true) + kind: Lit: Int(5) + [6] Symbol [14-15]: + name: x + type: UInt(None, false) + qsharp_type: Int + io_kind: Default + ClassicalDeclarationStmt [29-40]: + symbol_id: 7 + ty_span: [29-33] + init_expr: Expr [38-39]: + ty: UInt(None, true) + kind: Lit: Int(3) + [7] Symbol [34-35]: + name: y + type: UInt(None, false) + qsharp_type: Int + io_kind: Default + ClassicalDeclarationStmt [49-65]: + symbol_id: 8 + ty_span: [49-53] + init_expr: Expr [58-64]: + ty: Bool(false) + kind: BinaryOpExpr: + op: Gte + lhs: Expr [58-59]: + ty: UInt(None, false) + kind: SymbolId(6) + rhs: Expr [63-64]: + ty: UInt(None, false) + kind: SymbolId(7) + [8] Symbol [54-55]: + name: e + type: Bool(false) + qsharp_type: bool + io_kind: Default + "#]], + ); +} + +#[test] +fn less_than() { + let input = " + uint x = 5; + uint y = 3; + bool a = x < y; + "; + + check_classical_decls( + input, + &expect![[r#" + ClassicalDeclarationStmt [9-20]: + symbol_id: 6 + ty_span: [9-13] + init_expr: Expr [18-19]: + ty: UInt(None, true) + kind: Lit: Int(5) + [6] Symbol [14-15]: + name: x + type: UInt(None, false) + qsharp_type: Int + io_kind: Default + ClassicalDeclarationStmt [29-40]: + symbol_id: 7 + ty_span: [29-33] + init_expr: Expr [38-39]: + ty: UInt(None, true) + kind: Lit: Int(3) + [7] Symbol [34-35]: + name: y + type: UInt(None, false) + qsharp_type: Int + io_kind: Default + ClassicalDeclarationStmt [49-64]: + symbol_id: 8 + ty_span: [49-53] + init_expr: Expr [58-63]: + ty: Bool(false) + kind: BinaryOpExpr: + op: Lt + lhs: Expr [58-59]: + ty: UInt(None, false) + kind: SymbolId(6) + rhs: Expr [62-63]: + ty: UInt(None, false) + kind: SymbolId(7) + [8] Symbol [54-55]: + name: a + type: Bool(false) + qsharp_type: bool + io_kind: Default + "#]], + ); +} + +#[test] +fn less_than_equals() { + let input = " + uint x = 5; + uint y = 3; + bool c = x <= y; + "; + + check_classical_decls( + input, + &expect![[r#" + ClassicalDeclarationStmt [9-20]: + symbol_id: 6 + ty_span: [9-13] + init_expr: Expr [18-19]: + ty: UInt(None, true) + kind: Lit: Int(5) + [6] Symbol [14-15]: + name: x + type: UInt(None, false) + qsharp_type: Int + io_kind: Default + ClassicalDeclarationStmt [29-40]: + symbol_id: 7 + ty_span: [29-33] + init_expr: Expr [38-39]: + ty: UInt(None, true) + kind: Lit: Int(3) + [7] Symbol [34-35]: + name: y + type: UInt(None, false) + qsharp_type: Int + io_kind: Default + ClassicalDeclarationStmt [49-65]: + symbol_id: 8 + ty_span: [49-53] + init_expr: Expr [58-64]: + ty: Bool(false) + kind: BinaryOpExpr: + op: Lte + lhs: Expr [58-59]: + ty: UInt(None, false) + kind: SymbolId(6) + rhs: Expr [63-64]: + ty: UInt(None, false) + kind: SymbolId(7) + [8] Symbol [54-55]: + name: c + type: Bool(false) + qsharp_type: bool + io_kind: Default + "#]], + ); +} + +#[test] +fn equals() { + let input = " + uint x = 5; + uint y = 3; + bool b = x == y; + "; + + check_classical_decls( + input, + &expect![[r#" + ClassicalDeclarationStmt [9-20]: + symbol_id: 6 + ty_span: [9-13] + init_expr: Expr [18-19]: + ty: UInt(None, true) + kind: Lit: Int(5) + [6] Symbol [14-15]: + name: x + type: UInt(None, false) + qsharp_type: Int + io_kind: Default + ClassicalDeclarationStmt [29-40]: + symbol_id: 7 + ty_span: [29-33] + init_expr: Expr [38-39]: + ty: UInt(None, true) + kind: Lit: Int(3) + [7] Symbol [34-35]: + name: y + type: UInt(None, false) + qsharp_type: Int + io_kind: Default + ClassicalDeclarationStmt [49-65]: + symbol_id: 8 + ty_span: [49-53] + init_expr: Expr [58-64]: + ty: Bool(false) + kind: BinaryOpExpr: + op: Eq + lhs: Expr [58-59]: + ty: UInt(None, false) + kind: SymbolId(6) + rhs: Expr [63-64]: + ty: UInt(None, false) + kind: SymbolId(7) + [8] Symbol [54-55]: + name: b + type: Bool(false) + qsharp_type: bool + io_kind: Default + "#]], + ); +} + +#[test] +fn not_equals() { + let input = " + uint x = 5; + uint y = 3; + bool d = x != y; + "; + + check_classical_decls( + input, + &expect![[r#" + ClassicalDeclarationStmt [9-20]: + symbol_id: 6 + ty_span: [9-13] + init_expr: Expr [18-19]: + ty: UInt(None, true) + kind: Lit: Int(5) + [6] Symbol [14-15]: + name: x + type: UInt(None, false) + qsharp_type: Int + io_kind: Default + ClassicalDeclarationStmt [29-40]: + symbol_id: 7 + ty_span: [29-33] + init_expr: Expr [38-39]: + ty: UInt(None, true) + kind: Lit: Int(3) + [7] Symbol [34-35]: + name: y + type: UInt(None, false) + qsharp_type: Int + io_kind: Default + ClassicalDeclarationStmt [49-65]: + symbol_id: 8 + ty_span: [49-53] + init_expr: Expr [58-64]: + ty: Bool(false) + kind: BinaryOpExpr: + op: Neq + lhs: Expr [58-59]: + ty: UInt(None, false) + kind: SymbolId(6) + rhs: Expr [63-64]: + ty: UInt(None, false) + kind: SymbolId(7) + [8] Symbol [54-55]: + name: d + type: Bool(false) + qsharp_type: bool + io_kind: Default + "#]], + ); +} diff --git a/compiler/qsc_qasm3/src/semantic/tests/expression/binary/complex.rs b/compiler/qsc_qasm3/src/semantic/tests/expression/binary/complex.rs new file mode 100644 index 0000000000..8e3f78604f --- /dev/null +++ b/compiler/qsc_qasm3/src/semantic/tests/expression/binary/complex.rs @@ -0,0 +1,66 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +use expect_test::expect; + +use crate::semantic::tests::check_stmt_kinds; + +#[test] +#[ignore = "not yet implemented"] +fn subtraction() { + let input = " + input complex[float] a; + input complex[float] b; + complex x = (a - b); + "; + + check_stmt_kinds(input, &expect![[r#""#]]); +} + +#[test] +#[ignore = "not yet implemented"] +fn addition() { + let input = " + input complex[float] a; + input complex[float] b; + complex x = (a + b); + "; + + check_stmt_kinds(input, &expect![[r#""#]]); +} + +#[test] +#[ignore = "not yet implemented"] +fn multiplication() { + let input = " + input complex[float] a; + input complex[float] b; + complex x = (a * b); + "; + + check_stmt_kinds(input, &expect![[r#""#]]); +} + +#[test] +#[ignore = "not yet implemented"] +fn division() { + let input = " + input complex[float] a; + input complex[float] b; + complex x = (a / b); + "; + + check_stmt_kinds(input, &expect![[r#""#]]); +} + +#[test] +#[ignore = "not yet implemented"] +fn power() { + let input = " + input complex[float] a; + input complex[float] b; + complex x = (a ** b); + "; + + check_stmt_kinds(input, &expect![[r#""#]]); +} diff --git a/compiler/qsc_qasm3/src/semantic/tests/expression/binary/ident.rs b/compiler/qsc_qasm3/src/semantic/tests/expression/binary/ident.rs new file mode 100644 index 0000000000..60b21ef238 --- /dev/null +++ b/compiler/qsc_qasm3/src/semantic/tests/expression/binary/ident.rs @@ -0,0 +1,161 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +use expect_test::expect; + +use crate::semantic::tests::check_stmt_kinds; +#[test] +fn mutable_int_idents_without_width_can_be_multiplied() { + let input = " + int x = 5; + int y = 3; + x * y; + "; + + check_stmt_kinds( + input, + &expect![[r#" + ClassicalDeclarationStmt [9-19]: + symbol_id: 6 + ty_span: [9-12] + init_expr: Expr [17-18]: + ty: Int(None, true) + kind: Lit: Int(5) + ClassicalDeclarationStmt [28-38]: + symbol_id: 7 + ty_span: [28-31] + init_expr: Expr [36-37]: + ty: Int(None, true) + kind: Lit: Int(3) + ExprStmt [47-53]: + expr: Expr [47-52]: + ty: Int(None, false) + kind: BinaryOpExpr: + op: Mul + lhs: Expr [47-48]: + ty: Int(None, false) + kind: SymbolId(6) + rhs: Expr [51-52]: + ty: Int(None, false) + kind: SymbolId(7) + "#]], + ); +} + +#[test] +fn const_int_idents_without_width_can_be_multiplied() { + let input = " + const int x = 5; + const int y = 3; + x * y; + "; + + check_stmt_kinds( + input, + &expect![[r#" + ClassicalDeclarationStmt [9-25]: + symbol_id: 6 + ty_span: [15-18] + init_expr: Expr [23-24]: + ty: Int(None, true) + kind: Lit: Int(5) + ClassicalDeclarationStmt [34-50]: + symbol_id: 7 + ty_span: [40-43] + init_expr: Expr [48-49]: + ty: Int(None, true) + kind: Lit: Int(3) + ExprStmt [59-65]: + expr: Expr [59-64]: + ty: Int(None, true) + kind: BinaryOpExpr: + op: Mul + lhs: Expr [59-60]: + ty: Int(None, true) + kind: SymbolId(6) + rhs: Expr [63-64]: + ty: Int(None, true) + kind: SymbolId(7) + "#]], + ); +} + +#[test] +fn const_and_mut_int_idents_without_width_can_be_multiplied() { + let input = " + int x = 5; + const int y = 3; + x * y; + "; + + check_stmt_kinds( + input, + &expect![[r#" + ClassicalDeclarationStmt [9-19]: + symbol_id: 6 + ty_span: [9-12] + init_expr: Expr [17-18]: + ty: Int(None, true) + kind: Lit: Int(5) + ClassicalDeclarationStmt [28-44]: + symbol_id: 7 + ty_span: [34-37] + init_expr: Expr [42-43]: + ty: Int(None, true) + kind: Lit: Int(3) + ExprStmt [53-59]: + expr: Expr [53-58]: + ty: Int(None, false) + kind: BinaryOpExpr: + op: Mul + lhs: Expr [53-54]: + ty: Int(None, false) + kind: SymbolId(6) + rhs: Expr [57-58]: + ty: Int(None, true) + kind: SymbolId(7) + "#]], + ); +} + +#[test] +fn const_int_idents_widthless_lhs_can_be_multiplied_by_explicit_width_int() { + let input = " + const int[32] x = 5; + const int y = 3; + x * y; + "; + + check_stmt_kinds( + input, + &expect![[r#" + ClassicalDeclarationStmt [9-29]: + symbol_id: 6 + ty_span: [15-22] + init_expr: Expr [27-28]: + ty: Int(Some(32), true) + kind: Lit: Int(5) + ClassicalDeclarationStmt [38-54]: + symbol_id: 7 + ty_span: [44-47] + init_expr: Expr [52-53]: + ty: Int(None, true) + kind: Lit: Int(3) + ExprStmt [63-69]: + expr: Expr [63-68]: + ty: Int(None, true) + kind: BinaryOpExpr: + op: Mul + lhs: Expr [63-64]: + ty: Int(None, true) + kind: Cast [0-0]: + ty: Int(None, true) + expr: Expr [63-64]: + ty: Int(Some(32), true) + kind: SymbolId(6) + rhs: Expr [67-68]: + ty: Int(None, true) + kind: SymbolId(7) + "#]], + ); +} diff --git a/compiler/qsc_qasm3/src/semantic/tests/expression/implicit_cast_from_angle.rs b/compiler/qsc_qasm3/src/semantic/tests/expression/implicit_cast_from_angle.rs new file mode 100644 index 0000000000..b09516234a --- /dev/null +++ b/compiler/qsc_qasm3/src/semantic/tests/expression/implicit_cast_from_angle.rs @@ -0,0 +1,556 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +use expect_test::expect; + +use crate::semantic::tests::check_classical_decls; + +#[test] +fn to_bit_implicitly() { + let input = " + angle x = 42.; + bit y = x; + "; + + check_classical_decls( + input, + &expect![[r#" + ClassicalDeclarationStmt [9-23]: + symbol_id: 6 + ty_span: [9-14] + init_expr: Expr [19-22]: + ty: Angle(None, true) + kind: Lit: Float(42.0) + [6] Symbol [15-16]: + name: x + type: Angle(None, false) + qsharp_type: Double + io_kind: Default + ClassicalDeclarationStmt [32-42]: + symbol_id: 7 + ty_span: [32-35] + init_expr: Expr [40-41]: + ty: Bit(false) + kind: Cast [0-0]: + ty: Bit(false) + expr: Expr [40-41]: + ty: Angle(None, false) + kind: SymbolId(6) + [7] Symbol [36-37]: + name: y + type: Bit(false) + qsharp_type: Result + io_kind: Default + "#]], + ); +} + +#[test] +fn explicit_width_to_bit_implicitly_fails() { + let input = " + angle[64] x = 42.; + bit y = x; + "; + + check_classical_decls( + input, + &expect![[r#" + ClassicalDeclarationStmt [9-27]: + symbol_id: 6 + ty_span: [9-18] + init_expr: Expr [23-26]: + ty: Angle(Some(64), true) + kind: Lit: Float(42.0) + [6] Symbol [19-20]: + name: x + type: Angle(Some(64), false) + qsharp_type: Double + io_kind: Default + ClassicalDeclarationStmt [36-46]: + symbol_id: 7 + ty_span: [36-39] + init_expr: Expr [44-45]: + ty: Bit(false) + kind: Cast [0-0]: + ty: Bit(false) + expr: Expr [44-45]: + ty: Angle(Some(64), false) + kind: SymbolId(6) + [7] Symbol [40-41]: + name: y + type: Bit(false) + qsharp_type: Result + io_kind: Default + "#]], + ); +} + +#[test] +fn to_bool_implicitly() { + let input = " + angle x = 42.; + bool y = x; + "; + check_classical_decls( + input, + &expect![[r#" + ClassicalDeclarationStmt [9-23]: + symbol_id: 6 + ty_span: [9-14] + init_expr: Expr [19-22]: + ty: Angle(None, true) + kind: Lit: Float(42.0) + [6] Symbol [15-16]: + name: x + type: Angle(None, false) + qsharp_type: Double + io_kind: Default + ClassicalDeclarationStmt [32-43]: + symbol_id: 7 + ty_span: [32-36] + init_expr: Expr [41-42]: + ty: Bool(false) + kind: Cast [0-0]: + ty: Bool(false) + expr: Expr [41-42]: + ty: Angle(None, false) + kind: SymbolId(6) + [7] Symbol [37-38]: + name: y + type: Bool(false) + qsharp_type: bool + io_kind: Default + "#]], + ); +} + +#[test] +fn to_implicit_int_implicitly_fails() { + let input = " + angle x = 42.; + int y = x; + "; + + check_classical_decls( + input, + &expect![[r#" + Program: + version: + statements: + Stmt [9-23]: + annotations: + kind: ClassicalDeclarationStmt [9-23]: + symbol_id: 6 + ty_span: [9-14] + init_expr: Expr [19-22]: + ty: Angle(None, true) + kind: Lit: Float(42.0) + + [Qsc.Qasm3.Compile.CannotCast + + x Cannot cast expression of type Angle(None, false) to type Int(None, false) + ,-[test:3:9] + 2 | angle x = 42.; + 3 | int y = x; + : ^^^^^^^^^^ + 4 | + `---- + ]"#]], + ); +} + +#[test] +fn to_explicit_int_implicitly_fails() { + let input = " + angle x = 42.; + int[32] y = x; + "; + + check_classical_decls( + input, + &expect![[r#" + Program: + version: + statements: + Stmt [9-23]: + annotations: + kind: ClassicalDeclarationStmt [9-23]: + symbol_id: 6 + ty_span: [9-14] + init_expr: Expr [19-22]: + ty: Angle(None, true) + kind: Lit: Float(42.0) + + [Qsc.Qasm3.Compile.CannotCast + + x Cannot cast expression of type Angle(None, false) to type Int(Some(32), + | false) + ,-[test:3:9] + 2 | angle x = 42.; + 3 | int[32] y = x; + : ^^^^^^^^^^^^^^ + 4 | + `---- + ]"#]], + ); +} + +#[test] +fn to_implicit_uint_implicitly_fails() { + let input = " + angle x = 42.; + uint y = x; + "; + + check_classical_decls( + input, + &expect![[r#" + Program: + version: + statements: + Stmt [9-23]: + annotations: + kind: ClassicalDeclarationStmt [9-23]: + symbol_id: 6 + ty_span: [9-14] + init_expr: Expr [19-22]: + ty: Angle(None, true) + kind: Lit: Float(42.0) + + [Qsc.Qasm3.Compile.CannotCast + + x Cannot cast expression of type Angle(None, false) to type UInt(None, + | false) + ,-[test:3:9] + 2 | angle x = 42.; + 3 | uint y = x; + : ^^^^^^^^^^^ + 4 | + `---- + ]"#]], + ); +} + +#[test] +fn negative_lit_to_implicit_uint_implicitly_fails() { + let input = " + angle x = -42.; + uint y = x; + "; + + check_classical_decls( + input, + &expect![[r#" + Program: + version: + statements: + Stmt [9-24]: + annotations: + kind: ClassicalDeclarationStmt [9-24]: + symbol_id: 6 + ty_span: [9-14] + init_expr: Expr [20-23]: + ty: Angle(None, true) + kind: Lit: Float(-42.0) + + [Qsc.Qasm3.Compile.CannotCast + + x Cannot cast expression of type Angle(None, false) to type UInt(None, + | false) + ,-[test:3:9] + 2 | angle x = -42.; + 3 | uint y = x; + : ^^^^^^^^^^^ + 4 | + `---- + ]"#]], + ); +} + +#[test] +fn to_explicit_uint_implicitly_fails() { + let input = " + angle x = 42.; + uint[32] y = x; + "; + + check_classical_decls( + input, + &expect![[r#" + Program: + version: + statements: + Stmt [9-23]: + annotations: + kind: ClassicalDeclarationStmt [9-23]: + symbol_id: 6 + ty_span: [9-14] + init_expr: Expr [19-22]: + ty: Angle(None, true) + kind: Lit: Float(42.0) + + [Qsc.Qasm3.Compile.CannotCast + + x Cannot cast expression of type Angle(None, false) to type UInt(Some(32), + | false) + ,-[test:3:9] + 2 | angle x = 42.; + 3 | uint[32] y = x; + : ^^^^^^^^^^^^^^^ + 4 | + `---- + ]"#]], + ); +} + +#[test] +fn to_explicit_bigint_implicitly_fails() { + let input = " + angle x = 42.; + int[65] y = x; + "; + + check_classical_decls( + input, + &expect![[r#" + Program: + version: + statements: + Stmt [9-23]: + annotations: + kind: ClassicalDeclarationStmt [9-23]: + symbol_id: 6 + ty_span: [9-14] + init_expr: Expr [19-22]: + ty: Angle(None, true) + kind: Lit: Float(42.0) + + [Qsc.Qasm3.Compile.CannotCast + + x Cannot cast expression of type Angle(None, false) to type Int(Some(65), + | false) + ,-[test:3:9] + 2 | angle x = 42.; + 3 | int[65] y = x; + : ^^^^^^^^^^^^^^ + 4 | + `---- + ]"#]], + ); +} + +#[test] +fn to_implicit_float_implicitly_fails() { + let input = " + angle x = 42.; + float y = x; + "; + + check_classical_decls( + input, + &expect![[r#" + Program: + version: + statements: + Stmt [9-23]: + annotations: + kind: ClassicalDeclarationStmt [9-23]: + symbol_id: 6 + ty_span: [9-14] + init_expr: Expr [19-22]: + ty: Angle(None, true) + kind: Lit: Float(42.0) + + [Qsc.Qasm3.Compile.CannotCast + + x Cannot cast expression of type Angle(None, false) to type Float(None, + | false) + ,-[test:3:9] + 2 | angle x = 42.; + 3 | float y = x; + : ^^^^^^^^^^^^ + 4 | + `---- + ]"#]], + ); +} + +#[test] +fn to_explicit_float_implicitly_fails() { + let input = " + angle x = 42.; + float[32] y = x; + "; + + check_classical_decls( + input, + &expect![[r#" + Program: + version: + statements: + Stmt [9-23]: + annotations: + kind: ClassicalDeclarationStmt [9-23]: + symbol_id: 6 + ty_span: [9-14] + init_expr: Expr [19-22]: + ty: Angle(None, true) + kind: Lit: Float(42.0) + + [Qsc.Qasm3.Compile.CannotCast + + x Cannot cast expression of type Angle(None, false) to type Float(Some(32), + | false) + ,-[test:3:9] + 2 | angle x = 42.; + 3 | float[32] y = x; + : ^^^^^^^^^^^^^^^^ + 4 | + `---- + ]"#]], + ); +} + +#[test] +fn to_implicit_complex_implicitly_fails() { + let input = " + angle x = 42.; + complex[float] y = x; + "; + + check_classical_decls( + input, + &expect![[r#" + Program: + version: + statements: + Stmt [9-23]: + annotations: + kind: ClassicalDeclarationStmt [9-23]: + symbol_id: 6 + ty_span: [9-14] + init_expr: Expr [19-22]: + ty: Angle(None, true) + kind: Lit: Float(42.0) + + [Qsc.Qasm3.Compile.CannotCast + + x Cannot cast expression of type Angle(None, false) to type Complex(None, + | false) + ,-[test:3:9] + 2 | angle x = 42.; + 3 | complex[float] y = x; + : ^^^^^^^^^^^^^^^^^^^^^ + 4 | + `---- + ]"#]], + ); +} + +#[test] +fn to_explicit_complex_implicitly_fails() { + let input = " + angle x = 42.; + complex[float[32]] y = x; + "; + + check_classical_decls( + input, + &expect![[r#" + Program: + version: + statements: + Stmt [9-23]: + annotations: + kind: ClassicalDeclarationStmt [9-23]: + symbol_id: 6 + ty_span: [9-14] + init_expr: Expr [19-22]: + ty: Angle(None, true) + kind: Lit: Float(42.0) + + [Qsc.Qasm3.Compile.CannotCast + + x Cannot cast expression of type Angle(None, false) to type + | Complex(Some(32), false) + ,-[test:3:9] + 2 | angle x = 42.; + 3 | complex[float[32]] y = x; + : ^^^^^^^^^^^^^^^^^^^^^^^^^ + 4 | + `---- + ]"#]], + ); +} + +#[test] +fn to_angle_implicitly() { + let input = " + angle x = 42.; + angle y = x; + "; + + check_classical_decls( + input, + &expect![[r#" + ClassicalDeclarationStmt [9-23]: + symbol_id: 6 + ty_span: [9-14] + init_expr: Expr [19-22]: + ty: Angle(None, true) + kind: Lit: Float(42.0) + [6] Symbol [15-16]: + name: x + type: Angle(None, false) + qsharp_type: Double + io_kind: Default + ClassicalDeclarationStmt [32-44]: + symbol_id: 7 + ty_span: [32-37] + init_expr: Expr [42-43]: + ty: Angle(None, false) + kind: SymbolId(6) + [7] Symbol [38-39]: + name: y + type: Angle(None, false) + qsharp_type: Double + io_kind: Default + "#]], + ); +} + +#[test] +fn to_explicit_angle_implicitly() { + let input = " + angle x = 42.; + angle[4] y = x; + "; + + check_classical_decls( + input, + &expect![[r#" + ClassicalDeclarationStmt [9-23]: + symbol_id: 6 + ty_span: [9-14] + init_expr: Expr [19-22]: + ty: Angle(None, true) + kind: Lit: Float(42.0) + [6] Symbol [15-16]: + name: x + type: Angle(None, false) + qsharp_type: Double + io_kind: Default + ClassicalDeclarationStmt [32-47]: + symbol_id: 7 + ty_span: [32-40] + init_expr: Expr [45-46]: + ty: Angle(Some(4), false) + kind: SymbolId(6) + [7] Symbol [41-42]: + name: y + type: Angle(Some(4), false) + qsharp_type: Double + io_kind: Default + "#]], + ); +} diff --git a/compiler/qsc_qasm3/src/semantic/tests/expression/implicit_cast_from_bit.rs b/compiler/qsc_qasm3/src/semantic/tests/expression/implicit_cast_from_bit.rs new file mode 100644 index 0000000000..0c701f3601 --- /dev/null +++ b/compiler/qsc_qasm3/src/semantic/tests/expression/implicit_cast_from_bit.rs @@ -0,0 +1,303 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +use expect_test::expect; + +use crate::semantic::tests::check_classical_decls; + +#[test] +#[ignore = "not yet implemented"] +fn to_angle_implicitly() { + let input = r#" + bit x = 1; + angle y = x; + "#; + + check_classical_decls(input, &expect![[r#""#]]); +} + +#[test] +#[ignore = "not yet implemented"] +fn to_explicit_angle_implicitly() { + let input = r#" + bit x = 1; + angle[4] y = x; + "#; + + check_classical_decls(input, &expect![[r#""#]]); +} + +#[test] +fn to_bool_implicitly() { + let input = r#" + bit x = 1; + bool y = x; + "#; + + check_classical_decls( + input, + &expect![[r#" + ClassicalDeclarationStmt [10-20]: + symbol_id: 6 + ty_span: [10-13] + init_expr: Expr [18-19]: + ty: Bit(true) + kind: Lit: Int(1) + [6] Symbol [14-15]: + name: x + type: Bit(false) + qsharp_type: Result + io_kind: Default + ClassicalDeclarationStmt [30-41]: + symbol_id: 7 + ty_span: [30-34] + init_expr: Expr [39-40]: + ty: Bool(false) + kind: Cast [0-0]: + ty: Bool(false) + expr: Expr [39-40]: + ty: Bit(false) + kind: SymbolId(6) + [7] Symbol [35-36]: + name: y + type: Bool(false) + qsharp_type: bool + io_kind: Default + "#]], + ); +} + +#[test] +fn to_implicit_int_implicitly() { + let input = " + bit x = 1; + int y = x; + "; + + check_classical_decls( + input, + &expect![[r#" + ClassicalDeclarationStmt [9-19]: + symbol_id: 6 + ty_span: [9-12] + init_expr: Expr [17-18]: + ty: Bit(true) + kind: Lit: Int(1) + [6] Symbol [13-14]: + name: x + type: Bit(false) + qsharp_type: Result + io_kind: Default + ClassicalDeclarationStmt [28-38]: + symbol_id: 7 + ty_span: [28-31] + init_expr: Expr [36-37]: + ty: Int(None, false) + kind: Cast [0-0]: + ty: Int(None, false) + expr: Expr [36-37]: + ty: Bit(false) + kind: SymbolId(6) + [7] Symbol [32-33]: + name: y + type: Int(None, false) + qsharp_type: Int + io_kind: Default + "#]], + ); +} + +#[test] +fn to_explicit_int_implicitly() { + let input = " + bit x = 1; + int[32] y = x; + "; + + check_classical_decls( + input, + &expect![[r#" + ClassicalDeclarationStmt [9-19]: + symbol_id: 6 + ty_span: [9-12] + init_expr: Expr [17-18]: + ty: Bit(true) + kind: Lit: Int(1) + [6] Symbol [13-14]: + name: x + type: Bit(false) + qsharp_type: Result + io_kind: Default + ClassicalDeclarationStmt [28-42]: + symbol_id: 7 + ty_span: [28-35] + init_expr: Expr [40-41]: + ty: Int(Some(32), false) + kind: Cast [0-0]: + ty: Int(Some(32), false) + expr: Expr [40-41]: + ty: Bit(false) + kind: SymbolId(6) + [7] Symbol [36-37]: + name: y + type: Int(Some(32), false) + qsharp_type: Int + io_kind: Default + "#]], + ); +} + +#[test] +fn to_implicit_uint_implicitly() { + let input = " + bit x = 1; + uint y = x; + "; + + check_classical_decls( + input, + &expect![[r#" + ClassicalDeclarationStmt [9-19]: + symbol_id: 6 + ty_span: [9-12] + init_expr: Expr [17-18]: + ty: Bit(true) + kind: Lit: Int(1) + [6] Symbol [13-14]: + name: x + type: Bit(false) + qsharp_type: Result + io_kind: Default + ClassicalDeclarationStmt [28-39]: + symbol_id: 7 + ty_span: [28-32] + init_expr: Expr [37-38]: + ty: UInt(None, false) + kind: Cast [0-0]: + ty: UInt(None, false) + expr: Expr [37-38]: + ty: Bit(false) + kind: SymbolId(6) + [7] Symbol [33-34]: + name: y + type: UInt(None, false) + qsharp_type: Int + io_kind: Default + "#]], + ); +} + +#[test] +fn to_explicit_uint_implicitly() { + let input = " + bit x = 1; + uint[32] y = x; + "; + + check_classical_decls( + input, + &expect![[r#" + ClassicalDeclarationStmt [9-19]: + symbol_id: 6 + ty_span: [9-12] + init_expr: Expr [17-18]: + ty: Bit(true) + kind: Lit: Int(1) + [6] Symbol [13-14]: + name: x + type: Bit(false) + qsharp_type: Result + io_kind: Default + ClassicalDeclarationStmt [28-43]: + symbol_id: 7 + ty_span: [28-36] + init_expr: Expr [41-42]: + ty: UInt(Some(32), false) + kind: Cast [0-0]: + ty: UInt(Some(32), false) + expr: Expr [41-42]: + ty: Bit(false) + kind: SymbolId(6) + [7] Symbol [37-38]: + name: y + type: UInt(Some(32), false) + qsharp_type: Int + io_kind: Default + "#]], + ); +} + +#[test] +fn to_explicit_bigint_implicitly() { + let input = " + bit x = 1; + int[65] y = x; + "; + + check_classical_decls( + input, + &expect![[r#" + ClassicalDeclarationStmt [9-19]: + symbol_id: 6 + ty_span: [9-12] + init_expr: Expr [17-18]: + ty: Bit(true) + kind: Lit: Int(1) + [6] Symbol [13-14]: + name: x + type: Bit(false) + qsharp_type: Result + io_kind: Default + ClassicalDeclarationStmt [28-42]: + symbol_id: 7 + ty_span: [28-35] + init_expr: Expr [40-41]: + ty: Int(Some(65), false) + kind: Cast [0-0]: + ty: Int(Some(65), false) + expr: Expr [40-41]: + ty: Bit(false) + kind: SymbolId(6) + [7] Symbol [36-37]: + name: y + type: Int(Some(65), false) + qsharp_type: BigInt + io_kind: Default + "#]], + ); +} + +#[test] +fn to_implicit_float_implicitly_fails() { + let input = " + bit x = 1; + float y = x; + "; + + check_classical_decls( + input, + &expect![[r#" + Program: + version: + statements: + Stmt [9-19]: + annotations: + kind: ClassicalDeclarationStmt [9-19]: + symbol_id: 6 + ty_span: [9-12] + init_expr: Expr [17-18]: + ty: Bit(true) + kind: Lit: Int(1) + + [Qsc.Qasm3.Compile.CannotCast + + x Cannot cast expression of type Bit(false) to type Float(None, false) + ,-[test:3:9] + 2 | bit x = 1; + 3 | float y = x; + : ^^^^^^^^^^^^ + 4 | + `---- + ]"#]], + ); +} diff --git a/compiler/qsc_qasm3/src/semantic/tests/expression/implicit_cast_from_bitarray.rs b/compiler/qsc_qasm3/src/semantic/tests/expression/implicit_cast_from_bitarray.rs new file mode 100644 index 0000000000..019a219e04 --- /dev/null +++ b/compiler/qsc_qasm3/src/semantic/tests/expression/implicit_cast_from_bitarray.rs @@ -0,0 +1,97 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +use expect_test::expect; + +use crate::semantic::tests::check_classical_decls; + +#[test] +#[ignore = "not yet implemented"] +fn to_int_decl_implicitly() { + let input = r#" + bit[5] reg; + int b = reg; + "#; + + check_classical_decls(input, &expect![[r#""#]]); +} + +#[test] +#[ignore = "not yet implemented"] +fn to_int_assignment_implicitly() { + let input = r#" + bit[5] reg; + int a; + a = reg; + "#; + + check_classical_decls(input, &expect![[r#""#]]); +} + +#[test] +#[ignore = "not yet implemented"] +fn to_int_with_equal_width_in_assignment_implicitly() { + let input = r#" + bit[5] reg; + int[5] a; + a = reg; + "#; + + check_classical_decls(input, &expect![[r#""#]]); +} + +#[test] +#[ignore = "not yet implemented"] +fn to_int_with_equal_width_in_decl_implicitly() { + let input = r#" + bit[5] reg; + int[5] a = reg; + "#; + + check_classical_decls(input, &expect![[r#""#]]); +} + +#[test] +#[ignore = "not yet implemented"] +fn to_int_with_higher_width_implicitly_fails() { + let input = " + int[6] a; + bit[5] reg; + a = reg; + "; + + check_classical_decls(input, &expect![[r#""#]]); +} + +#[test] +#[ignore = "not yet implemented"] +fn to_int_with_higher_width_decl_implicitly_fails() { + let input = " + bit[5] reg; + int[6] a = reg; + "; + check_classical_decls(input, &expect![[r#""#]]); +} + +#[test] +#[ignore = "not yet implemented"] +fn to_int_with_lower_width_implicitly_fails() { + let input = " + input int[4] a; + bit[5] reg; + a = reg; + "; + + check_classical_decls(input, &expect![[r#""#]]); +} + +#[test] +#[ignore = "not yet implemented"] +fn to_int_with_lower_width_decl_implicitly_fails() { + let input = " + bit[5] reg; + int[4] a = reg; + "; + + check_classical_decls(input, &expect![[r#""#]]); +} diff --git a/compiler/qsc_qasm3/src/semantic/tests/expression/implicit_cast_from_bool.rs b/compiler/qsc_qasm3/src/semantic/tests/expression/implicit_cast_from_bool.rs new file mode 100644 index 0000000000..cd4178ebe8 --- /dev/null +++ b/compiler/qsc_qasm3/src/semantic/tests/expression/implicit_cast_from_bool.rs @@ -0,0 +1,326 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +use expect_test::expect; + +use crate::semantic::tests::check_classical_decls; + +#[test] +fn to_bit_implicitly() { + let input = " + bool x = true; + bit y = x; + "; + + check_classical_decls( + input, + &expect![[r#" + ClassicalDeclarationStmt [9-23]: + symbol_id: 6 + ty_span: [9-13] + init_expr: Expr [18-22]: + ty: Bool(true) + kind: Lit: Bool(true) + [6] Symbol [14-15]: + name: x + type: Bool(false) + qsharp_type: bool + io_kind: Default + ClassicalDeclarationStmt [32-42]: + symbol_id: 7 + ty_span: [32-35] + init_expr: Expr [40-41]: + ty: Bit(false) + kind: Cast [0-0]: + ty: Bit(false) + expr: Expr [40-41]: + ty: Bool(false) + kind: SymbolId(6) + [7] Symbol [36-37]: + name: y + type: Bit(false) + qsharp_type: Result + io_kind: Default + "#]], + ); +} + +#[test] +fn to_implicit_int_implicitly() { + let input = " + bool x = true; + int y = x; + "; + + check_classical_decls( + input, + &expect![[r#" + ClassicalDeclarationStmt [9-23]: + symbol_id: 6 + ty_span: [9-13] + init_expr: Expr [18-22]: + ty: Bool(true) + kind: Lit: Bool(true) + [6] Symbol [14-15]: + name: x + type: Bool(false) + qsharp_type: bool + io_kind: Default + ClassicalDeclarationStmt [32-42]: + symbol_id: 7 + ty_span: [32-35] + init_expr: Expr [40-41]: + ty: Int(None, false) + kind: Cast [0-0]: + ty: Int(None, false) + expr: Expr [40-41]: + ty: Bool(false) + kind: SymbolId(6) + [7] Symbol [36-37]: + name: y + type: Int(None, false) + qsharp_type: Int + io_kind: Default + "#]], + ); +} + +#[test] +fn to_explicit_int_implicitly() { + let input = " + bool x = true; + int[32] y = x; + "; + + check_classical_decls( + input, + &expect![[r#" + ClassicalDeclarationStmt [9-23]: + symbol_id: 6 + ty_span: [9-13] + init_expr: Expr [18-22]: + ty: Bool(true) + kind: Lit: Bool(true) + [6] Symbol [14-15]: + name: x + type: Bool(false) + qsharp_type: bool + io_kind: Default + ClassicalDeclarationStmt [32-46]: + symbol_id: 7 + ty_span: [32-39] + init_expr: Expr [44-45]: + ty: Int(Some(32), false) + kind: Cast [0-0]: + ty: Int(Some(32), false) + expr: Expr [44-45]: + ty: Bool(false) + kind: SymbolId(6) + [7] Symbol [40-41]: + name: y + type: Int(Some(32), false) + qsharp_type: Int + io_kind: Default + "#]], + ); +} + +#[test] +fn to_implicit_uint_implicitly() { + let input = " + bool x = true; + uint y = x; + "; + + check_classical_decls( + input, + &expect![[r#" + ClassicalDeclarationStmt [9-23]: + symbol_id: 6 + ty_span: [9-13] + init_expr: Expr [18-22]: + ty: Bool(true) + kind: Lit: Bool(true) + [6] Symbol [14-15]: + name: x + type: Bool(false) + qsharp_type: bool + io_kind: Default + ClassicalDeclarationStmt [32-43]: + symbol_id: 7 + ty_span: [32-36] + init_expr: Expr [41-42]: + ty: UInt(None, false) + kind: Cast [0-0]: + ty: UInt(None, false) + expr: Expr [41-42]: + ty: Bool(false) + kind: SymbolId(6) + [7] Symbol [37-38]: + name: y + type: UInt(None, false) + qsharp_type: Int + io_kind: Default + "#]], + ); +} + +#[test] +fn to_explicit_uint_implicitly() { + let input = " + bool x = true; + uint[32] y = x; + "; + + check_classical_decls( + input, + &expect![[r#" + ClassicalDeclarationStmt [9-23]: + symbol_id: 6 + ty_span: [9-13] + init_expr: Expr [18-22]: + ty: Bool(true) + kind: Lit: Bool(true) + [6] Symbol [14-15]: + name: x + type: Bool(false) + qsharp_type: bool + io_kind: Default + ClassicalDeclarationStmt [32-47]: + symbol_id: 7 + ty_span: [32-40] + init_expr: Expr [45-46]: + ty: UInt(Some(32), false) + kind: Cast [0-0]: + ty: UInt(Some(32), false) + expr: Expr [45-46]: + ty: Bool(false) + kind: SymbolId(6) + [7] Symbol [41-42]: + name: y + type: UInt(Some(32), false) + qsharp_type: Int + io_kind: Default + "#]], + ); +} + +#[test] +fn to_explicit_bigint_implicitly() { + let input = " + bool x = true; + int[65] y = x; + "; + + check_classical_decls( + input, + &expect![[r#" + ClassicalDeclarationStmt [9-23]: + symbol_id: 6 + ty_span: [9-13] + init_expr: Expr [18-22]: + ty: Bool(true) + kind: Lit: Bool(true) + [6] Symbol [14-15]: + name: x + type: Bool(false) + qsharp_type: bool + io_kind: Default + ClassicalDeclarationStmt [32-46]: + symbol_id: 7 + ty_span: [32-39] + init_expr: Expr [44-45]: + ty: Int(Some(65), false) + kind: Cast [0-0]: + ty: Int(Some(65), false) + expr: Expr [44-45]: + ty: Bool(false) + kind: SymbolId(6) + [7] Symbol [40-41]: + name: y + type: Int(Some(65), false) + qsharp_type: BigInt + io_kind: Default + "#]], + ); +} + +#[test] +fn to_implicit_float_implicitly() { + let input = " + bool x = true; + float y = x; + "; + + check_classical_decls( + input, + &expect![[r#" + ClassicalDeclarationStmt [9-23]: + symbol_id: 6 + ty_span: [9-13] + init_expr: Expr [18-22]: + ty: Bool(true) + kind: Lit: Bool(true) + [6] Symbol [14-15]: + name: x + type: Bool(false) + qsharp_type: bool + io_kind: Default + ClassicalDeclarationStmt [32-44]: + symbol_id: 7 + ty_span: [32-37] + init_expr: Expr [42-43]: + ty: Float(None, false) + kind: Cast [0-0]: + ty: Float(None, false) + expr: Expr [42-43]: + ty: Bool(false) + kind: SymbolId(6) + [7] Symbol [38-39]: + name: y + type: Float(None, false) + qsharp_type: Double + io_kind: Default + "#]], + ); +} + +#[test] +fn to_explicit_float_implicitly() { + let input = " + bool x = true; + float[32] y = x; + "; + + check_classical_decls( + input, + &expect![[r#" + ClassicalDeclarationStmt [9-23]: + symbol_id: 6 + ty_span: [9-13] + init_expr: Expr [18-22]: + ty: Bool(true) + kind: Lit: Bool(true) + [6] Symbol [14-15]: + name: x + type: Bool(false) + qsharp_type: bool + io_kind: Default + ClassicalDeclarationStmt [32-48]: + symbol_id: 7 + ty_span: [32-41] + init_expr: Expr [46-47]: + ty: Float(Some(32), false) + kind: Cast [0-0]: + ty: Float(Some(32), false) + expr: Expr [46-47]: + ty: Bool(false) + kind: SymbolId(6) + [7] Symbol [42-43]: + name: y + type: Float(Some(32), false) + qsharp_type: Double + io_kind: Default + "#]], + ); +} diff --git a/compiler/qsc_qasm3/src/semantic/tests/expression/implicit_cast_from_float.rs b/compiler/qsc_qasm3/src/semantic/tests/expression/implicit_cast_from_float.rs new file mode 100644 index 0000000000..bd1de21f74 --- /dev/null +++ b/compiler/qsc_qasm3/src/semantic/tests/expression/implicit_cast_from_float.rs @@ -0,0 +1,601 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +use expect_test::expect; + +use crate::semantic::tests::check_classical_decls; + +#[test] +fn to_bit_implicitly_fails() { + let input = " + float x = 42.; + bit y = x; + "; + + check_classical_decls( + input, + &expect![[r#" + Program: + version: + statements: + Stmt [9-23]: + annotations: + kind: ClassicalDeclarationStmt [9-23]: + symbol_id: 6 + ty_span: [9-14] + init_expr: Expr [19-22]: + ty: Float(None, true) + kind: Lit: Float(42.0) + + [Qsc.Qasm3.Compile.CannotCast + + x Cannot cast expression of type Float(None, false) to type Bit(false) + ,-[test:3:9] + 2 | float x = 42.; + 3 | bit y = x; + : ^^^^^^^^^^ + 4 | + `---- + ]"#]], + ); +} + +#[test] +fn explicit_width_to_bit_implicitly_fails() { + let input = " + float[64] x = 42.; + bit y = x; + "; + + check_classical_decls( + input, + &expect![[r#" + Program: + version: + statements: + Stmt [9-27]: + annotations: + kind: ClassicalDeclarationStmt [9-27]: + symbol_id: 6 + ty_span: [9-18] + init_expr: Expr [23-26]: + ty: Float(Some(64), true) + kind: Lit: Float(42.0) + + [Qsc.Qasm3.Compile.CannotCast + + x Cannot cast expression of type Float(Some(64), false) to type Bit(false) + ,-[test:3:9] + 2 | float[64] x = 42.; + 3 | bit y = x; + : ^^^^^^^^^^ + 4 | + `---- + ]"#]], + ); +} + +#[test] +fn to_bool_implicitly() { + let input = " + float x = 42.; + bool y = x; + "; + check_classical_decls( + input, + &expect![[r#" + ClassicalDeclarationStmt [9-23]: + symbol_id: 6 + ty_span: [9-14] + init_expr: Expr [19-22]: + ty: Float(None, true) + kind: Lit: Float(42.0) + [6] Symbol [15-16]: + name: x + type: Float(None, false) + qsharp_type: Double + io_kind: Default + ClassicalDeclarationStmt [32-43]: + symbol_id: 7 + ty_span: [32-36] + init_expr: Expr [41-42]: + ty: Bool(false) + kind: Cast [0-0]: + ty: Bool(false) + expr: Expr [41-42]: + ty: Float(None, false) + kind: SymbolId(6) + [7] Symbol [37-38]: + name: y + type: Bool(false) + qsharp_type: bool + io_kind: Default + "#]], + ); +} + +#[test] +fn to_implicit_int_implicitly() { + let input = " + float x = 42.; + int y = x; + "; + + check_classical_decls( + input, + &expect![[r#" + ClassicalDeclarationStmt [9-23]: + symbol_id: 6 + ty_span: [9-14] + init_expr: Expr [19-22]: + ty: Float(None, true) + kind: Lit: Float(42.0) + [6] Symbol [15-16]: + name: x + type: Float(None, false) + qsharp_type: Double + io_kind: Default + ClassicalDeclarationStmt [32-42]: + symbol_id: 7 + ty_span: [32-35] + init_expr: Expr [40-41]: + ty: Int(None, false) + kind: Cast [0-0]: + ty: Int(None, false) + expr: Expr [40-41]: + ty: Float(None, false) + kind: SymbolId(6) + [7] Symbol [36-37]: + name: y + type: Int(None, false) + qsharp_type: Int + io_kind: Default + "#]], + ); +} + +#[test] +fn to_explicit_int_implicitly() { + let input = " + float x = 42.; + int[32] y = x; + "; + + check_classical_decls( + input, + &expect![[r#" + ClassicalDeclarationStmt [9-23]: + symbol_id: 6 + ty_span: [9-14] + init_expr: Expr [19-22]: + ty: Float(None, true) + kind: Lit: Float(42.0) + [6] Symbol [15-16]: + name: x + type: Float(None, false) + qsharp_type: Double + io_kind: Default + ClassicalDeclarationStmt [32-46]: + symbol_id: 7 + ty_span: [32-39] + init_expr: Expr [44-45]: + ty: Int(Some(32), false) + kind: Cast [0-0]: + ty: Int(Some(32), false) + expr: Expr [44-45]: + ty: Float(None, false) + kind: SymbolId(6) + [7] Symbol [40-41]: + name: y + type: Int(Some(32), false) + qsharp_type: Int + io_kind: Default + "#]], + ); +} + +#[test] +fn to_implicit_uint_implicitly() { + let input = " + float x = 42.; + uint y = x; + "; + + check_classical_decls( + input, + &expect![[r#" + ClassicalDeclarationStmt [9-23]: + symbol_id: 6 + ty_span: [9-14] + init_expr: Expr [19-22]: + ty: Float(None, true) + kind: Lit: Float(42.0) + [6] Symbol [15-16]: + name: x + type: Float(None, false) + qsharp_type: Double + io_kind: Default + ClassicalDeclarationStmt [32-43]: + symbol_id: 7 + ty_span: [32-36] + init_expr: Expr [41-42]: + ty: UInt(None, false) + kind: Cast [0-0]: + ty: UInt(None, false) + expr: Expr [41-42]: + ty: Float(None, false) + kind: SymbolId(6) + [7] Symbol [37-38]: + name: y + type: UInt(None, false) + qsharp_type: Int + io_kind: Default + "#]], + ); +} + +#[test] +fn negative_lit_to_implicit_uint_implicitly() { + let input = " + float x = -42.; + uint y = x; + "; + + check_classical_decls( + input, + &expect![[r#" + ClassicalDeclarationStmt [9-24]: + symbol_id: 6 + ty_span: [9-14] + init_expr: Expr [20-23]: + ty: Float(None, true) + kind: Lit: Float(-42.0) + [6] Symbol [15-16]: + name: x + type: Float(None, false) + qsharp_type: Double + io_kind: Default + ClassicalDeclarationStmt [33-44]: + symbol_id: 7 + ty_span: [33-37] + init_expr: Expr [42-43]: + ty: UInt(None, false) + kind: Cast [0-0]: + ty: UInt(None, false) + expr: Expr [42-43]: + ty: Float(None, false) + kind: SymbolId(6) + [7] Symbol [38-39]: + name: y + type: UInt(None, false) + qsharp_type: Int + io_kind: Default + "#]], + ); +} + +#[test] +fn to_explicit_uint_implicitly() { + let input = " + float x = 42.; + uint[32] y = x; + "; + + check_classical_decls( + input, + &expect![[r#" + ClassicalDeclarationStmt [9-23]: + symbol_id: 6 + ty_span: [9-14] + init_expr: Expr [19-22]: + ty: Float(None, true) + kind: Lit: Float(42.0) + [6] Symbol [15-16]: + name: x + type: Float(None, false) + qsharp_type: Double + io_kind: Default + ClassicalDeclarationStmt [32-47]: + symbol_id: 7 + ty_span: [32-40] + init_expr: Expr [45-46]: + ty: UInt(Some(32), false) + kind: Cast [0-0]: + ty: UInt(Some(32), false) + expr: Expr [45-46]: + ty: Float(None, false) + kind: SymbolId(6) + [7] Symbol [41-42]: + name: y + type: UInt(Some(32), false) + qsharp_type: Int + io_kind: Default + "#]], + ); +} + +#[test] +fn to_explicit_bigint_implicitly() { + let input = " + float x = 42.; + int[65] y = x; + "; + + check_classical_decls( + input, + &expect![[r#" + ClassicalDeclarationStmt [9-23]: + symbol_id: 6 + ty_span: [9-14] + init_expr: Expr [19-22]: + ty: Float(None, true) + kind: Lit: Float(42.0) + [6] Symbol [15-16]: + name: x + type: Float(None, false) + qsharp_type: Double + io_kind: Default + ClassicalDeclarationStmt [32-46]: + symbol_id: 7 + ty_span: [32-39] + init_expr: Expr [44-45]: + ty: Int(Some(65), false) + kind: Cast [0-0]: + ty: Int(Some(65), false) + expr: Expr [44-45]: + ty: Float(None, false) + kind: SymbolId(6) + [7] Symbol [40-41]: + name: y + type: Int(Some(65), false) + qsharp_type: BigInt + io_kind: Default + "#]], + ); +} + +#[test] +fn to_implicit_float_implicitly() { + let input = " + float x = 42.; + float y = x; + "; + + check_classical_decls( + input, + &expect![[r#" + ClassicalDeclarationStmt [9-23]: + symbol_id: 6 + ty_span: [9-14] + init_expr: Expr [19-22]: + ty: Float(None, true) + kind: Lit: Float(42.0) + [6] Symbol [15-16]: + name: x + type: Float(None, false) + qsharp_type: Double + io_kind: Default + ClassicalDeclarationStmt [32-44]: + symbol_id: 7 + ty_span: [32-37] + init_expr: Expr [42-43]: + ty: Float(None, false) + kind: SymbolId(6) + [7] Symbol [38-39]: + name: y + type: Float(None, false) + qsharp_type: Double + io_kind: Default + "#]], + ); +} + +#[test] +fn to_explicit_float_implicitly() { + let input = " + float x = 42.; + float[32] y = x; + "; + + check_classical_decls( + input, + &expect![[r#" + ClassicalDeclarationStmt [9-23]: + symbol_id: 6 + ty_span: [9-14] + init_expr: Expr [19-22]: + ty: Float(None, true) + kind: Lit: Float(42.0) + [6] Symbol [15-16]: + name: x + type: Float(None, false) + qsharp_type: Double + io_kind: Default + ClassicalDeclarationStmt [32-48]: + symbol_id: 7 + ty_span: [32-41] + init_expr: Expr [46-47]: + ty: Float(Some(32), false) + kind: SymbolId(6) + [7] Symbol [42-43]: + name: y + type: Float(Some(32), false) + qsharp_type: Double + io_kind: Default + "#]], + ); +} + +#[test] +fn to_implicit_complex_implicitly() { + let input = " + float x = 42.; + complex[float] y = x; + "; + + check_classical_decls( + input, + &expect![[r#" + ClassicalDeclarationStmt [9-23]: + symbol_id: 6 + ty_span: [9-14] + init_expr: Expr [19-22]: + ty: Float(None, true) + kind: Lit: Float(42.0) + [6] Symbol [15-16]: + name: x + type: Float(None, false) + qsharp_type: Double + io_kind: Default + ClassicalDeclarationStmt [32-53]: + symbol_id: 7 + ty_span: [32-46] + init_expr: Expr [51-52]: + ty: Complex(None, false) + kind: Cast [0-0]: + ty: Complex(None, false) + expr: Expr [51-52]: + ty: Float(None, false) + kind: SymbolId(6) + [7] Symbol [47-48]: + name: y + type: Complex(None, false) + qsharp_type: Complex + io_kind: Default + "#]], + ); +} + +#[test] +fn to_explicit_complex_implicitly() { + let input = " + float x = 42.; + complex[float[32]] y = x; + "; + + check_classical_decls( + input, + &expect![[r#" + ClassicalDeclarationStmt [9-23]: + symbol_id: 6 + ty_span: [9-14] + init_expr: Expr [19-22]: + ty: Float(None, true) + kind: Lit: Float(42.0) + [6] Symbol [15-16]: + name: x + type: Float(None, false) + qsharp_type: Double + io_kind: Default + ClassicalDeclarationStmt [32-57]: + symbol_id: 7 + ty_span: [32-50] + init_expr: Expr [55-56]: + ty: Complex(Some(32), false) + kind: Cast [0-0]: + ty: Complex(Some(32), false) + expr: Expr [55-56]: + ty: Float(None, false) + kind: SymbolId(6) + [7] Symbol [51-52]: + name: y + type: Complex(Some(32), false) + qsharp_type: Complex + io_kind: Default + "#]], + ); +} + +#[test] +#[ignore = "not yet implemented"] +fn to_angle_implicitly() { + let input = " + float x = 42.; + angle y = x; + "; + + check_classical_decls( + input, + &expect![[r#" + Program: + version: + statements: + Stmt [9-23]: + annotations: + kind: ClassicalDeclarationStmt [9-23]: + symbol_id: 6 + ty_span: [9-14] + init_expr: Expr [19-22]: + ty: Float(None, true) + kind: Lit: Float(42.0) + + [Qsc.Qasm3.Compile.Unimplemented + + x this statement is not yet handled during OpenQASM 3 import: Cast float + | to angle + ,-[test:3:9] + 2 | float x = 42.; + 3 | angle y = x; + : ^^^^^^^^^^^^ + 4 | + `---- + , Qsc.Qasm3.Compile.CannotCast + + x Cannot cast expression of type Float(None, false) to type Angle(None, + | false) + ,-[test:3:9] + 2 | float x = 42.; + 3 | angle y = x; + : ^^^^^^^^^^^^ + 4 | + `---- + ]"#]], + ); +} + +#[test] +#[ignore = "not yet implemented"] +fn to_explicit_angle_implicitly() { + let input = " + float x = 42.; + angle[4] y = x; + "; + + check_classical_decls( + input, + &expect![[r#" + Program: + version: + statements: + Stmt [9-23]: + annotations: + kind: ClassicalDeclarationStmt [9-23]: + symbol_id: 6 + ty_span: [9-14] + init_expr: Expr [19-22]: + ty: Float(None, true) + kind: Lit: Float(42.0) + + [Qsc.Qasm3.Compile.Unimplemented + + x this statement is not yet handled during OpenQASM 3 import: Cast float + | to angle + ,-[test:3:9] + 2 | float x = 42.; + 3 | angle[4] y = x; + : ^^^^^^^^^^^^^^^ + 4 | + `---- + , Qsc.Qasm3.Compile.CannotCast + + x Cannot cast expression of type Float(None, false) to type Angle(Some(4), + | false) + ,-[test:3:9] + 2 | float x = 42.; + 3 | angle[4] y = x; + : ^^^^^^^^^^^^^^^ + 4 | + `---- + ]"#]], + ); +} diff --git a/compiler/qsc_qasm3/src/semantic/tests/expression/implicit_cast_from_int.rs b/compiler/qsc_qasm3/src/semantic/tests/expression/implicit_cast_from_int.rs new file mode 100644 index 0000000000..1e8664c0bb --- /dev/null +++ b/compiler/qsc_qasm3/src/semantic/tests/expression/implicit_cast_from_int.rs @@ -0,0 +1,442 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +use expect_test::expect; + +use crate::semantic::tests::check_classical_decls; + +#[test] +fn to_bit_implicitly() { + let input = " + int x = 42; + bit y = x; + "; + + check_classical_decls( + input, + &expect![[r#" + ClassicalDeclarationStmt [9-20]: + symbol_id: 6 + ty_span: [9-12] + init_expr: Expr [17-19]: + ty: Int(None, true) + kind: Lit: Int(42) + [6] Symbol [13-14]: + name: x + type: Int(None, false) + qsharp_type: Int + io_kind: Default + ClassicalDeclarationStmt [29-39]: + symbol_id: 7 + ty_span: [29-32] + init_expr: Expr [37-38]: + ty: Bit(false) + kind: Cast [0-0]: + ty: Bit(false) + expr: Expr [37-38]: + ty: Int(None, false) + kind: SymbolId(6) + [7] Symbol [33-34]: + name: y + type: Bit(false) + qsharp_type: Result + io_kind: Default + "#]], + ); +} + +#[test] +fn to_bool_implicitly() { + let input = " + int x = 42; + bool y = x; + "; + + check_classical_decls( + input, + &expect![[r#" + ClassicalDeclarationStmt [9-20]: + symbol_id: 6 + ty_span: [9-12] + init_expr: Expr [17-19]: + ty: Int(None, true) + kind: Lit: Int(42) + [6] Symbol [13-14]: + name: x + type: Int(None, false) + qsharp_type: Int + io_kind: Default + ClassicalDeclarationStmt [29-40]: + symbol_id: 7 + ty_span: [29-33] + init_expr: Expr [38-39]: + ty: Bool(false) + kind: Cast [0-0]: + ty: Bool(false) + expr: Expr [38-39]: + ty: Int(None, false) + kind: SymbolId(6) + [7] Symbol [34-35]: + name: y + type: Bool(false) + qsharp_type: bool + io_kind: Default + "#]], + ); +} + +#[test] +fn to_implicit_int_implicitly() { + let input = " + int x = 42; + int y = x; + "; + + check_classical_decls( + input, + &expect![[r#" + ClassicalDeclarationStmt [9-20]: + symbol_id: 6 + ty_span: [9-12] + init_expr: Expr [17-19]: + ty: Int(None, true) + kind: Lit: Int(42) + [6] Symbol [13-14]: + name: x + type: Int(None, false) + qsharp_type: Int + io_kind: Default + ClassicalDeclarationStmt [29-39]: + symbol_id: 7 + ty_span: [29-32] + init_expr: Expr [37-38]: + ty: Int(None, false) + kind: SymbolId(6) + [7] Symbol [33-34]: + name: y + type: Int(None, false) + qsharp_type: Int + io_kind: Default + "#]], + ); +} + +#[test] +fn to_explicit_int_implicitly() { + let input = " + int x = 42; + int[32] y = x; + "; + + check_classical_decls( + input, + &expect![[r#" + ClassicalDeclarationStmt [9-20]: + symbol_id: 6 + ty_span: [9-12] + init_expr: Expr [17-19]: + ty: Int(None, true) + kind: Lit: Int(42) + [6] Symbol [13-14]: + name: x + type: Int(None, false) + qsharp_type: Int + io_kind: Default + ClassicalDeclarationStmt [29-43]: + symbol_id: 7 + ty_span: [29-36] + init_expr: Expr [41-42]: + ty: Int(Some(32), false) + kind: Cast [0-0]: + ty: Int(Some(32), false) + expr: Expr [41-42]: + ty: Int(None, false) + kind: SymbolId(6) + [7] Symbol [37-38]: + name: y + type: Int(Some(32), false) + qsharp_type: Int + io_kind: Default + "#]], + ); +} + +#[test] +fn to_implicit_uint_implicitly() { + let input = " + int x = 42; + uint y = x; + "; + + check_classical_decls( + input, + &expect![[r#" + ClassicalDeclarationStmt [9-20]: + symbol_id: 6 + ty_span: [9-12] + init_expr: Expr [17-19]: + ty: Int(None, true) + kind: Lit: Int(42) + [6] Symbol [13-14]: + name: x + type: Int(None, false) + qsharp_type: Int + io_kind: Default + ClassicalDeclarationStmt [29-40]: + symbol_id: 7 + ty_span: [29-33] + init_expr: Expr [38-39]: + ty: UInt(None, false) + kind: Cast [0-0]: + ty: UInt(None, false) + expr: Expr [38-39]: + ty: Int(None, false) + kind: SymbolId(6) + [7] Symbol [34-35]: + name: y + type: UInt(None, false) + qsharp_type: Int + io_kind: Default + "#]], + ); +} + +#[test] +fn to_explicit_uint_implicitly() { + let input = " + int x = 42; + uint[32] y = x; + "; + + check_classical_decls( + input, + &expect![[r#" + ClassicalDeclarationStmt [9-20]: + symbol_id: 6 + ty_span: [9-12] + init_expr: Expr [17-19]: + ty: Int(None, true) + kind: Lit: Int(42) + [6] Symbol [13-14]: + name: x + type: Int(None, false) + qsharp_type: Int + io_kind: Default + ClassicalDeclarationStmt [29-44]: + symbol_id: 7 + ty_span: [29-37] + init_expr: Expr [42-43]: + ty: UInt(Some(32), false) + kind: Cast [0-0]: + ty: UInt(Some(32), false) + expr: Expr [42-43]: + ty: Int(None, false) + kind: SymbolId(6) + [7] Symbol [38-39]: + name: y + type: UInt(Some(32), false) + qsharp_type: Int + io_kind: Default + "#]], + ); +} + +#[test] +fn to_explicit_bigint_implicitly() { + let input = " + int x = 42; + int[65] y = x; + "; + + check_classical_decls( + input, + &expect![[r#" + ClassicalDeclarationStmt [9-20]: + symbol_id: 6 + ty_span: [9-12] + init_expr: Expr [17-19]: + ty: Int(None, true) + kind: Lit: Int(42) + [6] Symbol [13-14]: + name: x + type: Int(None, false) + qsharp_type: Int + io_kind: Default + ClassicalDeclarationStmt [29-43]: + symbol_id: 7 + ty_span: [29-36] + init_expr: Expr [41-42]: + ty: Int(Some(65), false) + kind: Cast [0-0]: + ty: Int(Some(65), false) + expr: Expr [41-42]: + ty: Int(None, false) + kind: SymbolId(6) + [7] Symbol [37-38]: + name: y + type: Int(Some(65), false) + qsharp_type: BigInt + io_kind: Default + "#]], + ); +} + +#[test] +fn to_implicit_float_implicitly() { + let input = " + int x = 42; + float y = x; + "; + + check_classical_decls( + input, + &expect![[r#" + ClassicalDeclarationStmt [9-20]: + symbol_id: 6 + ty_span: [9-12] + init_expr: Expr [17-19]: + ty: Int(None, true) + kind: Lit: Int(42) + [6] Symbol [13-14]: + name: x + type: Int(None, false) + qsharp_type: Int + io_kind: Default + ClassicalDeclarationStmt [29-41]: + symbol_id: 7 + ty_span: [29-34] + init_expr: Expr [39-40]: + ty: Float(None, false) + kind: Cast [0-0]: + ty: Float(None, false) + expr: Expr [39-40]: + ty: Int(None, false) + kind: SymbolId(6) + [7] Symbol [35-36]: + name: y + type: Float(None, false) + qsharp_type: Double + io_kind: Default + "#]], + ); +} + +#[test] +fn to_explicit_float_implicitly() { + let input = " + int x = 42; + float[32] y = x; + "; + + check_classical_decls( + input, + &expect![[r#" + ClassicalDeclarationStmt [9-20]: + symbol_id: 6 + ty_span: [9-12] + init_expr: Expr [17-19]: + ty: Int(None, true) + kind: Lit: Int(42) + [6] Symbol [13-14]: + name: x + type: Int(None, false) + qsharp_type: Int + io_kind: Default + ClassicalDeclarationStmt [29-45]: + symbol_id: 7 + ty_span: [29-38] + init_expr: Expr [43-44]: + ty: Float(Some(32), false) + kind: Cast [0-0]: + ty: Float(Some(32), false) + expr: Expr [43-44]: + ty: Int(None, false) + kind: SymbolId(6) + [7] Symbol [39-40]: + name: y + type: Float(Some(32), false) + qsharp_type: Double + io_kind: Default + "#]], + ); +} + +#[test] +fn to_implicit_complex_implicitly() { + let input = " + int x = 42; + complex[float] y = x; + "; + + check_classical_decls( + input, + &expect![[r#" + ClassicalDeclarationStmt [9-20]: + symbol_id: 6 + ty_span: [9-12] + init_expr: Expr [17-19]: + ty: Int(None, true) + kind: Lit: Int(42) + [6] Symbol [13-14]: + name: x + type: Int(None, false) + qsharp_type: Int + io_kind: Default + ClassicalDeclarationStmt [29-50]: + symbol_id: 7 + ty_span: [29-43] + init_expr: Expr [48-49]: + ty: Complex(None, false) + kind: Cast [0-0]: + ty: Complex(None, false) + expr: Expr [48-49]: + ty: Int(None, false) + kind: SymbolId(6) + [7] Symbol [44-45]: + name: y + type: Complex(None, false) + qsharp_type: Complex + io_kind: Default + "#]], + ); +} + +#[test] +fn to_explicit_complex_implicitly() { + let input = " + int x = 42; + complex[float[32]] y = x; + "; + + check_classical_decls( + input, + &expect![[r#" + ClassicalDeclarationStmt [9-20]: + symbol_id: 6 + ty_span: [9-12] + init_expr: Expr [17-19]: + ty: Int(None, true) + kind: Lit: Int(42) + [6] Symbol [13-14]: + name: x + type: Int(None, false) + qsharp_type: Int + io_kind: Default + ClassicalDeclarationStmt [29-54]: + symbol_id: 7 + ty_span: [29-47] + init_expr: Expr [52-53]: + ty: Complex(Some(32), false) + kind: Cast [0-0]: + ty: Complex(Some(32), false) + expr: Expr [52-53]: + ty: Int(None, false) + kind: SymbolId(6) + [7] Symbol [48-49]: + name: y + type: Complex(Some(32), false) + qsharp_type: Complex + io_kind: Default + "#]], + ); +} diff --git a/compiler/qsc_qasm3/src/semantic/types.rs b/compiler/qsc_qasm3/src/semantic/types.rs index f111bef11d..62ccc95288 100644 --- a/compiler/qsc_qasm3/src/semantic/types.rs +++ b/compiler/qsc_qasm3/src/semantic/types.rs @@ -6,7 +6,7 @@ use std::cmp::max; use core::fmt; use std::fmt::{Display, Formatter}; -use crate::ast::UnaryOp::NotL; +use crate::parser::ast as syntax; use super::ast::LiteralKind; @@ -87,14 +87,14 @@ impl Type { pub fn is_array(&self) -> bool { matches!( self, - Type::BitArray(..) - | Type::QubitArray(..) - | Type::AngleArray(..) + Type::AngleArray(..) + | Type::BitArray(..) | Type::BoolArray(..) | Type::ComplexArray(..) | Type::DurationArray(..) | Type::FloatArray(..) | Type::IntArray(..) + | Type::QubitArray(..) | Type::UIntArray(..) ) } @@ -151,6 +151,22 @@ impl Type { ) } + #[must_use] + pub fn num_dims(&self) -> usize { + match self { + Type::AngleArray(_, dims) + | Type::BitArray(dims, _) + | Type::BoolArray(dims) + | Type::DurationArray(dims) + | Type::ComplexArray(_, dims) + | Type::FloatArray(_, dims) + | Type::IntArray(_, dims) + | Type::QubitArray(dims) + | Type::UIntArray(_, dims) => dims.num_dims(), + _ => 0, + } + } + /// Get the indexed type of a given type. /// For example, if the type is `Int[2][3]`, the indexed type is `Int[2]`. /// If the type is `Int[2]`, the indexed type is `Int`. @@ -362,6 +378,13 @@ impl Type { _ => self.clone(), } } + + pub(crate) fn is_quantum(&self) -> bool { + matches!( + self, + Type::HardwareQubit | Type::Qubit | Type::QubitArray(_) + ) + } } #[derive(Debug, Clone, PartialEq, Eq, Hash, Default)] @@ -392,6 +415,22 @@ impl Display for ArrayDimensions { } } +impl ArrayDimensions { + #[must_use] + pub fn num_dims(&self) -> usize { + match self { + ArrayDimensions::One(_) => 1, + ArrayDimensions::Two(_, _) => 2, + ArrayDimensions::Three(_, _, _) => 3, + ArrayDimensions::Four(_, _, _, _) => 4, + ArrayDimensions::Five(_, _, _, _, _) => 5, + ArrayDimensions::Six(_, _, _, _, _, _) => 6, + ArrayDimensions::Seven(_, _, _, _, _, _, _) => 7, + ArrayDimensions::Err => 0, + } + } +} + /// When two types are combined, the result is a type that can represent both. /// For constness, the result is const iff both types are const. #[must_use] @@ -617,6 +656,11 @@ pub fn can_cast_literal(lhs_ty: &Type, ty_lit: &Type) -> bool { } base_types_equal(lhs_ty, ty_lit) + || matches!( + (lhs_ty, ty_lit), + (Type::Angle(_, _), Type::Float(_, _) | Type::Bit(..)) + ) + || matches!((lhs_ty, ty_lit), (Type::Bit(..), Type::Angle(..))) || matches!( (lhs_ty, ty_lit), ( @@ -656,9 +700,9 @@ pub(crate) fn can_cast_literal_with_value_knowledge(lhs_ty: &Type, kind: &Litera } // https://openqasm.com/language/classical.html -pub(crate) fn unary_op_can_be_applied_to_type(op: crate::ast::UnaryOp, ty: &Type) -> bool { +pub(crate) fn unary_op_can_be_applied_to_type(op: syntax::UnaryOp, ty: &Type) -> bool { match op { - crate::ast::UnaryOp::NotB => match ty { + syntax::UnaryOp::NotB => match ty { Type::Bit(_) | Type::UInt(_, _) | Type::Angle(_, _) => true, Type::BitArray(dims, _) | Type::UIntArray(_, dims) | Type::AngleArray(_, dims) => { // the spe says "registers of the same size" which is a bit ambiguous @@ -667,9 +711,148 @@ pub(crate) fn unary_op_can_be_applied_to_type(op: crate::ast::UnaryOp, ty: &Type } _ => false, }, - NotL => matches!(ty, Type::Bool(_)), - crate::ast::UnaryOp::Neg => { + syntax::UnaryOp::NotL => matches!(ty, Type::Bool(_)), + syntax::UnaryOp::Neg => { matches!(ty, Type::Int(_, _) | Type::Float(_, _) | Type::Angle(_, _)) } } } + +/// Bit arrays can be compared, but need to be converted to int first +pub(crate) fn binop_requires_int_conversion_for_type( + op: syntax::BinOp, + lhs: &Type, + rhs: &Type, +) -> bool { + match op { + syntax::BinOp::Eq + | syntax::BinOp::Gt + | syntax::BinOp::Gte + | syntax::BinOp::Lt + | syntax::BinOp::Lte + | syntax::BinOp::Neq => match (lhs, rhs) { + (Type::BitArray(lhs_dims, _), Type::BitArray(rhs_dims, _)) => { + match (lhs_dims, rhs_dims) { + (ArrayDimensions::One(lhs_size), ArrayDimensions::One(rhs_size)) => { + lhs_size == rhs_size + } + _ => false, + } + } + _ => false, + }, + _ => false, + } +} + +/// Symmetric arithmetic conversions are applied to: +/// binary arithmetic *, /, %, +, - +/// relational operators <, >, <=, >=, ==, != +/// binary bitwise arithmetic &, ^, |, +pub(crate) fn requires_symmetric_conversion(op: syntax::BinOp) -> bool { + match op { + syntax::BinOp::Add + | syntax::BinOp::AndB + | syntax::BinOp::AndL + | syntax::BinOp::Div + | syntax::BinOp::Eq + | syntax::BinOp::Exp + | syntax::BinOp::Gt + | syntax::BinOp::Gte + | syntax::BinOp::Lt + | syntax::BinOp::Lte + | syntax::BinOp::Mod + | syntax::BinOp::Mul + | syntax::BinOp::Neq + | syntax::BinOp::OrB + | syntax::BinOp::OrL + | syntax::BinOp::Sub + | syntax::BinOp::XorB => true, + syntax::BinOp::Shl | syntax::BinOp::Shr => false, + } +} + +pub(crate) fn try_promote_with_casting(left_type: &Type, right_type: &Type) -> Type { + let promoted_type = promote_types(left_type, right_type); + + if promoted_type != Type::Void { + return promoted_type; + } + if let Some(value) = try_promote_bitarray_to_int(left_type, right_type) { + return value; + } + // simple promotion failed, try a lossless cast + // each side to double + let promoted_rhs = promote_types(&Type::Float(None, false), right_type); + let promoted_lhs = promote_types(left_type, &Type::Float(None, false)); + + match (promoted_lhs, promoted_rhs) { + (Type::Void, Type::Void) => Type::Float(None, false), + (Type::Void, promoted_rhs) => promoted_rhs, + (promoted_lhs, Type::Void) => promoted_lhs, + (promoted_lhs, promoted_rhs) => { + // return the greater of the two promoted types + if matches!(promoted_lhs, Type::Complex(..)) { + promoted_lhs + } else if matches!(promoted_rhs, Type::Complex(..)) { + promoted_rhs + } else if matches!(promoted_lhs, Type::Float(..)) { + promoted_lhs + } else if matches!(promoted_rhs, Type::Float(..)) { + promoted_rhs + } else { + Type::Float(None, false) + } + } + } +} + +fn try_promote_bitarray_to_int(left_type: &Type, right_type: &Type) -> Option { + if matches!( + (left_type, right_type), + (Type::Int(..) | Type::UInt(..), Type::BitArray(..)) + ) { + let Type::BitArray(ArrayDimensions::One(size), _) = right_type else { + return None; + }; + + if left_type.width() != Some(*size) { + return None; + } + + return Some(left_type.clone()); + } + + if matches!( + (left_type, right_type), + (Type::BitArray(..), Type::Int(..) | Type::UInt(..)) + ) { + let Type::BitArray(ArrayDimensions::One(size), _) = left_type else { + return None; + }; + + if right_type.width() != Some(*size) { + return None; + } + + return Some(right_type.clone()); + } + None +} + +// integer promotions are applied only to both operands of +// the shift operators << and >> +pub(crate) fn binop_requires_symmetric_int_conversion(op: syntax::BinOp) -> bool { + matches!(op, syntax::BinOp::Shl | syntax::BinOp::Shr) +} + +pub(crate) fn is_complex_binop_supported(op: syntax::BinOp) -> bool { + matches!( + op, + syntax::BinOp::Add + | syntax::BinOp::Sub + | syntax::BinOp::Mul + | syntax::BinOp::Div + | syntax::BinOp::Exp + ) +} From 5ecebde7fcb80f58daff21e7246672227525c29d Mon Sep 17 00:00:00 2001 From: orpuente-MS <156957451+orpuente-MS@users.noreply.github.com> Date: Thu, 13 Mar 2025 11:26:07 -0700 Subject: [PATCH 059/108] Make control flow stmts use Stmt instead of List for their bodies. (#2226) If/else blocks, while, and for loops in QASM introduce new scopes only when their body enclosed by curly braces. To make lowering easier, this PR changes their bodies from `List` to `Stmt`. In the body enclosed in curly braces case, the Stmt will be of kind Block. --- compiler/qsc_qasm3/src/parser/ast.rs | 22 +- compiler/qsc_qasm3/src/parser/stmt.rs | 50 ++- .../src/parser/stmt/tests/for_loops.rs | 237 +++++++++---- .../src/parser/stmt/tests/if_stmt.rs | 327 ++++++++++++++---- .../parser/stmt/tests/invalid_stmts/branch.rs | 41 ++- .../parser/stmt/tests/invalid_stmts/loops.rs | 46 ++- .../src/parser/stmt/tests/while_loops.rs | 167 ++++++--- 7 files changed, 637 insertions(+), 253 deletions(-) diff --git a/compiler/qsc_qasm3/src/parser/ast.rs b/compiler/qsc_qasm3/src/parser/ast.rs index 03193a2d0c..922cc9a27a 100644 --- a/compiler/qsc_qasm3/src/parser/ast.rs +++ b/compiler/qsc_qasm3/src/parser/ast.rs @@ -317,7 +317,7 @@ pub enum StmtKind { Barrier(BarrierStmt), Box(BoxStmt), Break(BreakStmt), - Block(Box), + Block(Block), Cal(CalibrationStmt), CalibrationGrammar(CalibrationGrammarStmt), ClassicalDecl(ClassicalDeclarationStmt), @@ -419,16 +419,16 @@ impl Display for DefCalStmt { pub struct IfStmt { pub span: Span, pub condition: Expr, - pub if_block: List, - pub else_block: Option>, + pub if_body: Stmt, + pub else_body: Option, } impl Display for IfStmt { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { writeln_header(f, "IfStmt", self.span)?; writeln_field(f, "condition", &self.condition)?; - writeln_list_field(f, "if_block", &self.if_block)?; - write_opt_list_field(f, "else_block", self.else_block.as_ref()) + writeln_field(f, "if_body", &self.if_body)?; + write_opt_field(f, "else_body", self.else_body.as_ref()) } } @@ -1323,8 +1323,8 @@ pub struct DefStmt { pub span: Span, pub name: Box, pub params: List, - pub body: Box, - pub return_type: Option, + pub body: Block, + pub return_type: Option>, } impl Display for DefStmt { @@ -1354,14 +1354,14 @@ impl Display for ReturnStmt { pub struct WhileLoop { pub span: Span, pub while_condition: Expr, - pub block: List, + pub body: Stmt, } impl Display for WhileLoop { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { writeln_header(f, "WhileLoop", self.span)?; writeln_field(f, "condition", &self.while_condition)?; - write_list_field(f, "block", &self.block) + write_field(f, "body", &self.body) } } @@ -1371,7 +1371,7 @@ pub struct ForStmt { pub ty: ScalarType, pub identifier: Identifier, pub set_declaration: Box, - pub block: List, + pub body: Stmt, } impl Display for ForStmt { @@ -1380,7 +1380,7 @@ impl Display for ForStmt { writeln_field(f, "variable_type", &self.ty)?; writeln_field(f, "variable_name", &self.identifier)?; writeln_field(f, "iterable", &self.set_declaration)?; - write_list_field(f, "block", &self.block) + write_field(f, "body", &self.body) } } diff --git a/compiler/qsc_qasm3/src/parser/stmt.rs b/compiler/qsc_qasm3/src/parser/stmt.rs index c8f27caedd..2625a887cd 100644 --- a/compiler/qsc_qasm3/src/parser/stmt.rs +++ b/compiler/qsc_qasm3/src/parser/stmt.rs @@ -278,20 +278,22 @@ fn disambiguate_ident(s: &mut ParserContext, indexed_ident: IndexedIdent) -> Res #[allow(clippy::vec_box)] pub(super) fn parse_many(s: &mut ParserContext) -> Result>> { many(s, |s| { - recovering(s, default, &[TokenKind::Semicolon], parse_block_or_stmt) + recovering(s, default, &[TokenKind::Semicolon], |s| { + parse_block_or_stmt(s).map(Box::new) + }) }) } /// Grammar: `LBRACE statementOrScope* RBRACE`. -pub(super) fn parse_block(s: &mut ParserContext) -> Result> { +pub(super) fn parse_block(s: &mut ParserContext) -> Result { let lo = s.peek().span.lo; token(s, TokenKind::Open(Delim::Brace))?; let stmts = barrier(s, &[TokenKind::Close(Delim::Brace)], parse_many)?; recovering_token(s, TokenKind::Close(Delim::Brace)); - Ok(Box::new(Block { + Ok(Block { span: s.span(lo), stmts: stmts.into_boxed_slice(), - })) + }) } #[allow(clippy::unnecessary_box_returns)] @@ -454,7 +456,7 @@ fn parse_def(s: &mut ParserContext) -> Result { token(s, TokenKind::Open(Delim::Paren))?; let (exprs, _) = seq(s, arg_def)?; token(s, TokenKind::Close(Delim::Paren))?; - let return_type = opt(s, return_sig)?; + let return_type = opt(s, return_sig)?.map(Box::new); let body = parse_block(s)?; let kind = StmtKind::Def(DefStmt { span: s.span(lo), @@ -605,7 +607,7 @@ fn parse_gatedef(s: &mut ParserContext) -> Result { let ident = Box::new(prim::ident(s)?); let params = opt(s, gate_params)?.unwrap_or_else(Vec::new); let (qubits, _) = seq(s, prim::ident)?; - let body = parse_block(s)?; + let body = Box::new(parse_block(s)?); Ok(StmtKind::QuantumGateDefinition(QuantumGateDefinition { span: s.span(lo), ident, @@ -1122,7 +1124,7 @@ fn case_stmt(s: &mut ParserContext) -> Result { s.push_error(Error::new(ErrorKind::MissingSwitchCaseLabels(s.span(lo)))); } - let block = parse_block(s).map(|block| *block)?; + let block = parse_block(s)?; Ok(SwitchCase { span: s.span(lo), @@ -1134,27 +1136,19 @@ fn case_stmt(s: &mut ParserContext) -> Result { /// Grammar: `DEFAULT scope`. fn default_case_stmt(s: &mut ParserContext) -> Result { token(s, TokenKind::Keyword(Keyword::Default))?; - parse_block(s).map(|block| *block) + parse_block(s) } /// Grammar: `statement | scope`. -fn parse_block_or_stmt(s: &mut ParserContext) -> Result> { +fn parse_block_or_stmt(s: &mut ParserContext) -> Result { if let Some(block) = opt(s, parse_block)? { - Ok(Box::new(Stmt { + Ok(Stmt { span: block.span, annotations: Default::default(), kind: Box::new(StmtKind::Block(block)), - })) - } else { - Ok(parse(s)?) - } -} - -fn into_stmt_list(stmt: Stmt) -> List { - if let StmtKind::Block(block) = *stmt.kind { - block.stmts + }) } else { - Box::new([Box::new(stmt)]) + Ok(*parse(s)?) } } @@ -1167,9 +1161,9 @@ pub fn parse_if_stmt(s: &mut ParserContext) -> Result { let condition = expr::expr(s)?; recovering_token(s, TokenKind::Close(Delim::Paren)); - let if_block = into_stmt_list(*parse_block_or_stmt(s)?); + let if_block = parse_block_or_stmt(s)?; let else_block = if opt(s, |s| token(s, TokenKind::Keyword(Keyword::Else)))?.is_some() { - Some(into_stmt_list(*parse_block_or_stmt(s)?)) + Some(parse_block_or_stmt(s)?) } else { None }; @@ -1177,8 +1171,8 @@ pub fn parse_if_stmt(s: &mut ParserContext) -> Result { Ok(IfStmt { span: s.span(lo), condition, - if_block, - else_block, + if_body: if_block, + else_body: else_block, }) } @@ -1236,14 +1230,14 @@ pub fn parse_for_loop(s: &mut ParserContext) -> Result { let identifier = Identifier::Ident(Box::new(prim::ident(s)?)); token(s, TokenKind::Keyword(Keyword::In))?; let set_declaration = Box::new(for_loop_iterable_expr(s)?); - let block = into_stmt_list(*parse_block_or_stmt(s)?); + let block = parse_block_or_stmt(s)?; Ok(ForStmt { span: s.span(lo), ty, identifier, set_declaration, - block, + body: block, }) } @@ -1255,12 +1249,12 @@ pub fn parse_while_loop(s: &mut ParserContext) -> Result { token(s, TokenKind::Open(Delim::Paren))?; let while_condition = expr::expr(s)?; recovering_token(s, TokenKind::Close(Delim::Paren)); - let block = into_stmt_list(*parse_block_or_stmt(s)?); + let block = parse_block_or_stmt(s)?; Ok(WhileLoop { span: s.span(lo), while_condition, - block, + body: block, }) } diff --git a/compiler/qsc_qasm3/src/parser/stmt/tests/for_loops.rs b/compiler/qsc_qasm3/src/parser/stmt/tests/for_loops.rs index 30239971b5..0f5822a6e0 100644 --- a/compiler/qsc_qasm3/src/parser/stmt/tests/for_loops.rs +++ b/compiler/qsc_qasm3/src/parser/stmt/tests/for_loops.rs @@ -24,14 +24,16 @@ fn simple_for_loop() { Expr [19-20]: Lit: Int(1) Expr [22-23]: Lit: Int(2) Expr [25-26]: Lit: Int(3) - block: - Stmt [38-44]: - annotations: - kind: AssignStmt [38-44]: - lhs: IndexedIdent [38-39]: - name: Ident [38-39] "a" - indices: - rhs: Expr [42-43]: Lit: Int(0)"#]], + body: Stmt [28-50]: + annotations: + kind: Block [28-50]: + Stmt [38-44]: + annotations: + kind: AssignStmt [38-44]: + lhs: IndexedIdent [38-39]: + name: Ident [38-39] "a" + indices: + rhs: Expr [42-43]: Lit: Int(0)"#]], ); } @@ -49,7 +51,9 @@ fn empty_for_loop() { variable_name: Ident [8-9] "x" iterable: DiscreteSet [13-15]: values: - block: "#]], + body: Stmt [16-18]: + annotations: + kind: Block [16-18]: "#]], ); } @@ -73,14 +77,13 @@ fn simple_for_loop_stmt_body() { Expr [19-20]: Lit: Int(1) Expr [22-23]: Lit: Int(2) Expr [25-26]: Lit: Int(3) - block: - Stmt [36-42]: - annotations: - kind: AssignStmt [36-42]: - lhs: IndexedIdent [36-37]: - name: Ident [36-37] "a" - indices: - rhs: Expr [40-41]: Lit: Int(0)"#]], + body: Stmt [36-42]: + annotations: + kind: AssignStmt [36-42]: + lhs: IndexedIdent [36-37]: + name: Ident [36-37] "a" + indices: + rhs: Expr [40-41]: Lit: Int(0)"#]], ); } @@ -103,14 +106,16 @@ fn for_loop_range() { start: Expr [19-20]: Lit: Int(0) step: Expr [21-22]: Lit: Int(2) end: Expr [23-24]: Lit: Int(7) - block: - Stmt [36-42]: - annotations: - kind: AssignStmt [36-42]: - lhs: IndexedIdent [36-37]: - name: Ident [36-37] "a" - indices: - rhs: Expr [40-41]: Lit: Int(0)"#]], + body: Stmt [26-48]: + annotations: + kind: Block [26-48]: + Stmt [36-42]: + annotations: + kind: AssignStmt [36-42]: + lhs: IndexedIdent [36-37]: + name: Ident [36-37] "a" + indices: + rhs: Expr [40-41]: Lit: Int(0)"#]], ); } @@ -133,14 +138,16 @@ fn for_loop_range_no_step() { start: Expr [19-20]: Lit: Int(0) step: end: Expr [21-22]: Lit: Int(7) - block: - Stmt [34-40]: - annotations: - kind: AssignStmt [34-40]: - lhs: IndexedIdent [34-35]: - name: Ident [34-35] "a" - indices: - rhs: Expr [38-39]: Lit: Int(0)"#]], + body: Stmt [24-46]: + annotations: + kind: Block [24-46]: + Stmt [34-40]: + annotations: + kind: AssignStmt [34-40]: + lhs: IndexedIdent [34-35]: + name: Ident [34-35] "a" + indices: + rhs: Expr [38-39]: Lit: Int(0)"#]], ); } @@ -160,14 +167,16 @@ fn for_loop_expr() { size: variable_name: Ident [13-14] "x" iterable: Expr [18-20]: Ident [18-20] "xs" - block: - Stmt [31-37]: - annotations: - kind: AssignStmt [31-37]: - lhs: IndexedIdent [31-32]: - name: Ident [31-32] "a" - indices: - rhs: Expr [35-36]: Lit: Int(0)"#]], + body: Stmt [21-43]: + annotations: + kind: Block [21-43]: + Stmt [31-37]: + annotations: + kind: AssignStmt [31-37]: + lhs: IndexedIdent [31-32]: + name: Ident [31-32] "a" + indices: + rhs: Expr [35-36]: Lit: Int(0)"#]], ); } @@ -192,17 +201,19 @@ fn for_loop_with_continue_stmt() { Expr [19-20]: Lit: Int(1) Expr [22-23]: Lit: Int(2) Expr [25-26]: Lit: Int(3) - block: - Stmt [38-44]: - annotations: - kind: AssignStmt [38-44]: - lhs: IndexedIdent [38-39]: - name: Ident [38-39] "a" - indices: - rhs: Expr [42-43]: Lit: Int(0) - Stmt [53-62]: - annotations: - kind: ContinueStmt [53-62]"#]], + body: Stmt [28-68]: + annotations: + kind: Block [28-68]: + Stmt [38-44]: + annotations: + kind: AssignStmt [38-44]: + lhs: IndexedIdent [38-39]: + name: Ident [38-39] "a" + indices: + rhs: Expr [42-43]: Lit: Int(0) + Stmt [53-62]: + annotations: + kind: ContinueStmt [53-62]"#]], ); } @@ -227,16 +238,116 @@ fn for_loop_with_break_stmt() { Expr [19-20]: Lit: Int(1) Expr [22-23]: Lit: Int(2) Expr [25-26]: Lit: Int(3) - block: - Stmt [38-44]: - annotations: - kind: AssignStmt [38-44]: - lhs: IndexedIdent [38-39]: - name: Ident [38-39] "a" - indices: - rhs: Expr [42-43]: Lit: Int(0) - Stmt [53-59]: - annotations: - kind: BreakStmt [53-59]"#]], + body: Stmt [28-65]: + annotations: + kind: Block [28-65]: + Stmt [38-44]: + annotations: + kind: AssignStmt [38-44]: + lhs: IndexedIdent [38-39]: + name: Ident [38-39] "a" + indices: + rhs: Expr [42-43]: Lit: Int(0) + Stmt [53-59]: + annotations: + kind: BreakStmt [53-59]"#]], + ); +} + +#[test] +fn single_stmt_for_stmt() { + check( + parse, + "for int x in {} z q;", + &expect![[r#" + Stmt [0-20]: + annotations: + kind: ForStmt [0-20]: + variable_type: ScalarType [4-7]: IntType [4-7]: + size: + variable_name: Ident [8-9] "x" + iterable: DiscreteSet [13-15]: + values: + body: Stmt [16-20]: + annotations: + kind: GateCall [16-20]: + modifiers: + name: Ident [16-17] "z" + args: + duration: + qubits: + IndexedIdent [18-19]: + name: Ident [18-19] "q" + indices: "#]], + ); +} + +#[test] +fn annotations_in_single_stmt_for_stmt() { + check( + parse, + " + for int x in {} + @foo + @bar + x = 5;", + &expect![[r#" + Stmt [5-61]: + annotations: + kind: ForStmt [5-61]: + variable_type: ScalarType [9-12]: IntType [9-12]: + size: + variable_name: Ident [13-14] "x" + iterable: DiscreteSet [18-20]: + values: + body: Stmt [29-61]: + annotations: + Annotation [29-33]: + identifier: "foo" + value: + Annotation [42-46]: + identifier: "bar" + value: + kind: AssignStmt [55-61]: + lhs: IndexedIdent [55-56]: + name: Ident [55-56] "x" + indices: + rhs: Expr [59-60]: Lit: Int(5)"#]], + ); +} + +#[test] +fn nested_single_stmt_for_stmt() { + check( + parse, + "for int x in {} for int y in {} z q;", + &expect![[r#" + Stmt [0-36]: + annotations: + kind: ForStmt [0-36]: + variable_type: ScalarType [4-7]: IntType [4-7]: + size: + variable_name: Ident [8-9] "x" + iterable: DiscreteSet [13-15]: + values: + body: Stmt [16-36]: + annotations: + kind: ForStmt [16-36]: + variable_type: ScalarType [20-23]: IntType [20-23]: + size: + variable_name: Ident [24-25] "y" + iterable: DiscreteSet [29-31]: + values: + body: Stmt [32-36]: + annotations: + kind: GateCall [32-36]: + modifiers: + name: Ident [32-33] "z" + args: + duration: + qubits: + IndexedIdent [34-35]: + name: Ident [34-35] "q" + indices: "#]], ); } diff --git a/compiler/qsc_qasm3/src/parser/stmt/tests/if_stmt.rs b/compiler/qsc_qasm3/src/parser/stmt/tests/if_stmt.rs index e2c242942a..f74dbc053b 100644 --- a/compiler/qsc_qasm3/src/parser/stmt/tests/if_stmt.rs +++ b/compiler/qsc_qasm3/src/parser/stmt/tests/if_stmt.rs @@ -23,22 +23,26 @@ fn simple_if_stmt() { op: Eq lhs: Expr [9-10]: Ident [9-10] "x" rhs: Expr [14-15]: Ident [14-15] "y" - if_block: - Stmt [27-33]: - annotations: - kind: AssignStmt [27-33]: - lhs: IndexedIdent [27-28]: - name: Ident [27-28] "a" - indices: - rhs: Expr [31-32]: Lit: Int(0) - else_block: - Stmt [55-61]: - annotations: - kind: AssignStmt [55-61]: - lhs: IndexedIdent [55-56]: - name: Ident [55-56] "a" - indices: - rhs: Expr [59-60]: Lit: Int(1)"#]], + if_body: Stmt [17-39]: + annotations: + kind: Block [17-39]: + Stmt [27-33]: + annotations: + kind: AssignStmt [27-33]: + lhs: IndexedIdent [27-28]: + name: Ident [27-28] "a" + indices: + rhs: Expr [31-32]: Lit: Int(0) + else_body: Stmt [45-67]: + annotations: + kind: Block [45-67]: + Stmt [55-61]: + annotations: + kind: AssignStmt [55-61]: + lhs: IndexedIdent [55-56]: + name: Ident [55-56] "a" + indices: + rhs: Expr [59-60]: Lit: Int(1)"#]], ); } @@ -59,15 +63,17 @@ fn if_stmt_missing_else() { op: Eq lhs: Expr [9-10]: Ident [9-10] "x" rhs: Expr [14-15]: Ident [14-15] "y" - if_block: - Stmt [27-33]: - annotations: - kind: AssignStmt [27-33]: - lhs: IndexedIdent [27-28]: - name: Ident [27-28] "a" - indices: - rhs: Expr [31-32]: Lit: Int(0) - else_block: "#]], + if_body: Stmt [17-39]: + annotations: + kind: Block [17-39]: + Stmt [27-33]: + annotations: + kind: AssignStmt [27-33]: + lhs: IndexedIdent [27-28]: + name: Ident [27-28] "a" + indices: + rhs: Expr [31-32]: Lit: Int(0) + else_body: "#]], ); } @@ -98,53 +104,236 @@ fn nested_if_stmts() { op: Eq lhs: Expr [9-10]: Ident [9-10] "x" rhs: Expr [14-15]: Ident [14-15] "y" - if_block: - Stmt [27-107]: - annotations: - kind: IfStmt [27-107]: - condition: Expr [31-39]: BinaryOpExpr: - op: Eq - lhs: Expr [31-33]: Ident [31-33] "x1" - rhs: Expr [37-39]: Ident [37-39] "y1" - if_block: - Stmt [55-61]: + if_body: Stmt [17-113]: + annotations: + kind: Block [17-113]: + Stmt [27-107]: + annotations: + kind: IfStmt [27-107]: + condition: Expr [31-39]: BinaryOpExpr: + op: Eq + lhs: Expr [31-33]: Ident [31-33] "x1" + rhs: Expr [37-39]: Ident [37-39] "y1" + if_body: Stmt [41-71]: annotations: - kind: AssignStmt [55-61]: - lhs: IndexedIdent [55-56]: - name: Ident [55-56] "a" - indices: - rhs: Expr [59-60]: Lit: Int(0) - else_block: - Stmt [91-97]: + kind: Block [41-71]: + Stmt [55-61]: + annotations: + kind: AssignStmt [55-61]: + lhs: IndexedIdent [55-56]: + name: Ident [55-56] "a" + indices: + rhs: Expr [59-60]: Lit: Int(0) + else_body: Stmt [77-107]: annotations: - kind: AssignStmt [91-97]: - lhs: IndexedIdent [91-92]: - name: Ident [91-92] "a" - indices: - rhs: Expr [95-96]: Lit: Int(1) - else_block: - Stmt [129-209]: - annotations: - kind: IfStmt [129-209]: - condition: Expr [133-141]: BinaryOpExpr: - op: Eq - lhs: Expr [133-135]: Ident [133-135] "x2" - rhs: Expr [139-141]: Ident [139-141] "y2" - if_block: - Stmt [157-163]: + kind: Block [77-107]: + Stmt [91-97]: + annotations: + kind: AssignStmt [91-97]: + lhs: IndexedIdent [91-92]: + name: Ident [91-92] "a" + indices: + rhs: Expr [95-96]: Lit: Int(1) + else_body: Stmt [119-215]: + annotations: + kind: Block [119-215]: + Stmt [129-209]: + annotations: + kind: IfStmt [129-209]: + condition: Expr [133-141]: BinaryOpExpr: + op: Eq + lhs: Expr [133-135]: Ident [133-135] "x2" + rhs: Expr [139-141]: Ident [139-141] "y2" + if_body: Stmt [143-173]: annotations: - kind: AssignStmt [157-163]: - lhs: IndexedIdent [157-158]: - name: Ident [157-158] "a" - indices: - rhs: Expr [161-162]: Lit: Int(2) - else_block: - Stmt [193-199]: + kind: Block [143-173]: + Stmt [157-163]: + annotations: + kind: AssignStmt [157-163]: + lhs: IndexedIdent [157-158]: + name: Ident [157-158] "a" + indices: + rhs: Expr [161-162]: Lit: Int(2) + else_body: Stmt [179-209]: annotations: - kind: AssignStmt [193-199]: - lhs: IndexedIdent [193-194]: - name: Ident [193-194] "a" - indices: - rhs: Expr [197-198]: Lit: Int(3)"#]], + kind: Block [179-209]: + Stmt [193-199]: + annotations: + kind: AssignStmt [193-199]: + lhs: IndexedIdent [193-194]: + name: Ident [193-194] "a" + indices: + rhs: Expr [197-198]: Lit: Int(3)"#]], + ); +} + +#[test] +fn single_stmt_if_stmt() { + check( + parse, + "if (x) z q;", + &expect![[r#" + Stmt [0-11]: + annotations: + kind: IfStmt [0-11]: + condition: Expr [4-5]: Ident [4-5] "x" + if_body: Stmt [7-11]: + annotations: + kind: GateCall [7-11]: + modifiers: + name: Ident [7-8] "z" + args: + duration: + qubits: + IndexedIdent [9-10]: + name: Ident [9-10] "q" + indices: + else_body: "#]], + ); +} + +#[test] +fn annotations_in_single_stmt_if_stmt() { + check( + parse, + " + if (x) + @foo + @bar + x = 5;", + &expect![[r#" + Stmt [5-52]: + annotations: + kind: IfStmt [5-52]: + condition: Expr [9-10]: Ident [9-10] "x" + if_body: Stmt [20-52]: + annotations: + Annotation [20-24]: + identifier: "foo" + value: + Annotation [33-37]: + identifier: "bar" + value: + kind: AssignStmt [46-52]: + lhs: IndexedIdent [46-47]: + name: Ident [46-47] "x" + indices: + rhs: Expr [50-51]: Lit: Int(5) + else_body: "#]], + ); +} + +#[test] +fn nested_single_stmt_if_stmt() { + check( + parse, + "if (x) if (y) z q;", + &expect![[r#" + Stmt [0-18]: + annotations: + kind: IfStmt [0-18]: + condition: Expr [4-5]: Ident [4-5] "x" + if_body: Stmt [7-18]: + annotations: + kind: IfStmt [7-18]: + condition: Expr [11-12]: Ident [11-12] "y" + if_body: Stmt [14-18]: + annotations: + kind: GateCall [14-18]: + modifiers: + name: Ident [14-15] "z" + args: + duration: + qubits: + IndexedIdent [16-17]: + name: Ident [16-17] "q" + indices: + else_body: + else_body: "#]], + ); +} + +#[test] +fn nested_single_stmt_if_else_stmt() { + check( + parse, + "if (x) if (y) z q; else if (a) if (b) h q;", + &expect![[r#" + Stmt [0-42]: + annotations: + kind: IfStmt [0-42]: + condition: Expr [4-5]: Ident [4-5] "x" + if_body: Stmt [7-42]: + annotations: + kind: IfStmt [7-42]: + condition: Expr [11-12]: Ident [11-12] "y" + if_body: Stmt [14-18]: + annotations: + kind: GateCall [14-18]: + modifiers: + name: Ident [14-15] "z" + args: + duration: + qubits: + IndexedIdent [16-17]: + name: Ident [16-17] "q" + indices: + else_body: Stmt [24-42]: + annotations: + kind: IfStmt [24-42]: + condition: Expr [28-29]: Ident [28-29] "a" + if_body: Stmt [31-42]: + annotations: + kind: IfStmt [31-42]: + condition: Expr [35-36]: Ident [35-36] "b" + if_body: Stmt [38-42]: + annotations: + kind: GateCall [38-42]: + modifiers: + name: Ident [38-39] "h" + args: + duration: + qubits: + IndexedIdent [40-41]: + name: Ident [40-41] "q" + indices: + else_body: + else_body: + else_body: "#]], + ); +} + +#[test] +fn single_stmt_if_stmt_else_stmt() { + check( + parse, + "if (x) z q; else x q;", + &expect![[r#" + Stmt [0-21]: + annotations: + kind: IfStmt [0-21]: + condition: Expr [4-5]: Ident [4-5] "x" + if_body: Stmt [7-11]: + annotations: + kind: GateCall [7-11]: + modifiers: + name: Ident [7-8] "z" + args: + duration: + qubits: + IndexedIdent [9-10]: + name: Ident [9-10] "q" + indices: + else_body: Stmt [17-21]: + annotations: + kind: GateCall [17-21]: + modifiers: + name: Ident [17-18] "x" + args: + duration: + qubits: + IndexedIdent [19-20]: + name: Ident [19-20] "q" + indices: "#]], ); } diff --git a/compiler/qsc_qasm3/src/parser/stmt/tests/invalid_stmts/branch.rs b/compiler/qsc_qasm3/src/parser/stmt/tests/invalid_stmts/branch.rs index 0229f5bbb4..a054a7861a 100644 --- a/compiler/qsc_qasm3/src/parser/stmt/tests/invalid_stmts/branch.rs +++ b/compiler/qsc_qasm3/src/parser/stmt/tests/invalid_stmts/branch.rs @@ -60,12 +60,14 @@ fn assignment_in_if_condition() { annotations: kind: IfStmt [0-17]: condition: Expr [4-5]: Ident [4-5] "x" - if_block: - Stmt [13-15]: - annotations: - kind: ExprStmt [13-15]: - expr: Expr [13-14]: Lit: Int(3) - else_block: + if_body: Stmt [11-17]: + annotations: + kind: Block [11-17]: + Stmt [13-15]: + annotations: + kind: ExprStmt [13-15]: + expr: Expr [13-14]: Lit: Int(3) + else_body: [ Error( @@ -94,12 +96,14 @@ fn binary_op_assignment_in_if_condition() { annotations: kind: IfStmt [0-18]: condition: Expr [4-5]: Ident [4-5] "x" - if_block: - Stmt [14-16]: - annotations: - kind: ExprStmt [14-16]: - expr: Expr [14-15]: Lit: Int(3) - else_block: + if_body: Stmt [12-18]: + annotations: + kind: Block [12-18]: + Stmt [14-16]: + annotations: + kind: ExprStmt [14-16]: + expr: Expr [14-15]: Lit: Int(3) + else_body: [ Error( @@ -126,15 +130,14 @@ fn empty_if_block() { parse, "if (true);", &expect![[r#" - Stmt [0-10]: - annotations: - kind: IfStmt [0-10]: - condition: Expr [4-8]: Lit: Bool(true) - if_block: - Stmt [9-10]: + Stmt [0-10]: + annotations: + kind: IfStmt [0-10]: + condition: Expr [4-8]: Lit: Bool(true) + if_body: Stmt [9-10]: annotations: kind: Empty - else_block: "#]], + else_body: "#]], ); } diff --git a/compiler/qsc_qasm3/src/parser/stmt/tests/invalid_stmts/loops.rs b/compiler/qsc_qasm3/src/parser/stmt/tests/invalid_stmts/loops.rs index 5258d77869..9f6d3bd5b9 100644 --- a/compiler/qsc_qasm3/src/parser/stmt/tests/invalid_stmts/loops.rs +++ b/compiler/qsc_qasm3/src/parser/stmt/tests/invalid_stmts/loops.rs @@ -222,30 +222,29 @@ fn while_multi_condition() { parse, "while (true) (true) { x $0; }", &expect![[r#" - Stmt [0-19]: - annotations: - kind: WhileLoop [0-19]: - condition: Expr [7-11]: Lit: Bool(true) - block: - Stmt [13-19]: + Stmt [0-19]: + annotations: + kind: WhileLoop [0-19]: + condition: Expr [7-11]: Lit: Bool(true) + body: Stmt [13-19]: annotations: kind: ExprStmt [13-19]: expr: Expr [13-19]: Paren Expr [14-18]: Lit: Bool(true) - [ - Error( - Token( - Semicolon, - Open( - Brace, + [ + Error( + Token( + Semicolon, + Open( + Brace, + ), + Span { + lo: 20, + hi: 21, + }, ), - Span { - lo: 20, - hi: 21, - }, ), - ), - ]"#]], + ]"#]], ); } @@ -277,12 +276,11 @@ fn while_missing_body() { parse, "while (true);", &expect![[r#" - Stmt [0-13]: - annotations: - kind: WhileLoop [0-13]: - condition: Expr [7-11]: Lit: Bool(true) - block: - Stmt [12-13]: + Stmt [0-13]: + annotations: + kind: WhileLoop [0-13]: + condition: Expr [7-11]: Lit: Bool(true) + body: Stmt [12-13]: annotations: kind: Empty"#]], ); diff --git a/compiler/qsc_qasm3/src/parser/stmt/tests/while_loops.rs b/compiler/qsc_qasm3/src/parser/stmt/tests/while_loops.rs index 4c5a1e0af1..2f54709b85 100644 --- a/compiler/qsc_qasm3/src/parser/stmt/tests/while_loops.rs +++ b/compiler/qsc_qasm3/src/parser/stmt/tests/while_loops.rs @@ -20,14 +20,16 @@ fn simple_while() { op: Neq lhs: Expr [12-13]: Ident [12-13] "x" rhs: Expr [17-18]: Lit: Int(2) - block: - Stmt [30-36]: - annotations: - kind: AssignStmt [30-36]: - lhs: IndexedIdent [30-31]: - name: Ident [30-31] "a" - indices: - rhs: Expr [34-35]: Lit: Int(0)"#]], + body: Stmt [20-42]: + annotations: + kind: Block [20-42]: + Stmt [30-36]: + annotations: + kind: AssignStmt [30-36]: + lhs: IndexedIdent [30-31]: + name: Ident [30-31] "a" + indices: + rhs: Expr [34-35]: Lit: Int(0)"#]], ); } @@ -41,7 +43,9 @@ fn empty_while() { annotations: kind: WhileLoop [0-15]: condition: Expr [7-11]: Lit: Bool(true) - block: "#]], + body: Stmt [13-15]: + annotations: + kind: Block [13-15]: "#]], ); } @@ -60,14 +64,13 @@ fn while_stmt_body() { op: Neq lhs: Expr [12-13]: Ident [12-13] "x" rhs: Expr [17-18]: Lit: Int(2) - block: - Stmt [28-34]: - annotations: - kind: AssignStmt [28-34]: - lhs: IndexedIdent [28-29]: - name: Ident [28-29] "a" - indices: - rhs: Expr [32-33]: Lit: Int(0)"#]], + body: Stmt [28-34]: + annotations: + kind: AssignStmt [28-34]: + lhs: IndexedIdent [28-29]: + name: Ident [28-29] "a" + indices: + rhs: Expr [32-33]: Lit: Int(0)"#]], ); } @@ -88,17 +91,19 @@ fn while_loop_with_continue_stmt() { op: Neq lhs: Expr [12-13]: Ident [12-13] "x" rhs: Expr [17-18]: Lit: Int(2) - block: - Stmt [30-36]: - annotations: - kind: AssignStmt [30-36]: - lhs: IndexedIdent [30-31]: - name: Ident [30-31] "a" - indices: - rhs: Expr [34-35]: Lit: Int(0) - Stmt [45-54]: - annotations: - kind: ContinueStmt [45-54]"#]], + body: Stmt [20-60]: + annotations: + kind: Block [20-60]: + Stmt [30-36]: + annotations: + kind: AssignStmt [30-36]: + lhs: IndexedIdent [30-31]: + name: Ident [30-31] "a" + indices: + rhs: Expr [34-35]: Lit: Int(0) + Stmt [45-54]: + annotations: + kind: ContinueStmt [45-54]"#]], ); } @@ -119,16 +124,100 @@ fn while_loop_with_break_stmt() { op: Neq lhs: Expr [12-13]: Ident [12-13] "x" rhs: Expr [17-18]: Lit: Int(2) - block: - Stmt [30-36]: - annotations: - kind: AssignStmt [30-36]: - lhs: IndexedIdent [30-31]: - name: Ident [30-31] "a" - indices: - rhs: Expr [34-35]: Lit: Int(0) - Stmt [45-51]: - annotations: - kind: BreakStmt [45-51]"#]], + body: Stmt [20-57]: + annotations: + kind: Block [20-57]: + Stmt [30-36]: + annotations: + kind: AssignStmt [30-36]: + lhs: IndexedIdent [30-31]: + name: Ident [30-31] "a" + indices: + rhs: Expr [34-35]: Lit: Int(0) + Stmt [45-51]: + annotations: + kind: BreakStmt [45-51]"#]], + ); +} + +#[test] +fn single_stmt_while_stmt() { + check( + parse, + "while (x) z q;", + &expect![[r#" + Stmt [0-14]: + annotations: + kind: WhileLoop [0-14]: + condition: Expr [7-8]: Ident [7-8] "x" + body: Stmt [10-14]: + annotations: + kind: GateCall [10-14]: + modifiers: + name: Ident [10-11] "z" + args: + duration: + qubits: + IndexedIdent [12-13]: + name: Ident [12-13] "q" + indices: "#]], + ); +} + +#[test] +fn annotations_in_single_stmt_while_stmt() { + check( + parse, + " + while (x) + @foo + @bar + x = 5;", + &expect![[r#" + Stmt [5-55]: + annotations: + kind: WhileLoop [5-55]: + condition: Expr [12-13]: Ident [12-13] "x" + body: Stmt [23-55]: + annotations: + Annotation [23-27]: + identifier: "foo" + value: + Annotation [36-40]: + identifier: "bar" + value: + kind: AssignStmt [49-55]: + lhs: IndexedIdent [49-50]: + name: Ident [49-50] "x" + indices: + rhs: Expr [53-54]: Lit: Int(5)"#]], + ); +} + +#[test] +fn nested_single_stmt_while_stmt() { + check( + parse, + "while (x) while (y) z q;", + &expect![[r#" + Stmt [0-24]: + annotations: + kind: WhileLoop [0-24]: + condition: Expr [7-8]: Ident [7-8] "x" + body: Stmt [10-24]: + annotations: + kind: WhileLoop [10-24]: + condition: Expr [17-18]: Ident [17-18] "y" + body: Stmt [20-24]: + annotations: + kind: GateCall [20-24]: + modifiers: + name: Ident [20-21] "z" + args: + duration: + qubits: + IndexedIdent [22-23]: + name: Ident [22-23] "q" + indices: "#]], ); } From 98921aa94da832df6946c369633ff040bb66690b Mon Sep 17 00:00:00 2001 From: Ian Davis Date: Thu, 13 Mar 2025 12:16:27 -0700 Subject: [PATCH 060/108] Update cross file spans, separate parse/semantic result blurring (#2227) - Adding span offsetter for parsed files - Clean up error spans - Move box validation - Add a mutable visitor for AST nodes. --- compiler/qsc_qasm3/src/parser.rs | 61 +- compiler/qsc_qasm3/src/parser/ast.rs | 8 +- compiler/qsc_qasm3/src/parser/error.rs | 8 +- compiler/qsc_qasm3/src/parser/mut_visit.rs | 839 ++++++++++++++++++ compiler/qsc_qasm3/src/parser/prgm.rs | 2 +- compiler/qsc_qasm3/src/parser/stmt.rs | 121 ++- .../src/parser/stmt/tests/box_stmt.rs | 54 -- .../src/parser/stmt/tests/classical_decl.rs | 160 ++++ .../parser/stmt/tests/invalid_stmts/branch.rs | 17 +- .../parser/stmt/tests/invalid_stmts/loops.rs | 15 +- .../parser/stmt/tests/invalid_stmts/tokens.rs | 67 +- compiler/qsc_qasm3/src/semantic.rs | 35 +- compiler/qsc_qasm3/src/semantic/error.rs | 4 + compiler/qsc_qasm3/src/semantic/lowerer.rs | 75 +- compiler/qsc_qasm3/src/semantic/tests.rs | 146 ++- .../src/semantic/tests/decls/angle.rs | 27 - .../src/semantic/tests/decls/complex.rs | 54 -- .../src/semantic/tests/decls/float.rs | 27 - .../qsc_qasm3/src/semantic/tests/decls/int.rs | 54 -- .../src/semantic/tests/decls/uint.rs | 54 -- .../src/semantic/tests/statements.rs | 4 + .../src/semantic/tests/statements/box_stmt.rs | 50 ++ 22 files changed, 1447 insertions(+), 435 deletions(-) create mode 100644 compiler/qsc_qasm3/src/parser/mut_visit.rs create mode 100644 compiler/qsc_qasm3/src/semantic/tests/statements.rs create mode 100644 compiler/qsc_qasm3/src/semantic/tests/statements/box_stmt.rs diff --git a/compiler/qsc_qasm3/src/parser.rs b/compiler/qsc_qasm3/src/parser.rs index cdd177cc0f..6ec86a8472 100644 --- a/compiler/qsc_qasm3/src/parser.rs +++ b/compiler/qsc_qasm3/src/parser.rs @@ -4,6 +4,8 @@ pub mod ast; use crate::io::SourceResolver; use ast::{Program, StmtKind}; +use mut_visit::MutVisitor; +use qsc_data_structures::span::Span; use qsc_frontend::compile::SourceMap; use qsc_frontend::error::WithSource; use scan::ParserContext; @@ -17,11 +19,21 @@ mod completion; mod error; pub use error::Error; mod expr; +mod mut_visit; mod prgm; mod prim; mod scan; mod stmt; +struct Offsetter(pub(super) u32); + +impl MutVisitor for Offsetter { + fn visit_span(&mut self, span: &mut Span) { + span.lo += self.0; + span.hi += self.0; + } +} + pub struct QasmParseResult { pub source: QasmSource, pub source_map: SourceMap, @@ -31,6 +43,8 @@ impl QasmParseResult { #[must_use] pub fn new(source: QasmSource) -> QasmParseResult { let source_map = create_source_map(&source); + let mut source = source; + update_offsets(&source_map, &mut source); QasmParseResult { source, source_map } } @@ -63,19 +77,35 @@ impl QasmParseResult { } fn map_error(&self, error: Error) -> WithSource { - let path = self.source.path().display().to_string(); - let source = self.source_map.find_by_name(&path); - let offset = source.map_or(0, |source| source.offset); - - let offset_error = error.with_offset(offset); - WithSource::from_map( &self.source_map, - crate::Error(crate::ErrorKind::Parser(offset_error)), + crate::Error(crate::ErrorKind::Parser(error)), ) } } +/// all spans and errors spans are relative to the start of the file +/// We need to update the spans based on the offset of the file in the source map. +/// We have to do this after a full parse as we don't know what files will be loaded +/// until we have parsed all the includes. +fn update_offsets(source_map: &SourceMap, source: &mut QasmSource) { + let source_file = source_map.find_by_name(&source.path().display().to_string()); + let offset = source_file.map_or(0, |source| source.offset); + // Update the errors' offset + source + .errors + .iter_mut() + .for_each(|e| *e = e.clone().with_offset(offset)); + // Update the program's spans with the offset + let mut offsetter = Offsetter(offset); + offsetter.visit_program(&mut source.program); + + // Recursively update the includes, their programs, and errors + for include in source.includes_mut() { + update_offsets(source_map, include); + } +} + /// Parse a QASM file and return the parse result. /// This function will resolve includes using the provided resolver. /// If an include file cannot be resolved, an error will be returned. @@ -95,15 +125,7 @@ where /// and parse results. fn create_source_map(source: &QasmSource) -> SourceMap { let mut files: Vec<(Arc, Arc)> = Vec::new(); - files.push(( - Arc::from(source.path().to_string_lossy().to_string()), - Arc::from(source.source()), - )); - // Collect all source files from the includes, this - // begins the recursive process of collecting all source files. - for include in source.includes() { - collect_source_files(include, &mut files); - } + collect_source_files(source, &mut files); SourceMap::new(files, None) } @@ -113,6 +135,8 @@ fn collect_source_files(source: &QasmSource, files: &mut Vec<(Arc, Arc Arc::from(source.path().to_string_lossy().to_string()), Arc::from(source.source()), )); + // Collect all source files from the includes, this + // begins the recursive process of collecting all source files. for include in source.includes() { collect_source_files(include, files); } @@ -173,6 +197,11 @@ impl QasmSource { self.included.as_ref() } + #[must_use] + pub fn includes_mut(&mut self) -> &mut Vec { + self.included.as_mut() + } + #[must_use] pub fn program(&self) -> &Program { &self.program diff --git a/compiler/qsc_qasm3/src/parser/ast.rs b/compiler/qsc_qasm3/src/parser/ast.rs index 922cc9a27a..a4e829e319 100644 --- a/compiler/qsc_qasm3/src/parser/ast.rs +++ b/compiler/qsc_qasm3/src/parser/ast.rs @@ -686,9 +686,9 @@ impl Display for ClassicalArgument { #[derive(Clone, Debug)] pub enum ExternParameter { - Scalar(ScalarType, Span), - Quantum(Option, Span), ArrayReference(ArrayReferenceType, Span), + Quantum(Option, Span), + Scalar(ScalarType, Span), } impl Display for ExternParameter { @@ -1217,9 +1217,9 @@ impl Display for CalibrationStmt { #[derive(Clone, Debug)] pub enum TypedParameter { - Scalar(ScalarTypedParameter), - Quantum(QuantumTypedParameter), ArrayReference(ArrayTypedParameter), + Quantum(QuantumTypedParameter), + Scalar(ScalarTypedParameter), } impl WithSpan for TypedParameter { diff --git a/compiler/qsc_qasm3/src/parser/error.rs b/compiler/qsc_qasm3/src/parser/error.rs index fbba90d248..da16aa0353 100644 --- a/compiler/qsc_qasm3/src/parser/error.rs +++ b/compiler/qsc_qasm3/src/parser/error.rs @@ -95,15 +95,15 @@ pub enum ErrorKind { #[error("expected {0}, found {1}")] #[diagnostic(code("Qasm3.Parse.Token"))] Token(TokenKind, TokenKind, #[label] Span), + #[error("Empty statements are not supported")] + #[diagnostic(code("Qasm3.Parse.EmptyStatement"))] + EmptyStatement(#[label] Span), #[error("expected statement after annotation")] #[diagnostic(code("Qasm3.Parse.FloatingAnnotation"))] FloatingAnnotation(#[label] Span), #[error("expected {0}, found {1}")] #[diagnostic(code("Qasm3.Parse.Rule"))] Rule(&'static str, TokenKind, #[label] Span), - #[error("invalid classical statement in box")] - #[diagnostic(code("Qasm3.Parse.ClassicalStmtInBox"))] - ClassicalStmtInBox(#[label] Span), #[error("expected {0}, found {1}")] #[diagnostic(code("Qasm3.Parse.Convert"))] Convert(&'static str, &'static str, #[label] Span), @@ -146,8 +146,8 @@ impl ErrorKind { Self::Lit(name, span) => Self::Lit(name, span + offset), Self::Escape(ch, span) => Self::Escape(ch, span + offset), Self::Token(expected, actual, span) => Self::Token(expected, actual, span + offset), + Self::EmptyStatement(span) => Self::EmptyStatement(span + offset), Self::Rule(name, token, span) => Self::Rule(name, token, span + offset), - Self::ClassicalStmtInBox(span) => Self::ClassicalStmtInBox(span + offset), Self::Convert(expected, actual, span) => Self::Convert(expected, actual, span + offset), Self::MissingSemi(span) => Self::MissingSemi(span + offset), Self::MissingParens(span) => Self::MissingParens(span + offset), diff --git a/compiler/qsc_qasm3/src/parser/mut_visit.rs b/compiler/qsc_qasm3/src/parser/mut_visit.rs new file mode 100644 index 0000000000..b9ffcd2113 --- /dev/null +++ b/compiler/qsc_qasm3/src/parser/mut_visit.rs @@ -0,0 +1,839 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +use qsc_data_structures::span::Span; + +use super::ast::{ + AccessControl, AliasDeclStmt, Annotation, ArrayBaseTypeKind, ArrayReferenceType, ArrayType, + ArrayTypedParameter, AssignOpStmt, AssignStmt, BarrierStmt, BinOp, BinaryOpExpr, Block, + BoxStmt, BreakStmt, CalibrationGrammarStmt, CalibrationStmt, Cast, ClassicalDeclarationStmt, + ConstantDeclStmt, ContinueStmt, DefCalStmt, DefStmt, DelayStmt, DiscreteSet, EndStmt, + EnumerableSet, Expr, ExprStmt, ExternDecl, ExternParameter, ForStmt, FunctionCall, GPhase, + GateCall, GateModifierKind, GateOperand, HardwareQubit, IODeclaration, Ident, Identifier, + IfStmt, IncludeStmt, IndexElement, IndexExpr, IndexSet, IndexSetItem, IndexedIdent, Lit, + LiteralKind, MeasureExpr, MeasureStmt, Pragma, Program, QuantumGateDefinition, + QuantumGateModifier, QuantumTypedParameter, QubitDeclaration, RangeDefinition, ResetStmt, + ReturnStmt, ScalarType, ScalarTypedParameter, Stmt, StmtKind, SwitchCase, SwitchStmt, TypeDef, + TypedParameter, UnaryOp, UnaryOpExpr, ValueExpression, Version, WhileLoop, +}; + +pub trait MutVisitor: Sized { + fn visit_program(&mut self, program: &mut Program) { + walk_program(self, program); + } + + fn visit_block(&mut self, block: &mut Block) { + walk_block(self, block); + } + + fn visit_annotation(&mut self, annotation: &mut Annotation) { + walk_annotation(self, annotation); + } + + fn visit_stmt(&mut self, stmt: &mut Stmt) { + walk_stmt(self, stmt); + } + + fn visit_alias_decl_stmt(&mut self, stmt: &mut AliasDeclStmt) { + walk_alias_decl_stmt(self, stmt); + } + + fn visit_assign_stmt(&mut self, stmt: &mut AssignStmt) { + walk_assign_stmt(self, stmt); + } + + fn visit_assign_op_stmt(&mut self, stmt: &mut AssignOpStmt) { + walk_assign_op_stmt(self, stmt); + } + + fn visit_barrier_stmt(&mut self, stmt: &mut BarrierStmt) { + walk_barrier_stmt(self, stmt); + } + + fn visit_box_stmt(&mut self, stmt: &mut BoxStmt) { + walk_box_stmt(self, stmt); + } + + fn visit_break_stmt(&mut self, stmt: &mut BreakStmt) { + walk_break_stmt(self, stmt); + } + + fn visit_block_stmt(&mut self, stmt: &mut Block) { + walk_block_stmt(self, stmt); + } + + fn visit_cal_stmt(&mut self, stmt: &mut CalibrationStmt) { + walk_cal_stmt(self, stmt); + } + + fn visit_calibration_grammar_stmt(&mut self, stmt: &mut CalibrationGrammarStmt) { + walk_calibration_grammar_stmt(self, stmt); + } + + fn visit_classical_decl_stmt(&mut self, stmt: &mut ClassicalDeclarationStmt) { + walk_classical_decl_stmt(self, stmt); + } + + fn visit_const_decl_stmt(&mut self, stmt: &mut ConstantDeclStmt) { + walk_const_decl_stmt(self, stmt); + } + + fn visit_continue_stmt(&mut self, stmt: &mut ContinueStmt) { + walk_continue_stmt(self, stmt); + } + + fn visit_def_stmt(&mut self, stmt: &mut DefStmt) { + walk_def_stmt(self, stmt); + } + + fn visit_def_cal_stmt(&mut self, stmt: &mut DefCalStmt) { + walk_def_cal_stmt(self, stmt); + } + + fn visit_delay_stmt(&mut self, stmt: &mut DelayStmt) { + walk_delay_stmt(self, stmt); + } + + fn visit_end_stmt(&mut self, stmt: &mut EndStmt) { + walk_end_stmt(self, stmt); + } + + fn visit_expr_stmt(&mut self, stmt: &mut ExprStmt) { + walk_expr_stmt(self, stmt); + } + + fn visit_extern_decl_stmt(&mut self, stmt: &mut ExternDecl) { + walk_extern_stmt(self, stmt); + } + + fn visit_for_stmt(&mut self, stmt: &mut ForStmt) { + walk_for_stmt(self, stmt); + } + + fn visit_if_stmt(&mut self, stmt: &mut IfStmt) { + walk_if_stmt(self, stmt); + } + + fn visit_gate_call_stmt(&mut self, stmt: &mut GateCall) { + walk_gate_call_stmt(self, stmt); + } + + fn visit_gphase_stmt(&mut self, stmt: &mut GPhase) { + walk_gphase_stmt(self, stmt); + } + + fn visit_include_stmt(&mut self, stmt: &mut IncludeStmt) { + walk_include_stmt(self, stmt); + } + + fn visit_io_declaration_stmt(&mut self, stmt: &mut IODeclaration) { + walk_io_declaration_stmt(self, stmt); + } + + fn visit_measure_stmt(&mut self, stmt: &mut MeasureStmt) { + walk_measure_stmt(self, stmt); + } + + fn visit_pragma_stmt(&mut self, stmt: &mut Pragma) { + walk_pragma_stmt(self, stmt); + } + + fn visit_quantum_gate_definition_stmt(&mut self, stmt: &mut QuantumGateDefinition) { + walk_quantum_gate_definition_stmt(self, stmt); + } + + fn visit_quantum_decl_stmt(&mut self, stmt: &mut QubitDeclaration) { + walk_quantum_decl_stmt(self, stmt); + } + + fn visit_reset_stmt(&mut self, stmt: &mut ResetStmt) { + walk_reset_stmt(self, stmt); + } + + fn visit_return_stmt(&mut self, stmt: &mut ReturnStmt) { + walk_return_stmt(self, stmt); + } + + fn visit_switch_stmt(&mut self, stmt: &mut SwitchStmt) { + walk_switch_stmt(self, stmt); + } + + fn visit_while_loop_stmt(&mut self, stmt: &mut WhileLoop) { + walk_while_loop_stmt(self, stmt); + } + + fn visit_expr(&mut self, expr: &mut Expr) { + walk_expr(self, expr); + } + + fn visit_unary_op_expr(&mut self, expr: &mut UnaryOpExpr) { + walk_unary_op_expr(self, expr); + } + + fn visit_binary_op_expr(&mut self, expr: &mut BinaryOpExpr) { + walk_binary_op_expr(self, expr); + } + + fn visit_lit_expr(&mut self, expr: &mut Lit) { + walk_lit_expr(self, expr); + } + + fn visit_function_call_expr(&mut self, expr: &mut FunctionCall) { + walk_function_call_expr(self, expr); + } + + fn visit_cast_expr(&mut self, expr: &mut Cast) { + walk_cast_expr(self, expr); + } + + fn visit_index_expr(&mut self, expr: &mut IndexExpr) { + walk_index_expr(self, expr); + } + + fn visit_value_expr(&mut self, expr: &mut ValueExpression) { + walk_value_expr(self, expr); + } + + fn visit_measure_expr(&mut self, expr: &mut MeasureExpr) { + walk_measure_expr(self, expr); + } + + fn visit_identifier(&mut self, ident: &mut Identifier) { + walk_identifier(self, ident); + } + + fn visit_indexed_ident(&mut self, ident: &mut IndexedIdent) { + walk_indexed_ident(self, ident); + } + + fn visit_ident(&mut self, ident: &mut Ident) { + walk_ident(self, ident); + } + + fn visit_index_element(&mut self, elem: &mut IndexElement) { + walk_index_element(self, elem); + } + + fn visit_discrete_set(&mut self, set: &mut DiscreteSet) { + walk_discrete_set(self, set); + } + + fn visit_index_set(&mut self, set: &mut IndexSet) { + walk_index_set(self, set); + } + + fn visit_index_set_item(&mut self, item: &mut IndexSetItem) { + walk_index_set_item(self, item); + } + + fn visit_range_definition(&mut self, range: &mut RangeDefinition) { + walk_range_definition(self, range); + } + + fn visit_gate_operand(&mut self, operand: &mut GateOperand) { + walk_gate_operand(self, operand); + } + + fn visit_hardware_qubit(&mut self, qubit: &mut HardwareQubit) { + walk_hardware_qubit(self, qubit); + } + + fn visit_tydef(&mut self, ty: &mut TypeDef) { + walk_tydef(self, ty); + } + + fn visit_array_type(&mut self, ty: &mut ArrayType) { + walk_array_type(self, ty); + } + + fn visit_array_ref_type(&mut self, ty: &mut ArrayReferenceType) { + walk_array_ref_type(self, ty); + } + + fn visit_array_base_type(&mut self, ty: &mut ArrayBaseTypeKind) { + walk_array_base_type(self, ty); + } + + fn visit_scalar_type(&mut self, ty: &mut ScalarType) { + walk_scalar_type(self, ty); + } + + fn visit_typed_parameter(&mut self, param: &mut TypedParameter) { + walk_typed_parameter(self, param); + } + + fn visit_array_typed_parameter(&mut self, param: &mut ArrayTypedParameter) { + walk_array_typed_parameter(self, param); + } + + fn visit_quantum_typed_parameter(&mut self, param: &mut QuantumTypedParameter) { + walk_quantum_typed_parameter(self, param); + } + + fn visit_scalar_typed_parameter(&mut self, param: &mut ScalarTypedParameter) { + walk_scalar_typed_parameter(self, param); + } + + fn visit_extern_parameter(&mut self, param: &mut ExternParameter) { + walk_extern_parameter(self, param); + } + + fn visit_enumerable_set(&mut self, set: &mut EnumerableSet) { + walk_enumerable_set(self, set); + } + + fn visit_gate_modifier(&mut self, set: &mut QuantumGateModifier) { + walk_gate_modifier(self, set); + } + + fn visit_switch_case(&mut self, case: &mut SwitchCase) { + walk_switch_case(self, case); + } + + fn visit_access_control(&mut self, _: &mut AccessControl) {} + + fn visit_version(&mut self, _: &mut Version) {} + + fn visit_span(&mut self, _: &mut Span) {} + + fn visit_binop(&mut self, _: &mut BinOp) {} + + fn visit_unary_op(&mut self, _: &mut UnaryOp) {} +} + +pub fn walk_program(vis: &mut impl MutVisitor, program: &mut Program) { + vis.visit_span(&mut program.span); + program + .version + .iter_mut() + .for_each(|v| vis.visit_version(v)); + program + .statements + .iter_mut() + .for_each(|s| vis.visit_stmt(s)); +} + +pub fn walk_block(vis: &mut impl MutVisitor, block: &mut Block) { + vis.visit_span(&mut block.span); + block.stmts.iter_mut().for_each(|s| vis.visit_stmt(s)); +} + +pub fn walk_annotation(vis: &mut impl MutVisitor, annotation: &mut Annotation) { + vis.visit_span(&mut annotation.span); +} + +pub fn walk_stmt(vis: &mut impl MutVisitor, stmt: &mut Stmt) { + vis.visit_span(&mut stmt.span); + stmt.annotations + .iter_mut() + .for_each(|s| vis.visit_annotation(s)); + match &mut *stmt.kind { + StmtKind::Empty | StmtKind::Err => {} + StmtKind::Alias(alias_decl_stmt) => vis.visit_alias_decl_stmt(alias_decl_stmt), + StmtKind::Assign(assign_stmt) => vis.visit_assign_stmt(assign_stmt), + StmtKind::AssignOp(assign_op_stmt) => vis.visit_assign_op_stmt(assign_op_stmt), + StmtKind::Barrier(barrier_stmt) => vis.visit_barrier_stmt(barrier_stmt), + StmtKind::Box(box_stmt) => vis.visit_box_stmt(box_stmt), + StmtKind::Break(break_stmt) => vis.visit_break_stmt(break_stmt), + StmtKind::Block(block) => vis.visit_block_stmt(block), + StmtKind::Cal(calibration_stmt) => vis.visit_cal_stmt(calibration_stmt), + StmtKind::CalibrationGrammar(calibration_grammar_stmt) => { + vis.visit_calibration_grammar_stmt(calibration_grammar_stmt); + } + StmtKind::ClassicalDecl(classical_declaration_stmt) => { + vis.visit_classical_decl_stmt(classical_declaration_stmt); + } + StmtKind::ConstDecl(constant_decl_stmt) => vis.visit_const_decl_stmt(constant_decl_stmt), + StmtKind::Continue(continue_stmt) => vis.visit_continue_stmt(continue_stmt), + StmtKind::Def(def_stmt) => vis.visit_def_stmt(def_stmt), + StmtKind::DefCal(def_cal_stmt) => vis.visit_def_cal_stmt(def_cal_stmt), + StmtKind::Delay(delay_stmt) => vis.visit_delay_stmt(delay_stmt), + StmtKind::End(end_stmt) => vis.visit_end_stmt(end_stmt), + StmtKind::ExprStmt(expr_stmt) => vis.visit_expr_stmt(expr_stmt), + StmtKind::ExternDecl(extern_decl) => vis.visit_extern_decl_stmt(extern_decl), + StmtKind::For(for_stmt) => vis.visit_for_stmt(for_stmt), + StmtKind::If(if_stmt) => vis.visit_if_stmt(if_stmt), + StmtKind::GateCall(gate_call) => vis.visit_gate_call_stmt(gate_call), + StmtKind::GPhase(gphase) => vis.visit_gphase_stmt(gphase), + StmtKind::Include(include_stmt) => vis.visit_include_stmt(include_stmt), + StmtKind::IODeclaration(iodeclaration) => vis.visit_io_declaration_stmt(iodeclaration), + StmtKind::Measure(measure_stmt) => vis.visit_measure_stmt(measure_stmt), + StmtKind::Pragma(pragma) => vis.visit_pragma_stmt(pragma), + StmtKind::QuantumGateDefinition(quantum_gate_definition) => { + vis.visit_quantum_gate_definition_stmt(quantum_gate_definition); + } + StmtKind::QuantumDecl(qubit_declaration) => vis.visit_quantum_decl_stmt(qubit_declaration), + StmtKind::Reset(reset_stmt) => vis.visit_reset_stmt(reset_stmt), + StmtKind::Return(return_stmt) => vis.visit_return_stmt(return_stmt), + StmtKind::Switch(switch_stmt) => vis.visit_switch_stmt(switch_stmt), + StmtKind::WhileLoop(while_loop) => vis.visit_while_loop_stmt(while_loop), + } +} + +fn walk_alias_decl_stmt(vis: &mut impl MutVisitor, stmt: &mut AliasDeclStmt) { + vis.visit_span(&mut stmt.span); + vis.visit_identifier(&mut stmt.ident); + stmt.exprs.iter_mut().for_each(|e| vis.visit_expr(e)); +} + +fn walk_assign_stmt(vis: &mut impl MutVisitor, stmt: &mut AssignStmt) { + vis.visit_span(&mut stmt.span); + vis.visit_indexed_ident(&mut stmt.lhs); + vis.visit_expr(&mut stmt.rhs); +} + +fn walk_assign_op_stmt(vis: &mut impl MutVisitor, stmt: &mut AssignOpStmt) { + vis.visit_span(&mut stmt.span); + vis.visit_indexed_ident(&mut stmt.lhs); + vis.visit_binop(&mut stmt.op); + vis.visit_expr(&mut stmt.rhs); +} + +fn walk_barrier_stmt(vis: &mut impl MutVisitor, stmt: &mut BarrierStmt) { + vis.visit_span(&mut stmt.span); + stmt.qubits + .iter_mut() + .for_each(|operand| vis.visit_gate_operand(operand)); +} + +fn walk_box_stmt(vis: &mut impl MutVisitor, stmt: &mut BoxStmt) { + vis.visit_span(&mut stmt.span); + stmt.duration.iter_mut().for_each(|d| vis.visit_expr(d)); + stmt.body.iter_mut().for_each(|stmt| vis.visit_stmt(stmt)); +} + +fn walk_break_stmt(vis: &mut impl MutVisitor, stmt: &mut BreakStmt) { + vis.visit_span(&mut stmt.span); +} + +fn walk_block_stmt(vis: &mut impl MutVisitor, stmt: &mut Block) { + vis.visit_span(&mut stmt.span); + stmt.stmts.iter_mut().for_each(|stmt| vis.visit_stmt(stmt)); +} + +fn walk_cal_stmt(vis: &mut impl MutVisitor, stmt: &mut CalibrationStmt) { + vis.visit_span(&mut stmt.span); +} + +fn walk_calibration_grammar_stmt(vis: &mut impl MutVisitor, stmt: &mut CalibrationGrammarStmt) { + vis.visit_span(&mut stmt.span); +} + +fn walk_classical_decl_stmt(vis: &mut impl MutVisitor, stmt: &mut ClassicalDeclarationStmt) { + vis.visit_span(&mut stmt.span); + vis.visit_tydef(&mut stmt.ty); + vis.visit_ident(&mut stmt.identifier); + stmt.init_expr + .iter_mut() + .for_each(|e| vis.visit_value_expr(e)); +} + +fn walk_const_decl_stmt(vis: &mut impl MutVisitor, stmt: &mut ConstantDeclStmt) { + vis.visit_span(&mut stmt.span); + vis.visit_tydef(&mut stmt.ty); + vis.visit_ident(&mut stmt.identifier); + vis.visit_expr(&mut stmt.init_expr); +} + +fn walk_continue_stmt(vis: &mut impl MutVisitor, stmt: &mut ContinueStmt) { + vis.visit_span(&mut stmt.span); +} + +fn walk_def_stmt(vis: &mut impl MutVisitor, stmt: &mut DefStmt) { + vis.visit_span(&mut stmt.span); + vis.visit_ident(&mut stmt.name); + stmt.params + .iter_mut() + .for_each(|p| vis.visit_typed_parameter(p)); + stmt.return_type + .iter_mut() + .for_each(|ty| vis.visit_scalar_type(ty)); + vis.visit_block(&mut stmt.body); +} + +fn walk_def_cal_stmt(vis: &mut impl MutVisitor, stmt: &mut DefCalStmt) { + vis.visit_span(&mut stmt.span); +} + +fn walk_delay_stmt(vis: &mut impl MutVisitor, stmt: &mut DelayStmt) { + vis.visit_span(&mut stmt.span); + vis.visit_expr(&mut stmt.duration); + stmt.qubits + .iter_mut() + .for_each(|operand| vis.visit_gate_operand(operand)); +} + +fn walk_end_stmt(vis: &mut impl MutVisitor, stmt: &mut EndStmt) { + vis.visit_span(&mut stmt.span); +} + +fn walk_expr_stmt(vis: &mut impl MutVisitor, stmt: &mut ExprStmt) { + vis.visit_span(&mut stmt.span); + vis.visit_expr(&mut stmt.expr); +} + +fn walk_extern_stmt(vis: &mut impl MutVisitor, stmt: &mut ExternDecl) { + vis.visit_span(&mut stmt.span); + vis.visit_ident(&mut stmt.ident); + stmt.params + .iter_mut() + .for_each(|p| vis.visit_extern_parameter(p)); + stmt.return_type + .iter_mut() + .for_each(|ty| vis.visit_scalar_type(ty)); +} + +fn walk_for_stmt(vis: &mut impl MutVisitor, stmt: &mut ForStmt) { + vis.visit_span(&mut stmt.span); + vis.visit_scalar_type(&mut stmt.ty); + vis.visit_identifier(&mut stmt.identifier); + vis.visit_enumerable_set(&mut stmt.set_declaration); + vis.visit_stmt(&mut stmt.body); +} + +fn walk_if_stmt(vis: &mut impl MutVisitor, stmt: &mut IfStmt) { + vis.visit_span(&mut stmt.span); + vis.visit_expr(&mut stmt.condition); + vis.visit_stmt(&mut stmt.if_body); + stmt.else_body + .iter_mut() + .for_each(|else_body| vis.visit_stmt(else_body)); +} + +fn walk_gate_call_stmt(vis: &mut impl MutVisitor, stmt: &mut GateCall) { + vis.visit_span(&mut stmt.span); + vis.visit_ident(&mut stmt.name); + stmt.modifiers + .iter_mut() + .for_each(|m| vis.visit_gate_modifier(m)); + stmt.args.iter_mut().for_each(|arg| vis.visit_expr(arg)); + stmt.duration.iter_mut().for_each(|d| vis.visit_expr(d)); + stmt.qubits + .iter_mut() + .for_each(|q| vis.visit_gate_operand(q)); +} + +fn walk_gphase_stmt(vis: &mut impl MutVisitor, stmt: &mut GPhase) { + vis.visit_span(&mut stmt.span); + stmt.modifiers + .iter_mut() + .for_each(|m| vis.visit_gate_modifier(m)); + stmt.args.iter_mut().for_each(|arg| vis.visit_expr(arg)); + stmt.duration.iter_mut().for_each(|d| vis.visit_expr(d)); + stmt.qubits + .iter_mut() + .for_each(|q| vis.visit_gate_operand(q)); +} + +fn walk_include_stmt(vis: &mut impl MutVisitor, stmt: &mut IncludeStmt) { + vis.visit_span(&mut stmt.span); +} + +fn walk_io_declaration_stmt(vis: &mut impl MutVisitor, stmt: &mut IODeclaration) { + vis.visit_span(&mut stmt.span); + vis.visit_tydef(&mut stmt.ty); + vis.visit_ident(&mut stmt.ident); +} + +fn walk_measure_stmt(vis: &mut impl MutVisitor, stmt: &mut MeasureStmt) { + vis.visit_span(&mut stmt.span); + stmt.target + .iter_mut() + .for_each(|t| vis.visit_indexed_ident(t)); + vis.visit_measure_expr(&mut stmt.measurement); +} + +fn walk_pragma_stmt(vis: &mut impl MutVisitor, stmt: &mut Pragma) { + vis.visit_span(&mut stmt.span); +} + +fn walk_quantum_gate_definition_stmt(vis: &mut impl MutVisitor, stmt: &mut QuantumGateDefinition) { + vis.visit_span(&mut stmt.span); + vis.visit_ident(&mut stmt.ident); + stmt.params.iter_mut().for_each(|p| vis.visit_ident(p)); + stmt.qubits.iter_mut().for_each(|p| vis.visit_ident(p)); + vis.visit_block(&mut stmt.body); +} + +fn walk_quantum_decl_stmt(vis: &mut impl MutVisitor, stmt: &mut QubitDeclaration) { + vis.visit_span(&mut stmt.span); + vis.visit_ident(&mut stmt.qubit); + stmt.size.iter_mut().for_each(|s| vis.visit_expr(s)); +} + +fn walk_reset_stmt(vis: &mut impl MutVisitor, stmt: &mut ResetStmt) { + vis.visit_span(&mut stmt.span); + vis.visit_gate_operand(&mut stmt.operand); +} + +fn walk_return_stmt(vis: &mut impl MutVisitor, stmt: &mut ReturnStmt) { + vis.visit_span(&mut stmt.span); + stmt.expr.iter_mut().for_each(|e| vis.visit_value_expr(e)); +} + +fn walk_switch_stmt(vis: &mut impl MutVisitor, stmt: &mut SwitchStmt) { + vis.visit_span(&mut stmt.span); + vis.visit_expr(&mut stmt.target); + stmt.cases.iter_mut().for_each(|c| vis.visit_switch_case(c)); + stmt.default.iter_mut().for_each(|d| vis.visit_block(d)); +} + +fn walk_while_loop_stmt(vis: &mut impl MutVisitor, stmt: &mut WhileLoop) { + vis.visit_span(&mut stmt.span); + vis.visit_expr(&mut stmt.while_condition); + vis.visit_stmt(&mut stmt.body); +} + +fn walk_switch_case(vis: &mut impl MutVisitor, case: &mut SwitchCase) { + vis.visit_span(&mut case.span); + case.labels.iter_mut().for_each(|l| vis.visit_expr(l)); + vis.visit_block(&mut case.block); +} + +pub fn walk_expr(vis: &mut impl MutVisitor, expr: &mut Expr) { + vis.visit_span(&mut expr.span); + + match &mut *expr.kind { + super::ast::ExprKind::Err => {} + super::ast::ExprKind::Ident(ident) => vis.visit_ident(ident), + super::ast::ExprKind::UnaryOp(unary_op_expr) => vis.visit_unary_op_expr(unary_op_expr), + super::ast::ExprKind::BinaryOp(binary_op_expr) => vis.visit_binary_op_expr(binary_op_expr), + super::ast::ExprKind::Lit(lit) => vis.visit_lit_expr(lit), + super::ast::ExprKind::FunctionCall(function_call) => { + vis.visit_function_call_expr(function_call); + } + super::ast::ExprKind::Cast(cast) => vis.visit_cast_expr(cast), + super::ast::ExprKind::IndexExpr(index_expr) => vis.visit_index_expr(index_expr), + super::ast::ExprKind::Paren(expr) => vis.visit_expr(expr), + } +} + +pub fn walk_unary_op_expr(vis: &mut impl MutVisitor, expr: &mut UnaryOpExpr) { + vis.visit_unary_op(&mut expr.op); + vis.visit_expr(&mut expr.expr); +} + +pub fn walk_binary_op_expr(vis: &mut impl MutVisitor, expr: &mut BinaryOpExpr) { + vis.visit_expr(&mut expr.lhs); + vis.visit_binop(&mut expr.op); + vis.visit_expr(&mut expr.rhs); +} + +pub fn walk_lit_expr(vis: &mut impl MutVisitor, lit: &mut Lit) { + vis.visit_span(&mut lit.span); + if let LiteralKind::Array(exprs) = &mut lit.kind { + exprs.iter_mut().for_each(|e| vis.visit_expr(e)); + } +} + +pub fn walk_function_call_expr(vis: &mut impl MutVisitor, expr: &mut FunctionCall) { + vis.visit_span(&mut expr.span); + vis.visit_ident(&mut expr.name); + expr.args.iter_mut().for_each(|arg| vis.visit_expr(arg)); +} + +pub fn walk_cast_expr(vis: &mut impl MutVisitor, expr: &mut Cast) { + vis.visit_span(&mut expr.span); + vis.visit_tydef(&mut expr.ty); + vis.visit_expr(&mut expr.arg); +} + +pub fn walk_index_expr(vis: &mut impl MutVisitor, expr: &mut IndexExpr) { + vis.visit_span(&mut expr.span); + vis.visit_expr(&mut expr.collection); + vis.visit_index_element(&mut expr.index); +} + +pub fn walk_value_expr(vis: &mut impl MutVisitor, expr: &mut ValueExpression) { + match &mut *expr { + ValueExpression::Expr(expr) => vis.visit_expr(expr), + ValueExpression::Measurement(measure_expr) => vis.visit_measure_expr(measure_expr), + } +} + +pub fn walk_measure_expr(vis: &mut impl MutVisitor, expr: &mut MeasureExpr) { + vis.visit_span(&mut expr.span); + vis.visit_gate_operand(&mut expr.operand); +} + +pub fn walk_identifier(vis: &mut impl MutVisitor, ident: &mut Identifier) { + match ident { + Identifier::Ident(ident) => vis.visit_ident(ident), + Identifier::IndexedIdent(indexed_ident) => vis.visit_indexed_ident(indexed_ident), + } +} + +pub fn walk_indexed_ident(vis: &mut impl MutVisitor, ident: &mut IndexedIdent) { + vis.visit_span(&mut ident.span); + vis.visit_ident(&mut ident.name); + ident + .indices + .iter_mut() + .for_each(|elem| vis.visit_index_element(elem)); +} + +pub fn walk_ident(vis: &mut impl MutVisitor, ident: &mut Ident) { + vis.visit_span(&mut ident.span); +} + +pub fn walk_index_element(vis: &mut impl MutVisitor, elem: &mut IndexElement) { + match elem { + IndexElement::DiscreteSet(discrete_set) => vis.visit_discrete_set(discrete_set), + IndexElement::IndexSet(index_set) => vis.visit_index_set(index_set), + } +} + +pub fn walk_discrete_set(vis: &mut impl MutVisitor, set: &mut DiscreteSet) { + vis.visit_span(&mut set.span); + set.values.iter_mut().for_each(|e| vis.visit_expr(e)); +} + +pub fn walk_index_set(vis: &mut impl MutVisitor, set: &mut IndexSet) { + vis.visit_span(&mut set.span); + set.values + .iter_mut() + .for_each(|item| vis.visit_index_set_item(item)); +} + +pub fn walk_index_set_item(vis: &mut impl MutVisitor, item: &mut IndexSetItem) { + match item { + IndexSetItem::RangeDefinition(range_definition) => { + vis.visit_range_definition(range_definition); + } + IndexSetItem::Expr(expr) => vis.visit_expr(expr), + IndexSetItem::Err => {} + } +} + +pub fn walk_gate_operand(vis: &mut impl MutVisitor, operand: &mut GateOperand) { + match operand { + GateOperand::IndexedIdent(ident) => vis.visit_indexed_ident(ident), + GateOperand::HardwareQubit(hardware_qubit) => vis.visit_hardware_qubit(hardware_qubit), + GateOperand::Err => {} + } +} + +pub fn walk_tydef(vis: &mut impl MutVisitor, ty: &mut TypeDef) { + match ty { + TypeDef::Array(array) => vis.visit_array_type(array), + TypeDef::ArrayReference(array_ref) => vis.visit_array_ref_type(array_ref), + TypeDef::Scalar(scalar) => vis.visit_scalar_type(scalar), + } +} + +pub fn walk_array_type(vis: &mut impl MutVisitor, ty: &mut ArrayType) { + vis.visit_span(&mut ty.span); + vis.visit_array_base_type(&mut ty.base_type); + ty.dimensions.iter_mut().for_each(|d| vis.visit_expr(d)); +} + +pub fn walk_array_base_type(vis: &mut impl MutVisitor, ty: &mut ArrayBaseTypeKind) { + match ty { + ArrayBaseTypeKind::Int(ty) => vis.visit_span(&mut ty.span), + ArrayBaseTypeKind::UInt(ty) => vis.visit_span(&mut ty.span), + ArrayBaseTypeKind::Float(ty) => vis.visit_span(&mut ty.span), + ArrayBaseTypeKind::Complex(ty) => vis.visit_span(&mut ty.span), + ArrayBaseTypeKind::Angle(ty) => vis.visit_span(&mut ty.span), + _ => {} + } +} + +pub fn walk_array_ref_type(vis: &mut impl MutVisitor, ty: &mut ArrayReferenceType) { + vis.visit_span(&mut ty.span); + vis.visit_access_control(&mut ty.mutability); + vis.visit_array_base_type(&mut ty.base_type); + ty.dimensions.iter_mut().for_each(|d| vis.visit_expr(d)); +} + +pub fn walk_scalar_type(vis: &mut impl MutVisitor, ty: &mut ScalarType) { + vis.visit_span(&mut ty.span); + match &mut ty.kind { + super::ast::ScalarTypeKind::Bit(ty) => vis.visit_span(&mut ty.span), + super::ast::ScalarTypeKind::Int(ty) => vis.visit_span(&mut ty.span), + super::ast::ScalarTypeKind::UInt(ty) => vis.visit_span(&mut ty.span), + super::ast::ScalarTypeKind::Float(ty) => vis.visit_span(&mut ty.span), + super::ast::ScalarTypeKind::Complex(ty) => vis.visit_span(&mut ty.span), + super::ast::ScalarTypeKind::Angle(ty) => vis.visit_span(&mut ty.span), + _ => {} + } +} + +pub fn walk_typed_parameter(vis: &mut impl MutVisitor, ty: &mut TypedParameter) { + match ty { + TypedParameter::ArrayReference(array_typed_parameter) => { + vis.visit_array_typed_parameter(array_typed_parameter); + } + TypedParameter::Quantum(quantum_typed_parameter) => { + vis.visit_quantum_typed_parameter(quantum_typed_parameter); + } + TypedParameter::Scalar(scalar_typed_parameter) => { + vis.visit_scalar_typed_parameter(scalar_typed_parameter); + } + } +} + +pub fn walk_array_typed_parameter(vis: &mut impl MutVisitor, ty: &mut ArrayTypedParameter) { + vis.visit_span(&mut ty.span); + vis.visit_ident(&mut ty.ident); + vis.visit_array_ref_type(&mut ty.ty); +} + +pub fn walk_quantum_typed_parameter(vis: &mut impl MutVisitor, ty: &mut QuantumTypedParameter) { + vis.visit_span(&mut ty.span); + vis.visit_ident(&mut ty.ident); + ty.size.iter_mut().for_each(|s| vis.visit_expr(s)); +} + +pub fn walk_scalar_typed_parameter(vis: &mut impl MutVisitor, ty: &mut ScalarTypedParameter) { + vis.visit_span(&mut ty.span); + vis.visit_ident(&mut ty.ident); + vis.visit_scalar_type(&mut ty.ty); +} + +pub fn walk_extern_parameter(vis: &mut impl MutVisitor, param: &mut ExternParameter) { + match param { + ExternParameter::ArrayReference(ty, span) => { + vis.visit_span(span); + vis.visit_array_ref_type(ty); + } + ExternParameter::Quantum(expr, span) => { + vis.visit_span(span); + expr.iter_mut().for_each(|expr| vis.visit_expr(expr)); + } + ExternParameter::Scalar(ty, span) => { + vis.visit_span(span); + vis.visit_scalar_type(ty); + } + } +} + +pub fn walk_enumerable_set(vis: &mut impl MutVisitor, set: &mut EnumerableSet) { + match set { + EnumerableSet::DiscreteSet(set) => vis.visit_discrete_set(set), + EnumerableSet::RangeDefinition(range_definition) => { + vis.visit_range_definition(range_definition); + } + EnumerableSet::Expr(expr) => vis.visit_expr(expr), + } +} + +pub fn walk_gate_modifier(vis: &mut impl MutVisitor, modifier: &mut QuantumGateModifier) { + vis.visit_span(&mut modifier.span); + match &mut modifier.kind { + GateModifierKind::Inv => {} + GateModifierKind::Pow(expr) => vis.visit_expr(expr), + GateModifierKind::Ctrl(expr) => expr.iter_mut().for_each(|e| vis.visit_expr(e)), + GateModifierKind::NegCtrl(expr) => expr.iter_mut().for_each(|e| vis.visit_expr(e)), + } +} + +pub fn walk_hardware_qubit(vis: &mut impl MutVisitor, operand: &mut HardwareQubit) { + vis.visit_span(&mut operand.span); +} + +pub fn walk_range_definition(vis: &mut impl MutVisitor, range: &mut RangeDefinition) { + vis.visit_span(&mut range.span); + range.start.iter_mut().for_each(|s| vis.visit_expr(s)); + range.step.iter_mut().for_each(|s| vis.visit_expr(s)); + range.end.iter_mut().for_each(|s| vis.visit_expr(s)); +} diff --git a/compiler/qsc_qasm3/src/parser/prgm.rs b/compiler/qsc_qasm3/src/parser/prgm.rs index 52f836ba20..d87859a59e 100644 --- a/compiler/qsc_qasm3/src/parser/prgm.rs +++ b/compiler/qsc_qasm3/src/parser/prgm.rs @@ -74,6 +74,6 @@ fn parse_top_level_node(s: &mut ParserContext) -> Result { kind: Box::new(StmtKind::Block(block)), }) } else { - Ok(*stmt::parse(s)?) + Ok(stmt::parse(s)?) } } diff --git a/compiler/qsc_qasm3/src/parser/stmt.rs b/compiler/qsc_qasm3/src/parser/stmt.rs index 2625a887cd..08745117ef 100644 --- a/compiler/qsc_qasm3/src/parser/stmt.rs +++ b/compiler/qsc_qasm3/src/parser/stmt.rs @@ -73,85 +73,88 @@ use super::{prim::token, ParserContext}; /// | whileStatement /// ) /// ``` -pub(super) fn parse(s: &mut ParserContext) -> Result> { +pub(super) fn parse(s: &mut ParserContext) -> Result { let lo = s.peek().span.lo; if let Some(pragma) = opt(s, parse_pragma)? { - return Ok(Box::new(Stmt { + return Ok(Stmt { span: s.span(lo), annotations: [].into(), kind: Box::new(StmtKind::Pragma(pragma)), - })); + }); } let attrs = many(s, parse_annotation)?; let kind = if token(s, TokenKind::Semicolon).is_ok() { if attrs.is_empty() { - Box::new(StmtKind::Empty) - } else { let err_item = default(s.span(lo)); - s.push_error(Error::new(ErrorKind::FloatingAnnotation(err_item.span))); + s.push_error(Error::new(ErrorKind::EmptyStatement(err_item.span))); return Ok(err_item); } + let lo = attrs.iter().map(|a| a.span.lo).min().unwrap_or_default(); + let hi = attrs.iter().map(|a| a.span.hi).max().unwrap_or_default(); + let err_item = default(Span { lo, hi }); + s.push_error(Error::new(ErrorKind::FloatingAnnotation(err_item.span))); + return Ok(err_item); } else if let Some(decl) = opt(s, parse_gatedef)? { - Box::new(decl) + decl } else if let Some(decl) = opt(s, parse_def)? { - Box::new(decl) + decl } else if let Some(include) = opt(s, parse_include)? { - Box::new(include) + include } else if let Some(ty) = opt(s, scalar_or_array_type)? { - Box::new(disambiguate_type(s, ty)?) + disambiguate_type(s, ty)? } else if let Some(decl) = opt(s, parse_constant_classical_decl)? { - Box::new(decl) + decl } else if let Some(decl) = opt(s, parse_quantum_decl)? { - Box::new(decl) + decl } else if let Some(decl) = opt(s, parse_io_decl)? { - Box::new(decl) + decl } else if let Some(decl) = opt(s, qreg_decl)? { - Box::new(decl) + decl } else if let Some(decl) = opt(s, creg_decl)? { - Box::new(decl) + decl } else if let Some(decl) = opt(s, parse_extern)? { - Box::new(decl) + decl } else if let Some(switch) = opt(s, parse_switch_stmt)? { - Box::new(StmtKind::Switch(switch)) + StmtKind::Switch(switch) } else if let Some(stmt) = opt(s, parse_if_stmt)? { - Box::new(StmtKind::If(stmt)) + StmtKind::If(stmt) } else if let Some(stmt) = opt(s, parse_for_loop)? { - Box::new(StmtKind::For(stmt)) + StmtKind::For(stmt) } else if let Some(stmt) = opt(s, parse_while_loop)? { - Box::new(StmtKind::WhileLoop(stmt)) + StmtKind::WhileLoop(stmt) } else if let Some(stmt) = opt(s, parse_return)? { - Box::new(stmt) + stmt } else if let Some(stmt) = opt(s, parse_continue_stmt)? { - Box::new(StmtKind::Continue(stmt)) + StmtKind::Continue(stmt) } else if let Some(stmt) = opt(s, parse_break_stmt)? { - Box::new(StmtKind::Break(stmt)) + StmtKind::Break(stmt) } else if let Some(stmt) = opt(s, parse_end_stmt)? { - Box::new(StmtKind::End(stmt)) + StmtKind::End(stmt) } else if let Some(indexed_ident) = opt(s, indexed_identifier)? { - Box::new(disambiguate_ident(s, indexed_ident)?) + disambiguate_ident(s, indexed_ident)? } else if let Some(stmt_kind) = opt(s, parse_gate_call_stmt)? { - Box::new(stmt_kind) + stmt_kind } else if let Some(stmt) = opt(s, |s| parse_expression_stmt(s, None))? { - Box::new(StmtKind::ExprStmt(stmt)) + StmtKind::ExprStmt(stmt) } else if let Some(alias) = opt(s, parse_alias_stmt)? { - Box::new(StmtKind::Alias(alias)) + StmtKind::Alias(alias) } else if let Some(stmt) = opt(s, parse_box)? { - Box::new(StmtKind::Box(stmt)) + StmtKind::Box(stmt) } else if let Some(stmt) = opt(s, parse_calibration_grammar_stmt)? { - Box::new(StmtKind::CalibrationGrammar(stmt)) + StmtKind::CalibrationGrammar(stmt) } else if let Some(stmt) = opt(s, parse_defcal_stmt)? { - Box::new(StmtKind::DefCal(stmt)) + StmtKind::DefCal(stmt) } else if let Some(stmt) = opt(s, parse_cal)? { - Box::new(StmtKind::Cal(stmt)) + StmtKind::Cal(stmt) } else if let Some(stmt) = opt(s, parse_barrier)? { - Box::new(StmtKind::Barrier(stmt)) + StmtKind::Barrier(stmt) } else if let Some(stmt) = opt(s, parse_delay)? { - Box::new(StmtKind::Delay(stmt)) + StmtKind::Delay(stmt) } else if let Some(stmt) = opt(s, parse_reset)? { - Box::new(StmtKind::Reset(stmt)) + StmtKind::Reset(stmt) } else if let Some(stmt) = opt(s, parse_measure_stmt)? { - Box::new(StmtKind::Measure(stmt)) + StmtKind::Measure(stmt) } else { return Err(Error::new(ErrorKind::Rule( "statement", @@ -160,11 +163,11 @@ pub(super) fn parse(s: &mut ParserContext) -> Result> { ))); }; - Ok(Box::new(Stmt { + Ok(Stmt { span: s.span(lo), annotations: attrs.into_boxed_slice(), - kind, - })) + kind: Box::new(kind), + }) } /// This helper function allows us to disambiguate between @@ -276,10 +279,10 @@ fn disambiguate_ident(s: &mut ParserContext, indexed_ident: IndexedIdent) -> Res } #[allow(clippy::vec_box)] -pub(super) fn parse_many(s: &mut ParserContext) -> Result>> { +pub(super) fn parse_many(s: &mut ParserContext) -> Result> { many(s, |s| { recovering(s, default, &[TokenKind::Semicolon], |s| { - parse_block_or_stmt(s).map(Box::new) + parse_block_or_stmt(s) }) }) } @@ -292,17 +295,17 @@ pub(super) fn parse_block(s: &mut ParserContext) -> Result { recovering_token(s, TokenKind::Close(Delim::Brace)); Ok(Block { span: s.span(lo), - stmts: stmts.into_boxed_slice(), + stmts: list_from_iter(stmts), }) } #[allow(clippy::unnecessary_box_returns)] -fn default(span: Span) -> Box { - Box::new(Stmt { +fn default(span: Span) -> Stmt { + Stmt { span, annotations: Vec::new().into_boxed_slice(), kind: Box::new(StmtKind::Err), - }) + } } /// Grammar: `AnnotationKeyword RemainingLineContent?`. @@ -1148,7 +1151,7 @@ fn parse_block_or_stmt(s: &mut ParserContext) -> Result { kind: Box::new(StmtKind::Block(block)), }) } else { - Ok(*parse(s)?) + parse(s) } } @@ -1347,37 +1350,13 @@ fn parse_many_boxable_stmt(s: &mut ParserContext) -> Result> { annotations: Box::new([]), }, &[TokenKind::Semicolon], - parse_boxable_stmt, + parse, ) }); Ok(list_from_iter(stmts?)) } -/// These "boxable" stmts were taken from the reference parser at -/// . -/// Search for the definition of `Box` there, and then for all the classes -/// inhereting from `QuantumStatement`. -fn parse_boxable_stmt(s: &mut ParserContext) -> Result { - let stmt = *parse(s)?; - match &*stmt.kind { - StmtKind::Barrier(_) - | StmtKind::Delay(_) - | StmtKind::Reset(_) - | StmtKind::GateCall(_) - | StmtKind::GPhase(_) - | StmtKind::Box(_) => Ok(stmt), - _ => { - s.push_error(Error::new(ErrorKind::ClassicalStmtInBox(stmt.span))); - Ok(Stmt { - span: stmt.span, - annotations: stmt.annotations, - kind: Box::new(StmtKind::Err), - }) - } - } -} - /// In QASM3, it is hard to disambiguate between a quantum-gate-call missing modifiers /// and expression statements. Consider the following expressions: /// 1. `Ident(2, 3) a;` diff --git a/compiler/qsc_qasm3/src/parser/stmt/tests/box_stmt.rs b/compiler/qsc_qasm3/src/parser/stmt/tests/box_stmt.rs index 6e500319fa..6972c345af 100644 --- a/compiler/qsc_qasm3/src/parser/stmt/tests/box_stmt.rs +++ b/compiler/qsc_qasm3/src/parser/stmt/tests/box_stmt.rs @@ -86,57 +86,3 @@ fn box_stmt_with_designator() { indices: "#]], ); } - -#[test] -fn box_stmt_with_invalid_instruction() { - check( - parse, - "box { - H q0; - 2 + 4; - X q1; - }", - &expect![[r#" - Stmt [0-54]: - annotations: - kind: BoxStmt [0-54]: - duration: - body: - Stmt [14-19]: - annotations: - kind: GateCall [14-19]: - modifiers: - name: Ident [14-15] "H" - args: - duration: - qubits: - IndexedIdent [16-18]: - name: Ident [16-18] "q0" - indices: - Stmt [28-34]: - annotations: - kind: Err - Stmt [43-48]: - annotations: - kind: GateCall [43-48]: - modifiers: - name: Ident [43-44] "X" - args: - duration: - qubits: - IndexedIdent [45-47]: - name: Ident [45-47] "q1" - indices: - - [ - Error( - ClassicalStmtInBox( - Span { - lo: 28, - hi: 34, - }, - ), - ), - ]"#]], - ); -} diff --git a/compiler/qsc_qasm3/src/parser/stmt/tests/classical_decl.rs b/compiler/qsc_qasm3/src/parser/stmt/tests/classical_decl.rs index e9292638af..bdbe7580d9 100644 --- a/compiler/qsc_qasm3/src/parser/stmt/tests/classical_decl.rs +++ b/compiler/qsc_qasm3/src/parser/stmt/tests/classical_decl.rs @@ -288,6 +288,46 @@ fn const_complex_sized_decl_complex_lit() { ); } +#[test] +fn const_complex_implicit_bitness_default() { + check( + parse, + "const complex[float] x;", + &expect![[r#" + Error( + Token( + Eq, + Semicolon, + Span { + lo: 22, + hi: 23, + }, + ), + ) + "#]], + ); +} + +#[test] +fn const_complex_explicit_bitness_default() { + check( + parse, + "const complex[float[42]] x;", + &expect![[r#" + Error( + Token( + Eq, + Semicolon, + Span { + lo: 26, + hi: 27, + }, + ), + ) + "#]], + ); +} + #[test] fn int_decl() { check( @@ -320,6 +360,46 @@ fn int_decl_int_lit() { ); } +#[test] +fn const_int_explicit_bitness_int_default() { + check( + parse, + "const int[10] x;", + &expect![[r#" + Error( + Token( + Eq, + Semicolon, + Span { + lo: 15, + hi: 16, + }, + ), + ) + "#]], + ); +} + +#[test] +fn const_int_implicit_bitness_int_default() { + check( + parse, + "const int x;", + &expect![[r#" + Error( + Token( + Eq, + Semicolon, + Span { + lo: 11, + hi: 12, + }, + ), + ) + "#]], + ); +} + #[test] fn const_int_decl_int_lit() { check( @@ -416,6 +496,46 @@ fn uint_decl_uint_lit() { ); } +#[test] +fn const_uint_explicit_bitness_uint_default() { + check( + parse, + "const uint[10] x;", + &expect![[r#" + Error( + Token( + Eq, + Semicolon, + Span { + lo: 16, + hi: 17, + }, + ), + ) + "#]], + ); +} + +#[test] +fn const_uint_implicit_bitness_uint_default() { + check( + parse, + "const uint x;", + &expect![[r#" + Error( + Token( + Eq, + Semicolon, + Span { + lo: 12, + hi: 13, + }, + ), + ) + "#]], + ); +} + #[test] fn const_uint_decl_uint_lit() { check( @@ -528,6 +648,46 @@ fn const_float_decl_float_lit() { ); } +#[test] +fn const_float_default() { + check( + parse, + "const float x;", + &expect![[r#" + Error( + Token( + Eq, + Semicolon, + Span { + lo: 13, + hi: 14, + }, + ), + ) + "#]], + ); +} + +#[test] +fn const_float_sized_default() { + check( + parse, + "const float[64] x;", + &expect![[r#" + Error( + Token( + Eq, + Semicolon, + Span { + lo: 17, + hi: 18, + }, + ), + ) + "#]], + ); +} + #[test] fn float_sized_decl() { check( diff --git a/compiler/qsc_qasm3/src/parser/stmt/tests/invalid_stmts/branch.rs b/compiler/qsc_qasm3/src/parser/stmt/tests/invalid_stmts/branch.rs index a054a7861a..59d12cb6dc 100644 --- a/compiler/qsc_qasm3/src/parser/stmt/tests/invalid_stmts/branch.rs +++ b/compiler/qsc_qasm3/src/parser/stmt/tests/invalid_stmts/branch.rs @@ -125,7 +125,7 @@ fn binary_op_assignment_in_if_condition() { } #[test] -fn empty_if_block() { +fn empty_if_block_fails() { check( parse, "if (true);", @@ -136,8 +136,19 @@ fn empty_if_block() { condition: Expr [4-8]: Lit: Bool(true) if_body: Stmt [9-10]: annotations: - kind: Empty - else_body: "#]], + kind: Err + else_body: + + [ + Error( + EmptyStatement( + Span { + lo: 9, + hi: 10, + }, + ), + ), + ]"#]], ); } diff --git a/compiler/qsc_qasm3/src/parser/stmt/tests/invalid_stmts/loops.rs b/compiler/qsc_qasm3/src/parser/stmt/tests/invalid_stmts/loops.rs index 9f6d3bd5b9..5e775bc19f 100644 --- a/compiler/qsc_qasm3/src/parser/stmt/tests/invalid_stmts/loops.rs +++ b/compiler/qsc_qasm3/src/parser/stmt/tests/invalid_stmts/loops.rs @@ -271,7 +271,7 @@ fn while_with_for_syntax() { } #[test] -fn while_missing_body() { +fn while_missing_body_fails() { check( parse, "while (true);", @@ -282,6 +282,17 @@ fn while_missing_body() { condition: Expr [7-11]: Lit: Bool(true) body: Stmt [12-13]: annotations: - kind: Empty"#]], + kind: Err + + [ + Error( + EmptyStatement( + Span { + lo: 12, + hi: 13, + }, + ), + ), + ]"#]], ); } diff --git a/compiler/qsc_qasm3/src/parser/stmt/tests/invalid_stmts/tokens.rs b/compiler/qsc_qasm3/src/parser/stmt/tests/invalid_stmts/tokens.rs index 79c685326c..3be5bbc5ba 100644 --- a/compiler/qsc_qasm3/src/parser/stmt/tests/invalid_stmts/tokens.rs +++ b/compiler/qsc_qasm3/src/parser/stmt/tests/invalid_stmts/tokens.rs @@ -5,32 +5,41 @@ use crate::parser::{stmt::parse, tests::check}; use expect_test::expect; #[test] +#[allow(clippy::too_many_lines)] fn bad_tokens() { check( parse, "#;", &expect![[r#" - Stmt [1-2]: - annotations: - kind: Empty + Stmt [1-2]: + annotations: + kind: Err - [ - Error( - Lex( - Incomplete( - Ident, - Identifier, - Single( - Semi, + [ + Error( + Lex( + Incomplete( + Ident, + Identifier, + Single( + Semi, + ), + Span { + lo: 1, + hi: 2, + }, ), + ), + ), + Error( + EmptyStatement( Span { lo: 1, hi: 2, }, ), ), - ), - ]"#]], + ]"#]], ); check( @@ -121,23 +130,31 @@ fn bad_integer_literals() { parse, "3_4_;", &expect![[r#" - Stmt [4-5]: - annotations: - kind: Empty + Stmt [4-5]: + annotations: + kind: Err - [ - Error( - Lex( - Unknown( - '3', + [ + Error( + Lex( + Unknown( + '3', + Span { + lo: 0, + hi: 4, + }, + ), + ), + ), + Error( + EmptyStatement( Span { - lo: 0, - hi: 4, + lo: 4, + hi: 5, }, ), ), - ), - ]"#]], + ]"#]], ); check( diff --git a/compiler/qsc_qasm3/src/semantic.rs b/compiler/qsc_qasm3/src/semantic.rs index 1f676049b2..1682b3a953 100644 --- a/compiler/qsc_qasm3/src/semantic.rs +++ b/compiler/qsc_qasm3/src/semantic.rs @@ -4,13 +4,14 @@ use crate::io::SourceResolver; use crate::parser::QasmSource; +use lowerer::Lowerer; use qsc_frontend::compile::SourceMap; use qsc_frontend::error::WithSource; use symbols::SymbolTable; use std::path::Path; -mod ast; +pub(crate) mod ast; pub mod error; mod lowerer; pub use error::Error; @@ -25,7 +26,7 @@ pub struct QasmSemanticParseResult { pub source: QasmSource, pub source_map: SourceMap, pub symbols: self::symbols::SymbolTable, - pub program: self::ast::Program, + pub program: Option, pub errors: Vec>, } @@ -83,15 +84,9 @@ impl QasmSemanticParseResult { } fn map_parse_error(&self, error: crate::parser::Error) -> WithSource { - let path = self.source.path().display().to_string(); - let source = self.source_map.find_by_name(&path); - let offset = source.map_or(0, |source| source.offset); - - let offset_error = error.with_offset(offset); - WithSource::from_map( &self.source_map, - crate::Error(crate::ErrorKind::Parser(offset_error)), + crate::Error(crate::ErrorKind::Parser(error)), ) } } @@ -111,15 +106,19 @@ where R: SourceResolver, { let res = crate::parser::parse_source(source, path, resolver)?; - let analyzer = crate::semantic::lowerer::Lowerer { - source: res.source, - source_map: res.source_map, - errors: vec![], - file_stack: vec![], - symbols: SymbolTable::default(), - version: None, - stmts: vec![], - }; + + // If there are syntax errors, return early + if res.source.has_errors() { + return Ok(QasmSemanticParseResult { + source: res.source, + source_map: res.source_map, + symbols: SymbolTable::default(), + program: None, + errors: vec![], + }); + } + + let analyzer = Lowerer::new(res.source, res.source_map); let sem_res = analyzer.lower(); Ok(QasmSemanticParseResult { source: sem_res.source, diff --git a/compiler/qsc_qasm3/src/semantic/error.rs b/compiler/qsc_qasm3/src/semantic/error.rs index 48b7334ff6..68bb7d7721 100644 --- a/compiler/qsc_qasm3/src/semantic/error.rs +++ b/compiler/qsc_qasm3/src/semantic/error.rs @@ -60,6 +60,9 @@ pub enum SemanticErrorKind { #[error("Cannot cast expression of type {0} to type {1} as it would cause truncation.")] #[diagnostic(code("Qsc.Qasm3.Compile.CastWouldCauseTruncation"))] CastWouldCauseTruncation(String, String, #[label] Span), + #[error("invalid classical statement in box")] + #[diagnostic(code("Qsc.Qasm3.Compile.ClassicalStmtInBox"))] + ClassicalStmtInBox(#[label] Span), #[error("Complex numbers in assignment binary expressions are not yet supported.")] #[diagnostic(code("Qsc.Qasm3.Compile.ComplexBinaryAssignment"))] ComplexBinaryAssignment(#[label] Span), @@ -218,6 +221,7 @@ impl SemanticErrorKind { Self::CastWouldCauseTruncation(lhs, rhs, span) => { Self::CastWouldCauseTruncation(lhs, rhs, span + offset) } + Self::ClassicalStmtInBox(span) => Self::ClassicalStmtInBox(span + offset), Self::CalibrationsNotSupported(name, span) => { Self::CalibrationsNotSupported(name, span + offset) } diff --git a/compiler/qsc_qasm3/src/semantic/lowerer.rs b/compiler/qsc_qasm3/src/semantic/lowerer.rs index 4656c9a6b3..679700fef1 100644 --- a/compiler/qsc_qasm3/src/semantic/lowerer.rs +++ b/compiler/qsc_qasm3/src/semantic/lowerer.rs @@ -2,7 +2,6 @@ // Licensed under the MIT License. use std::ops::ShlAssign; -use std::path::PathBuf; use super::types::binop_requires_int_conversion_for_type; use super::types::binop_requires_symmetric_int_conversion; @@ -44,12 +43,26 @@ pub(super) struct Lowerer { /// When we include a file, we push the file path to the stack and pop it /// when we are done with the file. /// This allows us to report errors with the correct file path. - pub file_stack: Vec, pub symbols: SymbolTable, pub version: Option, pub stmts: Vec, } impl Lowerer { + pub fn new(source: QasmSource, source_map: SourceMap) -> Self { + let symbols = SymbolTable::default(); + let version = None; + let stmts = Vec::new(); + let errors = Vec::new(); + Self { + source, + source_map, + errors, + symbols, + version, + stmts, + } + } + pub fn lower(mut self) -> crate::semantic::QasmSemanticParseResult { // Should we fail if we see a version in included files? let source = &self.source.clone(); @@ -66,7 +79,7 @@ impl Lowerer { source: self.source, source_map: self.source_map, symbols: self.symbols, - program, + program: Some(program), errors: self.errors, } } @@ -97,11 +110,6 @@ impl Lowerer { /// Root recursive function for lowering the source. fn lower_source(&mut self, source: &QasmSource) { - // we push the file path to the stack so we can track the current file - // for reporting errors. This saves us from having to pass around - // the current QasmSource value. - self.file_stack.push(source.path()); - // we keep an iterator of the includes so we can match them with the // source includes. The include statements only have the path, but // we have already loaded all of source files in the @@ -139,10 +147,6 @@ impl Lowerer { } } } - - // Finally we pop the file path from the stack so that we - // can return to the previous file for error handling. - self.file_stack.pop(); } #[allow(clippy::too_many_lines)] @@ -316,13 +320,7 @@ impl Lowerer { /// Creates an error from the given kind with the current source mapping. fn create_err(&self, kind: crate::ErrorKind) -> WithSource { let error = crate::Error(kind); - let path = self.file_stack.last().map_or("", |p| { - p.to_str().expect("expected source mapping to exist.") - }); - let source = self.source_map.find_by_name(path); - let offset = source.map_or(0, |x| x.offset); - let offset_error = error.with_offset(offset); - WithSource::from_map(&self.source_map, offset_error) + WithSource::from_map(&self.source_map, error) } fn lower_alias(&mut self, alias: &syntax::AliasDeclStmt) -> Option { @@ -790,7 +788,46 @@ impl Lowerer { None } + /// The "boxable" stmts were taken from the reference parser at + /// . + /// Search for the definition of `Box` there, and then for all the classes + /// inhereting from `QuantumStatement`. fn lower_box(&mut self, stmt: &syntax::BoxStmt) -> Option { + let stmts = stmt + .body + .iter() + .map(|stmt| self.lower_stmt(stmt)) + .collect::>(); + + let mut has_invalid_stmt_kinds = false; + for stmt in &stmt.body { + match &*stmt.kind { + syntax::StmtKind::Barrier(_) + | syntax::StmtKind::Delay(_) + | syntax::StmtKind::Reset(_) + | syntax::StmtKind::GateCall(_) + | syntax::StmtKind::GPhase(_) + | syntax::StmtKind::Box(_) => { + // valid statements + } + _ => { + self.push_semantic_error(SemanticErrorKind::ClassicalStmtInBox(stmt.span)); + has_invalid_stmt_kinds = true; + } + } + } + + if let Some(duration) = &stmt.duration { + self.push_unsupported_error_message("Box with duration", duration.span); + return None; + } + + if has_invalid_stmt_kinds || stmts.len() != stmt.body.len() { + return None; + } + + // we semantically checked the stmts, but we still need to lower them + // with the correct behavior based on any pragmas that might be present self.push_unimplemented_error_message("box stmt", stmt.span); None } diff --git a/compiler/qsc_qasm3/src/semantic/tests.rs b/compiler/qsc_qasm3/src/semantic/tests.rs index 490a573561..cfa028a5b4 100644 --- a/compiler/qsc_qasm3/src/semantic/tests.rs +++ b/compiler/qsc_qasm3/src/semantic/tests.rs @@ -5,6 +5,7 @@ pub mod assignment; pub mod decls; pub mod expression; +pub mod statements; use std::path::Path; use std::sync::Arc; @@ -16,6 +17,7 @@ use super::parse_source; use super::QasmSemanticParseResult; +use expect_test::expect; use miette::Report; use expect_test::Expect; @@ -141,14 +143,74 @@ fn check_map( let res = parse_source(input, "test", &resolver) .map_err(|e| vec![e]) .expect("failed to parse"); + + let errors = res.all_errors(); + + assert!( + !res.has_syntax_errors(), + "syntax errors: {:?}", + res.parse_errors() + ); + + let program = res.program.expect("no program"); + + if errors.is_empty() { + expect.assert_eq(&selector(&program, &res.symbols)); + } else { + expect.assert_eq(&format!( + "{}\n\n{:?}", + program, + errors + .iter() + .map(|e| Report::new(e.clone())) + .collect::>() + )); + } +} + +pub(super) fn check_all

( + path: P, + sources: impl IntoIterator, Arc)>, + expect: &Expect, +) where + P: AsRef, +{ + check_map_all(path, sources, expect, |p, _| p.to_string()); +} + +fn check_map_all

( + path: P, + sources: impl IntoIterator, Arc)>, + expect: &Expect, + selector: impl FnOnce(&super::ast::Program, &super::SymbolTable) -> String, +) where + P: AsRef, +{ + let resolver = InMemorySourceResolver::from_iter(sources); + let source = resolver + .resolve(path.as_ref()) + .map_err(|e| vec![e]) + .expect("could not load source") + .1; + let res = parse_source(source, path, &resolver) + .map_err(|e| vec![e]) + .expect("failed to parse"); + let errors = res.all_errors(); + assert!( + !res.has_syntax_errors(), + "syntax errors: {:?}", + res.parse_errors() + ); + let program = res.program.expect("no program"); + if errors.is_empty() { - expect.assert_eq(&selector(&res.program, &res.symbols)); + expect.assert_eq(&selector(&program, &res.symbols)); } else { expect.assert_eq(&format!( "{}\n\n{:?}", - res.program, + program, errors .iter() .map(|e| Report::new(e.clone())) @@ -156,3 +218,83 @@ fn check_map( )); } } + +#[test] +fn semantic_errors_map_to_their_corresponding_file_specific_spans() { + let source0 = r#"OPENQASM 3.0; + include "stdgates.inc"; + include "source1.qasm"; + bit c = r; // undefined symbol r + "#; + let source1 = r#"include "source2.qasm"; + angle z = 7.0; + float k = z + false; // invalid cast"#; + let source2 = "bit x = 1; + bool x = y && x; // undefined y, redefine x"; + let all_sources = [ + ("source0.qasm".into(), source0.into()), + ("source1.qasm".into(), source1.into()), + ("source2.qasm".into(), source2.into()), + ]; + + check_all( + "source0.qasm", + all_sources, + &expect![[r#" + Program: + version: 3.0 + statements: + Stmt [196-206]: + annotations: + kind: ClassicalDeclarationStmt [196-206]: + symbol_id: 24 + ty_span: [196-199] + init_expr: Expr [204-205]: + ty: Bit(true) + kind: Lit: Int(1) + Stmt [140-154]: + annotations: + kind: ClassicalDeclarationStmt [140-154]: + symbol_id: 26 + ty_span: [140-145] + init_expr: Expr [150-153]: + ty: Angle(None, true) + kind: Lit: Float(7.0) + + [Qsc.Qasm3.Compile.UndefinedSymbol + + x Undefined symbol: y. + ,-[source2.qasm:2:14] + 1 | bit x = 1; + 2 | bool x = y && x; // undefined y, redefine x + : ^ + `---- + , Qsc.Qasm3.Compile.RedefinedSymbol + + x Redefined symbol: x. + ,-[source2.qasm:2:10] + 1 | bit x = 1; + 2 | bool x = y && x; // undefined y, redefine x + : ^ + `---- + , Qsc.Qasm3.Compile.CannotCast + + x Cannot cast expression of type Angle(None, false) to type Float(None, + | false) + ,-[source1.qasm:3:15] + 2 | angle z = 7.0; + 3 | float k = z + false; // invalid cast + : ^ + `---- + , Qsc.Qasm3.Compile.UndefinedSymbol + + x Undefined symbol: r. + ,-[source0.qasm:4:13] + 3 | include "source1.qasm"; + 4 | bit c = r; // undefined symbol r + : ^ + 5 | + `---- + ]"#]], + ); +} diff --git a/compiler/qsc_qasm3/src/semantic/tests/decls/angle.rs b/compiler/qsc_qasm3/src/semantic/tests/decls/angle.rs index 22759f8ce0..04bc074d19 100644 --- a/compiler/qsc_qasm3/src/semantic/tests/decls/angle.rs +++ b/compiler/qsc_qasm3/src/semantic/tests/decls/angle.rs @@ -24,33 +24,6 @@ fn implicit_bitness_default() { ); } -#[test] -fn const_default_fails() { - check_classical_decl( - "const angle x;", - &expect![[r#" - Program: - version: - statements: - - [Qasm3.Parse.Token - - x expected `=`, found `;` - ,-[test:1:14] - 1 | const angle x; - : ^ - `---- - , Qsc.Qasm3.Compile.UnexpectedParserError - - x Unexpected parser error: Unexpected error. - ,-[test:1:1] - 1 | const angle x; - : ^^^^^^^^^^^^^^ - `---- - ]"#]], - ); -} - #[test] fn lit() { check_classical_decl( diff --git a/compiler/qsc_qasm3/src/semantic/tests/decls/complex.rs b/compiler/qsc_qasm3/src/semantic/tests/decls/complex.rs index 62c3a38ad8..ab56fbd88b 100644 --- a/compiler/qsc_qasm3/src/semantic/tests/decls/complex.rs +++ b/compiler/qsc_qasm3/src/semantic/tests/decls/complex.rs @@ -24,33 +24,6 @@ fn implicit_bitness_default() { ); } -#[test] -fn const_implicit_bitness_default() { - check_classical_decl( - "const complex[float] x;", - &expect![[r#" - Program: - version: - statements: - - [Qasm3.Parse.Token - - x expected `=`, found `;` - ,-[test:1:23] - 1 | const complex[float] x; - : ^ - `---- - , Qsc.Qasm3.Compile.UnexpectedParserError - - x Unexpected parser error: Unexpected error. - ,-[test:1:1] - 1 | const complex[float] x; - : ^^^^^^^^^^^^^^^^^^^^^^^ - `---- - ]"#]], - ); -} - #[test] fn explicit_bitness_default() { check_classical_decl( @@ -70,33 +43,6 @@ fn explicit_bitness_default() { ); } -#[test] -fn const_explicit_bitness_default() { - check_classical_decl( - "const complex[float[42]] x;", - &expect![[r#" - Program: - version: - statements: - - [Qasm3.Parse.Token - - x expected `=`, found `;` - ,-[test:1:27] - 1 | const complex[float[42]] x; - : ^ - `---- - , Qsc.Qasm3.Compile.UnexpectedParserError - - x Unexpected parser error: Unexpected error. - ,-[test:1:1] - 1 | const complex[float[42]] x; - : ^^^^^^^^^^^^^^^^^^^^^^^^^^^ - `---- - ]"#]], - ); -} - #[test] fn const_implicit_bitness_double_img_only() { check_classical_decl( diff --git a/compiler/qsc_qasm3/src/semantic/tests/decls/float.rs b/compiler/qsc_qasm3/src/semantic/tests/decls/float.rs index 7975ff3a9e..c04f74943f 100644 --- a/compiler/qsc_qasm3/src/semantic/tests/decls/float.rs +++ b/compiler/qsc_qasm3/src/semantic/tests/decls/float.rs @@ -24,33 +24,6 @@ fn implicit_bitness_default() { ); } -#[test] -fn const_default() { - check_classical_decl( - "const float x;", - &expect![[r#" - Program: - version: - statements: - - [Qasm3.Parse.Token - - x expected `=`, found `;` - ,-[test:1:14] - 1 | const float x; - : ^ - `---- - , Qsc.Qasm3.Compile.UnexpectedParserError - - x Unexpected parser error: Unexpected error. - ,-[test:1:1] - 1 | const float x; - : ^^^^^^^^^^^^^^ - `---- - ]"#]], - ); -} - #[test] fn lit() { check_classical_decl( diff --git a/compiler/qsc_qasm3/src/semantic/tests/decls/int.rs b/compiler/qsc_qasm3/src/semantic/tests/decls/int.rs index 7a0f38a21c..be6373acae 100644 --- a/compiler/qsc_qasm3/src/semantic/tests/decls/int.rs +++ b/compiler/qsc_qasm3/src/semantic/tests/decls/int.rs @@ -62,33 +62,6 @@ fn implicit_bitness_int_default() { ); } -#[test] -fn const_implicit_bitness_int_default() { - check_classical_decl( - "const int x;", - &expect![[r#" - Program: - version: - statements: - - [Qasm3.Parse.Token - - x expected `=`, found `;` - ,-[test:1:12] - 1 | const int x; - : ^ - `---- - , Qsc.Qasm3.Compile.UnexpectedParserError - - x Unexpected parser error: Unexpected error. - ,-[test:1:1] - 1 | const int x; - : ^^^^^^^^^^^^ - `---- - ]"#]], - ); -} - #[test] fn const_implicit_bitness_int_lit() { check_classical_decl( @@ -336,33 +309,6 @@ fn explicit_bitness_int_default() { ); } -#[test] -fn const_explicit_bitness_int_default() { - check_classical_decl( - "const int[10] x;", - &expect![[r#" - Program: - version: - statements: - - [Qasm3.Parse.Token - - x expected `=`, found `;` - ,-[test:1:16] - 1 | const int[10] x; - : ^ - `---- - , Qsc.Qasm3.Compile.UnexpectedParserError - - x Unexpected parser error: Unexpected error. - ,-[test:1:1] - 1 | const int[10] x; - : ^^^^^^^^^^^^^^^^ - `---- - ]"#]], - ); -} - #[test] fn explicit_bitness_int() { check_classical_decl( diff --git a/compiler/qsc_qasm3/src/semantic/tests/decls/uint.rs b/compiler/qsc_qasm3/src/semantic/tests/decls/uint.rs index 78ffe2cc58..0e7ecb3f53 100644 --- a/compiler/qsc_qasm3/src/semantic/tests/decls/uint.rs +++ b/compiler/qsc_qasm3/src/semantic/tests/decls/uint.rs @@ -24,33 +24,6 @@ fn implicit_bitness_int_default() { ); } -#[test] -fn const_implicit_bitness_int_default() { - check_classical_decl( - "const uint x;", - &expect![[r#" - Program: - version: - statements: - - [Qasm3.Parse.Token - - x expected `=`, found `;` - ,-[test:1:13] - 1 | const uint x; - : ^ - `---- - , Qsc.Qasm3.Compile.UnexpectedParserError - - x Unexpected parser error: Unexpected error. - ,-[test:1:1] - 1 | const uint x; - : ^^^^^^^^^^^^^ - `---- - ]"#]], - ); -} - #[test] fn const_implicit_bitness_int_lit() { check_classical_decl( @@ -336,33 +309,6 @@ fn const_explicit_bitness_int() { ); } -#[test] -fn explicit_bitness_int() { - check_classical_decl( - "const uint[10] x;", - &expect![[r#" - Program: - version: - statements: - - [Qasm3.Parse.Token - - x expected `=`, found `;` - ,-[test:1:17] - 1 | const uint[10] x; - : ^ - `---- - , Qsc.Qasm3.Compile.UnexpectedParserError - - x Unexpected parser error: Unexpected error. - ,-[test:1:1] - 1 | const uint[10] x; - : ^^^^^^^^^^^^^^^^^ - `---- - ]"#]], - ); -} - #[test] fn assigning_uint_to_negative_lit_results_in_semantic_error() { check_classical_decl( diff --git a/compiler/qsc_qasm3/src/semantic/tests/statements.rs b/compiler/qsc_qasm3/src/semantic/tests/statements.rs new file mode 100644 index 0000000000..8b24eb8bbc --- /dev/null +++ b/compiler/qsc_qasm3/src/semantic/tests/statements.rs @@ -0,0 +1,4 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +pub mod box_stmt; diff --git a/compiler/qsc_qasm3/src/semantic/tests/statements/box_stmt.rs b/compiler/qsc_qasm3/src/semantic/tests/statements/box_stmt.rs new file mode 100644 index 0000000000..ff478ed7bd --- /dev/null +++ b/compiler/qsc_qasm3/src/semantic/tests/statements/box_stmt.rs @@ -0,0 +1,50 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +use expect_test::expect; + +use crate::semantic::tests::check_stmt_kinds; + +#[test] +fn with_invalid_instruction_fails() { + check_stmt_kinds( + "box { + 2 + 4; + }", + &expect![[r#" + Program: + version: + statements: + + [Qsc.Qasm3.Compile.ClassicalStmtInBox + + x invalid classical statement in box + ,-[test:2:9] + 1 | box { + 2 | 2 + 4; + : ^^^^^^ + 3 | } + `---- + ]"#]], + ); +} + +#[test] +fn with_duration_fails() { + check_stmt_kinds( + "box [4us] { }", + &expect![[r#" + Program: + version: + statements: + + [Qsc.Qasm3.Compile.NotSupported + + x Box with duration are not supported. + ,-[test:1:6] + 1 | box [4us] { } + : ^^^ + `---- + ]"#]], + ); +} From 09971214a9db9a37cb1e2f10f93148e8000b884f Mon Sep 17 00:00:00 2001 From: orpuente-MS <156957451+orpuente-MS@users.noreply.github.com> Date: Mon, 17 Mar 2025 06:13:34 -0700 Subject: [PATCH 061/108] Lower control flow stmts in QASM3 parser (#2232) This PR: 1. Aliases super::ast to semantic. 2. Fixes The bodies of if, for, and while to be `Stmt` instead of `List`. 3. Lowers the if, for, while, and switch stmts. 4. Refactors the pattern used to handle lowering of optional items by introducing the `short_circuit_opt_item!` macro. --- compiler/qsc_qasm3/src/parser/ast.rs | 4 +- compiler/qsc_qasm3/src/parser/mut_visit.rs | 2 +- compiler/qsc_qasm3/src/parser/stmt.rs | 8 +- .../src/parser/stmt/tests/for_loops.rs | 38 +- compiler/qsc_qasm3/src/semantic/ast.rs | 54 +- compiler/qsc_qasm3/src/semantic/error.rs | 6 + compiler/qsc_qasm3/src/semantic/lowerer.rs | 898 +++++++++++------- .../src/semantic/tests/statements.rs | 6 +- .../src/semantic/tests/statements/box_stmt.rs | 3 +- .../src/semantic/tests/statements/for_stmt.rs | 92 ++ .../src/semantic/tests/statements/if_stmt.rs | 175 ++++ .../semantic/tests/statements/switch_stmt.rs | 117 +++ .../semantic/tests/statements/while_stmt.rs | 94 ++ 13 files changed, 1128 insertions(+), 369 deletions(-) create mode 100644 compiler/qsc_qasm3/src/semantic/tests/statements/for_stmt.rs create mode 100644 compiler/qsc_qasm3/src/semantic/tests/statements/if_stmt.rs create mode 100644 compiler/qsc_qasm3/src/semantic/tests/statements/switch_stmt.rs create mode 100644 compiler/qsc_qasm3/src/semantic/tests/statements/while_stmt.rs diff --git a/compiler/qsc_qasm3/src/parser/ast.rs b/compiler/qsc_qasm3/src/parser/ast.rs index a4e829e319..3dea4a2096 100644 --- a/compiler/qsc_qasm3/src/parser/ast.rs +++ b/compiler/qsc_qasm3/src/parser/ast.rs @@ -1369,7 +1369,7 @@ impl Display for WhileLoop { pub struct ForStmt { pub span: Span, pub ty: ScalarType, - pub identifier: Identifier, + pub ident: Ident, pub set_declaration: Box, pub body: Stmt, } @@ -1378,7 +1378,7 @@ impl Display for ForStmt { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { writeln_header(f, "ForStmt", self.span)?; writeln_field(f, "variable_type", &self.ty)?; - writeln_field(f, "variable_name", &self.identifier)?; + writeln_field(f, "variable_name", &self.ident)?; writeln_field(f, "iterable", &self.set_declaration)?; write_field(f, "body", &self.body) } diff --git a/compiler/qsc_qasm3/src/parser/mut_visit.rs b/compiler/qsc_qasm3/src/parser/mut_visit.rs index b9ffcd2113..e25c5985c2 100644 --- a/compiler/qsc_qasm3/src/parser/mut_visit.rs +++ b/compiler/qsc_qasm3/src/parser/mut_visit.rs @@ -486,7 +486,7 @@ fn walk_extern_stmt(vis: &mut impl MutVisitor, stmt: &mut ExternDecl) { fn walk_for_stmt(vis: &mut impl MutVisitor, stmt: &mut ForStmt) { vis.visit_span(&mut stmt.span); vis.visit_scalar_type(&mut stmt.ty); - vis.visit_identifier(&mut stmt.identifier); + vis.visit_ident(&mut stmt.ident); vis.visit_enumerable_set(&mut stmt.set_declaration); vis.visit_stmt(&mut stmt.body); } diff --git a/compiler/qsc_qasm3/src/parser/stmt.rs b/compiler/qsc_qasm3/src/parser/stmt.rs index 08745117ef..c2915be11b 100644 --- a/compiler/qsc_qasm3/src/parser/stmt.rs +++ b/compiler/qsc_qasm3/src/parser/stmt.rs @@ -119,7 +119,7 @@ pub(super) fn parse(s: &mut ParserContext) -> Result { StmtKind::Switch(switch) } else if let Some(stmt) = opt(s, parse_if_stmt)? { StmtKind::If(stmt) - } else if let Some(stmt) = opt(s, parse_for_loop)? { + } else if let Some(stmt) = opt(s, parse_for_stmt)? { StmtKind::For(stmt) } else if let Some(stmt) = opt(s, parse_while_loop)? { StmtKind::WhileLoop(stmt) @@ -1226,11 +1226,11 @@ fn for_loop_iterable_expr(s: &mut ParserContext) -> Result { /// Grammar: /// `FOR scalarType Identifier IN (setExpression | LBRACKET rangeExpression RBRACKET | expression) body=statementOrScope`. /// Reference: . -pub fn parse_for_loop(s: &mut ParserContext) -> Result { +pub fn parse_for_stmt(s: &mut ParserContext) -> Result { let lo = s.peek().span.lo; token(s, TokenKind::Keyword(Keyword::For))?; let ty = scalar_type(s)?; - let identifier = Identifier::Ident(Box::new(prim::ident(s)?)); + let ident = prim::ident(s)?; token(s, TokenKind::Keyword(Keyword::In))?; let set_declaration = Box::new(for_loop_iterable_expr(s)?); let block = parse_block_or_stmt(s)?; @@ -1238,7 +1238,7 @@ pub fn parse_for_loop(s: &mut ParserContext) -> Result { Ok(ForStmt { span: s.span(lo), ty, - identifier, + ident, set_declaration, body: block, }) diff --git a/compiler/qsc_qasm3/src/parser/stmt/tests/for_loops.rs b/compiler/qsc_qasm3/src/parser/stmt/tests/for_loops.rs index 0f5822a6e0..c8d5517bcc 100644 --- a/compiler/qsc_qasm3/src/parser/stmt/tests/for_loops.rs +++ b/compiler/qsc_qasm3/src/parser/stmt/tests/for_loops.rs @@ -5,7 +5,7 @@ use crate::parser::{stmt::parse, tests::check}; use expect_test::expect; #[test] -fn simple_for_loop() { +fn simple_for_stmt() { check( parse, " @@ -38,7 +38,7 @@ fn simple_for_loop() { } #[test] -fn empty_for_loop() { +fn empty_for_stmt_body() { check( parse, "for int x in {} {}", @@ -58,7 +58,7 @@ fn empty_for_loop() { } #[test] -fn simple_for_loop_stmt_body() { +fn simple_for_stmt_stmt_body() { check( parse, " @@ -88,7 +88,7 @@ fn simple_for_loop_stmt_body() { } #[test] -fn for_loop_range() { +fn for_stmt_iterating_over_range() { check( parse, " @@ -120,7 +120,7 @@ fn for_loop_range() { } #[test] -fn for_loop_range_no_step() { +fn for_stmt_iterating_over_range_no_step() { check( parse, " @@ -152,7 +152,7 @@ fn for_loop_range_no_step() { } #[test] -fn for_loop_expr() { +fn for_stmt_iterating_over_expr() { check( parse, " @@ -181,7 +181,7 @@ fn for_loop_expr() { } #[test] -fn for_loop_with_continue_stmt() { +fn for_stmt_with_continue_stmt() { check( parse, " @@ -351,3 +351,27 @@ fn nested_single_stmt_for_stmt() { indices: "#]], ); } + +#[test] +fn for_stmt_with_indented_identifier_errors() { + check( + parse, + "for int x[2] in {} {}", + &expect![[r#" + Error( + Token( + Keyword( + In, + ), + Open( + Bracket, + ), + Span { + lo: 9, + hi: 10, + }, + ), + ) + "#]], + ); +} diff --git a/compiler/qsc_qasm3/src/semantic/ast.rs b/compiler/qsc_qasm3/src/semantic/ast.rs index ecb4d15e2f..9e004b5715 100644 --- a/compiler/qsc_qasm3/src/semantic/ast.rs +++ b/compiler/qsc_qasm3/src/semantic/ast.rs @@ -433,16 +433,16 @@ impl Display for DefCalStmt { pub struct IfStmt { pub span: Span, pub condition: Expr, - pub if_block: List, - pub else_block: Option>, + pub if_body: Stmt, + pub else_body: Option, } impl Display for IfStmt { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { writeln_header(f, "IfStmt", self.span)?; writeln_field(f, "condition", &self.condition)?; - writeln_list_field(f, "if_block", &self.if_block)?; - write_opt_list_field(f, "else_block", self.else_block.as_ref()) + writeln_field(f, "if_body", &self.if_body)?; + write_opt_field(f, "else_body", self.else_body.as_ref()) } } @@ -1316,33 +1316,31 @@ impl Display for ReturnStmt { pub struct WhileLoop { pub span: Span, pub while_condition: Expr, - pub block: List, + pub body: Stmt, } impl Display for WhileLoop { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { writeln_header(f, "WhileLoop", self.span)?; writeln_field(f, "condition", &self.while_condition)?; - write_list_field(f, "block", &self.block) + write_field(f, "body", &self.body) } } #[derive(Clone, Debug)] pub struct ForStmt { pub span: Span, - pub ty: ScalarType, - pub identifier: Identifier, + pub loop_variable: SymbolId, pub set_declaration: Box, - pub block: List, + pub body: Stmt, } impl Display for ForStmt { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { writeln_header(f, "ForStmt", self.span)?; - writeln_field(f, "variable_type", &self.ty)?; - writeln_field(f, "variable_name", &self.identifier)?; + writeln_field(f, "loop_variable", &self.loop_variable)?; writeln_field(f, "iterable", &self.set_declaration)?; - write_list_field(f, "block", &self.block) + write_field(f, "body", &self.body) } } @@ -1599,13 +1597,43 @@ impl Display for LiteralKind { } } -#[derive(Debug, Clone, Copy, PartialEq)] +#[derive(Debug, Clone, Copy)] pub struct Version { pub major: u32, pub minor: Option, pub span: Span, } +impl PartialEq for Version { + fn eq(&self, other: &Self) -> bool { + // If the minor versions are missing + // we assume them to be 0. + let self_minor = self.minor.unwrap_or_default(); + let other_minor = other.minor.unwrap_or_default(); + + // Then we check if the major and minor version are equal. + self.major == other.major && self_minor == other_minor + } +} + +impl PartialOrd for Version { + fn partial_cmp(&self, other: &Self) -> Option { + // If the minor versions are missing + // we assume them to be 0. + let self_minor = self.minor.unwrap_or_default(); + let other_minor = other.minor.unwrap_or_default(); + + // We compare the major versions. + match self.major.partial_cmp(&other.major) { + // If they are equal, we disambiguate + // using the minor versions. + Some(core::cmp::Ordering::Equal) => self_minor.partial_cmp(&other_minor), + // Else, we return their ordering. + ord => ord, + } + } +} + impl fmt::Display for Version { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self.minor { diff --git a/compiler/qsc_qasm3/src/semantic/error.rs b/compiler/qsc_qasm3/src/semantic/error.rs index 68bb7d7721..b520e6cfe4 100644 --- a/compiler/qsc_qasm3/src/semantic/error.rs +++ b/compiler/qsc_qasm3/src/semantic/error.rs @@ -132,6 +132,9 @@ pub enum SemanticErrorKind { #[error("{0} are not supported.")] #[diagnostic(code("Qsc.Qasm3.Compile.NotSupported"))] NotSupported(String, #[label] Span), + #[error("{0} were introduced in version {1}")] + #[diagnostic(code("Qsc.Qasm3.Compile.NotSupportedInThisVersion"))] + NotSupportedInThisVersion(String, String, #[label] Span), #[error("The operator {0} is not valid with lhs {1} and rhs {2}.")] #[diagnostic(code("Qsc.Qasm3.Compile.OperatorNotSupportedForTypes"))] OperatorNotSupportedForTypes(String, String, String, #[label] Span), @@ -286,6 +289,9 @@ impl SemanticErrorKind { } Self::NegativeControlCount(span) => Self::NegativeControlCount(span + offset), Self::NotSupported(name, span) => Self::NotSupported(name, span + offset), + Self::NotSupportedInThisVersion(name, version, span) => { + Self::NotSupportedInThisVersion(name, version, span + offset) + } Self::OperatorNotSupportedForTypes(op, lhs, rhs, span) => { Self::OperatorNotSupportedForTypes(op, lhs, rhs, span + offset) } diff --git a/compiler/qsc_qasm3/src/semantic/lowerer.rs b/compiler/qsc_qasm3/src/semantic/lowerer.rs index 679700fef1..51fff75839 100644 --- a/compiler/qsc_qasm3/src/semantic/lowerer.rs +++ b/compiler/qsc_qasm3/src/semantic/lowerer.rs @@ -3,6 +3,7 @@ use std::ops::ShlAssign; +use super::symbols::ScopeKind; use super::types::binop_requires_int_conversion_for_type; use super::types::binop_requires_symmetric_int_conversion; use super::types::is_complex_binop_supported; @@ -21,11 +22,13 @@ use qsc_frontend::{compile::SourceMap, error::WithSource}; use super::symbols::{IOKind, Symbol, SymbolTable}; use crate::oqasm_helpers::safe_i64_to_f64; +use crate::parser::ast::list_from_iter; use crate::parser::QasmSource; use crate::semantic::types::can_cast_literal; use crate::semantic::types::can_cast_literal_with_value_knowledge; use crate::semantic::types::ArrayDimensions; +use super::ast as semantic; use crate::parser::ast as syntax; use super::{ @@ -33,6 +36,55 @@ use super::{ SemanticErrorKind, }; +/// This macro allow us to apply the `?` to the inner option +/// in a situation where we have two nested options. This +/// situation is common when lowering items that were originally +/// optional. +/// +/// Example usage: +/// ```ignore +/// let item: Option = ...; +/// let item: Option> = item.as_ref().map(|s| self.lower_stmt(s)); +/// +/// // lower some other items in between +/// // ... +/// +/// // We short-circuit after lowering all the items to +/// // catch as many errors as possible before returning. +/// +/// // Note that here we are applying the `?` operator to +/// // the inner option, and not to the outer one. That is, +/// // because the outer option being `None` is not necessarily +/// // an error, e.g.: the else-body of an if_stmt. +/// // But the inner option being `None` is always an error, +/// // which occured during lowering. +/// let item: Option = short_circuit_opt_item!(item); +/// ``` +macro_rules! short_circuit_opt_item { + ($nested_opt:expr) => { + if let Some(inner_opt) = $nested_opt { + Some(inner_opt?) + } else { + None + } + }; +} + +/// This helper function evaluates the contents of `f` within a scope +/// of kind `kind`. It's purpose is to avoid making the mistake of +/// returning early from a function while a scope is pushed, for example, +/// by using the `?` operator. +#[must_use] +fn with_scope(lw: &mut Lowerer, kind: ScopeKind, f: F) -> Option +where + F: FnOnce(&mut Lowerer) -> Option, +{ + lw.symbols.push_scope(kind); + let res = f(lw); + lw.symbols.pop_scope(); + res +} + pub(super) struct Lowerer { /// The root QASM source to compile. pub source: QasmSource, @@ -70,7 +122,7 @@ impl Lowerer { self.lower_source(source); - let program = super::ast::Program { + let program = semantic::Program { version: self.version, statements: syntax::list_from_iter(self.stmts), }; @@ -150,83 +202,73 @@ impl Lowerer { } #[allow(clippy::too_many_lines)] - fn lower_stmt(&mut self, stmt: &syntax::Stmt) -> Option { + fn lower_stmt(&mut self, stmt: &syntax::Stmt) -> Option { let kind = match &*stmt.kind { - syntax::StmtKind::Alias(stmt) => super::ast::StmtKind::Alias(self.lower_alias(stmt)?), + syntax::StmtKind::Alias(stmt) => semantic::StmtKind::Alias(self.lower_alias(stmt)?), syntax::StmtKind::Assign(stmt) => self.lower_assign(stmt)?, syntax::StmtKind::AssignOp(stmt) => { - super::ast::StmtKind::AssignOp(self.lower_assign_op(stmt)?) + semantic::StmtKind::AssignOp(self.lower_assign_op(stmt)?) } syntax::StmtKind::Barrier(stmt) => { - super::ast::StmtKind::Barrier(self.lower_barrier(stmt)?) + semantic::StmtKind::Barrier(self.lower_barrier(stmt)?) } - syntax::StmtKind::Box(stmt) => super::ast::StmtKind::Box(self.lower_box(stmt)?), + syntax::StmtKind::Box(stmt) => semantic::StmtKind::Box(self.lower_box(stmt)?), syntax::StmtKind::Break(stmt) => self.lower_break(stmt)?, syntax::StmtKind::Block(stmt) => { - super::ast::StmtKind::Block(Box::new(self.lower_block(stmt)?)) + semantic::StmtKind::Block(Box::new(self.lower_block(stmt)?)) } syntax::StmtKind::Cal(stmt) => self.lower_calibration(stmt)?, syntax::StmtKind::CalibrationGrammar(stmt) => { - super::ast::StmtKind::CalibrationGrammar(self.lower_calibration_grammar(stmt)?) + semantic::StmtKind::CalibrationGrammar(self.lower_calibration_grammar(stmt)?) } syntax::StmtKind::ClassicalDecl(stmt) => { - super::ast::StmtKind::ClassicalDecl(self.lower_classical_decl(stmt)?) + semantic::StmtKind::ClassicalDecl(self.lower_classical_decl(stmt)?) } syntax::StmtKind::ConstDecl(stmt) => { - super::ast::StmtKind::ClassicalDecl(self.lower_const_decl(stmt)?) + semantic::StmtKind::ClassicalDecl(self.lower_const_decl(stmt)?) } syntax::StmtKind::Continue(stmt) => self.lower_continue_stmt(stmt)?, - syntax::StmtKind::Def(stmt) => super::ast::StmtKind::Def(self.lower_def(stmt)?), - syntax::StmtKind::DefCal(stmt) => { - super::ast::StmtKind::DefCal(self.lower_def_cal(stmt)?) - } - syntax::StmtKind::Delay(stmt) => super::ast::StmtKind::Delay(self.lower_delay(stmt)?), + syntax::StmtKind::Def(stmt) => semantic::StmtKind::Def(self.lower_def(stmt)?), + syntax::StmtKind::DefCal(stmt) => semantic::StmtKind::DefCal(self.lower_def_cal(stmt)?), + syntax::StmtKind::Delay(stmt) => semantic::StmtKind::Delay(self.lower_delay(stmt)?), syntax::StmtKind::Empty => { // we ignore empty statements None? } - syntax::StmtKind::End(stmt) => super::ast::StmtKind::End(self.lower_end_stmt(stmt)?), + syntax::StmtKind::End(stmt) => semantic::StmtKind::End(self.lower_end_stmt(stmt)?), syntax::StmtKind::ExprStmt(stmt) => { - super::ast::StmtKind::ExprStmt(self.lower_expr_stmt(stmt)?) + semantic::StmtKind::ExprStmt(self.lower_expr_stmt(stmt)?) } syntax::StmtKind::ExternDecl(extern_decl) => { - super::ast::StmtKind::ExternDecl(self.lower_extern(extern_decl)?) + semantic::StmtKind::ExternDecl(self.lower_extern(extern_decl)?) } - syntax::StmtKind::For(stmt) => super::ast::StmtKind::For(self.lower_for_stmt(stmt)?), - syntax::StmtKind::If(stmt) => super::ast::StmtKind::If(self.lower_if_stmt(stmt)?), + syntax::StmtKind::For(stmt) => semantic::StmtKind::For(self.lower_for_stmt(stmt)?), + syntax::StmtKind::If(stmt) => semantic::StmtKind::If(self.lower_if_stmt(stmt)?), syntax::StmtKind::GateCall(stmt) => { - super::ast::StmtKind::GateCall(self.lower_gate_call(stmt)?) - } - syntax::StmtKind::GPhase(stmt) => { - super::ast::StmtKind::GPhase(self.lower_gphase(stmt)?) + semantic::StmtKind::GateCall(self.lower_gate_call(stmt)?) } + syntax::StmtKind::GPhase(stmt) => semantic::StmtKind::GPhase(self.lower_gphase(stmt)?), syntax::StmtKind::Include(stmt) => { - super::ast::StmtKind::Include(self.lower_include(stmt)?) + semantic::StmtKind::Include(self.lower_include(stmt)?) } syntax::StmtKind::IODeclaration(stmt) => { - super::ast::StmtKind::IODeclaration(self.lower_io_decl(stmt)?) + semantic::StmtKind::IODeclaration(self.lower_io_decl(stmt)?) } syntax::StmtKind::Measure(stmt) => { - super::ast::StmtKind::Measure(self.lower_measure(stmt)?) - } - syntax::StmtKind::Pragma(stmt) => { - super::ast::StmtKind::Pragma(self.lower_pragma(stmt)?) + semantic::StmtKind::Measure(self.lower_measure(stmt)?) } + syntax::StmtKind::Pragma(stmt) => semantic::StmtKind::Pragma(self.lower_pragma(stmt)?), syntax::StmtKind::QuantumGateDefinition(stmt) => { - super::ast::StmtKind::QuantumGateDefinition(self.lower_gate_def(stmt)?) + semantic::StmtKind::QuantumGateDefinition(self.lower_gate_def(stmt)?) } syntax::StmtKind::QuantumDecl(stmt) => { - super::ast::StmtKind::QuantumDecl(self.lower_quantum_decl(stmt)?) - } - syntax::StmtKind::Reset(stmt) => super::ast::StmtKind::Reset(self.lower_reset(stmt)?), - syntax::StmtKind::Return(stmt) => { - super::ast::StmtKind::Return(self.lower_return(stmt)?) - } - syntax::StmtKind::Switch(stmt) => { - super::ast::StmtKind::Switch(self.lower_switch(stmt)?) + semantic::StmtKind::QuantumDecl(self.lower_quantum_decl(stmt)?) } + syntax::StmtKind::Reset(stmt) => semantic::StmtKind::Reset(self.lower_reset(stmt)?), + syntax::StmtKind::Return(stmt) => semantic::StmtKind::Return(self.lower_return(stmt)?), + syntax::StmtKind::Switch(stmt) => semantic::StmtKind::Switch(self.lower_switch(stmt)?), syntax::StmtKind::WhileLoop(stmt) => { - super::ast::StmtKind::WhileLoop(self.lower_while_loop(stmt)?) + semantic::StmtKind::WhileLoop(self.lower_while_stmt(stmt)?) } syntax::StmtKind::Err => { self.push_semantic_error(SemanticErrorKind::UnexpectedParserError( @@ -237,7 +279,7 @@ impl Lowerer { } }; let annotations = self.lower_annotations(&stmt.annotations, &stmt.kind); - Some(super::ast::Stmt { + Some(semantic::Stmt { span: stmt.span, annotations: syntax::list_from_iter(annotations), kind: Box::new(kind), @@ -304,6 +346,18 @@ impl Lowerer { self.push_semantic_error(kind); } + pub fn push_unsuported_in_this_version_error_message>( + &mut self, + message: S, + minimum_supported_version: &Version, + span: Span, + ) { + let message = message.as_ref().to_string(); + let msv = minimum_supported_version.to_string(); + let kind = SemanticErrorKind::NotSupportedInThisVersion(message, msv, span); + self.push_semantic_error(kind); + } + /// Pushes an unimplemented error with the supplied message. pub fn push_unimplemented_error_message>(&mut self, message: S, span: Span) { let kind = SemanticErrorKind::Unimplemented(message.as_ref().to_string(), span); @@ -323,7 +377,7 @@ impl Lowerer { WithSource::from_map(&self.source_map, error) } - fn lower_alias(&mut self, alias: &syntax::AliasDeclStmt) -> Option { + fn lower_alias(&mut self, alias: &syntax::AliasDeclStmt) -> Option { let name = get_identifier_name(&alias.ident); // alias statements do their types backwards, you read the right side // and assign it to the left side. @@ -365,20 +419,22 @@ impl Lowerer { // we failed return None; } - Some(super::ast::AliasDeclStmt { + Some(semantic::AliasDeclStmt { span: alias.span, symbol_id, exprs: syntax::list_from_iter(rhs), }) } - fn lower_assign(&mut self, stmt: &syntax::AssignStmt) -> Option { + fn lower_assign(&mut self, stmt: &syntax::AssignStmt) -> Option { if stmt.lhs.indices.is_empty() { - Some(super::ast::StmtKind::Assign( - self.lower_simple_assign_expr(&stmt.lhs.name, &stmt.rhs, stmt.span)?, - )) + Some(semantic::StmtKind::Assign(self.lower_simple_assign_expr( + &stmt.lhs.name, + &stmt.rhs, + stmt.span, + )?)) } else { - Some(super::ast::StmtKind::IndexedAssign( + Some(semantic::StmtKind::IndexedAssign( self.lower_indexed_assign_expr(&stmt.lhs, &stmt.rhs, stmt.span)?, )) } @@ -389,20 +445,19 @@ impl Lowerer { ident: &syntax::Ident, rhs: &syntax::Expr, span: Span, - ) -> Option { + ) -> Option { let (lhs_symbol_id, lhs_symbol) = self.symbols.get_symbol_by_name(&ident.name)?; let ty = lhs_symbol.ty.clone(); + let rhs = self.lower_expr_with_target_type(Some(rhs), &ty, span); if ty.is_const() { let kind = SemanticErrorKind::CannotUpdateConstVariable(ident.name.to_string(), ident.span); self.push_semantic_error(kind); - // usually we'd return None here, but we'll continue to compile the rhs - // looking for more errors. There is nothing in this type of error that - // would prevent us from compiling the rhs. + return None; } + let rhs = rhs?; - let rhs = self.lower_expr_with_target_type(Some(rhs), &ty, span)?; - Some(super::ast::AssignStmt { + Some(semantic::AssignStmt { symbold_id: lhs_symbol_id, rhs, span, @@ -414,25 +469,23 @@ impl Lowerer { index_expr: &syntax::IndexedIdent, rhs: &syntax::Expr, span: Span, - ) -> Option { + ) -> Option { let ident = index_expr.name.clone(); let (lhs_symbol_id, lhs_symbol) = self.symbols.get_symbol_by_name(&ident.name)?; let ty = lhs_symbol.ty.clone(); + let lhs = self.lower_indexed_ident_expr(index_expr); + let rhs = self.lower_expr_with_target_type(Some(rhs), &ty, span); + if ty.is_const() { let kind = SemanticErrorKind::CannotUpdateConstVariable(ident.name.to_string(), ident.span); self.push_semantic_error(kind); - // usually we'd return None here, but we'll continue to compile the rhs - // looking for more errors. There is nothing in this type of error that - // would prevent us from compiling the rhs. return None; } - let lhs = self.lower_indexed_ident_expr(index_expr); - let rhs = self.lower_expr_with_target_type(Some(rhs), &ty, span); let (lhs, rhs) = (lhs?, rhs?); - Some(super::ast::IndexedAssignStmt { + Some(semantic::IndexedAssignStmt { symbold_id: lhs_symbol_id, lhs, rhs, @@ -440,15 +493,14 @@ impl Lowerer { }) } - fn lower_assign_op(&mut self, stmt: &syntax::AssignOpStmt) -> Option { + fn lower_assign_op(&mut self, stmt: &syntax::AssignOpStmt) -> Option { let op = stmt.op; let lhs = &stmt.lhs; let rhs = &stmt.rhs; - let ident = lhs.name.clone(); - let (lhs_symbol_id, lhs_symbol) = self.symbols.get_symbol_by_name(&ident.name)?; let ty = lhs_symbol.ty.clone(); + if ty.is_const() { let kind = SemanticErrorKind::CannotUpdateConstVariable(ident.name.to_string(), ident.span); @@ -465,7 +517,7 @@ impl Lowerer { let (lhs, rhs) = (lhs?, rhs?); let rhs = self.lower_binary_op_expr(op, lhs.clone(), rhs, stmt.span)?; let rhs = self.cast_expr_to_type(&ty, &rhs, stmt.span)?; - Some(super::ast::AssignOpStmt { + Some(semantic::AssignOpStmt { span: stmt.span, symbold_id: lhs_symbol_id, lhs, @@ -473,7 +525,7 @@ impl Lowerer { }) } - fn lower_expr(&mut self, expr: &syntax::Expr) -> Option { + fn lower_expr(&mut self, expr: &syntax::Expr) -> Option { match &*expr.kind { syntax::ExprKind::BinaryOp(bin_op_expr) => { let lhs = self.lower_expr(&bin_op_expr.lhs); @@ -504,46 +556,46 @@ impl Lowerer { } } - fn lower_ident_expr(&mut self, ident: &syntax::Ident) -> Option { + fn lower_ident_expr(&mut self, ident: &syntax::Ident) -> Option { let name = ident.name.clone(); let Some((symbol_id, symbol)) = self.symbols.get_symbol_by_name(&name) else { self.push_missing_symbol_error(&name, ident.span); return None; }; - let kind = super::ast::ExprKind::Ident(symbol_id); - Some(super::ast::Expr { + let kind = semantic::ExprKind::Ident(symbol_id); + Some(semantic::Expr { span: ident.span, kind: Box::new(kind), ty: symbol.ty.clone(), }) } - fn lower_lit_expr(&mut self, expr: &syntax::Lit) -> Option { + fn lower_lit_expr(&mut self, expr: &syntax::Lit) -> Option { let (kind, ty) = match &expr.kind { syntax::LiteralKind::BigInt(value) => { // todo: this case is only valid when there is an integer literal // that requires more than 64 bits to represent. We should probably // introduce a new type for this as openqasm promotion rules don't // cover this case as far as I know. - (super::ast::LiteralKind::BigInt(value.clone()), Type::Err) + (semantic::LiteralKind::BigInt(value.clone()), Type::Err) } syntax::LiteralKind::Bitstring(value, size) => ( - super::ast::LiteralKind::Bitstring(value.clone(), *size), + semantic::LiteralKind::Bitstring(value.clone(), *size), Type::BitArray(super::types::ArrayDimensions::One(*size), true), ), syntax::LiteralKind::Bool(value) => { - (super::ast::LiteralKind::Bool(*value), Type::Bool(true)) + (semantic::LiteralKind::Bool(*value), Type::Bool(true)) } syntax::LiteralKind::Int(value) => { - (super::ast::LiteralKind::Int(*value), Type::Int(None, true)) + (semantic::LiteralKind::Int(*value), Type::Int(None, true)) } syntax::LiteralKind::Float(value) => ( - super::ast::LiteralKind::Float(*value), + semantic::LiteralKind::Float(*value), Type::Float(None, true), ), syntax::LiteralKind::Imaginary(value) => ( - super::ast::LiteralKind::Complex(0.0, *value), + semantic::LiteralKind::Complex(0.0, *value), Type::Complex(None, true), ), syntax::LiteralKind::String(_) => { @@ -573,31 +625,31 @@ impl Lowerer { } ( - super::ast::LiteralKind::Array(syntax::list_from_iter(texprs)), + semantic::LiteralKind::Array(syntax::list_from_iter(texprs)), Type::Err, ) } }; - Some(super::ast::Expr { + Some(semantic::Expr { span: expr.span, - kind: Box::new(super::ast::ExprKind::Lit(kind)), + kind: Box::new(semantic::ExprKind::Lit(kind)), ty, }) } - fn lower_paren_expr(&mut self, expr: &syntax::Expr) -> Option { + fn lower_paren_expr(&mut self, expr: &syntax::Expr) -> Option { let expr = self.lower_expr(expr)?; let span = expr.span; let ty = expr.ty.clone(); - let kind = super::ast::ExprKind::Paren(expr); - Some(super::ast::Expr { + let kind = semantic::ExprKind::Paren(expr); + Some(semantic::Expr { span, kind: Box::new(kind), ty, }) } - fn lower_unary_op_expr(&mut self, expr: &syntax::UnaryOpExpr) -> Option { + fn lower_unary_op_expr(&mut self, expr: &syntax::UnaryOpExpr) -> Option { match expr.op { syntax::UnaryOp::Neg => { if let syntax::ExprKind::Lit(lit) = expr.expr.kind.as_ref() { @@ -607,13 +659,13 @@ impl Lowerer { let ty = expr.ty.clone(); if unary_op_can_be_applied_to_type(syntax::UnaryOp::Neg, &ty) { let span = expr.span; - let unary = super::ast::UnaryOpExpr { - op: super::ast::UnaryOp::Neg, + let unary = semantic::UnaryOpExpr { + op: semantic::UnaryOp::Neg, expr, }; - Some(super::ast::Expr { + Some(semantic::Expr { span, - kind: Box::new(super::ast::ExprKind::UnaryOp(unary)), + kind: Box::new(semantic::ExprKind::UnaryOp(unary)), ty, }) } else { @@ -631,13 +683,13 @@ impl Lowerer { let ty = expr.ty.clone(); if unary_op_can_be_applied_to_type(syntax::UnaryOp::NotB, &ty) { let span = expr.span; - let unary = super::ast::UnaryOpExpr { - op: super::ast::UnaryOp::NotB, + let unary = semantic::UnaryOpExpr { + op: semantic::UnaryOp::NotB, expr, }; - Some(super::ast::Expr { + Some(semantic::Expr { span, - kind: Box::new(super::ast::ExprKind::UnaryOp(unary)), + kind: Box::new(semantic::ExprKind::UnaryOp(unary)), ty, }) } else { @@ -659,10 +711,10 @@ impl Lowerer { let ty = expr.ty.clone(); - Some(super::ast::Expr { + Some(semantic::Expr { span: expr.span, - kind: Box::new(super::ast::ExprKind::UnaryOp(super::ast::UnaryOpExpr { - op: super::ast::UnaryOp::NotL, + kind: Box::new(semantic::ExprKind::UnaryOp(semantic::UnaryOpExpr { + op: semantic::UnaryOp::NotL, expr, })), ty, @@ -675,7 +727,7 @@ impl Lowerer { &mut self, annotations: &[Box], kind: &syntax::StmtKind, - ) -> Vec { + ) -> Vec { annotations .iter() .map(|annotation| self.lower_annotation(annotation, kind)) @@ -686,7 +738,7 @@ impl Lowerer { &mut self, annotation: &syntax::Annotation, kind: &syntax::StmtKind, - ) -> super::ast::Annotation { + ) -> semantic::Annotation { if !matches!( annotation.identifier.to_string().as_str(), "SimulatableIntrinsic" | "Config" @@ -707,7 +759,7 @@ impl Lowerer { ); } - super::ast::Annotation { + semantic::Annotation { span: annotation.span, identifier: annotation.identifier.clone(), value: annotation.value.as_ref().map(Clone::clone), @@ -783,7 +835,7 @@ impl Lowerer { } } - fn lower_barrier(&mut self, stmt: &syntax::BarrierStmt) -> Option { + fn lower_barrier(&mut self, stmt: &syntax::BarrierStmt) -> Option { self.push_unimplemented_error_message("barrier stmt", stmt.span); None } @@ -792,7 +844,7 @@ impl Lowerer { /// . /// Search for the definition of `Box` there, and then for all the classes /// inhereting from `QuantumStatement`. - fn lower_box(&mut self, stmt: &syntax::BoxStmt) -> Option { + fn lower_box(&mut self, stmt: &syntax::BoxStmt) -> Option { let stmts = stmt .body .iter() @@ -832,20 +884,28 @@ impl Lowerer { None } - fn lower_break(&mut self, stmt: &syntax::BreakStmt) -> Option { + fn lower_break(&mut self, stmt: &syntax::BreakStmt) -> Option { self.push_unimplemented_error_message("break stmt", stmt.span); None } - fn lower_block(&mut self, stmt: &syntax::Block) -> Option { - self.push_unimplemented_error_message("block stmt", stmt.span); - None + fn lower_block(&mut self, stmt: &syntax::Block) -> Option { + self.symbols.push_scope(ScopeKind::Block); + let stmts = stmt.stmts.iter().filter_map(|stmt| self.lower_stmt(stmt)); + let stmts = list_from_iter(stmts); + self.symbols.pop_scope(); + + if stmts.len() != stmt.stmts.len() { + return None; + } + + Some(semantic::Block { + span: stmt.span, + stmts, + }) } - fn lower_calibration( - &mut self, - stmt: &syntax::CalibrationStmt, - ) -> Option { + fn lower_calibration(&mut self, stmt: &syntax::CalibrationStmt) -> Option { self.push_unimplemented_error_message("calibration stmt", stmt.span); None } @@ -853,7 +913,7 @@ impl Lowerer { fn lower_calibration_grammar( &mut self, stmt: &syntax::CalibrationGrammarStmt, - ) -> Option { + ) -> Option { self.push_unimplemented_error_message("calibration stmt", stmt.span); None } @@ -861,7 +921,7 @@ impl Lowerer { fn lower_classical_decl( &mut self, stmt: &syntax::ClassicalDeclarationStmt, - ) -> Option { + ) -> Option { let is_const = false; // const decls are handled separately let ty = self.get_semantic_type_from_tydef(&stmt.ty, is_const)?; @@ -883,14 +943,14 @@ impl Lowerer { Some(expr) => match expr { syntax::ValueExpression::Expr(expr) => self .lower_expr_with_target_type(Some(expr), &ty, stmt_span) - .map(super::ast::ValueExpression::Expr), + .map(semantic::ValueExpression::Expr), syntax::ValueExpression::Measurement(measure_expr) => self .lower_measure_expr_with_target_type(measure_expr, &ty, stmt_span) - .map(super::ast::ValueExpression::Measurement), + .map(semantic::ValueExpression::Measurement), }, None => self .lower_expr_with_target_type(None, &ty, stmt_span) - .map(super::ast::ValueExpression::Expr), + .map(semantic::ValueExpression::Expr), }; let Ok(symbol_id) = self.symbols.insert_symbol(symbol) else { @@ -902,7 +962,7 @@ impl Lowerer { // for classical declarations. So if None is returned we hit an error with the expression. let init_expr = init_expr?; - Some(super::ast::ClassicalDeclarationStmt { + Some(semantic::ClassicalDeclarationStmt { span: stmt_span, ty_span, symbol_id, @@ -913,7 +973,7 @@ impl Lowerer { fn lower_const_decl( &mut self, stmt: &syntax::ConstantDeclStmt, - ) -> Option { + ) -> Option { let is_const = true; let ty = self.get_semantic_type_from_tydef(&stmt.ty, is_const)?; let ty_span = stmt.ty.span(); @@ -939,68 +999,120 @@ impl Lowerer { // for classical declarations. So if None is returned we hit an error with the expression. let init_expr = init_expr?; - Some(super::ast::ClassicalDeclarationStmt { + Some(semantic::ClassicalDeclarationStmt { span: stmt.span, ty_span, symbol_id, - init_expr: Box::new(super::ast::ValueExpression::Expr(init_expr)), + init_expr: Box::new(semantic::ValueExpression::Expr(init_expr)), }) } - fn lower_continue_stmt(&mut self, stmt: &syntax::ContinueStmt) -> Option { + fn lower_continue_stmt(&mut self, stmt: &syntax::ContinueStmt) -> Option { self.push_unimplemented_error_message("continue stmt", stmt.span); None } - fn lower_def(&mut self, stmt: &syntax::DefStmt) -> Option { + fn lower_def(&mut self, stmt: &syntax::DefStmt) -> Option { self.push_unimplemented_error_message("def stmt", stmt.span); None } - fn lower_def_cal(&mut self, stmt: &syntax::DefCalStmt) -> Option { + fn lower_def_cal(&mut self, stmt: &syntax::DefCalStmt) -> Option { self.push_unimplemented_error_message("def cal stmt", stmt.span); None } - fn lower_delay(&mut self, stmt: &syntax::DelayStmt) -> Option { + fn lower_delay(&mut self, stmt: &syntax::DelayStmt) -> Option { self.push_unimplemented_error_message("delay stmt", stmt.span); None } - fn lower_end_stmt(&mut self, stmt: &syntax::EndStmt) -> Option { + fn lower_end_stmt(&mut self, stmt: &syntax::EndStmt) -> Option { self.push_unimplemented_error_message("end stmt", stmt.span); None } - fn lower_expr_stmt(&mut self, stmt: &syntax::ExprStmt) -> Option { + fn lower_expr_stmt(&mut self, stmt: &syntax::ExprStmt) -> Option { let expr = self.lower_expr(&stmt.expr)?; - Some(super::ast::ExprStmt { + Some(semantic::ExprStmt { span: stmt.span, expr, }) } - fn lower_extern(&mut self, stmt: &syntax::ExternDecl) -> Option { + fn lower_extern(&mut self, stmt: &syntax::ExternDecl) -> Option { self.push_unimplemented_error_message("extern stmt", stmt.span); None } - fn lower_for_stmt(&mut self, stmt: &syntax::ForStmt) -> Option { - self.push_unimplemented_error_message("for stmt", stmt.span); - None + fn lower_for_stmt(&mut self, stmt: &syntax::ForStmt) -> Option { + let set_declaration = self.lower_enumerable_set(&stmt.set_declaration); + + // Push scope where the loop variable lives. + let (loop_variable, body) = with_scope(self, ScopeKind::Block, |lw| { + let ty = lw.get_semantic_type_from_scalar_ty(&stmt.ty, false)?; + let qsharp_ty = lw.convert_semantic_type_to_qsharp_type(&ty.clone(), stmt.ty.span)?; + let symbol = Symbol { + name: stmt.ident.name.to_string(), + span: stmt.ident.span, + ty: ty.clone(), + qsharp_ty, + io_kind: IOKind::Default, + }; + + // This is the first variable in this scope, so + // we don't need to check for redefined symbols. + let symbol_id = lw + .symbols + .insert_symbol(symbol) + .expect("this should be the first variable in this scope"); + + // We lower the body after registering the loop variable symbol_id. + // The body of the for loop could be a single statement redefining + // the loop variable, in which case we need to push a redefined + // symbol error. + let body = lw.lower_stmt(&stmt.body); + + Some((symbol_id, body?)) + })?; + + // We use the `?` operator after lowering all the fields + // to report as many errors as possible before exiting the function. + Some(semantic::ForStmt { + span: stmt.span, + loop_variable, + set_declaration: Box::new(set_declaration?), + body, + }) } - fn lower_if_stmt(&mut self, stmt: &syntax::IfStmt) -> Option { - self.push_unimplemented_error_message("if stmt", stmt.span); - None + fn lower_if_stmt(&mut self, stmt: &syntax::IfStmt) -> Option { + let condition = self.lower_expr(&stmt.condition); + let if_body = self.lower_stmt(&stmt.if_body); + let else_body = stmt.else_body.as_ref().map(|body| self.lower_stmt(body)); + + // The semantics of a if statement is that the condition must be + // of type bool, so we try to cast it, inserting a cast if necessary. + let cond_ty = Type::Bool(false); + let condition = condition?; + let condition = self.cast_expr_to_type(&cond_ty, &condition, condition.span); + + // We use the `?` operator after lowering all the fields + // to report as many errors as possible before exiting the function. + Some(semantic::IfStmt { + span: stmt.span, + condition: condition?, + if_body: if_body?, + else_body: short_circuit_opt_item!(else_body), + }) } - fn lower_gate_call(&mut self, stmt: &syntax::GateCall) -> Option { + fn lower_gate_call(&mut self, stmt: &syntax::GateCall) -> Option { self.push_unimplemented_error_message("gate call stmt", stmt.span); None } - fn lower_gphase(&mut self, stmt: &syntax::GPhase) -> Option { + fn lower_gphase(&mut self, stmt: &syntax::GPhase) -> Option { self.push_unimplemented_error_message("gphase stmt", stmt.span); None } @@ -1008,7 +1120,7 @@ impl Lowerer { /// This function is always a indication of a error. Either the /// program is declaring include in a non-global scope or the /// include is not handled in `self.lower_source` properly. - fn lower_include(&mut self, stmt: &syntax::IncludeStmt) -> Option { + fn lower_include(&mut self, stmt: &syntax::IncludeStmt) -> Option { // if we are not in the root we should not be able to include if !self.symbols.is_current_scope_global() { let name = stmt.filename.to_string(); @@ -1021,7 +1133,7 @@ impl Lowerer { panic!("Include should have been handled in lower_source") } - fn lower_io_decl(&mut self, stmt: &syntax::IODeclaration) -> Option { + fn lower_io_decl(&mut self, stmt: &syntax::IODeclaration) -> Option { let is_const = false; let ty = self.get_semantic_type_from_tydef(&stmt.ty, is_const)?; let io_kind = stmt.io_identifier.into(); @@ -1029,7 +1141,7 @@ impl Lowerer { let ty_span = stmt.ty.span(); let stmt_span = stmt.span; let name = stmt.ident.name.clone(); - let qsharp_ty = self.convert_semantic_type_to_qsharp_type(&ty.clone(), ty_span)?; + let qsharp_ty = self.convert_semantic_type_to_qsharp_type(&ty, ty_span)?; let symbol = Symbol { name: name.to_string(), ty: ty.clone(), @@ -1048,7 +1160,7 @@ impl Lowerer { // to a parameter in the function signature once we generate the function if io_kind == IOKind::Output { let init_expr = self.get_default_value(&ty, stmt_span)?; - Some(super::ast::IODeclaration { + Some(semantic::IODeclaration { span: stmt_span, symbol_id, init_expr: Box::new(init_expr), @@ -1058,12 +1170,12 @@ impl Lowerer { } } - fn lower_measure(&mut self, stmt: &syntax::MeasureStmt) -> Option { + fn lower_measure(&mut self, stmt: &syntax::MeasureStmt) -> Option { self.push_unimplemented_error_message("measure stmt", stmt.span); None } - fn lower_pragma(&mut self, stmt: &syntax::Pragma) -> Option { + fn lower_pragma(&mut self, stmt: &syntax::Pragma) -> Option { self.push_unimplemented_error_message("pragma stmt", stmt.span); None } @@ -1071,7 +1183,7 @@ impl Lowerer { fn lower_gate_def( &mut self, stmt: &syntax::QuantumGateDefinition, - ) -> Option { + ) -> Option { self.push_unimplemented_error_message("gate def stmt", stmt.span); None } @@ -1079,29 +1191,133 @@ impl Lowerer { fn lower_quantum_decl( &mut self, stmt: &syntax::QubitDeclaration, - ) -> Option { + ) -> Option { self.push_unimplemented_error_message("qubit decl stmt", stmt.span); None } - fn lower_reset(&mut self, stmt: &syntax::ResetStmt) -> Option { + fn lower_reset(&mut self, stmt: &syntax::ResetStmt) -> Option { self.push_unimplemented_error_message("reset stmt", stmt.span); None } - fn lower_return(&mut self, stmt: &syntax::ReturnStmt) -> Option { + fn lower_return(&mut self, stmt: &syntax::ReturnStmt) -> Option { self.push_unimplemented_error_message("return stmt", stmt.span); None } - fn lower_switch(&mut self, stmt: &syntax::SwitchStmt) -> Option { - self.push_unimplemented_error_message("switch stmt", stmt.span); - None + fn lower_switch(&mut self, stmt: &syntax::SwitchStmt) -> Option { + // Semantics of switch case is that the outer block doesn't introduce + // a new scope but each case rhs does. + + // Can we add a new scope anyway to hold a temporary variable? + // if we do that, we can refer to a new variable instead of the control + // expr this would allow us to avoid the need to resolve the control + // expr multiple times in the case where we have to coerce the control + // expr to the correct type. Introducing a new variable without a new + // scope would effect output semantics. + let cases = stmt + .cases + .iter() + .filter_map(|case| self.lower_switch_case(case)) + .collect::>(); + let default = stmt.default.as_ref().map(|d| self.lower_block(d)); + let target = self.lower_expr(&stmt.target)?; + + // The condition for the switch statement must be an integer type + // so we use `cast_expr_to_type`, forcing the type to be an integer + // type with implicit casts if necessary. + let target_ty = Type::Int(None, false); + let target = self.cast_expr_to_type(&target_ty, &target, target.span)?; + + // We use the `?` operator after casting the condition to int + // to report as many errors as possible before exiting the function. + let default = short_circuit_opt_item!(default); + + if cases.len() != stmt.cases.len() { + return None; + } + + // It is a parse error to have a switch statement with no cases, + // even if the default block is present. Getting here means the + // parser is broken or they changed the grammar. + assert!( + !cases.is_empty(), + "switch statement must have a control expression and at least one case" + ); + + // We push a semantic error on switch statements if version is less than 3.1, + // as they were introduced in 3.1. + if let Some(ref version) = self.version { + const SWITCH_MINIMUM_SUPPORTED_VERSION: semantic::Version = semantic::Version { + major: 3, + minor: Some(1), + span: Span { lo: 0, hi: 0 }, + }; + if version < &SWITCH_MINIMUM_SUPPORTED_VERSION { + self.push_unsuported_in_this_version_error_message( + "switch statements", + &SWITCH_MINIMUM_SUPPORTED_VERSION, + stmt.span, + ); + return None; + } + } + + Some(semantic::SwitchStmt { + span: stmt.span, + target, + cases: list_from_iter(cases), + default, + }) } - fn lower_while_loop(&mut self, stmt: &syntax::WhileLoop) -> Option { - self.push_unimplemented_error_message("while loop stmt", stmt.span); - None + fn lower_switch_case( + &mut self, + switch_case: &syntax::SwitchCase, + ) -> Option { + let label_ty = Type::Int(None, false); + let labels = switch_case + .labels + .iter() + .filter_map(|label| { + // The labels for each switch case must be of integer type + // so we use `cast_expr_to_type`, forcing the type to be an integer + // type with implicit casts if necessary. + let label = self.lower_expr(label)?; + self.cast_expr_to_type(&label_ty, &label, label.span) + }) + .collect::>(); + + let block = self.lower_block(&switch_case.block)?; + + if labels.len() != switch_case.labels.len() { + return None; + } + + Some(semantic::SwitchCase { + span: switch_case.span, + labels: list_from_iter(labels), + block, + }) + } + + fn lower_while_stmt(&mut self, stmt: &syntax::WhileLoop) -> Option { + let while_condition = self.lower_expr(&stmt.while_condition); + let body = self.lower_stmt(&stmt.body); + + // The semantics of a while statement is that the condition must be + // of type bool, so we try to cast it, inserting a cast if necessary. + let cond_ty = Type::Bool(false); + let while_condition = while_condition?; + let while_condition = + self.cast_expr_to_type(&cond_ty, &while_condition, while_condition.span)?; + + Some(semantic::WhileLoop { + span: stmt.span, + while_condition, + body: body?, + }) } fn get_semantic_type_from_tydef( @@ -1241,7 +1457,7 @@ impl Lowerer { expr: Option<&syntax::Expr>, ty: &Type, span: Span, - ) -> Option { + ) -> Option { let Some(expr) = expr else { // In OpenQASM, classical variables may be uninitialized, but in Q#, // they must be initialized. We will use the default value for the type @@ -1257,7 +1473,7 @@ impl Lowerer { // if the rhs is a literal, we can try to cast it to the target type // if they share the same base type. - if let super::ast::ExprKind::Lit(lit) = &*rhs.kind { + if let semantic::ExprKind::Lit(lit) = &*rhs.kind { // if the rhs is a literal, we can try to coerce it to the lhs type // we can do better than just types given we have a literal value if can_cast_literal(ty, &rhs_ty) || can_cast_literal_with_value_knowledge(ty, lit) { @@ -1285,15 +1501,15 @@ impl Lowerer { _expr: &syntax::MeasureExpr, _ty: &Type, span: Span, - ) -> Option { + ) -> Option { self.push_unimplemented_error_message("measure expr with target type", span); None } - fn get_default_value(&mut self, ty: &Type, span: Span) -> Option { - use super::ast::Expr; - use super::ast::ExprKind; - use super::ast::LiteralKind; + fn get_default_value(&mut self, ty: &Type, span: Span) -> Option { + use semantic::Expr; + use semantic::ExprKind; + use semantic::LiteralKind; let from_lit_kind = |kind| -> Expr { Expr { span: Span::default(), @@ -1377,9 +1593,9 @@ impl Lowerer { fn coerce_literal_expr_to_type( &mut self, ty: &Type, - rhs: &super::ast::Expr, - kind: &super::ast::LiteralKind, - ) -> Option { + rhs: &semantic::Expr, + kind: &semantic::LiteralKind, + ) -> Option { if *ty == rhs.ty { // Base case, we shouldn't have gotten here // but if we did, we can just return the rhs @@ -1401,19 +1617,17 @@ impl Lowerer { let span = rhs.span; if matches!(lhs_ty, Type::Bit(..)) { - if let super::ast::LiteralKind::Int(value) = kind { + if let semantic::LiteralKind::Int(value) = kind { // can_cast_literal_with_value_knowledge guarantees that value is 0 or 1 - return Some(super::ast::Expr { + return Some(semantic::Expr { span, - kind: Box::new(super::ast::ExprKind::Lit(super::ast::LiteralKind::Int( - *value, - ))), + kind: Box::new(semantic::ExprKind::Lit(semantic::LiteralKind::Int(*value))), ty: lhs_ty.as_const(), }); - } else if let super::ast::LiteralKind::Bool(value) = kind { - return Some(super::ast::Expr { + } else if let semantic::LiteralKind::Bool(value) = kind { + return Some(semantic::Expr { span, - kind: Box::new(super::ast::ExprKind::Lit(super::ast::LiteralKind::Int( + kind: Box::new(semantic::ExprKind::Lit(semantic::LiteralKind::Int( i64::from(*value), ))), ty: lhs_ty.as_const(), @@ -1435,7 +1649,7 @@ impl Lowerer { _ => (false, 0), }; if is_int_to_bit_array { - if let super::ast::LiteralKind::Int(value) = kind { + if let semantic::LiteralKind::Int(value) = kind { if *value < 0 || *value >= (1 << size) { // todo: error message return None; @@ -1448,36 +1662,34 @@ impl Lowerer { return None; }; - return Some(super::ast::Expr { + return Some(semantic::Expr { span, - kind: Box::new(super::ast::ExprKind::Lit( - super::ast::LiteralKind::Bitstring(value, size), - )), + kind: Box::new(semantic::ExprKind::Lit(semantic::LiteralKind::Bitstring( + value, size, + ))), ty: lhs_ty.as_const(), }); } } if matches!(lhs_ty, Type::UInt(..)) { - if let super::ast::LiteralKind::Int(value) = kind { + if let semantic::LiteralKind::Int(value) = kind { // this should have been validated by can_cast_literal_with_value_knowledge - return Some(super::ast::Expr { + return Some(semantic::Expr { span, - kind: Box::new(super::ast::ExprKind::Lit(super::ast::LiteralKind::Int( - *value, - ))), + kind: Box::new(semantic::ExprKind::Lit(semantic::LiteralKind::Int(*value))), ty: lhs_ty.as_const(), }); } } let result = match (&lhs_ty, &rhs_ty) { (Type::Float(..), Type::Int(..) | Type::UInt(..)) => { - if let super::ast::LiteralKind::Int(value) = kind { + if let semantic::LiteralKind::Int(value) = kind { if let Some(value) = safe_i64_to_f64(*value) { - return Some(super::ast::Expr { + return Some(semantic::Expr { span, - kind: Box::new(super::ast::ExprKind::Lit( - super::ast::LiteralKind::Float(value), - )), + kind: Box::new(semantic::ExprKind::Lit(semantic::LiteralKind::Float( + value, + ))), ty: lhs_ty.as_const(), }); } @@ -1491,10 +1703,10 @@ impl Lowerer { None } (Type::Angle(..) | Type::Float(..), Type::Float(..)) => { - if let super::ast::LiteralKind::Float(value) = kind { - return Some(super::ast::Expr { + if let semantic::LiteralKind::Float(value) = kind { + return Some(semantic::Expr { span, - kind: Box::new(super::ast::ExprKind::Lit(super::ast::LiteralKind::Float( + kind: Box::new(semantic::ExprKind::Lit(semantic::LiteralKind::Float( *value, ))), ty: lhs_ty.as_const(), @@ -1503,24 +1715,24 @@ impl Lowerer { None } (Type::Complex(..), Type::Complex(..)) => { - if let super::ast::LiteralKind::Complex(real, imag) = kind { - return Some(super::ast::Expr { + if let semantic::LiteralKind::Complex(real, imag) = kind { + return Some(semantic::Expr { span, - kind: Box::new(super::ast::ExprKind::Lit( - super::ast::LiteralKind::Complex(*real, *imag), - )), + kind: Box::new(semantic::ExprKind::Lit(semantic::LiteralKind::Complex( + *real, *imag, + ))), ty: lhs_ty.as_const(), }); } None } (Type::Complex(..), Type::Float(..)) => { - if let super::ast::LiteralKind::Float(value) = kind { - return Some(super::ast::Expr { + if let semantic::LiteralKind::Float(value) = kind { + return Some(semantic::Expr { span, - kind: Box::new(super::ast::ExprKind::Lit( - super::ast::LiteralKind::Complex(*value, 0.0), - )), + kind: Box::new(semantic::ExprKind::Lit(semantic::LiteralKind::Complex( + *value, 0.0, + ))), ty: lhs_ty.as_const(), }); } @@ -1529,12 +1741,12 @@ impl Lowerer { (Type::Complex(..), Type::Int(..) | Type::UInt(..)) => { // complex requires a double as input, so we need to // convert the int to a double, then create the complex - if let super::ast::LiteralKind::Int(value) = kind { + if let semantic::LiteralKind::Int(value) = kind { if let Some(value) = safe_i64_to_f64(*value) { - return Some(super::ast::Expr { + return Some(semantic::Expr { span, - kind: Box::new(super::ast::ExprKind::Lit( - super::ast::LiteralKind::Complex(value, 0.0), + kind: Box::new(semantic::ExprKind::Lit( + semantic::LiteralKind::Complex(value, 0.0), )), ty: lhs_ty.as_const(), }); @@ -1551,13 +1763,13 @@ impl Lowerer { } (Type::Bit(..), Type::Int(..) | Type::UInt(..)) => { // we've already checked that the value is 0 or 1 - if let super::ast::LiteralKind::Int(value) = kind { + if let semantic::LiteralKind::Int(value) = kind { if *value == 0 || *value == 1 { - return Some(super::ast::Expr { + return Some(semantic::Expr { span, - kind: Box::new(super::ast::ExprKind::Lit( - super::ast::LiteralKind::Int(*value), - )), + kind: Box::new(semantic::ExprKind::Lit(semantic::LiteralKind::Int( + *value, + ))), ty: lhs_ty.as_const(), }); } @@ -1569,16 +1781,16 @@ impl Lowerer { (Type::Int(width, _), Type::Int(_, _) | Type::UInt(_, _)) => { // we've already checked that this conversion can happen from a signed to unsigned int match kind { - super::ast::LiteralKind::Int(value) => { - return Some(super::ast::Expr { + semantic::LiteralKind::Int(value) => { + return Some(semantic::Expr { span, - kind: Box::new(super::ast::ExprKind::Lit( - super::ast::LiteralKind::Int(*value), - )), + kind: Box::new(semantic::ExprKind::Lit(semantic::LiteralKind::Int( + *value, + ))), ty: lhs_ty.as_const(), }); } - super::ast::LiteralKind::BigInt(value) => { + semantic::LiteralKind::BigInt(value) => { if let Some(width) = width { let mut cap = BigInt::from_i64(1).expect("1 is a valid i64"); BigInt::shl_assign(&mut cap, width); @@ -1591,11 +1803,11 @@ impl Lowerer { return None; } } - return Some(super::ast::Expr { + return Some(semantic::Expr { span, - kind: Box::new(super::ast::ExprKind::Lit( - super::ast::LiteralKind::BigInt(value.clone()), - )), + kind: Box::new(semantic::ExprKind::Lit(semantic::LiteralKind::BigInt( + value.clone(), + ))), ty: lhs_ty.as_const(), }); } @@ -1621,36 +1833,33 @@ impl Lowerer { lit: &syntax::Lit, target_ty: Option, span: Span, - ) -> Option { + ) -> Option { let (kind, ty) = (match &lit.kind { syntax::LiteralKind::Float(value) => Some(( - super::ast::LiteralKind::Float(-value), + semantic::LiteralKind::Float(-value), Type::Float(None, true), )), syntax::LiteralKind::Imaginary(value) => Some(( - super::ast::LiteralKind::Complex(0.0, -value), + semantic::LiteralKind::Complex(0.0, -value), Type::Complex(None, true), )), syntax::LiteralKind::Int(value) => { - Some((super::ast::LiteralKind::Int(-value), Type::Int(None, true))) + Some((semantic::LiteralKind::Int(-value), Type::Int(None, true))) } syntax::LiteralKind::BigInt(value) => { let value = BigInt::from(-1) * value; - Some(( - super::ast::LiteralKind::BigInt(value), - Type::Int(None, true), - )) + Some((semantic::LiteralKind::BigInt(value), Type::Int(None, true))) } syntax::LiteralKind::Duration(value, time_unit) => { let unit = match time_unit { - syntax::TimeUnit::Dt => super::ast::TimeUnit::Dt, - syntax::TimeUnit::Ms => super::ast::TimeUnit::Ms, - syntax::TimeUnit::Ns => super::ast::TimeUnit::Ns, - syntax::TimeUnit::S => super::ast::TimeUnit::S, - syntax::TimeUnit::Us => super::ast::TimeUnit::Us, + syntax::TimeUnit::Dt => semantic::TimeUnit::Dt, + syntax::TimeUnit::Ms => semantic::TimeUnit::Ms, + syntax::TimeUnit::Ns => semantic::TimeUnit::Ns, + syntax::TimeUnit::S => semantic::TimeUnit::S, + syntax::TimeUnit::Us => semantic::TimeUnit::Us, }; Some(( - super::ast::LiteralKind::Duration(-value, unit), + semantic::LiteralKind::Duration(-value, unit), Type::Duration(true), )) } @@ -1672,9 +1881,9 @@ impl Lowerer { } })?; - let expr = super::ast::Expr { + let expr = semantic::Expr { span, - kind: Box::new(super::ast::ExprKind::Lit(kind.clone())), + kind: Box::new(semantic::ExprKind::Lit(kind.clone())), ty, }; if let Some(target_ty) = target_ty { @@ -1686,9 +1895,9 @@ impl Lowerer { fn cast_expr_to_type( &mut self, ty: &Type, - expr: &super::ast::Expr, + expr: &semantic::Expr, span: Span, - ) -> Option { + ) -> Option { let cast_expr = self.try_cast_expr_to_type(ty, expr, span); if cast_expr.is_none() { let rhs_ty_name = format!("{:?}", expr.ty); @@ -1702,9 +1911,9 @@ impl Lowerer { fn try_cast_expr_to_type( &mut self, ty: &Type, - expr: &super::ast::Expr, + expr: &semantic::Expr, span: Span, - ) -> Option { + ) -> Option { if *ty == expr.ty { // Base case, we shouldn't have gotten here // but if we did, we can just return the rhs @@ -1727,7 +1936,7 @@ impl Lowerer { | (Type::Float(w1, _), Type::Float(w2, _)) | (Type::Complex(w1, _), Type::Complex(w2, _)) => { if w1.is_none() && w2.is_some() { - return Some(super::ast::Expr { + return Some(semantic::Expr { span: expr.span, kind: expr.kind.clone(), ty: ty.clone(), @@ -1735,7 +1944,7 @@ impl Lowerer { } if *w1 >= *w2 { - return Some(super::ast::Expr { + return Some(semantic::Expr { span: expr.span, kind: expr.kind.clone(), ty: ty.clone(), @@ -1766,7 +1975,7 @@ impl Lowerer { /// +----------------+-------+-----+------+-------+-------+-----+----------+-------+ /// | angle | Yes | No | No | No | - | Yes | No | No | /// +----------------+-------+-----+------+-------+-------+-----+----------+-------+ - fn cast_angle_expr_to_type(ty: &Type, rhs: &super::ast::Expr) -> Option { + fn cast_angle_expr_to_type(ty: &Type, rhs: &semantic::Expr) -> Option { assert!(matches!(rhs.ty, Type::Angle(..))); match ty { Type::Bit(..) | Type::Bool(..) => { @@ -1786,9 +1995,9 @@ impl Lowerer { fn cast_bit_expr_to_type( &mut self, ty: &Type, - rhs: &super::ast::Expr, + rhs: &semantic::Expr, span: Span, - ) -> Option { + ) -> Option { assert!(matches!(rhs.ty, Type::Bit(..))); // There is no operand, choosing the span of the node // but we could use the expr span as well. @@ -1821,7 +2030,7 @@ impl Lowerer { /// +----------------+-------+-----+------+-------+-------+-----+----------+-------+ /// /// Additional cast to complex - fn cast_float_expr_to_type(ty: &Type, rhs: &super::ast::Expr) -> Option { + fn cast_float_expr_to_type(ty: &Type, rhs: &semantic::Expr) -> Option { assert!(matches!(rhs.ty, Type::Float(..))); match ty { &Type::Angle(..) @@ -1843,7 +2052,7 @@ impl Lowerer { /// +----------------+-------+-----+------+-------+-------+-----+----------+-------+ /// | bool | - | Yes | Yes | Yes | No | Yes | No | No | /// +----------------+-------+-----+------+-------+-------+-----+----------+-------+ - fn cast_bool_expr_to_type(ty: &Type, rhs: &super::ast::Expr) -> Option { + fn cast_bool_expr_to_type(ty: &Type, rhs: &semantic::Expr) -> Option { assert!(matches!(rhs.ty, Type::Bool(..))); match ty { &Type::Bit(_) | &Type::Float(_, _) | &Type::Int(_, _) | &Type::UInt(_, _) => { @@ -1865,7 +2074,7 @@ impl Lowerer { /// /// Additional cast to ``BigInt`` #[allow(clippy::too_many_lines)] - fn cast_int_expr_to_type(ty: &Type, rhs: &super::ast::Expr) -> Option { + fn cast_int_expr_to_type(ty: &Type, rhs: &semantic::Expr) -> Option { assert!(matches!(rhs.ty, Type::Int(..) | Type::UInt(..))); match ty { @@ -1883,8 +2092,8 @@ impl Lowerer { fn cast_bitarray_expr_to_type( dims: &ArrayDimensions, ty: &Type, - rhs: &super::ast::Expr, - ) -> Option { + rhs: &semantic::Expr, + ) -> Option { let ArrayDimensions::One(array_width) = dims else { return None; }; @@ -1906,10 +2115,10 @@ impl Lowerer { fn lower_binary_op_expr( &mut self, op: syntax::BinOp, - lhs: super::ast::Expr, - rhs: super::ast::Expr, + lhs: semantic::Expr, + rhs: semantic::Expr, span: Span, - ) -> Option { + ) -> Option { if lhs.ty.is_quantum() { let kind = SemanticErrorKind::QuantumTypesInBinaryExpression(lhs.span); self.push_semantic_error(kind); @@ -1961,13 +2170,13 @@ impl Lowerer { rhs }; - let bin_expr = super::ast::BinaryOpExpr { + let bin_expr = semantic::BinaryOpExpr { lhs: new_lhs, rhs: new_rhs, op: op.into(), }; - let kind = super::ast::ExprKind::BinaryOp(bin_expr); - let expr = super::ast::Expr { + let kind = semantic::ExprKind::BinaryOp(bin_expr); + let expr = semantic::Expr { span, kind: Box::new(kind), ty, @@ -2000,7 +2209,7 @@ impl Lowerer { lhs } else { match &lhs.kind.as_ref() { - super::ast::ExprKind::Lit(kind) => { + semantic::ExprKind::Lit(kind) => { if can_cast_literal(&promoted_type, &left_type) || can_cast_literal_with_value_knowledge(&promoted_type, kind) { @@ -2016,7 +2225,7 @@ impl Lowerer { rhs } else { match &rhs.kind.as_ref() { - super::ast::ExprKind::Lit(kind) => { + semantic::ExprKind::Lit(kind) => { if can_cast_literal(&promoted_type, &right_type) || can_cast_literal_with_value_knowledge(&promoted_type, kind) { @@ -2059,13 +2268,13 @@ impl Lowerer { None } } else { - let bin_expr = super::ast::BinaryOpExpr { + let bin_expr = semantic::BinaryOpExpr { op: op.into(), lhs, rhs, }; - let kind = super::ast::ExprKind::BinaryOp(bin_expr); - let expr = super::ast::Expr { + let kind = semantic::ExprKind::BinaryOp(bin_expr); + let expr = semantic::Expr { span, kind: Box::new(kind), ty: ty.clone(), @@ -2074,14 +2283,14 @@ impl Lowerer { }; let ty = match op.into() { - super::ast::BinOp::AndL - | super::ast::BinOp::Eq - | super::ast::BinOp::Gt - | super::ast::BinOp::Gte - | super::ast::BinOp::Lt - | super::ast::BinOp::Lte - | super::ast::BinOp::Neq - | super::ast::BinOp::OrL => Type::Bool(false), + semantic::BinOp::AndL + | semantic::BinOp::Eq + | semantic::BinOp::Gt + | semantic::BinOp::Gte + | semantic::BinOp::Lt + | semantic::BinOp::Lte + | semantic::BinOp::Neq + | semantic::BinOp::OrL => Type::Bool(false), _ => ty, }; let mut expr = expr?; @@ -2127,41 +2336,13 @@ impl Lowerer { fn lower_index_element( &mut self, index: &syntax::IndexElement, - ) -> Option { + ) -> Option { match index { - syntax::IndexElement::DiscreteSet(set) => { - let items = set - .values - .iter() - .filter_map(|expr| self.lower_expr(expr)) - .collect::>(); - if set.values.len() == items.len() { - return Some(super::ast::IndexElement::DiscreteSet( - super::ast::DiscreteSet { - span: set.span, - values: syntax::list_from_iter(items), - }, - )); - } - let kind = SemanticErrorKind::FailedToCompileExpressionList(set.span); - self.push_semantic_error(kind); - None - } + syntax::IndexElement::DiscreteSet(set) => Some(semantic::IndexElement::DiscreteSet( + self.lower_discrete_set(set)?, + )), syntax::IndexElement::IndexSet(set) => { - let items = set - .values - .iter() - .filter_map(|expr| self.lower_index_set_item(expr)) - .collect::>(); - if set.values.len() == items.len() { - return Some(super::ast::IndexElement::IndexSet(super::ast::IndexSet { - span: set.span, - values: syntax::list_from_iter(items), - })); - } - let kind = SemanticErrorKind::FailedToCompileExpressionList(set.span); - self.push_semantic_error(kind); - None + Some(semantic::IndexElement::IndexSet(self.lower_index_set(set)?)) } } } @@ -2169,15 +2350,15 @@ impl Lowerer { fn lower_index_set_item( &mut self, item: &syntax::IndexSetItem, - ) -> Option { + ) -> Option { let item = match item { syntax::IndexSetItem::RangeDefinition(range_definition) => { - super::ast::IndexSetItem::RangeDefinition( + semantic::IndexSetItem::RangeDefinition( self.lower_range_definition(range_definition)?, ) } syntax::IndexSetItem::Expr(expr) => { - super::ast::IndexSetItem::Expr(self.lower_expr(expr)?) + semantic::IndexSetItem::Expr(self.lower_expr(expr)?) } syntax::IndexSetItem::Err => { unreachable!("IndexSetItem::Err should have been handled") @@ -2186,44 +2367,83 @@ impl Lowerer { Some(item) } - fn lower_range_definition( + fn lower_enumerable_set( &mut self, - range_definition: &syntax::RangeDefinition, - ) -> Option { - let mut failed = false; - let start = range_definition.start.as_ref().map(|e| { - let expr = self.lower_expr(e); - if expr.is_none() { - failed = true; - } - expr - }); - let step = range_definition.step.as_ref().map(|e| { - let expr = self.lower_expr(e); - if expr.is_none() { - failed = true; - } - expr - }); - let end = range_definition.end.as_ref().map(|e| { - let expr = self.lower_expr(e); - if expr.is_none() { - failed = true; - } - expr - }); - if failed { + set: &syntax::EnumerableSet, + ) -> Option { + match set { + syntax::EnumerableSet::DiscreteSet(set) => Some(semantic::EnumerableSet::DiscreteSet( + self.lower_discrete_set(set)?, + )), + syntax::EnumerableSet::RangeDefinition(range_definition) => { + Some(semantic::EnumerableSet::RangeDefinition( + self.lower_range_definition(range_definition)?, + )) + } + syntax::EnumerableSet::Expr(expr) => { + Some(semantic::EnumerableSet::Expr(self.lower_expr(expr)?)) + } + } + } + + fn lower_index_set(&mut self, set: &syntax::IndexSet) -> Option { + let items = set + .values + .iter() + .filter_map(|expr| self.lower_index_set_item(expr)) + .collect::>(); + + if set.values.len() != items.len() { + let kind = SemanticErrorKind::FailedToCompileExpressionList(set.span); + self.push_semantic_error(kind); return None; } - Some(super::ast::RangeDefinition { + Some(semantic::IndexSet { + span: set.span, + values: syntax::list_from_iter(items), + }) + } + + fn lower_discrete_set(&mut self, set: &syntax::DiscreteSet) -> Option { + let items = set + .values + .iter() + .filter_map(|expr| self.lower_expr(expr)) + .collect::>(); + + if set.values.len() != items.len() { + let kind = SemanticErrorKind::FailedToCompileExpressionList(set.span); + self.push_semantic_error(kind); + return None; + } + + Some(semantic::DiscreteSet { + span: set.span, + values: list_from_iter(items), + }) + } + + fn lower_range_definition( + &mut self, + range_definition: &syntax::RangeDefinition, + ) -> Option { + let start = range_definition.start.as_ref().map(|e| self.lower_expr(e)); + let step = range_definition.step.as_ref().map(|e| self.lower_expr(e)); + let end = range_definition.end.as_ref().map(|e| self.lower_expr(e)); + + let start = short_circuit_opt_item!(start); + let step = short_circuit_opt_item!(step); + let end = short_circuit_opt_item!(end); + + Some(semantic::RangeDefinition { span: range_definition.span, - start: start.map(|s| s.expect("start should be Some")), - step: step.map(|s| s.expect("step should be Some")), - end: end.map(|e| e.expect("end should be Some")), + start, + step, + end, }) } - fn lower_index_expr(&mut self, expr: &syntax::IndexExpr) -> Option { + fn lower_index_expr(&mut self, expr: &syntax::IndexExpr) -> Option { let collection = self.lower_expr(&expr.collection); let index = self.lower_index_element(&expr.index); let collection = collection?; @@ -2231,9 +2451,9 @@ impl Lowerer { let indexed_ty = self.get_indexed_type(&collection.ty, expr.span, 1)?; - Some(super::ast::Expr { + Some(semantic::Expr { span: expr.span, - kind: Box::new(super::ast::ExprKind::IndexExpr(super::ast::IndexExpr { + kind: Box::new(semantic::ExprKind::IndexExpr(semantic::IndexExpr { span: expr.span, collection, index, @@ -2277,7 +2497,7 @@ impl Lowerer { fn lower_indexed_ident_expr( &mut self, indexed_ident: &syntax::IndexedIdent, - ) -> Option { + ) -> Option { let ident = indexed_ident.name.clone(); let indices = indexed_ident @@ -2296,10 +2516,10 @@ impl Lowerer { return None; } - Some(super::ast::Expr { + Some(semantic::Expr { span: indexed_ident.span, - kind: Box::new(super::ast::ExprKind::IndexedIdentifier( - super::ast::IndexedIdent { + kind: Box::new(semantic::ExprKind::IndexedIdentifier( + semantic::IndexedIdent { span: indexed_ident.span, symbol_id, indices: syntax::list_from_iter(indices), @@ -2310,10 +2530,10 @@ impl Lowerer { } } -fn wrap_expr_in_implicit_cast_expr(ty: Type, rhs: super::ast::Expr) -> super::ast::Expr { - super::ast::Expr { +fn wrap_expr_in_implicit_cast_expr(ty: Type, rhs: semantic::Expr) -> semantic::Expr { + semantic::Expr { span: rhs.span, - kind: Box::new(super::ast::ExprKind::Cast(super::ast::Cast { + kind: Box::new(semantic::ExprKind::Cast(semantic::Cast { span: Span::default(), expr: rhs, ty: ty.clone(), @@ -2329,7 +2549,7 @@ fn wrap_expr_in_implicit_cast_expr(ty: Type, rhs: super::ast::Expr) -> super::as /// +----------------+-------+-----+------+-------+-------+-----+----------+-------+ /// | complex | ?? | ?? | ?? | ?? | No | ?? | No | No | /// +----------------+-------+-----+------+-------+-------+-----+----------+-------+ -fn cast_complex_expr_to_type(ty: &Type, rhs: &super::ast::Expr) -> Option { +fn cast_complex_expr_to_type(ty: &Type, rhs: &semantic::Expr) -> Option { assert!(matches!(rhs.ty, Type::Complex(..))); if matches!((ty, &rhs.ty), (Type::Complex(..), Type::Complex(..))) { diff --git a/compiler/qsc_qasm3/src/semantic/tests/statements.rs b/compiler/qsc_qasm3/src/semantic/tests/statements.rs index 8b24eb8bbc..eda5cb3db6 100644 --- a/compiler/qsc_qasm3/src/semantic/tests/statements.rs +++ b/compiler/qsc_qasm3/src/semantic/tests/statements.rs @@ -1,4 +1,8 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. -pub mod box_stmt; +mod box_stmt; +mod for_stmt; +mod if_stmt; +mod switch_stmt; +mod while_stmt; diff --git a/compiler/qsc_qasm3/src/semantic/tests/statements/box_stmt.rs b/compiler/qsc_qasm3/src/semantic/tests/statements/box_stmt.rs index ff478ed7bd..4b731ec2d5 100644 --- a/compiler/qsc_qasm3/src/semantic/tests/statements/box_stmt.rs +++ b/compiler/qsc_qasm3/src/semantic/tests/statements/box_stmt.rs @@ -1,9 +1,8 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. -use expect_test::expect; - use crate::semantic::tests::check_stmt_kinds; +use expect_test::expect; #[test] fn with_invalid_instruction_fails() { diff --git a/compiler/qsc_qasm3/src/semantic/tests/statements/for_stmt.rs b/compiler/qsc_qasm3/src/semantic/tests/statements/for_stmt.rs new file mode 100644 index 0000000000..28d52475a3 --- /dev/null +++ b/compiler/qsc_qasm3/src/semantic/tests/statements/for_stmt.rs @@ -0,0 +1,92 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +use crate::semantic::tests::check_stmt_kinds; +use expect_test::expect; + +#[test] +fn shadowing_loop_variable_in_single_stmt_body_fails() { + check_stmt_kinds( + " + for int x in {} + int x = 2; + ", + &expect![[r#" + Program: + version: + statements: + + [Qsc.Qasm3.Compile.RedefinedSymbol + + x Redefined symbol: x. + ,-[test:3:13] + 2 | for int x in {} + 3 | int x = 2; + : ^ + 4 | + `---- + ]"#]], + ); +} + +#[test] +fn shadowing_loop_variable_in_block_body_succeeds() { + check_stmt_kinds( + " + for int x in {} { + int x = 2; + } + ", + &expect![[r#" + ForStmt [5-47]: + loop_variable: 6 + iterable: DiscreteSet [18-20]: + values: + body: Stmt [21-47]: + annotations: + kind: Block [21-47]: + Stmt [31-41]: + annotations: + kind: ClassicalDeclarationStmt [31-41]: + symbol_id: 7 + ty_span: [31-34] + init_expr: Expr [39-40]: + ty: Int(None, true) + kind: Lit: Int(2) + "#]], + ); +} + +#[test] +fn loop_creates_its_own_scope() { + check_stmt_kinds( + " + int a = 0; + for int x in {} + // shadowing works because this + // declaration is in a different + // scope from `int a = 0;` scope. + int a = 1; + ", + &expect![[r#" + ClassicalDeclarationStmt [5-15]: + symbol_id: 6 + ty_span: [5-8] + init_expr: Expr [13-14]: + ty: Int(None, true) + kind: Lit: Int(0) + ForStmt [20-177]: + loop_variable: 7 + iterable: DiscreteSet [33-35]: + values: + body: Stmt [167-177]: + annotations: + kind: ClassicalDeclarationStmt [167-177]: + symbol_id: 8 + ty_span: [167-170] + init_expr: Expr [175-176]: + ty: Int(None, true) + kind: Lit: Int(1) + "#]], + ); +} diff --git a/compiler/qsc_qasm3/src/semantic/tests/statements/if_stmt.rs b/compiler/qsc_qasm3/src/semantic/tests/statements/if_stmt.rs new file mode 100644 index 0000000000..4fb652259a --- /dev/null +++ b/compiler/qsc_qasm3/src/semantic/tests/statements/if_stmt.rs @@ -0,0 +1,175 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +use crate::semantic::tests::check_stmt_kinds; +use expect_test::expect; + +#[test] +fn if_branch_doesnt_create_its_own_scope() { + check_stmt_kinds( + " + int a = 0; + if (true) int a = 1; + ", + &expect![[r#" + Program: + version: + statements: + Stmt [5-15]: + annotations: + kind: ClassicalDeclarationStmt [5-15]: + symbol_id: 6 + ty_span: [5-8] + init_expr: Expr [13-14]: + ty: Int(None, true) + kind: Lit: Int(0) + + [Qsc.Qasm3.Compile.RedefinedSymbol + + x Redefined symbol: a. + ,-[test:3:19] + 2 | int a = 0; + 3 | if (true) int a = 1; + : ^ + 4 | + `---- + ]"#]], + ); +} + +#[test] +fn else_branch_doesnt_create_its_own_scope() { + check_stmt_kinds( + " + int a = 0; + if (true) {} + else int a = 1; + ", + &expect![[r#" + Program: + version: + statements: + Stmt [5-15]: + annotations: + kind: ClassicalDeclarationStmt [5-15]: + symbol_id: 6 + ty_span: [5-8] + init_expr: Expr [13-14]: + ty: Int(None, true) + kind: Lit: Int(0) + + [Qsc.Qasm3.Compile.RedefinedSymbol + + x Redefined symbol: a. + ,-[test:4:14] + 3 | if (true) {} + 4 | else int a = 1; + : ^ + 5 | + `---- + ]"#]], + ); +} + +#[test] +fn branch_block_creates_a_new_scope() { + check_stmt_kinds( + " + int a = 0; + if (true) { int a = 1; } + ", + &expect![[r#" + ClassicalDeclarationStmt [5-15]: + symbol_id: 6 + ty_span: [5-8] + init_expr: Expr [13-14]: + ty: Int(None, true) + kind: Lit: Int(0) + IfStmt [20-44]: + condition: Expr [24-28]: + ty: Bool(true) + kind: Lit: Bool(true) + if_body: Stmt [30-44]: + annotations: + kind: Block [30-44]: + Stmt [32-42]: + annotations: + kind: ClassicalDeclarationStmt [32-42]: + symbol_id: 7 + ty_span: [32-35] + init_expr: Expr [40-41]: + ty: Int(None, true) + kind: Lit: Int(1) + else_body: + "#]], + ); +} + +#[test] +fn if_scope_and_else_scope_are_different() { + check_stmt_kinds( + " + int a = 0; + if (true) { int a = 1; } + else { int a = 2; } + ", + &expect![[r#" + ClassicalDeclarationStmt [5-15]: + symbol_id: 6 + ty_span: [5-8] + init_expr: Expr [13-14]: + ty: Int(None, true) + kind: Lit: Int(0) + IfStmt [20-68]: + condition: Expr [24-28]: + ty: Bool(true) + kind: Lit: Bool(true) + if_body: Stmt [30-44]: + annotations: + kind: Block [30-44]: + Stmt [32-42]: + annotations: + kind: ClassicalDeclarationStmt [32-42]: + symbol_id: 7 + ty_span: [32-35] + init_expr: Expr [40-41]: + ty: Int(None, true) + kind: Lit: Int(1) + else_body: Stmt [54-68]: + annotations: + kind: Block [54-68]: + Stmt [56-66]: + annotations: + kind: ClassicalDeclarationStmt [56-66]: + symbol_id: 8 + ty_span: [56-59] + init_expr: Expr [64-65]: + ty: Int(None, true) + kind: Lit: Int(2) + "#]], + ); +} + +#[test] +fn condition_cast() { + check_stmt_kinds( + "if (1) true;", + &expect![[r#" + IfStmt [0-12]: + condition: Expr [4-5]: + ty: Bool(false) + kind: Cast [0-0]: + ty: Bool(false) + expr: Expr [4-5]: + ty: Int(None, true) + kind: Lit: Int(1) + if_body: Stmt [7-12]: + annotations: + kind: ExprStmt [7-12]: + expr: Expr [7-11]: + ty: Bool(true) + kind: Lit: Bool(true) + else_body: + "#]], + ); +} diff --git a/compiler/qsc_qasm3/src/semantic/tests/statements/switch_stmt.rs b/compiler/qsc_qasm3/src/semantic/tests/statements/switch_stmt.rs new file mode 100644 index 0000000000..2ed89fa9f4 --- /dev/null +++ b/compiler/qsc_qasm3/src/semantic/tests/statements/switch_stmt.rs @@ -0,0 +1,117 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +use crate::semantic::tests::check_stmt_kinds; +use expect_test::expect; + +#[test] +fn not_supported_before_version_3_1() { + check_stmt_kinds( + r#" + OPENQASM 3.0; + switch (1) { case 1 {} } + "#, + &expect![[r#" + Program: + version: 3.0 + statements: + + [Qsc.Qasm3.Compile.NotSupportedInThisVersion + + x switch statements were introduced in version 3.1 + ,-[test:3:5] + 2 | OPENQASM 3.0; + 3 | switch (1) { case 1 {} } + : ^^^^^^^^^^^^^^^^^^^^^^^^ + 4 | + `---- + ]"#]], + ); +} + +#[test] +fn cases_introduce_their_own_scope() { + check_stmt_kinds( + r#" + int a = 0; + switch (1) { + case 1 { int a = 1; } + case 2, 3 { int a = 2; } + } + "#, + &expect![[r#" + ClassicalDeclarationStmt [5-15]: + symbol_id: 6 + ty_span: [5-8] + init_expr: Expr [13-14]: + ty: Int(None, true) + kind: Lit: Int(0) + SwitchStmt [20-101]: + target: Expr [28-29]: + ty: Int(None, true) + kind: Lit: Int(1) + cases: + SwitchCase [41-62]: + labels: + Expr [46-47]: + ty: Int(None, true) + kind: Lit: Int(1) + block: Block [48-62]: + Stmt [50-60]: + annotations: + kind: ClassicalDeclarationStmt [50-60]: + symbol_id: 7 + ty_span: [50-53] + init_expr: Expr [58-59]: + ty: Int(None, true) + kind: Lit: Int(1) + SwitchCase [71-95]: + labels: + Expr [76-77]: + ty: Int(None, true) + kind: Lit: Int(2) + Expr [79-80]: + ty: Int(None, true) + kind: Lit: Int(3) + block: Block [81-95]: + Stmt [83-93]: + annotations: + kind: ClassicalDeclarationStmt [83-93]: + symbol_id: 8 + ty_span: [83-86] + init_expr: Expr [91-92]: + ty: Int(None, true) + kind: Lit: Int(2) + default_case: + "#]], + ); +} + +#[test] +fn target_cast() { + check_stmt_kinds( + "switch (true) { case false {} }", + &expect![[r#" + SwitchStmt [0-31]: + target: Expr [8-12]: + ty: Int(None, false) + kind: Cast [0-0]: + ty: Int(None, false) + expr: Expr [8-12]: + ty: Bool(true) + kind: Lit: Bool(true) + cases: + SwitchCase [16-29]: + labels: + Expr [21-26]: + ty: Int(None, false) + kind: Cast [0-0]: + ty: Int(None, false) + expr: Expr [21-26]: + ty: Bool(true) + kind: Lit: Bool(false) + block: Block [27-29]: + default_case: + "#]], + ); +} diff --git a/compiler/qsc_qasm3/src/semantic/tests/statements/while_stmt.rs b/compiler/qsc_qasm3/src/semantic/tests/statements/while_stmt.rs new file mode 100644 index 0000000000..c1e5a7f644 --- /dev/null +++ b/compiler/qsc_qasm3/src/semantic/tests/statements/while_stmt.rs @@ -0,0 +1,94 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +use crate::semantic::tests::check_stmt_kinds; +use expect_test::expect; + +#[test] +fn single_stmt_body_doesnt_creates_its_own_scope() { + check_stmt_kinds( + " + int a = 0; + while(true) int a = 1; + ", + &expect![[r#" + Program: + version: + statements: + Stmt [5-15]: + annotations: + kind: ClassicalDeclarationStmt [5-15]: + symbol_id: 6 + ty_span: [5-8] + init_expr: Expr [13-14]: + ty: Int(None, true) + kind: Lit: Int(0) + + [Qsc.Qasm3.Compile.RedefinedSymbol + + x Redefined symbol: a. + ,-[test:3:21] + 2 | int a = 0; + 3 | while(true) int a = 1; + : ^ + 4 | + `---- + ]"#]], + ); +} + +#[test] +fn block_body_creates_its_own_scope() { + check_stmt_kinds( + " + int a = 0; + while(true) { int a = 1; } + ", + &expect![[r#" + ClassicalDeclarationStmt [5-15]: + symbol_id: 6 + ty_span: [5-8] + init_expr: Expr [13-14]: + ty: Int(None, true) + kind: Lit: Int(0) + WhileLoop [20-46]: + condition: Expr [26-30]: + ty: Bool(true) + kind: Lit: Bool(true) + body: Stmt [32-46]: + annotations: + kind: Block [32-46]: + Stmt [34-44]: + annotations: + kind: ClassicalDeclarationStmt [34-44]: + symbol_id: 7 + ty_span: [34-37] + init_expr: Expr [42-43]: + ty: Int(None, true) + kind: Lit: Int(1) + "#]], + ); +} + +#[test] +fn condition_cast() { + check_stmt_kinds( + "while (1) true;", + &expect![[r#" + WhileLoop [0-15]: + condition: Expr [7-8]: + ty: Bool(false) + kind: Cast [0-0]: + ty: Bool(false) + expr: Expr [7-8]: + ty: Int(None, true) + kind: Lit: Int(1) + body: Stmt [10-15]: + annotations: + kind: ExprStmt [10-15]: + expr: Expr [10-14]: + ty: Bool(true) + kind: Lit: Bool(true) + "#]], + ); +} From 97a68193ff0b6f4139972c904e8442f06f466345 Mon Sep 17 00:00:00 2001 From: Ian Davis Date: Tue, 18 Mar 2025 14:48:12 -0700 Subject: [PATCH 062/108] Adding new compiler, lowerer is now infallible (#2239) --- compiler/qsc_qasm3/src/ast_builder.rs | 8 + compiler/qsc_qasm3/src/compile/tests.rs | 11 +- compiler/qsc_qasm3/src/compiler.rs | 1250 ++++++++++++++ compiler/qsc_qasm3/src/lib.rs | 1 + compiler/qsc_qasm3/src/parser/ast.rs | 5 +- compiler/qsc_qasm3/src/parser/expr.rs | 8 +- compiler/qsc_qasm3/src/parser/expr/tests.rs | 2 + compiler/qsc_qasm3/src/parser/mut_visit.rs | 2 +- .../src/parser/stmt/tests/barrier.rs | 2 + .../qsc_qasm3/src/parser/stmt/tests/block.rs | 43 +- .../src/parser/stmt/tests/box_stmt.rs | 4 + .../src/parser/stmt/tests/classical_decl.rs | 1 + .../qsc_qasm3/src/parser/stmt/tests/delay.rs | 2 + .../src/parser/stmt/tests/expr_stmt.rs | 8 + .../src/parser/stmt/tests/for_loops.rs | 10 + .../src/parser/stmt/tests/gate_call.rs | 13 + .../qsc_qasm3/src/parser/stmt/tests/gphase.rs | 5 + .../src/parser/stmt/tests/if_stmt.rs | 14 + .../src/parser/stmt/tests/measure.rs | 6 + .../qsc_qasm3/src/parser/stmt/tests/reset.rs | 2 + .../src/parser/stmt/tests/while_loops.rs | 7 + compiler/qsc_qasm3/src/semantic.rs | 8 +- compiler/qsc_qasm3/src/semantic/ast.rs | 58 +- compiler/qsc_qasm3/src/semantic/error.rs | 6 + compiler/qsc_qasm3/src/semantic/lowerer.rs | 1473 ++++++++--------- compiler/qsc_qasm3/src/semantic/symbols.rs | 109 +- compiler/qsc_qasm3/src/semantic/tests.rs | 92 +- .../qsc_qasm3/src/semantic/tests/decls.rs | 73 +- .../src/semantic/tests/decls/angle.rs | 29 +- .../src/semantic/tests/decls/duration.rs | 35 +- .../src/semantic/tests/decls/float.rs | 109 +- .../qsc_qasm3/src/semantic/tests/decls/int.rs | 46 +- .../src/semantic/tests/decls/stretch.rs | 17 +- .../src/semantic/tests/decls/uint.rs | 102 +- .../src/semantic/tests/expression.rs | 2 +- .../binary/comparison/bit_to_bit.rs | 16 +- .../binary/comparison/bool_to_bool.rs | 16 +- .../expression/implicit_cast_from_angle.rs | 92 +- .../expression/implicit_cast_from_bit.rs | 8 + .../expression/implicit_cast_from_float.rs | 72 +- .../src/semantic/tests/statements/box_stmt.rs | 25 +- .../src/semantic/tests/statements/for_stmt.rs | 16 +- .../src/semantic/tests/statements/if_stmt.rs | 32 + .../semantic/tests/statements/switch_stmt.rs | 16 +- .../semantic/tests/statements/while_stmt.rs | 14 + compiler/qsc_qasm3/src/tests.rs | 236 ++- compiler/qsc_qasm3/src/tests/assignment.rs | 14 +- .../qsc_qasm3/src/tests/assignment/alias.rs | 18 +- compiler/qsc_qasm3/src/tests/declaration.rs | 52 +- .../qsc_qasm3/src/tests/declaration/array.rs | 6 +- .../qsc_qasm3/src/tests/declaration/bool.rs | 16 - .../src/tests/declaration/complex.rs | 32 - .../qsc_qasm3/src/tests/declaration/float.rs | 34 +- .../src/tests/declaration/integer.rs | 46 +- .../tests/declaration/io/explicit_input.rs | 2 +- .../qsc_qasm3/src/tests/declaration/qubit.rs | 6 +- .../src/tests/declaration/unsigned_integer.rs | 62 - .../qsc_qasm3/src/tests/expression/binary.rs | 5 +- .../expression/implicit_cast_from_float.rs | 2 +- compiler/qsc_qasm3/src/tests/output.rs | 84 +- .../src/tests/sample_circuits/bell_pair.rs | 23 +- .../tests/sample_circuits/rgqft_multiplier.rs | 23 +- .../qsc_qasm3/src/tests/statement/include.rs | 22 +- .../qsc_qasm3/src/tests/statement/reset.rs | 22 +- 64 files changed, 3066 insertions(+), 1509 deletions(-) create mode 100644 compiler/qsc_qasm3/src/compiler.rs diff --git a/compiler/qsc_qasm3/src/ast_builder.rs b/compiler/qsc_qasm3/src/ast_builder.rs index 5f63aef4bb..5f65251db3 100644 --- a/compiler/qsc_qasm3/src/ast_builder.rs +++ b/compiler/qsc_qasm3/src/ast_builder.rs @@ -859,6 +859,14 @@ pub(crate) fn build_stmt_semi_from_expr(expr: Expr) -> Stmt { } } +pub(crate) fn build_stmt_semi_from_expr_with_span(expr: Expr, span: Span) -> Stmt { + Stmt { + id: NodeId::default(), + span, + kind: Box::new(StmtKind::Semi(Box::new(expr))), + } +} + pub(crate) fn build_wrapped_block_expr(block: Block) -> Expr { Expr { id: NodeId::default(), diff --git a/compiler/qsc_qasm3/src/compile/tests.rs b/compiler/qsc_qasm3/src/compile/tests.rs index 33c51c242e..72b0210d14 100644 --- a/compiler/qsc_qasm3/src/compile/tests.rs +++ b/compiler/qsc_qasm3/src/compile/tests.rs @@ -1,7 +1,7 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. -use crate::tests::{parse_all, print_compilation_errors, qasm_to_program_fragments}; +use crate::tests::{compile_all_fragments, print_compilation_errors}; use miette::Report; #[test] @@ -18,9 +18,7 @@ fn programs_with_includes_with_includes_can_be_compiled() -> miette::Result<(), ("source2.qasm".into(), source2.into()), ]; - let res = parse_all("source0.qasm", all_sources)?; - assert!(!res.has_errors()); - let unit = qasm_to_program_fragments(res.source, res.source_map); + let unit = compile_all_fragments("source0.qasm", all_sources)?; print_compilation_errors(&unit); assert!(!unit.has_errors()); Ok(()) @@ -41,10 +39,7 @@ fn including_stdgates_multiple_times_causes_symbol_redifintion_errors( ("source2.qasm".into(), source2.into()), ]; - let res = parse_all("source0.qasm", all_sources)?; - assert!(!res.has_errors()); - let unit = qasm_to_program_fragments(res.source, res.source_map); - + let unit = compile_all_fragments("source0.qasm", all_sources)?; assert!(unit.has_errors()); for error in unit.errors() { assert!(error.to_string().contains("Redefined symbol: ")); diff --git a/compiler/qsc_qasm3/src/compiler.rs b/compiler/qsc_qasm3/src/compiler.rs new file mode 100644 index 0000000000..eb71d6398c --- /dev/null +++ b/compiler/qsc_qasm3/src/compiler.rs @@ -0,0 +1,1250 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +use std::rc::Rc; + +use num_bigint::BigInt; +use qsc_data_structures::span::Span; +use qsc_frontend::{compile::SourceMap, error::WithSource}; + +use crate::{ + ast_builder::{ + build_arg_pat, build_array_reverse_expr, build_assignment_statement, build_binary_expr, + build_cast_call, build_cast_call_two_params, build_classical_decl, build_complex_from_expr, + build_convert_call_expr, build_if_expr_then_expr_else_expr, build_implicit_return_stmt, + build_lit_bigint_expr, build_lit_bool_expr, build_lit_complex_expr, build_lit_double_expr, + build_lit_int_expr, build_lit_result_array_expr_from_bitstring, build_lit_result_expr, + build_math_call_from_exprs, build_math_call_no_params, build_operation_with_stmts, + build_path_ident_expr, build_stmt_semi_from_expr, build_stmt_semi_from_expr_with_span, + build_top_level_ns_with_item, build_tuple_expr, build_unary_op_expr, build_while_stmt, + build_wrapped_block_expr, map_qsharp_type_to_ast_ty, wrap_expr_in_parens, + }, + parser::ast::{list_from_iter, List}, + runtime::{get_runtime_function_decls, RuntimeFunctions}, + semantic::{ + ast::{ + BinaryOpExpr, Cast, DiscreteSet, Expr, FunctionCall, IndexElement, IndexExpr, IndexSet, + IndexedIdent, LiteralKind, MeasureExpr, TimeUnit, UnaryOpExpr, + }, + symbols::{IOKind, Symbol, SymbolId, SymbolTable}, + types::{ArrayDimensions, Type}, + SemanticErrorKind, + }, + CompilerConfig, OperationSignature, OutputSemantics, ProgramType, QasmCompileUnit, +}; + +use crate::semantic::ast as semast; +use qsc_ast::ast::{self as qsast, NodeId, Package}; + +pub struct QasmCompiler { + /// The source map of QASM sources for error reporting. + pub source_map: SourceMap, + /// The configuration for the compiler. + /// This includes the qubit semantics to follow when compiling to Q# AST. + /// The output semantics to follow when compiling to Q# AST. + /// The program type to compile to. + pub config: CompilerConfig, + /// The compiled statments accumulated during compilation. + pub stmts: Vec, + /// The runtime functions that need to be included at the end of + /// compilation + pub runtime: RuntimeFunctions, + pub symbols: SymbolTable, + pub errors: Vec>, +} + +impl QasmCompiler { + /// The main entry into compilation. This function will compile the + /// source file and build the appropriate package based on the + /// configuration. + pub fn compile(mut self, program: &crate::semantic::ast::Program) -> QasmCompileUnit { + self.compile_stmts(&program.statements); + self.prepend_runtime_decls(); + let program_ty = self.config.program_ty.clone(); + let (package, signature) = match program_ty { + ProgramType::File => self.build_file(), + ProgramType::Operation => self.build_operation(), + ProgramType::Fragments => (self.build_fragments(), None), + }; + + QasmCompileUnit::new(self.source_map, self.errors, Some(package), signature) + } + + /// Build a package with namespace and an operation + /// containing the compiled statements. + fn build_file(&mut self) -> (Package, Option) { + let whole_span = Span::default(); + let operation_name = self.config.operation_name(); + let (operation, mut signature) = self.create_entry_operation(operation_name, whole_span); + let ns = self.config.namespace(); + signature.ns = Some(ns.to_string()); + let top = build_top_level_ns_with_item(whole_span, ns, operation); + ( + Package { + nodes: Box::new([top]), + ..Default::default() + }, + Some(signature), + ) + } + + /// Creates an operation with the given name. + fn build_operation(&mut self) -> (qsast::Package, Option) { + let whole_span = Span::default(); + let operation_name = self.config.operation_name(); + let (operation, signature) = self.create_entry_operation(operation_name, whole_span); + ( + Package { + nodes: Box::new([qsast::TopLevelNode::Stmt(Box::new(qsast::Stmt { + kind: Box::new(qsast::StmtKind::Item(Box::new(operation))), + span: whole_span, + id: qsast::NodeId::default(), + }))]), + ..Default::default() + }, + Some(signature), + ) + } + + /// Turns the compiled statements into package of top level nodes + fn build_fragments(&mut self) -> qsast::Package { + let nodes = self + .stmts + .drain(..) + .map(Box::new) + .map(qsast::TopLevelNode::Stmt) + .collect::>() + .into_boxed_slice(); + qsast::Package { + nodes, + ..Default::default() + } + } + + fn create_entry_operation>( + &mut self, + name: S, + whole_span: Span, + ) -> (qsast::Item, OperationSignature) { + let stmts = self.stmts.drain(..).collect::>(); + let input = self.symbols.get_input(); + let output = self.symbols.get_output(); + self.create_entry_item( + name, + stmts, + input, + output, + whole_span, + self.config.output_semantics, + ) + } + + fn create_entry_item>( + &mut self, + name: S, + stmts: Vec, + input: Option>>, + output: Option>>, + whole_span: Span, + output_semantics: OutputSemantics, + ) -> (qsast::Item, OperationSignature) { + let mut stmts = stmts; + let is_qiskit = matches!(output_semantics, OutputSemantics::Qiskit); + let mut signature = OperationSignature { + input: vec![], + output: String::new(), + name: name.as_ref().to_string(), + ns: None, + }; + let output_ty = if matches!(output_semantics, OutputSemantics::ResourceEstimation) { + // we have no output, but need to set the entry point return type + crate::types::Type::Tuple(vec![]) + } else if let Some(output) = output { + let output_exprs = if is_qiskit { + output + .iter() + .rev() + .filter(|symbol| { + matches!(symbol.ty, crate::semantic::types::Type::BitArray(..)) + }) + .map(|symbol| { + let ident = + build_path_ident_expr(symbol.name.as_str(), symbol.span, symbol.span); + + build_array_reverse_expr(ident) + }) + .collect::>() + } else { + output + .iter() + .map(|symbol| { + build_path_ident_expr(symbol.name.as_str(), symbol.span, symbol.span) + }) + .collect::>() + }; + // this is the output whether it is inferred or explicitly defined + // map the output symbols into a return statement, add it to the nodes list, + // and get the entry point return type + let output_types = if is_qiskit { + output + .iter() + .rev() + .filter(|symbol| { + matches!(symbol.ty, crate::semantic::types::Type::BitArray(..)) + }) + .map(|symbol| symbol.qsharp_ty.clone()) + .collect::>() + } else { + output + .iter() + .map(|symbol| symbol.qsharp_ty.clone()) + .collect::>() + }; + + let (output_ty, output_expr) = if output_types.len() == 1 { + (output_types[0].clone(), output_exprs[0].clone()) + } else { + let output_ty = crate::types::Type::Tuple(output_types); + let output_expr = build_tuple_expr(output_exprs); + (output_ty, output_expr) + }; + + let return_stmt = build_implicit_return_stmt(output_expr); + stmts.push(return_stmt); + output_ty + } else { + if is_qiskit { + let kind = SemanticErrorKind::QiskitEntryPointMissingOutput(whole_span); + self.push_semantic_error(kind); + } + crate::types::Type::Tuple(vec![]) + }; + + let ast_ty = map_qsharp_type_to_ast_ty(&output_ty); + signature.output = format!("{output_ty}"); + // TODO: This can create a collision on multiple compiles when interactive + // We also have issues with the new entry point inference logic + let input_desc = input + .iter() + .flat_map(|s| { + s.iter() + .map(|s| (s.name.to_string(), format!("{}", s.qsharp_ty))) + }) + .collect::>(); + signature.input = input_desc; + let input_pats = input + .into_iter() + .flat_map(|s| { + s.into_iter().map(|s| { + build_arg_pat( + s.name.clone(), + s.span, + map_qsharp_type_to_ast_ty(&s.qsharp_ty), + ) + }) + }) + .collect::>(); + + ( + build_operation_with_stmts(name, input_pats, ast_ty, stmts, whole_span), + signature, + ) + } + + /// Prepends the runtime declarations to the beginning of the statements. + /// Any runtime functions that are required by the compiled code are set + /// in the `self.runtime` field during compilation. + /// + /// We could declare these as top level functions when compiling to + /// `ProgramType::File`, but prepending them to the statements is the + /// most flexible approach. + fn prepend_runtime_decls(&mut self) { + let mut runtime = get_runtime_function_decls(self.runtime); + self.stmts.splice(0..0, runtime.drain(..)); + } + + fn compile_stmts(&mut self, smtms: &[Box]) { + for stmt in smtms { + let compiled_stmt = self.compile_stmt(stmt.as_ref()); + if let Some(stmt) = compiled_stmt { + self.stmts.push(stmt); + } + } + } + + fn compile_stmt(&mut self, stmt: &crate::semantic::ast::Stmt) -> Option { + match stmt.kind.as_ref() { + semast::StmtKind::Alias(stmt) => self.compile_alias_decl_stmt(stmt), + semast::StmtKind::Assign(stmt) => self.compile_assign_stmt(stmt), + semast::StmtKind::IndexedAssign(stmt) => self.compile_indexed_assign_stmt(stmt), + semast::StmtKind::AssignOp(stmt) => self.compile_assign_op_stmt(stmt), + semast::StmtKind::Barrier(stmt) => self.compile_barrier_stmt(stmt), + semast::StmtKind::Box(stmt) => self.compile_box_stmt(stmt), + semast::StmtKind::Block(stmt) => self.compile_block_stmt(stmt), + semast::StmtKind::CalibrationGrammar(stmt) => { + self.compile_calibration_grammar_stmt(stmt) + } + semast::StmtKind::ClassicalDecl(stmt) => self.compile_classical_decl(stmt), + semast::StmtKind::Def(stmt) => self.compile_def_stmt(stmt), + semast::StmtKind::DefCal(stmt) => self.compile_def_cal_stmt(stmt), + semast::StmtKind::Delay(stmt) => self.compile_delay_stmt(stmt), + semast::StmtKind::End(stmt) => self.compile_end_stmt(stmt), + semast::StmtKind::ExprStmt(stmt) => self.compile_expr_stmt(stmt), + semast::StmtKind::ExternDecl(stmt) => self.compile_extern_stmt(stmt), + semast::StmtKind::For(stmt) => self.compile_for_stmt(stmt), + semast::StmtKind::If(stmt) => self.compile_if_stmt(stmt), + semast::StmtKind::GateCall(stmt) => self.compile_gate_call_stmt(stmt), + semast::StmtKind::GPhase(stmt) => self.compile_gphase_stmt(stmt), + semast::StmtKind::Include(stmt) => self.compile_include_stmt(stmt), + semast::StmtKind::InputDeclaration(stmt) => self.compile_input_decl_stmt(stmt), + semast::StmtKind::OutputDeclaration(stmt) => self.compile_output_decl_stmt(stmt), + semast::StmtKind::Measure(stmt) => self.compile_measure_stmt(stmt), + semast::StmtKind::Pragma(stmt) => self.compile_pragma_stmt(stmt), + semast::StmtKind::QuantumGateDefinition(stmt) => self.compile_gate_decl_stmt(stmt), + semast::StmtKind::QuantumDecl(stmt) => self.compile_quantum_decl_stmt(stmt), + semast::StmtKind::Reset(stmt) => self.compile_reset_stmt(stmt), + semast::StmtKind::Return(stmt) => self.compile_return_stmt(stmt), + semast::StmtKind::Switch(stmt) => self.compile_switch_stmt(stmt), + semast::StmtKind::WhileLoop(stmt) => self.compile_while_stmt(stmt), + semast::StmtKind::Err => { + // todo: determine if we should push an error here + // Are we going to allow trying to compile a program with semantic errors? + None + } + } + } + + fn compile_alias_decl_stmt(&mut self, stmt: &semast::AliasDeclStmt) -> Option { + self.push_unimplemented_error_message("alias statements", stmt.span); + None + } + + fn compile_assign_stmt(&mut self, stmt: &semast::AssignStmt) -> Option { + let symbol = &self.symbols[stmt.symbol_id]; + let symbol = symbol.clone(); + let name = &symbol.name; + + let stmt_span = stmt.span; + let name_span = stmt.name_span; + + let rhs = self.compile_expr(&stmt.rhs)?; + let stmt = build_assignment_statement(name_span, name, rhs, stmt_span); + + Some(stmt) + } + + fn compile_indexed_assign_stmt( + &mut self, + stmt: &semast::IndexedAssignStmt, + ) -> Option { + self.push_unimplemented_error_message("indexed assignment statements", stmt.span); + None + } + + fn compile_assign_op_stmt(&mut self, stmt: &semast::AssignOpStmt) -> Option { + self.push_unimplemented_error_message("assignment op statements", stmt.span); + None + } + + fn compile_barrier_stmt(&mut self, stmt: &semast::BarrierStmt) -> Option { + self.push_unimplemented_error_message("barrier statements", stmt.span); + None + } + + fn compile_box_stmt(&mut self, stmt: &semast::BoxStmt) -> Option { + self.push_unimplemented_error_message("box statements", stmt.span); + None + } + + fn compile_block(&mut self, block: &semast::Block) -> qsast::Block { + let stmts = block + .stmts + .iter() + .filter_map(|stmt| self.compile_stmt(stmt)) + .collect::>(); + qsast::Block { + id: qsast::NodeId::default(), + stmts: list_from_iter(stmts), + span: block.span, + } + } + + fn compile_block_stmt(&mut self, block: &semast::Block) -> Option { + let block = self.compile_block(block); + Some(build_stmt_semi_from_expr(build_wrapped_block_expr(block))) + } + + fn compile_calibration_grammar_stmt( + &mut self, + stmt: &semast::CalibrationGrammarStmt, + ) -> Option { + self.push_unimplemented_error_message("calibration grammar statements", stmt.span); + None + } + + fn compile_classical_decl( + &mut self, + decl: &semast::ClassicalDeclarationStmt, + ) -> Option { + let symbol = &self.symbols[decl.symbol_id]; + let symbol = symbol.clone(); + let name = &symbol.name; + let is_const = symbol.ty.is_const(); + let ty_span = decl.ty_span; + let decl_span = decl.span; + let name_span = symbol.span; + let qsharp_ty = &symbol.qsharp_ty; + let expr = decl.init_expr.as_ref(); + + let stmt = match expr { + semast::ValueExpression::Expr(expr) => { + let expr = self.compile_expr(expr)?; + build_classical_decl( + name, is_const, ty_span, decl_span, name_span, qsharp_ty, expr, + ) + } + semast::ValueExpression::Measurement(expr) => { + let expr = self.compile_measure_expr(expr)?; + build_stmt_semi_from_expr_with_span(expr, decl.span) + } + }; + + Some(stmt) + } + + fn compile_def_stmt(&mut self, stmt: &semast::DefStmt) -> Option { + self.push_unimplemented_error_message("def statements", stmt.span); + None + } + + fn compile_def_cal_stmt(&mut self, stmt: &semast::DefCalStmt) -> Option { + self.push_unimplemented_error_message("def cal statements", stmt.span); + None + } + + fn compile_delay_stmt(&mut self, stmt: &semast::DelayStmt) -> Option { + self.push_unimplemented_error_message("dealy statements", stmt.span); + None + } + + fn compile_end_stmt(&mut self, stmt: &semast::EndStmt) -> Option { + self.push_unimplemented_error_message("end statements", stmt.span); + None + } + + fn compile_expr_stmt(&mut self, stmt: &semast::ExprStmt) -> Option { + let expr = self.compile_expr(&stmt.expr)?; + Some(build_stmt_semi_from_expr_with_span(expr, stmt.span)) + } + + fn compile_extern_stmt(&mut self, stmt: &semast::ExternDecl) -> Option { + self.push_unimplemented_error_message("extern statements", stmt.span); + None + } + + fn compile_for_stmt(&mut self, stmt: &semast::ForStmt) -> Option { + self.push_unimplemented_error_message("for statements", stmt.span); + None + } + + fn compile_if_stmt(&mut self, stmt: &semast::IfStmt) -> Option { + self.push_unimplemented_error_message("if statements", stmt.span); + None + } + + fn compile_gate_call_stmt(&mut self, stmt: &semast::GateCall) -> Option { + self.push_unimplemented_error_message("gate call statements", stmt.span); + None + } + + fn compile_gphase_stmt(&mut self, stmt: &semast::GPhase) -> Option { + self.push_unimplemented_error_message("gphase statements", stmt.span); + None + } + + fn compile_include_stmt(&mut self, stmt: &semast::IncludeStmt) -> Option { + self.push_unimplemented_error_message("include statements", stmt.span); + None + } + + #[allow(clippy::unused_self)] + fn compile_input_decl_stmt(&mut self, _stmt: &semast::InputDeclaration) -> Option { + None + } + + fn compile_output_decl_stmt( + &mut self, + stmt: &semast::OutputDeclaration, + ) -> Option { + let symbol = &self.symbols[stmt.symbol_id]; + + // input decls should have been pushed to symbol table, + // but should not be the stmts list. + // TODO: This may be an issue for tooling as there isn't a way to have a forward + // declared varible in Q#. + if symbol.io_kind != IOKind::Output { + //self.push_semantic_error(SemanticErrorKind::InvalidIODeclaration(stmt.span)); + return None; + } + + let symbol = symbol.clone(); + let name = &symbol.name; + let is_const = symbol.ty.is_const(); + let ty_span = stmt.ty_span; // todo + let decl_span = stmt.span; + let name_span = symbol.span; + let qsharp_ty = &symbol.qsharp_ty; + + let expr = stmt.init_expr.as_ref(); + + let expr = self.compile_expr(expr)?; + let stmt = build_classical_decl( + name, is_const, ty_span, decl_span, name_span, qsharp_ty, expr, + ); + + Some(stmt) + } + + fn compile_measure_stmt(&mut self, stmt: &semast::MeasureStmt) -> Option { + self.push_unimplemented_error_message("measure statements", stmt.span); + None + } + + fn compile_pragma_stmt(&mut self, stmt: &semast::Pragma) -> Option { + self.push_unimplemented_error_message("pragma statements", stmt.span); + None + } + + fn compile_gate_decl_stmt( + &mut self, + stmt: &semast::QuantumGateDefinition, + ) -> Option { + self.push_unimplemented_error_message("gate decl statements", stmt.span); + None + } + + fn compile_quantum_decl_stmt( + &mut self, + stmt: &semast::QubitDeclaration, + ) -> Option { + self.push_unimplemented_error_message("quantum decl statements", stmt.span); + None + } + + fn compile_reset_stmt(&mut self, stmt: &semast::ResetStmt) -> Option { + self.push_unimplemented_error_message("reset statements", stmt.span); + None + } + + fn compile_return_stmt(&mut self, stmt: &semast::ReturnStmt) -> Option { + self.push_unimplemented_error_message("return statements", stmt.span); + None + } + + fn compile_switch_stmt(&mut self, stmt: &semast::SwitchStmt) -> Option { + self.push_unimplemented_error_message("switch statements", stmt.span); + None + } + + fn compile_while_stmt(&mut self, stmt: &semast::WhileLoop) -> Option { + let condition = self.compile_expr(&stmt.while_condition)?; + match &*stmt.body.kind { + semast::StmtKind::Block(block) => { + let block = self.compile_block(block); + Some(build_while_stmt(condition, block, stmt.span)) + } + semast::StmtKind::Err => Some(qsast::Stmt { + id: NodeId::default(), + span: stmt.body.span, + kind: Box::new(qsast::StmtKind::Err), + }), + _ => { + let block_stmt = self.compile_stmt(&stmt.body)?; + let block = qsast::Block { + id: qsast::NodeId::default(), + stmts: list_from_iter([block_stmt]), + span: stmt.span, + }; + Some(build_while_stmt(condition, block, stmt.span)) + } + } + } + + fn compile_expr(&mut self, expr: &semast::Expr) -> Option { + let span = expr.span; + match expr.kind.as_ref() { + semast::ExprKind::Err => { + // todo: determine if we should push an error here + // Are we going to allow trying to compile a program with semantic errors? + None + } + semast::ExprKind::Ident(symbol_id) => self.compile_ident_expr(*symbol_id, span), + semast::ExprKind::IndexedIdentifier(indexed_ident) => { + self.compile_indexed_ident_expr(indexed_ident, span) + } + semast::ExprKind::UnaryOp(unary_op_expr) => self.compile_unary_op_expr(unary_op_expr), + semast::ExprKind::BinaryOp(binary_op_expr) => { + self.compile_binary_op_expr(binary_op_expr) + } + semast::ExprKind::Lit(literal_kind) => { + self.compile_literal_expr(literal_kind, expr.span) + } + semast::ExprKind::FunctionCall(function_call) => self.compile_call_expr(function_call), + semast::ExprKind::Cast(cast) => self.compile_cast_expr(cast), + semast::ExprKind::IndexExpr(index_expr) => self.compile_index_expr(index_expr), + semast::ExprKind::Paren(pexpr) => self.compile_paren_expr(pexpr, expr.span), + } + } + + fn compile_ident_expr(&mut self, symbol_id: SymbolId, span: Span) -> Option { + let symbol = &self.symbols[symbol_id]; + let expr = match symbol.name.as_str() { + "euler" | "ℇ" => build_math_call_no_params("E", span), + "pi" | "π" => build_math_call_no_params("PI", span), + "tau" | "τ" => { + let expr = build_math_call_no_params("PI", span); + qsast::Expr { + kind: Box::new(qsast::ExprKind::BinOp( + qsast::BinOp::Mul, + Box::new(build_lit_double_expr(2.0, span)), + Box::new(expr), + )), + span, + id: qsast::NodeId::default(), + } + } + _ => build_path_ident_expr(&symbol.name, span, span), + }; + Some(expr) + } + + /// The lowerer eliminated indexed identifiers with zero indices. + /// So we are safe to assume that the indices are non-empty. + fn compile_indexed_ident_expr( + &mut self, + indexed_ident: &IndexedIdent, + span: Span, + ) -> Option { + let index: Vec<_> = indexed_ident + .indices + .iter() + .filter_map(|elem| self.compile_index_element(elem)) + .collect(); + + if index.len() != 1 { + self.push_unimplemented_error_message( + "multi-dimensional array index expressions", + span, + ); + return None; + } + + let symbol = &self.symbols[indexed_ident.symbol_id]; + + let ident = + build_path_ident_expr(&symbol.name, indexed_ident.name_span, indexed_ident.span); + let expr = qsast::Expr { + id: qsast::NodeId::default(), + span, + kind: Box::new(qsast::ExprKind::Index( + Box::new(ident), + Box::new(index[0].clone()), + )), + }; + Some(expr) + } + + fn compile_unary_op_expr(&mut self, unary: &UnaryOpExpr) -> Option { + match unary.op { + semast::UnaryOp::Neg => self.compile_neg_expr(&unary.expr, unary.span), + semast::UnaryOp::NotB => self.compile_bitwise_not_expr(&unary.expr, unary.span), + semast::UnaryOp::NotL => self.compile_logical_not_expr(&unary.expr, unary.span), + } + } + fn compile_neg_expr(&mut self, expr: &Expr, span: Span) -> Option { + let expr = self.compile_expr(expr)?; + Some(build_unary_op_expr(qsast::UnOp::Neg, expr, span)) + } + + fn compile_bitwise_not_expr(&mut self, expr: &Expr, span: Span) -> Option { + let expr = self.compile_expr(expr)?; + Some(build_unary_op_expr(qsast::UnOp::NotB, expr, span)) + } + + fn compile_logical_not_expr(&mut self, expr: &Expr, span: Span) -> Option { + let expr = self.compile_expr(expr)?; + Some(build_unary_op_expr(qsast::UnOp::NotL, expr, span)) + } + + fn compile_binary_op_expr(&mut self, binary: &BinaryOpExpr) -> Option { + let lhs = self.compile_expr(&binary.lhs); + let rhs = self.compile_expr(&binary.rhs); + let (lhs, rhs) = (lhs?, rhs?); + let op = Self::map_bin_op(binary.op); + let is_assignment = false; + Some(build_binary_expr( + is_assignment, + op, + lhs, + rhs, + binary.span(), + )) + } + + fn compile_literal_expr(&mut self, lit: &LiteralKind, span: Span) -> Option { + match lit { + LiteralKind::Array(value) => self.compile_array_literal(value, span), + LiteralKind::Bitstring(big_int, width) => { + Self::compile_bitstring_literal(big_int, *width, span) + } + LiteralKind::Bool(value) => Self::compile_bool_literal(*value, span), + LiteralKind::Duration(value, time_unit) => { + self.compile_duration_literal(*value, *time_unit, span) + } + LiteralKind::Float(value) => Self::compile_float_literal(*value, span), + LiteralKind::Complex(real, imag) => Self::compile_complex_literal(*real, *imag, span), + LiteralKind::Int(value) => Self::compile_int_literal(*value, span), + LiteralKind::BigInt(value) => Self::compile_bigint_literal(value, span), + LiteralKind::String(value) => self.compile_string_literal(value, span), + } + } + + fn compile_call_expr(&mut self, call: &FunctionCall) -> Option { + self.push_unimplemented_error_message("function call expresssions", call.span); + None + } + + fn compile_cast_expr(&mut self, cast: &Cast) -> Option { + let expr = self.compile_expr(&cast.expr)?; + let cast_expr = match cast.expr.ty { + crate::semantic::types::Type::Bit(_) => { + self.cast_bit_expr_to_ty(expr, &cast.expr.ty, &cast.ty, cast.span) + } + crate::semantic::types::Type::Bool(_) => { + self.cast_bool_expr_to_ty(expr, &cast.expr.ty, &cast.ty, cast.span) + } + crate::semantic::types::Type::Duration(_) => { + self.cast_duration_expr_to_ty(expr, &cast.expr.ty, &cast.ty, cast.span) + } + crate::semantic::types::Type::Angle(_, _) => { + self.cast_angle_expr_to_ty(&expr, &cast.expr.ty, &cast.ty, cast.span) + } + crate::semantic::types::Type::Complex(_, _) => { + self.cast_complex_expr_to_ty(expr, &cast.expr.ty, &cast.ty, cast.span) + } + crate::semantic::types::Type::Float(_, _) => { + self.cast_float_expr_to_ty(expr, &cast.expr.ty, &cast.ty, cast.span) + } + crate::semantic::types::Type::Int(_, _) | crate::semantic::types::Type::UInt(_, _) => { + self.cast_int_expr_to_ty(expr, &cast.expr.ty, &cast.ty, cast.span) + } + crate::semantic::types::Type::BitArray(ArrayDimensions::One(size), _) => { + self.cast_bit_array_expr_to_ty(expr, &cast.expr.ty, &cast.ty, size, cast.span) + } + _ => None, + }; + if cast_expr.is_none() { + self.push_unsupported_error_message( + format!("casting {} to {} type", cast.expr.ty, cast.ty), + cast.span, + ); + } + cast_expr + } + + fn compile_index_expr(&mut self, index: &IndexExpr) -> Option { + self.push_unimplemented_error_message("index expressions", index.span); + None + } + + fn compile_paren_expr(&mut self, paren: &Expr, span: Span) -> Option { + let expr = self.compile_expr(paren)?; + Some(wrap_expr_in_parens(expr, span)) + } + + fn compile_measure_expr(&mut self, expr: &MeasureExpr) -> Option { + self.push_unimplemented_error_message("measure expressions", expr.span); + None + } + + fn compile_index_element(&mut self, elem: &IndexElement) -> Option { + match elem { + IndexElement::DiscreteSet(discrete_set) => self.compile_discrete_set(discrete_set), + IndexElement::IndexSet(index_set) => self.compile_index_set(index_set), + } + } + + fn compile_discrete_set(&mut self, set: &DiscreteSet) -> Option { + self.push_unimplemented_error_message("discrete set expressions", set.span); + None + } + + fn compile_index_set(&mut self, set: &IndexSet) -> Option { + self.push_unimplemented_error_message("index set expressions", set.span); + None + } + + fn compile_array_literal(&mut self, _value: &List, span: Span) -> Option { + self.push_unimplemented_error_message("array literals", span); + None + } + + fn compile_bool_literal(value: bool, span: Span) -> Option { + Some(build_lit_bool_expr(value, span)) + } + + fn compile_duration_literal( + &mut self, + _value: f64, + _unit: TimeUnit, + span: Span, + ) -> Option { + self.push_unsupported_error_message("timing literals", span); + None + } + + fn compile_bitstring_literal(value: &BigInt, width: u32, span: Span) -> Option { + let width = width as usize; + let bitstring = format!("Bitstring(\"{:0>width$}\")", value.to_str_radix(2)); + Some(build_lit_result_array_expr_from_bitstring(bitstring, span)) + } + + fn compile_complex_literal(real: f64, imag: f64, span: Span) -> Option { + Some(build_lit_complex_expr( + crate::types::Complex::new(real, imag), + span, + )) + } + + fn compile_float_literal(value: f64, span: Span) -> Option { + Some(build_lit_double_expr(value, span)) + } + + fn compile_int_literal(value: i64, span: Span) -> Option { + Some(build_lit_int_expr(value, span)) + } + + fn compile_bigint_literal(value: &BigInt, span: Span) -> Option { + Some(build_lit_bigint_expr(value.clone(), span)) + } + + fn compile_string_literal(&mut self, _value: &Rc, span: Span) -> Option { + self.push_unimplemented_error_message("string literal expressions", span); + None + } + + /// Pushes an unsupported error with the supplied message. + pub fn push_unsupported_error_message>(&mut self, message: S, span: Span) { + let kind = SemanticErrorKind::NotSupported(message.as_ref().to_string(), span); + self.push_semantic_error(kind); + } + + /// Pushes an unimplemented error with the supplied message. + pub fn push_unimplemented_error_message>(&mut self, message: S, span: Span) { + let kind = SemanticErrorKind::Unimplemented(message.as_ref().to_string(), span); + self.push_semantic_error(kind); + } + + /// Pushes a semantic error with the given kind. + pub fn push_semantic_error(&mut self, kind: SemanticErrorKind) { + let kind = crate::ErrorKind::Semantic(crate::semantic::Error(kind)); + let error = crate::Error(kind); + let error = WithSource::from_map(&self.source_map, error); + self.errors.push(error); + } + + /// +----------------+-------------------------------------------------------------+ + /// | Allowed casts | Casting To | + /// +----------------+-------+-----+------+-------+-------+-----+----------+-------+ + /// | Casting From | bool | int | uint | float | angle | bit | duration | qubit | + /// +----------------+-------+-----+------+-------+-------+-----+----------+-------+ + /// | angle | Yes | No | No | No | - | Yes | No | No | + /// +----------------+-------+-----+------+-------+-------+-----+----------+-------+ + fn cast_angle_expr_to_ty( + &mut self, + expr: &qsast::Expr, + expr_ty: &crate::semantic::types::Type, + ty: &crate::semantic::types::Type, + _span: Span, + ) -> Option { + assert!(matches!(expr_ty, Type::Angle(..))); + // https://openqasm.com/language/types.html#casting-from-angle + match ty { + Type::Angle(..) => { + let msg = "Cast angle to angle"; + self.push_unimplemented_error_message(msg, expr.span); + None + } + Type::Bit(..) => { + let msg = "Cast angle to bit"; + self.push_unimplemented_error_message(msg, expr.span); + None + } + Type::BitArray(..) => { + let msg = "Cast angle to bit array"; + self.push_unimplemented_error_message(msg, expr.span); + None + } + Type::Bool(..) => { + let msg = "Cast angle to bool"; + self.push_unimplemented_error_message(msg, expr.span); + None + } + + _ => None, + } + } + + /// +----------------+-------------------------------------------------------------+ + /// | Allowed casts | Casting To | + /// +----------------+-------+-----+------+-------+-------+-----+----------+-------+ + /// | Casting From | bool | int | uint | float | angle | bit | duration | qubit | + /// +----------------+-------+-----+------+-------+-------+-----+----------+-------+ + /// | bit | Yes | Yes | Yes | No | Yes | - | No | No | + /// +----------------+-------+-----+------+-------+-------+-----+----------+-------+ + fn cast_bit_expr_to_ty( + &mut self, + expr: qsast::Expr, + expr_ty: &crate::semantic::types::Type, + ty: &crate::semantic::types::Type, + span: Span, + ) -> Option { + assert!(matches!(expr_ty, Type::Bit(..))); + // There is no operand, choosing the span of the node + // but we could use the expr span as well. + let operand_span = expr.span; + let name_span = span; + match ty { + &Type::Angle(..) => { + let msg = "Cast bit to angle"; + self.push_unimplemented_error_message(msg, expr.span); + None + } + &Type::Bool(..) => { + self.runtime |= RuntimeFunctions::ResultAsBool; + Some(build_cast_call( + RuntimeFunctions::ResultAsBool, + expr, + name_span, + operand_span, + )) + } + &Type::Float(..) => { + // The spec says that this cast isn't supported, but it + // casts to other types that case to float. For now, we'll + // say it is invalid like the spec. + None + } + &Type::Int(w, _) | &Type::UInt(w, _) => { + let function = if let Some(width) = w { + if width > 64 { + RuntimeFunctions::ResultAsBigInt + } else { + RuntimeFunctions::ResultAsInt + } + } else { + RuntimeFunctions::ResultAsInt + }; + self.runtime |= function; + let expr = build_cast_call(function, expr, name_span, operand_span); + Some(expr) + } + + _ => None, + } + } + + fn cast_bit_array_expr_to_ty( + &mut self, + expr: qsast::Expr, + expr_ty: &crate::semantic::types::Type, + ty: &crate::semantic::types::Type, + size: u32, + span: Span, + ) -> Option { + assert!(matches!( + expr_ty, + Type::BitArray(ArrayDimensions::One(_), _) + )); + + let name_span = expr.span; + let operand_span = span; + + if !matches!(ty, Type::Int(..) | Type::UInt(..)) { + return None; + } + // we know we have a bit array being cast to an int/uint + // verfiy widths + let int_width = ty.width(); + + if int_width.is_none() || (int_width == Some(size)) { + self.runtime |= RuntimeFunctions::ResultArrayAsIntBE; + let expr = build_cast_call( + RuntimeFunctions::ResultArrayAsIntBE, + expr, + name_span, + operand_span, + ); + Some(expr) + } else { + None + } + } + + /// +----------------+-------------------------------------------------------------+ + /// | Allowed casts | Casting To | + /// +----------------+-------+-----+------+-------+-------+-----+----------+-------+ + /// | Casting From | bool | int | uint | float | angle | bit | duration | qubit | + /// +----------------+-------+-----+------+-------+-------+-----+----------+-------+ + /// | bool | - | Yes | Yes | Yes | No | Yes | No | No | + /// +----------------+-------+-----+------+-------+-------+-----+----------+-------+ + fn cast_bool_expr_to_ty( + &mut self, + expr: qsast::Expr, + expr_ty: &crate::semantic::types::Type, + ty: &crate::semantic::types::Type, + span: Span, + ) -> Option { + assert!(matches!(expr_ty, Type::Bool(..))); + + let name_span = expr.span; + let operand_span = span; + match ty { + Type::Bit(..) => { + self.runtime |= RuntimeFunctions::BoolAsResult; + let expr = build_cast_call( + RuntimeFunctions::BoolAsResult, + expr, + name_span, + operand_span, + ); + Some(expr) + } + Type::Float(..) => { + self.runtime |= RuntimeFunctions::BoolAsDouble; + let expr = build_cast_call( + RuntimeFunctions::BoolAsDouble, + expr, + name_span, + operand_span, + ); + Some(expr) + } + Type::Int(w, _) | Type::UInt(w, _) => { + let function = if let Some(width) = w { + if *width > 64 { + RuntimeFunctions::BoolAsBigInt + } else { + RuntimeFunctions::BoolAsInt + } + } else { + RuntimeFunctions::BoolAsInt + }; + self.runtime |= function; + let expr = build_cast_call(function, expr, name_span, operand_span); + Some(expr) + } + _ => None, + } + } + + fn cast_complex_expr_to_ty( + &mut self, + _expr: qsast::Expr, + _expr_ty: &crate::semantic::types::Type, + _ty: &crate::semantic::types::Type, + span: Span, + ) -> Option { + self.push_unimplemented_error_message("cast complex expressions", span); + None + } + + fn cast_duration_expr_to_ty( + &mut self, + _expr: qsast::Expr, + _expr_ty: &crate::semantic::types::Type, + _ty: &crate::semantic::types::Type, + span: Span, + ) -> Option { + self.push_unimplemented_error_message("cast duration expressions", span); + None + } + + /// +----------------+-------------------------------------------------------------+ + /// | Allowed casts | Casting To | + /// +----------------+-------+-----+------+-------+-------+-----+----------+-------+ + /// | Casting From | bool | int | uint | float | angle | bit | duration | qubit | + /// +----------------+-------+-----+------+-------+-------+-----+----------+-------+ + /// | float | Yes | Yes | Yes | - | Yes | No | No | No | + /// +----------------+-------+-----+------+-------+-------+-----+----------+-------+ + /// + /// Additional cast to complex + fn cast_float_expr_to_ty( + &mut self, + expr: qsast::Expr, + expr_ty: &crate::semantic::types::Type, + ty: &crate::semantic::types::Type, + span: Span, + ) -> Option { + assert!(matches!(expr_ty, Type::Float(..))); + match ty { + &Type::Complex(..) => { + let expr = build_complex_from_expr(expr); + Some(expr) + } + &Type::Angle(..) => { + let msg = "Cast float to angle"; + self.push_unimplemented_error_message(msg, expr.span); + None + } + &Type::Int(w, _) | &Type::UInt(w, _) => { + let expr = build_math_call_from_exprs("Truncate", vec![expr], span); + let expr = if let Some(w) = w { + if w > 64 { + build_convert_call_expr(expr, "IntAsBigInt") + } else { + expr + } + } else { + expr + }; + + Some(expr) + } + &Type::Bool(..) => { + let span = expr.span; + let expr = build_math_call_from_exprs("Truncate", vec![expr], span); + let const_int_zero_expr = build_lit_int_expr(0, span); + let qsop = qsast::BinOp::Eq; + let cond = build_binary_expr(false, qsop, expr, const_int_zero_expr, span); + let coerce_expr = build_if_expr_then_expr_else_expr( + cond, + build_lit_bool_expr(false, span), + build_lit_bool_expr(true, span), + span, + ); + Some(coerce_expr) + } + _ => None, + } + } + + /// +----------------+-------------------------------------------------------------+ + /// | Allowed casts | Casting To | + /// +----------------+-------+-----+------+-------+-------+-----+----------+-------+ + /// | Casting From | bool | int | uint | float | angle | bit | duration | qubit | + /// +----------------+-------+-----+------+-------+-------+-----+----------+-------+ + /// | int | Yes | - | Yes | Yes | No | Yes | No | No | + /// +----------------+-------+-----+------+-------+-------+-----+----------+-------+ + /// | uint | Yes | Yes | - | Yes | No | Yes | No | No | + /// +----------------+-------+-----+------+-------+-------+-----+----------+-------+ + /// + /// Additional cast to ``BigInt`` + /// With the exception of casting to ``BigInt``, there is no checking for overflow, + /// widths, truncation, etc. Qiskit doesn't do these kinds of casts. For general + /// `OpenQASM` support this will need to be fleshed out. + #[allow(clippy::too_many_lines)] + fn cast_int_expr_to_ty( + &mut self, + expr: qsast::Expr, + expr_ty: &crate::semantic::types::Type, + ty: &crate::semantic::types::Type, + span: Span, + ) -> Option { + assert!(matches!(expr_ty, Type::Int(..) | Type::UInt(..))); + let name_span = expr.span; + let operand_span = span; + match ty { + Type::BitArray(dims, _) => { + self.runtime |= RuntimeFunctions::IntAsResultArrayBE; + let ArrayDimensions::One(size) = dims else { + return None; + }; + let size = i64::from(*size); + + let size_expr = build_lit_int_expr(size, Span::default()); + let expr = build_cast_call_two_params( + RuntimeFunctions::IntAsResultArrayBE, + expr, + size_expr, + name_span, + operand_span, + ); + Some(expr) + } + Type::Float(..) => { + let expr = build_convert_call_expr(expr, "IntAsDouble"); + Some(expr) + } + Type::Int(tw, _) | Type::UInt(tw, _) => { + // uint to int, or int/uint to BigInt + if let Some(tw) = tw { + if *tw > 64 { + let expr = build_convert_call_expr(expr, "IntAsBigInt"); + Some(expr) + } else { + Some(expr) + } + } else { + Some(expr) + } + } + Type::Bool(..) => { + let expr_span = expr.span; + let const_int_zero_expr = build_lit_int_expr(0, expr.span); + let qsop = qsast::BinOp::Eq; + let cond = build_binary_expr(false, qsop, expr, const_int_zero_expr, expr_span); + let coerce_expr = build_if_expr_then_expr_else_expr( + cond, + build_lit_bool_expr(false, expr_span), + build_lit_bool_expr(true, expr_span), + expr_span, + ); + Some(coerce_expr) + } + Type::Bit(..) => { + let expr_span = expr.span; + let const_int_zero_expr = build_lit_int_expr(0, expr.span); + let qsop = qsast::BinOp::Eq; + let cond = build_binary_expr(false, qsop, expr, const_int_zero_expr, expr_span); + let coerce_expr = build_if_expr_then_expr_else_expr( + cond, + build_lit_result_expr(qsast::Result::One, expr_span), + build_lit_result_expr(qsast::Result::Zero, expr_span), + expr_span, + ); + Some(coerce_expr) + } + Type::Complex(..) => { + let expr = build_convert_call_expr(expr, "IntAsDouble"); + let expr = build_complex_from_expr(expr); + Some(expr) + } + _ => None, + } + } + + fn map_bin_op(op: semast::BinOp) -> qsast::BinOp { + match op { + semast::BinOp::Add => qsast::BinOp::Add, + semast::BinOp::AndB => qsast::BinOp::AndB, + semast::BinOp::AndL => qsast::BinOp::AndL, + semast::BinOp::Div => qsast::BinOp::Div, + semast::BinOp::Eq => qsast::BinOp::Eq, + semast::BinOp::Exp => qsast::BinOp::Exp, + semast::BinOp::Gt => qsast::BinOp::Gt, + semast::BinOp::Gte => qsast::BinOp::Gte, + semast::BinOp::Lt => qsast::BinOp::Lt, + semast::BinOp::Lte => qsast::BinOp::Lte, + semast::BinOp::Mod => qsast::BinOp::Mod, + semast::BinOp::Mul => qsast::BinOp::Mul, + semast::BinOp::Neq => qsast::BinOp::Neq, + semast::BinOp::OrB => qsast::BinOp::OrB, + semast::BinOp::OrL => qsast::BinOp::OrL, + semast::BinOp::Shl => qsast::BinOp::Shl, + semast::BinOp::Shr => qsast::BinOp::Shr, + semast::BinOp::Sub => qsast::BinOp::Sub, + semast::BinOp::XorB => qsast::BinOp::XorB, + } + } +} diff --git a/compiler/qsc_qasm3/src/lib.rs b/compiler/qsc_qasm3/src/lib.rs index 851252c7ee..0b58aa8c6f 100644 --- a/compiler/qsc_qasm3/src/lib.rs +++ b/compiler/qsc_qasm3/src/lib.rs @@ -7,6 +7,7 @@ mod angle; mod ast_builder; mod compile; +mod compiler; pub use compile::qasm_to_program; pub mod io; mod keyword; diff --git a/compiler/qsc_qasm3/src/parser/ast.rs b/compiler/qsc_qasm3/src/parser/ast.rs index 3dea4a2096..eafa672e70 100644 --- a/compiler/qsc_qasm3/src/parser/ast.rs +++ b/compiler/qsc_qasm3/src/parser/ast.rs @@ -326,8 +326,6 @@ pub enum StmtKind { Def(DefStmt), DefCal(DefCalStmt), Delay(DelayStmt), - /// An empty statement. - Empty, End(EndStmt), ExprStmt(ExprStmt), ExternDecl(ExternDecl), @@ -368,7 +366,6 @@ impl Display for StmtKind { StmtKind::Def(def) => write!(f, "{def}"), StmtKind::DefCal(defcal) => write!(f, "{defcal}"), StmtKind::Delay(delay) => write!(f, "{delay}"), - StmtKind::Empty => write!(f, "Empty"), StmtKind::End(end_stmt) => write!(f, "{end_stmt}"), StmtKind::ExprStmt(expr) => write!(f, "{expr}"), StmtKind::ExternDecl(decl) => write!(f, "{decl}"), @@ -529,6 +526,7 @@ impl WithSpan for Ident { #[derive(Clone, Debug)] pub struct IndexedIdent { pub span: Span, + pub index_span: Span, pub name: Ident, pub indices: List, } @@ -537,6 +535,7 @@ impl Display for IndexedIdent { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { writeln_header(f, "IndexedIdent", self.span)?; writeln_field(f, "name", &self.name)?; + writeln_field(f, "index_span", &self.index_span)?; write_list_field(f, "indices", &self.indices) } } diff --git a/compiler/qsc_qasm3/src/parser/expr.rs b/compiler/qsc_qasm3/src/parser/expr.rs index 6e775e807a..b26b9204f6 100644 --- a/compiler/qsc_qasm3/src/parser/expr.rs +++ b/compiler/qsc_qasm3/src/parser/expr.rs @@ -758,10 +758,16 @@ fn hardware_qubit(s: &mut ParserContext) -> Result { pub(crate) fn indexed_identifier(s: &mut ParserContext) -> Result { let lo = s.peek().span.lo; let name: Ident = ident(s)?; + let index_lo = s.peek().span.lo; let indices = list_from_iter(many(s, index_operand)?); - + let index_span = if indices.is_empty() { + Span::default() + } else { + s.span(index_lo) + }; Ok(IndexedIdent { span: s.span(lo), + index_span, name, indices, }) diff --git a/compiler/qsc_qasm3/src/parser/expr/tests.rs b/compiler/qsc_qasm3/src/parser/expr/tests.rs index 893f9db6fa..8bb39cccfc 100644 --- a/compiler/qsc_qasm3/src/parser/expr/tests.rs +++ b/compiler/qsc_qasm3/src/parser/expr/tests.rs @@ -1129,6 +1129,7 @@ fn indexed_identifier() { &expect![[r#" IndexedIdent [0-9]: name: Ident [0-3] "arr" + index_span: [3-9] indices: IndexSet [4-5]: values: @@ -1159,6 +1160,7 @@ fn measure_indexed_identifier() { MeasureExpr [0-7]: operand: IndexedIdent [8-20]: name: Ident [8-14] "qubits" + index_span: [14-20] indices: IndexSet [15-16]: values: diff --git a/compiler/qsc_qasm3/src/parser/mut_visit.rs b/compiler/qsc_qasm3/src/parser/mut_visit.rs index e25c5985c2..9f76826f03 100644 --- a/compiler/qsc_qasm3/src/parser/mut_visit.rs +++ b/compiler/qsc_qasm3/src/parser/mut_visit.rs @@ -328,7 +328,7 @@ pub fn walk_stmt(vis: &mut impl MutVisitor, stmt: &mut Stmt) { .iter_mut() .for_each(|s| vis.visit_annotation(s)); match &mut *stmt.kind { - StmtKind::Empty | StmtKind::Err => {} + StmtKind::Err => {} StmtKind::Alias(alias_decl_stmt) => vis.visit_alias_decl_stmt(alias_decl_stmt), StmtKind::Assign(assign_stmt) => vis.visit_assign_stmt(assign_stmt), StmtKind::AssignOp(assign_op_stmt) => vis.visit_assign_op_stmt(assign_op_stmt), diff --git a/compiler/qsc_qasm3/src/parser/stmt/tests/barrier.rs b/compiler/qsc_qasm3/src/parser/stmt/tests/barrier.rs index 4bcd0c5776..7622ada484 100644 --- a/compiler/qsc_qasm3/src/parser/stmt/tests/barrier.rs +++ b/compiler/qsc_qasm3/src/parser/stmt/tests/barrier.rs @@ -17,9 +17,11 @@ fn barrier() { operands: IndexedIdent [8-9]: name: Ident [8-9] "r" + index_span: [0-0] indices: IndexedIdent [11-15]: name: Ident [11-12] "q" + index_span: [12-15] indices: IndexSet [13-14]: values: diff --git a/compiler/qsc_qasm3/src/parser/stmt/tests/block.rs b/compiler/qsc_qasm3/src/parser/stmt/tests/block.rs index de70376f6b..06773eacd5 100644 --- a/compiler/qsc_qasm3/src/parser/stmt/tests/block.rs +++ b/compiler/qsc_qasm3/src/parser/stmt/tests/block.rs @@ -19,26 +19,27 @@ fn nested_blocks() { } }", &expect![[r#" - Block [5-106]: - Stmt [15-100]: - annotations: - kind: Block [15-100]: - Stmt [29-39]: - annotations: - kind: ClassicalDeclarationStmt [29-39]: - type: ScalarType [29-32]: IntType [29-32]: - size: - ident: Ident [33-34] "x" - init_expr: Expr [37-38]: Lit: Int(1) - Stmt [52-90]: - annotations: - kind: Block [52-90]: - Stmt [70-76]: - annotations: - kind: AssignStmt [70-76]: - lhs: IndexedIdent [70-71]: - name: Ident [70-71] "x" - indices: - rhs: Expr [74-75]: Lit: Int(2)"#]], + Block [5-106]: + Stmt [15-100]: + annotations: + kind: Block [15-100]: + Stmt [29-39]: + annotations: + kind: ClassicalDeclarationStmt [29-39]: + type: ScalarType [29-32]: IntType [29-32]: + size: + ident: Ident [33-34] "x" + init_expr: Expr [37-38]: Lit: Int(1) + Stmt [52-90]: + annotations: + kind: Block [52-90]: + Stmt [70-76]: + annotations: + kind: AssignStmt [70-76]: + lhs: IndexedIdent [70-71]: + name: Ident [70-71] "x" + index_span: [0-0] + indices: + rhs: Expr [74-75]: Lit: Int(2)"#]], ); } diff --git a/compiler/qsc_qasm3/src/parser/stmt/tests/box_stmt.rs b/compiler/qsc_qasm3/src/parser/stmt/tests/box_stmt.rs index 6972c345af..ac659f9903 100644 --- a/compiler/qsc_qasm3/src/parser/stmt/tests/box_stmt.rs +++ b/compiler/qsc_qasm3/src/parser/stmt/tests/box_stmt.rs @@ -30,6 +30,7 @@ fn box_stmt() { qubits: IndexedIdent [21-23]: name: Ident [21-23] "q0" + index_span: [0-0] indices: Stmt [33-44]: annotations: @@ -42,6 +43,7 @@ fn box_stmt() { qubits: IndexedIdent [41-43]: name: Ident [41-43] "q1" + index_span: [0-0] indices: "#]], ); } @@ -71,6 +73,7 @@ fn box_stmt_with_designator() { qubits: IndexedIdent [26-28]: name: Ident [26-28] "q0" + index_span: [0-0] indices: Stmt [38-49]: annotations: @@ -83,6 +86,7 @@ fn box_stmt_with_designator() { qubits: IndexedIdent [46-48]: name: Ident [46-48] "q1" + index_span: [0-0] indices: "#]], ); } diff --git a/compiler/qsc_qasm3/src/parser/stmt/tests/classical_decl.rs b/compiler/qsc_qasm3/src/parser/stmt/tests/classical_decl.rs index bdbe7580d9..c71849d8cd 100644 --- a/compiler/qsc_qasm3/src/parser/stmt/tests/classical_decl.rs +++ b/compiler/qsc_qasm3/src/parser/stmt/tests/classical_decl.rs @@ -1091,6 +1091,7 @@ fn measure_register_decl() { init_expr: MeasureExpr [10-17]: operand: IndexedIdent [18-30]: name: Ident [18-24] "qubits" + index_span: [24-30] indices: IndexSet [25-26]: values: diff --git a/compiler/qsc_qasm3/src/parser/stmt/tests/delay.rs b/compiler/qsc_qasm3/src/parser/stmt/tests/delay.rs index 2fb62a4e1f..174a38ed06 100644 --- a/compiler/qsc_qasm3/src/parser/stmt/tests/delay.rs +++ b/compiler/qsc_qasm3/src/parser/stmt/tests/delay.rs @@ -18,12 +18,14 @@ fn delay() { qubits: IndexedIdent [9-13]: name: Ident [9-10] "q" + index_span: [10-13] indices: IndexSet [11-12]: values: Expr [11-12]: Lit: Int(0) IndexedIdent [15-19]: name: Ident [15-16] "q" + index_span: [16-19] indices: IndexSet [17-18]: values: diff --git a/compiler/qsc_qasm3/src/parser/stmt/tests/expr_stmt.rs b/compiler/qsc_qasm3/src/parser/stmt/tests/expr_stmt.rs index 5fd49612b6..bfad136fb6 100644 --- a/compiler/qsc_qasm3/src/parser/stmt/tests/expr_stmt.rs +++ b/compiler/qsc_qasm3/src/parser/stmt/tests/expr_stmt.rs @@ -45,6 +45,7 @@ fn assignment() { kind: AssignStmt [0-6]: lhs: IndexedIdent [0-1]: name: Ident [0-1] "a" + index_span: [0-0] indices: rhs: Expr [4-5]: Lit: Int(1)"#]], ); @@ -61,6 +62,7 @@ fn index_assignment() { kind: AssignStmt [0-9]: lhs: IndexedIdent [0-4]: name: Ident [0-1] "a" + index_span: [1-4] indices: IndexSet [2-3]: values: @@ -80,6 +82,7 @@ fn multi_index_assignment() { kind: AssignStmt [0-12]: lhs: IndexedIdent [0-7]: name: Ident [0-1] "a" + index_span: [1-7] indices: IndexSet [2-3]: values: @@ -103,6 +106,7 @@ fn assignment_op() { op: Add lhs: IndexedIdent [0-1]: name: Ident [0-1] "a" + index_span: [0-0] indices: rhs: Expr [5-6]: Lit: Int(1)"#]], ); @@ -120,6 +124,7 @@ fn index_assignment_op() { op: Add lhs: IndexedIdent [0-4]: name: Ident [0-1] "a" + index_span: [1-4] indices: IndexSet [2-3]: values: @@ -140,6 +145,7 @@ fn multi_index_assignment_op() { op: Add lhs: IndexedIdent [0-7]: name: Ident [0-1] "a" + index_span: [1-7] indices: IndexSet [2-3]: values: @@ -162,6 +168,7 @@ fn assignment_and_unop() { kind: AssignStmt [0-12]: lhs: IndexedIdent [0-1]: name: Ident [0-1] "c" + index_span: [0-0] indices: rhs: Expr [4-11]: BinaryOpExpr: op: AndL @@ -183,6 +190,7 @@ fn assignment_unop_and() { kind: AssignStmt [0-12]: lhs: IndexedIdent [0-1]: name: Ident [0-1] "d" + index_span: [0-0] indices: rhs: Expr [4-11]: BinaryOpExpr: op: AndL diff --git a/compiler/qsc_qasm3/src/parser/stmt/tests/for_loops.rs b/compiler/qsc_qasm3/src/parser/stmt/tests/for_loops.rs index c8d5517bcc..eabf39e37b 100644 --- a/compiler/qsc_qasm3/src/parser/stmt/tests/for_loops.rs +++ b/compiler/qsc_qasm3/src/parser/stmt/tests/for_loops.rs @@ -32,6 +32,7 @@ fn simple_for_stmt() { kind: AssignStmt [38-44]: lhs: IndexedIdent [38-39]: name: Ident [38-39] "a" + index_span: [0-0] indices: rhs: Expr [42-43]: Lit: Int(0)"#]], ); @@ -82,6 +83,7 @@ fn simple_for_stmt_stmt_body() { kind: AssignStmt [36-42]: lhs: IndexedIdent [36-37]: name: Ident [36-37] "a" + index_span: [0-0] indices: rhs: Expr [40-41]: Lit: Int(0)"#]], ); @@ -114,6 +116,7 @@ fn for_stmt_iterating_over_range() { kind: AssignStmt [36-42]: lhs: IndexedIdent [36-37]: name: Ident [36-37] "a" + index_span: [0-0] indices: rhs: Expr [40-41]: Lit: Int(0)"#]], ); @@ -146,6 +149,7 @@ fn for_stmt_iterating_over_range_no_step() { kind: AssignStmt [34-40]: lhs: IndexedIdent [34-35]: name: Ident [34-35] "a" + index_span: [0-0] indices: rhs: Expr [38-39]: Lit: Int(0)"#]], ); @@ -175,6 +179,7 @@ fn for_stmt_iterating_over_expr() { kind: AssignStmt [31-37]: lhs: IndexedIdent [31-32]: name: Ident [31-32] "a" + index_span: [0-0] indices: rhs: Expr [35-36]: Lit: Int(0)"#]], ); @@ -209,6 +214,7 @@ fn for_stmt_with_continue_stmt() { kind: AssignStmt [38-44]: lhs: IndexedIdent [38-39]: name: Ident [38-39] "a" + index_span: [0-0] indices: rhs: Expr [42-43]: Lit: Int(0) Stmt [53-62]: @@ -246,6 +252,7 @@ fn for_loop_with_break_stmt() { kind: AssignStmt [38-44]: lhs: IndexedIdent [38-39]: name: Ident [38-39] "a" + index_span: [0-0] indices: rhs: Expr [42-43]: Lit: Int(0) Stmt [53-59]: @@ -278,6 +285,7 @@ fn single_stmt_for_stmt() { qubits: IndexedIdent [18-19]: name: Ident [18-19] "q" + index_span: [0-0] indices: "#]], ); } @@ -311,6 +319,7 @@ fn annotations_in_single_stmt_for_stmt() { kind: AssignStmt [55-61]: lhs: IndexedIdent [55-56]: name: Ident [55-56] "x" + index_span: [0-0] indices: rhs: Expr [59-60]: Lit: Int(5)"#]], ); @@ -348,6 +357,7 @@ fn nested_single_stmt_for_stmt() { qubits: IndexedIdent [34-35]: name: Ident [34-35] "q" + index_span: [0-0] indices: "#]], ); } diff --git a/compiler/qsc_qasm3/src/parser/stmt/tests/gate_call.rs b/compiler/qsc_qasm3/src/parser/stmt/tests/gate_call.rs index 78de9c02c0..e59d1e46e7 100644 --- a/compiler/qsc_qasm3/src/parser/stmt/tests/gate_call.rs +++ b/compiler/qsc_qasm3/src/parser/stmt/tests/gate_call.rs @@ -21,6 +21,7 @@ fn gate_call() { qubits: IndexedIdent [2-4]: name: Ident [2-4] "q0" + index_span: [0-0] indices: "#]], ); } @@ -41,6 +42,7 @@ fn gate_call_qubit_register() { qubits: IndexedIdent [2-6]: name: Ident [2-3] "q" + index_span: [3-6] indices: IndexSet [4-5]: values: @@ -64,9 +66,11 @@ fn gate_multiple_qubits() { qubits: IndexedIdent [5-7]: name: Ident [5-7] "q0" + index_span: [0-0] indices: IndexedIdent [9-13]: name: Ident [9-10] "q" + index_span: [10-13] indices: IndexSet [11-12]: values: @@ -123,6 +127,7 @@ fn gate_call_with_parameters() { qubits: IndexedIdent [11-13]: name: Ident [11-13] "q0" + index_span: [0-0] indices: "#]], ); } @@ -144,6 +149,7 @@ fn gate_call_inv_modifier() { qubits: IndexedIdent [8-10]: name: Ident [8-10] "q0" + index_span: [0-0] indices: "#]], ); } @@ -170,12 +176,15 @@ fn gate_call_ctrl_inv_modifiers() { qubits: IndexedIdent [27-29]: name: Ident [27-29] "c1" + index_span: [0-0] indices: IndexedIdent [31-33]: name: Ident [31-33] "c2" + index_span: [0-0] indices: IndexedIdent [35-37]: name: Ident [35-37] "q0" + index_span: [0-0] indices: "#]], ); } @@ -217,6 +226,7 @@ fn parametrized_gate_call() { qubits: IndexedIdent [11-12]: name: Ident [11-12] "q" + index_span: [0-0] indices: "#]], ); } @@ -239,6 +249,7 @@ fn parametrized_gate_call_with_designator() { qubits: IndexedIdent [14-15]: name: Ident [14-15] "q" + index_span: [0-0] indices: "#]], ); } @@ -277,6 +288,7 @@ fn gate_call_with_designator() { qubits: IndexedIdent [7-8]: name: Ident [7-8] "q" + index_span: [0-0] indices: "#]], ); } @@ -297,6 +309,7 @@ fn gate_call_with_invalid_designator() { qubits: IndexedIdent [10-11]: name: Ident [10-11] "q" + index_span: [0-0] indices: [ diff --git a/compiler/qsc_qasm3/src/parser/stmt/tests/gphase.rs b/compiler/qsc_qasm3/src/parser/stmt/tests/gphase.rs index 6b3278918d..be32d2fc52 100644 --- a/compiler/qsc_qasm3/src/parser/stmt/tests/gphase.rs +++ b/compiler/qsc_qasm3/src/parser/stmt/tests/gphase.rs @@ -41,6 +41,7 @@ fn gphase_qubit_ident() { qubits: IndexedIdent [10-12]: name: Ident [10-12] "q0" + index_span: [0-0] indices: "#]], ); } @@ -61,6 +62,7 @@ fn gphase_qubit_register() { qubits: IndexedIdent [10-14]: name: Ident [10-11] "q" + index_span: [11-14] indices: IndexSet [12-13]: values: @@ -84,9 +86,11 @@ fn gphase_multiple_qubits() { qubits: IndexedIdent [10-12]: name: Ident [10-12] "q0" + index_span: [0-0] indices: IndexedIdent [14-18]: name: Ident [14-15] "q" + index_span: [15-18] indices: IndexSet [16-17]: values: @@ -180,6 +184,7 @@ fn gphase_ctrl_inv_modifiers() { qubits: IndexedIdent [28-30]: name: Ident [28-30] "q0" + index_span: [0-0] indices: "#]], ); } diff --git a/compiler/qsc_qasm3/src/parser/stmt/tests/if_stmt.rs b/compiler/qsc_qasm3/src/parser/stmt/tests/if_stmt.rs index f74dbc053b..e8ceb04a91 100644 --- a/compiler/qsc_qasm3/src/parser/stmt/tests/if_stmt.rs +++ b/compiler/qsc_qasm3/src/parser/stmt/tests/if_stmt.rs @@ -31,6 +31,7 @@ fn simple_if_stmt() { kind: AssignStmt [27-33]: lhs: IndexedIdent [27-28]: name: Ident [27-28] "a" + index_span: [0-0] indices: rhs: Expr [31-32]: Lit: Int(0) else_body: Stmt [45-67]: @@ -41,6 +42,7 @@ fn simple_if_stmt() { kind: AssignStmt [55-61]: lhs: IndexedIdent [55-56]: name: Ident [55-56] "a" + index_span: [0-0] indices: rhs: Expr [59-60]: Lit: Int(1)"#]], ); @@ -71,6 +73,7 @@ fn if_stmt_missing_else() { kind: AssignStmt [27-33]: lhs: IndexedIdent [27-28]: name: Ident [27-28] "a" + index_span: [0-0] indices: rhs: Expr [31-32]: Lit: Int(0) else_body: "#]], @@ -122,6 +125,7 @@ fn nested_if_stmts() { kind: AssignStmt [55-61]: lhs: IndexedIdent [55-56]: name: Ident [55-56] "a" + index_span: [0-0] indices: rhs: Expr [59-60]: Lit: Int(0) else_body: Stmt [77-107]: @@ -132,6 +136,7 @@ fn nested_if_stmts() { kind: AssignStmt [91-97]: lhs: IndexedIdent [91-92]: name: Ident [91-92] "a" + index_span: [0-0] indices: rhs: Expr [95-96]: Lit: Int(1) else_body: Stmt [119-215]: @@ -152,6 +157,7 @@ fn nested_if_stmts() { kind: AssignStmt [157-163]: lhs: IndexedIdent [157-158]: name: Ident [157-158] "a" + index_span: [0-0] indices: rhs: Expr [161-162]: Lit: Int(2) else_body: Stmt [179-209]: @@ -162,6 +168,7 @@ fn nested_if_stmts() { kind: AssignStmt [193-199]: lhs: IndexedIdent [193-194]: name: Ident [193-194] "a" + index_span: [0-0] indices: rhs: Expr [197-198]: Lit: Int(3)"#]], ); @@ -187,6 +194,7 @@ fn single_stmt_if_stmt() { qubits: IndexedIdent [9-10]: name: Ident [9-10] "q" + index_span: [0-0] indices: else_body: "#]], ); @@ -217,6 +225,7 @@ fn annotations_in_single_stmt_if_stmt() { kind: AssignStmt [46-52]: lhs: IndexedIdent [46-47]: name: Ident [46-47] "x" + index_span: [0-0] indices: rhs: Expr [50-51]: Lit: Int(5) else_body: "#]], @@ -247,6 +256,7 @@ fn nested_single_stmt_if_stmt() { qubits: IndexedIdent [16-17]: name: Ident [16-17] "q" + index_span: [0-0] indices: else_body: else_body: "#]], @@ -277,6 +287,7 @@ fn nested_single_stmt_if_else_stmt() { qubits: IndexedIdent [16-17]: name: Ident [16-17] "q" + index_span: [0-0] indices: else_body: Stmt [24-42]: annotations: @@ -296,6 +307,7 @@ fn nested_single_stmt_if_else_stmt() { qubits: IndexedIdent [40-41]: name: Ident [40-41] "q" + index_span: [0-0] indices: else_body: else_body: @@ -323,6 +335,7 @@ fn single_stmt_if_stmt_else_stmt() { qubits: IndexedIdent [9-10]: name: Ident [9-10] "q" + index_span: [0-0] indices: else_body: Stmt [17-21]: annotations: @@ -334,6 +347,7 @@ fn single_stmt_if_stmt_else_stmt() { qubits: IndexedIdent [19-20]: name: Ident [19-20] "q" + index_span: [0-0] indices: "#]], ); } diff --git a/compiler/qsc_qasm3/src/parser/stmt/tests/measure.rs b/compiler/qsc_qasm3/src/parser/stmt/tests/measure.rs index dbf71b9d03..e58bf22e36 100644 --- a/compiler/qsc_qasm3/src/parser/stmt/tests/measure.rs +++ b/compiler/qsc_qasm3/src/parser/stmt/tests/measure.rs @@ -17,6 +17,7 @@ fn measure_identifier() { measurement: MeasureExpr [0-7]: operand: IndexedIdent [8-9]: name: Ident [8-9] "q" + index_span: [0-0] indices: target: "#]], ); @@ -34,6 +35,7 @@ fn measure_indented_ident() { measurement: MeasureExpr [0-7]: operand: IndexedIdent [8-12]: name: Ident [8-9] "q" + index_span: [9-12] indices: IndexSet [10-11]: values: @@ -69,9 +71,11 @@ fn measure_arrow_into_ident() { measurement: MeasureExpr [0-7]: operand: IndexedIdent [8-9]: name: Ident [8-9] "q" + index_span: [0-0] indices: target: IndexedIdent [13-14]: name: Ident [13-14] "a" + index_span: [0-0] indices: "#]], ); } @@ -88,9 +92,11 @@ fn measure_arrow_into_indented_ident() { measurement: MeasureExpr [0-7]: operand: IndexedIdent [8-9]: name: Ident [8-9] "q" + index_span: [0-0] indices: target: IndexedIdent [13-17]: name: Ident [13-14] "a" + index_span: [14-17] indices: IndexSet [15-16]: values: diff --git a/compiler/qsc_qasm3/src/parser/stmt/tests/reset.rs b/compiler/qsc_qasm3/src/parser/stmt/tests/reset.rs index 0b144e90b4..fd56798242 100644 --- a/compiler/qsc_qasm3/src/parser/stmt/tests/reset.rs +++ b/compiler/qsc_qasm3/src/parser/stmt/tests/reset.rs @@ -16,6 +16,7 @@ fn reset_ident() { kind: ResetStmt [0-8]: operand: IndexedIdent [6-7]: name: Ident [6-7] "a" + index_span: [0-0] indices: "#]], ); } @@ -31,6 +32,7 @@ fn reset_indexed_ident() { kind: ResetStmt [0-11]: operand: IndexedIdent [6-10]: name: Ident [6-7] "a" + index_span: [7-10] indices: IndexSet [8-9]: values: diff --git a/compiler/qsc_qasm3/src/parser/stmt/tests/while_loops.rs b/compiler/qsc_qasm3/src/parser/stmt/tests/while_loops.rs index 2f54709b85..1f44485846 100644 --- a/compiler/qsc_qasm3/src/parser/stmt/tests/while_loops.rs +++ b/compiler/qsc_qasm3/src/parser/stmt/tests/while_loops.rs @@ -28,6 +28,7 @@ fn simple_while() { kind: AssignStmt [30-36]: lhs: IndexedIdent [30-31]: name: Ident [30-31] "a" + index_span: [0-0] indices: rhs: Expr [34-35]: Lit: Int(0)"#]], ); @@ -69,6 +70,7 @@ fn while_stmt_body() { kind: AssignStmt [28-34]: lhs: IndexedIdent [28-29]: name: Ident [28-29] "a" + index_span: [0-0] indices: rhs: Expr [32-33]: Lit: Int(0)"#]], ); @@ -99,6 +101,7 @@ fn while_loop_with_continue_stmt() { kind: AssignStmt [30-36]: lhs: IndexedIdent [30-31]: name: Ident [30-31] "a" + index_span: [0-0] indices: rhs: Expr [34-35]: Lit: Int(0) Stmt [45-54]: @@ -132,6 +135,7 @@ fn while_loop_with_break_stmt() { kind: AssignStmt [30-36]: lhs: IndexedIdent [30-31]: name: Ident [30-31] "a" + index_span: [0-0] indices: rhs: Expr [34-35]: Lit: Int(0) Stmt [45-51]: @@ -160,6 +164,7 @@ fn single_stmt_while_stmt() { qubits: IndexedIdent [12-13]: name: Ident [12-13] "q" + index_span: [0-0] indices: "#]], ); } @@ -189,6 +194,7 @@ fn annotations_in_single_stmt_while_stmt() { kind: AssignStmt [49-55]: lhs: IndexedIdent [49-50]: name: Ident [49-50] "x" + index_span: [0-0] indices: rhs: Expr [53-54]: Lit: Int(5)"#]], ); @@ -218,6 +224,7 @@ fn nested_single_stmt_while_stmt() { qubits: IndexedIdent [22-23]: name: Ident [22-23] "q" + index_span: [0-0] indices: "#]], ); } diff --git a/compiler/qsc_qasm3/src/semantic.rs b/compiler/qsc_qasm3/src/semantic.rs index 1682b3a953..b63db4daa0 100644 --- a/compiler/qsc_qasm3/src/semantic.rs +++ b/compiler/qsc_qasm3/src/semantic.rs @@ -46,7 +46,7 @@ impl QasmSemanticParseResult { !self.errors.is_empty() } - pub fn parse_errors(&self) -> Vec> { + pub fn sytax_errors(&self) -> Vec> { let mut self_errors = self .source .errors() @@ -72,7 +72,7 @@ impl QasmSemanticParseResult { #[must_use] pub fn all_errors(&self) -> Vec> { - let mut parse_errors = self.parse_errors(); + let mut parse_errors = self.sytax_errors(); let sem_errors = self.semantic_errors(); parse_errors.extend(sem_errors); parse_errors @@ -106,7 +106,7 @@ where R: SourceResolver, { let res = crate::parser::parse_source(source, path, resolver)?; - + let errors = res.all_errors(); // If there are syntax errors, return early if res.source.has_errors() { return Ok(QasmSemanticParseResult { @@ -114,7 +114,7 @@ where source_map: res.source_map, symbols: SymbolTable::default(), program: None, - errors: vec![], + errors, }); } diff --git a/compiler/qsc_qasm3/src/semantic/ast.rs b/compiler/qsc_qasm3/src/semantic/ast.rs index 9e004b5715..0382d51406 100644 --- a/compiler/qsc_qasm3/src/semantic/ast.rs +++ b/compiler/qsc_qasm3/src/semantic/ast.rs @@ -343,8 +343,6 @@ pub enum StmtKind { Def(DefStmt), DefCal(DefCalStmt), Delay(DelayStmt), - /// An empty statement. - Empty, End(EndStmt), ExprStmt(ExprStmt), ExternDecl(ExternDecl), @@ -353,7 +351,8 @@ pub enum StmtKind { GateCall(GateCall), GPhase(GPhase), Include(IncludeStmt), - IODeclaration(IODeclaration), + InputDeclaration(InputDeclaration), + OutputDeclaration(OutputDeclaration), Measure(MeasureStmt), Pragma(Pragma), QuantumGateDefinition(QuantumGateDefinition), @@ -381,7 +380,6 @@ impl Display for StmtKind { StmtKind::Def(def) => write!(f, "{def}"), StmtKind::DefCal(defcal) => write!(f, "{defcal}"), StmtKind::Delay(delay) => write!(f, "{delay}"), - StmtKind::Empty => write!(f, "Empty"), StmtKind::End(end_stmt) => write!(f, "{end_stmt}"), StmtKind::ExprStmt(expr) => write!(f, "{expr}"), StmtKind::ExternDecl(decl) => write!(f, "{decl}"), @@ -391,7 +389,8 @@ impl Display for StmtKind { StmtKind::If(if_stmt) => write!(f, "{if_stmt}"), StmtKind::Include(include) => write!(f, "{include}"), StmtKind::IndexedAssign(assign) => write!(f, "{assign}"), - StmtKind::IODeclaration(io) => write!(f, "{io}"), + StmtKind::InputDeclaration(io) => write!(f, "{io}"), + StmtKind::OutputDeclaration(io) => write!(f, "{io}"), StmtKind::Measure(measure) => write!(f, "{measure}"), StmtKind::Pragma(pragma) => write!(f, "{pragma}"), StmtKind::QuantumGateDefinition(gate) => write!(f, "{gate}"), @@ -533,6 +532,8 @@ impl WithSpan for Ident { #[derive(Clone, Debug)] pub struct IndexedIdent { pub span: Span, + pub name_span: Span, + pub index_span: Span, pub symbol_id: SymbolId, pub indices: List, } @@ -541,6 +542,8 @@ impl Display for IndexedIdent { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { writeln_header(f, "IndexedIdent", self.span)?; writeln_field(f, "symbol_id", &self.symbol_id)?; + writeln_field(f, "name_span", &self.name_span)?; + writeln_field(f, "index_span", &self.index_span)?; write_list_field(f, "indices", &self.indices) } } @@ -1164,16 +1167,31 @@ impl Display for ValueExpression { } #[derive(Clone, Debug)] -pub struct IODeclaration { +pub struct InputDeclaration { pub span: Span, pub symbol_id: SymbolId, +} + +impl Display for InputDeclaration { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + writeln_header(f, "InputDeclaration", self.span)?; + write_field(f, "symbol_id", &self.symbol_id) + } +} + +#[derive(Clone, Debug)] +pub struct OutputDeclaration { + pub span: Span, + pub ty_span: Span, + pub symbol_id: SymbolId, pub init_expr: Box, } -impl Display for IODeclaration { +impl Display for OutputDeclaration { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { - writeln_header(f, "IODeclaration", self.span)?; + writeln_header(f, "OutputDeclaration", self.span)?; writeln_field(f, "symbol_id", &self.symbol_id)?; + writeln_field(f, "ty_span", &self.ty_span)?; write_field(f, "init_expr", &self.init_expr) } } @@ -1432,14 +1450,16 @@ impl Display for ExprKind { #[derive(Clone, Debug)] pub struct AssignStmt { pub span: Span, - pub symbold_id: SymbolId, + pub name_span: Span, + pub symbol_id: SymbolId, pub rhs: Expr, } impl Display for AssignStmt { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { writeln_header(f, "AssignStmt", self.span)?; - writeln_field(f, "symbol_id", &self.symbold_id)?; + writeln_field(f, "symbol_id", &self.symbol_id)?; + writeln_field(f, "name_span", &self.name_span)?; write_field(f, "rhs", &self.rhs) } } @@ -1447,7 +1467,7 @@ impl Display for AssignStmt { #[derive(Clone, Debug)] pub struct IndexedAssignStmt { pub span: Span, - pub symbold_id: SymbolId, + pub symbol_id: SymbolId, pub lhs: Expr, pub rhs: Expr, } @@ -1455,7 +1475,7 @@ pub struct IndexedAssignStmt { impl Display for IndexedAssignStmt { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { writeln_header(f, "AssignStmt", self.span)?; - writeln_field(f, "symbol_id", &self.symbold_id)?; + writeln_field(f, "symbol_id", &self.symbol_id)?; writeln_field(f, "lhs", &self.rhs)?; write_field(f, "rhs", &self.rhs) } @@ -1464,7 +1484,8 @@ impl Display for IndexedAssignStmt { #[derive(Clone, Debug)] pub struct AssignOpStmt { pub span: Span, - pub symbold_id: SymbolId, + pub symbol_id: SymbolId, + pub op: BinOp, pub lhs: Expr, pub rhs: Expr, } @@ -1472,7 +1493,8 @@ pub struct AssignOpStmt { impl Display for AssignOpStmt { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { writeln_header(f, "AssignOpStmt", self.span)?; - writeln_field(f, "symbol_id", &self.symbold_id)?; + writeln_field(f, "symbol_id", &self.symbol_id)?; + writeln_field(f, "op", &self.op)?; writeln_field(f, "lhs", &self.rhs)?; write_field(f, "rhs", &self.rhs) } @@ -1480,13 +1502,14 @@ impl Display for AssignOpStmt { #[derive(Clone, Debug)] pub struct UnaryOpExpr { + pub span: Span, pub op: UnaryOp, pub expr: Expr, } impl Display for UnaryOpExpr { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { - writeln!(f, "UnaryOpExpr:")?; + writeln_header(f, "UnaryOpExpr", self.span)?; writeln_field(f, "op", &self.op)?; write_field(f, "expr", &self.expr) } @@ -1662,6 +1685,7 @@ impl Display for IndexElement { pub enum IndexSetItem { RangeDefinition(RangeDefinition), Expr(Expr), + Err, } /// This is needed to able to use `IndexSetItem` in the `seq` combinator. @@ -1672,6 +1696,7 @@ impl WithSpan for IndexSetItem { IndexSetItem::RangeDefinition(range.with_span(span)) } IndexSetItem::Expr(expr) => IndexSetItem::Expr(expr.with_span(span)), + IndexSetItem::Err => IndexSetItem::Err, } } } @@ -1681,6 +1706,7 @@ impl Display for IndexSetItem { match self { IndexSetItem::RangeDefinition(range) => write!(f, "{range}"), IndexSetItem::Expr(expr) => write!(f, "{expr}"), + IndexSetItem::Err => write!(f, "Err"), } } } @@ -1700,7 +1726,7 @@ impl Display for IOKeyword { } } -#[derive(Clone, Debug)] +#[derive(Clone, Copy, Debug)] pub enum TimeUnit { Dt, /// Nanoseconds. diff --git a/compiler/qsc_qasm3/src/semantic/error.rs b/compiler/qsc_qasm3/src/semantic/error.rs index b520e6cfe4..02df4074f1 100644 --- a/compiler/qsc_qasm3/src/semantic/error.rs +++ b/compiler/qsc_qasm3/src/semantic/error.rs @@ -50,6 +50,9 @@ pub enum SemanticErrorKind { #[error("Cannot cast expression of type {0} to type {1}")] #[diagnostic(code("Qsc.Qasm3.Compile.CannotCast"))] CannotCast(String, String, #[label] Span), + #[error("Cannot cast literal expression of type {0} to type {1}")] + #[diagnostic(code("Qsc.Qasm3.Compile.CannotCastLiteral"))] + CannotCastLiteral(String, String, #[label] Span), #[error("Cannot index variables of type {0}")] #[diagnostic(code("Qsc.Qasm3.Compile.CannotIndexType"))] CannotIndexType(String, #[label] Span), @@ -221,6 +224,9 @@ impl SemanticErrorKind { Self::AnnotationWithoutStatement(span + offset) } Self::CannotCast(lhs, rhs, span) => Self::CannotCast(lhs, rhs, span + offset), + Self::CannotCastLiteral(lhs, rhs, span) => { + Self::CannotCastLiteral(lhs, rhs, span + offset) + } Self::CastWouldCauseTruncation(lhs, rhs, span) => { Self::CastWouldCauseTruncation(lhs, rhs, span + offset) } diff --git a/compiler/qsc_qasm3/src/semantic/lowerer.rs b/compiler/qsc_qasm3/src/semantic/lowerer.rs index 51fff75839..ed887adcca 100644 --- a/compiler/qsc_qasm3/src/semantic/lowerer.rs +++ b/compiler/qsc_qasm3/src/semantic/lowerer.rs @@ -36,53 +36,25 @@ use super::{ SemanticErrorKind, }; -/// This macro allow us to apply the `?` to the inner option -/// in a situation where we have two nested options. This -/// situation is common when lowering items that were originally -/// optional. -/// -/// Example usage: -/// ```ignore -/// let item: Option = ...; -/// let item: Option> = item.as_ref().map(|s| self.lower_stmt(s)); -/// -/// // lower some other items in between -/// // ... -/// -/// // We short-circuit after lowering all the items to -/// // catch as many errors as possible before returning. -/// -/// // Note that here we are applying the `?` operator to -/// // the inner option, and not to the outer one. That is, -/// // because the outer option being `None` is not necessarily -/// // an error, e.g.: the else-body of an if_stmt. -/// // But the inner option being `None` is always an error, -/// // which occured during lowering. -/// let item: Option = short_circuit_opt_item!(item); -/// ``` -macro_rules! short_circuit_opt_item { - ($nested_opt:expr) => { - if let Some(inner_opt) = $nested_opt { - Some(inner_opt?) - } else { - None +/// Macro to create an error expression. Used when we fail to +/// lower an expression. It is assumed that an error was +/// already reported. +macro_rules! err_expr { + ($ty:expr) => { + semantic::Expr { + span: Span::default(), + kind: Box::new(semantic::ExprKind::Err), + ty: $ty, } }; -} -/// This helper function evaluates the contents of `f` within a scope -/// of kind `kind`. It's purpose is to avoid making the mistake of -/// returning early from a function while a scope is pushed, for example, -/// by using the `?` operator. -#[must_use] -fn with_scope(lw: &mut Lowerer, kind: ScopeKind, f: F) -> Option -where - F: FnOnce(&mut Lowerer) -> Option, -{ - lw.symbols.push_scope(kind); - let res = f(lw); - lw.symbols.pop_scope(); - res + ($ty:expr, $span:expr) => { + semantic::Expr { + span: $span, + kind: Box::new(semantic::ExprKind::Err), + ty: $ty, + } + }; } pub(super) struct Lowerer { @@ -122,6 +94,11 @@ impl Lowerer { self.lower_source(source); + assert!( + self.symbols.is_current_scope_global(), + "Scope stack was non popped correctly." + ); + let program = semantic::Program { version: self.version, statements: syntax::list_from_iter(self.stmts), @@ -169,121 +146,79 @@ impl Lowerer { let mut includes = source.includes().iter(); for stmt in &source.program().statements { - match &*stmt.kind { - syntax::StmtKind::Include(include) => { - // if we are not in the root we should not be able to include - // as this is a limitation of the QASM3 language - if !self.symbols.is_current_scope_global() { - let kind = SemanticErrorKind::IncludeNotInGlobalScope( - include.filename.to_string(), - include.span, - ); - self.push_semantic_error(kind); - continue; - } - - // special case for stdgates.inc - // it won't be in the includes list - if include.filename.to_lowercase() == "stdgates.inc" { - self.define_stdgates(include); - continue; - } - - let include = includes.next().expect("missing include"); - self.lower_source(include); + if let syntax::StmtKind::Include(include) = &*stmt.kind { + // if we are not in the root we should not be able to include + // as this is a limitation of the QASM3 language + if !self.symbols.is_current_scope_global() { + let kind = SemanticErrorKind::IncludeNotInGlobalScope( + include.filename.to_string(), + include.span, + ); + self.push_semantic_error(kind); + continue; } - _ => { - if let Some(stmt) = self.lower_stmt(stmt) { - self.stmts.push(stmt); - } + + // special case for stdgates.inc + // it won't be in the includes list + if include.filename.to_lowercase() == "stdgates.inc" { + self.define_stdgates(include); + continue; } + + let include = includes.next().expect("missing include"); + self.lower_source(include); + } else { + let stmt = self.lower_stmt(stmt); + self.stmts.push(stmt); } } } #[allow(clippy::too_many_lines)] - fn lower_stmt(&mut self, stmt: &syntax::Stmt) -> Option { + fn lower_stmt(&mut self, stmt: &syntax::Stmt) -> semantic::Stmt { let kind = match &*stmt.kind { - syntax::StmtKind::Alias(stmt) => semantic::StmtKind::Alias(self.lower_alias(stmt)?), - syntax::StmtKind::Assign(stmt) => self.lower_assign(stmt)?, - syntax::StmtKind::AssignOp(stmt) => { - semantic::StmtKind::AssignOp(self.lower_assign_op(stmt)?) - } - syntax::StmtKind::Barrier(stmt) => { - semantic::StmtKind::Barrier(self.lower_barrier(stmt)?) - } - syntax::StmtKind::Box(stmt) => semantic::StmtKind::Box(self.lower_box(stmt)?), - syntax::StmtKind::Break(stmt) => self.lower_break(stmt)?, + syntax::StmtKind::Alias(stmt) => self.lower_alias(stmt), + syntax::StmtKind::Assign(stmt) => self.lower_assign(stmt), + syntax::StmtKind::AssignOp(stmt) => self.lower_assign_op(stmt), + syntax::StmtKind::Barrier(stmt) => self.lower_barrier(stmt), + syntax::StmtKind::Box(stmt) => self.lower_box(stmt), + syntax::StmtKind::Break(stmt) => self.lower_break(stmt), syntax::StmtKind::Block(stmt) => { - semantic::StmtKind::Block(Box::new(self.lower_block(stmt)?)) - } - syntax::StmtKind::Cal(stmt) => self.lower_calibration(stmt)?, - syntax::StmtKind::CalibrationGrammar(stmt) => { - semantic::StmtKind::CalibrationGrammar(self.lower_calibration_grammar(stmt)?) - } - syntax::StmtKind::ClassicalDecl(stmt) => { - semantic::StmtKind::ClassicalDecl(self.lower_classical_decl(stmt)?) - } - syntax::StmtKind::ConstDecl(stmt) => { - semantic::StmtKind::ClassicalDecl(self.lower_const_decl(stmt)?) - } - syntax::StmtKind::Continue(stmt) => self.lower_continue_stmt(stmt)?, - syntax::StmtKind::Def(stmt) => semantic::StmtKind::Def(self.lower_def(stmt)?), - syntax::StmtKind::DefCal(stmt) => semantic::StmtKind::DefCal(self.lower_def_cal(stmt)?), - syntax::StmtKind::Delay(stmt) => semantic::StmtKind::Delay(self.lower_delay(stmt)?), - syntax::StmtKind::Empty => { - // we ignore empty statements - None? - } - syntax::StmtKind::End(stmt) => semantic::StmtKind::End(self.lower_end_stmt(stmt)?), - syntax::StmtKind::ExprStmt(stmt) => { - semantic::StmtKind::ExprStmt(self.lower_expr_stmt(stmt)?) - } - syntax::StmtKind::ExternDecl(extern_decl) => { - semantic::StmtKind::ExternDecl(self.lower_extern(extern_decl)?) - } - syntax::StmtKind::For(stmt) => semantic::StmtKind::For(self.lower_for_stmt(stmt)?), - syntax::StmtKind::If(stmt) => semantic::StmtKind::If(self.lower_if_stmt(stmt)?), - syntax::StmtKind::GateCall(stmt) => { - semantic::StmtKind::GateCall(self.lower_gate_call(stmt)?) - } - syntax::StmtKind::GPhase(stmt) => semantic::StmtKind::GPhase(self.lower_gphase(stmt)?), - syntax::StmtKind::Include(stmt) => { - semantic::StmtKind::Include(self.lower_include(stmt)?) - } - syntax::StmtKind::IODeclaration(stmt) => { - semantic::StmtKind::IODeclaration(self.lower_io_decl(stmt)?) - } - syntax::StmtKind::Measure(stmt) => { - semantic::StmtKind::Measure(self.lower_measure(stmt)?) - } - syntax::StmtKind::Pragma(stmt) => semantic::StmtKind::Pragma(self.lower_pragma(stmt)?), - syntax::StmtKind::QuantumGateDefinition(stmt) => { - semantic::StmtKind::QuantumGateDefinition(self.lower_gate_def(stmt)?) - } - syntax::StmtKind::QuantumDecl(stmt) => { - semantic::StmtKind::QuantumDecl(self.lower_quantum_decl(stmt)?) - } - syntax::StmtKind::Reset(stmt) => semantic::StmtKind::Reset(self.lower_reset(stmt)?), - syntax::StmtKind::Return(stmt) => semantic::StmtKind::Return(self.lower_return(stmt)?), - syntax::StmtKind::Switch(stmt) => semantic::StmtKind::Switch(self.lower_switch(stmt)?), - syntax::StmtKind::WhileLoop(stmt) => { - semantic::StmtKind::WhileLoop(self.lower_while_stmt(stmt)?) - } - syntax::StmtKind::Err => { - self.push_semantic_error(SemanticErrorKind::UnexpectedParserError( - "Unexpected error".to_string(), - stmt.span, - )); - return None; - } + semantic::StmtKind::Block(Box::new(self.lower_block(stmt))) + } + syntax::StmtKind::Cal(stmt) => self.lower_calibration(stmt), + syntax::StmtKind::CalibrationGrammar(stmt) => self.lower_calibration_grammar(stmt), + syntax::StmtKind::ClassicalDecl(stmt) => self.lower_classical_decl(stmt), + syntax::StmtKind::ConstDecl(stmt) => self.lower_const_decl(stmt), + syntax::StmtKind::Continue(stmt) => self.lower_continue_stmt(stmt), + syntax::StmtKind::Def(stmt) => self.lower_def(stmt), + syntax::StmtKind::DefCal(stmt) => self.lower_def_cal(stmt), + syntax::StmtKind::Delay(stmt) => self.lower_delay(stmt), + syntax::StmtKind::End(stmt) => self.lower_end_stmt(stmt), + syntax::StmtKind::ExprStmt(stmt) => self.lower_expr_stmt(stmt), + syntax::StmtKind::ExternDecl(extern_decl) => self.lower_extern(extern_decl), + syntax::StmtKind::For(stmt) => self.lower_for_stmt(stmt), + syntax::StmtKind::If(stmt) => self.lower_if_stmt(stmt), + syntax::StmtKind::GateCall(stmt) => self.lower_gate_call(stmt), + syntax::StmtKind::GPhase(stmt) => self.lower_gphase(stmt), + syntax::StmtKind::Include(stmt) => self.lower_include(stmt), + syntax::StmtKind::IODeclaration(stmt) => self.lower_io_decl(stmt), + syntax::StmtKind::Measure(stmt) => self.lower_measure(stmt), + syntax::StmtKind::Pragma(stmt) => self.lower_pragma(stmt), + syntax::StmtKind::QuantumGateDefinition(stmt) => self.lower_gate_def(stmt), + syntax::StmtKind::QuantumDecl(stmt) => self.lower_quantum_decl(stmt), + syntax::StmtKind::Reset(stmt) => self.lower_reset(stmt), + syntax::StmtKind::Return(stmt) => self.lower_return(stmt), + syntax::StmtKind::Switch(stmt) => self.lower_switch(stmt), + syntax::StmtKind::WhileLoop(stmt) => self.lower_while_stmt(stmt), + syntax::StmtKind::Err => semantic::StmtKind::Err, }; let annotations = self.lower_annotations(&stmt.annotations, &stmt.kind); - Some(semantic::Stmt { + semantic::Stmt { span: stmt.span, annotations: syntax::list_from_iter(annotations), kind: Box::new(kind), - }) + } } /// Define the standard gates in the symbol table. @@ -326,58 +261,47 @@ impl Lowerer { } } - /// Pushes a missing symbol error with the given name - /// This is a convenience method for pushing a `SemanticErrorKind::UndefinedSymbol` error. - pub fn push_missing_symbol_error>(&mut self, name: S, span: Span) { - let kind = SemanticErrorKind::UndefinedSymbol(name.as_ref().to_string(), span); - self.push_semantic_error(kind); - } - - /// Pushes a redefined symbol error with the given name and span. - /// This is a convenience method for pushing a `SemanticErrorKind::RedefinedSymbol` error. - pub fn push_redefined_symbol_error>(&mut self, name: S, span: Span) { - let kind = SemanticErrorKind::RedefinedSymbol(name.as_ref().to_string(), span); - self.push_semantic_error(kind); - } - - /// Pushes an unsupported error with the supplied message. - pub fn push_unsupported_error_message>(&mut self, message: S, span: Span) { - let kind = SemanticErrorKind::NotSupported(message.as_ref().to_string(), span); - self.push_semantic_error(kind); - } - - pub fn push_unsuported_in_this_version_error_message>( + fn try_insert_or_get_existing_symbol_id( &mut self, - message: S, - minimum_supported_version: &Version, + name: S, + symbol: Symbol, span: Span, - ) { - let message = message.as_ref().to_string(); - let msv = minimum_supported_version.to_string(); - let kind = SemanticErrorKind::NotSupportedInThisVersion(message, msv, span); - self.push_semantic_error(kind); - } - - /// Pushes an unimplemented error with the supplied message. - pub fn push_unimplemented_error_message>(&mut self, message: S, span: Span) { - let kind = SemanticErrorKind::Unimplemented(message.as_ref().to_string(), span); - self.push_semantic_error(kind); - } - - /// Pushes a semantic error with the given kind. - pub fn push_semantic_error(&mut self, kind: SemanticErrorKind) { - let kind = crate::ErrorKind::Semantic(crate::semantic::Error(kind)); - let error = self.create_err(kind); - self.errors.push(error); + ) -> super::symbols::SymbolId + where + S: AsRef, + { + let symbol_id = match self.symbols.try_insert_or_get_existing(symbol) { + Ok(symbol_id) => symbol_id, + Err(symbol_id) => { + self.push_redefined_symbol_error(name.as_ref(), span); + symbol_id + } + }; + symbol_id } - /// Creates an error from the given kind with the current source mapping. - fn create_err(&self, kind: crate::ErrorKind) -> WithSource { - let error = crate::Error(kind); - WithSource::from_map(&self.source_map, error) + fn try_get_existing_or_insert_err_symbol( + &mut self, + name: S, + span: Span, + ) -> (super::symbols::SymbolId, std::rc::Rc) + where + S: AsRef, + { + let (symbol_id, symbol) = match self + .symbols + .try_get_existing_or_insert_err_symbol(name.as_ref(), span) + { + Ok((symbol_id, symbol)) => (symbol_id, symbol), + Err((symbol_id, symbol)) => { + self.push_missing_symbol_error(name, span); + (symbol_id, symbol) + } + }; + (symbol_id, symbol) } - fn lower_alias(&mut self, alias: &syntax::AliasDeclStmt) -> Option { + fn lower_alias(&mut self, alias: &syntax::AliasDeclStmt) -> semantic::StmtKind { let name = get_identifier_name(&alias.ident); // alias statements do their types backwards, you read the right side // and assign it to the left side. @@ -385,22 +309,19 @@ impl Lowerer { let rhs = alias .exprs .iter() - .filter_map(|expr| self.lower_expr(expr)) + .map(|expr| self.lower_expr(expr)) .collect::>(); let first = rhs.first().expect("missing rhs"); let symbol = Symbol { name: name.to_string(), ty: first.ty.clone(), - qsharp_ty: self.convert_semantic_type_to_qsharp_type(&first.ty, alias.ident.span())?, + qsharp_ty: self.convert_semantic_type_to_qsharp_type(&first.ty, alias.ident.span()), span: alias.ident.span(), io_kind: IOKind::Default, }; - let symbol_id = self.symbols.insert_symbol(symbol); - if symbol_id.is_err() { - self.push_redefined_symbol_error(name, alias.ident.span()); - } + let symbol_id = self.try_insert_or_get_existing_symbol_id(name, symbol, alias.ident.span()); if rhs.iter().any(|expr| expr.ty != first.ty) { let tys = rhs @@ -410,33 +331,20 @@ impl Lowerer { .join(", "); let kind = SemanticErrorKind::InconsistentTypesInAlias(tys, alias.span); self.push_semantic_error(kind); - return None; } - let symbol_id = symbol_id.ok()?; - - if rhs.len() != alias.exprs.len() { - // we failed - return None; - } - Some(semantic::AliasDeclStmt { + semantic::StmtKind::Alias(semantic::AliasDeclStmt { span: alias.span, symbol_id, exprs: syntax::list_from_iter(rhs), }) } - fn lower_assign(&mut self, stmt: &syntax::AssignStmt) -> Option { + fn lower_assign(&mut self, stmt: &syntax::AssignStmt) -> semantic::StmtKind { if stmt.lhs.indices.is_empty() { - Some(semantic::StmtKind::Assign(self.lower_simple_assign_expr( - &stmt.lhs.name, - &stmt.rhs, - stmt.span, - )?)) + self.lower_simple_assign_expr(&stmt.lhs.name, &stmt.rhs, stmt.span) } else { - Some(semantic::StmtKind::IndexedAssign( - self.lower_indexed_assign_expr(&stmt.lhs, &stmt.rhs, stmt.span)?, - )) + self.lower_indexed_assign_expr(&stmt.lhs, &stmt.rhs, stmt.span) } } @@ -445,20 +353,22 @@ impl Lowerer { ident: &syntax::Ident, rhs: &syntax::Expr, span: Span, - ) -> Option { - let (lhs_symbol_id, lhs_symbol) = self.symbols.get_symbol_by_name(&ident.name)?; - let ty = lhs_symbol.ty.clone(); + ) -> semantic::StmtKind { + let (symbol_id, symbol) = + self.try_get_existing_or_insert_err_symbol(&ident.name, ident.span); + + let ty = symbol.ty.clone(); let rhs = self.lower_expr_with_target_type(Some(rhs), &ty, span); if ty.is_const() { let kind = SemanticErrorKind::CannotUpdateConstVariable(ident.name.to_string(), ident.span); self.push_semantic_error(kind); - return None; + return semantic::StmtKind::Err; } - let rhs = rhs?; - Some(semantic::AssignStmt { - symbold_id: lhs_symbol_id, + semantic::StmtKind::Assign(semantic::AssignStmt { + symbol_id, + name_span: ident.span, rhs, span, }) @@ -469,142 +379,137 @@ impl Lowerer { index_expr: &syntax::IndexedIdent, rhs: &syntax::Expr, span: Span, - ) -> Option { + ) -> semantic::StmtKind { let ident = index_expr.name.clone(); - let (lhs_symbol_id, lhs_symbol) = self.symbols.get_symbol_by_name(&ident.name)?; - let ty = lhs_symbol.ty.clone(); + let (symbol_id, symbol) = + self.try_get_existing_or_insert_err_symbol(&ident.name, ident.span); + + let ty = &symbol.ty; let lhs = self.lower_indexed_ident_expr(index_expr); - let rhs = self.lower_expr_with_target_type(Some(rhs), &ty, span); + let rhs = self.lower_expr_with_target_type(Some(rhs), ty, span); if ty.is_const() { let kind = SemanticErrorKind::CannotUpdateConstVariable(ident.name.to_string(), ident.span); self.push_semantic_error(kind); - return None; } - let (lhs, rhs) = (lhs?, rhs?); - - Some(semantic::IndexedAssignStmt { - symbold_id: lhs_symbol_id, + semantic::StmtKind::IndexedAssign(semantic::IndexedAssignStmt { + span, + symbol_id, lhs, rhs, - span, }) } - fn lower_assign_op(&mut self, stmt: &syntax::AssignOpStmt) -> Option { - let op = stmt.op; + fn lower_assign_op(&mut self, stmt: &syntax::AssignOpStmt) -> semantic::StmtKind { + let op = stmt.op.into(); let lhs = &stmt.lhs; let rhs = &stmt.rhs; let ident = lhs.name.clone(); - let (lhs_symbol_id, lhs_symbol) = self.symbols.get_symbol_by_name(&ident.name)?; - let ty = lhs_symbol.ty.clone(); + + let (symbol_id, symbol) = + self.try_get_existing_or_insert_err_symbol(&ident.name, ident.span); + + let ty = &symbol.ty; if ty.is_const() { let kind = SemanticErrorKind::CannotUpdateConstVariable(ident.name.to_string(), ident.span); self.push_semantic_error(kind); - // usually we'd return None here, but we'll continue to compile the rhs - // looking for more errors. There is nothing in this type of error that - // would prevent us from compiling the rhs. - return None; } let lhs = self.lower_indexed_ident_expr(lhs); - let rhs = self.lower_expr(rhs); - let (lhs, rhs) = (lhs?, rhs?); - let rhs = self.lower_binary_op_expr(op, lhs.clone(), rhs, stmt.span)?; - let rhs = self.cast_expr_to_type(&ty, &rhs, stmt.span)?; - Some(semantic::AssignOpStmt { + let rhs = self.cast_expr_to_type(ty, &rhs, stmt.span); + + semantic::StmtKind::AssignOp(semantic::AssignOpStmt { span: stmt.span, - symbold_id: lhs_symbol_id, + symbol_id, + op, lhs, rhs, }) } - fn lower_expr(&mut self, expr: &syntax::Expr) -> Option { + fn lower_expr(&mut self, expr: &syntax::Expr) -> semantic::Expr { match &*expr.kind { syntax::ExprKind::BinaryOp(bin_op_expr) => { let lhs = self.lower_expr(&bin_op_expr.lhs); let rhs = self.lower_expr(&bin_op_expr.rhs); - // once we've compiled both sides, we can check if they failed - // and return None if they did so that the error is propagated - let (lhs, rhs) = (lhs?, rhs?); self.lower_binary_op_expr(bin_op_expr.op, lhs, rhs, expr.span) } syntax::ExprKind::Cast(_) => { self.push_unimplemented_error_message("cast expr", expr.span); - None - } - syntax::ExprKind::Err => { - unreachable!("Err expr should not be lowered"); + err_expr!(Type::Err, expr.span) } + syntax::ExprKind::Err => err_expr!(Type::Err, expr.span), syntax::ExprKind::FunctionCall(_) => { self.push_unimplemented_error_message("function call expr", expr.span); - None + err_expr!(Type::Err, expr.span) } syntax::ExprKind::Ident(ident) => self.lower_ident_expr(ident), syntax::ExprKind::IndexExpr(expr) => self.lower_index_expr(expr), syntax::ExprKind::Lit(lit) => self.lower_lit_expr(lit), - syntax::ExprKind::Paren(expr) => self.lower_paren_expr(expr), + syntax::ExprKind::Paren(pexpr) => self.lower_paren_expr(pexpr, expr.span), syntax::ExprKind::UnaryOp(expr) => self.lower_unary_op_expr(expr), } } - fn lower_ident_expr(&mut self, ident: &syntax::Ident) -> Option { + fn lower_ident_expr(&mut self, ident: &syntax::Ident) -> semantic::Expr { let name = ident.name.clone(); - let Some((symbol_id, symbol)) = self.symbols.get_symbol_by_name(&name) else { - self.push_missing_symbol_error(&name, ident.span); - return None; - }; + + let (symbol_id, symbol) = self.try_get_existing_or_insert_err_symbol(&name, ident.span); let kind = semantic::ExprKind::Ident(symbol_id); - Some(semantic::Expr { + semantic::Expr { span: ident.span, kind: Box::new(kind), ty: symbol.ty.clone(), - }) + } } - fn lower_lit_expr(&mut self, expr: &syntax::Lit) -> Option { + fn lower_lit_expr(&mut self, expr: &syntax::Lit) -> semantic::Expr { let (kind, ty) = match &expr.kind { syntax::LiteralKind::BigInt(value) => { - // todo: this case is only valid when there is an integer literal + // this case is only valid when there is an integer literal // that requires more than 64 bits to represent. We should probably // introduce a new type for this as openqasm promotion rules don't // cover this case as far as I know. - (semantic::LiteralKind::BigInt(value.clone()), Type::Err) + ( + semantic::ExprKind::Lit(semantic::LiteralKind::BigInt(value.clone())), + Type::Err, + ) } syntax::LiteralKind::Bitstring(value, size) => ( - semantic::LiteralKind::Bitstring(value.clone(), *size), + semantic::ExprKind::Lit(semantic::LiteralKind::Bitstring(value.clone(), *size)), Type::BitArray(super::types::ArrayDimensions::One(*size), true), ), - syntax::LiteralKind::Bool(value) => { - (semantic::LiteralKind::Bool(*value), Type::Bool(true)) - } - syntax::LiteralKind::Int(value) => { - (semantic::LiteralKind::Int(*value), Type::Int(None, true)) - } + syntax::LiteralKind::Bool(value) => ( + semantic::ExprKind::Lit(semantic::LiteralKind::Bool(*value)), + Type::Bool(true), + ), + syntax::LiteralKind::Int(value) => ( + semantic::ExprKind::Lit(semantic::LiteralKind::Int(*value)), + Type::Int(None, true), + ), syntax::LiteralKind::Float(value) => ( - semantic::LiteralKind::Float(*value), + semantic::ExprKind::Lit(semantic::LiteralKind::Float(*value)), Type::Float(None, true), ), syntax::LiteralKind::Imaginary(value) => ( - semantic::LiteralKind::Complex(0.0, *value), + semantic::ExprKind::Lit(semantic::LiteralKind::Complex(0.0, *value)), Type::Complex(None, true), ), syntax::LiteralKind::String(_) => { self.push_unsupported_error_message("String literals", expr.span); - return None; + (semantic::ExprKind::Err, Type::Err) } syntax::LiteralKind::Duration(_, _) => { self.push_unsupported_error_message("Duration literals", expr.span); - return None; + (semantic::ExprKind::Err, Type::Err) } syntax::LiteralKind::Array(exprs) => { // array literals are only valid in classical decals (const and mut) @@ -617,88 +522,58 @@ impl Lowerer { // add support for classical decls let texprs = exprs .iter() - .filter_map(|expr| self.lower_expr(expr)) + .map(|expr| self.lower_expr(expr)) .collect::>(); - if texprs.len() != exprs.len() { - // we failed to lower all the entries and an error was pushed - return None; - } ( - semantic::LiteralKind::Array(syntax::list_from_iter(texprs)), + semantic::ExprKind::Lit(semantic::LiteralKind::Array(syntax::list_from_iter( + texprs, + ))), Type::Err, ) } }; - Some(semantic::Expr { + semantic::Expr { span: expr.span, - kind: Box::new(semantic::ExprKind::Lit(kind)), + kind: Box::new(kind), ty, - }) + } } - fn lower_paren_expr(&mut self, expr: &syntax::Expr) -> Option { - let expr = self.lower_expr(expr)?; - let span = expr.span; + fn lower_paren_expr(&mut self, expr: &syntax::Expr, span: Span) -> semantic::Expr { + let expr = self.lower_expr(expr); let ty = expr.ty.clone(); let kind = semantic::ExprKind::Paren(expr); - Some(semantic::Expr { + semantic::Expr { span, kind: Box::new(kind), ty, - }) + } } - fn lower_unary_op_expr(&mut self, expr: &syntax::UnaryOpExpr) -> Option { + fn lower_unary_op_expr(&mut self, expr: &syntax::UnaryOpExpr) -> semantic::Expr { match expr.op { - syntax::UnaryOp::Neg => { - if let syntax::ExprKind::Lit(lit) = expr.expr.kind.as_ref() { - self.lower_negated_literal_as_ty(lit, None, expr.expr.span) - } else { - let expr = self.lower_expr(&expr.expr)?; - let ty = expr.ty.clone(); - if unary_op_can_be_applied_to_type(syntax::UnaryOp::Neg, &ty) { - let span = expr.span; - let unary = semantic::UnaryOpExpr { - op: semantic::UnaryOp::Neg, - expr, - }; - Some(semantic::Expr { - span, - kind: Box::new(semantic::ExprKind::UnaryOp(unary)), - ty, - }) - } else { - let kind = SemanticErrorKind::TypeDoesNotSupportedUnaryNegation( - expr.ty.to_string(), - expr.span, - ); - self.push_semantic_error(kind); - None - } - } - } - syntax::UnaryOp::NotB => { - let expr = self.lower_expr(&expr.expr)?; + syntax::UnaryOp::Neg | syntax::UnaryOp::NotB => { + let op = expr.op; + let expr = self.lower_expr(&expr.expr); let ty = expr.ty.clone(); - if unary_op_can_be_applied_to_type(syntax::UnaryOp::NotB, &ty) { - let span = expr.span; - let unary = semantic::UnaryOpExpr { - op: semantic::UnaryOp::NotB, - expr, - }; - Some(semantic::Expr { - span, - kind: Box::new(semantic::ExprKind::UnaryOp(unary)), - ty, - }) - } else { + if !unary_op_can_be_applied_to_type(op, &ty) { let kind = SemanticErrorKind::TypeDoesNotSupportedUnaryNegation( expr.ty.to_string(), expr.span, ); self.push_semantic_error(kind); - None + } + let span = expr.span; + let unary = semantic::UnaryOpExpr { + span, + op: semantic::UnaryOp::Neg, + expr, + }; + semantic::Expr { + span, + kind: Box::new(semantic::ExprKind::UnaryOp(unary)), + ty, } } syntax::UnaryOp::NotL => { @@ -707,18 +582,19 @@ impl Lowerer { // it seems that the ! operator coerces to a bool if possible let target_ty = Type::Bool(false); let expr = - self.lower_expr_with_target_type(Some(&expr.expr), &target_ty, expr.expr.span)?; + self.lower_expr_with_target_type(Some(&expr.expr), &target_ty, expr.expr.span); let ty = expr.ty.clone(); - Some(semantic::Expr { + semantic::Expr { span: expr.span, kind: Box::new(semantic::ExprKind::UnaryOp(semantic::UnaryOpExpr { + span: expr.span, op: semantic::UnaryOp::NotL, expr, })), ty, - }) + } } } } @@ -770,88 +646,83 @@ impl Lowerer { &mut self, ty: &super::types::Type, span: Span, - ) -> Option { + ) -> crate::types::Type { let is_const = ty.is_const(); match ty { - Type::Bit(_) => Some(crate::types::Type::Result(is_const)), - Type::Qubit => Some(crate::types::Type::Qubit), + Type::Bit(_) => crate::types::Type::Result(is_const), + Type::Qubit => crate::types::Type::Qubit, Type::HardwareQubit => { let message = "HardwareQubit to Q# type"; self.push_unsupported_error_message(message, span); - None + crate::types::Type::Err } Type::Int(width, _) | Type::UInt(width, _) => { if let Some(width) = width { if *width > 64 { - Some(crate::types::Type::BigInt(is_const)) + crate::types::Type::BigInt(is_const) } else { - Some(crate::types::Type::Int(is_const)) + crate::types::Type::Int(is_const) } } else { - Some(crate::types::Type::Int(is_const)) + crate::types::Type::Int(is_const) } } - Type::Float(_, _) | Type::Angle(_, _) => Some(crate::types::Type::Double(is_const)), - Type::Complex(_, _) => Some(crate::types::Type::Complex(is_const)), - Type::Bool(_) => Some(crate::types::Type::Bool(is_const)), + Type::Float(_, _) | Type::Angle(_, _) => crate::types::Type::Double(is_const), + Type::Complex(_, _) => crate::types::Type::Complex(is_const), + Type::Bool(_) => crate::types::Type::Bool(is_const), Type::Duration(_) => { self.push_unsupported_error_message("Duration type values", span); - None + crate::types::Type::Err } Type::Stretch(_) => { self.push_unsupported_error_message("Stretch type values", span); - None + crate::types::Type::Err } - Type::BitArray(dims, _) => Some(crate::types::Type::ResultArray(dims.into(), is_const)), - Type::QubitArray(dims) => Some(crate::types::Type::QubitArray(dims.into())), + Type::BitArray(dims, _) => crate::types::Type::ResultArray(dims.into(), is_const), + Type::QubitArray(dims) => crate::types::Type::QubitArray(dims.into()), Type::IntArray(size, dims) | Type::UIntArray(size, dims) => { if let Some(size) = size { if *size > 64 { - Some(crate::types::Type::BigIntArray(dims.into(), is_const)) + crate::types::Type::BigIntArray(dims.into(), is_const) } else { - Some(crate::types::Type::IntArray(dims.into(), is_const)) + crate::types::Type::IntArray(dims.into(), is_const) } } else { - Some(crate::types::Type::IntArray(dims.into(), is_const)) + crate::types::Type::IntArray(dims.into(), is_const) } } - Type::FloatArray(_, dims) => Some(crate::types::Type::DoubleArray(dims.into())), - Type::AngleArray(_, _) => todo!("AngleArray to Q# type"), - Type::ComplexArray(_, _) => todo!("ComplexArray to Q# type"), - Type::BoolArray(dims) => Some(crate::types::Type::BoolArray(dims.into(), is_const)), - Type::Gate(cargs, qargs) => Some(crate::types::Type::Callable( - crate::types::CallableKind::Operation, - *cargs, - *qargs, - )), - Type::Range => Some(crate::types::Type::Range), - Type::Set => todo!("Set to Q# type"), - Type::Void => Some(crate::types::Type::Tuple(vec![])), + Type::FloatArray(_, dims) => crate::types::Type::DoubleArray(dims.into()), + Type::BoolArray(dims) => crate::types::Type::BoolArray(dims.into(), is_const), + Type::Gate(cargs, qargs) => { + crate::types::Type::Callable(crate::types::CallableKind::Operation, *cargs, *qargs) + } + Type::Range => crate::types::Type::Range, + Type::Void => crate::types::Type::Tuple(vec![]), _ => { let msg = format!("Converting {ty:?} to Q# type"); self.push_unimplemented_error_message(msg, span); - None + crate::types::Type::Err } } } - fn lower_barrier(&mut self, stmt: &syntax::BarrierStmt) -> Option { + fn lower_barrier(&mut self, stmt: &syntax::BarrierStmt) -> semantic::StmtKind { self.push_unimplemented_error_message("barrier stmt", stmt.span); - None + semantic::StmtKind::Err } /// The "boxable" stmts were taken from the reference parser at /// . /// Search for the definition of `Box` there, and then for all the classes /// inhereting from `QuantumStatement`. - fn lower_box(&mut self, stmt: &syntax::BoxStmt) -> Option { - let stmts = stmt + fn lower_box(&mut self, stmt: &syntax::BoxStmt) -> semantic::StmtKind { + let _stmts = stmt .body .iter() .map(|stmt| self.lower_stmt(stmt)) .collect::>(); - let mut has_invalid_stmt_kinds = false; + let mut _has_invalid_stmt_kinds = false; for stmt in &stmt.body { match &*stmt.kind { syntax::StmtKind::Barrier(_) @@ -864,72 +735,63 @@ impl Lowerer { } _ => { self.push_semantic_error(SemanticErrorKind::ClassicalStmtInBox(stmt.span)); - has_invalid_stmt_kinds = true; + _has_invalid_stmt_kinds = true; } } } if let Some(duration) = &stmt.duration { self.push_unsupported_error_message("Box with duration", duration.span); - return None; - } - - if has_invalid_stmt_kinds || stmts.len() != stmt.body.len() { - return None; } // we semantically checked the stmts, but we still need to lower them // with the correct behavior based on any pragmas that might be present self.push_unimplemented_error_message("box stmt", stmt.span); - None + semantic::StmtKind::Err } - fn lower_break(&mut self, stmt: &syntax::BreakStmt) -> Option { + fn lower_break(&mut self, stmt: &syntax::BreakStmt) -> semantic::StmtKind { self.push_unimplemented_error_message("break stmt", stmt.span); - None + semantic::StmtKind::Err } - fn lower_block(&mut self, stmt: &syntax::Block) -> Option { + fn lower_block(&mut self, stmt: &syntax::Block) -> semantic::Block { self.symbols.push_scope(ScopeKind::Block); - let stmts = stmt.stmts.iter().filter_map(|stmt| self.lower_stmt(stmt)); + let stmts = stmt.stmts.iter().map(|stmt| self.lower_stmt(stmt)); let stmts = list_from_iter(stmts); self.symbols.pop_scope(); - if stmts.len() != stmt.stmts.len() { - return None; - } - - Some(semantic::Block { + semantic::Block { span: stmt.span, stmts, - }) + } } - fn lower_calibration(&mut self, stmt: &syntax::CalibrationStmt) -> Option { + fn lower_calibration(&mut self, stmt: &syntax::CalibrationStmt) -> semantic::StmtKind { self.push_unimplemented_error_message("calibration stmt", stmt.span); - None + semantic::StmtKind::Err } fn lower_calibration_grammar( &mut self, stmt: &syntax::CalibrationGrammarStmt, - ) -> Option { - self.push_unimplemented_error_message("calibration stmt", stmt.span); - None + ) -> semantic::StmtKind { + self.push_unimplemented_error_message("calibration grammar stmt", stmt.span); + semantic::StmtKind::Err } fn lower_classical_decl( &mut self, stmt: &syntax::ClassicalDeclarationStmt, - ) -> Option { + ) -> semantic::StmtKind { let is_const = false; // const decls are handled separately - let ty = self.get_semantic_type_from_tydef(&stmt.ty, is_const)?; + let ty = self.get_semantic_type_from_tydef(&stmt.ty, is_const); let init_expr = stmt.init_expr.as_deref(); let ty_span = stmt.ty.span(); let stmt_span = stmt.span; let name = stmt.identifier.name.clone(); - let qsharp_ty = self.convert_semantic_type_to_qsharp_type(&ty.clone(), ty_span)?; + let qsharp_ty = self.convert_semantic_type_to_qsharp_type(&ty.clone(), ty_span); let symbol = Symbol { name: name.to_string(), ty: ty.clone(), @@ -941,28 +803,24 @@ impl Lowerer { // process the symbol and init_expr gathering any errors let init_expr = match init_expr { Some(expr) => match expr { - syntax::ValueExpression::Expr(expr) => self - .lower_expr_with_target_type(Some(expr), &ty, stmt_span) - .map(semantic::ValueExpression::Expr), - syntax::ValueExpression::Measurement(measure_expr) => self - .lower_measure_expr_with_target_type(measure_expr, &ty, stmt_span) - .map(semantic::ValueExpression::Measurement), + syntax::ValueExpression::Expr(expr) => semantic::ValueExpression::Expr( + self.lower_expr_with_target_type(Some(expr), &ty, stmt_span), + ), + syntax::ValueExpression::Measurement(measure_expr) => { + semantic::ValueExpression::Measurement( + self.lower_measure_expr(measure_expr, stmt_span), + ) + } }, - None => self - .lower_expr_with_target_type(None, &ty, stmt_span) - .map(semantic::ValueExpression::Expr), - }; - - let Ok(symbol_id) = self.symbols.insert_symbol(symbol) else { - self.push_redefined_symbol_error(&name, stmt.identifier.span); - return None; + None => semantic::ValueExpression::Expr( + self.lower_expr_with_target_type(None, &ty, stmt_span), + ), }; - // even if init_expr was None, Q# semantics require that we have an initial value - // for classical declarations. So if None is returned we hit an error with the expression. - let init_expr = init_expr?; + let symbol_id = + self.try_insert_or_get_existing_symbol_id(name, symbol, stmt.identifier.span); - Some(semantic::ClassicalDeclarationStmt { + semantic::StmtKind::ClassicalDecl(semantic::ClassicalDeclarationStmt { span: stmt_span, ty_span, symbol_id, @@ -970,15 +828,12 @@ impl Lowerer { }) } - fn lower_const_decl( - &mut self, - stmt: &syntax::ConstantDeclStmt, - ) -> Option { + fn lower_const_decl(&mut self, stmt: &syntax::ConstantDeclStmt) -> semantic::StmtKind { let is_const = true; - let ty = self.get_semantic_type_from_tydef(&stmt.ty, is_const)?; + let ty = self.get_semantic_type_from_tydef(&stmt.ty, is_const); let ty_span = stmt.ty.span(); let name = stmt.identifier.name.clone(); - let qsharp_ty = self.convert_semantic_type_to_qsharp_type(&ty.clone(), stmt.ty.span())?; + let qsharp_ty = self.convert_semantic_type_to_qsharp_type(&ty.clone(), stmt.ty.span()); let symbol = Symbol { name: name.to_string(), ty: ty.clone(), @@ -990,16 +845,10 @@ impl Lowerer { // process the symbol and init_expr gathering any errors let init_expr = self.lower_expr_with_target_type(Some(&stmt.init_expr), &ty, stmt.span); - let Ok(symbol_id) = self.symbols.insert_symbol(symbol) else { - self.push_redefined_symbol_error(&name, stmt.identifier.span); - return None; - }; + let symbol_id = + self.try_insert_or_get_existing_symbol_id(name, symbol, stmt.identifier.span); - // even if init_expr was None, Q# semantics require that we have an initial value - // for classical declarations. So if None is returned we hit an error with the expression. - let init_expr = init_expr?; - - Some(semantic::ClassicalDeclarationStmt { + semantic::StmtKind::ClassicalDecl(semantic::ClassicalDeclarationStmt { span: stmt.span, ty_span, symbol_id, @@ -1007,86 +856,85 @@ impl Lowerer { }) } - fn lower_continue_stmt(&mut self, stmt: &syntax::ContinueStmt) -> Option { + fn lower_continue_stmt(&mut self, stmt: &syntax::ContinueStmt) -> semantic::StmtKind { self.push_unimplemented_error_message("continue stmt", stmt.span); - None + semantic::StmtKind::Err } - fn lower_def(&mut self, stmt: &syntax::DefStmt) -> Option { + fn lower_def(&mut self, stmt: &syntax::DefStmt) -> semantic::StmtKind { self.push_unimplemented_error_message("def stmt", stmt.span); - None + semantic::StmtKind::Err } - fn lower_def_cal(&mut self, stmt: &syntax::DefCalStmt) -> Option { + fn lower_def_cal(&mut self, stmt: &syntax::DefCalStmt) -> semantic::StmtKind { self.push_unimplemented_error_message("def cal stmt", stmt.span); - None + semantic::StmtKind::Err } - fn lower_delay(&mut self, stmt: &syntax::DelayStmt) -> Option { + fn lower_delay(&mut self, stmt: &syntax::DelayStmt) -> semantic::StmtKind { self.push_unimplemented_error_message("delay stmt", stmt.span); - None + semantic::StmtKind::Err } - fn lower_end_stmt(&mut self, stmt: &syntax::EndStmt) -> Option { + fn lower_end_stmt(&mut self, stmt: &syntax::EndStmt) -> semantic::StmtKind { self.push_unimplemented_error_message("end stmt", stmt.span); - None + semantic::StmtKind::Err } - fn lower_expr_stmt(&mut self, stmt: &syntax::ExprStmt) -> Option { - let expr = self.lower_expr(&stmt.expr)?; - Some(semantic::ExprStmt { + fn lower_expr_stmt(&mut self, stmt: &syntax::ExprStmt) -> semantic::StmtKind { + let expr = self.lower_expr(&stmt.expr); + semantic::StmtKind::ExprStmt(semantic::ExprStmt { span: stmt.span, expr, }) } - fn lower_extern(&mut self, stmt: &syntax::ExternDecl) -> Option { + fn lower_extern(&mut self, stmt: &syntax::ExternDecl) -> semantic::StmtKind { self.push_unimplemented_error_message("extern stmt", stmt.span); - None + semantic::StmtKind::Err } - fn lower_for_stmt(&mut self, stmt: &syntax::ForStmt) -> Option { + fn lower_for_stmt(&mut self, stmt: &syntax::ForStmt) -> semantic::StmtKind { let set_declaration = self.lower_enumerable_set(&stmt.set_declaration); // Push scope where the loop variable lives. - let (loop_variable, body) = with_scope(self, ScopeKind::Block, |lw| { - let ty = lw.get_semantic_type_from_scalar_ty(&stmt.ty, false)?; - let qsharp_ty = lw.convert_semantic_type_to_qsharp_type(&ty.clone(), stmt.ty.span)?; - let symbol = Symbol { - name: stmt.ident.name.to_string(), - span: stmt.ident.span, - ty: ty.clone(), - qsharp_ty, - io_kind: IOKind::Default, - }; + self.symbols.push_scope(ScopeKind::Block); + + let ty = self.get_semantic_type_from_scalar_ty(&stmt.ty, false); + let qsharp_ty = self.convert_semantic_type_to_qsharp_type(&ty.clone(), stmt.ty.span); + let symbol = Symbol { + name: stmt.ident.name.to_string(), + span: stmt.ident.span, + ty: ty.clone(), + qsharp_ty, + io_kind: IOKind::Default, + }; - // This is the first variable in this scope, so - // we don't need to check for redefined symbols. - let symbol_id = lw - .symbols - .insert_symbol(symbol) - .expect("this should be the first variable in this scope"); - - // We lower the body after registering the loop variable symbol_id. - // The body of the for loop could be a single statement redefining - // the loop variable, in which case we need to push a redefined - // symbol error. - let body = lw.lower_stmt(&stmt.body); - - Some((symbol_id, body?)) - })?; - - // We use the `?` operator after lowering all the fields - // to report as many errors as possible before exiting the function. - Some(semantic::ForStmt { + // This is the first variable in this scope, so + // we don't need to check for redefined symbols. + let symbol_id = self + .symbols + .insert_symbol(symbol) + .expect("this should be the first variable in this scope"); + + // We lower the body after registering the loop variable symbol_id. + // The body of the for loop could be a single statement redefining + // the loop variable, in which case we need to push a redefined + // symbol error. + let body = self.lower_stmt(&stmt.body); + + // pop the scope where the loop variable lives + self.symbols.pop_scope(); + + semantic::StmtKind::For(semantic::ForStmt { span: stmt.span, - loop_variable, - set_declaration: Box::new(set_declaration?), + loop_variable: symbol_id, + set_declaration: Box::new(set_declaration), body, }) } - fn lower_if_stmt(&mut self, stmt: &syntax::IfStmt) -> Option { + fn lower_if_stmt(&mut self, stmt: &syntax::IfStmt) -> semantic::StmtKind { let condition = self.lower_expr(&stmt.condition); let if_body = self.lower_stmt(&stmt.if_body); let else_body = stmt.else_body.as_ref().map(|body| self.lower_stmt(body)); @@ -1094,54 +942,56 @@ impl Lowerer { // The semantics of a if statement is that the condition must be // of type bool, so we try to cast it, inserting a cast if necessary. let cond_ty = Type::Bool(false); - let condition = condition?; let condition = self.cast_expr_to_type(&cond_ty, &condition, condition.span); - // We use the `?` operator after lowering all the fields - // to report as many errors as possible before exiting the function. - Some(semantic::IfStmt { + semantic::StmtKind::If(semantic::IfStmt { span: stmt.span, - condition: condition?, - if_body: if_body?, - else_body: short_circuit_opt_item!(else_body), + condition, + if_body, + else_body, }) } - fn lower_gate_call(&mut self, stmt: &syntax::GateCall) -> Option { + fn lower_gate_call(&mut self, stmt: &syntax::GateCall) -> semantic::StmtKind { self.push_unimplemented_error_message("gate call stmt", stmt.span); - None + semantic::StmtKind::Err } - fn lower_gphase(&mut self, stmt: &syntax::GPhase) -> Option { + fn lower_gphase(&mut self, stmt: &syntax::GPhase) -> semantic::StmtKind { self.push_unimplemented_error_message("gphase stmt", stmt.span); - None + semantic::StmtKind::Err } /// This function is always a indication of a error. Either the /// program is declaring include in a non-global scope or the /// include is not handled in `self.lower_source` properly. - fn lower_include(&mut self, stmt: &syntax::IncludeStmt) -> Option { + fn lower_include(&mut self, stmt: &syntax::IncludeStmt) -> semantic::StmtKind { // if we are not in the root we should not be able to include if !self.symbols.is_current_scope_global() { let name = stmt.filename.to_string(); let kind = SemanticErrorKind::IncludeNotInGlobalScope(name, stmt.span); self.push_semantic_error(kind); - return None; + return semantic::StmtKind::Err; } // if we are at the root and we have an include, we should have // already handled it and we are in an invalid state panic!("Include should have been handled in lower_source") } - fn lower_io_decl(&mut self, stmt: &syntax::IODeclaration) -> Option { + fn lower_io_decl(&mut self, stmt: &syntax::IODeclaration) -> semantic::StmtKind { let is_const = false; - let ty = self.get_semantic_type_from_tydef(&stmt.ty, is_const)?; + let ty = self.get_semantic_type_from_tydef(&stmt.ty, is_const); let io_kind = stmt.io_identifier.into(); + assert!( + io_kind == IOKind::Input || io_kind == IOKind::Output, + "IOKind should be Input or Output" + ); + let ty_span = stmt.ty.span(); let stmt_span = stmt.span; let name = stmt.ident.name.clone(); - let qsharp_ty = self.convert_semantic_type_to_qsharp_type(&ty, ty_span)?; + let qsharp_ty = self.convert_semantic_type_to_qsharp_type(&ty, ty_span); let symbol = Symbol { name: name.to_string(), ty: ty.clone(), @@ -1150,63 +1000,58 @@ impl Lowerer { io_kind, }; - let Ok(symbol_id) = self.symbols.insert_symbol(symbol) else { - self.push_redefined_symbol_error(&name, stmt.ident.span); - return None; - }; + let symbol_id = self.try_insert_or_get_existing_symbol_id(name, symbol, stmt.ident.span); - // if we have output, we need to assign a default value to declare the variable - // if we have input, we can keep return none as we would promote the variable - // to a parameter in the function signature once we generate the function - if io_kind == IOKind::Output { - let init_expr = self.get_default_value(&ty, stmt_span)?; - Some(semantic::IODeclaration { + if io_kind == IOKind::Input { + return semantic::StmtKind::InputDeclaration(semantic::InputDeclaration { span: stmt_span, symbol_id, - init_expr: Box::new(init_expr), - }) - } else { - None + }); } + + // if we have output, we need to assign a default value to declare the variable + // if we have input, we can keep return none as we would promote the variable + // to a parameter in the function signature once we generate the function + let init_expr = self.get_default_value(&ty, stmt_span); + semantic::StmtKind::OutputDeclaration(semantic::OutputDeclaration { + span: stmt_span, + ty_span, + symbol_id, + init_expr: Box::new(init_expr), + }) } - fn lower_measure(&mut self, stmt: &syntax::MeasureStmt) -> Option { + fn lower_measure(&mut self, stmt: &syntax::MeasureStmt) -> semantic::StmtKind { self.push_unimplemented_error_message("measure stmt", stmt.span); - None + semantic::StmtKind::Err } - fn lower_pragma(&mut self, stmt: &syntax::Pragma) -> Option { + fn lower_pragma(&mut self, stmt: &syntax::Pragma) -> semantic::StmtKind { self.push_unimplemented_error_message("pragma stmt", stmt.span); - None + semantic::StmtKind::Err } - fn lower_gate_def( - &mut self, - stmt: &syntax::QuantumGateDefinition, - ) -> Option { + fn lower_gate_def(&mut self, stmt: &syntax::QuantumGateDefinition) -> semantic::StmtKind { self.push_unimplemented_error_message("gate def stmt", stmt.span); - None + semantic::StmtKind::Err } - fn lower_quantum_decl( - &mut self, - stmt: &syntax::QubitDeclaration, - ) -> Option { + fn lower_quantum_decl(&mut self, stmt: &syntax::QubitDeclaration) -> semantic::StmtKind { self.push_unimplemented_error_message("qubit decl stmt", stmt.span); - None + semantic::StmtKind::Err } - fn lower_reset(&mut self, stmt: &syntax::ResetStmt) -> Option { + fn lower_reset(&mut self, stmt: &syntax::ResetStmt) -> semantic::StmtKind { self.push_unimplemented_error_message("reset stmt", stmt.span); - None + semantic::StmtKind::Err } - fn lower_return(&mut self, stmt: &syntax::ReturnStmt) -> Option { + fn lower_return(&mut self, stmt: &syntax::ReturnStmt) -> semantic::StmtKind { self.push_unimplemented_error_message("return stmt", stmt.span); - None + semantic::StmtKind::Err } - fn lower_switch(&mut self, stmt: &syntax::SwitchStmt) -> Option { + fn lower_switch(&mut self, stmt: &syntax::SwitchStmt) -> semantic::StmtKind { // Semantics of switch case is that the outer block doesn't introduce // a new scope but each case rhs does. @@ -1219,24 +1064,16 @@ impl Lowerer { let cases = stmt .cases .iter() - .filter_map(|case| self.lower_switch_case(case)) + .map(|case| self.lower_switch_case(case)) .collect::>(); let default = stmt.default.as_ref().map(|d| self.lower_block(d)); - let target = self.lower_expr(&stmt.target)?; + let target = self.lower_expr(&stmt.target); // The condition for the switch statement must be an integer type // so we use `cast_expr_to_type`, forcing the type to be an integer // type with implicit casts if necessary. let target_ty = Type::Int(None, false); - let target = self.cast_expr_to_type(&target_ty, &target, target.span)?; - - // We use the `?` operator after casting the condition to int - // to report as many errors as possible before exiting the function. - let default = short_circuit_opt_item!(default); - - if cases.len() != stmt.cases.len() { - return None; - } + let target = self.cast_expr_to_type(&target_ty, &target, target.span); // It is a parse error to have a switch statement with no cases, // even if the default block is present. Getting here means the @@ -1260,11 +1097,10 @@ impl Lowerer { &SWITCH_MINIMUM_SUPPORTED_VERSION, stmt.span, ); - return None; } } - Some(semantic::SwitchStmt { + semantic::StmtKind::Switch(semantic::SwitchStmt { span: stmt.span, target, cases: list_from_iter(cases), @@ -1272,51 +1108,43 @@ impl Lowerer { }) } - fn lower_switch_case( - &mut self, - switch_case: &syntax::SwitchCase, - ) -> Option { + fn lower_switch_case(&mut self, switch_case: &syntax::SwitchCase) -> semantic::SwitchCase { let label_ty = Type::Int(None, false); let labels = switch_case .labels .iter() - .filter_map(|label| { + .map(|label| { // The labels for each switch case must be of integer type // so we use `cast_expr_to_type`, forcing the type to be an integer // type with implicit casts if necessary. - let label = self.lower_expr(label)?; + let label = self.lower_expr(label); self.cast_expr_to_type(&label_ty, &label, label.span) }) .collect::>(); - let block = self.lower_block(&switch_case.block)?; + let block = self.lower_block(&switch_case.block); - if labels.len() != switch_case.labels.len() { - return None; - } - - Some(semantic::SwitchCase { + semantic::SwitchCase { span: switch_case.span, labels: list_from_iter(labels), block, - }) + } } - fn lower_while_stmt(&mut self, stmt: &syntax::WhileLoop) -> Option { + fn lower_while_stmt(&mut self, stmt: &syntax::WhileLoop) -> semantic::StmtKind { let while_condition = self.lower_expr(&stmt.while_condition); let body = self.lower_stmt(&stmt.body); // The semantics of a while statement is that the condition must be // of type bool, so we try to cast it, inserting a cast if necessary. let cond_ty = Type::Bool(false); - let while_condition = while_condition?; let while_condition = - self.cast_expr_to_type(&cond_ty, &while_condition, while_condition.span)?; + self.cast_expr_to_type(&cond_ty, &while_condition, while_condition.span); - Some(semantic::WhileLoop { + semantic::StmtKind::WhileLoop(semantic::WhileLoop { span: stmt.span, while_condition, - body: body?, + body, }) } @@ -1324,7 +1152,7 @@ impl Lowerer { &mut self, scalar_ty: &syntax::TypeDef, is_const: bool, - ) -> Option { + ) -> crate::semantic::types::Type { match scalar_ty { syntax::TypeDef::Scalar(scalar_type) => { self.get_semantic_type_from_scalar_ty(scalar_type, is_const) @@ -1374,61 +1202,72 @@ impl Lowerer { &mut self, scalar_ty: &syntax::ScalarType, is_const: bool, - ) -> Option { + ) -> crate::semantic::types::Type { match &scalar_ty.kind { syntax::ScalarTypeKind::Bit(bit_type) => match &bit_type.size { - Some(size) => Some(crate::semantic::types::Type::BitArray( - super::types::ArrayDimensions::One(self.get_size_designator_from_expr(size)?), - is_const, - )), - None => Some(crate::semantic::types::Type::Bit(is_const)), + Some(size) => { + let Some(size) = self.get_size_designator_from_expr(size) else { + return crate::semantic::types::Type::Err; + }; + crate::semantic::types::Type::BitArray( + super::types::ArrayDimensions::One(size), + is_const, + ) + } + None => crate::semantic::types::Type::Bit(is_const), }, syntax::ScalarTypeKind::Int(int_type) => match &int_type.size { - Some(size) => Some(crate::semantic::types::Type::Int( - Some(self.get_size_designator_from_expr(size)?), - is_const, - )), - None => Some(crate::semantic::types::Type::Int(None, is_const)), + Some(size) => { + let Some(size) = self.get_size_designator_from_expr(size) else { + return crate::semantic::types::Type::Err; + }; + crate::semantic::types::Type::Int(Some(size), is_const) + } + None => crate::semantic::types::Type::Int(None, is_const), }, syntax::ScalarTypeKind::UInt(uint_type) => match &uint_type.size { - Some(size) => Some(crate::semantic::types::Type::UInt( - Some(self.get_size_designator_from_expr(size)?), - is_const, - )), - None => Some(crate::semantic::types::Type::UInt(None, is_const)), + Some(size) => { + let Some(size) = self.get_size_designator_from_expr(size) else { + return crate::semantic::types::Type::Err; + }; + crate::semantic::types::Type::UInt(Some(size), is_const) + } + None => crate::semantic::types::Type::UInt(None, is_const), }, syntax::ScalarTypeKind::Float(float_type) => match &float_type.size { - Some(size) => Some(crate::semantic::types::Type::Float( - Some(self.get_size_designator_from_expr(size)?), - is_const, - )), - None => Some(crate::semantic::types::Type::Float(None, is_const)), + Some(size) => { + let Some(size) = self.get_size_designator_from_expr(size) else { + return crate::semantic::types::Type::Err; + }; + crate::semantic::types::Type::Float(Some(size), is_const) + } + None => crate::semantic::types::Type::Float(None, is_const), }, syntax::ScalarTypeKind::Complex(complex_type) => match &complex_type.base_size { Some(float_type) => match &float_type.size { - Some(size) => Some(crate::semantic::types::Type::Complex( - Some(self.get_size_designator_from_expr(size)?), - is_const, - )), - None => Some(crate::semantic::types::Type::Complex(None, is_const)), + Some(size) => { + let Some(size) = self.get_size_designator_from_expr(size) else { + return crate::semantic::types::Type::Err; + }; + crate::semantic::types::Type::Complex(Some(size), is_const) + } + None => crate::semantic::types::Type::Complex(None, is_const), }, - None => Some(crate::semantic::types::Type::Complex(None, is_const)), + None => crate::semantic::types::Type::Complex(None, is_const), }, syntax::ScalarTypeKind::Angle(angle_type) => match &angle_type.size { - Some(size) => Some(crate::semantic::types::Type::Angle( - Some(self.get_size_designator_from_expr(size)?), - is_const, - )), - None => Some(crate::semantic::types::Type::Angle(None, is_const)), + Some(size) => { + let Some(size) = self.get_size_designator_from_expr(size) else { + return crate::semantic::types::Type::Err; + }; + crate::semantic::types::Type::Angle(Some(size), is_const) + } + None => crate::semantic::types::Type::Angle(None, is_const), }, - syntax::ScalarTypeKind::BoolType => Some(crate::semantic::types::Type::Bool(is_const)), - syntax::ScalarTypeKind::Duration => { - Some(crate::semantic::types::Type::Duration(is_const)) - } - syntax::ScalarTypeKind::Stretch => { - Some(crate::semantic::types::Type::Stretch(is_const)) - } - syntax::ScalarTypeKind::Err => Some(crate::semantic::types::Type::Err), + syntax::ScalarTypeKind::BoolType => crate::semantic::types::Type::Bool(is_const), + syntax::ScalarTypeKind::Duration => crate::semantic::types::Type::Duration(is_const), + syntax::ScalarTypeKind::Stretch => crate::semantic::types::Type::Stretch(is_const), + syntax::ScalarTypeKind::Err => crate::semantic::types::Type::Err, } } @@ -1436,39 +1275,39 @@ impl Lowerer { &mut self, array_ty: &syntax::ArrayType, _is_const: bool, - ) -> Option { + ) -> crate::semantic::types::Type { self.push_unimplemented_error_message("semantic type from array type", array_ty.span); - None + crate::semantic::types::Type::Err } fn get_semantic_type_from_array_reference_ty( &mut self, array_ref_ty: &syntax::ArrayReferenceType, _is_const: bool, - ) -> Option { + ) -> crate::semantic::types::Type { self.push_unimplemented_error_message( "semantic type from array refence type", array_ref_ty.span, ); - None + crate::semantic::types::Type::Err } fn lower_expr_with_target_type( &mut self, expr: Option<&syntax::Expr>, ty: &Type, span: Span, - ) -> Option { + ) -> semantic::Expr { let Some(expr) = expr else { // In OpenQASM, classical variables may be uninitialized, but in Q#, // they must be initialized. We will use the default value for the type // to initialize the variable. return self.get_default_value(ty, span); }; - let rhs = self.lower_expr(expr)?; + let rhs = self.lower_expr(expr); let rhs_ty = rhs.ty.clone(); // if we have an exact type match, we can use the rhs as is if types_equal_except_const(ty, &rhs_ty) { - return Some(rhs); + return rhs; } // if the rhs is a literal, we can try to cast it to the target type @@ -1487,7 +1326,7 @@ impl Lowerer { span, ); self.push_semantic_error(kind); - return None; + return rhs; } // the lhs has a type, but the rhs may be of a different type with // implicit and explicit conversions. We need to cast the rhs to the @@ -1496,17 +1335,18 @@ impl Lowerer { self.cast_expr_to_type(ty, &rhs, span) } - fn lower_measure_expr_with_target_type( + fn lower_measure_expr( &mut self, - _expr: &syntax::MeasureExpr, - _ty: &Type, + expr: &syntax::MeasureExpr, span: Span, - ) -> Option { - self.push_unimplemented_error_message("measure expr with target type", span); - None + ) -> semantic::MeasureExpr { + semantic::MeasureExpr { + span, + operand: self.lower_gate_operand(&expr.operand), + } } - fn get_default_value(&mut self, ty: &Type, span: Span) -> Option { + fn get_default_value(&mut self, ty: &Type, span: Span) -> semantic::Expr { use semantic::Expr; use semantic::ExprKind; use semantic::LiteralKind; @@ -1517,7 +1357,7 @@ impl Lowerer { ty: ty.as_const(), } }; - match ty { + let expr = match ty { Type::Bit(_) | Type::Int(_, _) | Type::UInt(_, _) => { Some(from_lit_kind(LiteralKind::Int(0))) } @@ -1586,30 +1426,44 @@ impl Lowerer { self.push_unsupported_error_message(message, span); None } - } + }; + let Some(expr) = expr else { + return err_expr!(ty.as_const()); + }; + expr } - #[allow(clippy::too_many_lines)] fn coerce_literal_expr_to_type( + &mut self, + ty: &Type, + expr: &semantic::Expr, + kind: &semantic::LiteralKind, + ) -> semantic::Expr { + let Some(expr) = self.try_coerce_literal_expr_to_type(ty, expr, kind) else { + self.push_invalid_literal_cast_error(ty, &expr.ty, expr.span); + return expr.clone(); + }; + expr + } + #[allow(clippy::too_many_lines)] + fn try_coerce_literal_expr_to_type( &mut self, ty: &Type, rhs: &semantic::Expr, kind: &semantic::LiteralKind, ) -> Option { + assert!(matches!(*rhs.kind, semantic::ExprKind::Lit(..))); + assert!(rhs.ty.is_const(), "Literals must have const types"); + if *ty == rhs.ty { // Base case, we shouldn't have gotten here // but if we did, we can just return the rhs return Some(rhs.clone()); } + if types_equal_except_const(ty, &rhs.ty) { - // literals are always const, so we can safely return - // the const ty - if rhs.ty.is_const() { - return Some(rhs.clone()); - } - // the lsh is supposed to be const but is being initialized - // to a non-const value, we can't allow this - return None; + // lhs isn't const, but rhs is, this is allowed + return Some(rhs.clone()); } assert!(can_cast_literal(ty, &rhs.ty) || can_cast_literal_with_value_knowledge(ty, kind)); let lhs_ty = ty.clone(); @@ -1651,14 +1505,12 @@ impl Lowerer { if is_int_to_bit_array { if let semantic::LiteralKind::Int(value) = kind { if *value < 0 || *value >= (1 << size) { - // todo: error message return None; } let u_size = size as usize; let bitstring = format!("{value:0u_size$b}"); let Ok(value) = BigInt::from_str_radix(&bitstring, 2) else { - // todo: error message return None; }; @@ -1698,7 +1550,7 @@ impl Lowerer { lhs_ty.to_string(), span, )); - return None?; + return None; } None } @@ -1757,7 +1609,7 @@ impl Lowerer { span, ); self.push_semantic_error(kind); - return None?; + return None; } None } @@ -1825,86 +1677,16 @@ impl Lowerer { } } - // Rules for negating literals are different than that of expressions - // What those rules are is not clear from the spec, so this is a best guess - // based on other qasm implementations. - fn lower_negated_literal_as_ty( - &mut self, - lit: &syntax::Lit, - target_ty: Option, - span: Span, - ) -> Option { - let (kind, ty) = (match &lit.kind { - syntax::LiteralKind::Float(value) => Some(( - semantic::LiteralKind::Float(-value), - Type::Float(None, true), - )), - syntax::LiteralKind::Imaginary(value) => Some(( - semantic::LiteralKind::Complex(0.0, -value), - Type::Complex(None, true), - )), - syntax::LiteralKind::Int(value) => { - Some((semantic::LiteralKind::Int(-value), Type::Int(None, true))) - } - syntax::LiteralKind::BigInt(value) => { - let value = BigInt::from(-1) * value; - Some((semantic::LiteralKind::BigInt(value), Type::Int(None, true))) - } - syntax::LiteralKind::Duration(value, time_unit) => { - let unit = match time_unit { - syntax::TimeUnit::Dt => semantic::TimeUnit::Dt, - syntax::TimeUnit::Ms => semantic::TimeUnit::Ms, - syntax::TimeUnit::Ns => semantic::TimeUnit::Ns, - syntax::TimeUnit::S => semantic::TimeUnit::S, - syntax::TimeUnit::Us => semantic::TimeUnit::Us, - }; - Some(( - semantic::LiteralKind::Duration(-value, unit), - Type::Duration(true), - )) - } - syntax::LiteralKind::Array(_) => { - self.push_unsupported_error_message("negated array literal expressions", span); - None - } - syntax::LiteralKind::Bitstring(_, _) => { - self.push_unsupported_error_message("negated bitstring literal expressions", span); - None - } - syntax::LiteralKind::Bool(_) => { - self.push_unsupported_error_message("negated bool literal expressions", span); - None - } - syntax::LiteralKind::String(_) => { - self.push_unsupported_error_message("negated string literal expressions", span); - None - } - })?; - - let expr = semantic::Expr { - span, - kind: Box::new(semantic::ExprKind::Lit(kind.clone())), - ty, - }; - if let Some(target_ty) = target_ty { - return self.coerce_literal_expr_to_type(&target_ty, &expr, &kind); - } - Some(expr) - } - fn cast_expr_to_type( &mut self, ty: &Type, expr: &semantic::Expr, span: Span, - ) -> Option { - let cast_expr = self.try_cast_expr_to_type(ty, expr, span); - if cast_expr.is_none() { - let rhs_ty_name = format!("{:?}", expr.ty); - let lhs_ty_name = format!("{ty:?}"); - let kind = SemanticErrorKind::CannotCast(rhs_ty_name, lhs_ty_name, span); - self.push_semantic_error(kind); - } + ) -> semantic::Expr { + let Some(cast_expr) = self.try_cast_expr_to_type(ty, expr, span) else { + self.push_invalid_cast_error(ty, &expr.ty, span); + return expr.clone(); + }; cast_expr } @@ -1978,7 +1760,7 @@ impl Lowerer { fn cast_angle_expr_to_type(ty: &Type, rhs: &semantic::Expr) -> Option { assert!(matches!(rhs.ty, Type::Angle(..))); match ty { - Type::Bit(..) | Type::Bool(..) => { + Type::Angle(..) | Type::Bit(..) | Type::BitArray(..) | Type::Bool(..) => { Some(wrap_expr_in_implicit_cast_expr(ty.clone(), rhs.clone())) } _ => None, @@ -2118,7 +1900,7 @@ impl Lowerer { lhs: semantic::Expr, rhs: semantic::Expr, span: Span, - ) -> Option { + ) -> semantic::Expr { if lhs.ty.is_quantum() { let kind = SemanticErrorKind::QuantumTypesInBinaryExpression(lhs.span); self.push_semantic_error(kind); @@ -2158,14 +1940,25 @@ impl Lowerer { ); self.push_semantic_error(kind); } - return None; + let bin_expr = semantic::BinaryOpExpr { + op: op.into(), + lhs, + rhs, + }; + let kind = semantic::ExprKind::BinaryOp(bin_expr); + let expr = semantic::Expr { + span, + kind: Box::new(kind), + ty: target_ty, + }; + return expr; }; // Now that we know the effective Uint type, we can cast the lhs and rhs // so that operations can be performed on them. - let new_lhs = self.cast_expr_to_type(&ty, &lhs, lhs.span)?; + let new_lhs = self.cast_expr_to_type(&ty, &lhs, lhs.span); // only cast the rhs if the operator requires symmetric conversion let new_rhs = if Self::binop_requires_bitwise_symmetric_conversion(op) { - self.cast_expr_to_type(&ty, &rhs, rhs.span)? + self.cast_expr_to_type(&ty, &rhs, rhs.span) } else { rhs }; @@ -2182,8 +1975,8 @@ impl Lowerer { ty, }; - let final_expr = self.cast_expr_to_type(&left_type, &expr, span)?; - return Some(final_expr); + let final_expr = self.cast_expr_to_type(&left_type, &expr, span); + return final_expr; } // for int, uint, float, and complex, the lesser of the two types is cast to @@ -2195,13 +1988,13 @@ impl Lowerer { let (lhs, rhs, ty) = if matches!(op, syntax::BinOp::AndL | syntax::BinOp::OrL) { let ty = Type::Bool(false); - let new_lhs = self.cast_expr_to_type(&ty, &lhs, lhs.span)?; - let new_rhs = self.cast_expr_to_type(&ty, &rhs, rhs.span)?; + let new_lhs = self.cast_expr_to_type(&ty, &lhs, lhs.span); + let new_rhs = self.cast_expr_to_type(&ty, &rhs, rhs.span); (new_lhs, new_rhs, ty) } else if binop_requires_int_conversion_for_type(op, &left_type, &rhs.ty) { let ty = Type::Int(None, false); - let new_lhs = self.cast_expr_to_type(&ty, &lhs, lhs.span)?; - let new_rhs = self.cast_expr_to_type(&ty, &rhs, lhs.span)?; + let new_lhs = self.cast_expr_to_type(&ty, &lhs, lhs.span); + let new_rhs = self.cast_expr_to_type(&ty, &rhs, lhs.span); (new_lhs, new_rhs, ty) } else if requires_symmetric_conversion(op) { let promoted_type = try_promote_with_casting(&left_type, &right_type); @@ -2213,12 +2006,12 @@ impl Lowerer { if can_cast_literal(&promoted_type, &left_type) || can_cast_literal_with_value_knowledge(&promoted_type, kind) { - self.coerce_literal_expr_to_type(&promoted_type, &lhs, kind)? + self.coerce_literal_expr_to_type(&promoted_type, &lhs, kind) } else { - self.cast_expr_to_type(&promoted_type, &lhs, lhs.span)? + self.cast_expr_to_type(&promoted_type, &lhs, lhs.span) } } - _ => self.cast_expr_to_type(&promoted_type, &lhs, lhs.span)?, + _ => self.cast_expr_to_type(&promoted_type, &lhs, lhs.span), } }; let new_right = if promoted_type == right_type { @@ -2229,18 +2022,18 @@ impl Lowerer { if can_cast_literal(&promoted_type, &right_type) || can_cast_literal_with_value_knowledge(&promoted_type, kind) { - self.coerce_literal_expr_to_type(&promoted_type, &rhs, kind)? + self.coerce_literal_expr_to_type(&promoted_type, &rhs, kind) } else { - self.cast_expr_to_type(&promoted_type, &rhs, rhs.span)? + self.cast_expr_to_type(&promoted_type, &rhs, rhs.span) } } - _ => self.cast_expr_to_type(&promoted_type, &rhs, rhs.span)?, + _ => self.cast_expr_to_type(&promoted_type, &rhs, rhs.span), } }; (new_left, new_right, promoted_type) } else if binop_requires_symmetric_int_conversion(op) { let ty = Type::Int(None, false); - let new_rhs = self.cast_expr_to_type(&ty, &rhs, rhs.span)?; + let new_rhs = self.cast_expr_to_type(&ty, &rhs, rhs.span); (lhs, new_rhs, left_type) } else { (lhs, rhs, left_type) @@ -2256,7 +2049,7 @@ impl Lowerer { // this is going to be a call to a built-in function // that doesn't map to qasm def semantics self.push_unimplemented_error_message("complex binary exprs", span); - None + err_expr!(ty.clone(), span) } else { let kind = SemanticErrorKind::OperatorNotSupportedForTypes( format!("{op:?}"), @@ -2265,7 +2058,7 @@ impl Lowerer { span, ); self.push_semantic_error(kind); - None + err_expr!(ty.clone()) } } else { let bin_expr = semantic::BinaryOpExpr { @@ -2274,12 +2067,11 @@ impl Lowerer { rhs, }; let kind = semantic::ExprKind::BinaryOp(bin_expr); - let expr = semantic::Expr { + semantic::Expr { span, kind: Box::new(kind), ty: ty.clone(), - }; - Some(expr) + } }; let ty = match op.into() { @@ -2293,9 +2085,9 @@ impl Lowerer { | semantic::BinOp::OrL => Type::Bool(false), _ => ty, }; - let mut expr = expr?; + let mut expr = expr; expr.ty = ty; - Some(expr) + expr } fn binop_requires_bitwise_conversion(op: syntax::BinOp, left_type: &Type) -> bool { @@ -2333,125 +2125,94 @@ impl Lowerer { } // TODO: which these are parsed as different types, they are effectively the same - fn lower_index_element( - &mut self, - index: &syntax::IndexElement, - ) -> Option { + fn lower_index_element(&mut self, index: &syntax::IndexElement) -> semantic::IndexElement { match index { - syntax::IndexElement::DiscreteSet(set) => Some(semantic::IndexElement::DiscreteSet( - self.lower_discrete_set(set)?, - )), + syntax::IndexElement::DiscreteSet(set) => { + semantic::IndexElement::DiscreteSet(self.lower_discrete_set(set)) + } syntax::IndexElement::IndexSet(set) => { - Some(semantic::IndexElement::IndexSet(self.lower_index_set(set)?)) + semantic::IndexElement::IndexSet(self.lower_index_set(set)) } } } - fn lower_index_set_item( - &mut self, - item: &syntax::IndexSetItem, - ) -> Option { - let item = match item { + fn lower_index_set_item(&mut self, item: &syntax::IndexSetItem) -> semantic::IndexSetItem { + match item { syntax::IndexSetItem::RangeDefinition(range_definition) => { semantic::IndexSetItem::RangeDefinition( - self.lower_range_definition(range_definition)?, + self.lower_range_definition(range_definition), ) } - syntax::IndexSetItem::Expr(expr) => { - semantic::IndexSetItem::Expr(self.lower_expr(expr)?) - } - syntax::IndexSetItem::Err => { - unreachable!("IndexSetItem::Err should have been handled") - } - }; - Some(item) + syntax::IndexSetItem::Expr(expr) => semantic::IndexSetItem::Expr(self.lower_expr(expr)), + syntax::IndexSetItem::Err => semantic::IndexSetItem::Err, + } } - fn lower_enumerable_set( - &mut self, - set: &syntax::EnumerableSet, - ) -> Option { + fn lower_enumerable_set(&mut self, set: &syntax::EnumerableSet) -> semantic::EnumerableSet { match set { - syntax::EnumerableSet::DiscreteSet(set) => Some(semantic::EnumerableSet::DiscreteSet( - self.lower_discrete_set(set)?, - )), + syntax::EnumerableSet::DiscreteSet(set) => { + semantic::EnumerableSet::DiscreteSet(self.lower_discrete_set(set)) + } syntax::EnumerableSet::RangeDefinition(range_definition) => { - Some(semantic::EnumerableSet::RangeDefinition( - self.lower_range_definition(range_definition)?, - )) + semantic::EnumerableSet::RangeDefinition( + self.lower_range_definition(range_definition), + ) } syntax::EnumerableSet::Expr(expr) => { - Some(semantic::EnumerableSet::Expr(self.lower_expr(expr)?)) + semantic::EnumerableSet::Expr(self.lower_expr(expr)) } } } - fn lower_index_set(&mut self, set: &syntax::IndexSet) -> Option { + fn lower_index_set(&mut self, set: &syntax::IndexSet) -> semantic::IndexSet { let items = set .values .iter() - .filter_map(|expr| self.lower_index_set_item(expr)) + .map(|expr| self.lower_index_set_item(expr)) .collect::>(); - if set.values.len() != items.len() { - let kind = SemanticErrorKind::FailedToCompileExpressionList(set.span); - self.push_semantic_error(kind); - return None; - } - Some(semantic::IndexSet { + semantic::IndexSet { span: set.span, values: syntax::list_from_iter(items), - }) + } } - fn lower_discrete_set(&mut self, set: &syntax::DiscreteSet) -> Option { + fn lower_discrete_set(&mut self, set: &syntax::DiscreteSet) -> semantic::DiscreteSet { let items = set .values .iter() - .filter_map(|expr| self.lower_expr(expr)) + .map(|expr| self.lower_expr(expr)) .collect::>(); - if set.values.len() != items.len() { - let kind = SemanticErrorKind::FailedToCompileExpressionList(set.span); - self.push_semantic_error(kind); - return None; - } - - Some(semantic::DiscreteSet { + semantic::DiscreteSet { span: set.span, values: list_from_iter(items), - }) + } } fn lower_range_definition( &mut self, range_definition: &syntax::RangeDefinition, - ) -> Option { + ) -> semantic::RangeDefinition { let start = range_definition.start.as_ref().map(|e| self.lower_expr(e)); let step = range_definition.step.as_ref().map(|e| self.lower_expr(e)); let end = range_definition.end.as_ref().map(|e| self.lower_expr(e)); - let start = short_circuit_opt_item!(start); - let step = short_circuit_opt_item!(step); - let end = short_circuit_opt_item!(end); - - Some(semantic::RangeDefinition { + semantic::RangeDefinition { span: range_definition.span, start, step, end, - }) + } } - fn lower_index_expr(&mut self, expr: &syntax::IndexExpr) -> Option { + fn lower_index_expr(&mut self, expr: &syntax::IndexExpr) -> semantic::Expr { let collection = self.lower_expr(&expr.collection); let index = self.lower_index_element(&expr.index); - let collection = collection?; - let index = index?; - let indexed_ty = self.get_indexed_type(&collection.ty, expr.span, 1)?; + let indexed_ty = self.get_indexed_type(&collection.ty, expr.span, 1); - Some(semantic::Expr { + semantic::Expr { span: expr.span, kind: Box::new(semantic::ExprKind::IndexExpr(semantic::IndexExpr { span: expr.span, @@ -2459,7 +2220,7 @@ impl Lowerer { index, })), ty: indexed_ty, - }) + } } fn get_indexed_type( @@ -2467,17 +2228,17 @@ impl Lowerer { ty: &Type, span: Span, num_indices: usize, - ) -> Option { + ) -> super::types::Type { if !ty.is_array() { let kind = SemanticErrorKind::CannotIndexType(format!("{ty:?}"), span); self.push_semantic_error(kind); - return None; + return super::types::Type::Err; } if num_indices > ty.num_dims() { let kind = SemanticErrorKind::TooManyIndices(span); self.push_semantic_error(kind); - return None; + return super::types::Type::Err; } let mut indexed_ty = ty.clone(); @@ -2487,46 +2248,130 @@ impl Lowerer { // we should have caught this earlier with the two checks above let kind = SemanticErrorKind::CannotIndexType(format!("{ty:?}"), span); self.push_semantic_error(kind); - return None; + return super::types::Type::Err; }; indexed_ty = ty; } - Some(indexed_ty) + indexed_ty } - fn lower_indexed_ident_expr( - &mut self, - indexed_ident: &syntax::IndexedIdent, - ) -> Option { + /// Lower an indexed identifier expression + /// This is an identifier with *zero* or more indices + /// we tranform this into two different cases: + /// 1. An identifier with zero indices + /// 2. An identifier with one or more index + /// + /// This changes the type of expression we return to simplify downstream compilation + fn lower_indexed_ident_expr(&mut self, indexed_ident: &syntax::IndexedIdent) -> semantic::Expr { let ident = indexed_ident.name.clone(); + // if we have no indices, we can just lower the identifier + if indexed_ident.indices.is_empty() { + return self.lower_ident_expr(&ident); + } + let indices = indexed_ident .indices .iter() - .filter_map(|index| self.lower_index_element(index)) - .collect::>(); + .map(|index| self.lower_index_element(index)); + let indices = list_from_iter(indices); + + let Some((symbol_id, lhs_symbol)) = self.symbols.get_symbol_by_name(&ident.name) else { + self.push_missing_symbol_error(ident.name, ident.span); + return err_expr!(Type::Err, indexed_ident.span); + }; - let (symbol_id, lhs_symbol) = self.symbols.get_symbol_by_name(&ident.name)?; let ty = lhs_symbol.ty.clone(); // use the supplied number of indicies rathar than the number of indicies we lowered - let ty = self.get_indexed_type(&ty, indexed_ident.span, indexed_ident.indices.len())?; - - if indices.len() != indexed_ident.indices.len() { - // we failed to lower all the indices, error was already pushed - return None; - } + let ty = self.get_indexed_type(&ty, indexed_ident.span, indexed_ident.indices.len()); - Some(semantic::Expr { + semantic::Expr { span: indexed_ident.span, kind: Box::new(semantic::ExprKind::IndexedIdentifier( semantic::IndexedIdent { span: indexed_ident.span, + name_span: ident.span, + index_span: indexed_ident.index_span, symbol_id, - indices: syntax::list_from_iter(indices), + indices, }, )), ty, - }) + } + } + + #[allow(clippy::unused_self)] + fn lower_gate_operand(&mut self, operand: &syntax::GateOperand) -> semantic::GateOperand { + match operand { + syntax::GateOperand::IndexedIdent(_) + | syntax::GateOperand::HardwareQubit(_) + | syntax::GateOperand::Err => semantic::GateOperand::Err, + } + } + + fn push_invalid_cast_error(&mut self, target_ty: &Type, expr_ty: &Type, span: Span) { + let rhs_ty_name = format!("{expr_ty:?}"); + let lhs_ty_name = format!("{target_ty:?}"); + let kind = SemanticErrorKind::CannotCast(rhs_ty_name, lhs_ty_name, span); + self.push_semantic_error(kind); + } + + fn push_invalid_literal_cast_error(&mut self, target_ty: &Type, expr_ty: &Type, span: Span) { + let rhs_ty_name = format!("{expr_ty:?}"); + let lhs_ty_name = format!("{target_ty:?}"); + let kind = SemanticErrorKind::CannotCastLiteral(rhs_ty_name, lhs_ty_name, span); + self.push_semantic_error(kind); + } + + /// Pushes a missing symbol error with the given name + /// This is a convenience method for pushing a `SemanticErrorKind::UndefinedSymbol` error. + pub fn push_missing_symbol_error>(&mut self, name: S, span: Span) { + let kind = SemanticErrorKind::UndefinedSymbol(name.as_ref().to_string(), span); + self.push_semantic_error(kind); + } + + /// Pushes a redefined symbol error with the given name and span. + /// This is a convenience method for pushing a `SemanticErrorKind::RedefinedSymbol` error. + pub fn push_redefined_symbol_error>(&mut self, name: S, span: Span) { + let kind = SemanticErrorKind::RedefinedSymbol(name.as_ref().to_string(), span); + self.push_semantic_error(kind); + } + + /// Pushes an unsupported error with the supplied message. + pub fn push_unsupported_error_message>(&mut self, message: S, span: Span) { + let kind = SemanticErrorKind::NotSupported(message.as_ref().to_string(), span); + self.push_semantic_error(kind); + } + + pub fn push_unsuported_in_this_version_error_message>( + &mut self, + message: S, + minimum_supported_version: &Version, + span: Span, + ) { + let message = message.as_ref().to_string(); + let msv = minimum_supported_version.to_string(); + let kind = SemanticErrorKind::NotSupportedInThisVersion(message, msv, span); + self.push_semantic_error(kind); + } + + /// Pushes an unimplemented error with the supplied message. + pub fn push_unimplemented_error_message>(&mut self, message: S, span: Span) { + let kind = SemanticErrorKind::Unimplemented(message.as_ref().to_string(), span); + self.push_semantic_error(kind); + } + + /// Pushes a semantic error with the given kind. + pub fn push_semantic_error(&mut self, kind: SemanticErrorKind) { + let kind = crate::ErrorKind::Semantic(crate::semantic::Error(kind)); + let error = self.create_err(kind); + self.errors.push(error); + } + + /// Creates an error from the given kind with the current source mapping. + fn create_err(&self, kind: crate::ErrorKind) -> WithSource { + let error = crate::Error(kind); + WithSource::from_map(&self.source_map, error) } } diff --git a/compiler/qsc_qasm3/src/semantic/symbols.rs b/compiler/qsc_qasm3/src/semantic/symbols.rs index da03471c63..5479c8ada4 100644 --- a/compiler/qsc_qasm3/src/semantic/symbols.rs +++ b/compiler/qsc_qasm3/src/semantic/symbols.rs @@ -1,7 +1,9 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. -use qsc_data_structures::span::Span; +use std::rc::Rc; + +use qsc_data_structures::{index_map::IndexMap, span::Span}; use rustc_hash::FxHashMap; use super::types::Type; @@ -155,7 +157,7 @@ pub(crate) struct Scope { /// A map from symbol name to symbol ID. name_to_id: FxHashMap, /// A map from symbol ID to symbol. - id_to_symbol: FxHashMap, + id_to_symbol: FxHashMap>, /// The order in which symbols were inserted into the scope. /// This is used to determine the order of symbols in the output. order: Vec, @@ -180,7 +182,7 @@ impl Scope { /// /// This function will return an error if a symbol of the same name has already /// been declared in this scope. - pub fn insert_symbol(&mut self, id: SymbolId, symbol: Symbol) -> Result<(), SymbolError> { + pub fn insert_symbol(&mut self, id: SymbolId, symbol: Rc) -> Result<(), SymbolError> { if self.name_to_id.contains_key(&symbol.name) { return Err(SymbolError::AlreadyExists); } @@ -190,13 +192,13 @@ impl Scope { Ok(()) } - pub fn get_symbol_by_name(&self, name: &str) -> Option<(SymbolId, &Symbol)> { + pub fn get_symbol_by_name(&self, name: &str) -> Option<(SymbolId, Rc)> { self.name_to_id .get(name) - .and_then(|id| self.id_to_symbol.get(id).map(|s| (*id, s))) + .and_then(|id| self.id_to_symbol.get(id).map(|s| (*id, s.clone()))) } - fn get_ordered_symbols(&self) -> Vec { + fn get_ordered_symbols(&self) -> Vec> { self.order .iter() .map(|id| self.id_to_symbol.get(id).expect("ID should exist").clone()) @@ -207,6 +209,7 @@ impl Scope { /// A symbol table is a collection of scopes and manages the symbol ids. pub struct SymbolTable { scopes: Vec, + symbols: IndexMap>, current_id: SymbolId, } @@ -228,6 +231,7 @@ impl Default for SymbolTable { let mut slf = Self { scopes: vec![global], + symbols: IndexMap::default(), current_id: SymbolId::default(), }; @@ -259,28 +263,73 @@ impl SymbolTable { } pub fn insert_symbol(&mut self, symbol: Symbol) -> Result { + let symbol = Rc::new(symbol); let id = self.current_id; - self.current_id = self.current_id.successor(); - self.scopes + match self + .scopes .last_mut() .expect("At least one scope should be available") - .insert_symbol(id, symbol)?; + .insert_symbol(id, symbol.clone()) + { + Ok(()) => { + self.current_id = self.current_id.successor(); + self.symbols.insert(id, symbol); + Ok(id) + } + Err(SymbolError::AlreadyExists) => Err(SymbolError::AlreadyExists), + } + } - Ok(id) + fn insert_err_symbol(&mut self, name: &str, span: Span) -> (SymbolId, Rc) { + let symbol = Rc::new(Symbol { + name: name.to_string(), + span, + ty: Type::Err, + qsharp_ty: crate::types::Type::Err, + io_kind: IOKind::Default, + }); + let id = self.current_id; + self.current_id = self.current_id.successor(); + self.symbols.insert(id, symbol.clone()); + (id, symbol) } - #[must_use] - pub fn get_symbol_by_id(&self, id: SymbolId) -> Option<(SymbolId, &Symbol)> { - for scope in self.scopes.iter().rev() { - if let Some(symbol) = scope.id_to_symbol.get(&id) { - return Some((id, symbol)); - } + /// Gets the symbol with the given ID, or creates it with the given name and span. + /// the boolean value indicates if the symbol was created or not. + pub fn try_get_existing_or_insert_err_symbol( + &mut self, + name: &str, + span: Span, + ) -> Result<(SymbolId, Rc), (SymbolId, Rc)> { + // if we have the symbol, return it, otherswise create it with err values + if let Some((id, symbol)) = self.get_symbol_by_name(name) { + return Ok((id, symbol.clone())); + } + // if we don't have the symbol, create it with err values + Err(self.insert_err_symbol(name, span)) + } + + pub fn try_insert_or_get_existing(&mut self, symbol: Symbol) -> Result { + let name = symbol.name.clone(); + if let Ok(symbol_id) = self.insert_symbol(symbol) { + Ok(symbol_id) + } else { + let symbol_id = self + .get_symbol_by_name(&name) + .map(|(id, _)| id) + .expect("msg"); + Err(symbol_id) } - None } + /// Gets the symbol with the given name. This should only be used if you don't + /// have the symbold ID. This function will search the scopes in reverse order + /// and return the first symbol with the given name following the scoping rules. #[must_use] - pub fn get_symbol_by_name(&self, name: &str) -> Option<(SymbolId, &Symbol)> { + pub fn get_symbol_by_name(&self, name: S) -> Option<(SymbolId, Rc)> + where + S: AsRef, + { let scopes = self.scopes.iter().rev(); let predicate = |x: &Scope| { x.kind == ScopeKind::Block || x.kind == ScopeKind::Function || x.kind == ScopeKind::Gate @@ -303,13 +352,13 @@ impl SymbolTable { .take_while(|arg0: &&Scope| predicate(arg0)) .next() { - if let Some((id, symbol)) = scope.get_symbol_by_name(name) { + if let Some((id, symbol)) = scope.get_symbol_by_name(name.as_ref()) { return Some((id, symbol)); } } if let Some(scope) = last_false { - if let Some((id, symbol)) = scope.get_symbol_by_name(name) { + if let Some((id, symbol)) = scope.get_symbol_by_name(name.as_ref()) { if symbol.ty.is_const() || matches!(symbol.ty, Type::Gate(..) | Type::Void) || self.is_scope_rooted_in_global() @@ -320,7 +369,7 @@ impl SymbolTable { } // we should be at the global, function, or gate scope now for scope in scopes { - if let Some((id, symbol)) = scope.get_symbol_by_name(name) { + if let Some((id, symbol)) = scope.get_symbol_by_name(name.as_ref()) { if symbol.ty.is_const() || matches!(symbol.ty, Type::Gate(..) | Type::Void) { return Some((id, symbol)); } @@ -357,7 +406,7 @@ impl SymbolTable { } /// Get the input symbols in the program. - pub(crate) fn get_input(&self) -> Option> { + pub(crate) fn get_input(&self) -> Option>> { let io_input = self.get_io_input(); if io_input.is_empty() { None @@ -370,7 +419,7 @@ impl SymbolTable { /// Output symbols are either inferred or explicitly declared. /// If there are no explicitly declared output symbols, then the inferred /// output symbols are returned. - pub(crate) fn get_output(&self) -> Option> { + pub(crate) fn get_output(&self) -> Option>> { let io_ouput = self.get_io_output(); if io_ouput.is_some() { io_ouput @@ -382,7 +431,7 @@ impl SymbolTable { /// Get all symbols in the global scope that are inferred output symbols. /// Any global symbol that is not a built-in symbol and has a type that is /// inferred to be an output type is considered an inferred output symbol. - fn get_inferred_output(&self) -> Option> { + fn get_inferred_output(&self) -> Option>> { let mut symbols = vec![]; self.scopes .iter() @@ -407,7 +456,7 @@ impl SymbolTable { } /// Get all symbols in the global scope that are output symbols. - fn get_io_output(&self) -> Option> { + fn get_io_output(&self) -> Option>> { let mut symbols = vec![]; for scope in self .scopes @@ -428,7 +477,7 @@ impl SymbolTable { } /// Get all symbols in the global scope that are input symbols. - fn get_io_input(&self) -> Vec { + fn get_io_input(&self) -> Vec> { let mut symbols = vec![]; for scope in self .scopes @@ -444,3 +493,11 @@ impl SymbolTable { symbols } } + +impl std::ops::Index for SymbolTable { + type Output = Rc; + + fn index(&self, index: SymbolId) -> &Self::Output { + self.symbols.get(index).expect("Symbol should exist") + } +} diff --git a/compiler/qsc_qasm3/src/semantic/tests.rs b/compiler/qsc_qasm3/src/semantic/tests.rs index cfa028a5b4..1de94b14c7 100644 --- a/compiler/qsc_qasm3/src/semantic/tests.rs +++ b/compiler/qsc_qasm3/src/semantic/tests.rs @@ -66,8 +66,8 @@ pub(super) fn check(input: &str, expect: &Expect) { } pub(super) fn check_classical_decl(input: &str, expect: &Expect) { - check_map(input, expect, |p, s| { - let kind = p + check_map(input, expect, |program, symbol_table| { + let kind = program .statements .first() .expect("reading first statement") @@ -78,17 +78,15 @@ pub(super) fn check_classical_decl(input: &str, expect: &Expect) { }; let mut value = decl.to_string(); value.push('\n'); - let symbol = s - .get_symbol_by_id(decl.symbol_id) - .expect("getting symbol by id"); - value.push_str(&format!("[{}] {}", symbol.0, symbol.1)); + let symbol = &symbol_table[decl.symbol_id]; + value.push_str(&format!("[{}] {symbol}", decl.symbol_id)); value }); } pub(super) fn check_classical_decls(input: &str, expect: &Expect) { - check_map(input, expect, |p, s| { - let kinds = p + check_map(input, expect, |program, symbol_table| { + let kinds = program .statements .iter() .map(|stmt| stmt.kind.as_ref().clone()) @@ -97,16 +95,16 @@ pub(super) fn check_classical_decls(input: &str, expect: &Expect) { for kind in &kinds { let (symbol_id, str) = match kind { super::ast::StmtKind::ClassicalDecl(decl) => (decl.symbol_id, decl.to_string()), - super::ast::StmtKind::IODeclaration(decl) => (decl.symbol_id, decl.to_string()), - super::ast::StmtKind::Assign(stmt) => (stmt.symbold_id, stmt.to_string()), - super::ast::StmtKind::AssignOp(stmt) => (stmt.symbold_id, stmt.to_string()), + super::ast::StmtKind::OutputDeclaration(decl) => (decl.symbol_id, decl.to_string()), + super::ast::StmtKind::Assign(stmt) => (stmt.symbol_id, stmt.to_string()), + super::ast::StmtKind::AssignOp(stmt) => (stmt.symbol_id, stmt.to_string()), _ => panic!("unsupported stmt type {kind}"), }; value.push_str(&str); value.push('\n'); - let symbol = s.get_symbol_by_id(symbol_id).expect("getting symbol by id"); - value.push_str(&format!("[{}] {}", symbol.0, symbol.1)); + let symbol = &symbol_table[symbol_id]; + value.push_str(&format!("[{symbol_id}] {symbol}")); value.push('\n'); } @@ -149,7 +147,7 @@ fn check_map( assert!( !res.has_syntax_errors(), "syntax errors: {:?}", - res.parse_errors() + res.sytax_errors() ); let program = res.program.expect("no program"); @@ -201,7 +199,7 @@ fn check_map_all

( assert!( !res.has_syntax_errors(), "syntax errors: {:?}", - res.parse_errors() + res.sytax_errors() ); let program = res.program.expect("no program"); @@ -220,6 +218,7 @@ fn check_map_all

( } #[test] +#[allow(clippy::too_many_lines)] fn semantic_errors_map_to_their_corresponding_file_specific_spans() { let source0 = r#"OPENQASM 3.0; include "stdgates.inc"; @@ -252,6 +251,25 @@ fn semantic_errors_map_to_their_corresponding_file_specific_spans() { init_expr: Expr [204-205]: ty: Bit(true) kind: Lit: Int(1) + Stmt [211-227]: + annotations: + kind: ClassicalDeclarationStmt [211-227]: + symbol_id: 24 + ty_span: [211-215] + init_expr: Expr [220-226]: + ty: Bool(false) + kind: BinaryOpExpr: + op: AndL + lhs: Expr [220-221]: + ty: Err + kind: SymbolId(25) + rhs: Expr [225-226]: + ty: Bool(false) + kind: Cast [0-0]: + ty: Bool(false) + expr: Expr [225-226]: + ty: Bit(false) + kind: SymbolId(24) Stmt [140-154]: annotations: kind: ClassicalDeclarationStmt [140-154]: @@ -260,6 +278,33 @@ fn semantic_errors_map_to_their_corresponding_file_specific_spans() { init_expr: Expr [150-153]: ty: Angle(None, true) kind: Lit: Float(7.0) + Stmt [159-179]: + annotations: + kind: ClassicalDeclarationStmt [159-179]: + symbol_id: 27 + ty_span: [159-164] + init_expr: Expr [169-178]: + ty: Float(None, false) + kind: BinaryOpExpr: + op: Add + lhs: Expr [169-170]: + ty: Angle(None, false) + kind: SymbolId(26) + rhs: Expr [173-178]: + ty: Float(None, false) + kind: Cast [0-0]: + ty: Float(None, false) + expr: Expr [173-178]: + ty: Bool(true) + kind: Lit: Bool(false) + Stmt [74-84]: + annotations: + kind: ClassicalDeclarationStmt [74-84]: + symbol_id: 29 + ty_span: [74-77] + init_expr: Expr [82-83]: + ty: Err + kind: SymbolId(28) [Qsc.Qasm3.Compile.UndefinedSymbol @@ -269,6 +314,14 @@ fn semantic_errors_map_to_their_corresponding_file_specific_spans() { 2 | bool x = y && x; // undefined y, redefine x : ^ `---- + , Qsc.Qasm3.Compile.CannotCast + + x Cannot cast expression of type Err to type Bool(false) + ,-[source2.qasm:2:14] + 1 | bit x = 1; + 2 | bool x = y && x; // undefined y, redefine x + : ^ + `---- , Qsc.Qasm3.Compile.RedefinedSymbol x Redefined symbol: x. @@ -295,6 +348,15 @@ fn semantic_errors_map_to_their_corresponding_file_specific_spans() { : ^ 5 | `---- + , Qsc.Qasm3.Compile.CannotCast + + x Cannot cast expression of type Err to type Bit(false) + ,-[source0.qasm:4:5] + 3 | include "source1.qasm"; + 4 | bit c = r; // undefined symbol r + : ^^^^^^^^^^ + 5 | + `---- ]"#]], ); } diff --git a/compiler/qsc_qasm3/src/semantic/tests/decls.rs b/compiler/qsc_qasm3/src/semantic/tests/decls.rs index b9ed2cc462..ca1c08904f 100644 --- a/compiler/qsc_qasm3/src/semantic/tests/decls.rs +++ b/compiler/qsc_qasm3/src/semantic/tests/decls.rs @@ -57,7 +57,15 @@ fn scalar_ty_designator_must_be_positive() { &expect![[r#" Program: version: - statements: + statements: + Stmt [0-10]: + annotations: + kind: ClassicalDeclarationStmt [0-10]: + symbol_id: 6 + ty_span: [0-7] + init_expr: Expr [0-0]: + ty: Err + kind: Err [Qsc.Qasm3.Compile.DesignatorMustBePositiveIntLiteral @@ -66,6 +74,21 @@ fn scalar_ty_designator_must_be_positive() { 1 | int[-5] i; : ^^ `---- + , Qsc.Qasm3.Compile.Unimplemented + + x this statement is not yet handled during OpenQASM 3 import: Converting Err + | to Q# type + ,-[test:1:1] + 1 | int[-5] i; + : ^^^^^^^ + `---- + , Qsc.Qasm3.Compile.NotSupported + + x Default values for Err are unsupported. are not supported. + ,-[test:1:1] + 1 | int[-5] i; + : ^^^^^^^^^^ + `---- ]"#]], ); } @@ -77,7 +100,23 @@ fn scalar_ty_designator_must_be_int_literal() { &expect![[r#" Program: version: - statements: + statements: + Stmt [0-12]: + annotations: + kind: ClassicalDeclarationStmt [0-12]: + symbol_id: 6 + ty_span: [0-9] + init_expr: Expr [0-0]: + ty: Err + kind: Err + Stmt [13-26]: + annotations: + kind: ClassicalDeclarationStmt [13-26]: + symbol_id: 7 + ty_span: [13-23] + init_expr: Expr [0-0]: + ty: Err + kind: Err [Qsc.Qasm3.Compile.DesignatorMustBePositiveIntLiteral @@ -86,6 +125,21 @@ fn scalar_ty_designator_must_be_int_literal() { 1 | int[size] i; float[0.0] j; : ^^^^ `---- + , Qsc.Qasm3.Compile.Unimplemented + + x this statement is not yet handled during OpenQASM 3 import: Converting Err + | to Q# type + ,-[test:1:1] + 1 | int[size] i; float[0.0] j; + : ^^^^^^^^^ + `---- + , Qsc.Qasm3.Compile.NotSupported + + x Default values for Err are unsupported. are not supported. + ,-[test:1:1] + 1 | int[size] i; float[0.0] j; + : ^^^^^^^^^^^^ + `---- , Qsc.Qasm3.Compile.DesignatorMustBePositiveIntLiteral x Designator must be a positive literal integer. @@ -93,6 +147,21 @@ fn scalar_ty_designator_must_be_int_literal() { 1 | int[size] i; float[0.0] j; : ^^^ `---- + , Qsc.Qasm3.Compile.Unimplemented + + x this statement is not yet handled during OpenQASM 3 import: Converting Err + | to Q# type + ,-[test:1:14] + 1 | int[size] i; float[0.0] j; + : ^^^^^^^^^^ + `---- + , Qsc.Qasm3.Compile.NotSupported + + x Default values for Err are unsupported. are not supported. + ,-[test:1:14] + 1 | int[size] i; float[0.0] j; + : ^^^^^^^^^^^^^ + `---- ]"#]], ); } diff --git a/compiler/qsc_qasm3/src/semantic/tests/decls/angle.rs b/compiler/qsc_qasm3/src/semantic/tests/decls/angle.rs index 04bc074d19..8643afdd38 100644 --- a/compiler/qsc_qasm3/src/semantic/tests/decls/angle.rs +++ b/compiler/qsc_qasm3/src/semantic/tests/decls/angle.rs @@ -357,7 +357,15 @@ fn const_lit_decl_signed_float_lit_cast_neg() { ty_span: [6-11] init_expr: Expr [17-19]: ty: Angle(None, true) - kind: Lit: Float(-7.0) + kind: Cast [0-0]: + ty: Angle(None, true) + expr: Expr [17-19]: + ty: Float(None, true) + kind: UnaryOpExpr [17-19]: + op: Neg + expr: Expr [17-19]: + ty: Float(None, true) + kind: Lit: Float(7.0) [6] Symbol [12-13]: name: x type: Angle(None, true) @@ -373,12 +381,23 @@ fn const_lit_decl_signed_int_lit_cast_neg_fails() { &expect![[r#" Program: version: - statements: + statements: + Stmt [0-19]: + annotations: + kind: ClassicalDeclarationStmt [0-19]: + symbol_id: 6 + ty_span: [6-11] + init_expr: Expr [17-18]: + ty: Int(None, true) + kind: UnaryOpExpr [17-18]: + op: Neg + expr: Expr [17-18]: + ty: Int(None, true) + kind: Lit: Int(7) - [Qsc.Qasm3.Compile.CannotAssignToType + [Qsc.Qasm3.Compile.CannotCast - x Cannot assign a value of Int(None, true) type to a classical variable of - | Angle(None, true) type. + x Cannot cast expression of type Int(None, true) to type Angle(None, true) ,-[test:1:1] 1 | const angle x = -7; : ^^^^^^^^^^^^^^^^^^^ diff --git a/compiler/qsc_qasm3/src/semantic/tests/decls/duration.rs b/compiler/qsc_qasm3/src/semantic/tests/decls/duration.rs index a28b2a4110..870ea73d21 100644 --- a/compiler/qsc_qasm3/src/semantic/tests/decls/duration.rs +++ b/compiler/qsc_qasm3/src/semantic/tests/decls/duration.rs @@ -10,17 +10,32 @@ fn with_no_init_expr_has_generated_lit_expr() { check_classical_decl( "duration a;", &expect![[r#" - Program: - version: - statements: + Program: + version: + statements: + Stmt [0-11]: + annotations: + kind: ClassicalDeclarationStmt [0-11]: + symbol_id: 6 + ty_span: [0-8] + init_expr: Expr [0-0]: + ty: Duration(true) + kind: Err - [Qsc.Qasm3.Compile.NotSupported + [Qsc.Qasm3.Compile.NotSupported - x Duration type values are not supported. - ,-[test:1:1] - 1 | duration a; - : ^^^^^^^^ - `---- - ]"#]], + x Duration type values are not supported. + ,-[test:1:1] + 1 | duration a; + : ^^^^^^^^ + `---- + , Qsc.Qasm3.Compile.NotSupported + + x Default values for Duration(false) are unsupported. are not supported. + ,-[test:1:1] + 1 | duration a; + : ^^^^^^^^^^^ + `---- + ]"#]], ); } diff --git a/compiler/qsc_qasm3/src/semantic/tests/decls/float.rs b/compiler/qsc_qasm3/src/semantic/tests/decls/float.rs index c04f74943f..9af5a1933e 100644 --- a/compiler/qsc_qasm3/src/semantic/tests/decls/float.rs +++ b/compiler/qsc_qasm3/src/semantic/tests/decls/float.rs @@ -352,17 +352,21 @@ fn const_lit_decl_signed_float_lit_cast_neg() { check_classical_decl( "const float x = -7.;", &expect![[r#" - ClassicalDeclarationStmt [0-20]: - symbol_id: 6 - ty_span: [6-11] - init_expr: Expr [17-19]: - ty: Float(None, true) - kind: Lit: Float(-7.0) - [6] Symbol [12-13]: - name: x - type: Float(None, true) - qsharp_type: Double - io_kind: Default"#]], + ClassicalDeclarationStmt [0-20]: + symbol_id: 6 + ty_span: [6-11] + init_expr: Expr [17-19]: + ty: Float(None, true) + kind: UnaryOpExpr [17-19]: + op: Neg + expr: Expr [17-19]: + ty: Float(None, true) + kind: Lit: Float(7.0) + [6] Symbol [12-13]: + name: x + type: Float(None, true) + qsharp_type: Double + io_kind: Default"#]], ); } @@ -371,17 +375,25 @@ fn const_lit_decl_signed_int_lit_cast_neg() { check_classical_decl( "const float x = -7;", &expect![[r#" - ClassicalDeclarationStmt [0-19]: - symbol_id: 6 - ty_span: [6-11] - init_expr: Expr [17-18]: - ty: Float(None, true) - kind: Lit: Float(-7.0) - [6] Symbol [12-13]: - name: x - type: Float(None, true) - qsharp_type: Double - io_kind: Default"#]], + ClassicalDeclarationStmt [0-19]: + symbol_id: 6 + ty_span: [6-11] + init_expr: Expr [17-18]: + ty: Float(None, true) + kind: Cast [0-0]: + ty: Float(None, true) + expr: Expr [17-18]: + ty: Int(None, true) + kind: UnaryOpExpr [17-18]: + op: Neg + expr: Expr [17-18]: + ty: Int(None, true) + kind: Lit: Int(7) + [6] Symbol [12-13]: + name: x + type: Float(None, true) + qsharp_type: Double + io_kind: Default"#]], ); } @@ -414,7 +426,15 @@ fn init_float_with_int_value_greater_than_safely_representable_values() { &expect![[r#" Program: version: - statements: + statements: + Stmt [0-27]: + annotations: + kind: ClassicalDeclarationStmt [0-27]: + symbol_id: 6 + ty_span: [0-5] + init_expr: Expr [10-26]: + ty: Int(None, true) + kind: Lit: Int(9007199254740993) [Qsc.Qasm3.Compile.InvalidCastValueRange @@ -424,6 +444,14 @@ fn init_float_with_int_value_greater_than_safely_representable_values() { 1 | float a = 9007199254740993; : ^^^^^^^^^^^^^^^^ `---- + , Qsc.Qasm3.Compile.CannotCastLiteral + + x Cannot cast literal expression of type Int(None, true) to type Float(None, + | false) + ,-[test:1:11] + 1 | float a = 9007199254740993; + : ^^^^^^^^^^^^^^^^ + `---- ]"#]], ); } @@ -438,8 +466,16 @@ fn init_float_with_int_value_equal_min_safely_representable_values() { symbol_id: 6 ty_span: [0-5] init_expr: Expr [11-27]: - ty: Float(None, true) - kind: Lit: Float(-9007199254740992.0) + ty: Float(None, false) + kind: Cast [0-0]: + ty: Float(None, false) + expr: Expr [11-27]: + ty: Int(None, true) + kind: UnaryOpExpr [11-27]: + op: Neg + expr: Expr [11-27]: + ty: Int(None, true) + kind: Lit: Int(9007199254740992) [6] Symbol [6-7]: name: a type: Float(None, false) @@ -447,26 +483,3 @@ fn init_float_with_int_value_equal_min_safely_representable_values() { io_kind: Default"#]], ); } - -#[test] -fn init_float_with_int_value_less_than_safely_representable_values() { - let min_exact_int = -(2i64.pow(f64::MANTISSA_DIGITS)); - let next = min_exact_int - 1; - check_classical_decl( - &format!("float a = {next};"), - &expect![[r#" - Program: - version: - statements: - - [Qsc.Qasm3.Compile.InvalidCastValueRange - - x Assigning Int(None, true) values to Float(None, false) must be in a range - | that be converted to Float(None, false). - ,-[test:1:12] - 1 | float a = -9007199254740993; - : ^^^^^^^^^^^^^^^^ - `---- - ]"#]], - ); -} diff --git a/compiler/qsc_qasm3/src/semantic/tests/decls/int.rs b/compiler/qsc_qasm3/src/semantic/tests/decls/int.rs index be6373acae..2e7d7ed18d 100644 --- a/compiler/qsc_qasm3/src/semantic/tests/decls/int.rs +++ b/compiler/qsc_qasm3/src/semantic/tests/decls/int.rs @@ -15,7 +15,11 @@ fn implicit_bitness_int_negative() { ty_span: [0-3] init_expr: Expr [9-11]: ty: Int(None, true) - kind: Lit: Int(-42) + kind: UnaryOpExpr [9-11]: + op: Neg + expr: Expr [9-11]: + ty: Int(None, true) + kind: Lit: Int(42) [6] Symbol [4-5]: name: x type: Int(None, false) @@ -34,7 +38,11 @@ fn implicit_bitness_int_const_negative() { ty_span: [6-9] init_expr: Expr [15-17]: ty: Int(None, true) - kind: Lit: Int(-42) + kind: UnaryOpExpr [15-17]: + op: Neg + expr: Expr [15-17]: + ty: Int(None, true) + kind: Lit: Int(42) [6] Symbol [10-11]: name: x type: Int(None, true) @@ -348,22 +356,28 @@ fn const_explicit_bitness_int() { } #[test] -fn implicit_bitness_int_negative_float_decl_causes_semantic_error() { +fn implicit_bitness_int_negative_float_decl_is_runtime_conversion() { check_classical_decl( "int x = -42.;", &expect![[r#" - Program: - version: - statements: - - [Qsc.Qasm3.Compile.CannotAssignToType - - x Cannot assign a value of Float(None, true) type to a classical variable of - | Int(None, false) type. - ,-[test:1:1] - 1 | int x = -42.; - : ^^^^^^^^^^^^^ - `---- - ]"#]], + ClassicalDeclarationStmt [0-13]: + symbol_id: 6 + ty_span: [0-3] + init_expr: Expr [9-12]: + ty: Int(None, false) + kind: Cast [0-0]: + ty: Int(None, false) + expr: Expr [9-12]: + ty: Float(None, true) + kind: UnaryOpExpr [9-12]: + op: Neg + expr: Expr [9-12]: + ty: Float(None, true) + kind: Lit: Float(42.0) + [6] Symbol [4-5]: + name: x + type: Int(None, false) + qsharp_type: Int + io_kind: Default"#]], ); } diff --git a/compiler/qsc_qasm3/src/semantic/tests/decls/stretch.rs b/compiler/qsc_qasm3/src/semantic/tests/decls/stretch.rs index 488931a1d5..8c9848f665 100644 --- a/compiler/qsc_qasm3/src/semantic/tests/decls/stretch.rs +++ b/compiler/qsc_qasm3/src/semantic/tests/decls/stretch.rs @@ -12,7 +12,15 @@ fn with_no_init_expr_has_generated_lit_expr() { &expect![[r#" Program: version: - statements: + statements: + Stmt [0-10]: + annotations: + kind: ClassicalDeclarationStmt [0-10]: + symbol_id: 6 + ty_span: [0-7] + init_expr: Expr [0-0]: + ty: Stretch(true) + kind: Err [Qsc.Qasm3.Compile.NotSupported @@ -21,6 +29,13 @@ fn with_no_init_expr_has_generated_lit_expr() { 1 | stretch a; : ^^^^^^^ `---- + , Qsc.Qasm3.Compile.NotSupported + + x Stretch default values are not supported. + ,-[test:1:1] + 1 | stretch a; + : ^^^^^^^^^^ + `---- ]"#]], ); } diff --git a/compiler/qsc_qasm3/src/semantic/tests/decls/uint.rs b/compiler/qsc_qasm3/src/semantic/tests/decls/uint.rs index 0e7ecb3f53..f53b975b87 100644 --- a/compiler/qsc_qasm3/src/semantic/tests/decls/uint.rs +++ b/compiler/qsc_qasm3/src/semantic/tests/decls/uint.rs @@ -310,64 +310,82 @@ fn const_explicit_bitness_int() { } #[test] -fn assigning_uint_to_negative_lit_results_in_semantic_error() { +fn assigning_uint_to_negative_lit() { check_classical_decl( "const uint[10] x = -42;", &expect![[r#" - Program: - version: - statements: - - [Qsc.Qasm3.Compile.CannotAssignToType - - x Cannot assign a value of Int(None, true) type to a classical variable of - | UInt(Some(10), true) type. - ,-[test:1:1] - 1 | const uint[10] x = -42; - : ^^^^^^^^^^^^^^^^^^^^^^^ - `---- - ]"#]], + ClassicalDeclarationStmt [0-23]: + symbol_id: 6 + ty_span: [6-14] + init_expr: Expr [20-22]: + ty: UInt(Some(10), true) + kind: Cast [0-0]: + ty: UInt(Some(10), true) + expr: Expr [20-22]: + ty: Int(None, true) + kind: UnaryOpExpr [20-22]: + op: Neg + expr: Expr [20-22]: + ty: Int(None, true) + kind: Lit: Int(42) + [6] Symbol [15-16]: + name: x + type: UInt(Some(10), true) + qsharp_type: Int + io_kind: Default"#]], ); } #[test] -fn implicit_bitness_uint_const_negative_decl_raises_semantic_error() { +fn implicit_bitness_uint_const_negative_decl() { check_classical_decl( "const uint x = -42;", &expect![[r#" - Program: - version: - statements: - - [Qsc.Qasm3.Compile.CannotAssignToType - - x Cannot assign a value of Int(None, true) type to a classical variable of - | UInt(None, true) type. - ,-[test:1:1] - 1 | const uint x = -42; - : ^^^^^^^^^^^^^^^^^^^ - `---- - ]"#]], + ClassicalDeclarationStmt [0-19]: + symbol_id: 6 + ty_span: [6-10] + init_expr: Expr [16-18]: + ty: UInt(None, true) + kind: Cast [0-0]: + ty: UInt(None, true) + expr: Expr [16-18]: + ty: Int(None, true) + kind: UnaryOpExpr [16-18]: + op: Neg + expr: Expr [16-18]: + ty: Int(None, true) + kind: Lit: Int(42) + [6] Symbol [11-12]: + name: x + type: UInt(None, true) + qsharp_type: Int + io_kind: Default"#]], ); } #[test] -fn explicit_bitness_uint_const_negative_decl_raises_semantic_error() { +fn explicit_bitness_uint_const_negative_decl() { check_classical_decl( "const uint[32] x = -42;", &expect![[r#" - Program: - version: - statements: - - [Qsc.Qasm3.Compile.CannotAssignToType - - x Cannot assign a value of Int(None, true) type to a classical variable of - | UInt(Some(32), true) type. - ,-[test:1:1] - 1 | const uint[32] x = -42; - : ^^^^^^^^^^^^^^^^^^^^^^^ - `---- - ]"#]], + ClassicalDeclarationStmt [0-23]: + symbol_id: 6 + ty_span: [6-14] + init_expr: Expr [20-22]: + ty: UInt(Some(32), true) + kind: Cast [0-0]: + ty: UInt(Some(32), true) + expr: Expr [20-22]: + ty: Int(None, true) + kind: UnaryOpExpr [20-22]: + op: Neg + expr: Expr [20-22]: + ty: Int(None, true) + kind: Lit: Int(42) + [6] Symbol [15-16]: + name: x + type: UInt(Some(32), true) + qsharp_type: Int + io_kind: Default"#]], ); } diff --git a/compiler/qsc_qasm3/src/semantic/tests/expression.rs b/compiler/qsc_qasm3/src/semantic/tests/expression.rs index 783217bc1d..6102868912 100644 --- a/compiler/qsc_qasm3/src/semantic/tests/expression.rs +++ b/compiler/qsc_qasm3/src/semantic/tests/expression.rs @@ -47,7 +47,7 @@ fn a() { ExprStmt [55-61]: expr: Expr [56-60]: ty: Bool(true) - kind: UnaryOpExpr: + kind: UnaryOpExpr [56-60]: op: NotL expr: Expr [56-60]: ty: Bool(true) diff --git a/compiler/qsc_qasm3/src/semantic/tests/expression/binary/comparison/bit_to_bit.rs b/compiler/qsc_qasm3/src/semantic/tests/expression/binary/comparison/bit_to_bit.rs index 2aa0c1eec2..2f62c3202b 100644 --- a/compiler/qsc_qasm3/src/semantic/tests/expression/binary/comparison/bit_to_bit.rs +++ b/compiler/qsc_qasm3/src/semantic/tests/expression/binary/comparison/bit_to_bit.rs @@ -173,7 +173,7 @@ fn unop_not_logical_and_unop_not() { op: AndL lhs: Expr [57-58]: ty: Bool(false) - kind: UnaryOpExpr: + kind: UnaryOpExpr [57-58]: op: NotL expr: Expr [57-58]: ty: Bool(false) @@ -184,7 +184,7 @@ fn unop_not_logical_and_unop_not() { kind: SymbolId(6) rhs: Expr [63-64]: ty: Bool(false) - kind: UnaryOpExpr: + kind: UnaryOpExpr [63-64]: op: NotL expr: Expr [63-64]: ty: Bool(false) @@ -244,7 +244,7 @@ fn unop_not_logical_or_unop_not() { op: OrL lhs: Expr [57-58]: ty: Bool(false) - kind: UnaryOpExpr: + kind: UnaryOpExpr [57-58]: op: NotL expr: Expr [57-58]: ty: Bool(false) @@ -255,7 +255,7 @@ fn unop_not_logical_or_unop_not() { kind: SymbolId(6) rhs: Expr [63-64]: ty: Bool(false) - kind: UnaryOpExpr: + kind: UnaryOpExpr [63-64]: op: NotL expr: Expr [63-64]: ty: Bool(false) @@ -315,7 +315,7 @@ fn unop_not_logical_and() { op: AndL lhs: Expr [57-58]: ty: Bool(false) - kind: UnaryOpExpr: + kind: UnaryOpExpr [57-58]: op: NotL expr: Expr [57-58]: ty: Bool(false) @@ -382,7 +382,7 @@ fn unop_not_logical_or() { op: OrL lhs: Expr [57-58]: ty: Bool(false) - kind: UnaryOpExpr: + kind: UnaryOpExpr [57-58]: op: NotL expr: Expr [57-58]: ty: Bool(false) @@ -456,7 +456,7 @@ fn logical_and_unop_not() { kind: SymbolId(6) rhs: Expr [62-63]: ty: Bool(false) - kind: UnaryOpExpr: + kind: UnaryOpExpr [62-63]: op: NotL expr: Expr [62-63]: ty: Bool(false) @@ -523,7 +523,7 @@ fn logical_or_unop_not() { kind: SymbolId(6) rhs: Expr [62-63]: ty: Bool(false) - kind: UnaryOpExpr: + kind: UnaryOpExpr [62-63]: op: NotL expr: Expr [62-63]: ty: Bool(false) diff --git a/compiler/qsc_qasm3/src/semantic/tests/expression/binary/comparison/bool_to_bool.rs b/compiler/qsc_qasm3/src/semantic/tests/expression/binary/comparison/bool_to_bool.rs index df79f99889..56579fd91c 100644 --- a/compiler/qsc_qasm3/src/semantic/tests/expression/binary/comparison/bool_to_bool.rs +++ b/compiler/qsc_qasm3/src/semantic/tests/expression/binary/comparison/bool_to_bool.rs @@ -157,14 +157,14 @@ fn unop_not_logical_and_unop_not() { op: AndL lhs: Expr [66-67]: ty: Bool(false) - kind: UnaryOpExpr: + kind: UnaryOpExpr [66-67]: op: NotL expr: Expr [66-67]: ty: Bool(false) kind: SymbolId(6) rhs: Expr [72-73]: ty: Bool(false) - kind: UnaryOpExpr: + kind: UnaryOpExpr [72-73]: op: NotL expr: Expr [72-73]: ty: Bool(false) @@ -220,14 +220,14 @@ fn unop_not_logical_or_unop_not() { op: OrL lhs: Expr [66-67]: ty: Bool(false) - kind: UnaryOpExpr: + kind: UnaryOpExpr [66-67]: op: NotL expr: Expr [66-67]: ty: Bool(false) kind: SymbolId(6) rhs: Expr [72-73]: ty: Bool(false) - kind: UnaryOpExpr: + kind: UnaryOpExpr [72-73]: op: NotL expr: Expr [72-73]: ty: Bool(false) @@ -283,7 +283,7 @@ fn unop_not_logical_and() { op: AndL lhs: Expr [66-67]: ty: Bool(false) - kind: UnaryOpExpr: + kind: UnaryOpExpr [66-67]: op: NotL expr: Expr [66-67]: ty: Bool(false) @@ -342,7 +342,7 @@ fn unop_not_logical_or() { op: OrL lhs: Expr [66-67]: ty: Bool(false) - kind: UnaryOpExpr: + kind: UnaryOpExpr [66-67]: op: NotL expr: Expr [66-67]: ty: Bool(false) @@ -404,7 +404,7 @@ fn logical_and_unop_not() { kind: SymbolId(6) rhs: Expr [71-72]: ty: Bool(false) - kind: UnaryOpExpr: + kind: UnaryOpExpr [71-72]: op: NotL expr: Expr [71-72]: ty: Bool(false) @@ -463,7 +463,7 @@ fn logical_or_unop_not() { kind: SymbolId(6) rhs: Expr [71-72]: ty: Bool(false) - kind: UnaryOpExpr: + kind: UnaryOpExpr [71-72]: op: NotL expr: Expr [71-72]: ty: Bool(false) diff --git a/compiler/qsc_qasm3/src/semantic/tests/expression/implicit_cast_from_angle.rs b/compiler/qsc_qasm3/src/semantic/tests/expression/implicit_cast_from_angle.rs index b09516234a..02c6cb12cc 100644 --- a/compiler/qsc_qasm3/src/semantic/tests/expression/implicit_cast_from_angle.rs +++ b/compiler/qsc_qasm3/src/semantic/tests/expression/implicit_cast_from_angle.rs @@ -145,6 +145,14 @@ fn to_implicit_int_implicitly_fails() { init_expr: Expr [19-22]: ty: Angle(None, true) kind: Lit: Float(42.0) + Stmt [32-42]: + annotations: + kind: ClassicalDeclarationStmt [32-42]: + symbol_id: 7 + ty_span: [32-35] + init_expr: Expr [40-41]: + ty: Angle(None, false) + kind: SymbolId(6) [Qsc.Qasm3.Compile.CannotCast @@ -180,6 +188,14 @@ fn to_explicit_int_implicitly_fails() { init_expr: Expr [19-22]: ty: Angle(None, true) kind: Lit: Float(42.0) + Stmt [32-46]: + annotations: + kind: ClassicalDeclarationStmt [32-46]: + symbol_id: 7 + ty_span: [32-39] + init_expr: Expr [44-45]: + ty: Angle(None, false) + kind: SymbolId(6) [Qsc.Qasm3.Compile.CannotCast @@ -216,6 +232,14 @@ fn to_implicit_uint_implicitly_fails() { init_expr: Expr [19-22]: ty: Angle(None, true) kind: Lit: Float(42.0) + Stmt [32-43]: + annotations: + kind: ClassicalDeclarationStmt [32-43]: + symbol_id: 7 + ty_span: [32-36] + init_expr: Expr [41-42]: + ty: Angle(None, false) + kind: SymbolId(6) [Qsc.Qasm3.Compile.CannotCast @@ -250,8 +274,24 @@ fn negative_lit_to_implicit_uint_implicitly_fails() { symbol_id: 6 ty_span: [9-14] init_expr: Expr [20-23]: - ty: Angle(None, true) - kind: Lit: Float(-42.0) + ty: Angle(None, false) + kind: Cast [0-0]: + ty: Angle(None, false) + expr: Expr [20-23]: + ty: Float(None, true) + kind: UnaryOpExpr [20-23]: + op: Neg + expr: Expr [20-23]: + ty: Float(None, true) + kind: Lit: Float(42.0) + Stmt [33-44]: + annotations: + kind: ClassicalDeclarationStmt [33-44]: + symbol_id: 7 + ty_span: [33-37] + init_expr: Expr [42-43]: + ty: Angle(None, false) + kind: SymbolId(6) [Qsc.Qasm3.Compile.CannotCast @@ -288,6 +328,14 @@ fn to_explicit_uint_implicitly_fails() { init_expr: Expr [19-22]: ty: Angle(None, true) kind: Lit: Float(42.0) + Stmt [32-47]: + annotations: + kind: ClassicalDeclarationStmt [32-47]: + symbol_id: 7 + ty_span: [32-40] + init_expr: Expr [45-46]: + ty: Angle(None, false) + kind: SymbolId(6) [Qsc.Qasm3.Compile.CannotCast @@ -324,6 +372,14 @@ fn to_explicit_bigint_implicitly_fails() { init_expr: Expr [19-22]: ty: Angle(None, true) kind: Lit: Float(42.0) + Stmt [32-46]: + annotations: + kind: ClassicalDeclarationStmt [32-46]: + symbol_id: 7 + ty_span: [32-39] + init_expr: Expr [44-45]: + ty: Angle(None, false) + kind: SymbolId(6) [Qsc.Qasm3.Compile.CannotCast @@ -360,6 +416,14 @@ fn to_implicit_float_implicitly_fails() { init_expr: Expr [19-22]: ty: Angle(None, true) kind: Lit: Float(42.0) + Stmt [32-44]: + annotations: + kind: ClassicalDeclarationStmt [32-44]: + symbol_id: 7 + ty_span: [32-37] + init_expr: Expr [42-43]: + ty: Angle(None, false) + kind: SymbolId(6) [Qsc.Qasm3.Compile.CannotCast @@ -396,6 +460,14 @@ fn to_explicit_float_implicitly_fails() { init_expr: Expr [19-22]: ty: Angle(None, true) kind: Lit: Float(42.0) + Stmt [32-48]: + annotations: + kind: ClassicalDeclarationStmt [32-48]: + symbol_id: 7 + ty_span: [32-41] + init_expr: Expr [46-47]: + ty: Angle(None, false) + kind: SymbolId(6) [Qsc.Qasm3.Compile.CannotCast @@ -432,6 +504,14 @@ fn to_implicit_complex_implicitly_fails() { init_expr: Expr [19-22]: ty: Angle(None, true) kind: Lit: Float(42.0) + Stmt [32-53]: + annotations: + kind: ClassicalDeclarationStmt [32-53]: + symbol_id: 7 + ty_span: [32-46] + init_expr: Expr [51-52]: + ty: Angle(None, false) + kind: SymbolId(6) [Qsc.Qasm3.Compile.CannotCast @@ -468,6 +548,14 @@ fn to_explicit_complex_implicitly_fails() { init_expr: Expr [19-22]: ty: Angle(None, true) kind: Lit: Float(42.0) + Stmt [32-57]: + annotations: + kind: ClassicalDeclarationStmt [32-57]: + symbol_id: 7 + ty_span: [32-50] + init_expr: Expr [55-56]: + ty: Angle(None, false) + kind: SymbolId(6) [Qsc.Qasm3.Compile.CannotCast diff --git a/compiler/qsc_qasm3/src/semantic/tests/expression/implicit_cast_from_bit.rs b/compiler/qsc_qasm3/src/semantic/tests/expression/implicit_cast_from_bit.rs index 0c701f3601..7798b240e6 100644 --- a/compiler/qsc_qasm3/src/semantic/tests/expression/implicit_cast_from_bit.rs +++ b/compiler/qsc_qasm3/src/semantic/tests/expression/implicit_cast_from_bit.rs @@ -288,6 +288,14 @@ fn to_implicit_float_implicitly_fails() { init_expr: Expr [17-18]: ty: Bit(true) kind: Lit: Int(1) + Stmt [28-40]: + annotations: + kind: ClassicalDeclarationStmt [28-40]: + symbol_id: 7 + ty_span: [28-33] + init_expr: Expr [38-39]: + ty: Bit(false) + kind: SymbolId(6) [Qsc.Qasm3.Compile.CannotCast diff --git a/compiler/qsc_qasm3/src/semantic/tests/expression/implicit_cast_from_float.rs b/compiler/qsc_qasm3/src/semantic/tests/expression/implicit_cast_from_float.rs index bd1de21f74..1af9e85783 100644 --- a/compiler/qsc_qasm3/src/semantic/tests/expression/implicit_cast_from_float.rs +++ b/compiler/qsc_qasm3/src/semantic/tests/expression/implicit_cast_from_float.rs @@ -26,6 +26,14 @@ fn to_bit_implicitly_fails() { init_expr: Expr [19-22]: ty: Float(None, true) kind: Lit: Float(42.0) + Stmt [32-42]: + annotations: + kind: ClassicalDeclarationStmt [32-42]: + symbol_id: 7 + ty_span: [32-35] + init_expr: Expr [40-41]: + ty: Float(None, false) + kind: SymbolId(6) [Qsc.Qasm3.Compile.CannotCast @@ -61,6 +69,14 @@ fn explicit_width_to_bit_implicitly_fails() { init_expr: Expr [23-26]: ty: Float(Some(64), true) kind: Lit: Float(42.0) + Stmt [36-46]: + annotations: + kind: ClassicalDeclarationStmt [36-46]: + symbol_id: 7 + ty_span: [36-39] + init_expr: Expr [44-45]: + ty: Float(Some(64), false) + kind: SymbolId(6) [Qsc.Qasm3.Compile.CannotCast @@ -244,33 +260,37 @@ fn negative_lit_to_implicit_uint_implicitly() { check_classical_decls( input, &expect![[r#" - ClassicalDeclarationStmt [9-24]: - symbol_id: 6 - ty_span: [9-14] - init_expr: Expr [20-23]: - ty: Float(None, true) - kind: Lit: Float(-42.0) - [6] Symbol [15-16]: - name: x - type: Float(None, false) - qsharp_type: Double - io_kind: Default - ClassicalDeclarationStmt [33-44]: - symbol_id: 7 - ty_span: [33-37] - init_expr: Expr [42-43]: - ty: UInt(None, false) - kind: Cast [0-0]: + ClassicalDeclarationStmt [9-24]: + symbol_id: 6 + ty_span: [9-14] + init_expr: Expr [20-23]: + ty: Float(None, true) + kind: UnaryOpExpr [20-23]: + op: Neg + expr: Expr [20-23]: + ty: Float(None, true) + kind: Lit: Float(42.0) + [6] Symbol [15-16]: + name: x + type: Float(None, false) + qsharp_type: Double + io_kind: Default + ClassicalDeclarationStmt [33-44]: + symbol_id: 7 + ty_span: [33-37] + init_expr: Expr [42-43]: ty: UInt(None, false) - expr: Expr [42-43]: - ty: Float(None, false) - kind: SymbolId(6) - [7] Symbol [38-39]: - name: y - type: UInt(None, false) - qsharp_type: Int - io_kind: Default - "#]], + kind: Cast [0-0]: + ty: UInt(None, false) + expr: Expr [42-43]: + ty: Float(None, false) + kind: SymbolId(6) + [7] Symbol [38-39]: + name: y + type: UInt(None, false) + qsharp_type: Int + io_kind: Default + "#]], ); } diff --git a/compiler/qsc_qasm3/src/semantic/tests/statements/box_stmt.rs b/compiler/qsc_qasm3/src/semantic/tests/statements/box_stmt.rs index 4b731ec2d5..5089ce953f 100644 --- a/compiler/qsc_qasm3/src/semantic/tests/statements/box_stmt.rs +++ b/compiler/qsc_qasm3/src/semantic/tests/statements/box_stmt.rs @@ -13,7 +13,10 @@ fn with_invalid_instruction_fails() { &expect![[r#" Program: version: - statements: + statements: + Stmt [0-26]: + annotations: + kind: Err [Qsc.Qasm3.Compile.ClassicalStmtInBox @@ -24,6 +27,14 @@ fn with_invalid_instruction_fails() { : ^^^^^^ 3 | } `---- + , Qsc.Qasm3.Compile.Unimplemented + + x this statement is not yet handled during OpenQASM 3 import: box stmt + ,-[test:1:1] + 1 | ,-> box { + 2 | | 2 + 4; + 3 | `-> } + `---- ]"#]], ); } @@ -35,7 +46,10 @@ fn with_duration_fails() { &expect![[r#" Program: version: - statements: + statements: + Stmt [0-13]: + annotations: + kind: Err [Qsc.Qasm3.Compile.NotSupported @@ -44,6 +58,13 @@ fn with_duration_fails() { 1 | box [4us] { } : ^^^ `---- + , Qsc.Qasm3.Compile.Unimplemented + + x this statement is not yet handled during OpenQASM 3 import: box stmt + ,-[test:1:1] + 1 | box [4us] { } + : ^^^^^^^^^^^^^ + `---- ]"#]], ); } diff --git a/compiler/qsc_qasm3/src/semantic/tests/statements/for_stmt.rs b/compiler/qsc_qasm3/src/semantic/tests/statements/for_stmt.rs index 28d52475a3..620d87a20e 100644 --- a/compiler/qsc_qasm3/src/semantic/tests/statements/for_stmt.rs +++ b/compiler/qsc_qasm3/src/semantic/tests/statements/for_stmt.rs @@ -14,7 +14,21 @@ fn shadowing_loop_variable_in_single_stmt_body_fails() { &expect![[r#" Program: version: - statements: + statements: + Stmt [5-39]: + annotations: + kind: ForStmt [5-39]: + loop_variable: 6 + iterable: DiscreteSet [18-20]: + values: + body: Stmt [29-39]: + annotations: + kind: ClassicalDeclarationStmt [29-39]: + symbol_id: 6 + ty_span: [29-32] + init_expr: Expr [37-38]: + ty: Int(None, true) + kind: Lit: Int(2) [Qsc.Qasm3.Compile.RedefinedSymbol diff --git a/compiler/qsc_qasm3/src/semantic/tests/statements/if_stmt.rs b/compiler/qsc_qasm3/src/semantic/tests/statements/if_stmt.rs index 4fb652259a..1083cbb167 100644 --- a/compiler/qsc_qasm3/src/semantic/tests/statements/if_stmt.rs +++ b/compiler/qsc_qasm3/src/semantic/tests/statements/if_stmt.rs @@ -23,6 +23,21 @@ fn if_branch_doesnt_create_its_own_scope() { init_expr: Expr [13-14]: ty: Int(None, true) kind: Lit: Int(0) + Stmt [20-40]: + annotations: + kind: IfStmt [20-40]: + condition: Expr [24-28]: + ty: Bool(true) + kind: Lit: Bool(true) + if_body: Stmt [30-40]: + annotations: + kind: ClassicalDeclarationStmt [30-40]: + symbol_id: 6 + ty_span: [30-33] + init_expr: Expr [38-39]: + ty: Int(None, true) + kind: Lit: Int(1) + else_body: [Qsc.Qasm3.Compile.RedefinedSymbol @@ -57,6 +72,23 @@ fn else_branch_doesnt_create_its_own_scope() { init_expr: Expr [13-14]: ty: Int(None, true) kind: Lit: Int(0) + Stmt [20-52]: + annotations: + kind: IfStmt [20-52]: + condition: Expr [24-28]: + ty: Bool(true) + kind: Lit: Bool(true) + if_body: Stmt [30-32]: + annotations: + kind: Block [30-32]: + else_body: Stmt [42-52]: + annotations: + kind: ClassicalDeclarationStmt [42-52]: + symbol_id: 6 + ty_span: [42-45] + init_expr: Expr [50-51]: + ty: Int(None, true) + kind: Lit: Int(1) [Qsc.Qasm3.Compile.RedefinedSymbol diff --git a/compiler/qsc_qasm3/src/semantic/tests/statements/switch_stmt.rs b/compiler/qsc_qasm3/src/semantic/tests/statements/switch_stmt.rs index 2ed89fa9f4..6fd0950ff9 100644 --- a/compiler/qsc_qasm3/src/semantic/tests/statements/switch_stmt.rs +++ b/compiler/qsc_qasm3/src/semantic/tests/statements/switch_stmt.rs @@ -14,7 +14,21 @@ fn not_supported_before_version_3_1() { &expect![[r#" Program: version: 3.0 - statements: + statements: + Stmt [23-47]: + annotations: + kind: SwitchStmt [23-47]: + target: Expr [31-32]: + ty: Int(None, true) + kind: Lit: Int(1) + cases: + SwitchCase [36-45]: + labels: + Expr [41-42]: + ty: Int(None, true) + kind: Lit: Int(1) + block: Block [43-45]: + default_case: [Qsc.Qasm3.Compile.NotSupportedInThisVersion diff --git a/compiler/qsc_qasm3/src/semantic/tests/statements/while_stmt.rs b/compiler/qsc_qasm3/src/semantic/tests/statements/while_stmt.rs index c1e5a7f644..91b9fdfb3e 100644 --- a/compiler/qsc_qasm3/src/semantic/tests/statements/while_stmt.rs +++ b/compiler/qsc_qasm3/src/semantic/tests/statements/while_stmt.rs @@ -23,6 +23,20 @@ fn single_stmt_body_doesnt_creates_its_own_scope() { init_expr: Expr [13-14]: ty: Int(None, true) kind: Lit: Int(0) + Stmt [20-42]: + annotations: + kind: WhileLoop [20-42]: + condition: Expr [26-30]: + ty: Bool(true) + kind: Lit: Bool(true) + body: Stmt [32-42]: + annotations: + kind: ClassicalDeclarationStmt [32-42]: + symbol_id: 6 + ty_span: [32-35] + init_expr: Expr [40-41]: + ty: Int(None, true) + kind: Lit: Int(1) [Qsc.Qasm3.Compile.RedefinedSymbol diff --git a/compiler/qsc_qasm3/src/tests.rs b/compiler/qsc_qasm3/src/tests.rs index 66c8c67e60..6b0a9df1dd 100644 --- a/compiler/qsc_qasm3/src/tests.rs +++ b/compiler/qsc_qasm3/src/tests.rs @@ -1,10 +1,9 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. -use crate::{ - parse::{QasmParseResult, QasmSource}, - qasm_to_program, CompilerConfig, OutputSemantics, ProgramType, QasmCompileUnit, QubitSemantics, -}; +use crate::runtime::RuntimeFunctions; +use crate::semantic::symbols::SymbolTable; +use crate::{CompilerConfig, OutputSemantics, ProgramType, QasmCompileUnit, QubitSemantics}; use miette::Report; use qsc::interpret::Error; use qsc::{ @@ -14,10 +13,8 @@ use qsc::{ }; use std::{path::Path, sync::Arc}; -use crate::{ - io::{InMemorySourceResolver, SourceResolver}, - parse::parse_source, -}; +use crate::io::{InMemorySourceResolver, SourceResolver}; +use crate::semantic::{parse_source, QasmSemanticParseResult}; pub(crate) mod assignment; pub(crate) mod declaration; @@ -71,21 +68,124 @@ pub(crate) fn generate_qir_from_ast( ) } -fn compile_qasm_to_qir(source: &str, profile: Profile) -> Result> { +fn compile(source: S) -> miette::Result> +where + S: AsRef, +{ + let config = CompilerConfig::new( + QubitSemantics::Qiskit, + OutputSemantics::Qiskit, + ProgramType::File, + Some("Test".into()), + None, + ); + compile_with_config(source, config) +} + +fn compile_with_config( + source: S, + config: CompilerConfig, +) -> miette::Result> +where + S: AsRef, +{ let res = parse(source)?; - assert!(!res.has_errors()); - - let unit = qasm_to_program( - res.source, - res.source_map, - CompilerConfig::new( - QubitSemantics::Qiskit, - OutputSemantics::Qiskit, - ProgramType::File, - Some("Test".into()), - None, - ), + if res.has_syntax_errors() { + for e in res.sytax_errors() { + println!("{:?}", Report::new(e.clone())); + } + } + assert!(!res.has_syntax_errors()); + let program = res.program.expect("no program found"); + + let compiler = crate::compiler::QasmCompiler { + source_map: res.source_map, + config, + stmts: vec![], + runtime: RuntimeFunctions::empty(), + symbols: res.symbols, + errors: res.errors, + }; + + let unit = compiler.compile(&program); + Ok(unit) +} + +pub fn compile_all

( + path: P, + sources: impl IntoIterator, Arc)>, +) -> miette::Result> +where + P: AsRef, +{ + let config = CompilerConfig::new( + QubitSemantics::Qiskit, + OutputSemantics::Qiskit, + ProgramType::File, + Some("Test".into()), + None, + ); + compile_all_with_config(path, sources, config) +} + +pub fn compile_all_fragments

( + path: P, + sources: impl IntoIterator, Arc)>, +) -> miette::Result> +where + P: AsRef, +{ + let config = CompilerConfig::new( + QubitSemantics::Qiskit, + OutputSemantics::OpenQasm, + ProgramType::Fragments, + None, + None, ); + compile_all_with_config(path, sources, config) +} + +fn compile_fragments(source: S) -> miette::Result> +where + S: AsRef, +{ + let config = CompilerConfig::new( + QubitSemantics::Qiskit, + OutputSemantics::OpenQasm, + ProgramType::Fragments, + None, + None, + ); + compile_with_config(source, config) +} + +pub fn compile_all_with_config

( + path: P, + sources: impl IntoIterator, Arc)>, + config: CompilerConfig, +) -> miette::Result> +where + P: AsRef, +{ + let res = parse_all(path, sources)?; + assert!(!res.has_syntax_errors()); + let program = res.program.expect("no program found"); + + let compiler = crate::compiler::QasmCompiler { + source_map: res.source_map, + config, + stmts: vec![], + runtime: RuntimeFunctions::empty(), + symbols: SymbolTable::default(), + errors: res.errors, + }; + + let unit = compiler.compile(&program); + Ok(unit) +} + +fn compile_qasm_to_qir(source: &str, profile: Profile) -> Result> { + let unit = compile(source)?; fail_on_compilation_errors(&unit); let package = unit.package.expect("no package found"); let qir = generate_qir_from_ast(package, unit.source_map, profile).map_err(|errors| { @@ -109,7 +209,7 @@ pub(crate) fn compare_compilation_to_qsharp(unit: &QasmCompileUnit, expected: &s difference::assert_diff!(&qsharp, expected, "\n", 0); } -pub(crate) fn parse(source: S) -> miette::Result> +pub(crate) fn parse(source: S) -> miette::Result> where S: AsRef, { @@ -129,7 +229,7 @@ where pub(crate) fn parse_all

( path: P, sources: impl IntoIterator, Arc)>, -) -> miette::Result> +) -> miette::Result> where P: AsRef, { @@ -148,34 +248,15 @@ where } } -pub fn qasm_to_program_fragments(source: QasmSource, source_map: SourceMap) -> QasmCompileUnit { - qasm_to_program( - source, - source_map, - CompilerConfig::new( - QubitSemantics::Qiskit, - OutputSemantics::OpenQasm, - ProgramType::Fragments, - None, - None, - ), - ) -} - pub fn compile_qasm_to_qsharp_file(source: &str) -> miette::Result> { - let res = parse(source)?; - assert!(!res.has_errors()); - let unit = qasm_to_program( - res.source, - res.source_map, - CompilerConfig::new( - QubitSemantics::Qiskit, - OutputSemantics::OpenQasm, - ProgramType::File, - Some("Test".into()), - None, - ), + let config = CompilerConfig::new( + QubitSemantics::Qiskit, + OutputSemantics::OpenQasm, + ProgramType::File, + Some("Test".into()), + None, ); + let unit = compile_with_config(source, config)?; if unit.has_errors() { let errors = unit.errors.into_iter().map(Report::new).collect(); return Err(errors); @@ -188,19 +269,14 @@ pub fn compile_qasm_to_qsharp_file(source: &str) -> miette::Result miette::Result> { - let res = parse(source)?; - assert!(!res.has_errors()); - let unit = qasm_to_program( - res.source, - res.source_map, - CompilerConfig::new( - QubitSemantics::Qiskit, - OutputSemantics::OpenQasm, - ProgramType::Operation, - Some("Test".into()), - None, - ), + let config = CompilerConfig::new( + QubitSemantics::Qiskit, + OutputSemantics::OpenQasm, + ProgramType::Operation, + Some("Test".into()), + None, ); + let unit = compile_with_config(source, config)?; if unit.has_errors() { let errors = unit.errors.into_iter().map(Report::new).collect(); return Err(errors); @@ -220,19 +296,14 @@ pub fn compile_qasm_to_qsharp_with_semantics( source: &str, qubit_semantics: QubitSemantics, ) -> miette::Result> { - let res = parse(source)?; - assert!(!res.has_errors()); - let unit = qasm_to_program( - res.source, - res.source_map, - CompilerConfig::new( - qubit_semantics, - OutputSemantics::Qiskit, - ProgramType::Fragments, - None, - None, - ), + let config = CompilerConfig::new( + qubit_semantics, + OutputSemantics::Qiskit, + ProgramType::Fragments, + None, + None, ); + let unit = compile_with_config(source, config)?; qsharp_from_qasm_compilation(unit) } @@ -256,19 +327,14 @@ pub fn compile_qasm_stmt_to_qsharp_with_semantics( source: &str, qubit_semantics: QubitSemantics, ) -> miette::Result> { - let res = parse(source)?; - assert!(!res.has_errors()); - let unit = qasm_to_program( - res.source, - res.source_map, - CompilerConfig::new( - qubit_semantics, - OutputSemantics::Qiskit, - ProgramType::Fragments, - None, - None, - ), + let config = CompilerConfig::new( + qubit_semantics, + OutputSemantics::Qiskit, + ProgramType::Fragments, + None, + None, ); + let unit = compile_with_config(source, config)?; if unit.has_errors() { let errors = unit.errors.into_iter().map(Report::new).collect(); return Err(errors); diff --git a/compiler/qsc_qasm3/src/tests/assignment.rs b/compiler/qsc_qasm3/src/tests/assignment.rs index cf454274f6..0b5a3cce26 100644 --- a/compiler/qsc_qasm3/src/tests/assignment.rs +++ b/compiler/qsc_qasm3/src/tests/assignment.rs @@ -3,7 +3,7 @@ mod alias; -use crate::tests::{fail_on_compilation_errors, parse, qasm_to_program_fragments}; +use crate::tests::{compile_fragments, fail_on_compilation_errors}; use miette::Report; #[test] @@ -22,9 +22,7 @@ fn classical() -> miette::Result<(), Vec> { b = a == 0; "; - let res = parse(source)?; - assert!(!res.has_errors()); - let unit = qasm_to_program_fragments(res.source, res.source_map); + let unit = compile_fragments(source)?; fail_on_compilation_errors(&unit); Ok(()) } @@ -45,9 +43,7 @@ fn quantum() -> miette::Result<(), Vec> { b = a == 0; "; - let res = parse(source)?; - assert!(!res.has_errors()); - let unit = qasm_to_program_fragments(res.source, res.source_map); + let unit = compile_fragments(source)?; fail_on_compilation_errors(&unit); Ok(()) } @@ -68,9 +64,7 @@ fn classical_old_style_decls() -> miette::Result<(), Vec> { b = a == 0; "; - let res = parse(source)?; - assert!(!res.has_errors()); - let unit = qasm_to_program_fragments(res.source, res.source_map); + let unit = compile_fragments(source)?; fail_on_compilation_errors(&unit); Ok(()) } diff --git a/compiler/qsc_qasm3/src/tests/assignment/alias.rs b/compiler/qsc_qasm3/src/tests/assignment/alias.rs index 830646fc2b..eca2f60e2c 100644 --- a/compiler/qsc_qasm3/src/tests/assignment/alias.rs +++ b/compiler/qsc_qasm3/src/tests/assignment/alias.rs @@ -1,7 +1,7 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. -use crate::tests::{fail_on_compilation_errors, parse, qasm_to_program_fragments}; +use crate::tests::{compile_fragments, fail_on_compilation_errors}; use miette::Report; #[test] @@ -13,9 +13,7 @@ fn classical() -> miette::Result<(), Vec> { let c = a[{0,1}] ++ b[1:2]; "; - let res = parse(source)?; - assert!(!res.has_errors()); - let unit = qasm_to_program_fragments(res.source, res.source_map); + let unit = compile_fragments(source)?; fail_on_compilation_errors(&unit); Ok(()) } @@ -34,9 +32,7 @@ fn quantum() -> miette::Result<(), Vec> { let e = d[1]; "; - let res = parse(source)?; - assert!(!res.has_errors()); - let unit = qasm_to_program_fragments(res.source, res.source_map); + let unit = compile_fragments(source)?; fail_on_compilation_errors(&unit); Ok(()) } @@ -50,9 +46,7 @@ fn classical_old_style_decls() -> miette::Result<(), Vec> { let c = a[{0,1}] ++ b[1:2]; "; - let res = parse(source)?; - assert!(!res.has_errors()); - let unit = qasm_to_program_fragments(res.source, res.source_map); + let unit = compile_fragments(source)?; fail_on_compilation_errors(&unit); Ok(()) } @@ -71,9 +65,7 @@ fn quantum_old_style_decls() -> miette::Result<(), Vec> { let e = d[1]; "; - let res = parse(source)?; - assert!(!res.has_errors()); - let unit = qasm_to_program_fragments(res.source, res.source_map); + let unit = compile_fragments(source)?; fail_on_compilation_errors(&unit); Ok(()) } diff --git a/compiler/qsc_qasm3/src/tests/declaration.rs b/compiler/qsc_qasm3/src/tests/declaration.rs index bd4136c229..6d742ed4e3 100644 --- a/compiler/qsc_qasm3/src/tests/declaration.rs +++ b/compiler/qsc_qasm3/src/tests/declaration.rs @@ -13,14 +13,13 @@ mod qubit; mod unsigned_integer; use crate::{ - tests::{fail_on_compilation_errors, parse, qasm_to_program_fragments}, + tests::{compile_fragments, compile_with_config, fail_on_compilation_errors}, CompilerConfig, OutputSemantics, ProgramType, QubitSemantics, }; use miette::Report; #[test] -#[ignore = "oq3 parser bug, can't read float with leading dot"] fn classical() -> miette::Result<(), Vec> { let source = r#" int[10] a; @@ -43,9 +42,7 @@ fn classical() -> miette::Result<(), Vec> { float[32] m = .1e+3; "#; - let res = parse(source)?; - assert!(!res.has_errors()); - let unit = qasm_to_program_fragments(res.source, res.source_map); + let unit = compile_fragments(source)?; fail_on_compilation_errors(&unit); Ok(()) } @@ -60,21 +57,16 @@ fn duration_literal() -> miette::Result<(), Vec> { duration dur4 = 1s; "; - let res = parse(source)?; - assert!(!res.has_errors()); - let unit = crate::qasm_to_program( - res.source, - res.source_map, - CompilerConfig::new( - QubitSemantics::Qiskit, - OutputSemantics::OpenQasm, - ProgramType::Fragments, - None, - None, - ), + let config = CompilerConfig::new( + QubitSemantics::Qiskit, + OutputSemantics::OpenQasm, + ProgramType::Fragments, + None, + None, ); + let unit = compile_with_config(source, config).expect("parse failed"); println!("{:?}", unit.errors); - assert!(unit.errors.len() == 5); + assert_eq!(unit.errors.len(), 5); for error in &unit.errors { assert!( error @@ -95,23 +87,21 @@ fn stretch() { stretch s; "; - let res = parse(source).expect("should parse"); - assert!(!res.has_errors()); - let unit = crate::compile::qasm_to_program( - res.source, - res.source_map, - CompilerConfig::new( - QubitSemantics::Qiskit, - OutputSemantics::OpenQasm, - ProgramType::Fragments, - None, - None, - ), + let config = CompilerConfig::new( + QubitSemantics::Qiskit, + OutputSemantics::OpenQasm, + ProgramType::Fragments, + None, + None, ); + let unit = compile_with_config(source, config).expect("parse failed"); assert!(unit.has_errors()); println!("{:?}", unit.errors); - assert!(unit.errors.len() == 1); + assert!(unit.errors.len() == 2); assert!(unit.errors[0] .to_string() .contains("Stretch type values are not supported."),); + assert!(unit.errors[1] + .to_string() + .contains("Stretch default values are not supported."),); } diff --git a/compiler/qsc_qasm3/src/tests/declaration/array.rs b/compiler/qsc_qasm3/src/tests/declaration/array.rs index a6e2a874e5..344d89eb6b 100644 --- a/compiler/qsc_qasm3/src/tests/declaration/array.rs +++ b/compiler/qsc_qasm3/src/tests/declaration/array.rs @@ -4,7 +4,7 @@ mod bit; mod qubit; -use crate::tests::{fail_on_compilation_errors, parse, qasm_to_program_fragments}; +use crate::tests::{compile_fragments, fail_on_compilation_errors}; use miette::Report; #[test] @@ -28,9 +28,7 @@ fn arrays() -> miette::Result<(), Vec> { array[uint[32], 2, 2] x = y; "; - let res = parse(source)?; - assert!(!res.has_errors()); - let unit = qasm_to_program_fragments(res.source, res.source_map); + let unit = compile_fragments(source)?; fail_on_compilation_errors(&unit); Ok(()) } diff --git a/compiler/qsc_qasm3/src/tests/declaration/bool.rs b/compiler/qsc_qasm3/src/tests/declaration/bool.rs index a3bca38d69..18f7c24ce5 100644 --- a/compiler/qsc_qasm3/src/tests/declaration/bool.rs +++ b/compiler/qsc_qasm3/src/tests/declaration/bool.rs @@ -22,22 +22,6 @@ fn bool_default_decl() -> miette::Result<(), Vec> { Ok(()) } -#[test] -fn const_bool_default_decl() -> miette::Result<(), Vec> { - let source = " - const bool x; - "; - - let qsharp = compile_qasm_stmt_to_qsharp(source)?; - expect![ - r#" - let x = false; - "# - ] - .assert_eq(&qsharp); - Ok(()) -} - #[test] fn bool_true_decl() -> miette::Result<(), Vec> { let source = " diff --git a/compiler/qsc_qasm3/src/tests/declaration/complex.rs b/compiler/qsc_qasm3/src/tests/declaration/complex.rs index 88567b1478..039b17c667 100644 --- a/compiler/qsc_qasm3/src/tests/declaration/complex.rs +++ b/compiler/qsc_qasm3/src/tests/declaration/complex.rs @@ -22,22 +22,6 @@ fn implicit_bitness_default_decl() -> miette::Result<(), Vec> { Ok(()) } -#[test] -fn const_implicit_bitness_default_decl() -> miette::Result<(), Vec> { - let source = " - const complex[float] x; - "; - - let qsharp = compile_qasm_stmt_to_qsharp(source)?; - expect![ - r#" - let x = Microsoft.Quantum.Math.Complex(0., 0.); - "# - ] - .assert_eq(&qsharp); - Ok(()) -} - #[test] fn explicit_bitness_default_decl() -> miette::Result<(), Vec> { let source = " @@ -54,22 +38,6 @@ fn explicit_bitness_default_decl() -> miette::Result<(), Vec> { Ok(()) } -#[test] -fn const_explicit_bitness_default_decl() -> miette::Result<(), Vec> { - let source = " - const complex[float[42]] x; - "; - - let qsharp = compile_qasm_stmt_to_qsharp(source)?; - expect![ - r#" - let x = Microsoft.Quantum.Math.Complex(0., 0.); - "# - ] - .assert_eq(&qsharp); - Ok(()) -} - #[test] fn const_implicit_bitness_double_img_only_decl() -> miette::Result<(), Vec> { let source = " diff --git a/compiler/qsc_qasm3/src/tests/declaration/float.rs b/compiler/qsc_qasm3/src/tests/declaration/float.rs index 5b4eced981..e8eb351e99 100644 --- a/compiler/qsc_qasm3/src/tests/declaration/float.rs +++ b/compiler/qsc_qasm3/src/tests/declaration/float.rs @@ -22,22 +22,6 @@ fn implicit_bitness_default_decl() -> miette::Result<(), Vec> { Ok(()) } -#[test] -fn const_default_decl() -> miette::Result<(), Vec> { - let source = " - const float x; - "; - - let qsharp = compile_qasm_stmt_to_qsharp(source)?; - expect![ - r#" - let x = 0.; - "# - ] - .assert_eq(&qsharp); - Ok(()) -} - #[test] fn lit_decl() -> miette::Result<(), Vec> { let source = " @@ -338,7 +322,23 @@ fn const_lit_decl_signed_int_lit_cast_neg() -> miette::Result<(), Vec> { let qsharp = compile_qasm_stmt_to_qsharp(source)?; expect![ r#" - let x = -7.; + let x = Microsoft.Quantum.Convert.IntAsDouble(-7); + "# + ] + .assert_eq(&qsharp); + Ok(()) +} + +#[test] +fn init_float_with_int_value_less_than_safely_representable_values_is_runtime_conversion( +) -> miette::Result<(), Vec> { + let min_exact_int = -(2i64.pow(f64::MANTISSA_DIGITS)); + let next = min_exact_int - 1; + let source = &format!("float a = {next};"); + let qsharp = compile_qasm_stmt_to_qsharp(source)?; + expect![ + r#" + mutable a = Microsoft.Quantum.Convert.IntAsDouble(-9007199254740993); "# ] .assert_eq(&qsharp); diff --git a/compiler/qsc_qasm3/src/tests/declaration/integer.rs b/compiler/qsc_qasm3/src/tests/declaration/integer.rs index 529b9b1231..187431b62d 100644 --- a/compiler/qsc_qasm3/src/tests/declaration/integer.rs +++ b/compiler/qsc_qasm3/src/tests/declaration/integer.rs @@ -54,22 +54,6 @@ fn implicit_bitness_int_default_decl() -> miette::Result<(), Vec> { Ok(()) } -#[test] -fn const_implicit_bitness_int_default_decl() -> miette::Result<(), Vec> { - let source = " - const int x; - "; - - let qsharp = compile_qasm_stmt_to_qsharp(source)?; - expect![ - r#" - let x = 0; - "# - ] - .assert_eq(&qsharp); - Ok(()) -} - #[test] fn const_implicit_bitness_int_lit_decl() -> miette::Result<(), Vec> { let source = " @@ -282,22 +266,6 @@ fn explicit_bitness_int_default_decl() -> miette::Result<(), Vec> { Ok(()) } -#[test] -fn const_explicit_bitness_int_default_decl() -> miette::Result<(), Vec> { - let source = " - const int[10] x; - "; - - let qsharp = compile_qasm_stmt_to_qsharp(source)?; - expect![ - r#" - let x = 0; - "# - ] - .assert_eq(&qsharp); - Ok(()) -} - #[test] fn explicit_bitness_int_decl() -> miette::Result<(), Vec> { let source = " @@ -331,16 +299,18 @@ fn const_explicit_bitness_int_decl() -> miette::Result<(), Vec> { } #[test] -fn implicit_bitness_int_negative_float_decl_causes_semantic_error() { +fn implicit_bitness_int_negative_float_decl_creates_truncation_call( +) -> miette::Result<(), Vec> { let source = " int x = -42.; "; - let Err(errors) = compile_qasm_stmt_to_qsharp(source) else { - panic!("Expected error"); - }; + let qsharp = compile_qasm_stmt_to_qsharp(source)?; expect![ - r#"Cannot assign a value of Float(None, True) type to a classical variable of Int(None, False) type."# + r#" + mutable x = Microsoft.Quantum.Math.Truncate(-42.); + "# ] - .assert_eq(&errors[0].to_string()); + .assert_eq(&qsharp); + Ok(()) } diff --git a/compiler/qsc_qasm3/src/tests/declaration/io/explicit_input.rs b/compiler/qsc_qasm3/src/tests/declaration/io/explicit_input.rs index f479416272..38cd18c777 100644 --- a/compiler/qsc_qasm3/src/tests/declaration/io/explicit_input.rs +++ b/compiler/qsc_qasm3/src/tests/declaration/io/explicit_input.rs @@ -195,7 +195,7 @@ input qubit q; assert!(error[0] .to_string() - .contains("QASM3 Parse Error: Quantum type found in input/output declaration.")); + .contains("expected scalar or array type, found keyword `qubit`")); } #[test] diff --git a/compiler/qsc_qasm3/src/tests/declaration/qubit.rs b/compiler/qsc_qasm3/src/tests/declaration/qubit.rs index de647e60c7..15ec73d002 100644 --- a/compiler/qsc_qasm3/src/tests/declaration/qubit.rs +++ b/compiler/qsc_qasm3/src/tests/declaration/qubit.rs @@ -4,7 +4,7 @@ use expect_test::expect; use miette::Report; -use crate::tests::{fail_on_compilation_errors, parse, qasm_to_program_fragments}; +use crate::tests::{compile_fragments, fail_on_compilation_errors}; use crate::{ tests::{compile_qasm_stmt_to_qsharp, compile_qasm_stmt_to_qsharp_with_semantics}, QubitSemantics, @@ -17,9 +17,7 @@ fn quantum() -> miette::Result<(), Vec> { qubit q2; "; - let res = parse(source)?; - assert!(!res.has_errors()); - let unit = qasm_to_program_fragments(res.source, res.source_map); + let unit = compile_fragments(source)?; fail_on_compilation_errors(&unit); Ok(()) } diff --git a/compiler/qsc_qasm3/src/tests/declaration/unsigned_integer.rs b/compiler/qsc_qasm3/src/tests/declaration/unsigned_integer.rs index 736ccb6bda..0d4f1ba594 100644 --- a/compiler/qsc_qasm3/src/tests/declaration/unsigned_integer.rs +++ b/compiler/qsc_qasm3/src/tests/declaration/unsigned_integer.rs @@ -22,22 +22,6 @@ fn implicit_bitness_int_default_decl() -> miette::Result<(), Vec> { Ok(()) } -#[test] -fn const_implicit_bitness_int_default_decl() -> miette::Result<(), Vec> { - let source = " - const uint x; - "; - - let qsharp = compile_qasm_stmt_to_qsharp(source)?; - expect![ - r#" - let x = 0; - "# - ] - .assert_eq(&qsharp); - Ok(()) -} - #[test] fn const_implicit_bitness_int_lit_decl() -> miette::Result<(), Vec> { let source = " @@ -250,22 +234,6 @@ fn explicit_bitness_int_decl() -> miette::Result<(), Vec> { Ok(()) } -#[test] -fn const_explicit_bitness_int_decl() -> miette::Result<(), Vec> { - let source = " - const uint[10] x; - "; - - let qsharp = compile_qasm_stmt_to_qsharp(source)?; - expect![ - r#" - let x = 0; - "# - ] - .assert_eq(&qsharp); - Ok(()) -} - #[test] fn assigning_uint_to_negative_lit_results_in_semantic_error() { let source = " @@ -280,33 +248,3 @@ fn assigning_uint_to_negative_lit_results_in_semantic_error() { ] .assert_eq(&errors[0].to_string()); } - -#[test] -fn implicit_bitness_uint_const_negative_decl_raises_semantic_error() { - let source = " - const uint x = -42; - "; - - let Err(errors) = compile_qasm_stmt_to_qsharp(source) else { - panic!("Expected error"); - }; - expect![ - r#"Cannot assign a value of Negative Int type to a classical variable of UInt(None, True) type."# - ] - .assert_eq(&errors[0].to_string()); -} - -#[test] -fn explicit_bitness_uint_const_negative_decl_raises_semantic_error() { - let source = " - const uint[32] x = -42; - "; - - let Err(errors) = compile_qasm_stmt_to_qsharp(source) else { - panic!("Expected error"); - }; - expect![ - r#"Cannot assign a value of Negative Int type to a classical variable of UInt(Some(32), True) type."# - ] - .assert_eq(&errors[0].to_string()); -} diff --git a/compiler/qsc_qasm3/src/tests/expression/binary.rs b/compiler/qsc_qasm3/src/tests/expression/binary.rs index f15be6380e..533e9323c5 100644 --- a/compiler/qsc_qasm3/src/tests/expression/binary.rs +++ b/compiler/qsc_qasm3/src/tests/expression/binary.rs @@ -33,8 +33,7 @@ fn binary_expr_fail_parse_missing_lhs() { panic!("Expected error"); }; - expect![r#"QASM3 Parse Error: atom_expr: expected expression"#] - .assert_eq(&errors[0].to_string()); + expect![r#"expected EOF, found `<`"#].assert_eq(&errors[0].to_string()); } #[test] @@ -48,5 +47,5 @@ fn binary_expr_fail_parse_missing_rhs() { panic!("Expected error"); }; - expect![r#"QASM3 Parse Error: expr_bp: expected expression"#].assert_eq(&errors[0].to_string()); + expect![r#"expected expression, found `;`"#].assert_eq(&errors[0].to_string()); } diff --git a/compiler/qsc_qasm3/src/tests/expression/implicit_cast_from_float.rs b/compiler/qsc_qasm3/src/tests/expression/implicit_cast_from_float.rs index 99d28f98dc..d9280621ed 100644 --- a/compiler/qsc_qasm3/src/tests/expression/implicit_cast_from_float.rs +++ b/compiler/qsc_qasm3/src/tests/expression/implicit_cast_from_float.rs @@ -32,7 +32,7 @@ fn explicit_width_to_bit_implicitly() { panic!("Expected error") }; - expect![r#"Cannot cast expression of type Float(Some(64), False) to type Bit(False)"#] + expect![r#"Cannot cast expression of type Float(Some(64), false) to type Bit(false)"#] .assert_eq(&error[0].to_string()); } diff --git a/compiler/qsc_qasm3/src/tests/output.rs b/compiler/qsc_qasm3/src/tests/output.rs index 711b64e9ad..4f6fdbd3fd 100644 --- a/compiler/qsc_qasm3/src/tests/output.rs +++ b/compiler/qsc_qasm3/src/tests/output.rs @@ -2,15 +2,14 @@ // Licensed under the MIT License. use crate::{ - qasm_to_program, - tests::{fail_on_compilation_errors, gen_qsharp, parse}, + tests::{fail_on_compilation_errors, gen_qsharp}, CompilerConfig, OutputSemantics, ProgramType, QubitSemantics, }; use expect_test::expect; use miette::Report; use qsc::target::Profile; -use super::compile_qasm_to_qir; +use super::{compile_qasm_to_qir, compile_with_config}; #[test] fn using_re_semantics_removes_output() -> miette::Result<(), Vec> { @@ -29,20 +28,14 @@ fn using_re_semantics_removes_output() -> miette::Result<(), Vec> { c[0] = measure q[0]; c[1] = measure q[1]; "#; - - let res = parse(source)?; - assert!(!res.has_errors()); - let unit = qasm_to_program( - res.source, - res.source_map, - CompilerConfig::new( - QubitSemantics::Qiskit, - OutputSemantics::ResourceEstimation, - ProgramType::File, - Some("Test".into()), - None, - ), + let config = CompilerConfig::new( + QubitSemantics::Qiskit, + OutputSemantics::ResourceEstimation, + ProgramType::File, + Some("Test".into()), + None, ); + let unit = compile_with_config(source, config).expect("parse failed"); fail_on_compilation_errors(&unit); let qsharp = gen_qsharp(&unit.package.expect("no package found")); expect![[r#" @@ -83,19 +76,14 @@ fn using_qasm_semantics_captures_all_classical_decls_as_output() -> miette::Resu c[1] = measure q[1]; "#; - let res = parse(source)?; - assert!(!res.has_errors()); - let unit = qasm_to_program( - res.source, - res.source_map, - CompilerConfig::new( - QubitSemantics::Qiskit, - OutputSemantics::OpenQasm, - ProgramType::File, - Some("Test".into()), - None, - ), + let config = CompilerConfig::new( + QubitSemantics::Qiskit, + OutputSemantics::OpenQasm, + ProgramType::File, + Some("Test".into()), + None, ); + let unit = compile_with_config(source, config).expect("parse failed"); fail_on_compilation_errors(&unit); let qsharp = gen_qsharp(&unit.package.expect("no package found")); expect![[r#" @@ -136,20 +124,14 @@ fn using_qiskit_semantics_only_bit_array_is_captured_and_reversed( c[0] = measure q[0]; c[1] = measure q[1]; "#; - - let res = parse(source)?; - assert!(!res.has_errors()); - let unit = qasm_to_program( - res.source, - res.source_map, - CompilerConfig::new( - QubitSemantics::Qiskit, - OutputSemantics::Qiskit, - ProgramType::File, - Some("Test".into()), - None, - ), + let config = CompilerConfig::new( + QubitSemantics::Qiskit, + OutputSemantics::Qiskit, + ProgramType::File, + Some("Test".into()), + None, ); + let unit = compile_with_config(source, config).expect("parse failed"); fail_on_compilation_errors(&unit); let qsharp = gen_qsharp(&unit.package.expect("no package found")); expect![[r#" @@ -197,20 +179,14 @@ c2[0] = measure q[2]; c2[1] = measure q[3]; c2[2] = measure q[4]; "#; - - let res = parse(source)?; - assert!(!res.has_errors()); - let unit = qasm_to_program( - res.source, - res.source_map, - CompilerConfig::new( - QubitSemantics::Qiskit, - OutputSemantics::Qiskit, - ProgramType::File, - Some("Test".into()), - None, - ), + let config = CompilerConfig::new( + QubitSemantics::Qiskit, + OutputSemantics::Qiskit, + ProgramType::File, + Some("Test".into()), + None, ); + let unit = compile_with_config(source, config).expect("parse failed"); fail_on_compilation_errors(&unit); let package = unit.package.expect("no package found"); let qsharp = gen_qsharp(&package.clone()); diff --git a/compiler/qsc_qasm3/src/tests/sample_circuits/bell_pair.rs b/compiler/qsc_qasm3/src/tests/sample_circuits/bell_pair.rs index 274bb7ab3f..7e0ee021ee 100644 --- a/compiler/qsc_qasm3/src/tests/sample_circuits/bell_pair.rs +++ b/compiler/qsc_qasm3/src/tests/sample_circuits/bell_pair.rs @@ -2,8 +2,7 @@ // Licensed under the MIT License. use crate::{ - qasm_to_program, - tests::{gen_qsharp, parse, print_compilation_errors}, + tests::{compile_with_config, gen_qsharp, print_compilation_errors}, CompilerConfig, OutputSemantics, ProgramType, QubitSemantics, }; @@ -23,19 +22,15 @@ c[1] = measure q[1]; fn it_compiles() { let source = SOURCE; - let res = parse(source).expect("should parse"); - assert!(!res.has_errors()); - let unit = qasm_to_program( - res.source, - res.source_map, - CompilerConfig::new( - QubitSemantics::Qiskit, - OutputSemantics::OpenQasm, - ProgramType::File, - Some("Test".into()), - None, - ), + let connfig = CompilerConfig::new( + QubitSemantics::Qiskit, + OutputSemantics::OpenQasm, + ProgramType::File, + Some("Test".into()), + None, ); + let unit = compile_with_config(source, connfig).expect("parse failed"); + print_compilation_errors(&unit); assert!(!unit.has_errors()); let Some(package) = &unit.package else { diff --git a/compiler/qsc_qasm3/src/tests/sample_circuits/rgqft_multiplier.rs b/compiler/qsc_qasm3/src/tests/sample_circuits/rgqft_multiplier.rs index a686856fa8..ee0f9387a7 100644 --- a/compiler/qsc_qasm3/src/tests/sample_circuits/rgqft_multiplier.rs +++ b/compiler/qsc_qasm3/src/tests/sample_circuits/rgqft_multiplier.rs @@ -2,8 +2,7 @@ // Licensed under the MIT License. use crate::{ - qasm_to_program, - tests::{gen_qsharp, parse, print_compilation_errors}, + tests::{compile_with_config, gen_qsharp, print_compilation_errors}, CompilerConfig, OutputSemantics, ProgramType, QubitSemantics, }; @@ -11,19 +10,15 @@ use crate::{ fn it_compiles() { let source = SOURCE; - let res = parse(source).expect("should parse"); - assert!(!res.has_errors()); - let unit = qasm_to_program( - res.source, - res.source_map, - CompilerConfig::new( - QubitSemantics::Qiskit, - OutputSemantics::OpenQasm, - ProgramType::File, - Some("Test".into()), - None, - ), + let config = CompilerConfig::new( + QubitSemantics::Qiskit, + OutputSemantics::OpenQasm, + ProgramType::File, + Some("Test".into()), + None, ); + let unit = compile_with_config(source, config).expect("parse failed"); + print_compilation_errors(&unit); assert!(!unit.has_errors()); let Some(package) = &unit.package else { diff --git a/compiler/qsc_qasm3/src/tests/statement/include.rs b/compiler/qsc_qasm3/src/tests/statement/include.rs index 7d096298a4..fc21457b00 100644 --- a/compiler/qsc_qasm3/src/tests/statement/include.rs +++ b/compiler/qsc_qasm3/src/tests/statement/include.rs @@ -2,8 +2,7 @@ // Licensed under the MIT License. use crate::{ - qasm_to_program, - tests::{parse_all, qsharp_from_qasm_compilation}, + tests::{compile_all_with_config, qsharp_from_qasm_compilation}, CompilerConfig, OutputSemantics, ProgramType, QubitSemantics, }; use expect_test::expect; @@ -30,19 +29,14 @@ fn programs_with_includes_can_be_parsed() -> miette::Result<(), Vec> { ("source0.qasm".into(), source.into()), ("custom_intrinsics.inc".into(), custom_intrinsics.into()), ]; - - let res = parse_all("source0.qasm", all_sources)?; - let r = qasm_to_program( - res.source, - res.source_map, - CompilerConfig::new( - QubitSemantics::Qiskit, - OutputSemantics::Qiskit, - ProgramType::File, - Some("Test".into()), - None, - ), + let config = CompilerConfig::new( + QubitSemantics::Qiskit, + OutputSemantics::Qiskit, + ProgramType::File, + Some("Test".into()), + None, ); + let r = compile_all_with_config("source0.qasm", all_sources, config)?; let qsharp = qsharp_from_qasm_compilation(r)?; expect![[r#" namespace qasm3_import { diff --git a/compiler/qsc_qasm3/src/tests/statement/reset.rs b/compiler/qsc_qasm3/src/tests/statement/reset.rs index c6d9a7b86a..97698f9e2d 100644 --- a/compiler/qsc_qasm3/src/tests/statement/reset.rs +++ b/compiler/qsc_qasm3/src/tests/statement/reset.rs @@ -2,8 +2,7 @@ // Licensed under the MIT License. use crate::{ - qasm_to_program, - tests::{fail_on_compilation_errors, gen_qsharp, parse}, + tests::{compile_with_config, fail_on_compilation_errors, gen_qsharp}, CompilerConfig, OutputSemantics, ProgramType, QubitSemantics, }; use expect_test::expect; @@ -24,19 +23,14 @@ fn reset_calls_are_generated_from_qasm() -> miette::Result<(), Vec> { meas[0] = measure q[0]; "#; - let res = parse(source)?; - assert!(!res.has_errors()); - let unit = qasm_to_program( - res.source, - res.source_map, - CompilerConfig::new( - QubitSemantics::Qiskit, - OutputSemantics::Qiskit, - ProgramType::File, - Some("Test".into()), - None, - ), + let config = CompilerConfig::new( + QubitSemantics::Qiskit, + OutputSemantics::Qiskit, + ProgramType::File, + Some("Test".into()), + None, ); + let unit = compile_with_config(source, config)?; fail_on_compilation_errors(&unit); let qsharp = gen_qsharp(&unit.package.expect("no package found")); expect![ From 02681df54da400fffb2cf744128d296e55635580 Mon Sep 17 00:00:00 2001 From: Ian Davis Date: Fri, 21 Mar 2025 07:30:53 -0700 Subject: [PATCH 063/108] Unify compilation and error propagation. (#2247) --- compiler/qsc_qasm3/src/compile.rs | 5 -- compiler/qsc_qasm3/src/compile/tests.rs | 4 +- compiler/qsc_qasm3/src/compiler.rs | 59 +++++++++++++- compiler/qsc_qasm3/src/parse.rs | 4 +- .../src/parser/completion/word_kinds.rs | 3 - compiler/qsc_qasm3/src/semantic.rs | 18 +---- compiler/qsc_qasm3/src/semantic/lowerer.rs | 2 +- compiler/qsc_qasm3/src/semantic/tests.rs | 15 ++-- compiler/qsc_qasm3/src/tests.rs | 81 +++---------------- compiler/qsc_qasm3/src/tests/declaration.rs | 7 +- compiler/qsc_qasm3/src/tests/output.rs | 11 +-- .../src/tests/sample_circuits/bell_pair.rs | 7 +- .../tests/sample_circuits/rgqft_multiplier.rs | 5 +- .../qsc_qasm3/src/tests/statement/include.rs | 6 +- .../qsc_qasm3/src/tests/statement/reset.rs | 5 +- 15 files changed, 107 insertions(+), 125 deletions(-) diff --git a/compiler/qsc_qasm3/src/compile.rs b/compiler/qsc_qasm3/src/compile.rs index ca87db03fc..49b189d2b0 100644 --- a/compiler/qsc_qasm3/src/compile.rs +++ b/compiler/qsc_qasm3/src/compile.rs @@ -68,11 +68,6 @@ pub fn qasm_to_program( source_map: SourceMap, config: CompilerConfig, ) -> QasmCompileUnit { - assert!(!source.has_errors(), "Source has errors"); - assert!( - source.parse_result().have_parse(), - "Source has not been successfully parsed" - ); let compiler = QasmCompiler { source, source_map, diff --git a/compiler/qsc_qasm3/src/compile/tests.rs b/compiler/qsc_qasm3/src/compile/tests.rs index 72b0210d14..8c5fb59d12 100644 --- a/compiler/qsc_qasm3/src/compile/tests.rs +++ b/compiler/qsc_qasm3/src/compile/tests.rs @@ -18,7 +18,7 @@ fn programs_with_includes_with_includes_can_be_compiled() -> miette::Result<(), ("source2.qasm".into(), source2.into()), ]; - let unit = compile_all_fragments("source0.qasm", all_sources)?; + let unit = compile_all_fragments("source0.qasm", all_sources).map_err(|e| vec![e])?; print_compilation_errors(&unit); assert!(!unit.has_errors()); Ok(()) @@ -39,7 +39,7 @@ fn including_stdgates_multiple_times_causes_symbol_redifintion_errors( ("source2.qasm".into(), source2.into()), ]; - let unit = compile_all_fragments("source0.qasm", all_sources)?; + let unit = compile_all_fragments("source0.qasm", all_sources).map_err(|e| vec![e])?; assert!(unit.has_errors()); for error in unit.errors() { assert!(error.to_string().contains("Redefined symbol: ")); diff --git a/compiler/qsc_qasm3/src/compiler.rs b/compiler/qsc_qasm3/src/compiler.rs index eb71d6398c..fd1f51ac53 100644 --- a/compiler/qsc_qasm3/src/compiler.rs +++ b/compiler/qsc_qasm3/src/compiler.rs @@ -1,7 +1,7 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. -use std::rc::Rc; +use std::{path::Path, rc::Rc, sync::Arc}; use num_bigint::BigInt; use qsc_data_structures::span::Span; @@ -19,6 +19,7 @@ use crate::{ build_top_level_ns_with_item, build_tuple_expr, build_unary_op_expr, build_while_stmt, build_wrapped_block_expr, map_qsharp_type_to_ast_ty, wrap_expr_in_parens, }, + io::{InMemorySourceResolver, SourceResolver}, parser::ast::{list_from_iter, List}, runtime::{get_runtime_function_decls, RuntimeFunctions}, semantic::{ @@ -36,6 +37,62 @@ use crate::{ use crate::semantic::ast as semast; use qsc_ast::ast::{self as qsast, NodeId, Package}; +pub fn compile_anon_with_config( + source: S, + config: CompilerConfig, +) -> miette::Result +where + S: AsRef, +{ + let path = std::path::PathBuf::from("Test.qasm"); + let sources = [( + Arc::from(path.display().to_string().as_str()), + Arc::from(source.as_ref()), + )]; + let resolver = InMemorySourceResolver::from_iter(sources); + let source = resolver.resolve(&path)?.1; + compile_with_config(source, &path, &resolver, config) +} + +pub fn compile_all_with_config

( + path: P, + sources: impl IntoIterator, Arc)>, + config: CompilerConfig, +) -> miette::Result +where + P: AsRef, +{ + let resolver = InMemorySourceResolver::from_iter(sources); + let source = resolver.resolve(path.as_ref())?.1; + compile_with_config(source, path, &resolver, config) +} + +pub fn compile_with_config( + source: S, + path: P, + resolver: &R, + config: CompilerConfig, +) -> miette::Result +where + S: AsRef, + P: AsRef, + R: SourceResolver, +{ + let res = crate::semantic::parse_source(source, path, resolver)?; + let program = res.program; + + let compiler = crate::compiler::QasmCompiler { + source_map: res.source_map, + config, + stmts: vec![], + runtime: RuntimeFunctions::empty(), + symbols: res.symbols, + errors: res.errors, + }; + + Ok(compiler.compile(&program)) +} + pub struct QasmCompiler { /// The source map of QASM sources for error reporting. pub source_map: SourceMap, diff --git a/compiler/qsc_qasm3/src/parse.rs b/compiler/qsc_qasm3/src/parse.rs index be6c74ff1d..99c77efccc 100644 --- a/compiler/qsc_qasm3/src/parse.rs +++ b/compiler/qsc_qasm3/src/parse.rs @@ -91,9 +91,7 @@ fn create_source_map(source: &QasmSource) -> SourceMap { for include in source.includes() { collect_source_files(include, &mut files); } - // Map the main source file to the entry point expression - // This may be incorrect, but it's the best we can do for now. - SourceMap::new(files, Some(Arc::from(source.source()))) + SourceMap::new(files, None) } /// Recursively collect all source files from the includes diff --git a/compiler/qsc_qasm3/src/parser/completion/word_kinds.rs b/compiler/qsc_qasm3/src/parser/completion/word_kinds.rs index f300e4d26c..8ad18f770d 100644 --- a/compiler/qsc_qasm3/src/parser/completion/word_kinds.rs +++ b/compiler/qsc_qasm3/src/parser/completion/word_kinds.rs @@ -125,7 +125,6 @@ impl WordKinds { self.iter().filter_map(|p| match p { WordKinds::PathExpr => Some(NameKind::Path(PathKind::Expr)), WordKinds::PathSegment => Some(NameKind::PathSegment), - WordKinds::PrimitiveClass => Some(NameKind::PrimitiveClass), _ => None, }) } @@ -163,8 +162,6 @@ pub enum NameKind { /// A path segment that follows a `.` /// A more specific name kind can only be inferred from a recovered AST. PathSegment, - /// A primitive class, like Eq, Exp, or Add. - PrimitiveClass, } /// A path (see: [`Predictions`]) diff --git a/compiler/qsc_qasm3/src/semantic.rs b/compiler/qsc_qasm3/src/semantic.rs index b63db4daa0..6b470711b6 100644 --- a/compiler/qsc_qasm3/src/semantic.rs +++ b/compiler/qsc_qasm3/src/semantic.rs @@ -7,7 +7,6 @@ use crate::parser::QasmSource; use lowerer::Lowerer; use qsc_frontend::compile::SourceMap; use qsc_frontend::error::WithSource; -use symbols::SymbolTable; use std::path::Path; @@ -26,7 +25,7 @@ pub struct QasmSemanticParseResult { pub source: QasmSource, pub source_map: SourceMap, pub symbols: self::symbols::SymbolTable, - pub program: Option, + pub program: self::ast::Program, pub errors: Vec>, } @@ -106,25 +105,14 @@ where R: SourceResolver, { let res = crate::parser::parse_source(source, path, resolver)?; - let errors = res.all_errors(); - // If there are syntax errors, return early - if res.source.has_errors() { - return Ok(QasmSemanticParseResult { - source: res.source, - source_map: res.source_map, - symbols: SymbolTable::default(), - program: None, - errors, - }); - } - let analyzer = Lowerer::new(res.source, res.source_map); let sem_res = analyzer.lower(); + let errors = sem_res.all_errors(); Ok(QasmSemanticParseResult { source: sem_res.source, source_map: sem_res.source_map, symbols: sem_res.symbols, program: sem_res.program, - errors: sem_res.errors, + errors, }) } diff --git a/compiler/qsc_qasm3/src/semantic/lowerer.rs b/compiler/qsc_qasm3/src/semantic/lowerer.rs index ed887adcca..4c47394b48 100644 --- a/compiler/qsc_qasm3/src/semantic/lowerer.rs +++ b/compiler/qsc_qasm3/src/semantic/lowerer.rs @@ -108,7 +108,7 @@ impl Lowerer { source: self.source, source_map: self.source_map, symbols: self.symbols, - program: Some(program), + program, errors: self.errors, } } diff --git a/compiler/qsc_qasm3/src/semantic/tests.rs b/compiler/qsc_qasm3/src/semantic/tests.rs index 1de94b14c7..268ace49fb 100644 --- a/compiler/qsc_qasm3/src/semantic/tests.rs +++ b/compiler/qsc_qasm3/src/semantic/tests.rs @@ -133,7 +133,7 @@ pub(super) fn check_stmt_kinds(input: &str, expect: &Expect) { fn check_map( input: S, expect: &Expect, - selector: impl FnOnce(&super::ast::Program, &super::SymbolTable) -> String, + selector: impl FnOnce(&super::ast::Program, &super::symbols::SymbolTable) -> String, ) where S: AsRef, { @@ -150,14 +150,12 @@ fn check_map( res.sytax_errors() ); - let program = res.program.expect("no program"); - if errors.is_empty() { - expect.assert_eq(&selector(&program, &res.symbols)); + expect.assert_eq(&selector(&res.program, &res.symbols)); } else { expect.assert_eq(&format!( "{}\n\n{:?}", - program, + res.program, errors .iter() .map(|e| Report::new(e.clone())) @@ -180,7 +178,7 @@ fn check_map_all

( path: P, sources: impl IntoIterator, Arc)>, expect: &Expect, - selector: impl FnOnce(&super::ast::Program, &super::SymbolTable) -> String, + selector: impl FnOnce(&super::ast::Program, &super::symbols::SymbolTable) -> String, ) where P: AsRef, { @@ -201,14 +199,13 @@ fn check_map_all

( "syntax errors: {:?}", res.sytax_errors() ); - let program = res.program.expect("no program"); if errors.is_empty() { - expect.assert_eq(&selector(&program, &res.symbols)); + expect.assert_eq(&selector(&res.program, &res.symbols)); } else { expect.assert_eq(&format!( "{}\n\n{:?}", - program, + res.program, errors .iter() .map(|e| Report::new(e.clone())) diff --git a/compiler/qsc_qasm3/src/tests.rs b/compiler/qsc_qasm3/src/tests.rs index 6b0a9df1dd..1c2a10bee5 100644 --- a/compiler/qsc_qasm3/src/tests.rs +++ b/compiler/qsc_qasm3/src/tests.rs @@ -1,8 +1,7 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. -use crate::runtime::RuntimeFunctions; -use crate::semantic::symbols::SymbolTable; +use crate::compiler::compile_anon_with_config; use crate::{CompilerConfig, OutputSemantics, ProgramType, QasmCompileUnit, QubitSemantics}; use miette::Report; use qsc::interpret::Error; @@ -68,7 +67,7 @@ pub(crate) fn generate_qir_from_ast( ) } -fn compile(source: S) -> miette::Result> +fn compile(source: S) -> miette::Result where S: AsRef, { @@ -79,42 +78,13 @@ where Some("Test".into()), None, ); - compile_with_config(source, config) -} - -fn compile_with_config( - source: S, - config: CompilerConfig, -) -> miette::Result> -where - S: AsRef, -{ - let res = parse(source)?; - if res.has_syntax_errors() { - for e in res.sytax_errors() { - println!("{:?}", Report::new(e.clone())); - } - } - assert!(!res.has_syntax_errors()); - let program = res.program.expect("no program found"); - - let compiler = crate::compiler::QasmCompiler { - source_map: res.source_map, - config, - stmts: vec![], - runtime: RuntimeFunctions::empty(), - symbols: res.symbols, - errors: res.errors, - }; - - let unit = compiler.compile(&program); - Ok(unit) + compile_anon_with_config(source, config) } pub fn compile_all

( path: P, sources: impl IntoIterator, Arc)>, -) -> miette::Result> +) -> miette::Result where P: AsRef, { @@ -125,13 +95,13 @@ where Some("Test".into()), None, ); - compile_all_with_config(path, sources, config) + crate::compiler::compile_all_with_config(path, sources, config) } pub fn compile_all_fragments

( path: P, sources: impl IntoIterator, Arc)>, -) -> miette::Result> +) -> miette::Result where P: AsRef, { @@ -142,7 +112,7 @@ where None, None, ); - compile_all_with_config(path, sources, config) + crate::compiler::compile_all_with_config(path, sources, config) } fn compile_fragments(source: S) -> miette::Result> @@ -156,36 +126,11 @@ where None, None, ); - compile_with_config(source, config) -} - -pub fn compile_all_with_config

( - path: P, - sources: impl IntoIterator, Arc)>, - config: CompilerConfig, -) -> miette::Result> -where - P: AsRef, -{ - let res = parse_all(path, sources)?; - assert!(!res.has_syntax_errors()); - let program = res.program.expect("no program found"); - - let compiler = crate::compiler::QasmCompiler { - source_map: res.source_map, - config, - stmts: vec![], - runtime: RuntimeFunctions::empty(), - symbols: SymbolTable::default(), - errors: res.errors, - }; - - let unit = compiler.compile(&program); - Ok(unit) + compile_anon_with_config(source, config).map_err(|e| vec![e]) } fn compile_qasm_to_qir(source: &str, profile: Profile) -> Result> { - let unit = compile(source)?; + let unit = compile(source).map_err(|e| vec![e])?; fail_on_compilation_errors(&unit); let package = unit.package.expect("no package found"); let qir = generate_qir_from_ast(package, unit.source_map, profile).map_err(|errors| { @@ -256,7 +201,7 @@ pub fn compile_qasm_to_qsharp_file(source: &str) -> miette::Result miette::Result miette::Result<(), Vec> { None, None, ); - let unit = compile_with_config(source, config).expect("parse failed"); + let unit = compile_anon_with_config(source, config).expect("parse failed"); println!("{:?}", unit.errors); assert_eq!(unit.errors.len(), 5); for error in &unit.errors { @@ -94,7 +95,7 @@ fn stretch() { None, None, ); - let unit = compile_with_config(source, config).expect("parse failed"); + let unit = compile_anon_with_config(source, config).expect("parse failed"); assert!(unit.has_errors()); println!("{:?}", unit.errors); assert!(unit.errors.len() == 2); diff --git a/compiler/qsc_qasm3/src/tests/output.rs b/compiler/qsc_qasm3/src/tests/output.rs index 4f6fdbd3fd..b991b88e51 100644 --- a/compiler/qsc_qasm3/src/tests/output.rs +++ b/compiler/qsc_qasm3/src/tests/output.rs @@ -2,6 +2,7 @@ // Licensed under the MIT License. use crate::{ + compiler::compile_anon_with_config, tests::{fail_on_compilation_errors, gen_qsharp}, CompilerConfig, OutputSemantics, ProgramType, QubitSemantics, }; @@ -9,7 +10,7 @@ use expect_test::expect; use miette::Report; use qsc::target::Profile; -use super::{compile_qasm_to_qir, compile_with_config}; +use super::compile_qasm_to_qir; #[test] fn using_re_semantics_removes_output() -> miette::Result<(), Vec> { @@ -35,7 +36,7 @@ fn using_re_semantics_removes_output() -> miette::Result<(), Vec> { Some("Test".into()), None, ); - let unit = compile_with_config(source, config).expect("parse failed"); + let unit = compile_anon_with_config(source, config).expect("parse failed"); fail_on_compilation_errors(&unit); let qsharp = gen_qsharp(&unit.package.expect("no package found")); expect![[r#" @@ -83,7 +84,7 @@ fn using_qasm_semantics_captures_all_classical_decls_as_output() -> miette::Resu Some("Test".into()), None, ); - let unit = compile_with_config(source, config).expect("parse failed"); + let unit = compile_anon_with_config(source, config).expect("parse failed"); fail_on_compilation_errors(&unit); let qsharp = gen_qsharp(&unit.package.expect("no package found")); expect![[r#" @@ -131,7 +132,7 @@ fn using_qiskit_semantics_only_bit_array_is_captured_and_reversed( Some("Test".into()), None, ); - let unit = compile_with_config(source, config).expect("parse failed"); + let unit = compile_anon_with_config(source, config).expect("parse failed"); fail_on_compilation_errors(&unit); let qsharp = gen_qsharp(&unit.package.expect("no package found")); expect![[r#" @@ -186,7 +187,7 @@ c2[2] = measure q[4]; Some("Test".into()), None, ); - let unit = compile_with_config(source, config).expect("parse failed"); + let unit = compile_anon_with_config(source, config).expect("parse failed"); fail_on_compilation_errors(&unit); let package = unit.package.expect("no package found"); let qsharp = gen_qsharp(&package.clone()); diff --git a/compiler/qsc_qasm3/src/tests/sample_circuits/bell_pair.rs b/compiler/qsc_qasm3/src/tests/sample_circuits/bell_pair.rs index 7e0ee021ee..e0643fb9c2 100644 --- a/compiler/qsc_qasm3/src/tests/sample_circuits/bell_pair.rs +++ b/compiler/qsc_qasm3/src/tests/sample_circuits/bell_pair.rs @@ -2,7 +2,8 @@ // Licensed under the MIT License. use crate::{ - tests::{compile_with_config, gen_qsharp, print_compilation_errors}, + compiler::compile_anon_with_config, + tests::{gen_qsharp, print_compilation_errors}, CompilerConfig, OutputSemantics, ProgramType, QubitSemantics, }; @@ -22,14 +23,14 @@ c[1] = measure q[1]; fn it_compiles() { let source = SOURCE; - let connfig = CompilerConfig::new( + let config = CompilerConfig::new( QubitSemantics::Qiskit, OutputSemantics::OpenQasm, ProgramType::File, Some("Test".into()), None, ); - let unit = compile_with_config(source, connfig).expect("parse failed"); + let unit = compile_anon_with_config(source, config).expect("parse failed"); print_compilation_errors(&unit); assert!(!unit.has_errors()); diff --git a/compiler/qsc_qasm3/src/tests/sample_circuits/rgqft_multiplier.rs b/compiler/qsc_qasm3/src/tests/sample_circuits/rgqft_multiplier.rs index ee0f9387a7..e4ca4ace56 100644 --- a/compiler/qsc_qasm3/src/tests/sample_circuits/rgqft_multiplier.rs +++ b/compiler/qsc_qasm3/src/tests/sample_circuits/rgqft_multiplier.rs @@ -2,7 +2,8 @@ // Licensed under the MIT License. use crate::{ - tests::{compile_with_config, gen_qsharp, print_compilation_errors}, + compiler::compile_anon_with_config, + tests::{gen_qsharp, print_compilation_errors}, CompilerConfig, OutputSemantics, ProgramType, QubitSemantics, }; @@ -17,7 +18,7 @@ fn it_compiles() { Some("Test".into()), None, ); - let unit = compile_with_config(source, config).expect("parse failed"); + let unit = compile_anon_with_config(source, config).expect("parse failed"); print_compilation_errors(&unit); assert!(!unit.has_errors()); diff --git a/compiler/qsc_qasm3/src/tests/statement/include.rs b/compiler/qsc_qasm3/src/tests/statement/include.rs index fc21457b00..6d3879f262 100644 --- a/compiler/qsc_qasm3/src/tests/statement/include.rs +++ b/compiler/qsc_qasm3/src/tests/statement/include.rs @@ -2,8 +2,8 @@ // Licensed under the MIT License. use crate::{ - tests::{compile_all_with_config, qsharp_from_qasm_compilation}, - CompilerConfig, OutputSemantics, ProgramType, QubitSemantics, + compiler::compile_all_with_config, tests::qsharp_from_qasm_compilation, CompilerConfig, + OutputSemantics, ProgramType, QubitSemantics, }; use expect_test::expect; use miette::Report; @@ -36,7 +36,7 @@ fn programs_with_includes_can_be_parsed() -> miette::Result<(), Vec> { Some("Test".into()), None, ); - let r = compile_all_with_config("source0.qasm", all_sources, config)?; + let r = compile_all_with_config("source0.qasm", all_sources, config).map_err(|e| vec![e])?; let qsharp = qsharp_from_qasm_compilation(r)?; expect![[r#" namespace qasm3_import { diff --git a/compiler/qsc_qasm3/src/tests/statement/reset.rs b/compiler/qsc_qasm3/src/tests/statement/reset.rs index 97698f9e2d..48ffa34f49 100644 --- a/compiler/qsc_qasm3/src/tests/statement/reset.rs +++ b/compiler/qsc_qasm3/src/tests/statement/reset.rs @@ -2,7 +2,8 @@ // Licensed under the MIT License. use crate::{ - tests::{compile_with_config, fail_on_compilation_errors, gen_qsharp}, + compiler::compile_anon_with_config, + tests::{fail_on_compilation_errors, gen_qsharp}, CompilerConfig, OutputSemantics, ProgramType, QubitSemantics, }; use expect_test::expect; @@ -30,7 +31,7 @@ fn reset_calls_are_generated_from_qasm() -> miette::Result<(), Vec> { Some("Test".into()), None, ); - let unit = compile_with_config(source, config)?; + let unit = compile_anon_with_config(source, config).map_err(|e| vec![e])?; fail_on_compilation_errors(&unit); let qsharp = gen_qsharp(&unit.package.expect("no package found")); expect![ From fd43f81ff0184890b27bad012df8d110d28d59f0 Mon Sep 17 00:00:00 2001 From: orpuente-MS <156957451+orpuente-MS@users.noreply.github.com> Date: Mon, 24 Mar 2025 16:11:16 -0700 Subject: [PATCH 064/108] Add gate calls and the AST items it depends on to the new QASM3 compiler (#2246) This PR adds gate calls and the AST items it depends on to the new QASM3 compiler: - [x] Constant evaluation (needed for array sizes and type widths). - [x] Unit tests. - [x] QuantumDeclStmt - [x] IndexedAssignStmt. - [x] Bitarrays. - [x] MeasureExpr / MeasureStmt. - [x] Compilation to Result type (introduces `LiteralKind::Bit(bool)`) variant. - [x] GatecallStmt - [x] barrier - [x] reset --- compiler/qsc_qasm3/src/compiler.rs | 312 ++- compiler/qsc_qasm3/src/parser/ast.rs | 71 +- .../qsc_qasm3/src/parser/ast/display_utils.rs | 4 +- compiler/qsc_qasm3/src/parser/expr.rs | 44 +- compiler/qsc_qasm3/src/parser/expr/tests.rs | 28 +- compiler/qsc_qasm3/src/parser/mut_visit.rs | 38 +- compiler/qsc_qasm3/src/parser/stmt.rs | 21 +- .../src/parser/stmt/tests/barrier.rs | 27 +- .../src/parser/stmt/tests/box_stmt.rs | 36 +- .../src/parser/stmt/tests/classical_decl.rs | 28 +- .../qsc_qasm3/src/parser/stmt/tests/delay.rs | 30 +- .../src/parser/stmt/tests/for_loops.rs | 18 +- .../src/parser/stmt/tests/gate_call.rs | 129 +- .../qsc_qasm3/src/parser/stmt/tests/gphase.rs | 57 +- .../src/parser/stmt/tests/if_stmt.rs | 54 +- .../stmt/tests/invalid_stmts/gate_calls.rs | 191 +- .../stmt/tests/invalid_stmts/measure.rs | 109 +- .../src/parser/stmt/tests/measure.rs | 87 +- .../src/parser/stmt/tests/old_style_decl.rs | 2 + .../src/parser/stmt/tests/quantum_decl.rs | 4 + .../qsc_qasm3/src/parser/stmt/tests/reset.rs | 30 +- .../src/parser/stmt/tests/while_loops.rs | 18 +- compiler/qsc_qasm3/src/semantic/ast.rs | 132 +- .../qsc_qasm3/src/semantic/ast/const_eval.rs | 628 ++++++ compiler/qsc_qasm3/src/semantic/error.rs | 26 + compiler/qsc_qasm3/src/semantic/lowerer.rs | 789 ++++++-- compiler/qsc_qasm3/src/semantic/symbols.rs | 105 +- compiler/qsc_qasm3/src/semantic/tests.rs | 24 +- .../qsc_qasm3/src/semantic/tests/decls.rs | 97 +- .../src/semantic/tests/decls/angle.rs | 82 +- .../qsc_qasm3/src/semantic/tests/decls/bit.rs | 55 +- .../src/semantic/tests/decls/bool.rs | 24 +- .../src/semantic/tests/decls/complex.rs | 156 +- .../src/semantic/tests/decls/creg.rs | 31 +- .../src/semantic/tests/decls/duration.rs | 2 +- .../src/semantic/tests/decls/float.rs | 360 ++-- .../qsc_qasm3/src/semantic/tests/decls/int.rs | 88 +- .../src/semantic/tests/decls/qreg.rs | 28 +- .../src/semantic/tests/decls/stretch.rs | 2 +- .../src/semantic/tests/decls/uint.rs | 76 +- .../binary/arithmetic_conversions.rs | 336 ++-- .../binary/comparison/bit_to_bit.rs | 160 +- .../binary/comparison/bool_to_bool.rs | 160 +- .../binary/comparison/float_to_float.rs | 120 +- .../binary/comparison/int_to_int.rs | 120 +- .../binary/comparison/uint_to_uint.rs | 96 +- .../semantic/tests/expression/binary/ident.rs | 198 +- .../expression/implicit_cast_from_angle.rs | 276 +-- .../expression/implicit_cast_from_bit.rs | 84 +- .../expression/implicit_cast_from_bool.rs | 416 ++-- .../expression/implicit_cast_from_float.rs | 542 +++--- .../expression/implicit_cast_from_int.rs | 566 +++--- .../src/semantic/tests/statements/for_stmt.rs | 22 +- .../src/semantic/tests/statements/if_stmt.rs | 84 +- .../semantic/tests/statements/switch_stmt.rs | 24 +- .../semantic/tests/statements/while_stmt.rs | 30 +- compiler/qsc_qasm3/src/semantic/types.rs | 267 +-- .../expression/implicit_cast_from_bit.rs | 2 +- .../expression/implicit_cast_from_bitarray.rs | 8 +- .../expression/implicit_cast_from_float.rs | 2 +- compiler/qsc_qasm3/src/tests/statement.rs | 1 + .../src/tests/statement/const_eval.rs | 1732 +++++++++++++++++ .../src/tests/statement/gate_call.rs | 40 + .../qsc_qasm3/src/tests/statement/if_stmt.rs | 2 +- .../qsc_qasm3/src/tests/statement/measure.rs | 32 +- .../qsc_qasm3/src/tests/statement/switch.rs | 16 +- .../src/tests/statement/while_loop.rs | 2 +- 67 files changed, 6279 insertions(+), 3102 deletions(-) create mode 100644 compiler/qsc_qasm3/src/semantic/ast/const_eval.rs create mode 100644 compiler/qsc_qasm3/src/tests/statement/const_eval.rs diff --git a/compiler/qsc_qasm3/src/compiler.rs b/compiler/qsc_qasm3/src/compiler.rs index fd1f51ac53..b23563aa43 100644 --- a/compiler/qsc_qasm3/src/compiler.rs +++ b/compiler/qsc_qasm3/src/compiler.rs @@ -9,29 +9,37 @@ use qsc_frontend::{compile::SourceMap, error::WithSource}; use crate::{ ast_builder::{ - build_arg_pat, build_array_reverse_expr, build_assignment_statement, build_binary_expr, - build_cast_call, build_cast_call_two_params, build_classical_decl, build_complex_from_expr, - build_convert_call_expr, build_if_expr_then_expr_else_expr, build_implicit_return_stmt, - build_lit_bigint_expr, build_lit_bool_expr, build_lit_complex_expr, build_lit_double_expr, - build_lit_int_expr, build_lit_result_array_expr_from_bitstring, build_lit_result_expr, - build_math_call_from_exprs, build_math_call_no_params, build_operation_with_stmts, - build_path_ident_expr, build_stmt_semi_from_expr, build_stmt_semi_from_expr_with_span, - build_top_level_ns_with_item, build_tuple_expr, build_unary_op_expr, build_while_stmt, - build_wrapped_block_expr, map_qsharp_type_to_ast_ty, wrap_expr_in_parens, + build_arg_pat, build_array_reverse_expr, build_assignment_statement, build_barrier_call, + build_binary_expr, build_cast_call, build_cast_call_two_params, build_classical_decl, + build_complex_from_expr, build_convert_call_expr, build_expr_array_expr, + build_gate_call_param_expr, build_gate_call_with_params_and_callee, + build_if_expr_then_expr_else_expr, build_implicit_return_stmt, + build_indexed_assignment_statement, build_lit_bigint_expr, build_lit_bool_expr, + build_lit_complex_expr, build_lit_double_expr, build_lit_int_expr, + build_lit_result_array_expr_from_bitstring, build_lit_result_expr, + build_managed_qubit_alloc, build_math_call_from_exprs, build_math_call_no_params, + build_measure_call, build_operation_with_stmts, build_path_ident_expr, build_reset_call, + build_stmt_semi_from_expr, build_stmt_semi_from_expr_with_span, + build_top_level_ns_with_item, build_tuple_expr, build_unary_op_expr, + build_unmanaged_qubit_alloc, build_unmanaged_qubit_alloc_array, build_while_stmt, + build_wrapped_block_expr, managed_qubit_alloc_array, map_qsharp_type_to_ast_ty, + wrap_expr_in_parens, }, io::{InMemorySourceResolver, SourceResolver}, parser::ast::{list_from_iter, List}, runtime::{get_runtime_function_decls, RuntimeFunctions}, semantic::{ ast::{ - BinaryOpExpr, Cast, DiscreteSet, Expr, FunctionCall, IndexElement, IndexExpr, IndexSet, - IndexedIdent, LiteralKind, MeasureExpr, TimeUnit, UnaryOpExpr, + BinaryOpExpr, Cast, DiscreteSet, Expr, FunctionCall, GateOperand, GateOperandKind, + IndexElement, IndexExpr, IndexSet, IndexedIdent, LiteralKind, MeasureExpr, TimeUnit, + UnaryOpExpr, }, symbols::{IOKind, Symbol, SymbolId, SymbolTable}, types::{ArrayDimensions, Type}, SemanticErrorKind, }, CompilerConfig, OperationSignature, OutputSemantics, ProgramType, QasmCompileUnit, + QubitSemantics, }; use crate::semantic::ast as semast; @@ -355,10 +363,11 @@ impl QasmCompiler { semast::StmtKind::Include(stmt) => self.compile_include_stmt(stmt), semast::StmtKind::InputDeclaration(stmt) => self.compile_input_decl_stmt(stmt), semast::StmtKind::OutputDeclaration(stmt) => self.compile_output_decl_stmt(stmt), - semast::StmtKind::Measure(stmt) => self.compile_measure_stmt(stmt), + semast::StmtKind::MeasureArrow(stmt) => self.compile_measure_stmt(stmt), semast::StmtKind::Pragma(stmt) => self.compile_pragma_stmt(stmt), semast::StmtKind::QuantumGateDefinition(stmt) => self.compile_gate_decl_stmt(stmt), - semast::StmtKind::QuantumDecl(stmt) => self.compile_quantum_decl_stmt(stmt), + semast::StmtKind::QubitDecl(stmt) => self.compile_qubit_decl_stmt(stmt), + semast::StmtKind::QubitArrayDecl(stmt) => self.compile_qubit_array_decl_stmt(stmt), semast::StmtKind::Reset(stmt) => self.compile_reset_stmt(stmt), semast::StmtKind::Return(stmt) => self.compile_return_stmt(stmt), semast::StmtKind::Switch(stmt) => self.compile_switch_stmt(stmt), @@ -377,8 +386,7 @@ impl QasmCompiler { } fn compile_assign_stmt(&mut self, stmt: &semast::AssignStmt) -> Option { - let symbol = &self.symbols[stmt.symbol_id]; - let symbol = symbol.clone(); + let symbol = self.symbols[stmt.symbol_id].clone(); let name = &symbol.name; let stmt_span = stmt.span; @@ -394,8 +402,40 @@ impl QasmCompiler { &mut self, stmt: &semast::IndexedAssignStmt, ) -> Option { - self.push_unimplemented_error_message("indexed assignment statements", stmt.span); - None + let symbol = self.symbols[stmt.symbol_id].clone(); + + let indices: Vec<_> = stmt + .indices + .iter() + .filter_map(|elem| self.compile_index_element(elem)) + .collect(); + + let rhs = self.compile_expr(&stmt.rhs); + + if stmt.indices.len() != 1 { + self.push_unimplemented_error_message( + "multi-dimensional array index expressions", + stmt.span, + ); + return None; + } + + if indices.len() != stmt.indices.len() { + return None; + } + + // Use the `?` operator after compiling checking all other errors. + let (rhs, index_expr) = (rhs?, indices[0].clone()); + + let stmt = build_indexed_assignment_statement( + symbol.span, + symbol.name.clone(), + index_expr, + rhs, + stmt.span, + ); + + Some(stmt) } fn compile_assign_op_stmt(&mut self, stmt: &semast::AssignOpStmt) -> Option { @@ -404,8 +444,20 @@ impl QasmCompiler { } fn compile_barrier_stmt(&mut self, stmt: &semast::BarrierStmt) -> Option { - self.push_unimplemented_error_message("barrier statements", stmt.span); - None + let qubits: Vec<_> = stmt + .qubits + .iter() + .filter_map(|q| self.compile_gate_operand(q)) + .collect(); + + if stmt.qubits.len() != qubits.len() { + // if any of the qubit arguments failed to compile we can't proceed. + // This can happen if the qubit is not defined. + return None; + } + + self.runtime.insert(RuntimeFunctions::Barrier); + Some(build_barrier_call(stmt.span)) } fn compile_box_stmt(&mut self, stmt: &semast::BoxStmt) -> Option { @@ -443,8 +495,7 @@ impl QasmCompiler { &mut self, decl: &semast::ClassicalDeclarationStmt, ) -> Option { - let symbol = &self.symbols[decl.symbol_id]; - let symbol = symbol.clone(); + let symbol = &self.symbols[decl.symbol_id].clone(); let name = &symbol.name; let is_const = symbol.ty.is_const(); let ty_span = decl.ty_span; @@ -453,18 +504,10 @@ impl QasmCompiler { let qsharp_ty = &symbol.qsharp_ty; let expr = decl.init_expr.as_ref(); - let stmt = match expr { - semast::ValueExpression::Expr(expr) => { - let expr = self.compile_expr(expr)?; - build_classical_decl( - name, is_const, ty_span, decl_span, name_span, qsharp_ty, expr, - ) - } - semast::ValueExpression::Measurement(expr) => { - let expr = self.compile_measure_expr(expr)?; - build_stmt_semi_from_expr_with_span(expr, decl.span) - } - }; + let expr = self.compile_expr(expr)?; + let stmt = build_classical_decl( + name, is_const, ty_span, decl_span, name_span, qsharp_ty, expr, + ); Some(stmt) } @@ -480,7 +523,7 @@ impl QasmCompiler { } fn compile_delay_stmt(&mut self, stmt: &semast::DelayStmt) -> Option { - self.push_unimplemented_error_message("dealy statements", stmt.span); + self.push_unimplemented_error_message("delay statements", stmt.span); None } @@ -510,11 +553,101 @@ impl QasmCompiler { } fn compile_gate_call_stmt(&mut self, stmt: &semast::GateCall) -> Option { - self.push_unimplemented_error_message("gate call statements", stmt.span); - None + let symbol = self.symbols[stmt.symbol_id].clone(); + if symbol.name == "U" { + self.runtime |= RuntimeFunctions::U; + } + let mut qubits: Vec<_> = stmt + .qubits + .iter() + .filter_map(|q| self.compile_gate_operand(q)) + .collect(); + let args: Vec<_> = stmt + .args + .iter() + .filter_map(|arg| self.compile_expr(arg)) + .collect(); + + if qubits.len() != stmt.qubits.len() || args.len() != stmt.args.len() { + return None; + } + + // Take the number of qubit args that the gates expects from the source qubits. + let gate_qubits = qubits.split_off(qubits.len() - stmt.quantum_arity as usize); + // Then merge the classical args with the qubit args. This will give + // us the args for the call prior to wrapping in tuples for controls. + let args: Vec<_> = args.into_iter().chain(gate_qubits).collect(); + let mut args = build_gate_call_param_expr(args, qubits.len()); + let mut callee = build_path_ident_expr(&symbol.name, symbol.span, stmt.span); + + for modifier in &stmt.modifiers { + match &modifier.kind { + semast::GateModifierKind::Inv => { + callee = build_unary_op_expr( + qsast::UnOp::Functor(qsast::Functor::Adj), + callee, + modifier.span, + ); + } + semast::GateModifierKind::Pow(expr) => { + let exponent_expr = self.compile_expr(expr)?; + self.runtime |= RuntimeFunctions::Pow; + args = build_tuple_expr(vec![exponent_expr, callee, args]); + callee = build_path_ident_expr("__Pow__", modifier.span, stmt.span); + } + semast::GateModifierKind::Ctrl(num_ctrls) => { + // remove the last n qubits from the qubit list + if qubits.len() < *num_ctrls as usize { + let kind = SemanticErrorKind::InvalidNumberOfQubitArgs( + *num_ctrls as usize, + qubits.len(), + modifier.span, + ); + self.push_semantic_error(kind); + return None; + } + let ctrl = qubits.split_off(qubits.len() - *num_ctrls as usize); + let ctrls = build_expr_array_expr(ctrl, modifier.span); + args = build_tuple_expr(vec![ctrls, args]); + callee = build_unary_op_expr( + qsast::UnOp::Functor(qsast::Functor::Ctl), + callee, + modifier.span, + ); + } + semast::GateModifierKind::NegCtrl(num_ctrls) => { + // remove the last n qubits from the qubit list + if qubits.len() < *num_ctrls as usize { + let kind = SemanticErrorKind::InvalidNumberOfQubitArgs( + *num_ctrls as usize, + qubits.len(), + modifier.span, + ); + self.push_semantic_error(kind); + return None; + } + let ctrl = qubits.split_off(qubits.len() - *num_ctrls as usize); + let ctrls = build_expr_array_expr(ctrl, modifier.span); + let lit_0 = build_lit_int_expr(0, Span::default()); + args = build_tuple_expr(vec![lit_0, callee, ctrls, args]); + callee = + build_path_ident_expr("ApplyControlledOnInt", modifier.span, stmt.span); + } + } + } + + // This should never be reached, since semantic analysis during lowering + // makes sure the arities match. + if !qubits.is_empty() { + return None; + } + + let expr = build_gate_call_with_params_and_callee(args, callee, stmt.span); + Some(build_stmt_semi_from_expr(expr)) } fn compile_gphase_stmt(&mut self, stmt: &semast::GPhase) -> Option { + self.runtime |= RuntimeFunctions::Gphase; self.push_unimplemented_error_message("gphase statements", stmt.span); None } @@ -562,7 +695,7 @@ impl QasmCompiler { Some(stmt) } - fn compile_measure_stmt(&mut self, stmt: &semast::MeasureStmt) -> Option { + fn compile_measure_stmt(&mut self, stmt: &semast::MeasureArrowStmt) -> Option { self.push_unimplemented_error_message("measure statements", stmt.span); None } @@ -580,17 +713,46 @@ impl QasmCompiler { None } - fn compile_quantum_decl_stmt( + fn compile_qubit_decl_stmt(&mut self, stmt: &semast::QubitDeclaration) -> Option { + let symbol = self.symbols[stmt.symbol_id].clone(); + let name = &symbol.name; + let name_span = symbol.span; + + let stmt = match self.config.qubit_semantics { + QubitSemantics::QSharp => build_managed_qubit_alloc(name, stmt.span, name_span), + QubitSemantics::Qiskit => build_unmanaged_qubit_alloc(name, stmt.span, name_span), + }; + Some(stmt) + } + + fn compile_qubit_array_decl_stmt( &mut self, - stmt: &semast::QubitDeclaration, + stmt: &semast::QubitArrayDeclaration, ) -> Option { - self.push_unimplemented_error_message("quantum decl statements", stmt.span); - None + let symbol = self.symbols[stmt.symbol_id].clone(); + let name = &symbol.name; + let name_span = symbol.span; + + let stmt = match self.config.qubit_semantics { + QubitSemantics::QSharp => { + managed_qubit_alloc_array(name, stmt.size, stmt.span, name_span, stmt.size_span) + } + QubitSemantics::Qiskit => build_unmanaged_qubit_alloc_array( + name, + stmt.size, + stmt.span, + name_span, + stmt.size_span, + ), + }; + Some(stmt) } fn compile_reset_stmt(&mut self, stmt: &semast::ResetStmt) -> Option { - self.push_unimplemented_error_message("reset statements", stmt.span); - None + let operand = self.compile_gate_operand(&stmt.operand)?; + let operand_span = operand.span; + let expr = build_reset_call(operand, stmt.reset_token_span, operand_span); + Some(build_stmt_semi_from_expr(expr)) } fn compile_return_stmt(&mut self, stmt: &semast::ReturnStmt) -> Option { @@ -604,7 +766,7 @@ impl QasmCompiler { } fn compile_while_stmt(&mut self, stmt: &semast::WhileLoop) -> Option { - let condition = self.compile_expr(&stmt.while_condition)?; + let condition = self.compile_expr(&stmt.condition)?; match &*stmt.body.kind { semast::StmtKind::Block(block) => { let block = self.compile_block(block); @@ -628,16 +790,15 @@ impl QasmCompiler { } fn compile_expr(&mut self, expr: &semast::Expr) -> Option { - let span = expr.span; match expr.kind.as_ref() { semast::ExprKind::Err => { // todo: determine if we should push an error here // Are we going to allow trying to compile a program with semantic errors? None } - semast::ExprKind::Ident(symbol_id) => self.compile_ident_expr(*symbol_id, span), + semast::ExprKind::Ident(symbol_id) => self.compile_ident_expr(*symbol_id), semast::ExprKind::IndexedIdentifier(indexed_ident) => { - self.compile_indexed_ident_expr(indexed_ident, span) + self.compile_indexed_ident_expr(indexed_ident) } semast::ExprKind::UnaryOp(unary_op_expr) => self.compile_unary_op_expr(unary_op_expr), semast::ExprKind::BinaryOp(binary_op_expr) => { @@ -650,11 +811,13 @@ impl QasmCompiler { semast::ExprKind::Cast(cast) => self.compile_cast_expr(cast), semast::ExprKind::IndexExpr(index_expr) => self.compile_index_expr(index_expr), semast::ExprKind::Paren(pexpr) => self.compile_paren_expr(pexpr, expr.span), + semast::ExprKind::Measure(expr) => self.compile_measure_expr(expr), } } - fn compile_ident_expr(&mut self, symbol_id: SymbolId, span: Span) -> Option { + fn compile_ident_expr(&mut self, symbol_id: SymbolId) -> Option { let symbol = &self.symbols[symbol_id]; + let span = symbol.span; let expr = match symbol.name.as_str() { "euler" | "ℇ" => build_math_call_no_params("E", span), "pi" | "π" => build_math_call_no_params("PI", span), @@ -677,11 +840,8 @@ impl QasmCompiler { /// The lowerer eliminated indexed identifiers with zero indices. /// So we are safe to assume that the indices are non-empty. - fn compile_indexed_ident_expr( - &mut self, - indexed_ident: &IndexedIdent, - span: Span, - ) -> Option { + fn compile_indexed_ident_expr(&mut self, indexed_ident: &IndexedIdent) -> Option { + let span = indexed_ident.span; let index: Vec<_> = indexed_ident .indices .iter() @@ -754,6 +914,7 @@ impl QasmCompiler { LiteralKind::Bitstring(big_int, width) => { Self::compile_bitstring_literal(big_int, *width, span) } + LiteralKind::Bit(value) => Self::compile_bit_literal(*value, span), LiteralKind::Bool(value) => Self::compile_bool_literal(*value, span), LiteralKind::Duration(value, time_unit) => { self.compile_duration_literal(*value, *time_unit, span) @@ -820,8 +981,29 @@ impl QasmCompiler { } fn compile_measure_expr(&mut self, expr: &MeasureExpr) -> Option { - self.push_unimplemented_error_message("measure expressions", expr.span); - None + let call_span = expr.span; + let name_span = expr.measure_token_span; + let arg = self.compile_gate_operand(&expr.operand)?; + let operand_span = expr.operand.span; + let expr = build_measure_call(arg, name_span, operand_span, call_span); + Some(expr) + } + + fn compile_gate_operand(&mut self, op: &GateOperand) -> Option { + match &op.kind { + GateOperandKind::HardwareQubit(hw) => { + // We don't support hardware qubits, so we need to push an error + // but we can still create an identifier for the hardware qubit + // and let the rest of the containing expression compile to + // catch any other errors + let message = "Hardware qubit operands"; + self.push_unsupported_error_message(message, op.span); + let ident = build_path_ident_expr(hw.name.clone(), hw.span, op.span); + Some(ident) + } + GateOperandKind::Expr(expr) => self.compile_expr(expr), + GateOperandKind::Err => None, + } } fn compile_index_element(&mut self, elem: &IndexElement) -> Option { @@ -837,7 +1019,15 @@ impl QasmCompiler { } fn compile_index_set(&mut self, set: &IndexSet) -> Option { - self.push_unimplemented_error_message("index set expressions", set.span); + // This is a temporary limitation. We can only handle + // single index expressions for now. + if set.values.len() == 1 { + if let semast::IndexSetItem::Expr(expr) = &*set.values[0] { + return self.compile_expr(expr); + } + } + + self.push_unsupported_error_message("index set expressions with multiple values", set.span); None } @@ -846,6 +1036,10 @@ impl QasmCompiler { None } + fn compile_bit_literal(value: bool, span: Span) -> Option { + Some(build_lit_result_expr(value.into(), span)) + } + fn compile_bool_literal(value: bool, span: Span) -> Option { Some(build_lit_bool_expr(value, span)) } @@ -862,7 +1056,11 @@ impl QasmCompiler { fn compile_bitstring_literal(value: &BigInt, width: u32, span: Span) -> Option { let width = width as usize; - let bitstring = format!("Bitstring(\"{:0>width$}\")", value.to_str_radix(2)); + let bitstring = if value == &BigInt::ZERO && width == 0 { + "Bitstring(\"\")".to_string() + } else { + format!("Bitstring(\"{:0>width$}\")", value.to_str_radix(2)) + }; Some(build_lit_result_array_expr_from_bitstring(bitstring, span)) } diff --git a/compiler/qsc_qasm3/src/parser/ast.rs b/compiler/qsc_qasm3/src/parser/ast.rs index eafa672e70..cdcd499e30 100644 --- a/compiler/qsc_qasm3/src/parser/ast.rs +++ b/compiler/qsc_qasm3/src/parser/ast.rs @@ -147,6 +147,7 @@ impl WithSpan for Path { #[derive(Clone, Debug)] pub struct MeasureExpr { pub span: Span, + pub measure_token_span: Span, pub operand: GateOperand, } @@ -248,29 +249,51 @@ impl Display for UnaryOp { } #[derive(Clone, Debug, Default)] -pub enum GateOperand { +pub struct GateOperand { + pub span: Span, + pub kind: GateOperandKind, +} + +impl WithSpan for GateOperand { + fn with_span(self, span: Span) -> Self { + Self { + span, + kind: self.kind.with_span(span), + } + } +} + +impl Display for GateOperand { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + writeln_header(f, "GateOperand", self.span)?; + write_field(f, "kind", &self.kind) + } +} + +#[derive(Clone, Debug, Default)] +pub enum GateOperandKind { IndexedIdent(Box), HardwareQubit(Box), #[default] Err, } -impl Display for GateOperand { +impl Display for GateOperandKind { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { match self { - GateOperand::IndexedIdent(ident) => write!(f, "{ident}"), - GateOperand::HardwareQubit(qubit) => write!(f, "{qubit}"), - GateOperand::Err => write!(f, "Error"), + Self::IndexedIdent(ident) => write!(f, "{ident}"), + Self::HardwareQubit(qubit) => write!(f, "{qubit}"), + Self::Err => write!(f, "Error"), } } } -impl WithSpan for GateOperand { +impl WithSpan for GateOperandKind { fn with_span(self, span: Span) -> Self { match self { - GateOperand::IndexedIdent(ident) => GateOperand::IndexedIdent(ident.with_span(span)), - GateOperand::HardwareQubit(qubit) => GateOperand::HardwareQubit(qubit.with_span(span)), - GateOperand::Err => GateOperand::Err, + Self::IndexedIdent(ident) => Self::IndexedIdent(ident.with_span(span)), + Self::HardwareQubit(qubit) => Self::HardwareQubit(qubit.with_span(span)), + Self::Err => Self::Err, } } } @@ -335,7 +358,7 @@ pub enum StmtKind { GPhase(GPhase), Include(IncludeStmt), IODeclaration(IODeclaration), - Measure(MeasureStmt), + Measure(MeasureArrowStmt), Pragma(Pragma), QuantumGateDefinition(QuantumGateDefinition), QuantumDecl(QubitDeclaration), @@ -445,12 +468,14 @@ impl Display for BarrierStmt { #[derive(Clone, Debug)] pub struct ResetStmt { pub span: Span, + pub reset_token_span: Span, pub operand: Box, } impl Display for ResetStmt { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { writeln_header(f, "ResetStmt", self.span)?; + writeln_field(f, "reset_token_span", &self.reset_token_span)?; write_field(f, "operand", &self.operand) } } @@ -991,6 +1016,7 @@ impl Display for IncludeStmt { #[derive(Clone, Debug)] pub struct QubitDeclaration { pub span: Span, + pub ty_span: Span, pub qubit: Ident, pub size: Option, } @@ -998,6 +1024,7 @@ pub struct QubitDeclaration { impl Display for QubitDeclaration { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { writeln_header(f, "QubitDeclaration", self.span)?; + writeln_field(f, "ty_span", &self.ty_span)?; writeln_field(f, "ident", &self.qubit)?; write_opt_field(f, "size", self.size.as_ref()) } @@ -1110,15 +1137,15 @@ impl Display for BoxStmt { } #[derive(Clone, Debug)] -pub struct MeasureStmt { +pub struct MeasureArrowStmt { pub span: Span, pub measurement: MeasureExpr, pub target: Option>, } -impl Display for MeasureStmt { +impl Display for MeasureArrowStmt { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { - writeln_header(f, "MeasureStmt", self.span)?; + writeln_header(f, "MeasureArrowStmt", self.span)?; writeln_field(f, "measurement", &self.measurement)?; write_opt_field(f, "target", self.target.as_ref()) } @@ -1129,7 +1156,7 @@ pub struct ClassicalDeclarationStmt { pub span: Span, pub ty: Box, pub identifier: Ident, - pub init_expr: Option>, + pub init_expr: Option>, } impl Display for ClassicalDeclarationStmt { @@ -1142,16 +1169,16 @@ impl Display for ClassicalDeclarationStmt { } #[derive(Clone, Debug)] -pub enum ValueExpression { +pub enum ValueExpr { Expr(Expr), Measurement(MeasureExpr), } -impl Display for ValueExpression { +impl Display for ValueExpr { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { match self { - ValueExpression::Expr(expr) => write!(f, "{expr}"), - ValueExpression::Measurement(measure) => write!(f, "{measure}"), + Self::Expr(expr) => write!(f, "{expr}"), + Self::Measurement(measure) => write!(f, "{measure}"), } } } @@ -1178,7 +1205,7 @@ pub struct ConstantDeclStmt { pub span: Span, pub ty: TypeDef, pub identifier: Box, - pub init_expr: Expr, + pub init_expr: ValueExpr, } impl Display for ConstantDeclStmt { @@ -1339,7 +1366,7 @@ impl Display for DefStmt { #[derive(Clone, Debug)] pub struct ReturnStmt { pub span: Span, - pub expr: Option>, + pub expr: Option>, } impl Display for ReturnStmt { @@ -1470,7 +1497,7 @@ impl Display for ExprKind { pub struct AssignStmt { pub span: Span, pub lhs: IndexedIdent, - pub rhs: Expr, + pub rhs: ValueExpr, } impl Display for AssignStmt { @@ -1486,7 +1513,7 @@ pub struct AssignOpStmt { pub span: Span, pub op: BinOp, pub lhs: IndexedIdent, - pub rhs: Expr, + pub rhs: ValueExpr, } impl Display for AssignOpStmt { diff --git a/compiler/qsc_qasm3/src/parser/ast/display_utils.rs b/compiler/qsc_qasm3/src/parser/ast/display_utils.rs index 37b29d3198..4ffbb29042 100644 --- a/compiler/qsc_qasm3/src/parser/ast/display_utils.rs +++ b/compiler/qsc_qasm3/src/parser/ast/display_utils.rs @@ -129,7 +129,7 @@ pub(crate) fn writeln_opt_field( writeln!(f) } -/// Writes an field of a structure to the given buffer +/// Writes a field of a structure to the given buffer /// or stream with an additional indententation level. /// The field must be an iterable. pub(crate) fn write_list_field<'write, 'itemref, 'item, T, I>( @@ -148,7 +148,7 @@ where write_list(&mut indent, vals) } -/// Writes an field of a structure to the given buffer +/// Writes a field of a structure to the given buffer /// or stream with an additional indententation level. /// The field must be an iterable. /// Inserts a newline afterwards. diff --git a/compiler/qsc_qasm3/src/parser/expr.rs b/compiler/qsc_qasm3/src/parser/expr.rs index b26b9204f6..f657496b3a 100644 --- a/compiler/qsc_qasm3/src/parser/expr.rs +++ b/compiler/qsc_qasm3/src/parser/expr.rs @@ -24,9 +24,9 @@ use crate::parser::Result; use super::{ ast::{ list_from_iter, BinOp, BinaryOpExpr, Cast, DiscreteSet, Expr, ExprKind, FunctionCall, - GateOperand, HardwareQubit, Ident, IndexElement, IndexExpr, IndexSet, IndexSetItem, - IndexedIdent, List, Lit, LiteralKind, MeasureExpr, RangeDefinition, TimeUnit, TypeDef, - UnaryOp, UnaryOpExpr, ValueExpression, Version, + GateOperand, GateOperandKind, HardwareQubit, Ident, IndexElement, IndexExpr, IndexSet, + IndexSetItem, IndexedIdent, List, Lit, LiteralKind, MeasureExpr, RangeDefinition, TimeUnit, + TypeDef, UnaryOp, UnaryOpExpr, ValueExpr, Version, }, completion::WordKinds, error::{Error, ErrorKind}, @@ -708,9 +708,11 @@ fn lit_array_element(s: &mut ParserContext) -> Result { lit_array(s) } -pub(super) fn value_expr(s: &mut ParserContext) -> Result> { +/// These are expressions allowed in classical declarations. +/// Grammar: `arrayLiteral | expression | measureExpression`. +pub(super) fn declaration_expr(s: &mut ParserContext) -> Result { if let Some(measurement) = opt(s, measure_expr)? { - return Ok(Box::new(ValueExpression::Measurement(measurement))); + return Ok(ValueExpr::Measurement(measurement)); } let expr = if let Some(expr) = opt(s, expr)? { @@ -719,7 +721,17 @@ pub(super) fn value_expr(s: &mut ParserContext) -> Result> lit_array(s)? }; - Ok(Box::new(ValueExpression::Expr(expr))) + Ok(ValueExpr::Expr(expr)) +} + +/// These are expressions allowed in `Assign`, `AssignOp`, and return stmts. +/// Grammar: `expression | measureExpression`. +pub(super) fn expr_or_measurement(s: &mut ParserContext) -> Result { + if let Some(measurement) = opt(s, measure_expr)? { + return Ok(ValueExpr::Measurement(measurement)); + } + + Ok(ValueExpr::Expr(expr(s)?)) } pub(crate) fn expr_list(s: &mut ParserContext) -> Result> { @@ -729,18 +741,28 @@ pub(crate) fn expr_list(s: &mut ParserContext) -> Result> { pub(crate) fn measure_expr(s: &mut ParserContext) -> Result { let lo = s.peek().span.lo; token(s, TokenKind::Measure)?; + let measure_token_span = s.span(lo); + let operand = gate_operand(s)?; Ok(MeasureExpr { span: s.span(lo), - operand: gate_operand(s)?, + measure_token_span, + operand, }) } pub(crate) fn gate_operand(s: &mut ParserContext) -> Result { - if let Some(indexed_ident) = opt(s, indexed_identifier)? { - return Ok(GateOperand::IndexedIdent(Box::new(indexed_ident))); - } - Ok(GateOperand::HardwareQubit(Box::new(hardware_qubit(s)?))) + let lo = s.peek().span.lo; + let kind = if let Some(indexed_ident) = opt(s, indexed_identifier)? { + GateOperandKind::IndexedIdent(Box::new(indexed_ident)) + } else { + GateOperandKind::HardwareQubit(Box::new(hardware_qubit(s)?)) + }; + + Ok(GateOperand { + span: s.span(lo), + kind, + }) } fn hardware_qubit(s: &mut ParserContext) -> Result { diff --git a/compiler/qsc_qasm3/src/parser/expr/tests.rs b/compiler/qsc_qasm3/src/parser/expr/tests.rs index 8bb39cccfc..29e92bb9cd 100644 --- a/compiler/qsc_qasm3/src/parser/expr/tests.rs +++ b/compiler/qsc_qasm3/src/parser/expr/tests.rs @@ -1146,8 +1146,9 @@ fn measure_hardware_qubit() { super::measure_expr, "measure $12", &expect![[r#" - MeasureExpr [0-7]: - operand: HardwareQubit [8-11]: 12"#]], + MeasureExpr [0-11]: + operand: GateOperand [8-11]: + kind: HardwareQubit [8-11]: 12"#]], ); } @@ -1157,17 +1158,18 @@ fn measure_indexed_identifier() { super::measure_expr, "measure qubits[1][2]", &expect![[r#" - MeasureExpr [0-7]: - operand: IndexedIdent [8-20]: - name: Ident [8-14] "qubits" - index_span: [14-20] - indices: - IndexSet [15-16]: - values: - Expr [15-16]: Lit: Int(1) - IndexSet [18-19]: - values: - Expr [18-19]: Lit: Int(2)"#]], + MeasureExpr [0-20]: + operand: GateOperand [8-20]: + kind: IndexedIdent [8-20]: + name: Ident [8-14] "qubits" + index_span: [14-20] + indices: + IndexSet [15-16]: + values: + Expr [15-16]: Lit: Int(1) + IndexSet [18-19]: + values: + Expr [18-19]: Lit: Int(2)"#]], ); } diff --git a/compiler/qsc_qasm3/src/parser/mut_visit.rs b/compiler/qsc_qasm3/src/parser/mut_visit.rs index 9f76826f03..838523708f 100644 --- a/compiler/qsc_qasm3/src/parser/mut_visit.rs +++ b/compiler/qsc_qasm3/src/parser/mut_visit.rs @@ -9,12 +9,12 @@ use super::ast::{ BoxStmt, BreakStmt, CalibrationGrammarStmt, CalibrationStmt, Cast, ClassicalDeclarationStmt, ConstantDeclStmt, ContinueStmt, DefCalStmt, DefStmt, DelayStmt, DiscreteSet, EndStmt, EnumerableSet, Expr, ExprStmt, ExternDecl, ExternParameter, ForStmt, FunctionCall, GPhase, - GateCall, GateModifierKind, GateOperand, HardwareQubit, IODeclaration, Ident, Identifier, - IfStmt, IncludeStmt, IndexElement, IndexExpr, IndexSet, IndexSetItem, IndexedIdent, Lit, - LiteralKind, MeasureExpr, MeasureStmt, Pragma, Program, QuantumGateDefinition, + GateCall, GateModifierKind, GateOperand, GateOperandKind, HardwareQubit, IODeclaration, Ident, + Identifier, IfStmt, IncludeStmt, IndexElement, IndexExpr, IndexSet, IndexSetItem, IndexedIdent, + Lit, LiteralKind, MeasureArrowStmt, MeasureExpr, Pragma, Program, QuantumGateDefinition, QuantumGateModifier, QuantumTypedParameter, QubitDeclaration, RangeDefinition, ResetStmt, ReturnStmt, ScalarType, ScalarTypedParameter, Stmt, StmtKind, SwitchCase, SwitchStmt, TypeDef, - TypedParameter, UnaryOp, UnaryOpExpr, ValueExpression, Version, WhileLoop, + TypedParameter, UnaryOp, UnaryOpExpr, ValueExpr, Version, WhileLoop, }; pub trait MutVisitor: Sized { @@ -130,7 +130,7 @@ pub trait MutVisitor: Sized { walk_io_declaration_stmt(self, stmt); } - fn visit_measure_stmt(&mut self, stmt: &mut MeasureStmt) { + fn visit_measure_stmt(&mut self, stmt: &mut MeasureArrowStmt) { walk_measure_stmt(self, stmt); } @@ -190,7 +190,7 @@ pub trait MutVisitor: Sized { walk_index_expr(self, expr); } - fn visit_value_expr(&mut self, expr: &mut ValueExpression) { + fn visit_value_expr(&mut self, expr: &mut ValueExpr) { walk_value_expr(self, expr); } @@ -379,14 +379,14 @@ fn walk_alias_decl_stmt(vis: &mut impl MutVisitor, stmt: &mut AliasDeclStmt) { fn walk_assign_stmt(vis: &mut impl MutVisitor, stmt: &mut AssignStmt) { vis.visit_span(&mut stmt.span); vis.visit_indexed_ident(&mut stmt.lhs); - vis.visit_expr(&mut stmt.rhs); + vis.visit_value_expr(&mut stmt.rhs); } fn walk_assign_op_stmt(vis: &mut impl MutVisitor, stmt: &mut AssignOpStmt) { vis.visit_span(&mut stmt.span); vis.visit_indexed_ident(&mut stmt.lhs); vis.visit_binop(&mut stmt.op); - vis.visit_expr(&mut stmt.rhs); + vis.visit_value_expr(&mut stmt.rhs); } fn walk_barrier_stmt(vis: &mut impl MutVisitor, stmt: &mut BarrierStmt) { @@ -432,7 +432,7 @@ fn walk_const_decl_stmt(vis: &mut impl MutVisitor, stmt: &mut ConstantDeclStmt) vis.visit_span(&mut stmt.span); vis.visit_tydef(&mut stmt.ty); vis.visit_ident(&mut stmt.identifier); - vis.visit_expr(&mut stmt.init_expr); + vis.visit_value_expr(&mut stmt.init_expr); } fn walk_continue_stmt(vis: &mut impl MutVisitor, stmt: &mut ContinueStmt) { @@ -535,7 +535,7 @@ fn walk_io_declaration_stmt(vis: &mut impl MutVisitor, stmt: &mut IODeclaration) vis.visit_ident(&mut stmt.ident); } -fn walk_measure_stmt(vis: &mut impl MutVisitor, stmt: &mut MeasureStmt) { +fn walk_measure_stmt(vis: &mut impl MutVisitor, stmt: &mut MeasureArrowStmt) { vis.visit_span(&mut stmt.span); stmt.target .iter_mut() @@ -557,12 +557,14 @@ fn walk_quantum_gate_definition_stmt(vis: &mut impl MutVisitor, stmt: &mut Quant fn walk_quantum_decl_stmt(vis: &mut impl MutVisitor, stmt: &mut QubitDeclaration) { vis.visit_span(&mut stmt.span); + vis.visit_span(&mut stmt.ty_span); vis.visit_ident(&mut stmt.qubit); stmt.size.iter_mut().for_each(|s| vis.visit_expr(s)); } fn walk_reset_stmt(vis: &mut impl MutVisitor, stmt: &mut ResetStmt) { vis.visit_span(&mut stmt.span); + vis.visit_span(&mut stmt.reset_token_span); vis.visit_gate_operand(&mut stmt.operand); } @@ -644,15 +646,16 @@ pub fn walk_index_expr(vis: &mut impl MutVisitor, expr: &mut IndexExpr) { vis.visit_index_element(&mut expr.index); } -pub fn walk_value_expr(vis: &mut impl MutVisitor, expr: &mut ValueExpression) { +pub fn walk_value_expr(vis: &mut impl MutVisitor, expr: &mut ValueExpr) { match &mut *expr { - ValueExpression::Expr(expr) => vis.visit_expr(expr), - ValueExpression::Measurement(measure_expr) => vis.visit_measure_expr(measure_expr), + ValueExpr::Expr(expr) => vis.visit_expr(expr), + ValueExpr::Measurement(measure_expr) => vis.visit_measure_expr(measure_expr), } } pub fn walk_measure_expr(vis: &mut impl MutVisitor, expr: &mut MeasureExpr) { vis.visit_span(&mut expr.span); + vis.visit_span(&mut expr.measure_token_span); vis.visit_gate_operand(&mut expr.operand); } @@ -706,10 +709,11 @@ pub fn walk_index_set_item(vis: &mut impl MutVisitor, item: &mut IndexSetItem) { } pub fn walk_gate_operand(vis: &mut impl MutVisitor, operand: &mut GateOperand) { - match operand { - GateOperand::IndexedIdent(ident) => vis.visit_indexed_ident(ident), - GateOperand::HardwareQubit(hardware_qubit) => vis.visit_hardware_qubit(hardware_qubit), - GateOperand::Err => {} + vis.visit_span(&mut operand.span); + match &mut operand.kind { + GateOperandKind::IndexedIdent(ident) => vis.visit_indexed_ident(ident), + GateOperandKind::HardwareQubit(hardware_qubit) => vis.visit_hardware_qubit(hardware_qubit), + GateOperandKind::Err => {} } } diff --git a/compiler/qsc_qasm3/src/parser/stmt.rs b/compiler/qsc_qasm3/src/parser/stmt.rs index c2915be11b..27be880edc 100644 --- a/compiler/qsc_qasm3/src/parser/stmt.rs +++ b/compiler/qsc_qasm3/src/parser/stmt.rs @@ -27,7 +27,7 @@ use super::ast::{ DelayStmt, EndStmt, EnumerableSet, Expr, ExprKind, ExprStmt, ExternDecl, ExternParameter, FloatType, ForStmt, FunctionCall, GPhase, GateCall, GateModifierKind, GateOperand, IODeclaration, IOKeyword, Ident, Identifier, IfStmt, IncludeStmt, IndexElement, IndexExpr, - IndexSetItem, IndexedIdent, IntType, List, LiteralKind, MeasureStmt, Pragma, + IndexSetItem, IndexedIdent, IntType, List, LiteralKind, MeasureArrowStmt, Pragma, QuantumGateDefinition, QuantumGateModifier, QuantumTypedParameter, QubitDeclaration, RangeDefinition, ResetStmt, ReturnStmt, ScalarType, ScalarTypeKind, ScalarTypedParameter, Stmt, StmtKind, SwitchCase, SwitchStmt, TypeDef, TypedParameter, UIntType, WhileLoop, @@ -204,7 +204,7 @@ fn disambiguate_ident(s: &mut ParserContext, indexed_ident: IndexedIdent) -> Res let lo = indexed_ident.span.lo; if s.peek().kind == TokenKind::Eq { s.advance(); - let expr = expr::expr(s)?; + let expr = expr::expr_or_measurement(s)?; recovering_semi(s); Ok(StmtKind::Assign(AssignStmt { span: s.span(lo), @@ -214,7 +214,7 @@ fn disambiguate_ident(s: &mut ParserContext, indexed_ident: IndexedIdent) -> Res } else if let TokenKind::BinOpEq(op) = s.peek().kind { s.advance(); let op = expr::closed_bin_op(op); - let expr = expr::expr(s)?; + let expr = expr::expr_or_measurement(s)?; recovering_semi(s); Ok(StmtKind::AssignOp(AssignOpStmt { span: s.span(lo), @@ -631,7 +631,7 @@ fn gate_params(s: &mut ParserContext<'_>) -> Result> { fn parse_return(s: &mut ParserContext) -> Result { let lo = s.peek().span.lo; token(s, TokenKind::Keyword(crate::keyword::Keyword::Return))?; - let expr = opt(s, expr::value_expr)?; + let expr = opt(s, expr::expr_or_measurement)?.map(Box::new); recovering_semi(s); Ok(StmtKind::Return(ReturnStmt { span: s.span(lo), @@ -643,11 +643,13 @@ fn parse_return(s: &mut ParserContext) -> Result { fn parse_quantum_decl(s: &mut ParserContext) -> Result { let lo = s.peek().span.lo; let size = qubit_type(s)?; + let ty_span = s.span(lo); let ident = prim::ident(s)?; recovering_semi(s); Ok(StmtKind::QuantumDecl(QubitDeclaration { span: s.span(lo), + ty_span, qubit: ident, size, })) @@ -714,7 +716,7 @@ fn parse_non_constant_classical_decl( let identifier = prim::ident(s)?; let init_expr = if s.peek().kind == TokenKind::Eq { s.advance(); - Some(expr::value_expr(s)?) + Some(Box::new(expr::declaration_expr(s)?)) } else { None }; @@ -736,7 +738,7 @@ fn parse_constant_classical_decl(s: &mut ParserContext) -> Result { let ty = scalar_or_array_type(s)?; let identifier = Box::new(prim::ident(s)?); token(s, TokenKind::Eq)?; - let init_expr = expr::expr(s)?; + let init_expr = expr::declaration_expr(s)?; recovering_semi(s); let decl = ConstantDeclStmt { span: s.span(lo), @@ -878,6 +880,7 @@ fn qreg_decl(s: &mut ParserContext) -> Result { recovering_semi(s); Ok(StmtKind::QuantumDecl(QubitDeclaration { span: s.span(lo), + ty_span: s.span(lo), qubit: identifier, size, })) @@ -1751,17 +1754,19 @@ fn parse_delay(s: &mut ParserContext) -> Result { fn parse_reset(s: &mut ParserContext) -> Result { let lo = s.peek().span.lo; token(s, TokenKind::Keyword(Keyword::Reset))?; + let reset_token_span = s.span(lo); let operand = Box::new(gate_operand(s)?); recovering_semi(s); Ok(ResetStmt { span: s.span(lo), + reset_token_span, operand, }) } /// Grammar: `measureExpression (ARROW indexedIdentifier)? SEMICOLON`. -fn parse_measure_stmt(s: &mut ParserContext) -> Result { +fn parse_measure_stmt(s: &mut ParserContext) -> Result { let lo = s.peek().span.lo; let measure = expr::measure_expr(s)?; @@ -1772,7 +1777,7 @@ fn parse_measure_stmt(s: &mut ParserContext) -> Result { recovering_semi(s); - Ok(MeasureStmt { + Ok(MeasureArrowStmt { span: s.span(lo), measurement: measure, target, diff --git a/compiler/qsc_qasm3/src/parser/stmt/tests/barrier.rs b/compiler/qsc_qasm3/src/parser/stmt/tests/barrier.rs index 7622ada484..2a37910565 100644 --- a/compiler/qsc_qasm3/src/parser/stmt/tests/barrier.rs +++ b/compiler/qsc_qasm3/src/parser/stmt/tests/barrier.rs @@ -15,18 +15,21 @@ fn barrier() { annotations: kind: BarrierStmt [0-20]: operands: - IndexedIdent [8-9]: - name: Ident [8-9] "r" - index_span: [0-0] - indices: - IndexedIdent [11-15]: - name: Ident [11-12] "q" - index_span: [12-15] - indices: - IndexSet [13-14]: - values: - Expr [13-14]: Lit: Int(0) - HardwareQubit [17-19]: 2"#]], + GateOperand [8-9]: + kind: IndexedIdent [8-9]: + name: Ident [8-9] "r" + index_span: [0-0] + indices: + GateOperand [11-15]: + kind: IndexedIdent [11-15]: + name: Ident [11-12] "q" + index_span: [12-15] + indices: + IndexSet [13-14]: + values: + Expr [13-14]: Lit: Int(0) + GateOperand [17-19]: + kind: HardwareQubit [17-19]: 2"#]], ); } diff --git a/compiler/qsc_qasm3/src/parser/stmt/tests/box_stmt.rs b/compiler/qsc_qasm3/src/parser/stmt/tests/box_stmt.rs index ac659f9903..5695656f8b 100644 --- a/compiler/qsc_qasm3/src/parser/stmt/tests/box_stmt.rs +++ b/compiler/qsc_qasm3/src/parser/stmt/tests/box_stmt.rs @@ -28,10 +28,11 @@ fn box_stmt() { args: duration: qubits: - IndexedIdent [21-23]: - name: Ident [21-23] "q0" - index_span: [0-0] - indices: + GateOperand [21-23]: + kind: IndexedIdent [21-23]: + name: Ident [21-23] "q0" + index_span: [0-0] + indices: Stmt [33-44]: annotations: kind: GateCall [33-44]: @@ -41,10 +42,11 @@ fn box_stmt() { Expr [36-39]: Lit: Float(2.4) duration: qubits: - IndexedIdent [41-43]: - name: Ident [41-43] "q1" - index_span: [0-0] - indices: "#]], + GateOperand [41-43]: + kind: IndexedIdent [41-43]: + name: Ident [41-43] "q1" + index_span: [0-0] + indices: "#]], ); } @@ -71,10 +73,11 @@ fn box_stmt_with_designator() { args: duration: qubits: - IndexedIdent [26-28]: - name: Ident [26-28] "q0" - index_span: [0-0] - indices: + GateOperand [26-28]: + kind: IndexedIdent [26-28]: + name: Ident [26-28] "q0" + index_span: [0-0] + indices: Stmt [38-49]: annotations: kind: GateCall [38-49]: @@ -84,9 +87,10 @@ fn box_stmt_with_designator() { Expr [41-44]: Lit: Float(2.4) duration: qubits: - IndexedIdent [46-48]: - name: Ident [46-48] "q1" - index_span: [0-0] - indices: "#]], + GateOperand [46-48]: + kind: IndexedIdent [46-48]: + name: Ident [46-48] "q1" + index_span: [0-0] + indices: "#]], ); } diff --git a/compiler/qsc_qasm3/src/parser/stmt/tests/classical_decl.rs b/compiler/qsc_qasm3/src/parser/stmt/tests/classical_decl.rs index c71849d8cd..2ae53268ce 100644 --- a/compiler/qsc_qasm3/src/parser/stmt/tests/classical_decl.rs +++ b/compiler/qsc_qasm3/src/parser/stmt/tests/classical_decl.rs @@ -1071,8 +1071,9 @@ fn measure_hardware_qubit_decl() { type: ScalarType [0-3]: BitType [0-3]: size: ident: Ident [4-7] "res" - init_expr: MeasureExpr [10-17]: - operand: HardwareQubit [18-21]: 12"#]], + init_expr: MeasureExpr [10-21]: + operand: GateOperand [18-21]: + kind: HardwareQubit [18-21]: 12"#]], ); } @@ -1088,16 +1089,17 @@ fn measure_register_decl() { type: ScalarType [0-3]: BitType [0-3]: size: ident: Ident [4-7] "res" - init_expr: MeasureExpr [10-17]: - operand: IndexedIdent [18-30]: - name: Ident [18-24] "qubits" - index_span: [24-30] - indices: - IndexSet [25-26]: - values: - Expr [25-26]: Lit: Int(2) - IndexSet [28-29]: - values: - Expr [28-29]: Lit: Int(3)"#]], + init_expr: MeasureExpr [10-30]: + operand: GateOperand [18-30]: + kind: IndexedIdent [18-30]: + name: Ident [18-24] "qubits" + index_span: [24-30] + indices: + IndexSet [25-26]: + values: + Expr [25-26]: Lit: Int(2) + IndexSet [28-29]: + values: + Expr [28-29]: Lit: Int(3)"#]], ); } diff --git a/compiler/qsc_qasm3/src/parser/stmt/tests/delay.rs b/compiler/qsc_qasm3/src/parser/stmt/tests/delay.rs index 174a38ed06..35bf3bb7d1 100644 --- a/compiler/qsc_qasm3/src/parser/stmt/tests/delay.rs +++ b/compiler/qsc_qasm3/src/parser/stmt/tests/delay.rs @@ -16,19 +16,21 @@ fn delay() { kind: DelayStmt [0-20]: duration: Expr [6-7]: Ident [6-7] "a" qubits: - IndexedIdent [9-13]: - name: Ident [9-10] "q" - index_span: [10-13] - indices: - IndexSet [11-12]: - values: - Expr [11-12]: Lit: Int(0) - IndexedIdent [15-19]: - name: Ident [15-16] "q" - index_span: [16-19] - indices: - IndexSet [17-18]: - values: - Expr [17-18]: Lit: Int(1)"#]], + GateOperand [9-13]: + kind: IndexedIdent [9-13]: + name: Ident [9-10] "q" + index_span: [10-13] + indices: + IndexSet [11-12]: + values: + Expr [11-12]: Lit: Int(0) + GateOperand [15-19]: + kind: IndexedIdent [15-19]: + name: Ident [15-16] "q" + index_span: [16-19] + indices: + IndexSet [17-18]: + values: + Expr [17-18]: Lit: Int(1)"#]], ); } diff --git a/compiler/qsc_qasm3/src/parser/stmt/tests/for_loops.rs b/compiler/qsc_qasm3/src/parser/stmt/tests/for_loops.rs index eabf39e37b..a0debdda1c 100644 --- a/compiler/qsc_qasm3/src/parser/stmt/tests/for_loops.rs +++ b/compiler/qsc_qasm3/src/parser/stmt/tests/for_loops.rs @@ -283,10 +283,11 @@ fn single_stmt_for_stmt() { args: duration: qubits: - IndexedIdent [18-19]: - name: Ident [18-19] "q" - index_span: [0-0] - indices: "#]], + GateOperand [18-19]: + kind: IndexedIdent [18-19]: + name: Ident [18-19] "q" + index_span: [0-0] + indices: "#]], ); } @@ -355,10 +356,11 @@ fn nested_single_stmt_for_stmt() { args: duration: qubits: - IndexedIdent [34-35]: - name: Ident [34-35] "q" - index_span: [0-0] - indices: "#]], + GateOperand [34-35]: + kind: IndexedIdent [34-35]: + name: Ident [34-35] "q" + index_span: [0-0] + indices: "#]], ); } diff --git a/compiler/qsc_qasm3/src/parser/stmt/tests/gate_call.rs b/compiler/qsc_qasm3/src/parser/stmt/tests/gate_call.rs index e59d1e46e7..103b3f066f 100644 --- a/compiler/qsc_qasm3/src/parser/stmt/tests/gate_call.rs +++ b/compiler/qsc_qasm3/src/parser/stmt/tests/gate_call.rs @@ -19,10 +19,11 @@ fn gate_call() { args: duration: qubits: - IndexedIdent [2-4]: - name: Ident [2-4] "q0" - index_span: [0-0] - indices: "#]], + GateOperand [2-4]: + kind: IndexedIdent [2-4]: + name: Ident [2-4] "q0" + index_span: [0-0] + indices: "#]], ); } @@ -40,13 +41,14 @@ fn gate_call_qubit_register() { args: duration: qubits: - IndexedIdent [2-6]: - name: Ident [2-3] "q" - index_span: [3-6] - indices: - IndexSet [4-5]: - values: - Expr [4-5]: Lit: Int(2)"#]], + GateOperand [2-6]: + kind: IndexedIdent [2-6]: + name: Ident [2-3] "q" + index_span: [3-6] + indices: + IndexSet [4-5]: + values: + Expr [4-5]: Lit: Int(2)"#]], ); } @@ -64,17 +66,19 @@ fn gate_multiple_qubits() { args: duration: qubits: - IndexedIdent [5-7]: - name: Ident [5-7] "q0" - index_span: [0-0] - indices: - IndexedIdent [9-13]: - name: Ident [9-10] "q" - index_span: [10-13] - indices: - IndexSet [11-12]: - values: - Expr [11-12]: Lit: Int(4)"#]], + GateOperand [5-7]: + kind: IndexedIdent [5-7]: + name: Ident [5-7] "q0" + index_span: [0-0] + indices: + GateOperand [9-13]: + kind: IndexedIdent [9-13]: + name: Ident [9-10] "q" + index_span: [10-13] + indices: + IndexSet [11-12]: + values: + Expr [11-12]: Lit: Int(4)"#]], ); } @@ -125,10 +129,11 @@ fn gate_call_with_parameters() { rhs: Expr [8-9]: Lit: Int(2) duration: qubits: - IndexedIdent [11-13]: - name: Ident [11-13] "q0" - index_span: [0-0] - indices: "#]], + GateOperand [11-13]: + kind: IndexedIdent [11-13]: + name: Ident [11-13] "q0" + index_span: [0-0] + indices: "#]], ); } @@ -147,10 +152,11 @@ fn gate_call_inv_modifier() { args: duration: qubits: - IndexedIdent [8-10]: - name: Ident [8-10] "q0" - index_span: [0-0] - indices: "#]], + GateOperand [8-10]: + kind: IndexedIdent [8-10]: + name: Ident [8-10] "q0" + index_span: [0-0] + indices: "#]], ); } @@ -174,18 +180,21 @@ fn gate_call_ctrl_inv_modifiers() { rhs: Expr [24-25]: Lit: Int(2) duration: qubits: - IndexedIdent [27-29]: - name: Ident [27-29] "c1" - index_span: [0-0] - indices: - IndexedIdent [31-33]: - name: Ident [31-33] "c2" - index_span: [0-0] - indices: - IndexedIdent [35-37]: - name: Ident [35-37] "q0" - index_span: [0-0] - indices: "#]], + GateOperand [27-29]: + kind: IndexedIdent [27-29]: + name: Ident [27-29] "c1" + index_span: [0-0] + indices: + GateOperand [31-33]: + kind: IndexedIdent [31-33]: + name: Ident [31-33] "c2" + index_span: [0-0] + indices: + GateOperand [35-37]: + kind: IndexedIdent [35-37]: + name: Ident [35-37] "q0" + index_span: [0-0] + indices: "#]], ); } @@ -224,10 +233,11 @@ fn parametrized_gate_call() { Expr [8-9]: Lit: Int(3) duration: qubits: - IndexedIdent [11-12]: - name: Ident [11-12] "q" - index_span: [0-0] - indices: "#]], + GateOperand [11-12]: + kind: IndexedIdent [11-12]: + name: Ident [11-12] "q" + index_span: [0-0] + indices: "#]], ); } @@ -247,10 +257,11 @@ fn parametrized_gate_call_with_designator() { Expr [8-9]: Lit: Int(3) duration: Expr [11-12]: Lit: Int(1) qubits: - IndexedIdent [14-15]: - name: Ident [14-15] "q" - index_span: [0-0] - indices: "#]], + GateOperand [14-15]: + kind: IndexedIdent [14-15]: + name: Ident [14-15] "q" + index_span: [0-0] + indices: "#]], ); } @@ -286,10 +297,11 @@ fn gate_call_with_designator() { args: duration: Expr [2-5]: Lit: Duration(2.0, Us) qubits: - IndexedIdent [7-8]: - name: Ident [7-8] "q" - index_span: [0-0] - indices: "#]], + GateOperand [7-8]: + kind: IndexedIdent [7-8]: + name: Ident [7-8] "q" + index_span: [0-0] + indices: "#]], ); } @@ -307,10 +319,11 @@ fn gate_call_with_invalid_designator() { args: duration: Expr [2-5]: Lit: Duration(2.0, Us) qubits: - IndexedIdent [10-11]: - name: Ident [10-11] "q" - index_span: [0-0] - indices: + GateOperand [10-11]: + kind: IndexedIdent [10-11]: + name: Ident [10-11] "q" + index_span: [0-0] + indices: [ Error( diff --git a/compiler/qsc_qasm3/src/parser/stmt/tests/gphase.rs b/compiler/qsc_qasm3/src/parser/stmt/tests/gphase.rs index be32d2fc52..f2b5cb94f0 100644 --- a/compiler/qsc_qasm3/src/parser/stmt/tests/gphase.rs +++ b/compiler/qsc_qasm3/src/parser/stmt/tests/gphase.rs @@ -39,10 +39,11 @@ fn gphase_qubit_ident() { Expr [7-8]: Ident [7-8] "a" duration: qubits: - IndexedIdent [10-12]: - name: Ident [10-12] "q0" - index_span: [0-0] - indices: "#]], + GateOperand [10-12]: + kind: IndexedIdent [10-12]: + name: Ident [10-12] "q0" + index_span: [0-0] + indices: "#]], ); } @@ -60,13 +61,14 @@ fn gphase_qubit_register() { Expr [7-8]: Ident [7-8] "a" duration: qubits: - IndexedIdent [10-14]: - name: Ident [10-11] "q" - index_span: [11-14] - indices: - IndexSet [12-13]: - values: - Expr [12-13]: Lit: Int(2)"#]], + GateOperand [10-14]: + kind: IndexedIdent [10-14]: + name: Ident [10-11] "q" + index_span: [11-14] + indices: + IndexSet [12-13]: + values: + Expr [12-13]: Lit: Int(2)"#]], ); } @@ -84,17 +86,19 @@ fn gphase_multiple_qubits() { Expr [7-8]: Ident [7-8] "a" duration: qubits: - IndexedIdent [10-12]: - name: Ident [10-12] "q0" - index_span: [0-0] - indices: - IndexedIdent [14-18]: - name: Ident [14-15] "q" - index_span: [15-18] - indices: - IndexSet [16-17]: - values: - Expr [16-17]: Lit: Int(4)"#]], + GateOperand [10-12]: + kind: IndexedIdent [10-12]: + name: Ident [10-12] "q0" + index_span: [0-0] + indices: + GateOperand [14-18]: + kind: IndexedIdent [14-18]: + name: Ident [14-15] "q" + index_span: [15-18] + indices: + IndexSet [16-17]: + values: + Expr [16-17]: Lit: Int(4)"#]], ); } @@ -182,9 +186,10 @@ fn gphase_ctrl_inv_modifiers() { rhs: Expr [25-26]: Lit: Int(2) duration: qubits: - IndexedIdent [28-30]: - name: Ident [28-30] "q0" - index_span: [0-0] - indices: "#]], + GateOperand [28-30]: + kind: IndexedIdent [28-30]: + name: Ident [28-30] "q0" + index_span: [0-0] + indices: "#]], ); } diff --git a/compiler/qsc_qasm3/src/parser/stmt/tests/if_stmt.rs b/compiler/qsc_qasm3/src/parser/stmt/tests/if_stmt.rs index e8ceb04a91..b2d1bd177b 100644 --- a/compiler/qsc_qasm3/src/parser/stmt/tests/if_stmt.rs +++ b/compiler/qsc_qasm3/src/parser/stmt/tests/if_stmt.rs @@ -192,10 +192,11 @@ fn single_stmt_if_stmt() { args: duration: qubits: - IndexedIdent [9-10]: - name: Ident [9-10] "q" - index_span: [0-0] - indices: + GateOperand [9-10]: + kind: IndexedIdent [9-10]: + name: Ident [9-10] "q" + index_span: [0-0] + indices: else_body: "#]], ); } @@ -254,10 +255,11 @@ fn nested_single_stmt_if_stmt() { args: duration: qubits: - IndexedIdent [16-17]: - name: Ident [16-17] "q" - index_span: [0-0] - indices: + GateOperand [16-17]: + kind: IndexedIdent [16-17]: + name: Ident [16-17] "q" + index_span: [0-0] + indices: else_body: else_body: "#]], ); @@ -285,10 +287,11 @@ fn nested_single_stmt_if_else_stmt() { args: duration: qubits: - IndexedIdent [16-17]: - name: Ident [16-17] "q" - index_span: [0-0] - indices: + GateOperand [16-17]: + kind: IndexedIdent [16-17]: + name: Ident [16-17] "q" + index_span: [0-0] + indices: else_body: Stmt [24-42]: annotations: kind: IfStmt [24-42]: @@ -305,10 +308,11 @@ fn nested_single_stmt_if_else_stmt() { args: duration: qubits: - IndexedIdent [40-41]: - name: Ident [40-41] "q" - index_span: [0-0] - indices: + GateOperand [40-41]: + kind: IndexedIdent [40-41]: + name: Ident [40-41] "q" + index_span: [0-0] + indices: else_body: else_body: else_body: "#]], @@ -333,10 +337,11 @@ fn single_stmt_if_stmt_else_stmt() { args: duration: qubits: - IndexedIdent [9-10]: - name: Ident [9-10] "q" - index_span: [0-0] - indices: + GateOperand [9-10]: + kind: IndexedIdent [9-10]: + name: Ident [9-10] "q" + index_span: [0-0] + indices: else_body: Stmt [17-21]: annotations: kind: GateCall [17-21]: @@ -345,9 +350,10 @@ fn single_stmt_if_stmt_else_stmt() { args: duration: qubits: - IndexedIdent [19-20]: - name: Ident [19-20] "q" - index_span: [0-0] - indices: "#]], + GateOperand [19-20]: + kind: IndexedIdent [19-20]: + name: Ident [19-20] "q" + index_span: [0-0] + indices: "#]], ); } diff --git a/compiler/qsc_qasm3/src/parser/stmt/tests/invalid_stmts/gate_calls.rs b/compiler/qsc_qasm3/src/parser/stmt/tests/invalid_stmts/gate_calls.rs index 3268d1ec75..0fdeb38c57 100644 --- a/compiler/qsc_qasm3/src/parser/stmt/tests/invalid_stmts/gate_calls.rs +++ b/compiler/qsc_qasm3/src/parser/stmt/tests/invalid_stmts/gate_calls.rs @@ -78,31 +78,32 @@ fn pow_with_two_args() { parse, "pow(2, 3) @ x $0;", &expect![[r#" - Stmt [0-17]: - annotations: - kind: GateCall [0-17]: - modifiers: - QuantumGateModifier [0-11]: Pow Expr [4-5]: Lit: Int(2) - name: Ident [12-13] "x" - args: - duration: - qubits: - HardwareQubit [14-16]: 0 + Stmt [0-17]: + annotations: + kind: GateCall [0-17]: + modifiers: + QuantumGateModifier [0-11]: Pow Expr [4-5]: Lit: Int(2) + name: Ident [12-13] "x" + args: + duration: + qubits: + GateOperand [14-16]: + kind: HardwareQubit [14-16]: 0 - [ - Error( - Token( - Close( - Paren, + [ + Error( + Token( + Close( + Paren, + ), + Comma, + Span { + lo: 5, + hi: 6, + }, ), - Comma, - Span { - lo: 5, - hi: 6, - }, ), - ), - ]"#]], + ]"#]], ); } @@ -112,32 +113,34 @@ fn ctrl_with_two_args() { parse, "ctrl(2, 3) @ x $0, $1;", &expect![[r#" - Stmt [0-22]: - annotations: - kind: GateCall [0-22]: - modifiers: - QuantumGateModifier [0-12]: Ctrl Some(Expr { span: Span { lo: 5, hi: 6 }, kind: Lit(Lit { span: Span { lo: 5, hi: 6 }, kind: Int(2) }) }) - name: Ident [13-14] "x" - args: - duration: - qubits: - HardwareQubit [15-17]: 0 - HardwareQubit [19-21]: 1 + Stmt [0-22]: + annotations: + kind: GateCall [0-22]: + modifiers: + QuantumGateModifier [0-12]: Ctrl Some(Expr { span: Span { lo: 5, hi: 6 }, kind: Lit(Lit { span: Span { lo: 5, hi: 6 }, kind: Int(2) }) }) + name: Ident [13-14] "x" + args: + duration: + qubits: + GateOperand [15-17]: + kind: HardwareQubit [15-17]: 0 + GateOperand [19-21]: + kind: HardwareQubit [19-21]: 1 - [ - Error( - Token( - Close( - Paren, + [ + Error( + Token( + Close( + Paren, + ), + Comma, + Span { + lo: 6, + hi: 7, + }, ), - Comma, - Span { - lo: 6, - hi: 7, - }, ), - ), - ]"#]], + ]"#]], ); } @@ -147,32 +150,34 @@ fn negctrl_with_two_args() { parse, "negctrl(2, 3) @ x $0, $1;", &expect![[r#" - Stmt [0-25]: - annotations: - kind: GateCall [0-25]: - modifiers: - QuantumGateModifier [0-15]: NegCtrl Some(Expr { span: Span { lo: 8, hi: 9 }, kind: Lit(Lit { span: Span { lo: 8, hi: 9 }, kind: Int(2) }) }) - name: Ident [16-17] "x" - args: - duration: - qubits: - HardwareQubit [18-20]: 0 - HardwareQubit [22-24]: 1 + Stmt [0-25]: + annotations: + kind: GateCall [0-25]: + modifiers: + QuantumGateModifier [0-15]: NegCtrl Some(Expr { span: Span { lo: 8, hi: 9 }, kind: Lit(Lit { span: Span { lo: 8, hi: 9 }, kind: Int(2) }) }) + name: Ident [16-17] "x" + args: + duration: + qubits: + GateOperand [18-20]: + kind: HardwareQubit [18-20]: 0 + GateOperand [22-24]: + kind: HardwareQubit [22-24]: 1 - [ - Error( - Token( - Close( - Paren, + [ + Error( + Token( + Close( + Paren, + ), + Comma, + Span { + lo: 9, + hi: 10, + }, ), - Comma, - Span { - lo: 9, - hi: 10, - }, ), - ), - ]"#]], + ]"#]], ); } @@ -182,32 +187,34 @@ fn inv_with_arg() { parse, "inv(1) @ ctrl @ x $0, $1;", &expect![[r#" - Stmt [0-25]: - annotations: - kind: GateCall [0-25]: - modifiers: - QuantumGateModifier [0-8]: Inv - QuantumGateModifier [9-15]: Ctrl None - name: Ident [16-17] "x" - args: - duration: - qubits: - HardwareQubit [18-20]: 0 - HardwareQubit [22-24]: 1 + Stmt [0-25]: + annotations: + kind: GateCall [0-25]: + modifiers: + QuantumGateModifier [0-8]: Inv + QuantumGateModifier [9-15]: Ctrl None + name: Ident [16-17] "x" + args: + duration: + qubits: + GateOperand [18-20]: + kind: HardwareQubit [18-20]: 0 + GateOperand [22-24]: + kind: HardwareQubit [22-24]: 1 - [ - Error( - Token( - At, - Open( - Paren, + [ + Error( + Token( + At, + Open( + Paren, + ), + Span { + lo: 3, + hi: 4, + }, ), - Span { - lo: 3, - hi: 4, - }, ), - ), - ]"#]], + ]"#]], ); } diff --git a/compiler/qsc_qasm3/src/parser/stmt/tests/invalid_stmts/measure.rs b/compiler/qsc_qasm3/src/parser/stmt/tests/invalid_stmts/measure.rs index 1a236c2ecb..b23fb4a5b3 100644 --- a/compiler/qsc_qasm3/src/parser/stmt/tests/invalid_stmts/measure.rs +++ b/compiler/qsc_qasm3/src/parser/stmt/tests/invalid_stmts/measure.rs @@ -10,25 +10,26 @@ fn measure_multiple_qubits() { parse, "measure $0, $1;", &expect![[r#" - Stmt [0-10]: - annotations: - kind: MeasureStmt [0-10]: - measurement: MeasureExpr [0-7]: - operand: HardwareQubit [8-10]: 0 - target: + Stmt [0-10]: + annotations: + kind: MeasureArrowStmt [0-10]: + measurement: MeasureExpr [0-10]: + operand: GateOperand [8-10]: + kind: HardwareQubit [8-10]: 0 + target: - [ - Error( - Token( - Semicolon, - Comma, - Span { - lo: 10, - hi: 11, - }, + [ + Error( + Token( + Semicolon, + Comma, + Span { + lo: 10, + hi: 11, + }, + ), ), - ), - ]"#]], + ]"#]], ); } @@ -38,17 +39,35 @@ fn assign_measure_multiple_qubits() { parse, "a[0:1] = measure $0, $1;", &expect![[r#" - Error( - Rule( - "expression", - Measure, - Span { - lo: 9, - hi: 16, - }, - ), - ) - "#]], + Stmt [0-19]: + annotations: + kind: AssignStmt [0-19]: + lhs: IndexedIdent [0-6]: + name: Ident [0-1] "a" + index_span: [1-6] + indices: + IndexSet [2-5]: + values: + RangeDefinition [2-5]: + start: Expr [2-3]: Lit: Int(0) + step: + end: Expr [4-5]: Lit: Int(1) + rhs: MeasureExpr [9-19]: + operand: GateOperand [17-19]: + kind: HardwareQubit [17-19]: 0 + + [ + Error( + Token( + Semicolon, + Comma, + Span { + lo: 19, + hi: 20, + }, + ), + ), + ]"#]], ); } @@ -58,17 +77,29 @@ fn assign_arrow() { parse, "a = measure $0 -> b;", &expect![[r#" - Error( - Rule( - "expression", - Measure, - Span { - lo: 4, - hi: 11, - }, - ), - ) - "#]], + Stmt [0-14]: + annotations: + kind: AssignStmt [0-14]: + lhs: IndexedIdent [0-1]: + name: Ident [0-1] "a" + index_span: [0-0] + indices: + rhs: MeasureExpr [4-14]: + operand: GateOperand [12-14]: + kind: HardwareQubit [12-14]: 0 + + [ + Error( + Token( + Semicolon, + Arrow, + Span { + lo: 15, + hi: 17, + }, + ), + ), + ]"#]], ); } diff --git a/compiler/qsc_qasm3/src/parser/stmt/tests/measure.rs b/compiler/qsc_qasm3/src/parser/stmt/tests/measure.rs index e58bf22e36..fd2518ff97 100644 --- a/compiler/qsc_qasm3/src/parser/stmt/tests/measure.rs +++ b/compiler/qsc_qasm3/src/parser/stmt/tests/measure.rs @@ -13,12 +13,13 @@ fn measure_identifier() { &expect![[r#" Stmt [0-10]: annotations: - kind: MeasureStmt [0-10]: - measurement: MeasureExpr [0-7]: - operand: IndexedIdent [8-9]: - name: Ident [8-9] "q" - index_span: [0-0] - indices: + kind: MeasureArrowStmt [0-10]: + measurement: MeasureExpr [0-9]: + operand: GateOperand [8-9]: + kind: IndexedIdent [8-9]: + name: Ident [8-9] "q" + index_span: [0-0] + indices: target: "#]], ); } @@ -31,15 +32,16 @@ fn measure_indented_ident() { &expect![[r#" Stmt [0-13]: annotations: - kind: MeasureStmt [0-13]: - measurement: MeasureExpr [0-7]: - operand: IndexedIdent [8-12]: - name: Ident [8-9] "q" - index_span: [9-12] - indices: - IndexSet [10-11]: - values: - Expr [10-11]: Lit: Int(2) + kind: MeasureArrowStmt [0-13]: + measurement: MeasureExpr [0-12]: + operand: GateOperand [8-12]: + kind: IndexedIdent [8-12]: + name: Ident [8-9] "q" + index_span: [9-12] + indices: + IndexSet [10-11]: + values: + Expr [10-11]: Lit: Int(2) target: "#]], ); } @@ -52,9 +54,10 @@ fn measure_hardware_qubit() { &expect![[r#" Stmt [0-12]: annotations: - kind: MeasureStmt [0-12]: - measurement: MeasureExpr [0-7]: - operand: HardwareQubit [8-11]: 42 + kind: MeasureArrowStmt [0-12]: + measurement: MeasureExpr [0-11]: + operand: GateOperand [8-11]: + kind: HardwareQubit [8-11]: 42 target: "#]], ); } @@ -67,12 +70,13 @@ fn measure_arrow_into_ident() { &expect![[r#" Stmt [0-15]: annotations: - kind: MeasureStmt [0-15]: - measurement: MeasureExpr [0-7]: - operand: IndexedIdent [8-9]: - name: Ident [8-9] "q" - index_span: [0-0] - indices: + kind: MeasureArrowStmt [0-15]: + measurement: MeasureExpr [0-9]: + operand: GateOperand [8-9]: + kind: IndexedIdent [8-9]: + name: Ident [8-9] "q" + index_span: [0-0] + indices: target: IndexedIdent [13-14]: name: Ident [13-14] "a" index_span: [0-0] @@ -88,12 +92,13 @@ fn measure_arrow_into_indented_ident() { &expect![[r#" Stmt [0-18]: annotations: - kind: MeasureStmt [0-18]: - measurement: MeasureExpr [0-7]: - operand: IndexedIdent [8-9]: - name: Ident [8-9] "q" - index_span: [0-0] - indices: + kind: MeasureArrowStmt [0-18]: + measurement: MeasureExpr [0-9]: + operand: GateOperand [8-9]: + kind: IndexedIdent [8-9]: + name: Ident [8-9] "q" + index_span: [0-0] + indices: target: IndexedIdent [13-17]: name: Ident [13-14] "a" index_span: [14-17] @@ -103,3 +108,25 @@ fn measure_arrow_into_indented_ident() { Expr [15-16]: Lit: Int(1)"#]], ); } + +#[test] +fn assign_measure_stmt() { + check( + parse, + "c = measure q;", + &expect![[r#" + Stmt [0-14]: + annotations: + kind: AssignStmt [0-14]: + lhs: IndexedIdent [0-1]: + name: Ident [0-1] "c" + index_span: [0-0] + indices: + rhs: MeasureExpr [4-13]: + operand: GateOperand [12-13]: + kind: IndexedIdent [12-13]: + name: Ident [12-13] "q" + index_span: [0-0] + indices: "#]], + ); +} diff --git a/compiler/qsc_qasm3/src/parser/stmt/tests/old_style_decl.rs b/compiler/qsc_qasm3/src/parser/stmt/tests/old_style_decl.rs index ad0d3b7e1c..5b9071def6 100644 --- a/compiler/qsc_qasm3/src/parser/stmt/tests/old_style_decl.rs +++ b/compiler/qsc_qasm3/src/parser/stmt/tests/old_style_decl.rs @@ -48,6 +48,7 @@ fn qreg_decl() { Stmt [0-7]: annotations: kind: QubitDeclaration [0-7]: + ty_span: [0-7] ident: Ident [5-6] "q" size: "#]], ); @@ -62,6 +63,7 @@ fn qreg_array_decl() { Stmt [0-10]: annotations: kind: QubitDeclaration [0-10]: + ty_span: [0-10] ident: Ident [5-6] "q" size: Expr [7-8]: Ident [7-8] "n""#]], ); diff --git a/compiler/qsc_qasm3/src/parser/stmt/tests/quantum_decl.rs b/compiler/qsc_qasm3/src/parser/stmt/tests/quantum_decl.rs index 1bf02de48d..67031b9bd2 100644 --- a/compiler/qsc_qasm3/src/parser/stmt/tests/quantum_decl.rs +++ b/compiler/qsc_qasm3/src/parser/stmt/tests/quantum_decl.rs @@ -16,6 +16,7 @@ fn quantum_decl() { Stmt [0-8]: annotations: kind: QubitDeclaration [0-8]: + ty_span: [0-5] ident: Ident [6-7] "q" size: "#]], ); @@ -35,6 +36,7 @@ fn annotated_quantum_decl() { identifier: "a.b.c" value: "123" kind: QubitDeclaration [28-36]: + ty_span: [28-33] ident: Ident [34-35] "q" size: "#]], ); @@ -62,6 +64,7 @@ fn multi_annotated_quantum_decl() { identifier: "a.b.c" value: "123" kind: QubitDeclaration [100-108]: + ty_span: [100-105] ident: Ident [106-107] "q" size: "#]], ); @@ -96,6 +99,7 @@ fn quantum_decl_with_designator() { Stmt [0-16]: annotations: kind: QubitDeclaration [0-16]: + ty_span: [0-8] ident: Ident [9-15] "qubits" size: Expr [6-7]: Lit: Int(5)"#]], ); diff --git a/compiler/qsc_qasm3/src/parser/stmt/tests/reset.rs b/compiler/qsc_qasm3/src/parser/stmt/tests/reset.rs index fd56798242..dc8670b04a 100644 --- a/compiler/qsc_qasm3/src/parser/stmt/tests/reset.rs +++ b/compiler/qsc_qasm3/src/parser/stmt/tests/reset.rs @@ -14,10 +14,12 @@ fn reset_ident() { Stmt [0-8]: annotations: kind: ResetStmt [0-8]: - operand: IndexedIdent [6-7]: - name: Ident [6-7] "a" - index_span: [0-0] - indices: "#]], + reset_token_span: [0-5] + operand: GateOperand [6-7]: + kind: IndexedIdent [6-7]: + name: Ident [6-7] "a" + index_span: [0-0] + indices: "#]], ); } @@ -30,13 +32,15 @@ fn reset_indexed_ident() { Stmt [0-11]: annotations: kind: ResetStmt [0-11]: - operand: IndexedIdent [6-10]: - name: Ident [6-7] "a" - index_span: [7-10] - indices: - IndexSet [8-9]: - values: - Expr [8-9]: Lit: Int(1)"#]], + reset_token_span: [0-5] + operand: GateOperand [6-10]: + kind: IndexedIdent [6-10]: + name: Ident [6-7] "a" + index_span: [7-10] + indices: + IndexSet [8-9]: + values: + Expr [8-9]: Lit: Int(1)"#]], ); } @@ -49,6 +53,8 @@ fn reset_hardware_qubit() { Stmt [0-10]: annotations: kind: ResetStmt [0-10]: - operand: HardwareQubit [6-9]: 42"#]], + reset_token_span: [0-5] + operand: GateOperand [6-9]: + kind: HardwareQubit [6-9]: 42"#]], ); } diff --git a/compiler/qsc_qasm3/src/parser/stmt/tests/while_loops.rs b/compiler/qsc_qasm3/src/parser/stmt/tests/while_loops.rs index 1f44485846..0e5493d1f5 100644 --- a/compiler/qsc_qasm3/src/parser/stmt/tests/while_loops.rs +++ b/compiler/qsc_qasm3/src/parser/stmt/tests/while_loops.rs @@ -162,10 +162,11 @@ fn single_stmt_while_stmt() { args: duration: qubits: - IndexedIdent [12-13]: - name: Ident [12-13] "q" - index_span: [0-0] - indices: "#]], + GateOperand [12-13]: + kind: IndexedIdent [12-13]: + name: Ident [12-13] "q" + index_span: [0-0] + indices: "#]], ); } @@ -222,9 +223,10 @@ fn nested_single_stmt_while_stmt() { args: duration: qubits: - IndexedIdent [22-23]: - name: Ident [22-23] "q" - index_span: [0-0] - indices: "#]], + GateOperand [22-23]: + kind: IndexedIdent [22-23]: + name: Ident [22-23] "q" + index_span: [0-0] + indices: "#]], ); } diff --git a/compiler/qsc_qasm3/src/semantic/ast.rs b/compiler/qsc_qasm3/src/semantic/ast.rs index 0382d51406..f1bde908fe 100644 --- a/compiler/qsc_qasm3/src/semantic/ast.rs +++ b/compiler/qsc_qasm3/src/semantic/ast.rs @@ -1,6 +1,8 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. +pub mod const_eval; + use num_bigint::BigInt; use qsc_data_structures::span::{Span, WithSpan}; use std::{ @@ -141,12 +143,14 @@ impl WithSpan for Path { #[derive(Clone, Debug)] pub struct MeasureExpr { pub span: Span, + pub measure_token_span: Span, pub operand: GateOperand, } impl Display for MeasureExpr { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { writeln_header(f, "MeasureExpr", self.span)?; + writeln_field(f, "measure_token_span", &self.measure_token_span)?; write_field(f, "operand", &self.operand) } } @@ -268,29 +272,33 @@ impl Display for UnaryOp { } #[derive(Clone, Debug, Default)] -pub enum GateOperand { - IndexedIdent(Box), - HardwareQubit(Box), - #[default] - Err, +pub struct GateOperand { + pub span: Span, + pub kind: GateOperandKind, } impl Display for GateOperand { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { - match self { - GateOperand::IndexedIdent(ident) => write!(f, "{ident}"), - GateOperand::HardwareQubit(qubit) => write!(f, "{qubit}"), - GateOperand::Err => write!(f, "Error"), - } + writeln_header(f, "GateOperand", self.span)?; + write_field(f, "kind", &self.kind) } } -impl WithSpan for GateOperand { - fn with_span(self, span: Span) -> Self { +#[derive(Clone, Debug, Default)] +pub enum GateOperandKind { + /// `IndexedIdent` and `Ident` get lowered to an `Expr`. + Expr(Box), + HardwareQubit(HardwareQubit), + #[default] + Err, +} + +impl Display for GateOperandKind { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { match self { - GateOperand::IndexedIdent(ident) => GateOperand::IndexedIdent(ident.with_span(span)), - GateOperand::HardwareQubit(qubit) => GateOperand::HardwareQubit(qubit.with_span(span)), - GateOperand::Err => GateOperand::Err, + Self::Expr(expr) => write!(f, "{expr}"), + Self::HardwareQubit(qubit) => write!(f, "{qubit}"), + Self::Err => write!(f, "Err"), } } } @@ -353,10 +361,11 @@ pub enum StmtKind { Include(IncludeStmt), InputDeclaration(InputDeclaration), OutputDeclaration(OutputDeclaration), - Measure(MeasureStmt), + MeasureArrow(MeasureArrowStmt), Pragma(Pragma), QuantumGateDefinition(QuantumGateDefinition), - QuantumDecl(QubitDeclaration), + QubitDecl(QubitDeclaration), + QubitArrayDecl(QubitArrayDeclaration), Reset(ResetStmt), Return(ReturnStmt), Switch(SwitchStmt), @@ -391,10 +400,11 @@ impl Display for StmtKind { StmtKind::IndexedAssign(assign) => write!(f, "{assign}"), StmtKind::InputDeclaration(io) => write!(f, "{io}"), StmtKind::OutputDeclaration(io) => write!(f, "{io}"), - StmtKind::Measure(measure) => write!(f, "{measure}"), + StmtKind::MeasureArrow(measure) => write!(f, "{measure}"), StmtKind::Pragma(pragma) => write!(f, "{pragma}"), StmtKind::QuantumGateDefinition(gate) => write!(f, "{gate}"), - StmtKind::QuantumDecl(decl) => write!(f, "{decl}"), + StmtKind::QubitDecl(decl) => write!(f, "{decl}"), + StmtKind::QubitArrayDecl(decl) => write!(f, "{decl}"), StmtKind::Reset(reset_stmt) => write!(f, "{reset_stmt}"), StmtKind::Return(return_stmt) => write!(f, "{return_stmt}"), StmtKind::Switch(switch_stmt) => write!(f, "{switch_stmt}"), @@ -461,12 +471,14 @@ impl Display for BarrierStmt { #[derive(Clone, Debug)] pub struct ResetStmt { pub span: Span, + pub reset_token_span: Span, pub operand: Box, } impl Display for ResetStmt { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { writeln_header(f, "ResetStmt", self.span)?; + writeln_field(f, "reset_token_span", &self.reset_token_span)?; write_field(f, "operand", &self.operand) } } @@ -653,8 +665,8 @@ impl Display for QuantumGateModifier { pub enum GateModifierKind { Inv, Pow(Expr), - Ctrl(Option), - NegCtrl(Option), + Ctrl(u32), + NegCtrl(u32), } impl Display for GateModifierKind { @@ -662,8 +674,8 @@ impl Display for GateModifierKind { match self { GateModifierKind::Inv => write!(f, "Inv"), GateModifierKind::Pow(expr) => write!(f, "Pow {expr}"), - GateModifierKind::Ctrl(expr) => write!(f, "Ctrl {expr:?}"), - GateModifierKind::NegCtrl(expr) => write!(f, "NegCtrl {expr:?}"), + GateModifierKind::Ctrl(ctrls) => write!(f, "Ctrl {ctrls:?}"), + GateModifierKind::NegCtrl(ctrls) => write!(f, "NegCtrl {ctrls:?}"), } } } @@ -1001,15 +1013,30 @@ impl Display for IncludeStmt { #[derive(Clone, Debug)] pub struct QubitDeclaration { pub span: Span, - pub qubit: Box, - pub size: Option, + pub symbol_id: SymbolId, } impl Display for QubitDeclaration { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { writeln_header(f, "QubitDeclaration", self.span)?; - writeln_field(f, "ident", &self.qubit)?; - write_opt_field(f, "size", self.size.as_ref()) + write_field(f, "symbol_id", &self.symbol_id) + } +} + +#[derive(Clone, Debug)] +pub struct QubitArrayDeclaration { + pub span: Span, + pub symbol_id: SymbolId, + pub size: u32, + pub size_span: Span, +} + +impl Display for QubitArrayDeclaration { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + writeln_header(f, "QubitArrayDeclaration", self.span)?; + writeln_field(f, "symbol_id", &self.symbol_id)?; + writeln_field(f, "size", &self.size)?; + write_field(f, "size_span", &self.size_span) } } @@ -1053,20 +1080,28 @@ impl Display for ExternDecl { pub struct GateCall { pub span: Span, pub modifiers: List, - pub name: Ident, + pub symbol_id: SymbolId, pub args: List, pub qubits: List, pub duration: Option, + pub quantum_arity: u32, + pub quantum_arity_with_modifiers: u32, } impl Display for GateCall { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { writeln_header(f, "GateCall", self.span)?; writeln_list_field(f, "modifiers", &self.modifiers)?; - writeln_field(f, "name", &self.name)?; + writeln_field(f, "symbol_id", &self.symbol_id)?; writeln_list_field(f, "args", &self.args)?; writeln_opt_field(f, "duration", self.duration.as_ref())?; - write_list_field(f, "qubits", &self.qubits) + writeln_list_field(f, "qubits", &self.qubits)?; + writeln_field(f, "quantum_arity", &self.quantum_arity)?; + write_field( + f, + "quantum_arity_with_modifiers", + &self.quantum_arity_with_modifiers, + ) } } @@ -1120,15 +1155,15 @@ impl Display for BoxStmt { } #[derive(Clone, Debug)] -pub struct MeasureStmt { +pub struct MeasureArrowStmt { pub span: Span, pub measurement: MeasureExpr, pub target: Option>, } -impl Display for MeasureStmt { +impl Display for MeasureArrowStmt { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { - writeln_header(f, "MeasureStmt", self.span)?; + writeln_header(f, "MeasureArrowStmt", self.span)?; writeln_field(f, "measurement", &self.measurement)?; write_opt_field(f, "target", self.target.as_ref()) } @@ -1139,7 +1174,7 @@ pub struct ClassicalDeclarationStmt { pub span: Span, pub ty_span: Span, pub symbol_id: SymbolId, - pub init_expr: Box, + pub init_expr: Box, } impl Display for ClassicalDeclarationStmt { @@ -1151,21 +1186,6 @@ impl Display for ClassicalDeclarationStmt { } } -#[derive(Clone, Debug)] -pub enum ValueExpression { - Expr(Expr), - Measurement(MeasureExpr), -} - -impl Display for ValueExpression { - fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { - match self { - ValueExpression::Expr(expr) => write!(f, "{expr}"), - ValueExpression::Measurement(measure) => write!(f, "{measure}"), - } - } -} - #[derive(Clone, Debug)] pub struct InputDeclaration { pub span: Span, @@ -1320,7 +1340,7 @@ impl Display for DefStmt { #[derive(Clone, Debug)] pub struct ReturnStmt { pub span: Span, - pub expr: Option>, + pub expr: Option>, } impl Display for ReturnStmt { @@ -1333,14 +1353,14 @@ impl Display for ReturnStmt { #[derive(Clone, Debug)] pub struct WhileLoop { pub span: Span, - pub while_condition: Expr, + pub condition: Expr, pub body: Stmt, } impl Display for WhileLoop { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { writeln_header(f, "WhileLoop", self.span)?; - writeln_field(f, "condition", &self.while_condition)?; + writeln_field(f, "condition", &self.condition)?; write_field(f, "body", &self.body) } } @@ -1428,6 +1448,7 @@ pub enum ExprKind { Cast(Cast), IndexExpr(IndexExpr), Paren(Expr), + Measure(MeasureExpr), } impl Display for ExprKind { @@ -1443,6 +1464,7 @@ impl Display for ExprKind { ExprKind::Cast(expr) => write!(f, "{expr}"), ExprKind::IndexExpr(expr) => write!(f, "{expr}"), ExprKind::Paren(expr) => write!(f, "Paren {expr}"), + ExprKind::Measure(expr) => write!(f, "{expr}"), } } } @@ -1468,7 +1490,7 @@ impl Display for AssignStmt { pub struct IndexedAssignStmt { pub span: Span, pub symbol_id: SymbolId, - pub lhs: Expr, + pub indices: List, pub rhs: Expr, } @@ -1476,7 +1498,7 @@ impl Display for IndexedAssignStmt { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { writeln_header(f, "AssignStmt", self.span)?; writeln_field(f, "symbol_id", &self.symbol_id)?; - writeln_field(f, "lhs", &self.rhs)?; + writeln_list_field(f, "indices", &self.indices)?; write_field(f, "rhs", &self.rhs) } } @@ -1596,6 +1618,7 @@ pub enum LiteralKind { Int(i64), BigInt(BigInt), String(Rc), + Bit(bool), } impl Display for LiteralKind { @@ -1606,6 +1629,7 @@ impl Display for LiteralKind { let width = *width as usize; write!(f, "Bitstring(\"{:0>width$}\")", value.to_str_radix(2)) } + LiteralKind::Bit(b) => write!(f, "Bit({:?})", u8::from(*b)), LiteralKind::Bool(b) => write!(f, "Bool({b:?})"), LiteralKind::Complex(real, imag) => write!(f, "Complex({real:?}, {imag:?})"), LiteralKind::Duration(value, unit) => { diff --git a/compiler/qsc_qasm3/src/semantic/ast/const_eval.rs b/compiler/qsc_qasm3/src/semantic/ast/const_eval.rs new file mode 100644 index 0000000000..15f4ca2344 --- /dev/null +++ b/compiler/qsc_qasm3/src/semantic/ast/const_eval.rs @@ -0,0 +1,628 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +//! This module allows us to perform const evaluation at lowering time. +//! The purpose of this is to be able to compute the widths of types +//! and sizes of arrays. Therefore, those are the only const evaluation +//! paths that are implemented. + +use std::f64; + +use super::{ + BinOp, BinaryOpExpr, Cast, Expr, ExprKind, FunctionCall, IndexExpr, IndexedIdent, LiteralKind, + SymbolId, UnaryOp, UnaryOpExpr, +}; +use crate::semantic::{ + symbols::SymbolTable, + types::{ArrayDimensions, Type}, +}; +use num_bigint::BigInt; + +impl Expr { + pub fn const_eval(&self, symbols: &SymbolTable) -> Option { + let ty = &self.ty; + if !ty.is_const() { + return None; + } + + match &*self.kind { + ExprKind::Ident(symbol_id) => symbol_id.const_eval(symbols), + ExprKind::IndexedIdentifier(indexed_ident) => indexed_ident.const_eval(symbols), + ExprKind::UnaryOp(unary_op_expr) => unary_op_expr.const_eval(symbols), + ExprKind::BinaryOp(binary_op_expr) => binary_op_expr.const_eval(symbols), + ExprKind::Lit(literal_kind) => Some(literal_kind.clone()), + ExprKind::FunctionCall(function_call) => function_call.const_eval(symbols, ty), + ExprKind::Cast(cast) => cast.const_eval(symbols, ty), + ExprKind::IndexExpr(index_expr) => index_expr.const_eval(symbols, ty), + ExprKind::Paren(expr) => expr.const_eval(symbols), + // Measurements are non-const, so we don't need to implement them. + ExprKind::Measure(_) | ExprKind::Err => None, + } + } +} + +impl SymbolId { + fn const_eval(self, symbols: &SymbolTable) -> Option { + let symbol = symbols[self].clone(); + symbol + .get_const_expr() // get the value of the symbol (an Expr) + .const_eval(symbols) // const eval that Expr + } +} + +impl IndexedIdent { + #[allow(clippy::unused_self)] + fn const_eval(&self, _symbols: &SymbolTable) -> Option { + None + } +} + +/// A helper macro for evaluating unary and binary operations of values +/// wrapped in the `semantic::LiteralKind` enum. Unwraps the value in the +/// `LiteralKind` and rewraps it in another `LiteralKind` variant while +/// applying some operation to it. +macro_rules! rewrap_lit { + // This pattern is used for unary expressions. + ($lit:expr, $pat:pat, $out:expr) => { + if let $pat = $lit { + Some($out) + } else { + None + } + }; +} + +impl UnaryOpExpr { + fn const_eval(&self, symbols: &SymbolTable) -> Option { + use LiteralKind::{Bit, Bitstring, Bool, Float, Int}; + let operand_ty = &self.expr.ty; + let lit = self.expr.const_eval(symbols)?; + + match &self.op { + UnaryOp::Neg => match operand_ty { + Type::Int(..) => rewrap_lit!(lit, Int(val), Int(-val)), + Type::Float(..) => rewrap_lit!(lit, Float(val), Float(-val)), + Type::Angle(..) => rewrap_lit!(lit, Float(val), Float(f64::consts::TAU - val)), + _ => None, + }, + UnaryOp::NotB => match operand_ty { + Type::Int(size, _) | Type::UInt(size, _) => rewrap_lit!(lit, Int(val), { + let mask = (1 << (*size)?) - 1; + Int(!val & mask) + }), + Type::Bit(..) => rewrap_lit!(lit, Bit(val), Bit(!val)), + Type::BitArray(..) => { + rewrap_lit!(lit, Bitstring(val, size), { + let mask = BigInt::from((1 << size) - 1); + Bitstring(!val & mask, size) + }) + } + // Angle is treated like a unit in the QASM3 Spec, but we are currently + // treating it as a float, so we can't apply bitwise negation to it. + _ => None, + }, + UnaryOp::NotL => match operand_ty { + Type::Bool(..) => rewrap_lit!(lit, Bool(val), Bool(!val)), + _ => None, + }, + } + } +} + +/// By this point it is guaranteed that the lhs and rhs are of the same type. +/// Any conversions have been made explicit by inserting casts during lowering. +/// Note: the type of the binary expression doesn't need to be the same as the +/// operands, for example, comparison operators can have integer operands +/// but their type is boolean. +/// We can write a simpler implementation under that assumption. +/// +/// There are some exceptions: +/// 1. The rhs in Shl and Shr must be of type `UInt`. +/// 2. Angle can be multiplied and divided by `UInt`. +fn assert_binary_op_ty_invariant(op: BinOp, lhs_ty: &Type, rhs_ty: &Type) { + // Exceptions: + if matches!( + (op, lhs_ty, rhs_ty), + (BinOp::Shl | BinOp::Shr, _, _) + | (BinOp::Mul | BinOp::Div, Type::Angle(..), Type::UInt(..)) + | (BinOp::Mul, Type::UInt(..), Type::Angle(..)) + ) { + return; + } + + assert_eq!(lhs_ty, rhs_ty); +} + +impl BinaryOpExpr { + #[allow(clippy::too_many_lines)] + fn const_eval(&self, symbols: &SymbolTable) -> Option { + use LiteralKind::{Bit, Bitstring, Bool, Float, Int}; + + assert_binary_op_ty_invariant(self.op, &self.lhs.ty, &self.rhs.ty); + let lhs = self.lhs.const_eval(symbols)?; + let rhs = self.rhs.const_eval(symbols)?; + let lhs_ty = &self.lhs.ty; + + match &self.op { + // Bit Shifts + BinOp::Shl => match lhs_ty { + Type::UInt(Some(size), _) => rewrap_lit!((lhs, rhs), (Int(lhs), Int(rhs)), { + let mask = (1 << size) - 1; + Int((lhs << rhs) & mask) + }), + Type::UInt(..) => rewrap_lit!((lhs, rhs), (Int(lhs), Int(rhs)), Int(lhs << rhs)), + Type::Bit(..) => rewrap_lit!((lhs, rhs), (Bit(lhs), Int(rhs)), { + // The Spec says "The shift operators shift bits off the end." + // Therefore if the rhs is > 0 the value becomes zero. + Bit(rhs == 0 && lhs) + }), + Type::BitArray(..) => rewrap_lit!((lhs, rhs), (Bitstring(lhs, size), Int(rhs)), { + let mask = BigInt::from((1 << size) - 1); + Bitstring((lhs << rhs) & mask, size) + }), + _ => None, + }, + BinOp::Shr => match lhs_ty { + Type::UInt(..) => rewrap_lit!((lhs, rhs), (Int(lhs), Int(rhs)), Int(lhs >> rhs)), + Type::Bit(..) => rewrap_lit!((lhs, rhs), (Bit(lhs), Int(rhs)), { + // The Spec says "The shift operators shift bits off the end." + // Therefore if the rhs is > 0 the value becomes zero. + Bit(rhs == 0 && lhs) + }), + Type::BitArray(..) => rewrap_lit!((lhs, rhs), (Bitstring(lhs, size), Int(rhs)), { + Bitstring(lhs >> rhs, size) + }), + _ => None, + }, + + // Bitwise + BinOp::AndB => match lhs_ty { + Type::UInt(..) => rewrap_lit!((lhs, rhs), (Int(lhs), Int(rhs)), Int(lhs & rhs)), + Type::Bit(..) => rewrap_lit!((lhs, rhs), (Bit(lhs), Bit(rhs)), Bit(lhs & rhs)), + Type::BitArray(..) => rewrap_lit!( + (lhs, rhs), + (Bitstring(lhs, lsize), Bitstring(rhs, rsize)), + Bitstring(lhs & rhs, lsize.min(rsize)) + ), + _ => None, + }, + BinOp::OrB => match lhs_ty { + Type::UInt(..) => rewrap_lit!((lhs, rhs), (Int(lhs), Int(rhs)), Int(lhs | rhs)), + Type::Bit(..) => rewrap_lit!((lhs, rhs), (Bit(lhs), Bit(rhs)), Bit(lhs | rhs)), + Type::BitArray(..) => rewrap_lit!( + (lhs, rhs), + (Bitstring(lhs, lsize), Bitstring(rhs, rsize)), + Bitstring(lhs | rhs, lsize.max(rsize)) + ), + _ => None, + }, + BinOp::XorB => match lhs_ty { + Type::UInt(..) => rewrap_lit!((lhs, rhs), (Int(lhs), Int(rhs)), Int(lhs ^ rhs)), + Type::Bit(..) => rewrap_lit!((lhs, rhs), (Bit(lhs), Bit(rhs)), Bit(lhs ^ rhs)), + Type::BitArray(..) => rewrap_lit!( + (lhs, rhs), + (Bitstring(lhs, lsize), Bitstring(rhs, rsize)), + Bitstring(lhs ^ rhs, lsize.max(rsize)) + ), + _ => None, + }, + + // Logical + BinOp::AndL => match lhs_ty { + Type::Bool(..) => rewrap_lit!((lhs, rhs), (Bool(lhs), Bool(rhs)), Bool(lhs && rhs)), + _ => None, + }, + BinOp::OrL => match lhs_ty { + Type::Bool(..) => rewrap_lit!((lhs, rhs), (Bool(lhs), Bool(rhs)), Bool(lhs || rhs)), + _ => None, + }, + + // Comparison + BinOp::Eq => match lhs_ty { + Type::Int(..) | Type::UInt(..) => { + rewrap_lit!((lhs, rhs), (Int(lhs), Int(rhs)), Bool(lhs == rhs)) + } + Type::Float(..) | Type::Angle(..) => { + rewrap_lit!((lhs, rhs), (Float(lhs), Float(rhs)), { + // TODO: we need to issue the same lint in Q#. + #[allow(clippy::float_cmp)] + Bool(lhs == rhs) + }) + } + Type::Bit(..) => rewrap_lit!((lhs, rhs), (Bit(lhs), Bit(rhs)), Bool(lhs == rhs)), + Type::BitArray(..) => rewrap_lit!( + (lhs, rhs), + (Bitstring(lhs, _), Bitstring(rhs, _)), + Bool(lhs == rhs) + ), + _ => None, + }, + BinOp::Neq => match lhs_ty { + Type::Int(..) | Type::UInt(..) => { + rewrap_lit!((lhs, rhs), (Int(lhs), Int(rhs)), Bool(lhs != rhs)) + } + Type::Float(..) | Type::Angle(..) => { + rewrap_lit!((lhs, rhs), (Float(lhs), Float(rhs)), { + // TODO: we need to issue the same lint in Q#. + #[allow(clippy::float_cmp)] + Bool(lhs != rhs) + }) + } + Type::Bit(..) => rewrap_lit!((lhs, rhs), (Bit(lhs), Bit(rhs)), Bool(lhs != rhs)), + Type::BitArray(..) => rewrap_lit!( + (lhs, rhs), + (Bitstring(lhs, _), Bitstring(rhs, _)), + Bool(lhs != rhs) + ), + _ => None, + }, + BinOp::Gt => match lhs_ty { + Type::Int(..) | Type::UInt(..) => { + rewrap_lit!((lhs, rhs), (Int(lhs), Int(rhs)), Bool(lhs > rhs)) + } + Type::Float(..) | Type::Angle(..) => { + rewrap_lit!((lhs, rhs), (Float(lhs), Float(rhs)), Bool(lhs > rhs)) + } + // This was originally `lhs > rhs` but clippy suggested this expression. + Type::Bit(..) => rewrap_lit!((lhs, rhs), (Bit(lhs), Bit(rhs)), Bool(lhs && !rhs)), + Type::BitArray(..) => rewrap_lit!( + (lhs, rhs), + (Bitstring(lhs, _), Bitstring(rhs, _)), + Bool(lhs > rhs) + ), + _ => None, + }, + BinOp::Gte => match lhs_ty { + Type::Int(..) | Type::UInt(..) => { + rewrap_lit!((lhs, rhs), (Int(lhs), Int(rhs)), Bool(lhs >= rhs)) + } + Type::Float(..) | Type::Angle(..) => { + rewrap_lit!((lhs, rhs), (Float(lhs), Float(rhs)), Bool(lhs >= rhs)) + } + Type::Bit(..) => rewrap_lit!((lhs, rhs), (Bit(lhs), Bit(rhs)), Bool(lhs >= rhs)), + Type::BitArray(..) => rewrap_lit!( + (lhs, rhs), + (Bitstring(lhs, _), Bitstring(rhs, _)), + Bool(lhs >= rhs) + ), + _ => None, + }, + BinOp::Lt => match lhs_ty { + Type::Int(..) | Type::UInt(..) => { + rewrap_lit!((lhs, rhs), (Int(lhs), Int(rhs)), Bool(lhs < rhs)) + } + Type::Float(..) | Type::Angle(..) => { + rewrap_lit!((lhs, rhs), (Float(lhs), Float(rhs)), Bool(lhs < rhs)) + } + // This was originally `lhs < rhs` but clippy suggested this expression. + Type::Bit(..) => rewrap_lit!((lhs, rhs), (Bit(lhs), Bit(rhs)), Bool(!lhs & rhs)), + Type::BitArray(..) => rewrap_lit!( + (lhs, rhs), + (Bitstring(lhs, _), Bitstring(rhs, _)), + Bool(lhs < rhs) + ), + _ => None, + }, + BinOp::Lte => match lhs_ty { + Type::Int(..) | Type::UInt(..) => { + rewrap_lit!((lhs, rhs), (Int(lhs), Int(rhs)), Bool(lhs <= rhs)) + } + Type::Float(..) | Type::Angle(..) => { + rewrap_lit!((lhs, rhs), (Float(lhs), Float(rhs)), Bool(lhs <= rhs)) + } + Type::Bit(..) => rewrap_lit!((lhs, rhs), (Bit(lhs), Bit(rhs)), Bool(lhs <= rhs)), + Type::BitArray(..) => rewrap_lit!( + (lhs, rhs), + (Bitstring(lhs, _), Bitstring(rhs, _)), + Bool(lhs <= rhs) + ), + _ => None, + }, + + // Arithmetic + BinOp::Add => match lhs_ty { + Type::Int(..) | Type::UInt(..) => { + rewrap_lit!((lhs, rhs), (Int(lhs), Int(rhs)), Int(lhs + rhs)) + } + Type::Float(..) => { + rewrap_lit!((lhs, rhs), (Float(lhs), Float(rhs)), Float(lhs + rhs)) + } + Type::Angle(..) => rewrap_lit!((lhs, rhs), (Float(lhs), Float(rhs)), { + let mut ans = lhs + rhs; + if ans >= f64::consts::TAU { + ans -= f64::consts::TAU; + } + Float(ans) + }), + _ => None, + }, + BinOp::Sub => match lhs_ty { + Type::Int(..) | Type::UInt(..) => { + rewrap_lit!((lhs, rhs), (Int(lhs), Int(rhs)), Int(lhs - rhs)) + } + Type::Float(..) => { + rewrap_lit!((lhs, rhs), (Float(lhs), Float(rhs)), Float(lhs - rhs)) + } + Type::Angle(..) => rewrap_lit!((lhs, rhs), (Float(lhs), Float(rhs)), { + let mut ans = lhs - rhs; + if ans < 0.0 { + ans += f64::consts::TAU; + } + Float(ans) + }), + _ => None, + }, + BinOp::Mul => match lhs_ty { + Type::Int(..) => rewrap_lit!((lhs, rhs), (Int(lhs), Int(rhs)), Int(lhs * rhs)), + Type::UInt(..) => match &self.rhs.ty { + Type::UInt(..) => rewrap_lit!((lhs, rhs), (Int(lhs), Int(rhs)), Int(lhs * rhs)), + Type::Angle(..) => rewrap_lit!((lhs, rhs), (Int(lhs), Float(rhs)), { + // allow reason: angles are in [0, 2π) + #[allow(clippy::cast_precision_loss)] + let mut ans = (lhs as f64) * rhs; + while ans >= f64::consts::TAU { + ans -= f64::consts::TAU; + } + Float(ans) + }), + _ => None, + }, + Type::Float(..) => { + rewrap_lit!((lhs, rhs), (Float(lhs), Float(rhs)), Float(lhs * rhs)) + } + Type::Angle(..) => rewrap_lit!((lhs, rhs), (Float(lhs), Int(rhs)), { + // allow reason: angles are in [0, 2π) + #[allow(clippy::cast_precision_loss)] + let mut ans = lhs * (rhs as f64); + while ans >= f64::consts::TAU { + ans -= f64::consts::TAU; + } + Float(ans) + }), + _ => None, + }, + BinOp::Div => match lhs_ty { + Type::Int(..) | Type::UInt(..) => { + rewrap_lit!((lhs, rhs), (Int(lhs), Int(rhs)), Int(lhs / rhs)) + } + Type::Float(..) => { + rewrap_lit!((lhs, rhs), (Float(lhs), Float(rhs)), Float(lhs / rhs)) + } + Type::Angle(..) => rewrap_lit!((lhs, rhs), (Float(lhs), Int(rhs)), { + // allow reason: angles are in [0, 2π) + #[allow(clippy::cast_precision_loss)] + Float(lhs / (rhs as f64)) + }), + _ => None, + }, + BinOp::Mod => match lhs_ty { + Type::Int(..) | Type::UInt(..) => { + rewrap_lit!((lhs, rhs), (Int(lhs), Int(rhs)), Int(lhs % rhs)) + } + _ => None, + }, + BinOp::Exp => match lhs_ty { + Type::Int(..) | Type::UInt(..) => { + rewrap_lit!( + (lhs, rhs), + (Int(lhs), Int(rhs)), + Int(lhs.wrapping_pow(u32::try_from(rhs).ok()?)) + ) + } + Type::Float(..) => { + rewrap_lit!((lhs, rhs), (Float(lhs), Float(rhs)), Float(lhs.powf(rhs))) + } + _ => None, + }, + } + } +} + +impl FunctionCall { + #[allow(clippy::unused_self)] + fn const_eval(&self, _symbols: &SymbolTable, _ty: &Type) -> Option { + None + } +} + +impl IndexExpr { + #[allow(clippy::unused_self)] + fn const_eval(&self, _symbols: &SymbolTable, _ty: &Type) -> Option { + None + } +} + +impl Cast { + fn const_eval(&self, symbols: &SymbolTable, ty: &Type) -> Option { + let lit = self.expr.const_eval(symbols)?; + let from_ty = &self.expr.ty; + + match ty { + Type::Bool(_) => cast_to_bool(from_ty, lit), + Type::Int(_, _) => cast_to_int(from_ty, lit), + Type::UInt(_, _) => cast_to_uint(from_ty, lit), + Type::Float(_, _) => cast_to_float(from_ty, lit), + Type::Angle(_, _) => cast_to_angle(from_ty, lit), + Type::Bit(_) => cast_to_bit(from_ty, lit), + Type::BitArray(dims, _) => cast_to_bitarray(from_ty, lit, dims), + _ => None, + } + } +} + +/// +---------------+-----------------------------------------+ +/// | Allowed casts | Casting from | +/// +---------------+------+-----+------+-------+-------+-----+ +/// | Casting to | bool | int | uint | float | angle | bit | +/// +---------------+------+-----+------+-------+-------+-----+ +/// | bool | - | Yes | Yes | Yes | Yes | Yes | +/// +---------------+------+-----+------+-------+-------+-----+ +fn cast_to_bool(ty: &Type, lit: LiteralKind) -> Option { + use LiteralKind::{Bit, Bitstring, Bool, Float, Int}; + + match ty { + Type::Bool(..) => Some(lit), + Type::Bit(..) => rewrap_lit!(lit, Bit(val), Bool(val)), + Type::BitArray(..) => rewrap_lit!(lit, Bitstring(val, _), Bool(val != BigInt::ZERO)), + Type::Int(..) | Type::UInt(..) => rewrap_lit!(lit, Int(val), Bool(val != 0)), + Type::Float(..) | Type::Angle(..) => rewrap_lit!(lit, Float(val), Bool(val != 0.0)), + _ => None, + } +} + +/// +---------------+-----------------------------------------+ +/// | Allowed casts | Casting from | +/// +---------------+------+-----+------+-------+-------+-----+ +/// | Casting to | bool | int | uint | float | angle | bit | +/// +---------------+------+-----+------+-------+-------+-----+ +/// | int | Yes | - | Yes | Yes | No | Yes | +/// +---------------+------+-----+------+-------+-------+-----+ +fn cast_to_int(ty: &Type, lit: LiteralKind) -> Option { + use LiteralKind::{Bit, Bitstring, Bool, Float, Int}; + + match ty { + Type::Bool(..) => rewrap_lit!(lit, Bool(val), Int(i64::from(val))), + Type::Bit(..) => rewrap_lit!(lit, Bit(val), Int(i64::from(val))), + Type::BitArray(..) => { + rewrap_lit!(lit, Bitstring(val, _), Int(i64::try_from(val).ok()?)) + } + // TODO: UInt Overflowing behavior. + // This is tricky because the inner repersentation + // already is a i64. Therefore, there is nothing to do? + Type::Int(..) | Type::UInt(..) => Some(lit), + Type::Float(..) => rewrap_lit!(lit, Float(val), { + // TODO: we need to issue the same lint in Q#. + #[allow(clippy::cast_possible_truncation)] + Int(val as i64) + }), + _ => None, + } +} + +/// +---------------+-----------------------------------------+ +/// | Allowed casts | Casting from | +/// +---------------+------+-----+------+-------+-------+-----+ +/// | Casting from | bool | int | uint | float | angle | bit | +/// +---------------+------+-----+------+-------+-------+-----+ +/// | uint | Yes | Yes | - | Yes | No | Yes | +/// +---------------+------+-----+------+-------+-------+-----+ +fn cast_to_uint(ty: &Type, lit: LiteralKind) -> Option { + use LiteralKind::{Bit, Bitstring, Bool, Float, Int}; + + match ty { + Type::Bool(..) => rewrap_lit!(lit, Bool(val), Int(i64::from(val))), + Type::Bit(..) => rewrap_lit!(lit, Bit(val), Int(i64::from(val))), + Type::BitArray(..) => { + rewrap_lit!(lit, Bitstring(val, _), Int(i64::try_from(val).ok()?)) + } + // TODO: Int Overflowing behavior. + // This is tricky because the inner representation + // is a i64. Therefore, even we might end with the + // same result anyways. Need to think through this. + Type::Int(..) | Type::UInt(..) => Some(lit), + Type::Float(..) => rewrap_lit!(lit, Float(val), { + // TODO: we need to issue the same lint in Q#. + #[allow(clippy::cast_possible_truncation)] + Int(val as i64) + }), + _ => None, + } +} + +/// +---------------+-----------------------------------------+ +/// | Allowed casts | Casting from | +/// +---------------+------+-----+------+-------+-------+-----+ +/// | Casting to | bool | int | uint | float | angle | bit | +/// +---------------+------+-----+------+-------+-------+-----+ +/// | float | Yes | Yes | Yes | - | No | No | +/// +---------------+------+-----+------+-------+-------+-----+ +fn cast_to_float(ty: &Type, lit: LiteralKind) -> Option { + use LiteralKind::{Bool, Float, Int}; + + match ty { + Type::Bool(..) => rewrap_lit!(lit, Bool(val), Float(if val { 1.0 } else { 0.0 })), + Type::Int(..) | Type::UInt(..) => rewrap_lit!(lit, Int(val), { + // TODO: we need to issue the same lint in Q#. + #[allow(clippy::cast_precision_loss)] + Float(val as f64) + }), + Type::Float(..) => Some(lit), + _ => None, + } +} + +/// +---------------+-----------------------------------------+ +/// | Allowed casts | Casting from | +/// +---------------+------+-----+------+-------+-------+-----+ +/// | Casting to | bool | int | uint | float | angle | bit | +/// +---------------+------+-----+------+-------+-------+-----+ +/// | angle | No | No | No | Yes | - | Yes | +/// +---------------+------+-----+------+-------+-------+-----+ +fn cast_to_angle(ty: &Type, lit: LiteralKind) -> Option { + match ty { + Type::Float(..) | Type::Angle(..) => Some(lit), + _ => None, + } +} + +/// +---------------+-----------------------------------------+ +/// | Allowed casts | Casting from | +/// +---------------+------+-----+------+-------+-------+-----+ +/// | Casting to | bool | int | uint | float | angle | bit | +/// +---------------+------+-----+------+-------+-------+-----+ +/// | bit | Yes | Yes | Yes | No | Yes | - | +/// +---------------+------+-----+------+-------+-------+-----+ +fn cast_to_bit(ty: &Type, lit: LiteralKind) -> Option { + use LiteralKind::{Bit, Bool, Int}; + + match ty { + Type::Bool(..) => rewrap_lit!(lit, Bool(val), Bit(val)), + Type::Bit(..) => Some(lit), + Type::Int(..) | Type::UInt(..) => rewrap_lit!(lit, Int(val), Bit(val != 0)), + _ => None, + } +} + +/// +---------------+-----------------------------------------+ +/// | Allowed casts | Casting from | +/// +---------------+------+-----+------+-------+-------+-----+ +/// | Casting to | bool | int | uint | float | angle | bit | +/// +---------------+------+-----+------+-------+-------+-----+ +/// | bitarray | Yes | Yes | Yes | No | Yes | - | +/// +---------------+------+-----+------+-------+-------+-----+ +fn cast_to_bitarray(ty: &Type, lit: LiteralKind, dims: &ArrayDimensions) -> Option { + use LiteralKind::{Bit, Bitstring, Bool, Int}; + + let ArrayDimensions::One(size) = dims else { + return None; + }; + let size = *size; + + match ty { + Type::Bool(..) => rewrap_lit!(lit, Bool(val), Bitstring(BigInt::from(val), size)), + Type::Bit(..) => rewrap_lit!(lit, Bit(val), Bitstring(BigInt::from(val), size)), + Type::BitArray(..) => rewrap_lit!(lit, Bitstring(val, rhs_size), { + if rhs_size < size { + return None; + } + Bitstring(val, size) + }), + Type::Int(..) | Type::UInt(..) => rewrap_lit!(lit, Int(val), { + let actual_bits = number_of_bits(val); + if actual_bits > size { + return None; + } + Bitstring(BigInt::from(val), size) + }), + _ => None, + } +} + +fn number_of_bits(mut val: i64) -> u32 { + let mut bits = 0; + while val != 0 { + val >>= 1; + bits += 1; + } + bits +} diff --git a/compiler/qsc_qasm3/src/semantic/error.rs b/compiler/qsc_qasm3/src/semantic/error.rs index 02df4074f1..6a6652456a 100644 --- a/compiler/qsc_qasm3/src/semantic/error.rs +++ b/compiler/qsc_qasm3/src/semantic/error.rs @@ -29,6 +29,12 @@ pub enum SemanticErrorKind { #[error("Array literals are only allowed in classical declarations.")] #[diagnostic(code("Qsc.Qasm3.Compile.ArrayLiteralInNonClassicalDecl"))] ArrayLiteralInNonClassicalDecl(#[label] Span), + #[error("{0} must fit in a u32")] + #[diagnostic(code("Qsc.Qasm3.Compile.ExprMustFitInU32"))] + ExprMustFitInU32(String, #[label] Span), + #[error("{0} must be a const expression")] + #[diagnostic(code("Qsc.Qasm3.Compile.ExprMustBeConst"))] + ExprMustBeConst(String, #[label] Span), #[error("Annotation missing target statement.")] #[diagnostic(code("Qsc.Qasm3.Compile.AnnotationWithoutStatement"))] AnnotationWithoutStatement(#[label] Span), @@ -72,6 +78,12 @@ pub enum SemanticErrorKind { #[error("Designator must be a positive literal integer.")] #[diagnostic(code("Qsc.Qasm3.Compile.DesignatorMustBePositiveIntLiteral"))] DesignatorMustBePositiveIntLiteral(#[label] Span), + #[error("Type width must be a positive integer const expression.")] + #[diagnostic(code("Qsc.Qasm3.Compile.TypeWidthMustBePositiveIntConstExpr"))] + TypeWidthMustBePositiveIntConstExpr(#[label] Span), + #[error("Array size must be a non-negative integer const expression.")] + #[diagnostic(code("Qsc.Qasm3.Compile.ArraySizeMustBeNonNegativeConstExpr"))] + ArraySizeMustBeNonNegativeConstExpr(#[label] Span), #[error("Designator is too large.")] #[diagnostic(code("Qsc.Qasm3.Compile.DesignatorTooLarge"))] DesignatorTooLarge(#[label] Span), @@ -171,6 +183,9 @@ pub enum SemanticErrorKind { #[error("Return statements are only allowed within subroutines.")] #[diagnostic(code("Qsc.Qasm3.Compile.ReturnNotInSubroutine"))] ReturnNotInSubroutine(#[label] Span), + #[error("Switch statement must have at least one non-default case.")] + #[diagnostic(code("Qsc.Qasm3.Compile.SwitchStatementMustHaveAtLeastOneCase"))] + SwitchStatementMustHaveAtLeastOneCase(#[label] Span), #[error("Too many controls specified.")] #[diagnostic(code("Qsc.Qasm3.Compile.TooManyControls"))] TooManyControls(#[label] Span), @@ -220,6 +235,8 @@ impl SemanticErrorKind { Self::ArrayLiteralInNonClassicalDecl(span) => { Self::ArrayLiteralInNonClassicalDecl(span + offset) } + Self::ExprMustBeConst(name, span) => Self::ExprMustBeConst(name, span + offset), + Self::ExprMustFitInU32(name, span) => Self::ExprMustFitInU32(name, span + offset), Self::AnnotationWithoutStatement(span) => { Self::AnnotationWithoutStatement(span + offset) } @@ -250,6 +267,12 @@ impl SemanticErrorKind { Self::DesignatorMustBePositiveIntLiteral(span) => { Self::DesignatorMustBePositiveIntLiteral(span + offset) } + Self::TypeWidthMustBePositiveIntConstExpr(span) => { + Self::TypeWidthMustBePositiveIntConstExpr(span + offset) + } + Self::ArraySizeMustBeNonNegativeConstExpr(span) => { + Self::ArraySizeMustBeNonNegativeConstExpr(span + offset) + } Self::DesignatorTooLarge(span) => Self::DesignatorTooLarge(span + offset), Self::FailedToCompileExpressionList(span) => { Self::FailedToCompileExpressionList(span + offset) @@ -327,6 +350,9 @@ impl SemanticErrorKind { Self::ResetExpressionMustHaveName(span + offset) } Self::ReturnNotInSubroutine(span) => Self::ReturnNotInSubroutine(span + offset), + Self::SwitchStatementMustHaveAtLeastOneCase(span) => { + Self::SwitchStatementMustHaveAtLeastOneCase(span + offset) + } Self::TooManyControls(span) => Self::TooManyControls(span + offset), Self::TooManyIndices(span) => Self::TooManyIndices(span + offset), Self::TypeDoesNotSupportBitwiseNot(name, span) => { diff --git a/compiler/qsc_qasm3/src/semantic/lowerer.rs b/compiler/qsc_qasm3/src/semantic/lowerer.rs index 4c47394b48..dd4e054af3 100644 --- a/compiler/qsc_qasm3/src/semantic/lowerer.rs +++ b/compiler/qsc_qasm3/src/semantic/lowerer.rs @@ -2,6 +2,7 @@ // Licensed under the MIT License. use std::ops::ShlAssign; +use std::rc::Rc; use super::symbols::ScopeKind; use super::types::binop_requires_int_conversion_for_type; @@ -27,6 +28,7 @@ use crate::parser::QasmSource; use crate::semantic::types::can_cast_literal; use crate::semantic::types::can_cast_literal_with_value_knowledge; use crate::semantic::types::ArrayDimensions; +use crate::types::get_qsharp_gate_name; use super::ast as semantic; use crate::parser::ast as syntax; @@ -180,7 +182,7 @@ impl Lowerer { syntax::StmtKind::Alias(stmt) => self.lower_alias(stmt), syntax::StmtKind::Assign(stmt) => self.lower_assign(stmt), syntax::StmtKind::AssignOp(stmt) => self.lower_assign_op(stmt), - syntax::StmtKind::Barrier(stmt) => self.lower_barrier(stmt), + syntax::StmtKind::Barrier(stmt) => self.lower_barrier_stmt(stmt), syntax::StmtKind::Box(stmt) => self.lower_box(stmt), syntax::StmtKind::Break(stmt) => self.lower_break(stmt), syntax::StmtKind::Block(stmt) => { @@ -227,11 +229,13 @@ impl Lowerer { /// when calling them. fn define_stdgates(&mut self, include: &syntax::IncludeStmt) { fn gate_symbol(name: &str, cargs: u32, qargs: u32) -> Symbol { - Symbol { - name: name.to_string(), - ty: Type::Gate(cargs, qargs), - ..Default::default() - } + Symbol::new( + name, + Span::default(), + Type::Gate(cargs, qargs), + Default::default(), + Default::default(), + ) } let gates = vec![ gate_symbol("X", 0, 1), @@ -313,13 +317,13 @@ impl Lowerer { .collect::>(); let first = rhs.first().expect("missing rhs"); - let symbol = Symbol { - name: name.to_string(), - ty: first.ty.clone(), - qsharp_ty: self.convert_semantic_type_to_qsharp_type(&first.ty, alias.ident.span()), - span: alias.ident.span(), - io_kind: IOKind::Default, - }; + let symbol = Symbol::new( + &name, + alias.ident.span(), + first.ty.clone(), + self.convert_semantic_type_to_qsharp_type(&first.ty, alias.ident.span()), + IOKind::Default, + ); let symbol_id = self.try_insert_or_get_existing_symbol_id(name, symbol, alias.ident.span()); @@ -351,14 +355,24 @@ impl Lowerer { fn lower_simple_assign_expr( &mut self, ident: &syntax::Ident, - rhs: &syntax::Expr, + rhs: &syntax::ValueExpr, span: Span, ) -> semantic::StmtKind { let (symbol_id, symbol) = self.try_get_existing_or_insert_err_symbol(&ident.name, ident.span); let ty = symbol.ty.clone(); - let rhs = self.lower_expr_with_target_type(Some(rhs), &ty, span); + let rhs = match rhs { + syntax::ValueExpr::Expr(expr) => { + let expr = self.lower_expr(expr); + self.cast_expr_with_target_type_or_default(Some(expr), &ty, span) + } + syntax::ValueExpr::Measurement(measure_expr) => { + let expr = self.lower_measure_expr(measure_expr); + self.cast_expr_to_type(&ty, &expr) + } + }; + if ty.is_const() { let kind = SemanticErrorKind::CannotUpdateConstVariable(ident.name.to_string(), ident.span); @@ -377,18 +391,36 @@ impl Lowerer { fn lower_indexed_assign_expr( &mut self, index_expr: &syntax::IndexedIdent, - rhs: &syntax::Expr, + rhs: &syntax::ValueExpr, span: Span, ) -> semantic::StmtKind { let ident = index_expr.name.clone(); let (symbol_id, symbol) = self.try_get_existing_or_insert_err_symbol(&ident.name, ident.span); - let ty = &symbol.ty; - let lhs = self.lower_indexed_ident_expr(index_expr); - let rhs = self.lower_expr_with_target_type(Some(rhs), ty, span); + let indexed_ty = &symbol + .ty + .get_indexed_type() + .expect("we only get here if there is at least one index"); - if ty.is_const() { + let indices = index_expr + .indices + .iter() + .map(|index| self.lower_index_element(index)); + let indices = list_from_iter(indices); + + let rhs = match rhs { + syntax::ValueExpr::Expr(expr) => { + let expr = self.lower_expr(expr); + self.cast_expr_with_target_type_or_default(Some(expr), indexed_ty, span) + } + syntax::ValueExpr::Measurement(measure_expr) => { + let expr = self.lower_measure_expr(measure_expr); + self.cast_expr_to_type(indexed_ty, &expr) + } + }; + + if symbol.ty.is_const() { let kind = SemanticErrorKind::CannotUpdateConstVariable(ident.name.to_string(), ident.span); self.push_semantic_error(kind); @@ -397,7 +429,7 @@ impl Lowerer { semantic::StmtKind::IndexedAssign(semantic::IndexedAssignStmt { span, symbol_id, - lhs, + indices, rhs, }) } @@ -420,8 +452,16 @@ impl Lowerer { } let lhs = self.lower_indexed_ident_expr(lhs); - let rhs = self.lower_expr(rhs); - let rhs = self.cast_expr_to_type(ty, &rhs, stmt.span); + let rhs = match rhs { + syntax::ValueExpr::Expr(expr) => { + let expr = self.lower_expr(expr); + self.cast_expr_with_target_type_or_default(Some(expr), ty, stmt.span) + } + syntax::ValueExpr::Measurement(measure_expr) => { + let expr = self.lower_measure_expr(measure_expr); + self.cast_expr_to_type(ty, &expr) + } + }; semantic::StmtKind::AssignOp(semantic::AssignOpStmt { span: stmt.span, @@ -485,7 +525,7 @@ impl Lowerer { } syntax::LiteralKind::Bitstring(value, size) => ( semantic::ExprKind::Lit(semantic::LiteralKind::Bitstring(value.clone(), *size)), - Type::BitArray(super::types::ArrayDimensions::One(*size), true), + Type::BitArray(super::types::ArrayDimensions::from(*size), true), ), syntax::LiteralKind::Bool(value) => ( semantic::ExprKind::Lit(semantic::LiteralKind::Bool(*value)), @@ -553,7 +593,7 @@ impl Lowerer { fn lower_unary_op_expr(&mut self, expr: &syntax::UnaryOpExpr) -> semantic::Expr { match expr.op { - syntax::UnaryOp::Neg | syntax::UnaryOp::NotB => { + syntax::UnaryOp::Neg => { let op = expr.op; let expr = self.lower_expr(&expr.expr); let ty = expr.ty.clone(); @@ -576,13 +616,39 @@ impl Lowerer { ty, } } + syntax::UnaryOp::NotB => { + let op = expr.op; + let expr = self.lower_expr(&expr.expr); + let ty = expr.ty.clone(); + if !unary_op_can_be_applied_to_type(op, &ty) { + let kind = SemanticErrorKind::TypeDoesNotSupportedUnaryNegation( + expr.ty.to_string(), + expr.span, + ); + self.push_semantic_error(kind); + } + let span = expr.span; + let unary = semantic::UnaryOpExpr { + span, + op: semantic::UnaryOp::NotB, + expr, + }; + semantic::Expr { + span, + kind: Box::new(semantic::ExprKind::UnaryOp(unary)), + ty, + } + } syntax::UnaryOp::NotL => { // this is the only unary operator that tries to coerce the type // I can't find it in the spec, but when looking at existing code // it seems that the ! operator coerces to a bool if possible - let target_ty = Type::Bool(false); + let expr = self.lower_expr(&expr.expr); + let expr_span = expr.span; + let target_ty = Type::Bool(expr.ty.is_const()); + let expr = - self.lower_expr_with_target_type(Some(&expr.expr), &target_ty, expr.expr.span); + self.cast_expr_with_target_type_or_default(Some(expr), &target_ty, expr_span); let ty = expr.ty.clone(); @@ -698,6 +764,7 @@ impl Lowerer { } Type::Range => crate::types::Type::Range, Type::Void => crate::types::Type::Tuple(vec![]), + Type::Err => crate::types::Type::Err, _ => { let msg = format!("Converting {ty:?} to Q# type"); self.push_unimplemented_error_message(msg, span); @@ -706,9 +773,13 @@ impl Lowerer { } } - fn lower_barrier(&mut self, stmt: &syntax::BarrierStmt) -> semantic::StmtKind { - self.push_unimplemented_error_message("barrier stmt", stmt.span); - semantic::StmtKind::Err + fn lower_barrier_stmt(&mut self, stmt: &syntax::BarrierStmt) -> semantic::StmtKind { + let qubits = stmt.qubits.iter().map(|q| self.lower_gate_operand(q)); + let qubits = list_from_iter(qubits); + semantic::StmtKind::Barrier(semantic::BarrierStmt { + span: stmt.span, + qubits, + }) } /// The "boxable" stmts were taken from the reference parser at @@ -792,29 +863,27 @@ impl Lowerer { let stmt_span = stmt.span; let name = stmt.identifier.name.clone(); let qsharp_ty = self.convert_semantic_type_to_qsharp_type(&ty.clone(), ty_span); - let symbol = Symbol { - name: name.to_string(), - ty: ty.clone(), + let symbol = Symbol::new( + &name, + stmt.identifier.span, + ty.clone(), qsharp_ty, - span: stmt.identifier.span, - io_kind: IOKind::Default, - }; + IOKind::Default, + ); // process the symbol and init_expr gathering any errors let init_expr = match init_expr { Some(expr) => match expr { - syntax::ValueExpression::Expr(expr) => semantic::ValueExpression::Expr( - self.lower_expr_with_target_type(Some(expr), &ty, stmt_span), - ), - syntax::ValueExpression::Measurement(measure_expr) => { - semantic::ValueExpression::Measurement( - self.lower_measure_expr(measure_expr, stmt_span), - ) + syntax::ValueExpr::Expr(expr) => { + let expr = self.lower_expr(expr); + self.cast_expr_with_target_type_or_default(Some(expr), &ty, stmt_span) + } + syntax::ValueExpr::Measurement(measure_expr) => { + let expr = self.lower_measure_expr(measure_expr); + self.cast_expr_to_type(&ty, &expr) } }, - None => semantic::ValueExpression::Expr( - self.lower_expr_with_target_type(None, &ty, stmt_span), - ), + None => self.cast_expr_with_target_type_or_default(None, &ty, stmt_span), }; let symbol_id = @@ -834,25 +903,41 @@ impl Lowerer { let ty_span = stmt.ty.span(); let name = stmt.identifier.name.clone(); let qsharp_ty = self.convert_semantic_type_to_qsharp_type(&ty.clone(), stmt.ty.span()); - let symbol = Symbol { - name: name.to_string(), - ty: ty.clone(), - qsharp_ty, - span: stmt.identifier.span, - io_kind: IOKind::Default, + let init_expr = match &stmt.init_expr { + syntax::ValueExpr::Expr(expr) => { + let expr = self.lower_expr(expr); + self.cast_expr_with_target_type_or_default(Some(expr), &ty, stmt.span) + } + syntax::ValueExpr::Measurement(measure_expr) => self.lower_measure_expr(measure_expr), }; - // process the symbol and init_expr gathering any errors - let init_expr = self.lower_expr_with_target_type(Some(&stmt.init_expr), &ty, stmt.span); + let mut symbol = Symbol::new( + &name, + stmt.identifier.span, + ty.clone(), + qsharp_ty, + IOKind::Default, + ); + + if init_expr.ty.is_const() { + symbol = symbol.with_const_expr(Rc::new(init_expr.clone())); + } let symbol_id = self.try_insert_or_get_existing_symbol_id(name, symbol, stmt.identifier.span); + if !init_expr.ty.is_const() { + self.push_semantic_error(SemanticErrorKind::ExprMustBeConst( + "const decl init expr".to_string(), + init_expr.span, + )); + } + semantic::StmtKind::ClassicalDecl(semantic::ClassicalDeclarationStmt { span: stmt.span, ty_span, symbol_id, - init_expr: Box::new(semantic::ValueExpression::Expr(init_expr)), + init_expr: Box::new(init_expr), }) } @@ -902,13 +987,13 @@ impl Lowerer { let ty = self.get_semantic_type_from_scalar_ty(&stmt.ty, false); let qsharp_ty = self.convert_semantic_type_to_qsharp_type(&ty.clone(), stmt.ty.span); - let symbol = Symbol { - name: stmt.ident.name.to_string(), - span: stmt.ident.span, - ty: ty.clone(), + let symbol = Symbol::new( + &stmt.ident.name, + stmt.ident.span, + ty.clone(), qsharp_ty, - io_kind: IOKind::Default, - }; + IOKind::Default, + ); // This is the first variable in this scope, so // we don't need to check for redefined symbols. @@ -941,8 +1026,8 @@ impl Lowerer { // The semantics of a if statement is that the condition must be // of type bool, so we try to cast it, inserting a cast if necessary. - let cond_ty = Type::Bool(false); - let condition = self.cast_expr_to_type(&cond_ty, &condition, condition.span); + let cond_ty = Type::Bool(condition.ty.is_const()); + let condition = self.cast_expr_to_type(&cond_ty, &condition); semantic::StmtKind::If(semantic::IfStmt { span: stmt.span, @@ -953,8 +1038,172 @@ impl Lowerer { } fn lower_gate_call(&mut self, stmt: &syntax::GateCall) -> semantic::StmtKind { - self.push_unimplemented_error_message("gate call stmt", stmt.span); - semantic::StmtKind::Err + // 1. Lower all the fields: + // 1.1. Lower the modifiers. + let mut modifiers = stmt + .modifiers + .iter() + .filter_map(|modifier| self.lower_modifier(modifier)) + .collect::>(); + // If we couldn't compute the modifiers there is no way to compile the gates + // correctly, since we can't check its arity. In this case we return an Err. + if modifiers.len() != stmt.modifiers.len() { + return semantic::StmtKind::Err; + } + + // 1.3. Lower the args. + let args = stmt.args.iter().map(|arg| self.lower_expr(arg)); + let args = list_from_iter(args); + // 1.4. Lower the qubits. + let qubits = stmt.qubits.iter().map(|q| self.lower_gate_operand(q)); + let qubits = list_from_iter(qubits); + // 1.5. Lower the duration. + let duration = stmt.duration.as_ref().map(|d| self.lower_expr(d)); + + if let Some(duration) = &duration { + self.push_unsupported_error_message("gate call duration", duration.span); + } + + let mut name = stmt.name.name.to_string(); + if let Some((qsharp_name, implicit_modifier)) = + try_get_qsharp_name_and_implicit_modifiers(&name, stmt.name.span) + { + // Override the gate name if Q# name is another. + name = qsharp_name; + + // 2. Get implicit modifiers and make them explicit. + // Q: Do we need this during lowering? + // A: Yes, we need it to check the gate_call arity. + modifiers.push(implicit_modifier); + } else { + name = get_qsharp_gate_name(&name).unwrap_or(&name).to_string(); + } + + // 3. Check that the gate_name actually refers to a gate in the symbol table + // and get its symbol_id & symbol. Make sure to use the name that could've + // been overriden by the Q# name and the span of the original name. + let (symbol_id, symbol) = self.try_get_existing_or_insert_err_symbol(name, stmt.name.span); + + let (classical_arity, quantum_arity) = + if let Type::Gate(classical_arity, quantum_arity) = &symbol.ty { + (*classical_arity, *quantum_arity) + } else { + self.push_semantic_error(SemanticErrorKind::CannotCallNonGate(symbol.span)); + (0, 0) + }; + + // 4. Check that gate_call classical arity matches the number of classical args. + if classical_arity as usize != args.len() { + self.push_semantic_error(SemanticErrorKind::InvalidNumberOfClassicalArgs( + classical_arity as usize, + args.len(), + stmt.span, + )); + } + + // 5. Check that gate_call quantum arity with modifiers matches the + // number of qubit args. + let mut quantum_arity_with_modifiers = quantum_arity; + for modifier in &modifiers { + match &modifier.kind { + semantic::GateModifierKind::Inv | semantic::GateModifierKind::Pow(_) => (), + semantic::GateModifierKind::Ctrl(n) | semantic::GateModifierKind::NegCtrl(n) => { + quantum_arity_with_modifiers += n; + } + } + } + + if quantum_arity_with_modifiers as usize != qubits.len() { + self.push_semantic_error(SemanticErrorKind::InvalidNumberOfQubitArgs( + quantum_arity_with_modifiers as usize, + qubits.len(), + stmt.span, + )); + } + + // 6. Return: + // 6.1. Gate symbol_id. + // 6.2. All controls made explicit. + // 6.3. Classical args. + // 6.4. Quantum args in the order expected by the compiler. + modifiers.reverse(); + let modifiers = list_from_iter(modifiers); + semantic::StmtKind::GateCall(semantic::GateCall { + span: stmt.span, + modifiers, + symbol_id, + args, + qubits, + duration, + quantum_arity, + quantum_arity_with_modifiers, + }) + + // The compiler will be left to do all things that need explicit Q# knowledge. + // But it won't need to check arities, know about implicit modifiers, or do + // any casting of classical args. There is still some inherit complexity to + // building a Q# gate call with this information, but it won't be cluttered + // by all the semantic analysis. + } + + fn lower_modifier( + &mut self, + modifier: &syntax::QuantumGateModifier, + ) -> Option { + let kind = match &modifier.kind { + syntax::GateModifierKind::Inv => semantic::GateModifierKind::Inv, + syntax::GateModifierKind::Pow(expr) => { + semantic::GateModifierKind::Pow(self.lower_expr(expr)) + } + syntax::GateModifierKind::Ctrl(expr) => { + let ctrl_args = self.lower_modifier_ctrl_args(expr.as_ref())?; + semantic::GateModifierKind::Ctrl(ctrl_args) + } + syntax::GateModifierKind::NegCtrl(expr) => { + let ctrl_args = self.lower_modifier_ctrl_args(expr.as_ref())?; + semantic::GateModifierKind::NegCtrl(ctrl_args) + } + }; + + Some(semantic::QuantumGateModifier { + span: modifier.span, + kind, + }) + } + + fn lower_modifier_ctrl_args(&mut self, expr: Option<&syntax::Expr>) -> Option { + let Some(expr) = expr else { + return Some(1); + }; + + let expr = self.lower_expr(expr); + + let target_ty = &Type::UInt(None, true); + let Some(expr) = self.try_cast_expr_to_type(target_ty, &expr) else { + self.push_invalid_cast_error(target_ty, &expr.ty, expr.span); + return None; + }; + let Some(lit) = expr.const_eval(&self.symbols) else { + self.push_semantic_error(SemanticErrorKind::ExprMustBeConst( + "ctrl modifier argument".into(), + expr.span, + )); + return None; + }; + + let semantic::LiteralKind::Int(n) = lit else { + unreachable!("we casted the expr to UInt before const evaluating it") + }; + + let Ok(n) = u32::try_from(n) else { + self.push_semantic_error(SemanticErrorKind::ExprMustFitInU32( + "ctrl modifier argument".into(), + expr.span, + )); + return None; + }; + + Some(n) } fn lower_gphase(&mut self, stmt: &syntax::GPhase) -> semantic::StmtKind { @@ -992,13 +1241,7 @@ impl Lowerer { let stmt_span = stmt.span; let name = stmt.ident.name.clone(); let qsharp_ty = self.convert_semantic_type_to_qsharp_type(&ty, ty_span); - let symbol = Symbol { - name: name.to_string(), - ty: ty.clone(), - qsharp_ty, - span: stmt.ident.span, - io_kind, - }; + let symbol = Symbol::new(&name, stmt.ident.span, ty.clone(), qsharp_ty, io_kind); let symbol_id = self.try_insert_or_get_existing_symbol_id(name, symbol, stmt.ident.span); @@ -1021,9 +1264,21 @@ impl Lowerer { }) } - fn lower_measure(&mut self, stmt: &syntax::MeasureStmt) -> semantic::StmtKind { - self.push_unimplemented_error_message("measure stmt", stmt.span); - semantic::StmtKind::Err + fn lower_measure(&mut self, stmt: &syntax::MeasureArrowStmt) -> semantic::StmtKind { + // `measure q -> c;` is syntax sugar for `c = measure q;` + if let Some(target) = &stmt.target { + self.lower_assign(&syntax::AssignStmt { + span: stmt.span, + lhs: *target.clone(), + rhs: syntax::ValueExpr::Measurement(stmt.measurement.clone()), + }) + } else { + let measure = self.lower_measure_expr(&stmt.measurement); + semantic::StmtKind::ExprStmt(semantic::ExprStmt { + span: stmt.span, + expr: measure, + }) + } } fn lower_pragma(&mut self, stmt: &syntax::Pragma) -> semantic::StmtKind { @@ -1037,13 +1292,69 @@ impl Lowerer { } fn lower_quantum_decl(&mut self, stmt: &syntax::QubitDeclaration) -> semantic::StmtKind { - self.push_unimplemented_error_message("qubit decl stmt", stmt.span); - semantic::StmtKind::Err + // If there wasn't an explicit size, infer the size to be 1. + let (ty, size_and_span) = if let Some(size_expr) = &stmt.size { + let size_expr = self.lower_expr(size_expr); + let span = size_expr.span; + let size_expr = self.try_cast_expr_to_type(&Type::UInt(None, true), &size_expr); + + if let Some(Some(semantic::LiteralKind::Int(val))) = + size_expr.map(|expr| expr.const_eval(&self.symbols)) + { + if let Ok(size) = u32::try_from(val) { + ( + Type::QubitArray(ArrayDimensions::One(size)), + Some((size, span)), + ) + } else { + let message = "quantum register size".into(); + self.push_semantic_error(SemanticErrorKind::ExprMustFitInU32(message, span)); + return semantic::StmtKind::Err; + } + } else { + let message = "quantum register size".into(); + self.push_semantic_error(SemanticErrorKind::ExprMustBeConst(message, span)); + return semantic::StmtKind::Err; + } + } else { + (Type::Qubit, None) + }; + + let name = stmt.qubit.name.clone(); + let qsharp_ty = self.convert_semantic_type_to_qsharp_type(&ty.clone(), stmt.ty_span); + + let symbol = Symbol::new( + &name, + stmt.qubit.span, + ty.clone(), + qsharp_ty, + IOKind::Default, + ); + + let symbol_id = self.try_insert_or_get_existing_symbol_id(name, symbol, stmt.qubit.span); + + if let Some((size, size_span)) = size_and_span { + semantic::StmtKind::QubitArrayDecl(semantic::QubitArrayDeclaration { + span: stmt.span, + symbol_id, + size, + size_span, + }) + } else { + semantic::StmtKind::QubitDecl(semantic::QubitDeclaration { + span: stmt.span, + symbol_id, + }) + } } fn lower_reset(&mut self, stmt: &syntax::ResetStmt) -> semantic::StmtKind { - self.push_unimplemented_error_message("reset stmt", stmt.span); - semantic::StmtKind::Err + let operand = self.lower_gate_operand(&stmt.operand); + semantic::StmtKind::Reset(semantic::ResetStmt { + span: stmt.span, + reset_token_span: stmt.reset_token_span, + operand: Box::new(operand), + }) } fn lower_return(&mut self, stmt: &syntax::ReturnStmt) -> semantic::StmtKind { @@ -1072,16 +1383,17 @@ impl Lowerer { // The condition for the switch statement must be an integer type // so we use `cast_expr_to_type`, forcing the type to be an integer // type with implicit casts if necessary. - let target_ty = Type::Int(None, false); - let target = self.cast_expr_to_type(&target_ty, &target, target.span); + let target_ty = Type::Int(None, target.ty.is_const()); + let target = self.cast_expr_to_type(&target_ty, &target); // It is a parse error to have a switch statement with no cases, // even if the default block is present. Getting here means the // parser is broken or they changed the grammar. - assert!( - !cases.is_empty(), - "switch statement must have a control expression and at least one case" - ); + if cases.is_empty() { + self.push_semantic_error(SemanticErrorKind::SwitchStatementMustHaveAtLeastOneCase( + stmt.span, + )); + } // We push a semantic error on switch statements if version is less than 3.1, // as they were introduced in 3.1. @@ -1109,7 +1421,7 @@ impl Lowerer { } fn lower_switch_case(&mut self, switch_case: &syntax::SwitchCase) -> semantic::SwitchCase { - let label_ty = Type::Int(None, false); + let label_ty = Type::Int(None, true); let labels = switch_case .labels .iter() @@ -1118,7 +1430,7 @@ impl Lowerer { // so we use `cast_expr_to_type`, forcing the type to be an integer // type with implicit casts if necessary. let label = self.lower_expr(label); - self.cast_expr_to_type(&label_ty, &label, label.span) + self.cast_expr_to_type(&label_ty, &label) }) .collect::>(); @@ -1132,28 +1444,27 @@ impl Lowerer { } fn lower_while_stmt(&mut self, stmt: &syntax::WhileLoop) -> semantic::StmtKind { - let while_condition = self.lower_expr(&stmt.while_condition); + let condition = self.lower_expr(&stmt.while_condition); let body = self.lower_stmt(&stmt.body); // The semantics of a while statement is that the condition must be // of type bool, so we try to cast it, inserting a cast if necessary. - let cond_ty = Type::Bool(false); - let while_condition = - self.cast_expr_to_type(&cond_ty, &while_condition, while_condition.span); + let cond_ty = Type::Bool(condition.ty.is_const()); + let while_condition = self.cast_expr_to_type(&cond_ty, &condition); semantic::StmtKind::WhileLoop(semantic::WhileLoop { span: stmt.span, - while_condition, + condition: while_condition, body, }) } fn get_semantic_type_from_tydef( &mut self, - scalar_ty: &syntax::TypeDef, + ty: &syntax::TypeDef, is_const: bool, ) -> crate::semantic::types::Type { - match scalar_ty { + match ty { syntax::TypeDef::Scalar(scalar_type) => { self.get_semantic_type_from_scalar_ty(scalar_type, is_const) } @@ -1166,38 +1477,73 @@ impl Lowerer { } } - /// designators are positive integer literals when used - /// in the context of a type definition. - fn get_size_designator_from_expr(&mut self, expr: &syntax::Expr) -> Option { - if let syntax::ExprKind::Lit(lit) = expr.kind.as_ref() { - if let syntax::LiteralKind::Int(value) = lit.kind { - if value > 0 { - if let Ok(value) = u32::try_from(value) { - Some(value) - } else { - self.push_semantic_error(SemanticErrorKind::DesignatorTooLarge(lit.span)); - None - } - } else { - self.push_semantic_error( - SemanticErrorKind::DesignatorMustBePositiveIntLiteral(lit.span), - ); - None - } - } else { - self.push_semantic_error(SemanticErrorKind::DesignatorMustBePositiveIntLiteral( - lit.span, - )); - None - } + /// Helper function for const evaluating array sizes, type widths, and durations. + fn const_eval_designator(&mut self, expr: &syntax::Expr) -> Option { + let expr = self.lower_expr(expr); + let expr_span = expr.span; + let expr = self.cast_expr_with_target_type_or_default( + Some(expr), + &Type::UInt(None, true), + expr_span, + ); + + if let Some(lit) = expr.const_eval(&self.symbols) { + Some(lit) } else { - self.push_semantic_error(SemanticErrorKind::DesignatorMustBePositiveIntLiteral( + self.push_semantic_error(SemanticErrorKind::ExprMustBeConst( + "designator".to_string(), expr.span, )); None } } + fn const_eval_array_size_designator_from_expr(&mut self, expr: &syntax::Expr) -> Option { + let semantic::LiteralKind::Int(val) = self.const_eval_designator(expr)? else { + self.push_semantic_error(SemanticErrorKind::ArraySizeMustBeNonNegativeConstExpr( + expr.span, + )); + return None; + }; + + if val < 0 { + self.push_semantic_error(SemanticErrorKind::ArraySizeMustBeNonNegativeConstExpr( + expr.span, + )); + return None; + }; + + let Ok(val) = u32::try_from(val) else { + self.push_semantic_error(SemanticErrorKind::DesignatorTooLarge(expr.span)); + return None; + }; + + Some(val) + } + + fn const_eval_type_width_designator_from_expr(&mut self, expr: &syntax::Expr) -> Option { + let semantic::LiteralKind::Int(val) = self.const_eval_designator(expr)? else { + self.push_semantic_error(SemanticErrorKind::TypeWidthMustBePositiveIntConstExpr( + expr.span, + )); + return None; + }; + + if val < 1 { + self.push_semantic_error(SemanticErrorKind::TypeWidthMustBePositiveIntConstExpr( + expr.span, + )); + return None; + }; + + let Ok(val) = u32::try_from(val) else { + self.push_semantic_error(SemanticErrorKind::DesignatorTooLarge(expr.span)); + return None; + }; + + Some(val) + } + fn get_semantic_type_from_scalar_ty( &mut self, scalar_ty: &syntax::ScalarType, @@ -1206,11 +1552,11 @@ impl Lowerer { match &scalar_ty.kind { syntax::ScalarTypeKind::Bit(bit_type) => match &bit_type.size { Some(size) => { - let Some(size) = self.get_size_designator_from_expr(size) else { + let Some(size) = self.const_eval_array_size_designator_from_expr(size) else { return crate::semantic::types::Type::Err; }; crate::semantic::types::Type::BitArray( - super::types::ArrayDimensions::One(size), + super::types::ArrayDimensions::from(size), is_const, ) } @@ -1218,7 +1564,7 @@ impl Lowerer { }, syntax::ScalarTypeKind::Int(int_type) => match &int_type.size { Some(size) => { - let Some(size) = self.get_size_designator_from_expr(size) else { + let Some(size) = self.const_eval_type_width_designator_from_expr(size) else { return crate::semantic::types::Type::Err; }; crate::semantic::types::Type::Int(Some(size), is_const) @@ -1227,7 +1573,7 @@ impl Lowerer { }, syntax::ScalarTypeKind::UInt(uint_type) => match &uint_type.size { Some(size) => { - let Some(size) = self.get_size_designator_from_expr(size) else { + let Some(size) = self.const_eval_type_width_designator_from_expr(size) else { return crate::semantic::types::Type::Err; }; crate::semantic::types::Type::UInt(Some(size), is_const) @@ -1236,7 +1582,7 @@ impl Lowerer { }, syntax::ScalarTypeKind::Float(float_type) => match &float_type.size { Some(size) => { - let Some(size) = self.get_size_designator_from_expr(size) else { + let Some(size) = self.const_eval_type_width_designator_from_expr(size) else { return crate::semantic::types::Type::Err; }; crate::semantic::types::Type::Float(Some(size), is_const) @@ -1246,7 +1592,8 @@ impl Lowerer { syntax::ScalarTypeKind::Complex(complex_type) => match &complex_type.base_size { Some(float_type) => match &float_type.size { Some(size) => { - let Some(size) = self.get_size_designator_from_expr(size) else { + let Some(size) = self.const_eval_type_width_designator_from_expr(size) + else { return crate::semantic::types::Type::Err; }; crate::semantic::types::Type::Complex(Some(size), is_const) @@ -1257,7 +1604,7 @@ impl Lowerer { }, syntax::ScalarTypeKind::Angle(angle_type) => match &angle_type.size { Some(size) => { - let Some(size) = self.get_size_designator_from_expr(size) else { + let Some(size) = self.const_eval_type_width_designator_from_expr(size) else { return crate::semantic::types::Type::Err; }; crate::semantic::types::Type::Angle(Some(size), is_const) @@ -1291,22 +1638,33 @@ impl Lowerer { ); crate::semantic::types::Type::Err } - fn lower_expr_with_target_type( + + fn cast_expr_with_target_type_or_default( &mut self, - expr: Option<&syntax::Expr>, + expr: Option, ty: &Type, span: Span, ) -> semantic::Expr { - let Some(expr) = expr else { + let Some(mut rhs) = expr else { // In OpenQASM, classical variables may be uninitialized, but in Q#, // they must be initialized. We will use the default value for the type // to initialize the variable. return self.get_default_value(ty, span); }; - let rhs = self.lower_expr(expr); + let rhs_ty = rhs.ty.clone(); + + // avoid the cast + if *ty == rhs_ty { + // if the types are the same, we can use the rhs as is + return rhs; + } + // if we have an exact type match, we can use the rhs as is if types_equal_except_const(ty, &rhs_ty) { + // Since one the two exprs is non-const, we need to relax + // the constness in the returned expr. + rhs.ty = rhs.ty.as_non_const(); return rhs; } @@ -1332,17 +1690,19 @@ impl Lowerer { // implicit and explicit conversions. We need to cast the rhs to the // lhs type, but if that cast fails, we will have already pushed an error // and we can't proceed - self.cast_expr_to_type(ty, &rhs, span) + self.cast_expr_to_type(ty, &rhs) } - fn lower_measure_expr( - &mut self, - expr: &syntax::MeasureExpr, - span: Span, - ) -> semantic::MeasureExpr { - semantic::MeasureExpr { - span, + fn lower_measure_expr(&mut self, expr: &syntax::MeasureExpr) -> semantic::Expr { + let measurement = semantic::MeasureExpr { + span: expr.span, + measure_token_span: expr.measure_token_span, operand: self.lower_gate_operand(&expr.operand), + }; + semantic::Expr { + span: expr.span, + kind: Box::new(semantic::ExprKind::Measure(measurement)), + ty: Type::Bit(false), } } @@ -1358,9 +1718,8 @@ impl Lowerer { } }; let expr = match ty { - Type::Bit(_) | Type::Int(_, _) | Type::UInt(_, _) => { - Some(from_lit_kind(LiteralKind::Int(0))) - } + Type::Bit(_) => Some(from_lit_kind(LiteralKind::Bit(false))), + Type::Int(_, _) | Type::UInt(_, _) => Some(from_lit_kind(LiteralKind::Int(0))), Type::Bool(_) => Some(from_lit_kind(LiteralKind::Bool(false))), Type::Angle(_, _) | Type::Float(_, _) => Some(from_lit_kind(LiteralKind::Float(0.0))), Type::Complex(_, _) => Some(from_lit_kind(LiteralKind::Complex(0.0, 0.0))), @@ -1384,10 +1743,19 @@ impl Lowerer { self.push_unsupported_error_message(message, span); None } - Type::BitArray(_, _) => { - self.push_unimplemented_error_message("bit array default value", span); - None - } + Type::BitArray(dims, _) => match dims { + ArrayDimensions::One(size) => Some(from_lit_kind( + semantic::LiteralKind::Bitstring(BigInt::ZERO, *size), + )), + ArrayDimensions::Err => None, + _ => { + self.push_unimplemented_error_message( + "multidimensional bit array default value", + span, + ); + None + } + }, Type::BoolArray(_) => { self.push_unimplemented_error_message("bool array default value", span); None @@ -1416,16 +1784,12 @@ impl Lowerer { self.push_unimplemented_error_message("uint array default value", span); None } - Type::Duration(_) - | Type::Err - | Type::Gate(_, _) - | Type::Range - | Type::Set - | Type::Void => { + Type::Duration(_) | Type::Gate(_, _) | Type::Range | Type::Set | Type::Void => { let message = format!("Default values for {ty:?} are unsupported."); self.push_unsupported_error_message(message, span); None } + Type::Err => None, }; let Some(expr) = expr else { return err_expr!(ty.as_const()); @@ -1445,6 +1809,7 @@ impl Lowerer { }; expr } + #[allow(clippy::too_many_lines)] fn try_coerce_literal_expr_to_type( &mut self, @@ -1475,15 +1840,15 @@ impl Lowerer { // can_cast_literal_with_value_knowledge guarantees that value is 0 or 1 return Some(semantic::Expr { span, - kind: Box::new(semantic::ExprKind::Lit(semantic::LiteralKind::Int(*value))), + kind: Box::new(semantic::ExprKind::Lit(semantic::LiteralKind::Bit( + *value != 0, + ))), ty: lhs_ty.as_const(), }); } else if let semantic::LiteralKind::Bool(value) = kind { return Some(semantic::Expr { span, - kind: Box::new(semantic::ExprKind::Lit(semantic::LiteralKind::Int( - i64::from(*value), - ))), + kind: Box::new(semantic::ExprKind::Lit(semantic::LiteralKind::Bit(*value))), ty: lhs_ty.as_const(), }); } @@ -1677,14 +2042,9 @@ impl Lowerer { } } - fn cast_expr_to_type( - &mut self, - ty: &Type, - expr: &semantic::Expr, - span: Span, - ) -> semantic::Expr { - let Some(cast_expr) = self.try_cast_expr_to_type(ty, expr, span) else { - self.push_invalid_cast_error(ty, &expr.ty, span); + fn cast_expr_to_type(&mut self, ty: &Type, expr: &semantic::Expr) -> semantic::Expr { + let Some(cast_expr) = self.try_cast_expr_to_type(ty, expr) else { + self.push_invalid_cast_error(ty, &expr.ty, expr.span); return expr.clone(); }; cast_expr @@ -1694,7 +2054,6 @@ impl Lowerer { &mut self, ty: &Type, expr: &semantic::Expr, - span: Span, ) -> Option { if *ty == expr.ty { // Base case, we shouldn't have gotten here @@ -1704,7 +2063,10 @@ impl Lowerer { if types_equal_except_const(ty, &expr.ty) { if expr.ty.is_const() { // lhs isn't const, but rhs is, we can just return the rhs - return Some(expr.clone()); + let mut expr = expr.clone(); + // relax constness + expr.ty = expr.ty.as_non_const(); + return Some(expr); } // the lsh is supposed to be const but is being initialized // to a non-const value, we can't allow this @@ -1740,7 +2102,7 @@ impl Lowerer { // the standard library. match &expr.ty { Type::Angle(_, _) => Self::cast_angle_expr_to_type(ty, expr), - Type::Bit(_) => self.cast_bit_expr_to_type(ty, expr, span), + Type::Bit(_) => self.cast_bit_expr_to_type(ty, expr), Type::Bool(_) => Self::cast_bool_expr_to_type(ty, expr), Type::Complex(_, _) => cast_complex_expr_to_type(ty, expr), Type::Float(_, _) => Self::cast_float_expr_to_type(ty, expr), @@ -1774,24 +2136,19 @@ impl Lowerer { /// +----------------+-------+-----+------+-------+-------+-----+----------+-------+ /// | bit | Yes | Yes | Yes | No | Yes | - | No | No | /// +----------------+-------+-----+------+-------+-------+-----+----------+-------+ - fn cast_bit_expr_to_type( - &mut self, - ty: &Type, - rhs: &semantic::Expr, - span: Span, - ) -> Option { + fn cast_bit_expr_to_type(&mut self, ty: &Type, rhs: &semantic::Expr) -> Option { assert!(matches!(rhs.ty, Type::Bit(..))); // There is no operand, choosing the span of the node // but we could use the expr span as well. match ty { &Type::Angle(..) => { let msg = "Cast bit to angle"; - self.push_unimplemented_error_message(msg, span); + self.push_unimplemented_error_message(msg, rhs.span); None } &Type::Float(..) => { // The spec says that this cast isn't supported, but it - // casts to other types that case to float. For now, we'll + // casts to other types that cast to float. For now, we'll // say it is invalid like the spec. None } @@ -1921,7 +2278,7 @@ impl Lowerer { let (ty, lhs_uint_promotion, rhs_uint_promotion) = promote_to_uint_ty(&left_type, &right_type); let Some(ty) = ty else { - let target_ty = Type::UInt(None, false); + let target_ty = Type::UInt(None, left_type.is_const() && right_type.is_const()); if lhs_uint_promotion.is_none() { let target_str: String = format!("{target_ty:?}"); let kind = SemanticErrorKind::CannotCast( @@ -1955,10 +2312,10 @@ impl Lowerer { }; // Now that we know the effective Uint type, we can cast the lhs and rhs // so that operations can be performed on them. - let new_lhs = self.cast_expr_to_type(&ty, &lhs, lhs.span); + let new_lhs = self.cast_expr_to_type(&ty, &lhs); // only cast the rhs if the operator requires symmetric conversion let new_rhs = if Self::binop_requires_bitwise_symmetric_conversion(op) { - self.cast_expr_to_type(&ty, &rhs, rhs.span) + self.cast_expr_to_type(&ty, &rhs) } else { rhs }; @@ -1975,7 +2332,7 @@ impl Lowerer { ty, }; - let final_expr = self.cast_expr_to_type(&left_type, &expr, span); + let final_expr = self.cast_expr_to_type(&left_type, &expr); return final_expr; } @@ -1986,15 +2343,17 @@ impl Lowerer { // Q# has built-in functions: IntAsDouble, IntAsBigInt to handle two cases. // If the width of a float is greater than 64, we can't represent it as a double. + let ty_constness = lhs.ty.is_const() && rhs.ty.is_const(); + let (lhs, rhs, ty) = if matches!(op, syntax::BinOp::AndL | syntax::BinOp::OrL) { - let ty = Type::Bool(false); - let new_lhs = self.cast_expr_to_type(&ty, &lhs, lhs.span); - let new_rhs = self.cast_expr_to_type(&ty, &rhs, rhs.span); + let ty = Type::Bool(ty_constness); + let new_lhs = self.cast_expr_to_type(&ty, &lhs); + let new_rhs = self.cast_expr_to_type(&ty, &rhs); (new_lhs, new_rhs, ty) } else if binop_requires_int_conversion_for_type(op, &left_type, &rhs.ty) { - let ty = Type::Int(None, false); - let new_lhs = self.cast_expr_to_type(&ty, &lhs, lhs.span); - let new_rhs = self.cast_expr_to_type(&ty, &rhs, lhs.span); + let ty = Type::Int(None, ty_constness); + let new_lhs = self.cast_expr_to_type(&ty, &lhs); + let new_rhs = self.cast_expr_to_type(&ty, &rhs); (new_lhs, new_rhs, ty) } else if requires_symmetric_conversion(op) { let promoted_type = try_promote_with_casting(&left_type, &right_type); @@ -2008,10 +2367,10 @@ impl Lowerer { { self.coerce_literal_expr_to_type(&promoted_type, &lhs, kind) } else { - self.cast_expr_to_type(&promoted_type, &lhs, lhs.span) + self.cast_expr_to_type(&promoted_type, &lhs) } } - _ => self.cast_expr_to_type(&promoted_type, &lhs, lhs.span), + _ => self.cast_expr_to_type(&promoted_type, &lhs), } }; let new_right = if promoted_type == right_type { @@ -2024,16 +2383,16 @@ impl Lowerer { { self.coerce_literal_expr_to_type(&promoted_type, &rhs, kind) } else { - self.cast_expr_to_type(&promoted_type, &rhs, rhs.span) + self.cast_expr_to_type(&promoted_type, &rhs) } } - _ => self.cast_expr_to_type(&promoted_type, &rhs, rhs.span), + _ => self.cast_expr_to_type(&promoted_type, &rhs), } }; (new_left, new_right, promoted_type) } else if binop_requires_symmetric_int_conversion(op) { - let ty = Type::Int(None, false); - let new_rhs = self.cast_expr_to_type(&ty, &rhs, rhs.span); + let ty = Type::Int(None, ty_constness); + let new_rhs = self.cast_expr_to_type(&ty, &rhs); (lhs, new_rhs, left_type) } else { (lhs, rhs, left_type) @@ -2082,7 +2441,7 @@ impl Lowerer { | semantic::BinOp::Lt | semantic::BinOp::Lte | semantic::BinOp::Neq - | semantic::BinOp::OrL => Type::Bool(false), + | semantic::BinOp::OrL => Type::Bool(ty_constness), _ => ty, }; let mut expr = expr; @@ -2302,10 +2661,27 @@ impl Lowerer { #[allow(clippy::unused_self)] fn lower_gate_operand(&mut self, operand: &syntax::GateOperand) -> semantic::GateOperand { - match operand { - syntax::GateOperand::IndexedIdent(_) - | syntax::GateOperand::HardwareQubit(_) - | syntax::GateOperand::Err => semantic::GateOperand::Err, + let kind = match &operand.kind { + syntax::GateOperandKind::IndexedIdent(indexed_ident) => { + semantic::GateOperandKind::Expr(Box::new( + self.lower_indexed_ident_expr(indexed_ident), + )) + } + syntax::GateOperandKind::HardwareQubit(hw) => { + semantic::GateOperandKind::HardwareQubit(Self::lower_hardware_qubit(hw)) + } + syntax::GateOperandKind::Err => semantic::GateOperandKind::Err, + }; + semantic::GateOperand { + span: operand.span, + kind, + } + } + + fn lower_hardware_qubit(hw: &syntax::HardwareQubit) -> semantic::HardwareQubit { + semantic::HardwareQubit { + span: hw.span, + name: hw.name.clone(), } } @@ -2421,3 +2797,26 @@ fn get_identifier_name(identifier: &syntax::Identifier) -> std::rc::Rc { syntax::Identifier::IndexedIdent(ident) => ident.name.name.clone(), } } + +fn try_get_qsharp_name_and_implicit_modifiers>( + gate_name: S, + name_span: Span, +) -> Option<(String, semantic::QuantumGateModifier)> { + use semantic::GateModifierKind::*; + + let make_modifier = |kind| semantic::QuantumGateModifier { + span: name_span, + kind, + }; + + // ch, crx, cry, crz, sdg, and tdg + match gate_name.as_ref() { + "ch" => Some(("H".to_string(), make_modifier(Ctrl(1)))), + "crx" => Some(("Rx".to_string(), make_modifier(Ctrl(1)))), + "cry" => Some(("Ry".to_string(), make_modifier(Ctrl(1)))), + "crz" => Some(("Rz".to_string(), make_modifier(Ctrl(1)))), + "sdg" => Some(("S".to_string(), make_modifier(Inv))), + "tdg" => Some(("T".to_string(), make_modifier(Inv))), + _ => None, + } +} diff --git a/compiler/qsc_qasm3/src/semantic/symbols.rs b/compiler/qsc_qasm3/src/semantic/symbols.rs index 5479c8ada4..11a0a01dd6 100644 --- a/compiler/qsc_qasm3/src/semantic/symbols.rs +++ b/compiler/qsc_qasm3/src/semantic/symbols.rs @@ -1,12 +1,16 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. +use core::f64; use std::rc::Rc; use qsc_data_structures::{index_map::IndexMap, span::Span}; use rustc_hash::FxHashMap; -use super::types::Type; +use super::{ + ast::{Expr, ExprKind, LiteralKind}, + types::Type, +}; /// We need a symbol table to keep track of the symbols in the program. /// The scoping rules for QASM3 are slightly different from Q#. This also @@ -89,13 +93,59 @@ impl std::fmt::Display for SymbolId { } } -#[derive(Debug, Clone, PartialEq, Eq)] +#[derive(Debug, Clone)] pub struct Symbol { pub name: String, pub span: Span, pub ty: Type, pub qsharp_ty: crate::types::Type, pub io_kind: IOKind, + /// Used for const evaluation. This field should only be Some(_) + /// if the symbol is const. This Expr holds the whole const expr + /// unevaluated. + const_expr: Option>, +} + +impl Symbol { + #[must_use] + pub fn new( + name: &str, + span: Span, + ty: Type, + qsharp_ty: crate::types::Type, + io_kind: IOKind, + ) -> Self { + Self { + name: name.to_string(), + span, + ty, + qsharp_ty, + io_kind, + const_expr: None, + } + } + + #[must_use] + pub fn with_const_expr(self, value: Rc) -> Self { + assert!( + value.ty.is_const(), + "this builder pattern should only be used with const expressions" + ); + Symbol { + const_expr: Some(value), + ..self + } + } + + /// Returns the value of the symbol. + #[must_use] + pub fn get_const_expr(&self) -> Rc { + if let Some(val) = &self.const_expr { + val.clone() + } else { + unreachable!("this function should only be called on const symbols"); + } + } } impl std::fmt::Display for Symbol { @@ -119,6 +169,7 @@ impl Default for Symbol { ty: Type::Err, qsharp_ty: crate::types::Type::Tuple(vec![]), io_kind: IOKind::default(), + const_expr: None, } } } @@ -223,7 +274,14 @@ pub enum ScopeKind { Block, } -const BUILTIN_SYMBOLS: [&str; 6] = ["pi", "π", "tau", "τ", "euler", "ℇ"]; +const BUILTIN_SYMBOLS: [(&str, f64); 6] = [ + ("pi", f64::consts::PI), + ("π", f64::consts::PI), + ("tau", f64::consts::TAU), + ("τ", f64::consts::TAU), + ("euler", f64::consts::E), + ("ℇ", f64::consts::E), +]; impl Default for SymbolTable { fn default() -> Self { @@ -235,14 +293,42 @@ impl Default for SymbolTable { current_id: SymbolId::default(), }; - // Define global constants - for symbol in BUILTIN_SYMBOLS { + slf.insert_symbol(Symbol { + name: "U".to_string(), + span: Span::default(), + ty: Type::Gate(3, 1), + qsharp_ty: crate::types::Type::Callable(crate::types::CallableKind::Operation, 3, 1), + io_kind: IOKind::Default, + const_expr: None, + }) + .unwrap_or_else(|_| panic!("Failed to insert symbol: U")); + + slf.insert_symbol(Symbol { + name: "gphase".to_string(), + span: Span::default(), + ty: Type::Gate(1, 0), + qsharp_ty: crate::types::Type::Callable(crate::types::CallableKind::Operation, 1, 0), + io_kind: IOKind::Default, + const_expr: None, + }) + .unwrap_or_else(|_| panic!("Failed to insert symbol: gphase")); + + // Define global constants. + for (symbol, val) in BUILTIN_SYMBOLS { + let ty = Type::Float(None, true); + let expr = Expr { + span: Span::default(), + kind: Box::new(ExprKind::Lit(LiteralKind::Float(val))), + ty: ty.clone(), + }; + slf.insert_symbol(Symbol { name: symbol.to_string(), span: Span::default(), - ty: Type::Float(None, true), + ty, qsharp_ty: crate::types::Type::Double(true), io_kind: IOKind::Default, + const_expr: Some(Rc::new(expr)), }) .unwrap_or_else(|_| panic!("Failed to insert symbol: {symbol}")); } @@ -287,6 +373,7 @@ impl SymbolTable { ty: Type::Err, qsharp_ty: crate::types::Type::Err, io_kind: IOKind::Default, + const_expr: None, }); let id = self.current_id; self.current_id = self.current_id.successor(); @@ -440,7 +527,11 @@ impl SymbolTable { for symbol in scope .get_ordered_symbols() .into_iter() - .filter(|symbol| !BUILTIN_SYMBOLS.contains(&symbol.name.as_str())) + .filter(|symbol| { + !BUILTIN_SYMBOLS + .map(|pair| pair.0) + .contains(&symbol.name.as_str()) + }) .filter(|symbol| symbol.io_kind == IOKind::Default) { if symbol.ty.is_inferred_output_type() { diff --git a/compiler/qsc_qasm3/src/semantic/tests.rs b/compiler/qsc_qasm3/src/semantic/tests.rs index 268ace49fb..ce82fc5ff6 100644 --- a/compiler/qsc_qasm3/src/semantic/tests.rs +++ b/compiler/qsc_qasm3/src/semantic/tests.rs @@ -243,15 +243,15 @@ fn semantic_errors_map_to_their_corresponding_file_specific_spans() { Stmt [196-206]: annotations: kind: ClassicalDeclarationStmt [196-206]: - symbol_id: 24 + symbol_id: 26 ty_span: [196-199] init_expr: Expr [204-205]: ty: Bit(true) - kind: Lit: Int(1) + kind: Lit: Bit(1) Stmt [211-227]: annotations: kind: ClassicalDeclarationStmt [211-227]: - symbol_id: 24 + symbol_id: 26 ty_span: [211-215] init_expr: Expr [220-226]: ty: Bool(false) @@ -259,18 +259,18 @@ fn semantic_errors_map_to_their_corresponding_file_specific_spans() { op: AndL lhs: Expr [220-221]: ty: Err - kind: SymbolId(25) + kind: SymbolId(27) rhs: Expr [225-226]: ty: Bool(false) kind: Cast [0-0]: ty: Bool(false) expr: Expr [225-226]: ty: Bit(false) - kind: SymbolId(24) + kind: SymbolId(26) Stmt [140-154]: annotations: kind: ClassicalDeclarationStmt [140-154]: - symbol_id: 26 + symbol_id: 28 ty_span: [140-145] init_expr: Expr [150-153]: ty: Angle(None, true) @@ -278,7 +278,7 @@ fn semantic_errors_map_to_their_corresponding_file_specific_spans() { Stmt [159-179]: annotations: kind: ClassicalDeclarationStmt [159-179]: - symbol_id: 27 + symbol_id: 29 ty_span: [159-164] init_expr: Expr [169-178]: ty: Float(None, false) @@ -286,7 +286,7 @@ fn semantic_errors_map_to_their_corresponding_file_specific_spans() { op: Add lhs: Expr [169-170]: ty: Angle(None, false) - kind: SymbolId(26) + kind: SymbolId(28) rhs: Expr [173-178]: ty: Float(None, false) kind: Cast [0-0]: @@ -297,11 +297,11 @@ fn semantic_errors_map_to_their_corresponding_file_specific_spans() { Stmt [74-84]: annotations: kind: ClassicalDeclarationStmt [74-84]: - symbol_id: 29 + symbol_id: 31 ty_span: [74-77] init_expr: Expr [82-83]: ty: Err - kind: SymbolId(28) + kind: SymbolId(30) [Qsc.Qasm3.Compile.UndefinedSymbol @@ -348,10 +348,10 @@ fn semantic_errors_map_to_their_corresponding_file_specific_spans() { , Qsc.Qasm3.Compile.CannotCast x Cannot cast expression of type Err to type Bit(false) - ,-[source0.qasm:4:5] + ,-[source0.qasm:4:13] 3 | include "source1.qasm"; 4 | bit c = r; // undefined symbol r - : ^^^^^^^^^^ + : ^ 5 | `---- ]"#]], diff --git a/compiler/qsc_qasm3/src/semantic/tests/decls.rs b/compiler/qsc_qasm3/src/semantic/tests/decls.rs index ca1c08904f..daf0381d85 100644 --- a/compiler/qsc_qasm3/src/semantic/tests/decls.rs +++ b/compiler/qsc_qasm3/src/semantic/tests/decls.rs @@ -61,106 +61,61 @@ fn scalar_ty_designator_must_be_positive() { Stmt [0-10]: annotations: kind: ClassicalDeclarationStmt [0-10]: - symbol_id: 6 + symbol_id: 8 ty_span: [0-7] init_expr: Expr [0-0]: ty: Err kind: Err - [Qsc.Qasm3.Compile.DesignatorMustBePositiveIntLiteral + [Qsc.Qasm3.Compile.TypeWidthMustBePositiveIntConstExpr - x Designator must be a positive literal integer. + x Type width must be a positive integer const expression. ,-[test:1:5] 1 | int[-5] i; : ^^ `---- - , Qsc.Qasm3.Compile.Unimplemented - - x this statement is not yet handled during OpenQASM 3 import: Converting Err - | to Q# type - ,-[test:1:1] - 1 | int[-5] i; - : ^^^^^^^ - `---- - , Qsc.Qasm3.Compile.NotSupported - - x Default values for Err are unsupported. are not supported. - ,-[test:1:1] - 1 | int[-5] i; - : ^^^^^^^^^^ - `---- ]"#]], ); } #[test] -fn scalar_ty_designator_must_be_int_literal() { +fn scalar_ty_designator_must_be_castable_to_const_int() { check( - r#"int[size] i; float[0.0] j;"#, + r#"const angle size = 2.0; int[size] i;"#, &expect![[r#" Program: version: statements: - Stmt [0-12]: + Stmt [0-23]: annotations: - kind: ClassicalDeclarationStmt [0-12]: - symbol_id: 6 - ty_span: [0-9] - init_expr: Expr [0-0]: - ty: Err - kind: Err - Stmt [13-26]: + kind: ClassicalDeclarationStmt [0-23]: + symbol_id: 8 + ty_span: [6-11] + init_expr: Expr [19-22]: + ty: Angle(None, true) + kind: Lit: Float(2.0) + Stmt [24-36]: annotations: - kind: ClassicalDeclarationStmt [13-26]: - symbol_id: 7 - ty_span: [13-23] + kind: ClassicalDeclarationStmt [24-36]: + symbol_id: 9 + ty_span: [24-33] init_expr: Expr [0-0]: ty: Err kind: Err - [Qsc.Qasm3.Compile.DesignatorMustBePositiveIntLiteral + [Qsc.Qasm3.Compile.CannotCast - x Designator must be a positive literal integer. - ,-[test:1:5] - 1 | int[size] i; float[0.0] j; - : ^^^^ + x Cannot cast expression of type Angle(None, true) to type UInt(None, true) + ,-[test:1:29] + 1 | const angle size = 2.0; int[size] i; + : ^^^^ `---- - , Qsc.Qasm3.Compile.Unimplemented - - x this statement is not yet handled during OpenQASM 3 import: Converting Err - | to Q# type - ,-[test:1:1] - 1 | int[size] i; float[0.0] j; - : ^^^^^^^^^ - `---- - , Qsc.Qasm3.Compile.NotSupported - - x Default values for Err are unsupported. are not supported. - ,-[test:1:1] - 1 | int[size] i; float[0.0] j; - : ^^^^^^^^^^^^ - `---- - , Qsc.Qasm3.Compile.DesignatorMustBePositiveIntLiteral - - x Designator must be a positive literal integer. - ,-[test:1:20] - 1 | int[size] i; float[0.0] j; - : ^^^ - `---- - , Qsc.Qasm3.Compile.Unimplemented - - x this statement is not yet handled during OpenQASM 3 import: Converting Err - | to Q# type - ,-[test:1:14] - 1 | int[size] i; float[0.0] j; - : ^^^^^^^^^^ - `---- - , Qsc.Qasm3.Compile.NotSupported + , Qsc.Qasm3.Compile.TypeWidthMustBePositiveIntConstExpr - x Default values for Err are unsupported. are not supported. - ,-[test:1:14] - 1 | int[size] i; float[0.0] j; - : ^^^^^^^^^^^^^ + x Type width must be a positive integer const expression. + ,-[test:1:29] + 1 | const angle size = 2.0; int[size] i; + : ^^^^ `---- ]"#]], ); diff --git a/compiler/qsc_qasm3/src/semantic/tests/decls/angle.rs b/compiler/qsc_qasm3/src/semantic/tests/decls/angle.rs index 8643afdd38..74363762e3 100644 --- a/compiler/qsc_qasm3/src/semantic/tests/decls/angle.rs +++ b/compiler/qsc_qasm3/src/semantic/tests/decls/angle.rs @@ -11,12 +11,12 @@ fn implicit_bitness_default() { "angle x;", &expect![[r#" ClassicalDeclarationStmt [0-8]: - symbol_id: 6 + symbol_id: 8 ty_span: [0-5] init_expr: Expr [0-0]: ty: Angle(None, true) kind: Lit: Float(0.0) - [6] Symbol [6-7]: + [8] Symbol [6-7]: name: x type: Angle(None, false) qsharp_type: Double @@ -30,12 +30,12 @@ fn lit() { "angle x = 42.1;", &expect![[r#" ClassicalDeclarationStmt [0-15]: - symbol_id: 6 + symbol_id: 8 ty_span: [0-5] init_expr: Expr [10-14]: ty: Angle(None, true) kind: Lit: Float(42.1) - [6] Symbol [6-7]: + [8] Symbol [6-7]: name: x type: Angle(None, false) qsharp_type: Double @@ -49,12 +49,12 @@ fn const_lit() { "const angle x = 42.1;", &expect![[r#" ClassicalDeclarationStmt [0-21]: - symbol_id: 6 + symbol_id: 8 ty_span: [6-11] init_expr: Expr [16-20]: ty: Angle(None, true) kind: Lit: Float(42.1) - [6] Symbol [12-13]: + [8] Symbol [12-13]: name: x type: Angle(None, true) qsharp_type: Double @@ -68,12 +68,12 @@ fn lit_explicit_width() { "angle[64] x = 42.1;", &expect![[r#" ClassicalDeclarationStmt [0-19]: - symbol_id: 6 + symbol_id: 8 ty_span: [0-9] init_expr: Expr [14-18]: ty: Angle(Some(64), true) kind: Lit: Float(42.1) - [6] Symbol [10-11]: + [8] Symbol [10-11]: name: x type: Angle(Some(64), false) qsharp_type: Double @@ -87,12 +87,12 @@ fn const_explicit_width_lit() { "const angle[64] x = 42.1;", &expect![[r#" ClassicalDeclarationStmt [0-25]: - symbol_id: 6 + symbol_id: 8 ty_span: [6-15] init_expr: Expr [20-24]: ty: Angle(Some(64), true) kind: Lit: Float(42.1) - [6] Symbol [16-17]: + [8] Symbol [16-17]: name: x type: Angle(Some(64), true) qsharp_type: Double @@ -106,12 +106,12 @@ fn lit_decl_leading_dot() { "angle x = .421;", &expect![[r#" ClassicalDeclarationStmt [0-15]: - symbol_id: 6 + symbol_id: 8 ty_span: [0-5] init_expr: Expr [10-14]: ty: Angle(None, true) kind: Lit: Float(0.421) - [6] Symbol [6-7]: + [8] Symbol [6-7]: name: x type: Angle(None, false) qsharp_type: Double @@ -125,12 +125,12 @@ fn const_lit_decl_leading_dot() { "const angle x = .421;", &expect![[r#" ClassicalDeclarationStmt [0-21]: - symbol_id: 6 + symbol_id: 8 ty_span: [6-11] init_expr: Expr [16-20]: ty: Angle(None, true) kind: Lit: Float(0.421) - [6] Symbol [12-13]: + [8] Symbol [12-13]: name: x type: Angle(None, true) qsharp_type: Double @@ -144,12 +144,12 @@ fn const_lit_decl_leading_dot_scientific() { "const angle x = .421e2;", &expect![[r#" ClassicalDeclarationStmt [0-23]: - symbol_id: 6 + symbol_id: 8 ty_span: [6-11] init_expr: Expr [16-22]: ty: Angle(None, true) kind: Lit: Float(42.1) - [6] Symbol [12-13]: + [8] Symbol [12-13]: name: x type: Angle(None, true) qsharp_type: Double @@ -163,12 +163,12 @@ fn lit_decl_trailing_dot() { "angle x = 421.;", &expect![[r#" ClassicalDeclarationStmt [0-15]: - symbol_id: 6 + symbol_id: 8 ty_span: [0-5] init_expr: Expr [10-14]: ty: Angle(None, true) kind: Lit: Float(421.0) - [6] Symbol [6-7]: + [8] Symbol [6-7]: name: x type: Angle(None, false) qsharp_type: Double @@ -182,12 +182,12 @@ fn const_lit_decl_trailing_dot() { "const angle x = 421.;", &expect![[r#" ClassicalDeclarationStmt [0-21]: - symbol_id: 6 + symbol_id: 8 ty_span: [6-11] init_expr: Expr [16-20]: ty: Angle(None, true) kind: Lit: Float(421.0) - [6] Symbol [12-13]: + [8] Symbol [12-13]: name: x type: Angle(None, true) qsharp_type: Double @@ -201,12 +201,12 @@ fn lit_decl_scientific() { "angle x = 4.21e1;", &expect![[r#" ClassicalDeclarationStmt [0-17]: - symbol_id: 6 + symbol_id: 8 ty_span: [0-5] init_expr: Expr [10-16]: ty: Angle(None, true) kind: Lit: Float(42.1) - [6] Symbol [6-7]: + [8] Symbol [6-7]: name: x type: Angle(None, false) qsharp_type: Double @@ -220,12 +220,12 @@ fn const_lit_decl_scientific() { "const angle x = 4.21e1;", &expect![[r#" ClassicalDeclarationStmt [0-23]: - symbol_id: 6 + symbol_id: 8 ty_span: [6-11] init_expr: Expr [16-22]: ty: Angle(None, true) kind: Lit: Float(42.1) - [6] Symbol [12-13]: + [8] Symbol [12-13]: name: x type: Angle(None, true) qsharp_type: Double @@ -239,12 +239,12 @@ fn lit_decl_scientific_signed_pos() { "angle x = 4.21e+1;", &expect![[r#" ClassicalDeclarationStmt [0-18]: - symbol_id: 6 + symbol_id: 8 ty_span: [0-5] init_expr: Expr [10-17]: ty: Angle(None, true) kind: Lit: Float(42.1) - [6] Symbol [6-7]: + [8] Symbol [6-7]: name: x type: Angle(None, false) qsharp_type: Double @@ -258,12 +258,12 @@ fn const_lit_decl_scientific_signed_pos() { "const angle x = 4.21e+1;", &expect![[r#" ClassicalDeclarationStmt [0-24]: - symbol_id: 6 + symbol_id: 8 ty_span: [6-11] init_expr: Expr [16-23]: ty: Angle(None, true) kind: Lit: Float(42.1) - [6] Symbol [12-13]: + [8] Symbol [12-13]: name: x type: Angle(None, true) qsharp_type: Double @@ -277,12 +277,12 @@ fn lit_decl_scientific_cap_e() { "angle x = 4.21E1;", &expect![[r#" ClassicalDeclarationStmt [0-17]: - symbol_id: 6 + symbol_id: 8 ty_span: [0-5] init_expr: Expr [10-16]: ty: Angle(None, true) kind: Lit: Float(42.1) - [6] Symbol [6-7]: + [8] Symbol [6-7]: name: x type: Angle(None, false) qsharp_type: Double @@ -296,12 +296,12 @@ fn const_lit_decl_scientific_cap_e() { "const angle x = 4.21E1;", &expect![[r#" ClassicalDeclarationStmt [0-23]: - symbol_id: 6 + symbol_id: 8 ty_span: [6-11] init_expr: Expr [16-22]: ty: Angle(None, true) kind: Lit: Float(42.1) - [6] Symbol [12-13]: + [8] Symbol [12-13]: name: x type: Angle(None, true) qsharp_type: Double @@ -315,12 +315,12 @@ fn lit_decl_scientific_signed_neg() { "angle x = 421.0e-1;", &expect![[r#" ClassicalDeclarationStmt [0-19]: - symbol_id: 6 + symbol_id: 8 ty_span: [0-5] init_expr: Expr [10-18]: ty: Angle(None, true) kind: Lit: Float(42.1) - [6] Symbol [6-7]: + [8] Symbol [6-7]: name: x type: Angle(None, false) qsharp_type: Double @@ -334,12 +334,12 @@ fn const_lit_decl_scientific_signed_neg() { "const angle x = 421.0e-1;", &expect![[r#" ClassicalDeclarationStmt [0-25]: - symbol_id: 6 + symbol_id: 8 ty_span: [6-11] init_expr: Expr [16-24]: ty: Angle(None, true) kind: Lit: Float(42.1) - [6] Symbol [12-13]: + [8] Symbol [12-13]: name: x type: Angle(None, true) qsharp_type: Double @@ -353,7 +353,7 @@ fn const_lit_decl_signed_float_lit_cast_neg() { "const angle x = -7.;", &expect![[r#" ClassicalDeclarationStmt [0-20]: - symbol_id: 6 + symbol_id: 8 ty_span: [6-11] init_expr: Expr [17-19]: ty: Angle(None, true) @@ -366,7 +366,7 @@ fn const_lit_decl_signed_float_lit_cast_neg() { expr: Expr [17-19]: ty: Float(None, true) kind: Lit: Float(7.0) - [6] Symbol [12-13]: + [8] Symbol [12-13]: name: x type: Angle(None, true) qsharp_type: Double @@ -385,7 +385,7 @@ fn const_lit_decl_signed_int_lit_cast_neg_fails() { Stmt [0-19]: annotations: kind: ClassicalDeclarationStmt [0-19]: - symbol_id: 6 + symbol_id: 8 ty_span: [6-11] init_expr: Expr [17-18]: ty: Int(None, true) @@ -398,9 +398,9 @@ fn const_lit_decl_signed_int_lit_cast_neg_fails() { [Qsc.Qasm3.Compile.CannotCast x Cannot cast expression of type Int(None, true) to type Angle(None, true) - ,-[test:1:1] + ,-[test:1:18] 1 | const angle x = -7; - : ^^^^^^^^^^^^^^^^^^^ + : ^ `---- ]"#]], ); diff --git a/compiler/qsc_qasm3/src/semantic/tests/decls/bit.rs b/compiler/qsc_qasm3/src/semantic/tests/decls/bit.rs index dc29049bf5..6a12f8b39e 100644 --- a/compiler/qsc_qasm3/src/semantic/tests/decls/bit.rs +++ b/compiler/qsc_qasm3/src/semantic/tests/decls/bit.rs @@ -11,12 +11,12 @@ fn with_no_init_expr_has_generated_lit_expr() { "bit a;", &expect![[r#" ClassicalDeclarationStmt [0-6]: - symbol_id: 6 + symbol_id: 8 ty_span: [0-3] init_expr: Expr [0-0]: ty: Bit(true) - kind: Lit: Int(0) - [6] Symbol [4-5]: + kind: Lit: Bit(0) + [8] Symbol [4-5]: name: a type: Bit(false) qsharp_type: Result @@ -25,24 +25,21 @@ fn with_no_init_expr_has_generated_lit_expr() { } #[test] -#[ignore = "Unimplemented"] fn array_with_no_init_expr_has_generated_lit_expr() { check_classical_decl( "bit[4] a;", &expect![[r#" - Program: - version: - statements: - - [Qsc.Qasm3.Compile.Unimplemented - - x this statement is not yet handled during OpenQASM 3 import: bit array - | default value - ,-[test:1:1] - 1 | bit[4] a; - : ^^^^^^^^^ - `---- - ]"#]], + ClassicalDeclarationStmt [0-9]: + symbol_id: 8 + ty_span: [0-6] + init_expr: Expr [0-0]: + ty: BitArray(One(4), true) + kind: Lit: Bitstring("0000") + [8] Symbol [7-8]: + name: a + type: BitArray(One(4), false) + qsharp_type: Result[] + io_kind: Default"#]], ); } @@ -52,12 +49,12 @@ fn decl_with_lit_0_init_expr() { "bit a = 0;", &expect![[r#" ClassicalDeclarationStmt [0-10]: - symbol_id: 6 + symbol_id: 8 ty_span: [0-3] init_expr: Expr [8-9]: ty: Bit(true) - kind: Lit: Int(0) - [6] Symbol [4-5]: + kind: Lit: Bit(0) + [8] Symbol [4-5]: name: a type: Bit(false) qsharp_type: Result @@ -71,12 +68,12 @@ fn decl_with_lit_1_init_expr() { "bit a = 1;", &expect![[r#" ClassicalDeclarationStmt [0-10]: - symbol_id: 6 + symbol_id: 8 ty_span: [0-3] init_expr: Expr [8-9]: ty: Bit(true) - kind: Lit: Int(1) - [6] Symbol [4-5]: + kind: Lit: Bit(1) + [8] Symbol [4-5]: name: a type: Bit(false) qsharp_type: Result @@ -90,12 +87,12 @@ fn const_decl_with_lit_0_init_expr() { "const bit a = 0;", &expect![[r#" ClassicalDeclarationStmt [0-16]: - symbol_id: 6 + symbol_id: 8 ty_span: [6-9] init_expr: Expr [14-15]: ty: Bit(true) - kind: Lit: Int(0) - [6] Symbol [10-11]: + kind: Lit: Bit(0) + [8] Symbol [10-11]: name: a type: Bit(true) qsharp_type: Result @@ -109,12 +106,12 @@ fn const_decl_with_lit_1_init_expr() { "const bit a = 1;", &expect![[r#" ClassicalDeclarationStmt [0-16]: - symbol_id: 6 + symbol_id: 8 ty_span: [6-9] init_expr: Expr [14-15]: ty: Bit(true) - kind: Lit: Int(1) - [6] Symbol [10-11]: + kind: Lit: Bit(1) + [8] Symbol [10-11]: name: a type: Bit(true) qsharp_type: Result diff --git a/compiler/qsc_qasm3/src/semantic/tests/decls/bool.rs b/compiler/qsc_qasm3/src/semantic/tests/decls/bool.rs index d6b7dc7ea3..895c0a6942 100644 --- a/compiler/qsc_qasm3/src/semantic/tests/decls/bool.rs +++ b/compiler/qsc_qasm3/src/semantic/tests/decls/bool.rs @@ -11,12 +11,12 @@ fn with_no_init_expr_has_generated_lit_expr() { "bool a;", &expect![[r#" ClassicalDeclarationStmt [0-7]: - symbol_id: 6 + symbol_id: 8 ty_span: [0-4] init_expr: Expr [0-0]: ty: Bool(true) kind: Lit: Bool(false) - [6] Symbol [5-6]: + [8] Symbol [5-6]: name: a type: Bool(false) qsharp_type: bool @@ -52,12 +52,12 @@ fn decl_with_lit_false_init_expr() { "bool a = false;", &expect![[r#" ClassicalDeclarationStmt [0-15]: - symbol_id: 6 + symbol_id: 8 ty_span: [0-4] init_expr: Expr [9-14]: - ty: Bool(true) + ty: Bool(false) kind: Lit: Bool(false) - [6] Symbol [5-6]: + [8] Symbol [5-6]: name: a type: Bool(false) qsharp_type: bool @@ -71,12 +71,12 @@ fn decl_with_lit_true_init_expr() { "bool a = true;", &expect![[r#" ClassicalDeclarationStmt [0-14]: - symbol_id: 6 + symbol_id: 8 ty_span: [0-4] init_expr: Expr [9-13]: - ty: Bool(true) + ty: Bool(false) kind: Lit: Bool(true) - [6] Symbol [5-6]: + [8] Symbol [5-6]: name: a type: Bool(false) qsharp_type: bool @@ -90,12 +90,12 @@ fn const_decl_with_lit_false_init_expr() { "const bool a = false;", &expect![[r#" ClassicalDeclarationStmt [0-21]: - symbol_id: 6 + symbol_id: 8 ty_span: [6-10] init_expr: Expr [15-20]: ty: Bool(true) kind: Lit: Bool(false) - [6] Symbol [11-12]: + [8] Symbol [11-12]: name: a type: Bool(true) qsharp_type: bool @@ -109,12 +109,12 @@ fn const_decl_with_lit_true_init_expr() { "const bool a = true;", &expect![[r#" ClassicalDeclarationStmt [0-20]: - symbol_id: 6 + symbol_id: 8 ty_span: [6-10] init_expr: Expr [15-19]: ty: Bool(true) kind: Lit: Bool(true) - [6] Symbol [11-12]: + [8] Symbol [11-12]: name: a type: Bool(true) qsharp_type: bool diff --git a/compiler/qsc_qasm3/src/semantic/tests/decls/complex.rs b/compiler/qsc_qasm3/src/semantic/tests/decls/complex.rs index ab56fbd88b..6435cfa507 100644 --- a/compiler/qsc_qasm3/src/semantic/tests/decls/complex.rs +++ b/compiler/qsc_qasm3/src/semantic/tests/decls/complex.rs @@ -11,12 +11,12 @@ fn implicit_bitness_default() { "complex[float] x;", &expect![[r#" ClassicalDeclarationStmt [0-17]: - symbol_id: 6 + symbol_id: 8 ty_span: [0-14] init_expr: Expr [0-0]: ty: Complex(None, true) kind: Lit: Complex(0.0, 0.0) - [6] Symbol [15-16]: + [8] Symbol [15-16]: name: x type: Complex(None, false) qsharp_type: Complex @@ -30,12 +30,12 @@ fn explicit_bitness_default() { "complex[float[42]] x;", &expect![[r#" ClassicalDeclarationStmt [0-21]: - symbol_id: 6 + symbol_id: 8 ty_span: [0-18] init_expr: Expr [0-0]: ty: Complex(Some(42), true) kind: Lit: Complex(0.0, 0.0) - [6] Symbol [19-20]: + [8] Symbol [19-20]: name: x type: Complex(Some(42), false) qsharp_type: Complex @@ -48,17 +48,17 @@ fn const_implicit_bitness_double_img_only() { check_classical_decl( "const complex[float] x = 1.01im;", &expect![[r#" - ClassicalDeclarationStmt [0-32]: - symbol_id: 6 - ty_span: [6-20] - init_expr: Expr [25-31]: - ty: Complex(None, true) - kind: Lit: Complex(0.0, 1.01) - [6] Symbol [21-22]: - name: x - type: Complex(None, true) - qsharp_type: Complex - io_kind: Default"#]], + ClassicalDeclarationStmt [0-32]: + symbol_id: 8 + ty_span: [6-20] + init_expr: Expr [25-31]: + ty: Complex(None, true) + kind: Lit: Complex(0.0, 1.01) + [8] Symbol [21-22]: + name: x + type: Complex(None, true) + qsharp_type: Complex + io_kind: Default"#]], ); } @@ -67,17 +67,17 @@ fn const_implicit_bitness_int_img_only() { check_classical_decl( "const complex[float] x = 1im;", &expect![[r#" - ClassicalDeclarationStmt [0-29]: - symbol_id: 6 - ty_span: [6-20] - init_expr: Expr [25-28]: - ty: Complex(None, true) - kind: Lit: Complex(0.0, 1.0) - [6] Symbol [21-22]: - name: x - type: Complex(None, true) - qsharp_type: Complex - io_kind: Default"#]], + ClassicalDeclarationStmt [0-29]: + symbol_id: 8 + ty_span: [6-20] + init_expr: Expr [25-28]: + ty: Complex(None, true) + kind: Lit: Complex(0.0, 1.0) + [8] Symbol [21-22]: + name: x + type: Complex(None, true) + qsharp_type: Complex + io_kind: Default"#]], ); } @@ -87,12 +87,12 @@ fn const_explicit_bitness_double_img_only() { "const complex[float[42]] x = 1.01im;", &expect![[r#" ClassicalDeclarationStmt [0-36]: - symbol_id: 6 + symbol_id: 8 ty_span: [6-24] init_expr: Expr [29-35]: ty: Complex(Some(42), true) kind: Lit: Complex(0.0, 1.01) - [6] Symbol [25-26]: + [8] Symbol [25-26]: name: x type: Complex(Some(42), true) qsharp_type: Complex @@ -106,12 +106,12 @@ fn const_explicit_bitness_int_img_only() { "const complex[float[42]] x = 1im;", &expect![[r#" ClassicalDeclarationStmt [0-33]: - symbol_id: 6 + symbol_id: 8 ty_span: [6-24] init_expr: Expr [29-32]: ty: Complex(Some(42), true) kind: Lit: Complex(0.0, 1.0) - [6] Symbol [25-26]: + [8] Symbol [25-26]: name: x type: Complex(Some(42), true) qsharp_type: Complex @@ -124,17 +124,17 @@ fn implicit_bitness_double_img_only() { check_classical_decl( "complex[float] x = 1.01im;", &expect![[r#" - ClassicalDeclarationStmt [0-26]: - symbol_id: 6 - ty_span: [0-14] - init_expr: Expr [19-25]: - ty: Complex(None, true) - kind: Lit: Complex(0.0, 1.01) - [6] Symbol [15-16]: - name: x - type: Complex(None, false) - qsharp_type: Complex - io_kind: Default"#]], + ClassicalDeclarationStmt [0-26]: + symbol_id: 8 + ty_span: [0-14] + init_expr: Expr [19-25]: + ty: Complex(None, false) + kind: Lit: Complex(0.0, 1.01) + [8] Symbol [15-16]: + name: x + type: Complex(None, false) + qsharp_type: Complex + io_kind: Default"#]], ); } @@ -143,17 +143,17 @@ fn implicit_bitness_int_img_only() { check_classical_decl( "complex[float] x = 1im;", &expect![[r#" - ClassicalDeclarationStmt [0-23]: - symbol_id: 6 - ty_span: [0-14] - init_expr: Expr [19-22]: - ty: Complex(None, true) - kind: Lit: Complex(0.0, 1.0) - [6] Symbol [15-16]: - name: x - type: Complex(None, false) - qsharp_type: Complex - io_kind: Default"#]], + ClassicalDeclarationStmt [0-23]: + symbol_id: 8 + ty_span: [0-14] + init_expr: Expr [19-22]: + ty: Complex(None, false) + kind: Lit: Complex(0.0, 1.0) + [8] Symbol [15-16]: + name: x + type: Complex(None, false) + qsharp_type: Complex + io_kind: Default"#]], ); } @@ -162,17 +162,17 @@ fn const_implicit_bitness_double_real_only() { check_classical_decl( "const complex[float] x = 1.01;", &expect![[r#" - ClassicalDeclarationStmt [0-30]: - symbol_id: 6 - ty_span: [6-20] - init_expr: Expr [25-29]: - ty: Complex(None, true) - kind: Lit: Complex(1.01, 0.0) - [6] Symbol [21-22]: - name: x - type: Complex(None, true) - qsharp_type: Complex - io_kind: Default"#]], + ClassicalDeclarationStmt [0-30]: + symbol_id: 8 + ty_span: [6-20] + init_expr: Expr [25-29]: + ty: Complex(None, true) + kind: Lit: Complex(1.01, 0.0) + [8] Symbol [21-22]: + name: x + type: Complex(None, true) + qsharp_type: Complex + io_kind: Default"#]], ); } @@ -181,17 +181,17 @@ fn const_implicit_bitness_int_real_only() { check_classical_decl( "const complex[float] x = 1;", &expect![[r#" - ClassicalDeclarationStmt [0-27]: - symbol_id: 6 - ty_span: [6-20] - init_expr: Expr [25-26]: - ty: Complex(None, true) - kind: Lit: Complex(1.0, 0.0) - [6] Symbol [21-22]: - name: x - type: Complex(None, true) - qsharp_type: Complex - io_kind: Default"#]], + ClassicalDeclarationStmt [0-27]: + symbol_id: 8 + ty_span: [6-20] + init_expr: Expr [25-26]: + ty: Complex(None, true) + kind: Lit: Complex(1.0, 0.0) + [8] Symbol [21-22]: + name: x + type: Complex(None, true) + qsharp_type: Complex + io_kind: Default"#]], ); } @@ -201,12 +201,12 @@ fn implicit_bitness_double_real_only() { "complex[float] x = 1.01;", &expect![[r#" ClassicalDeclarationStmt [0-24]: - symbol_id: 6 + symbol_id: 8 ty_span: [0-14] init_expr: Expr [19-23]: ty: Complex(None, true) kind: Lit: Complex(1.01, 0.0) - [6] Symbol [15-16]: + [8] Symbol [15-16]: name: x type: Complex(None, false) qsharp_type: Complex @@ -220,12 +220,12 @@ fn implicit_bitness_int_real_only() { "complex[float] x = 1;", &expect![[r#" ClassicalDeclarationStmt [0-21]: - symbol_id: 6 + symbol_id: 8 ty_span: [0-14] init_expr: Expr [19-20]: ty: Complex(None, true) kind: Lit: Complex(1.0, 0.0) - [6] Symbol [15-16]: + [8] Symbol [15-16]: name: x type: Complex(None, false) qsharp_type: Complex diff --git a/compiler/qsc_qasm3/src/semantic/tests/decls/creg.rs b/compiler/qsc_qasm3/src/semantic/tests/decls/creg.rs index 1f7014b74e..7ac120ca04 100644 --- a/compiler/qsc_qasm3/src/semantic/tests/decls/creg.rs +++ b/compiler/qsc_qasm3/src/semantic/tests/decls/creg.rs @@ -11,12 +11,12 @@ fn with_no_init_expr_has_generated_lit_expr() { "creg a;", &expect![[r#" ClassicalDeclarationStmt [0-7]: - symbol_id: 6 + symbol_id: 8 ty_span: [0-7] init_expr: Expr [0-0]: ty: Bit(true) - kind: Lit: Int(0) - [6] Symbol [5-6]: + kind: Lit: Bit(0) + [8] Symbol [5-6]: name: a type: Bit(false) qsharp_type: Result @@ -25,23 +25,20 @@ fn with_no_init_expr_has_generated_lit_expr() { } #[test] -#[ignore = "Unimplemented"] fn array_with_no_init_expr_has_generated_lit_expr() { check_classical_decl( "creg a[4];", &expect![[r#" - Program: - version: - statements: - - [Qsc.Qasm3.Compile.Unimplemented - - x this statement is not yet handled during OpenQASM 3 import: bit array - | default value - ,-[test:1:1] - 1 | creg a[4]; - : ^^^^^^^^^^ - `---- - ]"#]], + ClassicalDeclarationStmt [0-10]: + symbol_id: 8 + ty_span: [0-10] + init_expr: Expr [0-0]: + ty: BitArray(One(4), true) + kind: Lit: Bitstring("0000") + [8] Symbol [5-6]: + name: a + type: BitArray(One(4), false) + qsharp_type: Result[] + io_kind: Default"#]], ); } diff --git a/compiler/qsc_qasm3/src/semantic/tests/decls/duration.rs b/compiler/qsc_qasm3/src/semantic/tests/decls/duration.rs index 870ea73d21..9f8a20da98 100644 --- a/compiler/qsc_qasm3/src/semantic/tests/decls/duration.rs +++ b/compiler/qsc_qasm3/src/semantic/tests/decls/duration.rs @@ -16,7 +16,7 @@ fn with_no_init_expr_has_generated_lit_expr() { Stmt [0-11]: annotations: kind: ClassicalDeclarationStmt [0-11]: - symbol_id: 6 + symbol_id: 8 ty_span: [0-8] init_expr: Expr [0-0]: ty: Duration(true) diff --git a/compiler/qsc_qasm3/src/semantic/tests/decls/float.rs b/compiler/qsc_qasm3/src/semantic/tests/decls/float.rs index 9af5a1933e..5c372ba5b6 100644 --- a/compiler/qsc_qasm3/src/semantic/tests/decls/float.rs +++ b/compiler/qsc_qasm3/src/semantic/tests/decls/float.rs @@ -11,12 +11,12 @@ fn implicit_bitness_default() { "float x;", &expect![[r#" ClassicalDeclarationStmt [0-8]: - symbol_id: 6 + symbol_id: 8 ty_span: [0-5] init_expr: Expr [0-0]: ty: Float(None, true) kind: Lit: Float(0.0) - [6] Symbol [6-7]: + [8] Symbol [6-7]: name: x type: Float(None, false) qsharp_type: Double @@ -29,17 +29,17 @@ fn lit() { check_classical_decl( "float x = 42.1;", &expect![[r#" - ClassicalDeclarationStmt [0-15]: - symbol_id: 6 - ty_span: [0-5] - init_expr: Expr [10-14]: - ty: Float(None, true) - kind: Lit: Float(42.1) - [6] Symbol [6-7]: - name: x - type: Float(None, false) - qsharp_type: Double - io_kind: Default"#]], + ClassicalDeclarationStmt [0-15]: + symbol_id: 8 + ty_span: [0-5] + init_expr: Expr [10-14]: + ty: Float(None, false) + kind: Lit: Float(42.1) + [8] Symbol [6-7]: + name: x + type: Float(None, false) + qsharp_type: Double + io_kind: Default"#]], ); } @@ -48,17 +48,17 @@ fn const_lit() { check_classical_decl( "const float x = 42.1;", &expect![[r#" - ClassicalDeclarationStmt [0-21]: - symbol_id: 6 - ty_span: [6-11] - init_expr: Expr [16-20]: - ty: Float(None, true) - kind: Lit: Float(42.1) - [6] Symbol [12-13]: - name: x - type: Float(None, true) - qsharp_type: Double - io_kind: Default"#]], + ClassicalDeclarationStmt [0-21]: + symbol_id: 8 + ty_span: [6-11] + init_expr: Expr [16-20]: + ty: Float(None, true) + kind: Lit: Float(42.1) + [8] Symbol [12-13]: + name: x + type: Float(None, true) + qsharp_type: Double + io_kind: Default"#]], ); } @@ -68,12 +68,12 @@ fn lit_explicit_width() { "float[64] x = 42.1;", &expect![[r#" ClassicalDeclarationStmt [0-19]: - symbol_id: 6 + symbol_id: 8 ty_span: [0-9] init_expr: Expr [14-18]: ty: Float(Some(64), true) kind: Lit: Float(42.1) - [6] Symbol [10-11]: + [8] Symbol [10-11]: name: x type: Float(Some(64), false) qsharp_type: Double @@ -87,12 +87,12 @@ fn const_explicit_width_lit() { "const float[64] x = 42.1;", &expect![[r#" ClassicalDeclarationStmt [0-25]: - symbol_id: 6 + symbol_id: 8 ty_span: [6-15] init_expr: Expr [20-24]: ty: Float(Some(64), true) kind: Lit: Float(42.1) - [6] Symbol [16-17]: + [8] Symbol [16-17]: name: x type: Float(Some(64), true) qsharp_type: Double @@ -105,17 +105,17 @@ fn lit_decl_leading_dot() { check_classical_decl( "float x = .421;", &expect![[r#" - ClassicalDeclarationStmt [0-15]: - symbol_id: 6 - ty_span: [0-5] - init_expr: Expr [10-14]: - ty: Float(None, true) - kind: Lit: Float(0.421) - [6] Symbol [6-7]: - name: x - type: Float(None, false) - qsharp_type: Double - io_kind: Default"#]], + ClassicalDeclarationStmt [0-15]: + symbol_id: 8 + ty_span: [0-5] + init_expr: Expr [10-14]: + ty: Float(None, false) + kind: Lit: Float(0.421) + [8] Symbol [6-7]: + name: x + type: Float(None, false) + qsharp_type: Double + io_kind: Default"#]], ); } @@ -124,17 +124,17 @@ fn const_lit_decl_leading_dot() { check_classical_decl( "const float x = .421;", &expect![[r#" - ClassicalDeclarationStmt [0-21]: - symbol_id: 6 - ty_span: [6-11] - init_expr: Expr [16-20]: - ty: Float(None, true) - kind: Lit: Float(0.421) - [6] Symbol [12-13]: - name: x - type: Float(None, true) - qsharp_type: Double - io_kind: Default"#]], + ClassicalDeclarationStmt [0-21]: + symbol_id: 8 + ty_span: [6-11] + init_expr: Expr [16-20]: + ty: Float(None, true) + kind: Lit: Float(0.421) + [8] Symbol [12-13]: + name: x + type: Float(None, true) + qsharp_type: Double + io_kind: Default"#]], ); } @@ -143,17 +143,17 @@ fn const_lit_decl_leading_dot_scientific() { check_classical_decl( "const float x = .421e2;", &expect![[r#" - ClassicalDeclarationStmt [0-23]: - symbol_id: 6 - ty_span: [6-11] - init_expr: Expr [16-22]: - ty: Float(None, true) - kind: Lit: Float(42.1) - [6] Symbol [12-13]: - name: x - type: Float(None, true) - qsharp_type: Double - io_kind: Default"#]], + ClassicalDeclarationStmt [0-23]: + symbol_id: 8 + ty_span: [6-11] + init_expr: Expr [16-22]: + ty: Float(None, true) + kind: Lit: Float(42.1) + [8] Symbol [12-13]: + name: x + type: Float(None, true) + qsharp_type: Double + io_kind: Default"#]], ); } @@ -162,17 +162,17 @@ fn lit_decl_trailing_dot() { check_classical_decl( "float x = 421.;", &expect![[r#" - ClassicalDeclarationStmt [0-15]: - symbol_id: 6 - ty_span: [0-5] - init_expr: Expr [10-14]: - ty: Float(None, true) - kind: Lit: Float(421.0) - [6] Symbol [6-7]: - name: x - type: Float(None, false) - qsharp_type: Double - io_kind: Default"#]], + ClassicalDeclarationStmt [0-15]: + symbol_id: 8 + ty_span: [0-5] + init_expr: Expr [10-14]: + ty: Float(None, false) + kind: Lit: Float(421.0) + [8] Symbol [6-7]: + name: x + type: Float(None, false) + qsharp_type: Double + io_kind: Default"#]], ); } @@ -181,17 +181,17 @@ fn const_lit_decl_trailing_dot() { check_classical_decl( "const float x = 421.;", &expect![[r#" - ClassicalDeclarationStmt [0-21]: - symbol_id: 6 - ty_span: [6-11] - init_expr: Expr [16-20]: - ty: Float(None, true) - kind: Lit: Float(421.0) - [6] Symbol [12-13]: - name: x - type: Float(None, true) - qsharp_type: Double - io_kind: Default"#]], + ClassicalDeclarationStmt [0-21]: + symbol_id: 8 + ty_span: [6-11] + init_expr: Expr [16-20]: + ty: Float(None, true) + kind: Lit: Float(421.0) + [8] Symbol [12-13]: + name: x + type: Float(None, true) + qsharp_type: Double + io_kind: Default"#]], ); } @@ -200,17 +200,17 @@ fn lit_decl_scientific() { check_classical_decl( "float x = 4.21e1;", &expect![[r#" - ClassicalDeclarationStmt [0-17]: - symbol_id: 6 - ty_span: [0-5] - init_expr: Expr [10-16]: - ty: Float(None, true) - kind: Lit: Float(42.1) - [6] Symbol [6-7]: - name: x - type: Float(None, false) - qsharp_type: Double - io_kind: Default"#]], + ClassicalDeclarationStmt [0-17]: + symbol_id: 8 + ty_span: [0-5] + init_expr: Expr [10-16]: + ty: Float(None, false) + kind: Lit: Float(42.1) + [8] Symbol [6-7]: + name: x + type: Float(None, false) + qsharp_type: Double + io_kind: Default"#]], ); } @@ -219,17 +219,17 @@ fn const_lit_decl_scientific() { check_classical_decl( "const float x = 4.21e1;", &expect![[r#" - ClassicalDeclarationStmt [0-23]: - symbol_id: 6 - ty_span: [6-11] - init_expr: Expr [16-22]: - ty: Float(None, true) - kind: Lit: Float(42.1) - [6] Symbol [12-13]: - name: x - type: Float(None, true) - qsharp_type: Double - io_kind: Default"#]], + ClassicalDeclarationStmt [0-23]: + symbol_id: 8 + ty_span: [6-11] + init_expr: Expr [16-22]: + ty: Float(None, true) + kind: Lit: Float(42.1) + [8] Symbol [12-13]: + name: x + type: Float(None, true) + qsharp_type: Double + io_kind: Default"#]], ); } @@ -238,17 +238,17 @@ fn lit_decl_scientific_signed_pos() { check_classical_decl( "float x = 4.21e+1;", &expect![[r#" - ClassicalDeclarationStmt [0-18]: - symbol_id: 6 - ty_span: [0-5] - init_expr: Expr [10-17]: - ty: Float(None, true) - kind: Lit: Float(42.1) - [6] Symbol [6-7]: - name: x - type: Float(None, false) - qsharp_type: Double - io_kind: Default"#]], + ClassicalDeclarationStmt [0-18]: + symbol_id: 8 + ty_span: [0-5] + init_expr: Expr [10-17]: + ty: Float(None, false) + kind: Lit: Float(42.1) + [8] Symbol [6-7]: + name: x + type: Float(None, false) + qsharp_type: Double + io_kind: Default"#]], ); } @@ -257,17 +257,17 @@ fn const_lit_decl_scientific_signed_pos() { check_classical_decl( "const float x = 4.21e+1;", &expect![[r#" - ClassicalDeclarationStmt [0-24]: - symbol_id: 6 - ty_span: [6-11] - init_expr: Expr [16-23]: - ty: Float(None, true) - kind: Lit: Float(42.1) - [6] Symbol [12-13]: - name: x - type: Float(None, true) - qsharp_type: Double - io_kind: Default"#]], + ClassicalDeclarationStmt [0-24]: + symbol_id: 8 + ty_span: [6-11] + init_expr: Expr [16-23]: + ty: Float(None, true) + kind: Lit: Float(42.1) + [8] Symbol [12-13]: + name: x + type: Float(None, true) + qsharp_type: Double + io_kind: Default"#]], ); } @@ -276,17 +276,17 @@ fn lit_decl_scientific_cap_e() { check_classical_decl( "float x = 4.21E1;", &expect![[r#" - ClassicalDeclarationStmt [0-17]: - symbol_id: 6 - ty_span: [0-5] - init_expr: Expr [10-16]: - ty: Float(None, true) - kind: Lit: Float(42.1) - [6] Symbol [6-7]: - name: x - type: Float(None, false) - qsharp_type: Double - io_kind: Default"#]], + ClassicalDeclarationStmt [0-17]: + symbol_id: 8 + ty_span: [0-5] + init_expr: Expr [10-16]: + ty: Float(None, false) + kind: Lit: Float(42.1) + [8] Symbol [6-7]: + name: x + type: Float(None, false) + qsharp_type: Double + io_kind: Default"#]], ); } @@ -295,17 +295,17 @@ fn const_lit_decl_scientific_cap_e() { check_classical_decl( "const float x = 4.21E1;", &expect![[r#" - ClassicalDeclarationStmt [0-23]: - symbol_id: 6 - ty_span: [6-11] - init_expr: Expr [16-22]: - ty: Float(None, true) - kind: Lit: Float(42.1) - [6] Symbol [12-13]: - name: x - type: Float(None, true) - qsharp_type: Double - io_kind: Default"#]], + ClassicalDeclarationStmt [0-23]: + symbol_id: 8 + ty_span: [6-11] + init_expr: Expr [16-22]: + ty: Float(None, true) + kind: Lit: Float(42.1) + [8] Symbol [12-13]: + name: x + type: Float(None, true) + qsharp_type: Double + io_kind: Default"#]], ); } @@ -314,17 +314,17 @@ fn lit_decl_scientific_signed_neg() { check_classical_decl( "float x = 421.0e-1;", &expect![[r#" - ClassicalDeclarationStmt [0-19]: - symbol_id: 6 - ty_span: [0-5] - init_expr: Expr [10-18]: - ty: Float(None, true) - kind: Lit: Float(42.1) - [6] Symbol [6-7]: - name: x - type: Float(None, false) - qsharp_type: Double - io_kind: Default"#]], + ClassicalDeclarationStmt [0-19]: + symbol_id: 8 + ty_span: [0-5] + init_expr: Expr [10-18]: + ty: Float(None, false) + kind: Lit: Float(42.1) + [8] Symbol [6-7]: + name: x + type: Float(None, false) + qsharp_type: Double + io_kind: Default"#]], ); } @@ -333,17 +333,17 @@ fn const_lit_decl_scientific_signed_neg() { check_classical_decl( "const float x = 421.0e-1;", &expect![[r#" - ClassicalDeclarationStmt [0-25]: - symbol_id: 6 - ty_span: [6-11] - init_expr: Expr [16-24]: - ty: Float(None, true) - kind: Lit: Float(42.1) - [6] Symbol [12-13]: - name: x - type: Float(None, true) - qsharp_type: Double - io_kind: Default"#]], + ClassicalDeclarationStmt [0-25]: + symbol_id: 8 + ty_span: [6-11] + init_expr: Expr [16-24]: + ty: Float(None, true) + kind: Lit: Float(42.1) + [8] Symbol [12-13]: + name: x + type: Float(None, true) + qsharp_type: Double + io_kind: Default"#]], ); } @@ -353,7 +353,7 @@ fn const_lit_decl_signed_float_lit_cast_neg() { "const float x = -7.;", &expect![[r#" ClassicalDeclarationStmt [0-20]: - symbol_id: 6 + symbol_id: 8 ty_span: [6-11] init_expr: Expr [17-19]: ty: Float(None, true) @@ -362,7 +362,7 @@ fn const_lit_decl_signed_float_lit_cast_neg() { expr: Expr [17-19]: ty: Float(None, true) kind: Lit: Float(7.0) - [6] Symbol [12-13]: + [8] Symbol [12-13]: name: x type: Float(None, true) qsharp_type: Double @@ -376,7 +376,7 @@ fn const_lit_decl_signed_int_lit_cast_neg() { "const float x = -7;", &expect![[r#" ClassicalDeclarationStmt [0-19]: - symbol_id: 6 + symbol_id: 8 ty_span: [6-11] init_expr: Expr [17-18]: ty: Float(None, true) @@ -389,7 +389,7 @@ fn const_lit_decl_signed_int_lit_cast_neg() { expr: Expr [17-18]: ty: Int(None, true) kind: Lit: Int(7) - [6] Symbol [12-13]: + [8] Symbol [12-13]: name: x type: Float(None, true) qsharp_type: Double @@ -404,12 +404,12 @@ fn init_float_with_int_value_equal_max_safely_representable_values() { &format!("float a = {max_exact_int};"), &expect![[r#" ClassicalDeclarationStmt [0-27]: - symbol_id: 6 + symbol_id: 8 ty_span: [0-5] init_expr: Expr [10-26]: ty: Float(None, true) kind: Lit: Float(9007199254740992.0) - [6] Symbol [6-7]: + [8] Symbol [6-7]: name: a type: Float(None, false) qsharp_type: Double @@ -430,7 +430,7 @@ fn init_float_with_int_value_greater_than_safely_representable_values() { Stmt [0-27]: annotations: kind: ClassicalDeclarationStmt [0-27]: - symbol_id: 6 + symbol_id: 8 ty_span: [0-5] init_expr: Expr [10-26]: ty: Int(None, true) @@ -463,7 +463,7 @@ fn init_float_with_int_value_equal_min_safely_representable_values() { &format!("float a = {min_exact_int};"), &expect![[r#" ClassicalDeclarationStmt [0-28]: - symbol_id: 6 + symbol_id: 8 ty_span: [0-5] init_expr: Expr [11-27]: ty: Float(None, false) @@ -476,7 +476,7 @@ fn init_float_with_int_value_equal_min_safely_representable_values() { expr: Expr [11-27]: ty: Int(None, true) kind: Lit: Int(9007199254740992) - [6] Symbol [6-7]: + [8] Symbol [6-7]: name: a type: Float(None, false) qsharp_type: Double diff --git a/compiler/qsc_qasm3/src/semantic/tests/decls/int.rs b/compiler/qsc_qasm3/src/semantic/tests/decls/int.rs index 2e7d7ed18d..6beda22ff3 100644 --- a/compiler/qsc_qasm3/src/semantic/tests/decls/int.rs +++ b/compiler/qsc_qasm3/src/semantic/tests/decls/int.rs @@ -11,16 +11,16 @@ fn implicit_bitness_int_negative() { "int x = -42;", &expect![[r#" ClassicalDeclarationStmt [0-12]: - symbol_id: 6 + symbol_id: 8 ty_span: [0-3] init_expr: Expr [9-11]: - ty: Int(None, true) + ty: Int(None, false) kind: UnaryOpExpr [9-11]: op: Neg expr: Expr [9-11]: ty: Int(None, true) kind: Lit: Int(42) - [6] Symbol [4-5]: + [8] Symbol [4-5]: name: x type: Int(None, false) qsharp_type: Int @@ -34,7 +34,7 @@ fn implicit_bitness_int_const_negative() { "const int x = -42;", &expect![[r#" ClassicalDeclarationStmt [0-18]: - symbol_id: 6 + symbol_id: 8 ty_span: [6-9] init_expr: Expr [15-17]: ty: Int(None, true) @@ -43,7 +43,7 @@ fn implicit_bitness_int_const_negative() { expr: Expr [15-17]: ty: Int(None, true) kind: Lit: Int(42) - [6] Symbol [10-11]: + [8] Symbol [10-11]: name: x type: Int(None, true) qsharp_type: Int @@ -57,12 +57,12 @@ fn implicit_bitness_int_default() { "int x;", &expect![[r#" ClassicalDeclarationStmt [0-6]: - symbol_id: 6 + symbol_id: 8 ty_span: [0-3] init_expr: Expr [0-0]: ty: Int(None, true) kind: Lit: Int(0) - [6] Symbol [4-5]: + [8] Symbol [4-5]: name: x type: Int(None, false) qsharp_type: Int @@ -76,12 +76,12 @@ fn const_implicit_bitness_int_lit() { "const int x = 42;", &expect![[r#" ClassicalDeclarationStmt [0-17]: - symbol_id: 6 + symbol_id: 8 ty_span: [6-9] init_expr: Expr [14-16]: ty: Int(None, true) kind: Lit: Int(42) - [6] Symbol [10-11]: + [8] Symbol [10-11]: name: x type: Int(None, true) qsharp_type: Int @@ -95,12 +95,12 @@ fn implicit_bitness_int_hex_cap() { "int x = 0XFa_1F;", &expect![[r#" ClassicalDeclarationStmt [0-16]: - symbol_id: 6 + symbol_id: 8 ty_span: [0-3] init_expr: Expr [8-15]: - ty: Int(None, true) + ty: Int(None, false) kind: Lit: Int(64031) - [6] Symbol [4-5]: + [8] Symbol [4-5]: name: x type: Int(None, false) qsharp_type: Int @@ -114,12 +114,12 @@ fn const_implicit_bitness_int_hex_cap() { "const int y = 0XFa_1F;", &expect![[r#" ClassicalDeclarationStmt [0-22]: - symbol_id: 6 + symbol_id: 8 ty_span: [6-9] init_expr: Expr [14-21]: ty: Int(None, true) kind: Lit: Int(64031) - [6] Symbol [10-11]: + [8] Symbol [10-11]: name: y type: Int(None, true) qsharp_type: Int @@ -133,12 +133,12 @@ fn implicit_bitness_int_octal() { "int x = 0o42;", &expect![[r#" ClassicalDeclarationStmt [0-13]: - symbol_id: 6 + symbol_id: 8 ty_span: [0-3] init_expr: Expr [8-12]: - ty: Int(None, true) + ty: Int(None, false) kind: Lit: Int(34) - [6] Symbol [4-5]: + [8] Symbol [4-5]: name: x type: Int(None, false) qsharp_type: Int @@ -152,12 +152,12 @@ fn const_implicit_bitness_int_octal() { "const int x = 0o42;", &expect![[r#" ClassicalDeclarationStmt [0-19]: - symbol_id: 6 + symbol_id: 8 ty_span: [6-9] init_expr: Expr [14-18]: ty: Int(None, true) kind: Lit: Int(34) - [6] Symbol [10-11]: + [8] Symbol [10-11]: name: x type: Int(None, true) qsharp_type: Int @@ -171,12 +171,12 @@ fn const_implicit_bitness_int_octal_cap() { "const int x = 0O42;", &expect![[r#" ClassicalDeclarationStmt [0-19]: - symbol_id: 6 + symbol_id: 8 ty_span: [6-9] init_expr: Expr [14-18]: ty: Int(None, true) kind: Lit: Int(34) - [6] Symbol [10-11]: + [8] Symbol [10-11]: name: x type: Int(None, true) qsharp_type: Int @@ -190,12 +190,12 @@ fn implicit_bitness_int_binary_low() { "int x = 0b1001_1001;", &expect![[r#" ClassicalDeclarationStmt [0-20]: - symbol_id: 6 + symbol_id: 8 ty_span: [0-3] init_expr: Expr [8-19]: - ty: Int(None, true) + ty: Int(None, false) kind: Lit: Int(153) - [6] Symbol [4-5]: + [8] Symbol [4-5]: name: x type: Int(None, false) qsharp_type: Int @@ -209,12 +209,12 @@ fn implicit_bitness_int_binary_cap() { "int x = 0B1010;", &expect![[r#" ClassicalDeclarationStmt [0-15]: - symbol_id: 6 + symbol_id: 8 ty_span: [0-3] init_expr: Expr [8-14]: - ty: Int(None, true) + ty: Int(None, false) kind: Lit: Int(10) - [6] Symbol [4-5]: + [8] Symbol [4-5]: name: x type: Int(None, false) qsharp_type: Int @@ -228,12 +228,12 @@ fn const_implicit_bitness_int_binary_low() { "const int x = 0b1001_1001;", &expect![[r#" ClassicalDeclarationStmt [0-26]: - symbol_id: 6 + symbol_id: 8 ty_span: [6-9] init_expr: Expr [14-25]: ty: Int(None, true) kind: Lit: Int(153) - [6] Symbol [10-11]: + [8] Symbol [10-11]: name: x type: Int(None, true) qsharp_type: Int @@ -247,12 +247,12 @@ fn const_implicit_bitness_int_binary_cap() { "const int x = 0B1010;", &expect![[r#" ClassicalDeclarationStmt [0-21]: - symbol_id: 6 + symbol_id: 8 ty_span: [6-9] init_expr: Expr [14-20]: ty: Int(None, true) kind: Lit: Int(10) - [6] Symbol [10-11]: + [8] Symbol [10-11]: name: x type: Int(None, true) qsharp_type: Int @@ -266,12 +266,12 @@ fn implicit_bitness_int_formatted() { "int x = 2_0_00;", &expect![[r#" ClassicalDeclarationStmt [0-15]: - symbol_id: 6 + symbol_id: 8 ty_span: [0-3] init_expr: Expr [8-14]: - ty: Int(None, true) + ty: Int(None, false) kind: Lit: Int(2000) - [6] Symbol [4-5]: + [8] Symbol [4-5]: name: x type: Int(None, false) qsharp_type: Int @@ -285,12 +285,12 @@ fn const_implicit_bitness_int_formatted() { "const int x = 2_0_00;", &expect![[r#" ClassicalDeclarationStmt [0-21]: - symbol_id: 6 + symbol_id: 8 ty_span: [6-9] init_expr: Expr [14-20]: ty: Int(None, true) kind: Lit: Int(2000) - [6] Symbol [10-11]: + [8] Symbol [10-11]: name: x type: Int(None, true) qsharp_type: Int @@ -304,12 +304,12 @@ fn explicit_bitness_int_default() { "int[10] x;", &expect![[r#" ClassicalDeclarationStmt [0-10]: - symbol_id: 6 + symbol_id: 8 ty_span: [0-7] init_expr: Expr [0-0]: ty: Int(Some(10), true) kind: Lit: Int(0) - [6] Symbol [8-9]: + [8] Symbol [8-9]: name: x type: Int(Some(10), false) qsharp_type: Int @@ -323,12 +323,12 @@ fn explicit_bitness_int() { "int[10] x = 42;", &expect![[r#" ClassicalDeclarationStmt [0-15]: - symbol_id: 6 + symbol_id: 8 ty_span: [0-7] init_expr: Expr [12-14]: ty: Int(Some(10), true) kind: Lit: Int(42) - [6] Symbol [8-9]: + [8] Symbol [8-9]: name: x type: Int(Some(10), false) qsharp_type: Int @@ -342,12 +342,12 @@ fn const_explicit_bitness_int() { "const int[10] x = 42;", &expect![[r#" ClassicalDeclarationStmt [0-21]: - symbol_id: 6 + symbol_id: 8 ty_span: [6-13] init_expr: Expr [18-20]: ty: Int(Some(10), true) kind: Lit: Int(42) - [6] Symbol [14-15]: + [8] Symbol [14-15]: name: x type: Int(Some(10), true) qsharp_type: Int @@ -361,7 +361,7 @@ fn implicit_bitness_int_negative_float_decl_is_runtime_conversion() { "int x = -42.;", &expect![[r#" ClassicalDeclarationStmt [0-13]: - symbol_id: 6 + symbol_id: 8 ty_span: [0-3] init_expr: Expr [9-12]: ty: Int(None, false) @@ -374,7 +374,7 @@ fn implicit_bitness_int_negative_float_decl_is_runtime_conversion() { expr: Expr [9-12]: ty: Float(None, true) kind: Lit: Float(42.0) - [6] Symbol [4-5]: + [8] Symbol [4-5]: name: x type: Int(None, false) qsharp_type: Int diff --git a/compiler/qsc_qasm3/src/semantic/tests/decls/qreg.rs b/compiler/qsc_qasm3/src/semantic/tests/decls/qreg.rs index 37fb09e2a4..349b3bc768 100644 --- a/compiler/qsc_qasm3/src/semantic/tests/decls/qreg.rs +++ b/compiler/qsc_qasm3/src/semantic/tests/decls/qreg.rs @@ -6,23 +6,23 @@ use expect_test::expect; use crate::semantic::tests::check_stmt_kind; #[test] -#[ignore = "unimplemented"] -fn with_no_init_expr_has_generated_lit_expr() { +fn with_no_init_expr() { check_stmt_kind( "qreg a;", &expect![[r#" - Program: - version: - statements: - - [Qsc.Qasm3.Compile.Unimplemented + QubitDeclaration [0-7]: + symbol_id: 8"#]], + ); +} - x this statement is not yet handled during OpenQASM 3 import: qubit decl - | stmt - ,-[test:1:1] - 1 | qreg a; - : ^^^^^^^ - `---- - ]"#]], +#[test] +fn array_with_no_init_expr() { + check_stmt_kind( + "qreg a[3];", + &expect![[r#" + QubitArrayDeclaration [0-10]: + symbol_id: 8 + size: 3 + size_span: [7-8]"#]], ); } diff --git a/compiler/qsc_qasm3/src/semantic/tests/decls/stretch.rs b/compiler/qsc_qasm3/src/semantic/tests/decls/stretch.rs index 8c9848f665..56f4f0dd61 100644 --- a/compiler/qsc_qasm3/src/semantic/tests/decls/stretch.rs +++ b/compiler/qsc_qasm3/src/semantic/tests/decls/stretch.rs @@ -16,7 +16,7 @@ fn with_no_init_expr_has_generated_lit_expr() { Stmt [0-10]: annotations: kind: ClassicalDeclarationStmt [0-10]: - symbol_id: 6 + symbol_id: 8 ty_span: [0-7] init_expr: Expr [0-0]: ty: Stretch(true) diff --git a/compiler/qsc_qasm3/src/semantic/tests/decls/uint.rs b/compiler/qsc_qasm3/src/semantic/tests/decls/uint.rs index f53b975b87..29eaf3c1ae 100644 --- a/compiler/qsc_qasm3/src/semantic/tests/decls/uint.rs +++ b/compiler/qsc_qasm3/src/semantic/tests/decls/uint.rs @@ -11,12 +11,12 @@ fn implicit_bitness_int_default() { "uint x;", &expect![[r#" ClassicalDeclarationStmt [0-7]: - symbol_id: 6 + symbol_id: 8 ty_span: [0-4] init_expr: Expr [0-0]: ty: UInt(None, true) kind: Lit: Int(0) - [6] Symbol [5-6]: + [8] Symbol [5-6]: name: x type: UInt(None, false) qsharp_type: Int @@ -30,12 +30,12 @@ fn const_implicit_bitness_int_lit() { "const uint x = 42;", &expect![[r#" ClassicalDeclarationStmt [0-18]: - symbol_id: 6 + symbol_id: 8 ty_span: [6-10] init_expr: Expr [15-17]: ty: UInt(None, true) kind: Lit: Int(42) - [6] Symbol [11-12]: + [8] Symbol [11-12]: name: x type: UInt(None, true) qsharp_type: Int @@ -49,12 +49,12 @@ fn implicit_bitness_int_hex_cap() { "uint x = 0XFa_1F;", &expect![[r#" ClassicalDeclarationStmt [0-17]: - symbol_id: 6 + symbol_id: 8 ty_span: [0-4] init_expr: Expr [9-16]: ty: UInt(None, true) kind: Lit: Int(64031) - [6] Symbol [5-6]: + [8] Symbol [5-6]: name: x type: UInt(None, false) qsharp_type: Int @@ -68,12 +68,12 @@ fn const_implicit_bitness_int_hex_low() { "const uint x = 0xFa_1F;", &expect![[r#" ClassicalDeclarationStmt [0-23]: - symbol_id: 6 + symbol_id: 8 ty_span: [6-10] init_expr: Expr [15-22]: ty: UInt(None, true) kind: Lit: Int(64031) - [6] Symbol [11-12]: + [8] Symbol [11-12]: name: x type: UInt(None, true) qsharp_type: Int @@ -87,12 +87,12 @@ fn const_implicit_bitness_int_hex_cap() { "const uint y = 0XFa_1F;", &expect![[r#" ClassicalDeclarationStmt [0-23]: - symbol_id: 6 + symbol_id: 8 ty_span: [6-10] init_expr: Expr [15-22]: ty: UInt(None, true) kind: Lit: Int(64031) - [6] Symbol [11-12]: + [8] Symbol [11-12]: name: y type: UInt(None, true) qsharp_type: Int @@ -106,12 +106,12 @@ fn implicit_bitness_int_octal_low() { "uint x = 0o42;", &expect![[r#" ClassicalDeclarationStmt [0-14]: - symbol_id: 6 + symbol_id: 8 ty_span: [0-4] init_expr: Expr [9-13]: ty: UInt(None, true) kind: Lit: Int(34) - [6] Symbol [5-6]: + [8] Symbol [5-6]: name: x type: UInt(None, false) qsharp_type: Int @@ -125,12 +125,12 @@ fn implicit_bitness_int_octal_cap() { "uint x = 0O42;", &expect![[r#" ClassicalDeclarationStmt [0-14]: - symbol_id: 6 + symbol_id: 8 ty_span: [0-4] init_expr: Expr [9-13]: ty: UInt(None, true) kind: Lit: Int(34) - [6] Symbol [5-6]: + [8] Symbol [5-6]: name: x type: UInt(None, false) qsharp_type: Int @@ -144,12 +144,12 @@ fn const_implicit_bitness_int_octal_low() { "const uint x = 0o42;", &expect![[r#" ClassicalDeclarationStmt [0-20]: - symbol_id: 6 + symbol_id: 8 ty_span: [6-10] init_expr: Expr [15-19]: ty: UInt(None, true) kind: Lit: Int(34) - [6] Symbol [11-12]: + [8] Symbol [11-12]: name: x type: UInt(None, true) qsharp_type: Int @@ -163,12 +163,12 @@ fn const_implicit_bitness_int_octal_cap() { "const uint x = 0O42;", &expect![[r#" ClassicalDeclarationStmt [0-20]: - symbol_id: 6 + symbol_id: 8 ty_span: [6-10] init_expr: Expr [15-19]: ty: UInt(None, true) kind: Lit: Int(34) - [6] Symbol [11-12]: + [8] Symbol [11-12]: name: x type: UInt(None, true) qsharp_type: Int @@ -182,12 +182,12 @@ fn implicit_bitness_int_binary_low() { "uint x = 0b1001_1001;", &expect![[r#" ClassicalDeclarationStmt [0-21]: - symbol_id: 6 + symbol_id: 8 ty_span: [0-4] init_expr: Expr [9-20]: ty: UInt(None, true) kind: Lit: Int(153) - [6] Symbol [5-6]: + [8] Symbol [5-6]: name: x type: UInt(None, false) qsharp_type: Int @@ -201,12 +201,12 @@ fn implicit_bitness_int_binary_cap() { "uint x = 0B1010;", &expect![[r#" ClassicalDeclarationStmt [0-16]: - symbol_id: 6 + symbol_id: 8 ty_span: [0-4] init_expr: Expr [9-15]: ty: UInt(None, true) kind: Lit: Int(10) - [6] Symbol [5-6]: + [8] Symbol [5-6]: name: x type: UInt(None, false) qsharp_type: Int @@ -220,12 +220,12 @@ fn const_implicit_bitness_int_binary_low() { "const uint x = 0b1001_1001;", &expect![[r#" ClassicalDeclarationStmt [0-27]: - symbol_id: 6 + symbol_id: 8 ty_span: [6-10] init_expr: Expr [15-26]: ty: UInt(None, true) kind: Lit: Int(153) - [6] Symbol [11-12]: + [8] Symbol [11-12]: name: x type: UInt(None, true) qsharp_type: Int @@ -239,12 +239,12 @@ fn const_implicit_bitness_int_binary_cap() { "const uint x = 0B1010;", &expect![[r#" ClassicalDeclarationStmt [0-22]: - symbol_id: 6 + symbol_id: 8 ty_span: [6-10] init_expr: Expr [15-21]: ty: UInt(None, true) kind: Lit: Int(10) - [6] Symbol [11-12]: + [8] Symbol [11-12]: name: x type: UInt(None, true) qsharp_type: Int @@ -258,12 +258,12 @@ fn implicit_bitness_int_formatted() { "uint x = 2_0_00;", &expect![[r#" ClassicalDeclarationStmt [0-16]: - symbol_id: 6 + symbol_id: 8 ty_span: [0-4] init_expr: Expr [9-15]: ty: UInt(None, true) kind: Lit: Int(2000) - [6] Symbol [5-6]: + [8] Symbol [5-6]: name: x type: UInt(None, false) qsharp_type: Int @@ -277,12 +277,12 @@ fn const_implicit_bitness_int_formatted() { "const uint x = 2_0_00;", &expect![[r#" ClassicalDeclarationStmt [0-22]: - symbol_id: 6 + symbol_id: 8 ty_span: [6-10] init_expr: Expr [15-21]: ty: UInt(None, true) kind: Lit: Int(2000) - [6] Symbol [11-12]: + [8] Symbol [11-12]: name: x type: UInt(None, true) qsharp_type: Int @@ -296,12 +296,12 @@ fn const_explicit_bitness_int() { "uint[10] x;", &expect![[r#" ClassicalDeclarationStmt [0-11]: - symbol_id: 6 + symbol_id: 8 ty_span: [0-8] init_expr: Expr [0-0]: ty: UInt(Some(10), true) kind: Lit: Int(0) - [6] Symbol [9-10]: + [8] Symbol [9-10]: name: x type: UInt(Some(10), false) qsharp_type: Int @@ -315,7 +315,7 @@ fn assigning_uint_to_negative_lit() { "const uint[10] x = -42;", &expect![[r#" ClassicalDeclarationStmt [0-23]: - symbol_id: 6 + symbol_id: 8 ty_span: [6-14] init_expr: Expr [20-22]: ty: UInt(Some(10), true) @@ -328,7 +328,7 @@ fn assigning_uint_to_negative_lit() { expr: Expr [20-22]: ty: Int(None, true) kind: Lit: Int(42) - [6] Symbol [15-16]: + [8] Symbol [15-16]: name: x type: UInt(Some(10), true) qsharp_type: Int @@ -342,7 +342,7 @@ fn implicit_bitness_uint_const_negative_decl() { "const uint x = -42;", &expect![[r#" ClassicalDeclarationStmt [0-19]: - symbol_id: 6 + symbol_id: 8 ty_span: [6-10] init_expr: Expr [16-18]: ty: UInt(None, true) @@ -355,7 +355,7 @@ fn implicit_bitness_uint_const_negative_decl() { expr: Expr [16-18]: ty: Int(None, true) kind: Lit: Int(42) - [6] Symbol [11-12]: + [8] Symbol [11-12]: name: x type: UInt(None, true) qsharp_type: Int @@ -369,7 +369,7 @@ fn explicit_bitness_uint_const_negative_decl() { "const uint[32] x = -42;", &expect![[r#" ClassicalDeclarationStmt [0-23]: - symbol_id: 6 + symbol_id: 8 ty_span: [6-14] init_expr: Expr [20-22]: ty: UInt(Some(32), true) @@ -382,7 +382,7 @@ fn explicit_bitness_uint_const_negative_decl() { expr: Expr [20-22]: ty: Int(None, true) kind: Lit: Int(42) - [6] Symbol [15-16]: + [8] Symbol [15-16]: name: x type: UInt(Some(32), true) qsharp_type: Int diff --git a/compiler/qsc_qasm3/src/semantic/tests/expression/binary/arithmetic_conversions.rs b/compiler/qsc_qasm3/src/semantic/tests/expression/binary/arithmetic_conversions.rs index 1df284f262..6b8223047b 100644 --- a/compiler/qsc_qasm3/src/semantic/tests/expression/binary/arithmetic_conversions.rs +++ b/compiler/qsc_qasm3/src/semantic/tests/expression/binary/arithmetic_conversions.rs @@ -16,30 +16,30 @@ fn int_idents_without_width_can_be_multiplied() { check_stmt_kinds( input, &expect![[r#" - ClassicalDeclarationStmt [9-19]: - symbol_id: 6 - ty_span: [9-12] - init_expr: Expr [17-18]: - ty: Int(None, true) - kind: Lit: Int(5) - ClassicalDeclarationStmt [28-38]: - symbol_id: 7 - ty_span: [28-31] - init_expr: Expr [36-37]: - ty: Int(None, true) - kind: Lit: Int(3) - ExprStmt [47-53]: - expr: Expr [47-52]: - ty: Int(None, false) - kind: BinaryOpExpr: - op: Mul - lhs: Expr [47-48]: - ty: Int(None, false) - kind: SymbolId(6) - rhs: Expr [51-52]: - ty: Int(None, false) - kind: SymbolId(7) - "#]], + ClassicalDeclarationStmt [9-19]: + symbol_id: 8 + ty_span: [9-12] + init_expr: Expr [17-18]: + ty: Int(None, false) + kind: Lit: Int(5) + ClassicalDeclarationStmt [28-38]: + symbol_id: 9 + ty_span: [28-31] + init_expr: Expr [36-37]: + ty: Int(None, false) + kind: Lit: Int(3) + ExprStmt [47-53]: + expr: Expr [47-52]: + ty: Int(None, false) + kind: BinaryOpExpr: + op: Mul + lhs: Expr [47-48]: + ty: Int(None, false) + kind: SymbolId(8) + rhs: Expr [51-52]: + ty: Int(None, false) + kind: SymbolId(9) + "#]], ); } @@ -54,30 +54,30 @@ fn int_idents_with_same_width_can_be_multiplied() { check_stmt_kinds( input, &expect![[r#" - ClassicalDeclarationStmt [9-23]: - symbol_id: 6 - ty_span: [9-16] - init_expr: Expr [21-22]: - ty: Int(Some(32), true) - kind: Lit: Int(5) - ClassicalDeclarationStmt [32-46]: - symbol_id: 7 - ty_span: [32-39] - init_expr: Expr [44-45]: - ty: Int(Some(32), true) - kind: Lit: Int(3) - ExprStmt [55-61]: - expr: Expr [55-60]: - ty: Int(Some(32), false) - kind: BinaryOpExpr: - op: Mul - lhs: Expr [55-56]: - ty: Int(Some(32), false) - kind: SymbolId(6) - rhs: Expr [59-60]: - ty: Int(Some(32), false) - kind: SymbolId(7) - "#]], + ClassicalDeclarationStmt [9-23]: + symbol_id: 8 + ty_span: [9-16] + init_expr: Expr [21-22]: + ty: Int(Some(32), true) + kind: Lit: Int(5) + ClassicalDeclarationStmt [32-46]: + symbol_id: 9 + ty_span: [32-39] + init_expr: Expr [44-45]: + ty: Int(Some(32), true) + kind: Lit: Int(3) + ExprStmt [55-61]: + expr: Expr [55-60]: + ty: Int(Some(32), false) + kind: BinaryOpExpr: + op: Mul + lhs: Expr [55-56]: + ty: Int(Some(32), false) + kind: SymbolId(8) + rhs: Expr [59-60]: + ty: Int(Some(32), false) + kind: SymbolId(9) + "#]], ); } @@ -92,34 +92,34 @@ fn int_idents_with_different_width_can_be_multiplied() { check_stmt_kinds( input, &expect![[r#" - ClassicalDeclarationStmt [9-23]: - symbol_id: 6 - ty_span: [9-16] - init_expr: Expr [21-22]: - ty: Int(Some(32), true) - kind: Lit: Int(5) - ClassicalDeclarationStmt [32-46]: - symbol_id: 7 - ty_span: [32-39] - init_expr: Expr [44-45]: - ty: Int(Some(64), true) - kind: Lit: Int(3) - ExprStmt [55-61]: - expr: Expr [55-60]: - ty: Int(Some(64), false) - kind: BinaryOpExpr: - op: Mul - lhs: Expr [55-56]: - ty: Int(Some(64), false) - kind: Cast [0-0]: + ClassicalDeclarationStmt [9-23]: + symbol_id: 8 + ty_span: [9-16] + init_expr: Expr [21-22]: + ty: Int(Some(32), true) + kind: Lit: Int(5) + ClassicalDeclarationStmt [32-46]: + symbol_id: 9 + ty_span: [32-39] + init_expr: Expr [44-45]: + ty: Int(Some(64), true) + kind: Lit: Int(3) + ExprStmt [55-61]: + expr: Expr [55-60]: + ty: Int(Some(64), false) + kind: BinaryOpExpr: + op: Mul + lhs: Expr [55-56]: ty: Int(Some(64), false) - expr: Expr [55-56]: - ty: Int(Some(32), false) - kind: SymbolId(6) - rhs: Expr [59-60]: - ty: Int(Some(64), false) - kind: SymbolId(7) - "#]], + kind: Cast [0-0]: + ty: Int(Some(64), false) + expr: Expr [55-56]: + ty: Int(Some(32), false) + kind: SymbolId(8) + rhs: Expr [59-60]: + ty: Int(Some(64), false) + kind: SymbolId(9) + "#]], ); } @@ -134,36 +134,36 @@ fn multiplying_int_idents_with_different_width_result_in_higher_width_result() { check_stmt_kinds( input, &expect![[r#" - ClassicalDeclarationStmt [9-23]: - symbol_id: 6 - ty_span: [9-16] - init_expr: Expr [21-22]: - ty: Int(Some(32), true) - kind: Lit: Int(5) - ClassicalDeclarationStmt [32-46]: - symbol_id: 7 - ty_span: [32-39] - init_expr: Expr [44-45]: - ty: Int(Some(64), true) - kind: Lit: Int(3) - ClassicalDeclarationStmt [55-73]: - symbol_id: 8 - ty_span: [55-62] - init_expr: Expr [67-72]: - ty: Int(Some(64), false) - kind: BinaryOpExpr: - op: Mul - lhs: Expr [67-68]: - ty: Int(Some(64), false) - kind: Cast [0-0]: + ClassicalDeclarationStmt [9-23]: + symbol_id: 8 + ty_span: [9-16] + init_expr: Expr [21-22]: + ty: Int(Some(32), true) + kind: Lit: Int(5) + ClassicalDeclarationStmt [32-46]: + symbol_id: 9 + ty_span: [32-39] + init_expr: Expr [44-45]: + ty: Int(Some(64), true) + kind: Lit: Int(3) + ClassicalDeclarationStmt [55-73]: + symbol_id: 10 + ty_span: [55-62] + init_expr: Expr [67-72]: + ty: Int(Some(64), false) + kind: BinaryOpExpr: + op: Mul + lhs: Expr [67-68]: + ty: Int(Some(64), false) + kind: Cast [0-0]: + ty: Int(Some(64), false) + expr: Expr [67-68]: + ty: Int(Some(32), false) + kind: SymbolId(8) + rhs: Expr [71-72]: ty: Int(Some(64), false) - expr: Expr [67-68]: - ty: Int(Some(32), false) - kind: SymbolId(6) - rhs: Expr [71-72]: - ty: Int(Some(64), false) - kind: SymbolId(7) - "#]], + kind: SymbolId(9) + "#]], ); } @@ -178,40 +178,40 @@ fn multiplying_int_idents_with_different_width_result_in_no_width_result() { check_stmt_kinds( input, &expect![[r#" - ClassicalDeclarationStmt [9-23]: - symbol_id: 6 - ty_span: [9-16] - init_expr: Expr [21-22]: - ty: Int(Some(32), true) - kind: Lit: Int(5) - ClassicalDeclarationStmt [32-46]: - symbol_id: 7 - ty_span: [32-39] - init_expr: Expr [44-45]: - ty: Int(Some(64), true) - kind: Lit: Int(3) - ClassicalDeclarationStmt [55-69]: - symbol_id: 8 - ty_span: [55-58] - init_expr: Expr [63-68]: - ty: Int(None, false) - kind: Cast [0-0]: + ClassicalDeclarationStmt [9-23]: + symbol_id: 8 + ty_span: [9-16] + init_expr: Expr [21-22]: + ty: Int(Some(32), true) + kind: Lit: Int(5) + ClassicalDeclarationStmt [32-46]: + symbol_id: 9 + ty_span: [32-39] + init_expr: Expr [44-45]: + ty: Int(Some(64), true) + kind: Lit: Int(3) + ClassicalDeclarationStmt [55-69]: + symbol_id: 10 + ty_span: [55-58] + init_expr: Expr [63-68]: ty: Int(None, false) - expr: Expr [63-68]: - ty: Int(Some(64), false) - kind: BinaryOpExpr: - op: Mul - lhs: Expr [63-64]: - ty: Int(Some(64), false) - kind: Cast [0-0]: + kind: Cast [0-0]: + ty: Int(None, false) + expr: Expr [63-68]: + ty: Int(Some(64), false) + kind: BinaryOpExpr: + op: Mul + lhs: Expr [63-64]: ty: Int(Some(64), false) - expr: Expr [63-64]: - ty: Int(Some(32), false) - kind: SymbolId(6) - rhs: Expr [67-68]: - ty: Int(Some(64), false) - kind: SymbolId(7) - "#]], + kind: Cast [0-0]: + ty: Int(Some(64), false) + expr: Expr [63-64]: + ty: Int(Some(32), false) + kind: SymbolId(8) + rhs: Expr [67-68]: + ty: Int(Some(64), false) + kind: SymbolId(9) + "#]], ); } @@ -226,39 +226,39 @@ fn multiplying_int_idents_with_width_greater_than_64_result_in_bigint_result() { check_stmt_kinds( input, &expect![[r#" - ClassicalDeclarationStmt [9-23]: - symbol_id: 6 - ty_span: [9-16] - init_expr: Expr [21-22]: - ty: Int(Some(32), true) - kind: Lit: Int(5) - ClassicalDeclarationStmt [32-46]: - symbol_id: 7 - ty_span: [32-39] - init_expr: Expr [44-45]: - ty: Int(Some(64), true) - kind: Lit: Int(3) - ClassicalDeclarationStmt [55-73]: - symbol_id: 8 - ty_span: [55-62] - init_expr: Expr [67-72]: - ty: Int(Some(67), false) - kind: Cast [0-0]: + ClassicalDeclarationStmt [9-23]: + symbol_id: 8 + ty_span: [9-16] + init_expr: Expr [21-22]: + ty: Int(Some(32), true) + kind: Lit: Int(5) + ClassicalDeclarationStmt [32-46]: + symbol_id: 9 + ty_span: [32-39] + init_expr: Expr [44-45]: + ty: Int(Some(64), true) + kind: Lit: Int(3) + ClassicalDeclarationStmt [55-73]: + symbol_id: 10 + ty_span: [55-62] + init_expr: Expr [67-72]: ty: Int(Some(67), false) - expr: Expr [67-72]: - ty: Int(Some(64), false) - kind: BinaryOpExpr: - op: Mul - lhs: Expr [67-68]: - ty: Int(Some(64), false) - kind: Cast [0-0]: + kind: Cast [0-0]: + ty: Int(Some(67), false) + expr: Expr [67-72]: + ty: Int(Some(64), false) + kind: BinaryOpExpr: + op: Mul + lhs: Expr [67-68]: ty: Int(Some(64), false) - expr: Expr [67-68]: - ty: Int(Some(32), false) - kind: SymbolId(6) - rhs: Expr [71-72]: - ty: Int(Some(64), false) - kind: SymbolId(7) - "#]], + kind: Cast [0-0]: + ty: Int(Some(64), false) + expr: Expr [67-68]: + ty: Int(Some(32), false) + kind: SymbolId(8) + rhs: Expr [71-72]: + ty: Int(Some(64), false) + kind: SymbolId(9) + "#]], ); } diff --git a/compiler/qsc_qasm3/src/semantic/tests/expression/binary/comparison/bit_to_bit.rs b/compiler/qsc_qasm3/src/semantic/tests/expression/binary/comparison/bit_to_bit.rs index 2f62c3202b..435eec3e15 100644 --- a/compiler/qsc_qasm3/src/semantic/tests/expression/binary/comparison/bit_to_bit.rs +++ b/compiler/qsc_qasm3/src/semantic/tests/expression/binary/comparison/bit_to_bit.rs @@ -17,29 +17,29 @@ fn logical_and() { input, &expect![[r#" ClassicalDeclarationStmt [9-19]: - symbol_id: 6 + symbol_id: 8 ty_span: [9-12] init_expr: Expr [17-18]: ty: Bit(true) - kind: Lit: Int(1) - [6] Symbol [13-14]: + kind: Lit: Bit(1) + [8] Symbol [13-14]: name: x type: Bit(false) qsharp_type: Result io_kind: Default ClassicalDeclarationStmt [28-38]: - symbol_id: 7 + symbol_id: 9 ty_span: [28-31] init_expr: Expr [36-37]: ty: Bit(true) - kind: Lit: Int(0) - [7] Symbol [32-33]: + kind: Lit: Bit(0) + [9] Symbol [32-33]: name: y type: Bit(false) qsharp_type: Result io_kind: Default ClassicalDeclarationStmt [47-63]: - symbol_id: 8 + symbol_id: 10 ty_span: [47-51] init_expr: Expr [56-62]: ty: Bool(false) @@ -51,15 +51,15 @@ fn logical_and() { ty: Bool(false) expr: Expr [56-57]: ty: Bit(false) - kind: SymbolId(6) + kind: SymbolId(8) rhs: Expr [61-62]: ty: Bool(false) kind: Cast [0-0]: ty: Bool(false) expr: Expr [61-62]: ty: Bit(false) - kind: SymbolId(7) - [8] Symbol [52-53]: + kind: SymbolId(9) + [10] Symbol [52-53]: name: a type: Bool(false) qsharp_type: bool @@ -80,29 +80,29 @@ fn logical_or() { input, &expect![[r#" ClassicalDeclarationStmt [9-19]: - symbol_id: 6 + symbol_id: 8 ty_span: [9-12] init_expr: Expr [17-18]: ty: Bit(true) - kind: Lit: Int(1) - [6] Symbol [13-14]: + kind: Lit: Bit(1) + [8] Symbol [13-14]: name: x type: Bit(false) qsharp_type: Result io_kind: Default ClassicalDeclarationStmt [28-38]: - symbol_id: 7 + symbol_id: 9 ty_span: [28-31] init_expr: Expr [36-37]: ty: Bit(true) - kind: Lit: Int(0) - [7] Symbol [32-33]: + kind: Lit: Bit(0) + [9] Symbol [32-33]: name: y type: Bit(false) qsharp_type: Result io_kind: Default ClassicalDeclarationStmt [47-63]: - symbol_id: 8 + symbol_id: 10 ty_span: [47-51] init_expr: Expr [56-62]: ty: Bool(false) @@ -114,15 +114,15 @@ fn logical_or() { ty: Bool(false) expr: Expr [56-57]: ty: Bit(false) - kind: SymbolId(6) + kind: SymbolId(8) rhs: Expr [61-62]: ty: Bool(false) kind: Cast [0-0]: ty: Bool(false) expr: Expr [61-62]: ty: Bit(false) - kind: SymbolId(7) - [8] Symbol [52-53]: + kind: SymbolId(9) + [10] Symbol [52-53]: name: a type: Bool(false) qsharp_type: bool @@ -143,29 +143,29 @@ fn unop_not_logical_and_unop_not() { input, &expect![[r#" ClassicalDeclarationStmt [9-19]: - symbol_id: 6 + symbol_id: 8 ty_span: [9-12] init_expr: Expr [17-18]: ty: Bit(true) - kind: Lit: Int(1) - [6] Symbol [13-14]: + kind: Lit: Bit(1) + [8] Symbol [13-14]: name: x type: Bit(false) qsharp_type: Result io_kind: Default ClassicalDeclarationStmt [28-38]: - symbol_id: 7 + symbol_id: 9 ty_span: [28-31] init_expr: Expr [36-37]: ty: Bit(true) - kind: Lit: Int(0) - [7] Symbol [32-33]: + kind: Lit: Bit(0) + [9] Symbol [32-33]: name: y type: Bit(false) qsharp_type: Result io_kind: Default ClassicalDeclarationStmt [47-65]: - symbol_id: 8 + symbol_id: 10 ty_span: [47-51] init_expr: Expr [56-64]: ty: Bool(false) @@ -181,7 +181,7 @@ fn unop_not_logical_and_unop_not() { ty: Bool(false) expr: Expr [57-58]: ty: Bit(false) - kind: SymbolId(6) + kind: SymbolId(8) rhs: Expr [63-64]: ty: Bool(false) kind: UnaryOpExpr [63-64]: @@ -192,8 +192,8 @@ fn unop_not_logical_and_unop_not() { ty: Bool(false) expr: Expr [63-64]: ty: Bit(false) - kind: SymbolId(7) - [8] Symbol [52-53]: + kind: SymbolId(9) + [10] Symbol [52-53]: name: a type: Bool(false) qsharp_type: bool @@ -214,29 +214,29 @@ fn unop_not_logical_or_unop_not() { input, &expect![[r#" ClassicalDeclarationStmt [9-19]: - symbol_id: 6 + symbol_id: 8 ty_span: [9-12] init_expr: Expr [17-18]: ty: Bit(true) - kind: Lit: Int(1) - [6] Symbol [13-14]: + kind: Lit: Bit(1) + [8] Symbol [13-14]: name: x type: Bit(false) qsharp_type: Result io_kind: Default ClassicalDeclarationStmt [28-38]: - symbol_id: 7 + symbol_id: 9 ty_span: [28-31] init_expr: Expr [36-37]: ty: Bit(true) - kind: Lit: Int(0) - [7] Symbol [32-33]: + kind: Lit: Bit(0) + [9] Symbol [32-33]: name: y type: Bit(false) qsharp_type: Result io_kind: Default ClassicalDeclarationStmt [47-65]: - symbol_id: 8 + symbol_id: 10 ty_span: [47-51] init_expr: Expr [56-64]: ty: Bool(false) @@ -252,7 +252,7 @@ fn unop_not_logical_or_unop_not() { ty: Bool(false) expr: Expr [57-58]: ty: Bit(false) - kind: SymbolId(6) + kind: SymbolId(8) rhs: Expr [63-64]: ty: Bool(false) kind: UnaryOpExpr [63-64]: @@ -263,8 +263,8 @@ fn unop_not_logical_or_unop_not() { ty: Bool(false) expr: Expr [63-64]: ty: Bit(false) - kind: SymbolId(7) - [8] Symbol [52-53]: + kind: SymbolId(9) + [10] Symbol [52-53]: name: a type: Bool(false) qsharp_type: bool @@ -285,29 +285,29 @@ fn unop_not_logical_and() { input, &expect![[r#" ClassicalDeclarationStmt [9-19]: - symbol_id: 6 + symbol_id: 8 ty_span: [9-12] init_expr: Expr [17-18]: ty: Bit(true) - kind: Lit: Int(1) - [6] Symbol [13-14]: + kind: Lit: Bit(1) + [8] Symbol [13-14]: name: x type: Bit(false) qsharp_type: Result io_kind: Default ClassicalDeclarationStmt [28-38]: - symbol_id: 7 + symbol_id: 9 ty_span: [28-31] init_expr: Expr [36-37]: ty: Bit(true) - kind: Lit: Int(0) - [7] Symbol [32-33]: + kind: Lit: Bit(0) + [9] Symbol [32-33]: name: y type: Bit(false) qsharp_type: Result io_kind: Default ClassicalDeclarationStmt [47-64]: - symbol_id: 8 + symbol_id: 10 ty_span: [47-51] init_expr: Expr [56-63]: ty: Bool(false) @@ -323,15 +323,15 @@ fn unop_not_logical_and() { ty: Bool(false) expr: Expr [57-58]: ty: Bit(false) - kind: SymbolId(6) + kind: SymbolId(8) rhs: Expr [62-63]: ty: Bool(false) kind: Cast [0-0]: ty: Bool(false) expr: Expr [62-63]: ty: Bit(false) - kind: SymbolId(7) - [8] Symbol [52-53]: + kind: SymbolId(9) + [10] Symbol [52-53]: name: a type: Bool(false) qsharp_type: bool @@ -352,29 +352,29 @@ fn unop_not_logical_or() { input, &expect![[r#" ClassicalDeclarationStmt [9-19]: - symbol_id: 6 + symbol_id: 8 ty_span: [9-12] init_expr: Expr [17-18]: ty: Bit(true) - kind: Lit: Int(1) - [6] Symbol [13-14]: + kind: Lit: Bit(1) + [8] Symbol [13-14]: name: x type: Bit(false) qsharp_type: Result io_kind: Default ClassicalDeclarationStmt [28-38]: - symbol_id: 7 + symbol_id: 9 ty_span: [28-31] init_expr: Expr [36-37]: ty: Bit(true) - kind: Lit: Int(0) - [7] Symbol [32-33]: + kind: Lit: Bit(0) + [9] Symbol [32-33]: name: y type: Bit(false) qsharp_type: Result io_kind: Default ClassicalDeclarationStmt [47-64]: - symbol_id: 8 + symbol_id: 10 ty_span: [47-51] init_expr: Expr [56-63]: ty: Bool(false) @@ -390,15 +390,15 @@ fn unop_not_logical_or() { ty: Bool(false) expr: Expr [57-58]: ty: Bit(false) - kind: SymbolId(6) + kind: SymbolId(8) rhs: Expr [62-63]: ty: Bool(false) kind: Cast [0-0]: ty: Bool(false) expr: Expr [62-63]: ty: Bit(false) - kind: SymbolId(7) - [8] Symbol [52-53]: + kind: SymbolId(9) + [10] Symbol [52-53]: name: a type: Bool(false) qsharp_type: bool @@ -419,29 +419,29 @@ fn logical_and_unop_not() { input, &expect![[r#" ClassicalDeclarationStmt [9-19]: - symbol_id: 6 + symbol_id: 8 ty_span: [9-12] init_expr: Expr [17-18]: ty: Bit(true) - kind: Lit: Int(1) - [6] Symbol [13-14]: + kind: Lit: Bit(1) + [8] Symbol [13-14]: name: x type: Bit(false) qsharp_type: Result io_kind: Default ClassicalDeclarationStmt [28-38]: - symbol_id: 7 + symbol_id: 9 ty_span: [28-31] init_expr: Expr [36-37]: ty: Bit(true) - kind: Lit: Int(0) - [7] Symbol [32-33]: + kind: Lit: Bit(0) + [9] Symbol [32-33]: name: y type: Bit(false) qsharp_type: Result io_kind: Default ClassicalDeclarationStmt [47-64]: - symbol_id: 8 + symbol_id: 10 ty_span: [47-51] init_expr: Expr [56-63]: ty: Bool(false) @@ -453,7 +453,7 @@ fn logical_and_unop_not() { ty: Bool(false) expr: Expr [56-57]: ty: Bit(false) - kind: SymbolId(6) + kind: SymbolId(8) rhs: Expr [62-63]: ty: Bool(false) kind: UnaryOpExpr [62-63]: @@ -464,8 +464,8 @@ fn logical_and_unop_not() { ty: Bool(false) expr: Expr [62-63]: ty: Bit(false) - kind: SymbolId(7) - [8] Symbol [52-53]: + kind: SymbolId(9) + [10] Symbol [52-53]: name: a type: Bool(false) qsharp_type: bool @@ -486,29 +486,29 @@ fn logical_or_unop_not() { input, &expect![[r#" ClassicalDeclarationStmt [9-19]: - symbol_id: 6 + symbol_id: 8 ty_span: [9-12] init_expr: Expr [17-18]: ty: Bit(true) - kind: Lit: Int(1) - [6] Symbol [13-14]: + kind: Lit: Bit(1) + [8] Symbol [13-14]: name: x type: Bit(false) qsharp_type: Result io_kind: Default ClassicalDeclarationStmt [28-38]: - symbol_id: 7 + symbol_id: 9 ty_span: [28-31] init_expr: Expr [36-37]: ty: Bit(true) - kind: Lit: Int(0) - [7] Symbol [32-33]: + kind: Lit: Bit(0) + [9] Symbol [32-33]: name: y type: Bit(false) qsharp_type: Result io_kind: Default ClassicalDeclarationStmt [47-64]: - symbol_id: 8 + symbol_id: 10 ty_span: [47-51] init_expr: Expr [56-63]: ty: Bool(false) @@ -520,7 +520,7 @@ fn logical_or_unop_not() { ty: Bool(false) expr: Expr [56-57]: ty: Bit(false) - kind: SymbolId(6) + kind: SymbolId(8) rhs: Expr [62-63]: ty: Bool(false) kind: UnaryOpExpr [62-63]: @@ -531,8 +531,8 @@ fn logical_or_unop_not() { ty: Bool(false) expr: Expr [62-63]: ty: Bit(false) - kind: SymbolId(7) - [8] Symbol [52-53]: + kind: SymbolId(9) + [10] Symbol [52-53]: name: a type: Bool(false) qsharp_type: bool diff --git a/compiler/qsc_qasm3/src/semantic/tests/expression/binary/comparison/bool_to_bool.rs b/compiler/qsc_qasm3/src/semantic/tests/expression/binary/comparison/bool_to_bool.rs index 56579fd91c..d1e2ee4edb 100644 --- a/compiler/qsc_qasm3/src/semantic/tests/expression/binary/comparison/bool_to_bool.rs +++ b/compiler/qsc_qasm3/src/semantic/tests/expression/binary/comparison/bool_to_bool.rs @@ -17,29 +17,29 @@ fn logical_and() { input, &expect![[r#" ClassicalDeclarationStmt [9-23]: - symbol_id: 6 + symbol_id: 8 ty_span: [9-13] init_expr: Expr [18-22]: - ty: Bool(true) + ty: Bool(false) kind: Lit: Bool(true) - [6] Symbol [14-15]: + [8] Symbol [14-15]: name: x type: Bool(false) qsharp_type: bool io_kind: Default ClassicalDeclarationStmt [32-47]: - symbol_id: 7 + symbol_id: 9 ty_span: [32-36] init_expr: Expr [41-46]: - ty: Bool(true) + ty: Bool(false) kind: Lit: Bool(false) - [7] Symbol [37-38]: + [9] Symbol [37-38]: name: y type: Bool(false) qsharp_type: bool io_kind: Default ClassicalDeclarationStmt [56-72]: - symbol_id: 8 + symbol_id: 10 ty_span: [56-60] init_expr: Expr [65-71]: ty: Bool(false) @@ -47,11 +47,11 @@ fn logical_and() { op: AndL lhs: Expr [65-66]: ty: Bool(false) - kind: SymbolId(6) + kind: SymbolId(8) rhs: Expr [70-71]: ty: Bool(false) - kind: SymbolId(7) - [8] Symbol [61-62]: + kind: SymbolId(9) + [10] Symbol [61-62]: name: a type: Bool(false) qsharp_type: bool @@ -72,29 +72,29 @@ fn logical_or() { input, &expect![[r#" ClassicalDeclarationStmt [9-23]: - symbol_id: 6 + symbol_id: 8 ty_span: [9-13] init_expr: Expr [18-22]: - ty: Bool(true) + ty: Bool(false) kind: Lit: Bool(true) - [6] Symbol [14-15]: + [8] Symbol [14-15]: name: x type: Bool(false) qsharp_type: bool io_kind: Default ClassicalDeclarationStmt [32-47]: - symbol_id: 7 + symbol_id: 9 ty_span: [32-36] init_expr: Expr [41-46]: - ty: Bool(true) + ty: Bool(false) kind: Lit: Bool(false) - [7] Symbol [37-38]: + [9] Symbol [37-38]: name: y type: Bool(false) qsharp_type: bool io_kind: Default ClassicalDeclarationStmt [56-72]: - symbol_id: 8 + symbol_id: 10 ty_span: [56-60] init_expr: Expr [65-71]: ty: Bool(false) @@ -102,11 +102,11 @@ fn logical_or() { op: OrL lhs: Expr [65-66]: ty: Bool(false) - kind: SymbolId(6) + kind: SymbolId(8) rhs: Expr [70-71]: ty: Bool(false) - kind: SymbolId(7) - [8] Symbol [61-62]: + kind: SymbolId(9) + [10] Symbol [61-62]: name: a type: Bool(false) qsharp_type: bool @@ -127,29 +127,29 @@ fn unop_not_logical_and_unop_not() { input, &expect![[r#" ClassicalDeclarationStmt [9-23]: - symbol_id: 6 + symbol_id: 8 ty_span: [9-13] init_expr: Expr [18-22]: - ty: Bool(true) + ty: Bool(false) kind: Lit: Bool(true) - [6] Symbol [14-15]: + [8] Symbol [14-15]: name: x type: Bool(false) qsharp_type: bool io_kind: Default ClassicalDeclarationStmt [32-47]: - symbol_id: 7 + symbol_id: 9 ty_span: [32-36] init_expr: Expr [41-46]: - ty: Bool(true) + ty: Bool(false) kind: Lit: Bool(false) - [7] Symbol [37-38]: + [9] Symbol [37-38]: name: y type: Bool(false) qsharp_type: bool io_kind: Default ClassicalDeclarationStmt [56-74]: - symbol_id: 8 + symbol_id: 10 ty_span: [56-60] init_expr: Expr [65-73]: ty: Bool(false) @@ -161,15 +161,15 @@ fn unop_not_logical_and_unop_not() { op: NotL expr: Expr [66-67]: ty: Bool(false) - kind: SymbolId(6) + kind: SymbolId(8) rhs: Expr [72-73]: ty: Bool(false) kind: UnaryOpExpr [72-73]: op: NotL expr: Expr [72-73]: ty: Bool(false) - kind: SymbolId(7) - [8] Symbol [61-62]: + kind: SymbolId(9) + [10] Symbol [61-62]: name: a type: Bool(false) qsharp_type: bool @@ -190,29 +190,29 @@ fn unop_not_logical_or_unop_not() { input, &expect![[r#" ClassicalDeclarationStmt [9-23]: - symbol_id: 6 + symbol_id: 8 ty_span: [9-13] init_expr: Expr [18-22]: - ty: Bool(true) + ty: Bool(false) kind: Lit: Bool(true) - [6] Symbol [14-15]: + [8] Symbol [14-15]: name: x type: Bool(false) qsharp_type: bool io_kind: Default ClassicalDeclarationStmt [32-47]: - symbol_id: 7 + symbol_id: 9 ty_span: [32-36] init_expr: Expr [41-46]: - ty: Bool(true) + ty: Bool(false) kind: Lit: Bool(false) - [7] Symbol [37-38]: + [9] Symbol [37-38]: name: y type: Bool(false) qsharp_type: bool io_kind: Default ClassicalDeclarationStmt [56-74]: - symbol_id: 8 + symbol_id: 10 ty_span: [56-60] init_expr: Expr [65-73]: ty: Bool(false) @@ -224,15 +224,15 @@ fn unop_not_logical_or_unop_not() { op: NotL expr: Expr [66-67]: ty: Bool(false) - kind: SymbolId(6) + kind: SymbolId(8) rhs: Expr [72-73]: ty: Bool(false) kind: UnaryOpExpr [72-73]: op: NotL expr: Expr [72-73]: ty: Bool(false) - kind: SymbolId(7) - [8] Symbol [61-62]: + kind: SymbolId(9) + [10] Symbol [61-62]: name: a type: Bool(false) qsharp_type: bool @@ -253,29 +253,29 @@ fn unop_not_logical_and() { input, &expect![[r#" ClassicalDeclarationStmt [9-23]: - symbol_id: 6 + symbol_id: 8 ty_span: [9-13] init_expr: Expr [18-22]: - ty: Bool(true) + ty: Bool(false) kind: Lit: Bool(true) - [6] Symbol [14-15]: + [8] Symbol [14-15]: name: x type: Bool(false) qsharp_type: bool io_kind: Default ClassicalDeclarationStmt [32-47]: - symbol_id: 7 + symbol_id: 9 ty_span: [32-36] init_expr: Expr [41-46]: - ty: Bool(true) + ty: Bool(false) kind: Lit: Bool(false) - [7] Symbol [37-38]: + [9] Symbol [37-38]: name: y type: Bool(false) qsharp_type: bool io_kind: Default ClassicalDeclarationStmt [56-73]: - symbol_id: 8 + symbol_id: 10 ty_span: [56-60] init_expr: Expr [65-72]: ty: Bool(false) @@ -287,11 +287,11 @@ fn unop_not_logical_and() { op: NotL expr: Expr [66-67]: ty: Bool(false) - kind: SymbolId(6) + kind: SymbolId(8) rhs: Expr [71-72]: ty: Bool(false) - kind: SymbolId(7) - [8] Symbol [61-62]: + kind: SymbolId(9) + [10] Symbol [61-62]: name: a type: Bool(false) qsharp_type: bool @@ -312,29 +312,29 @@ fn unop_not_logical_or() { input, &expect![[r#" ClassicalDeclarationStmt [9-23]: - symbol_id: 6 + symbol_id: 8 ty_span: [9-13] init_expr: Expr [18-22]: - ty: Bool(true) + ty: Bool(false) kind: Lit: Bool(true) - [6] Symbol [14-15]: + [8] Symbol [14-15]: name: x type: Bool(false) qsharp_type: bool io_kind: Default ClassicalDeclarationStmt [32-47]: - symbol_id: 7 + symbol_id: 9 ty_span: [32-36] init_expr: Expr [41-46]: - ty: Bool(true) + ty: Bool(false) kind: Lit: Bool(false) - [7] Symbol [37-38]: + [9] Symbol [37-38]: name: y type: Bool(false) qsharp_type: bool io_kind: Default ClassicalDeclarationStmt [56-73]: - symbol_id: 8 + symbol_id: 10 ty_span: [56-60] init_expr: Expr [65-72]: ty: Bool(false) @@ -346,11 +346,11 @@ fn unop_not_logical_or() { op: NotL expr: Expr [66-67]: ty: Bool(false) - kind: SymbolId(6) + kind: SymbolId(8) rhs: Expr [71-72]: ty: Bool(false) - kind: SymbolId(7) - [8] Symbol [61-62]: + kind: SymbolId(9) + [10] Symbol [61-62]: name: a type: Bool(false) qsharp_type: bool @@ -371,29 +371,29 @@ fn logical_and_unop_not() { input, &expect![[r#" ClassicalDeclarationStmt [9-23]: - symbol_id: 6 + symbol_id: 8 ty_span: [9-13] init_expr: Expr [18-22]: - ty: Bool(true) + ty: Bool(false) kind: Lit: Bool(true) - [6] Symbol [14-15]: + [8] Symbol [14-15]: name: x type: Bool(false) qsharp_type: bool io_kind: Default ClassicalDeclarationStmt [32-47]: - symbol_id: 7 + symbol_id: 9 ty_span: [32-36] init_expr: Expr [41-46]: - ty: Bool(true) + ty: Bool(false) kind: Lit: Bool(false) - [7] Symbol [37-38]: + [9] Symbol [37-38]: name: y type: Bool(false) qsharp_type: bool io_kind: Default ClassicalDeclarationStmt [56-73]: - symbol_id: 8 + symbol_id: 10 ty_span: [56-60] init_expr: Expr [65-72]: ty: Bool(false) @@ -401,15 +401,15 @@ fn logical_and_unop_not() { op: AndL lhs: Expr [65-66]: ty: Bool(false) - kind: SymbolId(6) + kind: SymbolId(8) rhs: Expr [71-72]: ty: Bool(false) kind: UnaryOpExpr [71-72]: op: NotL expr: Expr [71-72]: ty: Bool(false) - kind: SymbolId(7) - [8] Symbol [61-62]: + kind: SymbolId(9) + [10] Symbol [61-62]: name: a type: Bool(false) qsharp_type: bool @@ -430,29 +430,29 @@ fn logical_or_unop_not() { input, &expect![[r#" ClassicalDeclarationStmt [9-23]: - symbol_id: 6 + symbol_id: 8 ty_span: [9-13] init_expr: Expr [18-22]: - ty: Bool(true) + ty: Bool(false) kind: Lit: Bool(true) - [6] Symbol [14-15]: + [8] Symbol [14-15]: name: x type: Bool(false) qsharp_type: bool io_kind: Default ClassicalDeclarationStmt [32-47]: - symbol_id: 7 + symbol_id: 9 ty_span: [32-36] init_expr: Expr [41-46]: - ty: Bool(true) + ty: Bool(false) kind: Lit: Bool(false) - [7] Symbol [37-38]: + [9] Symbol [37-38]: name: y type: Bool(false) qsharp_type: bool io_kind: Default ClassicalDeclarationStmt [56-73]: - symbol_id: 8 + symbol_id: 10 ty_span: [56-60] init_expr: Expr [65-72]: ty: Bool(false) @@ -460,15 +460,15 @@ fn logical_or_unop_not() { op: OrL lhs: Expr [65-66]: ty: Bool(false) - kind: SymbolId(6) + kind: SymbolId(8) rhs: Expr [71-72]: ty: Bool(false) kind: UnaryOpExpr [71-72]: op: NotL expr: Expr [71-72]: ty: Bool(false) - kind: SymbolId(7) - [8] Symbol [61-62]: + kind: SymbolId(9) + [10] Symbol [61-62]: name: a type: Bool(false) qsharp_type: bool diff --git a/compiler/qsc_qasm3/src/semantic/tests/expression/binary/comparison/float_to_float.rs b/compiler/qsc_qasm3/src/semantic/tests/expression/binary/comparison/float_to_float.rs index 76cf8683f6..9439a6c6d2 100644 --- a/compiler/qsc_qasm3/src/semantic/tests/expression/binary/comparison/float_to_float.rs +++ b/compiler/qsc_qasm3/src/semantic/tests/expression/binary/comparison/float_to_float.rs @@ -17,29 +17,29 @@ fn greater_than() { input, &expect![[r#" ClassicalDeclarationStmt [9-22]: - symbol_id: 6 + symbol_id: 8 ty_span: [9-14] init_expr: Expr [19-21]: - ty: Float(None, true) + ty: Float(None, false) kind: Lit: Float(5.0) - [6] Symbol [15-16]: + [8] Symbol [15-16]: name: x type: Float(None, false) qsharp_type: Double io_kind: Default ClassicalDeclarationStmt [31-44]: - symbol_id: 7 + symbol_id: 9 ty_span: [31-36] init_expr: Expr [41-43]: - ty: Float(None, true) + ty: Float(None, false) kind: Lit: Float(3.0) - [7] Symbol [37-38]: + [9] Symbol [37-38]: name: y type: Float(None, false) qsharp_type: Double io_kind: Default ClassicalDeclarationStmt [53-68]: - symbol_id: 8 + symbol_id: 10 ty_span: [53-57] init_expr: Expr [62-67]: ty: Bool(false) @@ -47,11 +47,11 @@ fn greater_than() { op: Gt lhs: Expr [62-63]: ty: Float(None, false) - kind: SymbolId(6) + kind: SymbolId(8) rhs: Expr [66-67]: ty: Float(None, false) - kind: SymbolId(7) - [8] Symbol [58-59]: + kind: SymbolId(9) + [10] Symbol [58-59]: name: f type: Bool(false) qsharp_type: bool @@ -72,29 +72,29 @@ fn greater_than_equals() { input, &expect![[r#" ClassicalDeclarationStmt [9-22]: - symbol_id: 6 + symbol_id: 8 ty_span: [9-14] init_expr: Expr [19-21]: - ty: Float(None, true) + ty: Float(None, false) kind: Lit: Float(5.0) - [6] Symbol [15-16]: + [8] Symbol [15-16]: name: x type: Float(None, false) qsharp_type: Double io_kind: Default ClassicalDeclarationStmt [31-44]: - symbol_id: 7 + symbol_id: 9 ty_span: [31-36] init_expr: Expr [41-43]: - ty: Float(None, true) + ty: Float(None, false) kind: Lit: Float(3.0) - [7] Symbol [37-38]: + [9] Symbol [37-38]: name: y type: Float(None, false) qsharp_type: Double io_kind: Default ClassicalDeclarationStmt [53-69]: - symbol_id: 8 + symbol_id: 10 ty_span: [53-57] init_expr: Expr [62-68]: ty: Bool(false) @@ -102,11 +102,11 @@ fn greater_than_equals() { op: Gte lhs: Expr [62-63]: ty: Float(None, false) - kind: SymbolId(6) + kind: SymbolId(8) rhs: Expr [67-68]: ty: Float(None, false) - kind: SymbolId(7) - [8] Symbol [58-59]: + kind: SymbolId(9) + [10] Symbol [58-59]: name: e type: Bool(false) qsharp_type: bool @@ -127,29 +127,29 @@ fn less_than() { input, &expect![[r#" ClassicalDeclarationStmt [9-22]: - symbol_id: 6 + symbol_id: 8 ty_span: [9-14] init_expr: Expr [19-21]: - ty: Float(None, true) + ty: Float(None, false) kind: Lit: Float(5.0) - [6] Symbol [15-16]: + [8] Symbol [15-16]: name: x type: Float(None, false) qsharp_type: Double io_kind: Default ClassicalDeclarationStmt [31-44]: - symbol_id: 7 + symbol_id: 9 ty_span: [31-36] init_expr: Expr [41-43]: - ty: Float(None, true) + ty: Float(None, false) kind: Lit: Float(3.0) - [7] Symbol [37-38]: + [9] Symbol [37-38]: name: y type: Float(None, false) qsharp_type: Double io_kind: Default ClassicalDeclarationStmt [53-68]: - symbol_id: 8 + symbol_id: 10 ty_span: [53-57] init_expr: Expr [62-67]: ty: Bool(false) @@ -157,11 +157,11 @@ fn less_than() { op: Lt lhs: Expr [62-63]: ty: Float(None, false) - kind: SymbolId(6) + kind: SymbolId(8) rhs: Expr [66-67]: ty: Float(None, false) - kind: SymbolId(7) - [8] Symbol [58-59]: + kind: SymbolId(9) + [10] Symbol [58-59]: name: a type: Bool(false) qsharp_type: bool @@ -182,29 +182,29 @@ fn less_than_equals() { input, &expect![[r#" ClassicalDeclarationStmt [9-22]: - symbol_id: 6 + symbol_id: 8 ty_span: [9-14] init_expr: Expr [19-21]: - ty: Float(None, true) + ty: Float(None, false) kind: Lit: Float(5.0) - [6] Symbol [15-16]: + [8] Symbol [15-16]: name: x type: Float(None, false) qsharp_type: Double io_kind: Default ClassicalDeclarationStmt [31-44]: - symbol_id: 7 + symbol_id: 9 ty_span: [31-36] init_expr: Expr [41-43]: - ty: Float(None, true) + ty: Float(None, false) kind: Lit: Float(3.0) - [7] Symbol [37-38]: + [9] Symbol [37-38]: name: y type: Float(None, false) qsharp_type: Double io_kind: Default ClassicalDeclarationStmt [53-69]: - symbol_id: 8 + symbol_id: 10 ty_span: [53-57] init_expr: Expr [62-68]: ty: Bool(false) @@ -212,11 +212,11 @@ fn less_than_equals() { op: Lte lhs: Expr [62-63]: ty: Float(None, false) - kind: SymbolId(6) + kind: SymbolId(8) rhs: Expr [67-68]: ty: Float(None, false) - kind: SymbolId(7) - [8] Symbol [58-59]: + kind: SymbolId(9) + [10] Symbol [58-59]: name: c type: Bool(false) qsharp_type: bool @@ -237,29 +237,29 @@ fn equals() { input, &expect![[r#" ClassicalDeclarationStmt [9-22]: - symbol_id: 6 + symbol_id: 8 ty_span: [9-14] init_expr: Expr [19-21]: - ty: Float(None, true) + ty: Float(None, false) kind: Lit: Float(5.0) - [6] Symbol [15-16]: + [8] Symbol [15-16]: name: x type: Float(None, false) qsharp_type: Double io_kind: Default ClassicalDeclarationStmt [31-44]: - symbol_id: 7 + symbol_id: 9 ty_span: [31-36] init_expr: Expr [41-43]: - ty: Float(None, true) + ty: Float(None, false) kind: Lit: Float(3.0) - [7] Symbol [37-38]: + [9] Symbol [37-38]: name: y type: Float(None, false) qsharp_type: Double io_kind: Default ClassicalDeclarationStmt [53-69]: - symbol_id: 8 + symbol_id: 10 ty_span: [53-57] init_expr: Expr [62-68]: ty: Bool(false) @@ -267,11 +267,11 @@ fn equals() { op: Eq lhs: Expr [62-63]: ty: Float(None, false) - kind: SymbolId(6) + kind: SymbolId(8) rhs: Expr [67-68]: ty: Float(None, false) - kind: SymbolId(7) - [8] Symbol [58-59]: + kind: SymbolId(9) + [10] Symbol [58-59]: name: b type: Bool(false) qsharp_type: bool @@ -292,29 +292,29 @@ fn not_equals() { input, &expect![[r#" ClassicalDeclarationStmt [9-22]: - symbol_id: 6 + symbol_id: 8 ty_span: [9-14] init_expr: Expr [19-21]: - ty: Float(None, true) + ty: Float(None, false) kind: Lit: Float(5.0) - [6] Symbol [15-16]: + [8] Symbol [15-16]: name: x type: Float(None, false) qsharp_type: Double io_kind: Default ClassicalDeclarationStmt [31-44]: - symbol_id: 7 + symbol_id: 9 ty_span: [31-36] init_expr: Expr [41-43]: - ty: Float(None, true) + ty: Float(None, false) kind: Lit: Float(3.0) - [7] Symbol [37-38]: + [9] Symbol [37-38]: name: y type: Float(None, false) qsharp_type: Double io_kind: Default ClassicalDeclarationStmt [53-69]: - symbol_id: 8 + symbol_id: 10 ty_span: [53-57] init_expr: Expr [62-68]: ty: Bool(false) @@ -322,11 +322,11 @@ fn not_equals() { op: Neq lhs: Expr [62-63]: ty: Float(None, false) - kind: SymbolId(6) + kind: SymbolId(8) rhs: Expr [67-68]: ty: Float(None, false) - kind: SymbolId(7) - [8] Symbol [58-59]: + kind: SymbolId(9) + [10] Symbol [58-59]: name: d type: Bool(false) qsharp_type: bool diff --git a/compiler/qsc_qasm3/src/semantic/tests/expression/binary/comparison/int_to_int.rs b/compiler/qsc_qasm3/src/semantic/tests/expression/binary/comparison/int_to_int.rs index b9a35b85f0..a3ff2d14e1 100644 --- a/compiler/qsc_qasm3/src/semantic/tests/expression/binary/comparison/int_to_int.rs +++ b/compiler/qsc_qasm3/src/semantic/tests/expression/binary/comparison/int_to_int.rs @@ -17,29 +17,29 @@ fn greater_than() { input, &expect![[r#" ClassicalDeclarationStmt [9-19]: - symbol_id: 6 + symbol_id: 8 ty_span: [9-12] init_expr: Expr [17-18]: - ty: Int(None, true) + ty: Int(None, false) kind: Lit: Int(5) - [6] Symbol [13-14]: + [8] Symbol [13-14]: name: x type: Int(None, false) qsharp_type: Int io_kind: Default ClassicalDeclarationStmt [28-38]: - symbol_id: 7 + symbol_id: 9 ty_span: [28-31] init_expr: Expr [36-37]: - ty: Int(None, true) + ty: Int(None, false) kind: Lit: Int(3) - [7] Symbol [32-33]: + [9] Symbol [32-33]: name: y type: Int(None, false) qsharp_type: Int io_kind: Default ClassicalDeclarationStmt [47-62]: - symbol_id: 8 + symbol_id: 10 ty_span: [47-51] init_expr: Expr [56-61]: ty: Bool(false) @@ -47,11 +47,11 @@ fn greater_than() { op: Gt lhs: Expr [56-57]: ty: Int(None, false) - kind: SymbolId(6) + kind: SymbolId(8) rhs: Expr [60-61]: ty: Int(None, false) - kind: SymbolId(7) - [8] Symbol [52-53]: + kind: SymbolId(9) + [10] Symbol [52-53]: name: f type: Bool(false) qsharp_type: bool @@ -72,29 +72,29 @@ fn greater_than_equals() { input, &expect![[r#" ClassicalDeclarationStmt [9-19]: - symbol_id: 6 + symbol_id: 8 ty_span: [9-12] init_expr: Expr [17-18]: - ty: Int(None, true) + ty: Int(None, false) kind: Lit: Int(5) - [6] Symbol [13-14]: + [8] Symbol [13-14]: name: x type: Int(None, false) qsharp_type: Int io_kind: Default ClassicalDeclarationStmt [28-38]: - symbol_id: 7 + symbol_id: 9 ty_span: [28-31] init_expr: Expr [36-37]: - ty: Int(None, true) + ty: Int(None, false) kind: Lit: Int(3) - [7] Symbol [32-33]: + [9] Symbol [32-33]: name: y type: Int(None, false) qsharp_type: Int io_kind: Default ClassicalDeclarationStmt [47-63]: - symbol_id: 8 + symbol_id: 10 ty_span: [47-51] init_expr: Expr [56-62]: ty: Bool(false) @@ -102,11 +102,11 @@ fn greater_than_equals() { op: Gte lhs: Expr [56-57]: ty: Int(None, false) - kind: SymbolId(6) + kind: SymbolId(8) rhs: Expr [61-62]: ty: Int(None, false) - kind: SymbolId(7) - [8] Symbol [52-53]: + kind: SymbolId(9) + [10] Symbol [52-53]: name: e type: Bool(false) qsharp_type: bool @@ -127,29 +127,29 @@ fn less_than() { input, &expect![[r#" ClassicalDeclarationStmt [9-19]: - symbol_id: 6 + symbol_id: 8 ty_span: [9-12] init_expr: Expr [17-18]: - ty: Int(None, true) + ty: Int(None, false) kind: Lit: Int(5) - [6] Symbol [13-14]: + [8] Symbol [13-14]: name: x type: Int(None, false) qsharp_type: Int io_kind: Default ClassicalDeclarationStmt [28-38]: - symbol_id: 7 + symbol_id: 9 ty_span: [28-31] init_expr: Expr [36-37]: - ty: Int(None, true) + ty: Int(None, false) kind: Lit: Int(3) - [7] Symbol [32-33]: + [9] Symbol [32-33]: name: y type: Int(None, false) qsharp_type: Int io_kind: Default ClassicalDeclarationStmt [47-62]: - symbol_id: 8 + symbol_id: 10 ty_span: [47-51] init_expr: Expr [56-61]: ty: Bool(false) @@ -157,11 +157,11 @@ fn less_than() { op: Lt lhs: Expr [56-57]: ty: Int(None, false) - kind: SymbolId(6) + kind: SymbolId(8) rhs: Expr [60-61]: ty: Int(None, false) - kind: SymbolId(7) - [8] Symbol [52-53]: + kind: SymbolId(9) + [10] Symbol [52-53]: name: a type: Bool(false) qsharp_type: bool @@ -182,29 +182,29 @@ fn less_than_equals() { input, &expect![[r#" ClassicalDeclarationStmt [9-19]: - symbol_id: 6 + symbol_id: 8 ty_span: [9-12] init_expr: Expr [17-18]: - ty: Int(None, true) + ty: Int(None, false) kind: Lit: Int(5) - [6] Symbol [13-14]: + [8] Symbol [13-14]: name: x type: Int(None, false) qsharp_type: Int io_kind: Default ClassicalDeclarationStmt [28-38]: - symbol_id: 7 + symbol_id: 9 ty_span: [28-31] init_expr: Expr [36-37]: - ty: Int(None, true) + ty: Int(None, false) kind: Lit: Int(3) - [7] Symbol [32-33]: + [9] Symbol [32-33]: name: y type: Int(None, false) qsharp_type: Int io_kind: Default ClassicalDeclarationStmt [47-63]: - symbol_id: 8 + symbol_id: 10 ty_span: [47-51] init_expr: Expr [56-62]: ty: Bool(false) @@ -212,11 +212,11 @@ fn less_than_equals() { op: Lte lhs: Expr [56-57]: ty: Int(None, false) - kind: SymbolId(6) + kind: SymbolId(8) rhs: Expr [61-62]: ty: Int(None, false) - kind: SymbolId(7) - [8] Symbol [52-53]: + kind: SymbolId(9) + [10] Symbol [52-53]: name: c type: Bool(false) qsharp_type: bool @@ -237,29 +237,29 @@ fn equals() { input, &expect![[r#" ClassicalDeclarationStmt [9-19]: - symbol_id: 6 + symbol_id: 8 ty_span: [9-12] init_expr: Expr [17-18]: - ty: Int(None, true) + ty: Int(None, false) kind: Lit: Int(5) - [6] Symbol [13-14]: + [8] Symbol [13-14]: name: x type: Int(None, false) qsharp_type: Int io_kind: Default ClassicalDeclarationStmt [28-38]: - symbol_id: 7 + symbol_id: 9 ty_span: [28-31] init_expr: Expr [36-37]: - ty: Int(None, true) + ty: Int(None, false) kind: Lit: Int(3) - [7] Symbol [32-33]: + [9] Symbol [32-33]: name: y type: Int(None, false) qsharp_type: Int io_kind: Default ClassicalDeclarationStmt [47-63]: - symbol_id: 8 + symbol_id: 10 ty_span: [47-51] init_expr: Expr [56-62]: ty: Bool(false) @@ -267,11 +267,11 @@ fn equals() { op: Eq lhs: Expr [56-57]: ty: Int(None, false) - kind: SymbolId(6) + kind: SymbolId(8) rhs: Expr [61-62]: ty: Int(None, false) - kind: SymbolId(7) - [8] Symbol [52-53]: + kind: SymbolId(9) + [10] Symbol [52-53]: name: b type: Bool(false) qsharp_type: bool @@ -292,29 +292,29 @@ fn not_equals() { input, &expect![[r#" ClassicalDeclarationStmt [9-19]: - symbol_id: 6 + symbol_id: 8 ty_span: [9-12] init_expr: Expr [17-18]: - ty: Int(None, true) + ty: Int(None, false) kind: Lit: Int(5) - [6] Symbol [13-14]: + [8] Symbol [13-14]: name: x type: Int(None, false) qsharp_type: Int io_kind: Default ClassicalDeclarationStmt [28-38]: - symbol_id: 7 + symbol_id: 9 ty_span: [28-31] init_expr: Expr [36-37]: - ty: Int(None, true) + ty: Int(None, false) kind: Lit: Int(3) - [7] Symbol [32-33]: + [9] Symbol [32-33]: name: y type: Int(None, false) qsharp_type: Int io_kind: Default ClassicalDeclarationStmt [47-63]: - symbol_id: 8 + symbol_id: 10 ty_span: [47-51] init_expr: Expr [56-62]: ty: Bool(false) @@ -322,11 +322,11 @@ fn not_equals() { op: Neq lhs: Expr [56-57]: ty: Int(None, false) - kind: SymbolId(6) + kind: SymbolId(8) rhs: Expr [61-62]: ty: Int(None, false) - kind: SymbolId(7) - [8] Symbol [52-53]: + kind: SymbolId(9) + [10] Symbol [52-53]: name: d type: Bool(false) qsharp_type: bool diff --git a/compiler/qsc_qasm3/src/semantic/tests/expression/binary/comparison/uint_to_uint.rs b/compiler/qsc_qasm3/src/semantic/tests/expression/binary/comparison/uint_to_uint.rs index ba39fdf302..37847ee8e2 100644 --- a/compiler/qsc_qasm3/src/semantic/tests/expression/binary/comparison/uint_to_uint.rs +++ b/compiler/qsc_qasm3/src/semantic/tests/expression/binary/comparison/uint_to_uint.rs @@ -17,29 +17,29 @@ fn greater_than() { input, &expect![[r#" ClassicalDeclarationStmt [9-20]: - symbol_id: 6 + symbol_id: 8 ty_span: [9-13] init_expr: Expr [18-19]: ty: UInt(None, true) kind: Lit: Int(5) - [6] Symbol [14-15]: + [8] Symbol [14-15]: name: x type: UInt(None, false) qsharp_type: Int io_kind: Default ClassicalDeclarationStmt [29-40]: - symbol_id: 7 + symbol_id: 9 ty_span: [29-33] init_expr: Expr [38-39]: ty: UInt(None, true) kind: Lit: Int(3) - [7] Symbol [34-35]: + [9] Symbol [34-35]: name: y type: UInt(None, false) qsharp_type: Int io_kind: Default ClassicalDeclarationStmt [49-64]: - symbol_id: 8 + symbol_id: 10 ty_span: [49-53] init_expr: Expr [58-63]: ty: Bool(false) @@ -47,11 +47,11 @@ fn greater_than() { op: Gt lhs: Expr [58-59]: ty: UInt(None, false) - kind: SymbolId(6) + kind: SymbolId(8) rhs: Expr [62-63]: ty: UInt(None, false) - kind: SymbolId(7) - [8] Symbol [54-55]: + kind: SymbolId(9) + [10] Symbol [54-55]: name: f type: Bool(false) qsharp_type: bool @@ -72,29 +72,29 @@ fn greater_than_equals() { input, &expect![[r#" ClassicalDeclarationStmt [9-20]: - symbol_id: 6 + symbol_id: 8 ty_span: [9-13] init_expr: Expr [18-19]: ty: UInt(None, true) kind: Lit: Int(5) - [6] Symbol [14-15]: + [8] Symbol [14-15]: name: x type: UInt(None, false) qsharp_type: Int io_kind: Default ClassicalDeclarationStmt [29-40]: - symbol_id: 7 + symbol_id: 9 ty_span: [29-33] init_expr: Expr [38-39]: ty: UInt(None, true) kind: Lit: Int(3) - [7] Symbol [34-35]: + [9] Symbol [34-35]: name: y type: UInt(None, false) qsharp_type: Int io_kind: Default ClassicalDeclarationStmt [49-65]: - symbol_id: 8 + symbol_id: 10 ty_span: [49-53] init_expr: Expr [58-64]: ty: Bool(false) @@ -102,11 +102,11 @@ fn greater_than_equals() { op: Gte lhs: Expr [58-59]: ty: UInt(None, false) - kind: SymbolId(6) + kind: SymbolId(8) rhs: Expr [63-64]: ty: UInt(None, false) - kind: SymbolId(7) - [8] Symbol [54-55]: + kind: SymbolId(9) + [10] Symbol [54-55]: name: e type: Bool(false) qsharp_type: bool @@ -127,29 +127,29 @@ fn less_than() { input, &expect![[r#" ClassicalDeclarationStmt [9-20]: - symbol_id: 6 + symbol_id: 8 ty_span: [9-13] init_expr: Expr [18-19]: ty: UInt(None, true) kind: Lit: Int(5) - [6] Symbol [14-15]: + [8] Symbol [14-15]: name: x type: UInt(None, false) qsharp_type: Int io_kind: Default ClassicalDeclarationStmt [29-40]: - symbol_id: 7 + symbol_id: 9 ty_span: [29-33] init_expr: Expr [38-39]: ty: UInt(None, true) kind: Lit: Int(3) - [7] Symbol [34-35]: + [9] Symbol [34-35]: name: y type: UInt(None, false) qsharp_type: Int io_kind: Default ClassicalDeclarationStmt [49-64]: - symbol_id: 8 + symbol_id: 10 ty_span: [49-53] init_expr: Expr [58-63]: ty: Bool(false) @@ -157,11 +157,11 @@ fn less_than() { op: Lt lhs: Expr [58-59]: ty: UInt(None, false) - kind: SymbolId(6) + kind: SymbolId(8) rhs: Expr [62-63]: ty: UInt(None, false) - kind: SymbolId(7) - [8] Symbol [54-55]: + kind: SymbolId(9) + [10] Symbol [54-55]: name: a type: Bool(false) qsharp_type: bool @@ -182,29 +182,29 @@ fn less_than_equals() { input, &expect![[r#" ClassicalDeclarationStmt [9-20]: - symbol_id: 6 + symbol_id: 8 ty_span: [9-13] init_expr: Expr [18-19]: ty: UInt(None, true) kind: Lit: Int(5) - [6] Symbol [14-15]: + [8] Symbol [14-15]: name: x type: UInt(None, false) qsharp_type: Int io_kind: Default ClassicalDeclarationStmt [29-40]: - symbol_id: 7 + symbol_id: 9 ty_span: [29-33] init_expr: Expr [38-39]: ty: UInt(None, true) kind: Lit: Int(3) - [7] Symbol [34-35]: + [9] Symbol [34-35]: name: y type: UInt(None, false) qsharp_type: Int io_kind: Default ClassicalDeclarationStmt [49-65]: - symbol_id: 8 + symbol_id: 10 ty_span: [49-53] init_expr: Expr [58-64]: ty: Bool(false) @@ -212,11 +212,11 @@ fn less_than_equals() { op: Lte lhs: Expr [58-59]: ty: UInt(None, false) - kind: SymbolId(6) + kind: SymbolId(8) rhs: Expr [63-64]: ty: UInt(None, false) - kind: SymbolId(7) - [8] Symbol [54-55]: + kind: SymbolId(9) + [10] Symbol [54-55]: name: c type: Bool(false) qsharp_type: bool @@ -237,29 +237,29 @@ fn equals() { input, &expect![[r#" ClassicalDeclarationStmt [9-20]: - symbol_id: 6 + symbol_id: 8 ty_span: [9-13] init_expr: Expr [18-19]: ty: UInt(None, true) kind: Lit: Int(5) - [6] Symbol [14-15]: + [8] Symbol [14-15]: name: x type: UInt(None, false) qsharp_type: Int io_kind: Default ClassicalDeclarationStmt [29-40]: - symbol_id: 7 + symbol_id: 9 ty_span: [29-33] init_expr: Expr [38-39]: ty: UInt(None, true) kind: Lit: Int(3) - [7] Symbol [34-35]: + [9] Symbol [34-35]: name: y type: UInt(None, false) qsharp_type: Int io_kind: Default ClassicalDeclarationStmt [49-65]: - symbol_id: 8 + symbol_id: 10 ty_span: [49-53] init_expr: Expr [58-64]: ty: Bool(false) @@ -267,11 +267,11 @@ fn equals() { op: Eq lhs: Expr [58-59]: ty: UInt(None, false) - kind: SymbolId(6) + kind: SymbolId(8) rhs: Expr [63-64]: ty: UInt(None, false) - kind: SymbolId(7) - [8] Symbol [54-55]: + kind: SymbolId(9) + [10] Symbol [54-55]: name: b type: Bool(false) qsharp_type: bool @@ -292,29 +292,29 @@ fn not_equals() { input, &expect![[r#" ClassicalDeclarationStmt [9-20]: - symbol_id: 6 + symbol_id: 8 ty_span: [9-13] init_expr: Expr [18-19]: ty: UInt(None, true) kind: Lit: Int(5) - [6] Symbol [14-15]: + [8] Symbol [14-15]: name: x type: UInt(None, false) qsharp_type: Int io_kind: Default ClassicalDeclarationStmt [29-40]: - symbol_id: 7 + symbol_id: 9 ty_span: [29-33] init_expr: Expr [38-39]: ty: UInt(None, true) kind: Lit: Int(3) - [7] Symbol [34-35]: + [9] Symbol [34-35]: name: y type: UInt(None, false) qsharp_type: Int io_kind: Default ClassicalDeclarationStmt [49-65]: - symbol_id: 8 + symbol_id: 10 ty_span: [49-53] init_expr: Expr [58-64]: ty: Bool(false) @@ -322,11 +322,11 @@ fn not_equals() { op: Neq lhs: Expr [58-59]: ty: UInt(None, false) - kind: SymbolId(6) + kind: SymbolId(8) rhs: Expr [63-64]: ty: UInt(None, false) - kind: SymbolId(7) - [8] Symbol [54-55]: + kind: SymbolId(9) + [10] Symbol [54-55]: name: d type: Bool(false) qsharp_type: bool diff --git a/compiler/qsc_qasm3/src/semantic/tests/expression/binary/ident.rs b/compiler/qsc_qasm3/src/semantic/tests/expression/binary/ident.rs index 60b21ef238..a88ba0f04f 100644 --- a/compiler/qsc_qasm3/src/semantic/tests/expression/binary/ident.rs +++ b/compiler/qsc_qasm3/src/semantic/tests/expression/binary/ident.rs @@ -15,30 +15,30 @@ fn mutable_int_idents_without_width_can_be_multiplied() { check_stmt_kinds( input, &expect![[r#" - ClassicalDeclarationStmt [9-19]: - symbol_id: 6 - ty_span: [9-12] - init_expr: Expr [17-18]: - ty: Int(None, true) - kind: Lit: Int(5) - ClassicalDeclarationStmt [28-38]: - symbol_id: 7 - ty_span: [28-31] - init_expr: Expr [36-37]: - ty: Int(None, true) - kind: Lit: Int(3) - ExprStmt [47-53]: - expr: Expr [47-52]: - ty: Int(None, false) - kind: BinaryOpExpr: - op: Mul - lhs: Expr [47-48]: - ty: Int(None, false) - kind: SymbolId(6) - rhs: Expr [51-52]: - ty: Int(None, false) - kind: SymbolId(7) - "#]], + ClassicalDeclarationStmt [9-19]: + symbol_id: 8 + ty_span: [9-12] + init_expr: Expr [17-18]: + ty: Int(None, false) + kind: Lit: Int(5) + ClassicalDeclarationStmt [28-38]: + symbol_id: 9 + ty_span: [28-31] + init_expr: Expr [36-37]: + ty: Int(None, false) + kind: Lit: Int(3) + ExprStmt [47-53]: + expr: Expr [47-52]: + ty: Int(None, false) + kind: BinaryOpExpr: + op: Mul + lhs: Expr [47-48]: + ty: Int(None, false) + kind: SymbolId(8) + rhs: Expr [51-52]: + ty: Int(None, false) + kind: SymbolId(9) + "#]], ); } @@ -53,30 +53,30 @@ fn const_int_idents_without_width_can_be_multiplied() { check_stmt_kinds( input, &expect![[r#" - ClassicalDeclarationStmt [9-25]: - symbol_id: 6 - ty_span: [15-18] - init_expr: Expr [23-24]: - ty: Int(None, true) - kind: Lit: Int(5) - ClassicalDeclarationStmt [34-50]: - symbol_id: 7 - ty_span: [40-43] - init_expr: Expr [48-49]: - ty: Int(None, true) - kind: Lit: Int(3) - ExprStmt [59-65]: - expr: Expr [59-64]: - ty: Int(None, true) - kind: BinaryOpExpr: - op: Mul - lhs: Expr [59-60]: - ty: Int(None, true) - kind: SymbolId(6) - rhs: Expr [63-64]: - ty: Int(None, true) - kind: SymbolId(7) - "#]], + ClassicalDeclarationStmt [9-25]: + symbol_id: 8 + ty_span: [15-18] + init_expr: Expr [23-24]: + ty: Int(None, true) + kind: Lit: Int(5) + ClassicalDeclarationStmt [34-50]: + symbol_id: 9 + ty_span: [40-43] + init_expr: Expr [48-49]: + ty: Int(None, true) + kind: Lit: Int(3) + ExprStmt [59-65]: + expr: Expr [59-64]: + ty: Int(None, true) + kind: BinaryOpExpr: + op: Mul + lhs: Expr [59-60]: + ty: Int(None, true) + kind: SymbolId(8) + rhs: Expr [63-64]: + ty: Int(None, true) + kind: SymbolId(9) + "#]], ); } @@ -91,30 +91,30 @@ fn const_and_mut_int_idents_without_width_can_be_multiplied() { check_stmt_kinds( input, &expect![[r#" - ClassicalDeclarationStmt [9-19]: - symbol_id: 6 - ty_span: [9-12] - init_expr: Expr [17-18]: - ty: Int(None, true) - kind: Lit: Int(5) - ClassicalDeclarationStmt [28-44]: - symbol_id: 7 - ty_span: [34-37] - init_expr: Expr [42-43]: - ty: Int(None, true) - kind: Lit: Int(3) - ExprStmt [53-59]: - expr: Expr [53-58]: - ty: Int(None, false) - kind: BinaryOpExpr: - op: Mul - lhs: Expr [53-54]: - ty: Int(None, false) - kind: SymbolId(6) - rhs: Expr [57-58]: - ty: Int(None, true) - kind: SymbolId(7) - "#]], + ClassicalDeclarationStmt [9-19]: + symbol_id: 8 + ty_span: [9-12] + init_expr: Expr [17-18]: + ty: Int(None, false) + kind: Lit: Int(5) + ClassicalDeclarationStmt [28-44]: + symbol_id: 9 + ty_span: [34-37] + init_expr: Expr [42-43]: + ty: Int(None, true) + kind: Lit: Int(3) + ExprStmt [53-59]: + expr: Expr [53-58]: + ty: Int(None, false) + kind: BinaryOpExpr: + op: Mul + lhs: Expr [53-54]: + ty: Int(None, false) + kind: SymbolId(8) + rhs: Expr [57-58]: + ty: Int(None, false) + kind: SymbolId(9) + "#]], ); } @@ -129,33 +129,33 @@ fn const_int_idents_widthless_lhs_can_be_multiplied_by_explicit_width_int() { check_stmt_kinds( input, &expect![[r#" - ClassicalDeclarationStmt [9-29]: - symbol_id: 6 - ty_span: [15-22] - init_expr: Expr [27-28]: - ty: Int(Some(32), true) - kind: Lit: Int(5) - ClassicalDeclarationStmt [38-54]: - symbol_id: 7 - ty_span: [44-47] - init_expr: Expr [52-53]: - ty: Int(None, true) - kind: Lit: Int(3) - ExprStmt [63-69]: - expr: Expr [63-68]: - ty: Int(None, true) - kind: BinaryOpExpr: - op: Mul - lhs: Expr [63-64]: - ty: Int(None, true) - kind: Cast [0-0]: + ClassicalDeclarationStmt [9-29]: + symbol_id: 8 + ty_span: [15-22] + init_expr: Expr [27-28]: + ty: Int(Some(32), true) + kind: Lit: Int(5) + ClassicalDeclarationStmt [38-54]: + symbol_id: 9 + ty_span: [44-47] + init_expr: Expr [52-53]: + ty: Int(None, true) + kind: Lit: Int(3) + ExprStmt [63-69]: + expr: Expr [63-68]: + ty: Int(None, true) + kind: BinaryOpExpr: + op: Mul + lhs: Expr [63-64]: + ty: Int(None, true) + kind: Cast [0-0]: + ty: Int(None, true) + expr: Expr [63-64]: + ty: Int(Some(32), true) + kind: SymbolId(8) + rhs: Expr [67-68]: ty: Int(None, true) - expr: Expr [63-64]: - ty: Int(Some(32), true) - kind: SymbolId(6) - rhs: Expr [67-68]: - ty: Int(None, true) - kind: SymbolId(7) - "#]], + kind: SymbolId(9) + "#]], ); } diff --git a/compiler/qsc_qasm3/src/semantic/tests/expression/implicit_cast_from_angle.rs b/compiler/qsc_qasm3/src/semantic/tests/expression/implicit_cast_from_angle.rs index 02c6cb12cc..fb6601199e 100644 --- a/compiler/qsc_qasm3/src/semantic/tests/expression/implicit_cast_from_angle.rs +++ b/compiler/qsc_qasm3/src/semantic/tests/expression/implicit_cast_from_angle.rs @@ -15,33 +15,33 @@ fn to_bit_implicitly() { check_classical_decls( input, &expect![[r#" - ClassicalDeclarationStmt [9-23]: - symbol_id: 6 - ty_span: [9-14] - init_expr: Expr [19-22]: - ty: Angle(None, true) - kind: Lit: Float(42.0) - [6] Symbol [15-16]: - name: x - type: Angle(None, false) - qsharp_type: Double - io_kind: Default - ClassicalDeclarationStmt [32-42]: - symbol_id: 7 - ty_span: [32-35] - init_expr: Expr [40-41]: - ty: Bit(false) - kind: Cast [0-0]: + ClassicalDeclarationStmt [9-23]: + symbol_id: 8 + ty_span: [9-14] + init_expr: Expr [19-22]: + ty: Angle(None, true) + kind: Lit: Float(42.0) + [8] Symbol [15-16]: + name: x + type: Angle(None, false) + qsharp_type: Double + io_kind: Default + ClassicalDeclarationStmt [32-42]: + symbol_id: 9 + ty_span: [32-35] + init_expr: Expr [40-41]: ty: Bit(false) - expr: Expr [40-41]: - ty: Angle(None, false) - kind: SymbolId(6) - [7] Symbol [36-37]: - name: y - type: Bit(false) - qsharp_type: Result - io_kind: Default - "#]], + kind: Cast [0-0]: + ty: Bit(false) + expr: Expr [40-41]: + ty: Angle(None, false) + kind: SymbolId(8) + [9] Symbol [36-37]: + name: y + type: Bit(false) + qsharp_type: Result + io_kind: Default + "#]], ); } @@ -55,33 +55,33 @@ fn explicit_width_to_bit_implicitly_fails() { check_classical_decls( input, &expect![[r#" - ClassicalDeclarationStmt [9-27]: - symbol_id: 6 - ty_span: [9-18] - init_expr: Expr [23-26]: - ty: Angle(Some(64), true) - kind: Lit: Float(42.0) - [6] Symbol [19-20]: - name: x - type: Angle(Some(64), false) - qsharp_type: Double - io_kind: Default - ClassicalDeclarationStmt [36-46]: - symbol_id: 7 - ty_span: [36-39] - init_expr: Expr [44-45]: - ty: Bit(false) - kind: Cast [0-0]: + ClassicalDeclarationStmt [9-27]: + symbol_id: 8 + ty_span: [9-18] + init_expr: Expr [23-26]: + ty: Angle(Some(64), true) + kind: Lit: Float(42.0) + [8] Symbol [19-20]: + name: x + type: Angle(Some(64), false) + qsharp_type: Double + io_kind: Default + ClassicalDeclarationStmt [36-46]: + symbol_id: 9 + ty_span: [36-39] + init_expr: Expr [44-45]: ty: Bit(false) - expr: Expr [44-45]: - ty: Angle(Some(64), false) - kind: SymbolId(6) - [7] Symbol [40-41]: - name: y - type: Bit(false) - qsharp_type: Result - io_kind: Default - "#]], + kind: Cast [0-0]: + ty: Bit(false) + expr: Expr [44-45]: + ty: Angle(Some(64), false) + kind: SymbolId(8) + [9] Symbol [40-41]: + name: y + type: Bit(false) + qsharp_type: Result + io_kind: Default + "#]], ); } @@ -94,33 +94,33 @@ fn to_bool_implicitly() { check_classical_decls( input, &expect![[r#" - ClassicalDeclarationStmt [9-23]: - symbol_id: 6 - ty_span: [9-14] - init_expr: Expr [19-22]: - ty: Angle(None, true) - kind: Lit: Float(42.0) - [6] Symbol [15-16]: - name: x - type: Angle(None, false) - qsharp_type: Double - io_kind: Default - ClassicalDeclarationStmt [32-43]: - symbol_id: 7 - ty_span: [32-36] - init_expr: Expr [41-42]: - ty: Bool(false) - kind: Cast [0-0]: + ClassicalDeclarationStmt [9-23]: + symbol_id: 8 + ty_span: [9-14] + init_expr: Expr [19-22]: + ty: Angle(None, true) + kind: Lit: Float(42.0) + [8] Symbol [15-16]: + name: x + type: Angle(None, false) + qsharp_type: Double + io_kind: Default + ClassicalDeclarationStmt [32-43]: + symbol_id: 9 + ty_span: [32-36] + init_expr: Expr [41-42]: ty: Bool(false) - expr: Expr [41-42]: - ty: Angle(None, false) - kind: SymbolId(6) - [7] Symbol [37-38]: - name: y - type: Bool(false) - qsharp_type: bool - io_kind: Default - "#]], + kind: Cast [0-0]: + ty: Bool(false) + expr: Expr [41-42]: + ty: Angle(None, false) + kind: SymbolId(8) + [9] Symbol [37-38]: + name: y + type: Bool(false) + qsharp_type: bool + io_kind: Default + "#]], ); } @@ -140,7 +140,7 @@ fn to_implicit_int_implicitly_fails() { Stmt [9-23]: annotations: kind: ClassicalDeclarationStmt [9-23]: - symbol_id: 6 + symbol_id: 8 ty_span: [9-14] init_expr: Expr [19-22]: ty: Angle(None, true) @@ -148,19 +148,19 @@ fn to_implicit_int_implicitly_fails() { Stmt [32-42]: annotations: kind: ClassicalDeclarationStmt [32-42]: - symbol_id: 7 + symbol_id: 9 ty_span: [32-35] init_expr: Expr [40-41]: ty: Angle(None, false) - kind: SymbolId(6) + kind: SymbolId(8) [Qsc.Qasm3.Compile.CannotCast x Cannot cast expression of type Angle(None, false) to type Int(None, false) - ,-[test:3:9] + ,-[test:3:17] 2 | angle x = 42.; 3 | int y = x; - : ^^^^^^^^^^ + : ^ 4 | `---- ]"#]], @@ -183,7 +183,7 @@ fn to_explicit_int_implicitly_fails() { Stmt [9-23]: annotations: kind: ClassicalDeclarationStmt [9-23]: - symbol_id: 6 + symbol_id: 8 ty_span: [9-14] init_expr: Expr [19-22]: ty: Angle(None, true) @@ -191,20 +191,20 @@ fn to_explicit_int_implicitly_fails() { Stmt [32-46]: annotations: kind: ClassicalDeclarationStmt [32-46]: - symbol_id: 7 + symbol_id: 9 ty_span: [32-39] init_expr: Expr [44-45]: ty: Angle(None, false) - kind: SymbolId(6) + kind: SymbolId(8) [Qsc.Qasm3.Compile.CannotCast x Cannot cast expression of type Angle(None, false) to type Int(Some(32), | false) - ,-[test:3:9] + ,-[test:3:21] 2 | angle x = 42.; 3 | int[32] y = x; - : ^^^^^^^^^^^^^^ + : ^ 4 | `---- ]"#]], @@ -227,7 +227,7 @@ fn to_implicit_uint_implicitly_fails() { Stmt [9-23]: annotations: kind: ClassicalDeclarationStmt [9-23]: - symbol_id: 6 + symbol_id: 8 ty_span: [9-14] init_expr: Expr [19-22]: ty: Angle(None, true) @@ -235,20 +235,20 @@ fn to_implicit_uint_implicitly_fails() { Stmt [32-43]: annotations: kind: ClassicalDeclarationStmt [32-43]: - symbol_id: 7 + symbol_id: 9 ty_span: [32-36] init_expr: Expr [41-42]: ty: Angle(None, false) - kind: SymbolId(6) + kind: SymbolId(8) [Qsc.Qasm3.Compile.CannotCast x Cannot cast expression of type Angle(None, false) to type UInt(None, | false) - ,-[test:3:9] + ,-[test:3:18] 2 | angle x = 42.; 3 | uint y = x; - : ^^^^^^^^^^^ + : ^ 4 | `---- ]"#]], @@ -271,7 +271,7 @@ fn negative_lit_to_implicit_uint_implicitly_fails() { Stmt [9-24]: annotations: kind: ClassicalDeclarationStmt [9-24]: - symbol_id: 6 + symbol_id: 8 ty_span: [9-14] init_expr: Expr [20-23]: ty: Angle(None, false) @@ -287,20 +287,20 @@ fn negative_lit_to_implicit_uint_implicitly_fails() { Stmt [33-44]: annotations: kind: ClassicalDeclarationStmt [33-44]: - symbol_id: 7 + symbol_id: 9 ty_span: [33-37] init_expr: Expr [42-43]: ty: Angle(None, false) - kind: SymbolId(6) + kind: SymbolId(8) [Qsc.Qasm3.Compile.CannotCast x Cannot cast expression of type Angle(None, false) to type UInt(None, | false) - ,-[test:3:9] + ,-[test:3:18] 2 | angle x = -42.; 3 | uint y = x; - : ^^^^^^^^^^^ + : ^ 4 | `---- ]"#]], @@ -323,7 +323,7 @@ fn to_explicit_uint_implicitly_fails() { Stmt [9-23]: annotations: kind: ClassicalDeclarationStmt [9-23]: - symbol_id: 6 + symbol_id: 8 ty_span: [9-14] init_expr: Expr [19-22]: ty: Angle(None, true) @@ -331,20 +331,20 @@ fn to_explicit_uint_implicitly_fails() { Stmt [32-47]: annotations: kind: ClassicalDeclarationStmt [32-47]: - symbol_id: 7 + symbol_id: 9 ty_span: [32-40] init_expr: Expr [45-46]: ty: Angle(None, false) - kind: SymbolId(6) + kind: SymbolId(8) [Qsc.Qasm3.Compile.CannotCast x Cannot cast expression of type Angle(None, false) to type UInt(Some(32), | false) - ,-[test:3:9] + ,-[test:3:22] 2 | angle x = 42.; 3 | uint[32] y = x; - : ^^^^^^^^^^^^^^^ + : ^ 4 | `---- ]"#]], @@ -367,7 +367,7 @@ fn to_explicit_bigint_implicitly_fails() { Stmt [9-23]: annotations: kind: ClassicalDeclarationStmt [9-23]: - symbol_id: 6 + symbol_id: 8 ty_span: [9-14] init_expr: Expr [19-22]: ty: Angle(None, true) @@ -375,20 +375,20 @@ fn to_explicit_bigint_implicitly_fails() { Stmt [32-46]: annotations: kind: ClassicalDeclarationStmt [32-46]: - symbol_id: 7 + symbol_id: 9 ty_span: [32-39] init_expr: Expr [44-45]: ty: Angle(None, false) - kind: SymbolId(6) + kind: SymbolId(8) [Qsc.Qasm3.Compile.CannotCast x Cannot cast expression of type Angle(None, false) to type Int(Some(65), | false) - ,-[test:3:9] + ,-[test:3:21] 2 | angle x = 42.; 3 | int[65] y = x; - : ^^^^^^^^^^^^^^ + : ^ 4 | `---- ]"#]], @@ -411,7 +411,7 @@ fn to_implicit_float_implicitly_fails() { Stmt [9-23]: annotations: kind: ClassicalDeclarationStmt [9-23]: - symbol_id: 6 + symbol_id: 8 ty_span: [9-14] init_expr: Expr [19-22]: ty: Angle(None, true) @@ -419,20 +419,20 @@ fn to_implicit_float_implicitly_fails() { Stmt [32-44]: annotations: kind: ClassicalDeclarationStmt [32-44]: - symbol_id: 7 + symbol_id: 9 ty_span: [32-37] init_expr: Expr [42-43]: ty: Angle(None, false) - kind: SymbolId(6) + kind: SymbolId(8) [Qsc.Qasm3.Compile.CannotCast x Cannot cast expression of type Angle(None, false) to type Float(None, | false) - ,-[test:3:9] + ,-[test:3:19] 2 | angle x = 42.; 3 | float y = x; - : ^^^^^^^^^^^^ + : ^ 4 | `---- ]"#]], @@ -455,7 +455,7 @@ fn to_explicit_float_implicitly_fails() { Stmt [9-23]: annotations: kind: ClassicalDeclarationStmt [9-23]: - symbol_id: 6 + symbol_id: 8 ty_span: [9-14] init_expr: Expr [19-22]: ty: Angle(None, true) @@ -463,20 +463,20 @@ fn to_explicit_float_implicitly_fails() { Stmt [32-48]: annotations: kind: ClassicalDeclarationStmt [32-48]: - symbol_id: 7 + symbol_id: 9 ty_span: [32-41] init_expr: Expr [46-47]: ty: Angle(None, false) - kind: SymbolId(6) + kind: SymbolId(8) [Qsc.Qasm3.Compile.CannotCast x Cannot cast expression of type Angle(None, false) to type Float(Some(32), | false) - ,-[test:3:9] + ,-[test:3:23] 2 | angle x = 42.; 3 | float[32] y = x; - : ^^^^^^^^^^^^^^^^ + : ^ 4 | `---- ]"#]], @@ -499,7 +499,7 @@ fn to_implicit_complex_implicitly_fails() { Stmt [9-23]: annotations: kind: ClassicalDeclarationStmt [9-23]: - symbol_id: 6 + symbol_id: 8 ty_span: [9-14] init_expr: Expr [19-22]: ty: Angle(None, true) @@ -507,20 +507,20 @@ fn to_implicit_complex_implicitly_fails() { Stmt [32-53]: annotations: kind: ClassicalDeclarationStmt [32-53]: - symbol_id: 7 + symbol_id: 9 ty_span: [32-46] init_expr: Expr [51-52]: ty: Angle(None, false) - kind: SymbolId(6) + kind: SymbolId(8) [Qsc.Qasm3.Compile.CannotCast x Cannot cast expression of type Angle(None, false) to type Complex(None, | false) - ,-[test:3:9] + ,-[test:3:28] 2 | angle x = 42.; 3 | complex[float] y = x; - : ^^^^^^^^^^^^^^^^^^^^^ + : ^ 4 | `---- ]"#]], @@ -543,7 +543,7 @@ fn to_explicit_complex_implicitly_fails() { Stmt [9-23]: annotations: kind: ClassicalDeclarationStmt [9-23]: - symbol_id: 6 + symbol_id: 8 ty_span: [9-14] init_expr: Expr [19-22]: ty: Angle(None, true) @@ -551,20 +551,20 @@ fn to_explicit_complex_implicitly_fails() { Stmt [32-57]: annotations: kind: ClassicalDeclarationStmt [32-57]: - symbol_id: 7 + symbol_id: 9 ty_span: [32-50] init_expr: Expr [55-56]: ty: Angle(None, false) - kind: SymbolId(6) + kind: SymbolId(8) [Qsc.Qasm3.Compile.CannotCast x Cannot cast expression of type Angle(None, false) to type | Complex(Some(32), false) - ,-[test:3:9] + ,-[test:3:32] 2 | angle x = 42.; 3 | complex[float[32]] y = x; - : ^^^^^^^^^^^^^^^^^^^^^^^^^ + : ^ 4 | `---- ]"#]], @@ -582,23 +582,23 @@ fn to_angle_implicitly() { input, &expect![[r#" ClassicalDeclarationStmt [9-23]: - symbol_id: 6 + symbol_id: 8 ty_span: [9-14] init_expr: Expr [19-22]: ty: Angle(None, true) kind: Lit: Float(42.0) - [6] Symbol [15-16]: + [8] Symbol [15-16]: name: x type: Angle(None, false) qsharp_type: Double io_kind: Default ClassicalDeclarationStmt [32-44]: - symbol_id: 7 + symbol_id: 9 ty_span: [32-37] init_expr: Expr [42-43]: ty: Angle(None, false) - kind: SymbolId(6) - [7] Symbol [38-39]: + kind: SymbolId(8) + [9] Symbol [38-39]: name: y type: Angle(None, false) qsharp_type: Double @@ -618,23 +618,23 @@ fn to_explicit_angle_implicitly() { input, &expect![[r#" ClassicalDeclarationStmt [9-23]: - symbol_id: 6 + symbol_id: 8 ty_span: [9-14] init_expr: Expr [19-22]: ty: Angle(None, true) kind: Lit: Float(42.0) - [6] Symbol [15-16]: + [8] Symbol [15-16]: name: x type: Angle(None, false) qsharp_type: Double io_kind: Default ClassicalDeclarationStmt [32-47]: - symbol_id: 7 + symbol_id: 9 ty_span: [32-40] init_expr: Expr [45-46]: ty: Angle(Some(4), false) - kind: SymbolId(6) - [7] Symbol [41-42]: + kind: SymbolId(8) + [9] Symbol [41-42]: name: y type: Angle(Some(4), false) qsharp_type: Double diff --git a/compiler/qsc_qasm3/src/semantic/tests/expression/implicit_cast_from_bit.rs b/compiler/qsc_qasm3/src/semantic/tests/expression/implicit_cast_from_bit.rs index 7798b240e6..9e207073e3 100644 --- a/compiler/qsc_qasm3/src/semantic/tests/expression/implicit_cast_from_bit.rs +++ b/compiler/qsc_qasm3/src/semantic/tests/expression/implicit_cast_from_bit.rs @@ -38,18 +38,18 @@ fn to_bool_implicitly() { input, &expect![[r#" ClassicalDeclarationStmt [10-20]: - symbol_id: 6 + symbol_id: 8 ty_span: [10-13] init_expr: Expr [18-19]: ty: Bit(true) - kind: Lit: Int(1) - [6] Symbol [14-15]: + kind: Lit: Bit(1) + [8] Symbol [14-15]: name: x type: Bit(false) qsharp_type: Result io_kind: Default ClassicalDeclarationStmt [30-41]: - symbol_id: 7 + symbol_id: 9 ty_span: [30-34] init_expr: Expr [39-40]: ty: Bool(false) @@ -57,8 +57,8 @@ fn to_bool_implicitly() { ty: Bool(false) expr: Expr [39-40]: ty: Bit(false) - kind: SymbolId(6) - [7] Symbol [35-36]: + kind: SymbolId(8) + [9] Symbol [35-36]: name: y type: Bool(false) qsharp_type: bool @@ -78,18 +78,18 @@ fn to_implicit_int_implicitly() { input, &expect![[r#" ClassicalDeclarationStmt [9-19]: - symbol_id: 6 + symbol_id: 8 ty_span: [9-12] init_expr: Expr [17-18]: ty: Bit(true) - kind: Lit: Int(1) - [6] Symbol [13-14]: + kind: Lit: Bit(1) + [8] Symbol [13-14]: name: x type: Bit(false) qsharp_type: Result io_kind: Default ClassicalDeclarationStmt [28-38]: - symbol_id: 7 + symbol_id: 9 ty_span: [28-31] init_expr: Expr [36-37]: ty: Int(None, false) @@ -97,8 +97,8 @@ fn to_implicit_int_implicitly() { ty: Int(None, false) expr: Expr [36-37]: ty: Bit(false) - kind: SymbolId(6) - [7] Symbol [32-33]: + kind: SymbolId(8) + [9] Symbol [32-33]: name: y type: Int(None, false) qsharp_type: Int @@ -118,18 +118,18 @@ fn to_explicit_int_implicitly() { input, &expect![[r#" ClassicalDeclarationStmt [9-19]: - symbol_id: 6 + symbol_id: 8 ty_span: [9-12] init_expr: Expr [17-18]: ty: Bit(true) - kind: Lit: Int(1) - [6] Symbol [13-14]: + kind: Lit: Bit(1) + [8] Symbol [13-14]: name: x type: Bit(false) qsharp_type: Result io_kind: Default ClassicalDeclarationStmt [28-42]: - symbol_id: 7 + symbol_id: 9 ty_span: [28-35] init_expr: Expr [40-41]: ty: Int(Some(32), false) @@ -137,8 +137,8 @@ fn to_explicit_int_implicitly() { ty: Int(Some(32), false) expr: Expr [40-41]: ty: Bit(false) - kind: SymbolId(6) - [7] Symbol [36-37]: + kind: SymbolId(8) + [9] Symbol [36-37]: name: y type: Int(Some(32), false) qsharp_type: Int @@ -158,18 +158,18 @@ fn to_implicit_uint_implicitly() { input, &expect![[r#" ClassicalDeclarationStmt [9-19]: - symbol_id: 6 + symbol_id: 8 ty_span: [9-12] init_expr: Expr [17-18]: ty: Bit(true) - kind: Lit: Int(1) - [6] Symbol [13-14]: + kind: Lit: Bit(1) + [8] Symbol [13-14]: name: x type: Bit(false) qsharp_type: Result io_kind: Default ClassicalDeclarationStmt [28-39]: - symbol_id: 7 + symbol_id: 9 ty_span: [28-32] init_expr: Expr [37-38]: ty: UInt(None, false) @@ -177,8 +177,8 @@ fn to_implicit_uint_implicitly() { ty: UInt(None, false) expr: Expr [37-38]: ty: Bit(false) - kind: SymbolId(6) - [7] Symbol [33-34]: + kind: SymbolId(8) + [9] Symbol [33-34]: name: y type: UInt(None, false) qsharp_type: Int @@ -198,18 +198,18 @@ fn to_explicit_uint_implicitly() { input, &expect![[r#" ClassicalDeclarationStmt [9-19]: - symbol_id: 6 + symbol_id: 8 ty_span: [9-12] init_expr: Expr [17-18]: ty: Bit(true) - kind: Lit: Int(1) - [6] Symbol [13-14]: + kind: Lit: Bit(1) + [8] Symbol [13-14]: name: x type: Bit(false) qsharp_type: Result io_kind: Default ClassicalDeclarationStmt [28-43]: - symbol_id: 7 + symbol_id: 9 ty_span: [28-36] init_expr: Expr [41-42]: ty: UInt(Some(32), false) @@ -217,8 +217,8 @@ fn to_explicit_uint_implicitly() { ty: UInt(Some(32), false) expr: Expr [41-42]: ty: Bit(false) - kind: SymbolId(6) - [7] Symbol [37-38]: + kind: SymbolId(8) + [9] Symbol [37-38]: name: y type: UInt(Some(32), false) qsharp_type: Int @@ -238,18 +238,18 @@ fn to_explicit_bigint_implicitly() { input, &expect![[r#" ClassicalDeclarationStmt [9-19]: - symbol_id: 6 + symbol_id: 8 ty_span: [9-12] init_expr: Expr [17-18]: ty: Bit(true) - kind: Lit: Int(1) - [6] Symbol [13-14]: + kind: Lit: Bit(1) + [8] Symbol [13-14]: name: x type: Bit(false) qsharp_type: Result io_kind: Default ClassicalDeclarationStmt [28-42]: - symbol_id: 7 + symbol_id: 9 ty_span: [28-35] init_expr: Expr [40-41]: ty: Int(Some(65), false) @@ -257,8 +257,8 @@ fn to_explicit_bigint_implicitly() { ty: Int(Some(65), false) expr: Expr [40-41]: ty: Bit(false) - kind: SymbolId(6) - [7] Symbol [36-37]: + kind: SymbolId(8) + [9] Symbol [36-37]: name: y type: Int(Some(65), false) qsharp_type: BigInt @@ -283,27 +283,27 @@ fn to_implicit_float_implicitly_fails() { Stmt [9-19]: annotations: kind: ClassicalDeclarationStmt [9-19]: - symbol_id: 6 + symbol_id: 8 ty_span: [9-12] init_expr: Expr [17-18]: ty: Bit(true) - kind: Lit: Int(1) + kind: Lit: Bit(1) Stmt [28-40]: annotations: kind: ClassicalDeclarationStmt [28-40]: - symbol_id: 7 + symbol_id: 9 ty_span: [28-33] init_expr: Expr [38-39]: ty: Bit(false) - kind: SymbolId(6) + kind: SymbolId(8) [Qsc.Qasm3.Compile.CannotCast x Cannot cast expression of type Bit(false) to type Float(None, false) - ,-[test:3:9] + ,-[test:3:19] 2 | bit x = 1; 3 | float y = x; - : ^^^^^^^^^^^^ + : ^ 4 | `---- ]"#]], diff --git a/compiler/qsc_qasm3/src/semantic/tests/expression/implicit_cast_from_bool.rs b/compiler/qsc_qasm3/src/semantic/tests/expression/implicit_cast_from_bool.rs index cd4178ebe8..5f444fb295 100644 --- a/compiler/qsc_qasm3/src/semantic/tests/expression/implicit_cast_from_bool.rs +++ b/compiler/qsc_qasm3/src/semantic/tests/expression/implicit_cast_from_bool.rs @@ -15,33 +15,33 @@ fn to_bit_implicitly() { check_classical_decls( input, &expect![[r#" - ClassicalDeclarationStmt [9-23]: - symbol_id: 6 - ty_span: [9-13] - init_expr: Expr [18-22]: - ty: Bool(true) - kind: Lit: Bool(true) - [6] Symbol [14-15]: - name: x - type: Bool(false) - qsharp_type: bool - io_kind: Default - ClassicalDeclarationStmt [32-42]: - symbol_id: 7 - ty_span: [32-35] - init_expr: Expr [40-41]: - ty: Bit(false) - kind: Cast [0-0]: + ClassicalDeclarationStmt [9-23]: + symbol_id: 8 + ty_span: [9-13] + init_expr: Expr [18-22]: + ty: Bool(false) + kind: Lit: Bool(true) + [8] Symbol [14-15]: + name: x + type: Bool(false) + qsharp_type: bool + io_kind: Default + ClassicalDeclarationStmt [32-42]: + symbol_id: 9 + ty_span: [32-35] + init_expr: Expr [40-41]: ty: Bit(false) - expr: Expr [40-41]: - ty: Bool(false) - kind: SymbolId(6) - [7] Symbol [36-37]: - name: y - type: Bit(false) - qsharp_type: Result - io_kind: Default - "#]], + kind: Cast [0-0]: + ty: Bit(false) + expr: Expr [40-41]: + ty: Bool(false) + kind: SymbolId(8) + [9] Symbol [36-37]: + name: y + type: Bit(false) + qsharp_type: Result + io_kind: Default + "#]], ); } @@ -55,33 +55,33 @@ fn to_implicit_int_implicitly() { check_classical_decls( input, &expect![[r#" - ClassicalDeclarationStmt [9-23]: - symbol_id: 6 - ty_span: [9-13] - init_expr: Expr [18-22]: - ty: Bool(true) - kind: Lit: Bool(true) - [6] Symbol [14-15]: - name: x - type: Bool(false) - qsharp_type: bool - io_kind: Default - ClassicalDeclarationStmt [32-42]: - symbol_id: 7 - ty_span: [32-35] - init_expr: Expr [40-41]: - ty: Int(None, false) - kind: Cast [0-0]: + ClassicalDeclarationStmt [9-23]: + symbol_id: 8 + ty_span: [9-13] + init_expr: Expr [18-22]: + ty: Bool(false) + kind: Lit: Bool(true) + [8] Symbol [14-15]: + name: x + type: Bool(false) + qsharp_type: bool + io_kind: Default + ClassicalDeclarationStmt [32-42]: + symbol_id: 9 + ty_span: [32-35] + init_expr: Expr [40-41]: ty: Int(None, false) - expr: Expr [40-41]: - ty: Bool(false) - kind: SymbolId(6) - [7] Symbol [36-37]: - name: y - type: Int(None, false) - qsharp_type: Int - io_kind: Default - "#]], + kind: Cast [0-0]: + ty: Int(None, false) + expr: Expr [40-41]: + ty: Bool(false) + kind: SymbolId(8) + [9] Symbol [36-37]: + name: y + type: Int(None, false) + qsharp_type: Int + io_kind: Default + "#]], ); } @@ -95,33 +95,33 @@ fn to_explicit_int_implicitly() { check_classical_decls( input, &expect![[r#" - ClassicalDeclarationStmt [9-23]: - symbol_id: 6 - ty_span: [9-13] - init_expr: Expr [18-22]: - ty: Bool(true) - kind: Lit: Bool(true) - [6] Symbol [14-15]: - name: x - type: Bool(false) - qsharp_type: bool - io_kind: Default - ClassicalDeclarationStmt [32-46]: - symbol_id: 7 - ty_span: [32-39] - init_expr: Expr [44-45]: - ty: Int(Some(32), false) - kind: Cast [0-0]: + ClassicalDeclarationStmt [9-23]: + symbol_id: 8 + ty_span: [9-13] + init_expr: Expr [18-22]: + ty: Bool(false) + kind: Lit: Bool(true) + [8] Symbol [14-15]: + name: x + type: Bool(false) + qsharp_type: bool + io_kind: Default + ClassicalDeclarationStmt [32-46]: + symbol_id: 9 + ty_span: [32-39] + init_expr: Expr [44-45]: ty: Int(Some(32), false) - expr: Expr [44-45]: - ty: Bool(false) - kind: SymbolId(6) - [7] Symbol [40-41]: - name: y - type: Int(Some(32), false) - qsharp_type: Int - io_kind: Default - "#]], + kind: Cast [0-0]: + ty: Int(Some(32), false) + expr: Expr [44-45]: + ty: Bool(false) + kind: SymbolId(8) + [9] Symbol [40-41]: + name: y + type: Int(Some(32), false) + qsharp_type: Int + io_kind: Default + "#]], ); } @@ -135,33 +135,33 @@ fn to_implicit_uint_implicitly() { check_classical_decls( input, &expect![[r#" - ClassicalDeclarationStmt [9-23]: - symbol_id: 6 - ty_span: [9-13] - init_expr: Expr [18-22]: - ty: Bool(true) - kind: Lit: Bool(true) - [6] Symbol [14-15]: - name: x - type: Bool(false) - qsharp_type: bool - io_kind: Default - ClassicalDeclarationStmt [32-43]: - symbol_id: 7 - ty_span: [32-36] - init_expr: Expr [41-42]: - ty: UInt(None, false) - kind: Cast [0-0]: + ClassicalDeclarationStmt [9-23]: + symbol_id: 8 + ty_span: [9-13] + init_expr: Expr [18-22]: + ty: Bool(false) + kind: Lit: Bool(true) + [8] Symbol [14-15]: + name: x + type: Bool(false) + qsharp_type: bool + io_kind: Default + ClassicalDeclarationStmt [32-43]: + symbol_id: 9 + ty_span: [32-36] + init_expr: Expr [41-42]: ty: UInt(None, false) - expr: Expr [41-42]: - ty: Bool(false) - kind: SymbolId(6) - [7] Symbol [37-38]: - name: y - type: UInt(None, false) - qsharp_type: Int - io_kind: Default - "#]], + kind: Cast [0-0]: + ty: UInt(None, false) + expr: Expr [41-42]: + ty: Bool(false) + kind: SymbolId(8) + [9] Symbol [37-38]: + name: y + type: UInt(None, false) + qsharp_type: Int + io_kind: Default + "#]], ); } @@ -175,33 +175,33 @@ fn to_explicit_uint_implicitly() { check_classical_decls( input, &expect![[r#" - ClassicalDeclarationStmt [9-23]: - symbol_id: 6 - ty_span: [9-13] - init_expr: Expr [18-22]: - ty: Bool(true) - kind: Lit: Bool(true) - [6] Symbol [14-15]: - name: x - type: Bool(false) - qsharp_type: bool - io_kind: Default - ClassicalDeclarationStmt [32-47]: - symbol_id: 7 - ty_span: [32-40] - init_expr: Expr [45-46]: - ty: UInt(Some(32), false) - kind: Cast [0-0]: + ClassicalDeclarationStmt [9-23]: + symbol_id: 8 + ty_span: [9-13] + init_expr: Expr [18-22]: + ty: Bool(false) + kind: Lit: Bool(true) + [8] Symbol [14-15]: + name: x + type: Bool(false) + qsharp_type: bool + io_kind: Default + ClassicalDeclarationStmt [32-47]: + symbol_id: 9 + ty_span: [32-40] + init_expr: Expr [45-46]: ty: UInt(Some(32), false) - expr: Expr [45-46]: - ty: Bool(false) - kind: SymbolId(6) - [7] Symbol [41-42]: - name: y - type: UInt(Some(32), false) - qsharp_type: Int - io_kind: Default - "#]], + kind: Cast [0-0]: + ty: UInt(Some(32), false) + expr: Expr [45-46]: + ty: Bool(false) + kind: SymbolId(8) + [9] Symbol [41-42]: + name: y + type: UInt(Some(32), false) + qsharp_type: Int + io_kind: Default + "#]], ); } @@ -215,33 +215,33 @@ fn to_explicit_bigint_implicitly() { check_classical_decls( input, &expect![[r#" - ClassicalDeclarationStmt [9-23]: - symbol_id: 6 - ty_span: [9-13] - init_expr: Expr [18-22]: - ty: Bool(true) - kind: Lit: Bool(true) - [6] Symbol [14-15]: - name: x - type: Bool(false) - qsharp_type: bool - io_kind: Default - ClassicalDeclarationStmt [32-46]: - symbol_id: 7 - ty_span: [32-39] - init_expr: Expr [44-45]: - ty: Int(Some(65), false) - kind: Cast [0-0]: + ClassicalDeclarationStmt [9-23]: + symbol_id: 8 + ty_span: [9-13] + init_expr: Expr [18-22]: + ty: Bool(false) + kind: Lit: Bool(true) + [8] Symbol [14-15]: + name: x + type: Bool(false) + qsharp_type: bool + io_kind: Default + ClassicalDeclarationStmt [32-46]: + symbol_id: 9 + ty_span: [32-39] + init_expr: Expr [44-45]: ty: Int(Some(65), false) - expr: Expr [44-45]: - ty: Bool(false) - kind: SymbolId(6) - [7] Symbol [40-41]: - name: y - type: Int(Some(65), false) - qsharp_type: BigInt - io_kind: Default - "#]], + kind: Cast [0-0]: + ty: Int(Some(65), false) + expr: Expr [44-45]: + ty: Bool(false) + kind: SymbolId(8) + [9] Symbol [40-41]: + name: y + type: Int(Some(65), false) + qsharp_type: BigInt + io_kind: Default + "#]], ); } @@ -255,33 +255,33 @@ fn to_implicit_float_implicitly() { check_classical_decls( input, &expect![[r#" - ClassicalDeclarationStmt [9-23]: - symbol_id: 6 - ty_span: [9-13] - init_expr: Expr [18-22]: - ty: Bool(true) - kind: Lit: Bool(true) - [6] Symbol [14-15]: - name: x - type: Bool(false) - qsharp_type: bool - io_kind: Default - ClassicalDeclarationStmt [32-44]: - symbol_id: 7 - ty_span: [32-37] - init_expr: Expr [42-43]: - ty: Float(None, false) - kind: Cast [0-0]: + ClassicalDeclarationStmt [9-23]: + symbol_id: 8 + ty_span: [9-13] + init_expr: Expr [18-22]: + ty: Bool(false) + kind: Lit: Bool(true) + [8] Symbol [14-15]: + name: x + type: Bool(false) + qsharp_type: bool + io_kind: Default + ClassicalDeclarationStmt [32-44]: + symbol_id: 9 + ty_span: [32-37] + init_expr: Expr [42-43]: ty: Float(None, false) - expr: Expr [42-43]: - ty: Bool(false) - kind: SymbolId(6) - [7] Symbol [38-39]: - name: y - type: Float(None, false) - qsharp_type: Double - io_kind: Default - "#]], + kind: Cast [0-0]: + ty: Float(None, false) + expr: Expr [42-43]: + ty: Bool(false) + kind: SymbolId(8) + [9] Symbol [38-39]: + name: y + type: Float(None, false) + qsharp_type: Double + io_kind: Default + "#]], ); } @@ -295,32 +295,32 @@ fn to_explicit_float_implicitly() { check_classical_decls( input, &expect![[r#" - ClassicalDeclarationStmt [9-23]: - symbol_id: 6 - ty_span: [9-13] - init_expr: Expr [18-22]: - ty: Bool(true) - kind: Lit: Bool(true) - [6] Symbol [14-15]: - name: x - type: Bool(false) - qsharp_type: bool - io_kind: Default - ClassicalDeclarationStmt [32-48]: - symbol_id: 7 - ty_span: [32-41] - init_expr: Expr [46-47]: - ty: Float(Some(32), false) - kind: Cast [0-0]: + ClassicalDeclarationStmt [9-23]: + symbol_id: 8 + ty_span: [9-13] + init_expr: Expr [18-22]: + ty: Bool(false) + kind: Lit: Bool(true) + [8] Symbol [14-15]: + name: x + type: Bool(false) + qsharp_type: bool + io_kind: Default + ClassicalDeclarationStmt [32-48]: + symbol_id: 9 + ty_span: [32-41] + init_expr: Expr [46-47]: ty: Float(Some(32), false) - expr: Expr [46-47]: - ty: Bool(false) - kind: SymbolId(6) - [7] Symbol [42-43]: - name: y - type: Float(Some(32), false) - qsharp_type: Double - io_kind: Default - "#]], + kind: Cast [0-0]: + ty: Float(Some(32), false) + expr: Expr [46-47]: + ty: Bool(false) + kind: SymbolId(8) + [9] Symbol [42-43]: + name: y + type: Float(Some(32), false) + qsharp_type: Double + io_kind: Default + "#]], ); } diff --git a/compiler/qsc_qasm3/src/semantic/tests/expression/implicit_cast_from_float.rs b/compiler/qsc_qasm3/src/semantic/tests/expression/implicit_cast_from_float.rs index 1af9e85783..78ab02b6b9 100644 --- a/compiler/qsc_qasm3/src/semantic/tests/expression/implicit_cast_from_float.rs +++ b/compiler/qsc_qasm3/src/semantic/tests/expression/implicit_cast_from_float.rs @@ -21,27 +21,27 @@ fn to_bit_implicitly_fails() { Stmt [9-23]: annotations: kind: ClassicalDeclarationStmt [9-23]: - symbol_id: 6 + symbol_id: 8 ty_span: [9-14] init_expr: Expr [19-22]: - ty: Float(None, true) + ty: Float(None, false) kind: Lit: Float(42.0) Stmt [32-42]: annotations: kind: ClassicalDeclarationStmt [32-42]: - symbol_id: 7 + symbol_id: 9 ty_span: [32-35] init_expr: Expr [40-41]: ty: Float(None, false) - kind: SymbolId(6) + kind: SymbolId(8) [Qsc.Qasm3.Compile.CannotCast x Cannot cast expression of type Float(None, false) to type Bit(false) - ,-[test:3:9] + ,-[test:3:17] 2 | float x = 42.; 3 | bit y = x; - : ^^^^^^^^^^ + : ^ 4 | `---- ]"#]], @@ -64,7 +64,7 @@ fn explicit_width_to_bit_implicitly_fails() { Stmt [9-27]: annotations: kind: ClassicalDeclarationStmt [9-27]: - symbol_id: 6 + symbol_id: 8 ty_span: [9-18] init_expr: Expr [23-26]: ty: Float(Some(64), true) @@ -72,19 +72,19 @@ fn explicit_width_to_bit_implicitly_fails() { Stmt [36-46]: annotations: kind: ClassicalDeclarationStmt [36-46]: - symbol_id: 7 + symbol_id: 9 ty_span: [36-39] init_expr: Expr [44-45]: ty: Float(Some(64), false) - kind: SymbolId(6) + kind: SymbolId(8) [Qsc.Qasm3.Compile.CannotCast x Cannot cast expression of type Float(Some(64), false) to type Bit(false) - ,-[test:3:9] + ,-[test:3:17] 2 | float[64] x = 42.; 3 | bit y = x; - : ^^^^^^^^^^ + : ^ 4 | `---- ]"#]], @@ -100,33 +100,33 @@ fn to_bool_implicitly() { check_classical_decls( input, &expect![[r#" - ClassicalDeclarationStmt [9-23]: - symbol_id: 6 - ty_span: [9-14] - init_expr: Expr [19-22]: - ty: Float(None, true) - kind: Lit: Float(42.0) - [6] Symbol [15-16]: - name: x - type: Float(None, false) - qsharp_type: Double - io_kind: Default - ClassicalDeclarationStmt [32-43]: - symbol_id: 7 - ty_span: [32-36] - init_expr: Expr [41-42]: - ty: Bool(false) - kind: Cast [0-0]: + ClassicalDeclarationStmt [9-23]: + symbol_id: 8 + ty_span: [9-14] + init_expr: Expr [19-22]: + ty: Float(None, false) + kind: Lit: Float(42.0) + [8] Symbol [15-16]: + name: x + type: Float(None, false) + qsharp_type: Double + io_kind: Default + ClassicalDeclarationStmt [32-43]: + symbol_id: 9 + ty_span: [32-36] + init_expr: Expr [41-42]: ty: Bool(false) - expr: Expr [41-42]: - ty: Float(None, false) - kind: SymbolId(6) - [7] Symbol [37-38]: - name: y - type: Bool(false) - qsharp_type: bool - io_kind: Default - "#]], + kind: Cast [0-0]: + ty: Bool(false) + expr: Expr [41-42]: + ty: Float(None, false) + kind: SymbolId(8) + [9] Symbol [37-38]: + name: y + type: Bool(false) + qsharp_type: bool + io_kind: Default + "#]], ); } @@ -140,33 +140,33 @@ fn to_implicit_int_implicitly() { check_classical_decls( input, &expect![[r#" - ClassicalDeclarationStmt [9-23]: - symbol_id: 6 - ty_span: [9-14] - init_expr: Expr [19-22]: - ty: Float(None, true) - kind: Lit: Float(42.0) - [6] Symbol [15-16]: - name: x - type: Float(None, false) - qsharp_type: Double - io_kind: Default - ClassicalDeclarationStmt [32-42]: - symbol_id: 7 - ty_span: [32-35] - init_expr: Expr [40-41]: - ty: Int(None, false) - kind: Cast [0-0]: + ClassicalDeclarationStmt [9-23]: + symbol_id: 8 + ty_span: [9-14] + init_expr: Expr [19-22]: + ty: Float(None, false) + kind: Lit: Float(42.0) + [8] Symbol [15-16]: + name: x + type: Float(None, false) + qsharp_type: Double + io_kind: Default + ClassicalDeclarationStmt [32-42]: + symbol_id: 9 + ty_span: [32-35] + init_expr: Expr [40-41]: ty: Int(None, false) - expr: Expr [40-41]: - ty: Float(None, false) - kind: SymbolId(6) - [7] Symbol [36-37]: - name: y - type: Int(None, false) - qsharp_type: Int - io_kind: Default - "#]], + kind: Cast [0-0]: + ty: Int(None, false) + expr: Expr [40-41]: + ty: Float(None, false) + kind: SymbolId(8) + [9] Symbol [36-37]: + name: y + type: Int(None, false) + qsharp_type: Int + io_kind: Default + "#]], ); } @@ -180,33 +180,33 @@ fn to_explicit_int_implicitly() { check_classical_decls( input, &expect![[r#" - ClassicalDeclarationStmt [9-23]: - symbol_id: 6 - ty_span: [9-14] - init_expr: Expr [19-22]: - ty: Float(None, true) - kind: Lit: Float(42.0) - [6] Symbol [15-16]: - name: x - type: Float(None, false) - qsharp_type: Double - io_kind: Default - ClassicalDeclarationStmt [32-46]: - symbol_id: 7 - ty_span: [32-39] - init_expr: Expr [44-45]: - ty: Int(Some(32), false) - kind: Cast [0-0]: + ClassicalDeclarationStmt [9-23]: + symbol_id: 8 + ty_span: [9-14] + init_expr: Expr [19-22]: + ty: Float(None, false) + kind: Lit: Float(42.0) + [8] Symbol [15-16]: + name: x + type: Float(None, false) + qsharp_type: Double + io_kind: Default + ClassicalDeclarationStmt [32-46]: + symbol_id: 9 + ty_span: [32-39] + init_expr: Expr [44-45]: ty: Int(Some(32), false) - expr: Expr [44-45]: - ty: Float(None, false) - kind: SymbolId(6) - [7] Symbol [40-41]: - name: y - type: Int(Some(32), false) - qsharp_type: Int - io_kind: Default - "#]], + kind: Cast [0-0]: + ty: Int(Some(32), false) + expr: Expr [44-45]: + ty: Float(None, false) + kind: SymbolId(8) + [9] Symbol [40-41]: + name: y + type: Int(Some(32), false) + qsharp_type: Int + io_kind: Default + "#]], ); } @@ -220,33 +220,33 @@ fn to_implicit_uint_implicitly() { check_classical_decls( input, &expect![[r#" - ClassicalDeclarationStmt [9-23]: - symbol_id: 6 - ty_span: [9-14] - init_expr: Expr [19-22]: - ty: Float(None, true) - kind: Lit: Float(42.0) - [6] Symbol [15-16]: - name: x - type: Float(None, false) - qsharp_type: Double - io_kind: Default - ClassicalDeclarationStmt [32-43]: - symbol_id: 7 - ty_span: [32-36] - init_expr: Expr [41-42]: - ty: UInt(None, false) - kind: Cast [0-0]: + ClassicalDeclarationStmt [9-23]: + symbol_id: 8 + ty_span: [9-14] + init_expr: Expr [19-22]: + ty: Float(None, false) + kind: Lit: Float(42.0) + [8] Symbol [15-16]: + name: x + type: Float(None, false) + qsharp_type: Double + io_kind: Default + ClassicalDeclarationStmt [32-43]: + symbol_id: 9 + ty_span: [32-36] + init_expr: Expr [41-42]: ty: UInt(None, false) - expr: Expr [41-42]: - ty: Float(None, false) - kind: SymbolId(6) - [7] Symbol [37-38]: - name: y - type: UInt(None, false) - qsharp_type: Int - io_kind: Default - "#]], + kind: Cast [0-0]: + ty: UInt(None, false) + expr: Expr [41-42]: + ty: Float(None, false) + kind: SymbolId(8) + [9] Symbol [37-38]: + name: y + type: UInt(None, false) + qsharp_type: Int + io_kind: Default + "#]], ); } @@ -261,22 +261,22 @@ fn negative_lit_to_implicit_uint_implicitly() { input, &expect![[r#" ClassicalDeclarationStmt [9-24]: - symbol_id: 6 + symbol_id: 8 ty_span: [9-14] init_expr: Expr [20-23]: - ty: Float(None, true) + ty: Float(None, false) kind: UnaryOpExpr [20-23]: op: Neg expr: Expr [20-23]: ty: Float(None, true) kind: Lit: Float(42.0) - [6] Symbol [15-16]: + [8] Symbol [15-16]: name: x type: Float(None, false) qsharp_type: Double io_kind: Default ClassicalDeclarationStmt [33-44]: - symbol_id: 7 + symbol_id: 9 ty_span: [33-37] init_expr: Expr [42-43]: ty: UInt(None, false) @@ -284,8 +284,8 @@ fn negative_lit_to_implicit_uint_implicitly() { ty: UInt(None, false) expr: Expr [42-43]: ty: Float(None, false) - kind: SymbolId(6) - [7] Symbol [38-39]: + kind: SymbolId(8) + [9] Symbol [38-39]: name: y type: UInt(None, false) qsharp_type: Int @@ -304,33 +304,33 @@ fn to_explicit_uint_implicitly() { check_classical_decls( input, &expect![[r#" - ClassicalDeclarationStmt [9-23]: - symbol_id: 6 - ty_span: [9-14] - init_expr: Expr [19-22]: - ty: Float(None, true) - kind: Lit: Float(42.0) - [6] Symbol [15-16]: - name: x - type: Float(None, false) - qsharp_type: Double - io_kind: Default - ClassicalDeclarationStmt [32-47]: - symbol_id: 7 - ty_span: [32-40] - init_expr: Expr [45-46]: - ty: UInt(Some(32), false) - kind: Cast [0-0]: + ClassicalDeclarationStmt [9-23]: + symbol_id: 8 + ty_span: [9-14] + init_expr: Expr [19-22]: + ty: Float(None, false) + kind: Lit: Float(42.0) + [8] Symbol [15-16]: + name: x + type: Float(None, false) + qsharp_type: Double + io_kind: Default + ClassicalDeclarationStmt [32-47]: + symbol_id: 9 + ty_span: [32-40] + init_expr: Expr [45-46]: ty: UInt(Some(32), false) - expr: Expr [45-46]: - ty: Float(None, false) - kind: SymbolId(6) - [7] Symbol [41-42]: - name: y - type: UInt(Some(32), false) - qsharp_type: Int - io_kind: Default - "#]], + kind: Cast [0-0]: + ty: UInt(Some(32), false) + expr: Expr [45-46]: + ty: Float(None, false) + kind: SymbolId(8) + [9] Symbol [41-42]: + name: y + type: UInt(Some(32), false) + qsharp_type: Int + io_kind: Default + "#]], ); } @@ -344,33 +344,33 @@ fn to_explicit_bigint_implicitly() { check_classical_decls( input, &expect![[r#" - ClassicalDeclarationStmt [9-23]: - symbol_id: 6 - ty_span: [9-14] - init_expr: Expr [19-22]: - ty: Float(None, true) - kind: Lit: Float(42.0) - [6] Symbol [15-16]: - name: x - type: Float(None, false) - qsharp_type: Double - io_kind: Default - ClassicalDeclarationStmt [32-46]: - symbol_id: 7 - ty_span: [32-39] - init_expr: Expr [44-45]: - ty: Int(Some(65), false) - kind: Cast [0-0]: + ClassicalDeclarationStmt [9-23]: + symbol_id: 8 + ty_span: [9-14] + init_expr: Expr [19-22]: + ty: Float(None, false) + kind: Lit: Float(42.0) + [8] Symbol [15-16]: + name: x + type: Float(None, false) + qsharp_type: Double + io_kind: Default + ClassicalDeclarationStmt [32-46]: + symbol_id: 9 + ty_span: [32-39] + init_expr: Expr [44-45]: ty: Int(Some(65), false) - expr: Expr [44-45]: - ty: Float(None, false) - kind: SymbolId(6) - [7] Symbol [40-41]: - name: y - type: Int(Some(65), false) - qsharp_type: BigInt - io_kind: Default - "#]], + kind: Cast [0-0]: + ty: Int(Some(65), false) + expr: Expr [44-45]: + ty: Float(None, false) + kind: SymbolId(8) + [9] Symbol [40-41]: + name: y + type: Int(Some(65), false) + qsharp_type: BigInt + io_kind: Default + "#]], ); } @@ -384,29 +384,29 @@ fn to_implicit_float_implicitly() { check_classical_decls( input, &expect![[r#" - ClassicalDeclarationStmt [9-23]: - symbol_id: 6 - ty_span: [9-14] - init_expr: Expr [19-22]: - ty: Float(None, true) - kind: Lit: Float(42.0) - [6] Symbol [15-16]: - name: x - type: Float(None, false) - qsharp_type: Double - io_kind: Default - ClassicalDeclarationStmt [32-44]: - symbol_id: 7 - ty_span: [32-37] - init_expr: Expr [42-43]: - ty: Float(None, false) - kind: SymbolId(6) - [7] Symbol [38-39]: - name: y - type: Float(None, false) - qsharp_type: Double - io_kind: Default - "#]], + ClassicalDeclarationStmt [9-23]: + symbol_id: 8 + ty_span: [9-14] + init_expr: Expr [19-22]: + ty: Float(None, false) + kind: Lit: Float(42.0) + [8] Symbol [15-16]: + name: x + type: Float(None, false) + qsharp_type: Double + io_kind: Default + ClassicalDeclarationStmt [32-44]: + symbol_id: 9 + ty_span: [32-37] + init_expr: Expr [42-43]: + ty: Float(None, false) + kind: SymbolId(8) + [9] Symbol [38-39]: + name: y + type: Float(None, false) + qsharp_type: Double + io_kind: Default + "#]], ); } @@ -420,29 +420,29 @@ fn to_explicit_float_implicitly() { check_classical_decls( input, &expect![[r#" - ClassicalDeclarationStmt [9-23]: - symbol_id: 6 - ty_span: [9-14] - init_expr: Expr [19-22]: - ty: Float(None, true) - kind: Lit: Float(42.0) - [6] Symbol [15-16]: - name: x - type: Float(None, false) - qsharp_type: Double - io_kind: Default - ClassicalDeclarationStmt [32-48]: - symbol_id: 7 - ty_span: [32-41] - init_expr: Expr [46-47]: - ty: Float(Some(32), false) - kind: SymbolId(6) - [7] Symbol [42-43]: - name: y - type: Float(Some(32), false) - qsharp_type: Double - io_kind: Default - "#]], + ClassicalDeclarationStmt [9-23]: + symbol_id: 8 + ty_span: [9-14] + init_expr: Expr [19-22]: + ty: Float(None, false) + kind: Lit: Float(42.0) + [8] Symbol [15-16]: + name: x + type: Float(None, false) + qsharp_type: Double + io_kind: Default + ClassicalDeclarationStmt [32-48]: + symbol_id: 9 + ty_span: [32-41] + init_expr: Expr [46-47]: + ty: Float(Some(32), false) + kind: SymbolId(8) + [9] Symbol [42-43]: + name: y + type: Float(Some(32), false) + qsharp_type: Double + io_kind: Default + "#]], ); } @@ -456,33 +456,33 @@ fn to_implicit_complex_implicitly() { check_classical_decls( input, &expect![[r#" - ClassicalDeclarationStmt [9-23]: - symbol_id: 6 - ty_span: [9-14] - init_expr: Expr [19-22]: - ty: Float(None, true) - kind: Lit: Float(42.0) - [6] Symbol [15-16]: - name: x - type: Float(None, false) - qsharp_type: Double - io_kind: Default - ClassicalDeclarationStmt [32-53]: - symbol_id: 7 - ty_span: [32-46] - init_expr: Expr [51-52]: - ty: Complex(None, false) - kind: Cast [0-0]: + ClassicalDeclarationStmt [9-23]: + symbol_id: 8 + ty_span: [9-14] + init_expr: Expr [19-22]: + ty: Float(None, false) + kind: Lit: Float(42.0) + [8] Symbol [15-16]: + name: x + type: Float(None, false) + qsharp_type: Double + io_kind: Default + ClassicalDeclarationStmt [32-53]: + symbol_id: 9 + ty_span: [32-46] + init_expr: Expr [51-52]: ty: Complex(None, false) - expr: Expr [51-52]: - ty: Float(None, false) - kind: SymbolId(6) - [7] Symbol [47-48]: - name: y - type: Complex(None, false) - qsharp_type: Complex - io_kind: Default - "#]], + kind: Cast [0-0]: + ty: Complex(None, false) + expr: Expr [51-52]: + ty: Float(None, false) + kind: SymbolId(8) + [9] Symbol [47-48]: + name: y + type: Complex(None, false) + qsharp_type: Complex + io_kind: Default + "#]], ); } @@ -496,33 +496,33 @@ fn to_explicit_complex_implicitly() { check_classical_decls( input, &expect![[r#" - ClassicalDeclarationStmt [9-23]: - symbol_id: 6 - ty_span: [9-14] - init_expr: Expr [19-22]: - ty: Float(None, true) - kind: Lit: Float(42.0) - [6] Symbol [15-16]: - name: x - type: Float(None, false) - qsharp_type: Double - io_kind: Default - ClassicalDeclarationStmt [32-57]: - symbol_id: 7 - ty_span: [32-50] - init_expr: Expr [55-56]: - ty: Complex(Some(32), false) - kind: Cast [0-0]: + ClassicalDeclarationStmt [9-23]: + symbol_id: 8 + ty_span: [9-14] + init_expr: Expr [19-22]: + ty: Float(None, false) + kind: Lit: Float(42.0) + [8] Symbol [15-16]: + name: x + type: Float(None, false) + qsharp_type: Double + io_kind: Default + ClassicalDeclarationStmt [32-57]: + symbol_id: 9 + ty_span: [32-50] + init_expr: Expr [55-56]: ty: Complex(Some(32), false) - expr: Expr [55-56]: - ty: Float(None, false) - kind: SymbolId(6) - [7] Symbol [51-52]: - name: y - type: Complex(Some(32), false) - qsharp_type: Complex - io_kind: Default - "#]], + kind: Cast [0-0]: + ty: Complex(Some(32), false) + expr: Expr [55-56]: + ty: Float(None, false) + kind: SymbolId(8) + [9] Symbol [51-52]: + name: y + type: Complex(Some(32), false) + qsharp_type: Complex + io_kind: Default + "#]], ); } diff --git a/compiler/qsc_qasm3/src/semantic/tests/expression/implicit_cast_from_int.rs b/compiler/qsc_qasm3/src/semantic/tests/expression/implicit_cast_from_int.rs index 1e8664c0bb..b1e756ab95 100644 --- a/compiler/qsc_qasm3/src/semantic/tests/expression/implicit_cast_from_int.rs +++ b/compiler/qsc_qasm3/src/semantic/tests/expression/implicit_cast_from_int.rs @@ -15,33 +15,33 @@ fn to_bit_implicitly() { check_classical_decls( input, &expect![[r#" - ClassicalDeclarationStmt [9-20]: - symbol_id: 6 - ty_span: [9-12] - init_expr: Expr [17-19]: - ty: Int(None, true) - kind: Lit: Int(42) - [6] Symbol [13-14]: - name: x - type: Int(None, false) - qsharp_type: Int - io_kind: Default - ClassicalDeclarationStmt [29-39]: - symbol_id: 7 - ty_span: [29-32] - init_expr: Expr [37-38]: - ty: Bit(false) - kind: Cast [0-0]: + ClassicalDeclarationStmt [9-20]: + symbol_id: 8 + ty_span: [9-12] + init_expr: Expr [17-19]: + ty: Int(None, false) + kind: Lit: Int(42) + [8] Symbol [13-14]: + name: x + type: Int(None, false) + qsharp_type: Int + io_kind: Default + ClassicalDeclarationStmt [29-39]: + symbol_id: 9 + ty_span: [29-32] + init_expr: Expr [37-38]: ty: Bit(false) - expr: Expr [37-38]: - ty: Int(None, false) - kind: SymbolId(6) - [7] Symbol [33-34]: - name: y - type: Bit(false) - qsharp_type: Result - io_kind: Default - "#]], + kind: Cast [0-0]: + ty: Bit(false) + expr: Expr [37-38]: + ty: Int(None, false) + kind: SymbolId(8) + [9] Symbol [33-34]: + name: y + type: Bit(false) + qsharp_type: Result + io_kind: Default + "#]], ); } @@ -55,33 +55,33 @@ fn to_bool_implicitly() { check_classical_decls( input, &expect![[r#" - ClassicalDeclarationStmt [9-20]: - symbol_id: 6 - ty_span: [9-12] - init_expr: Expr [17-19]: - ty: Int(None, true) - kind: Lit: Int(42) - [6] Symbol [13-14]: - name: x - type: Int(None, false) - qsharp_type: Int - io_kind: Default - ClassicalDeclarationStmt [29-40]: - symbol_id: 7 - ty_span: [29-33] - init_expr: Expr [38-39]: - ty: Bool(false) - kind: Cast [0-0]: + ClassicalDeclarationStmt [9-20]: + symbol_id: 8 + ty_span: [9-12] + init_expr: Expr [17-19]: + ty: Int(None, false) + kind: Lit: Int(42) + [8] Symbol [13-14]: + name: x + type: Int(None, false) + qsharp_type: Int + io_kind: Default + ClassicalDeclarationStmt [29-40]: + symbol_id: 9 + ty_span: [29-33] + init_expr: Expr [38-39]: ty: Bool(false) - expr: Expr [38-39]: - ty: Int(None, false) - kind: SymbolId(6) - [7] Symbol [34-35]: - name: y - type: Bool(false) - qsharp_type: bool - io_kind: Default - "#]], + kind: Cast [0-0]: + ty: Bool(false) + expr: Expr [38-39]: + ty: Int(None, false) + kind: SymbolId(8) + [9] Symbol [34-35]: + name: y + type: Bool(false) + qsharp_type: bool + io_kind: Default + "#]], ); } @@ -95,29 +95,29 @@ fn to_implicit_int_implicitly() { check_classical_decls( input, &expect![[r#" - ClassicalDeclarationStmt [9-20]: - symbol_id: 6 - ty_span: [9-12] - init_expr: Expr [17-19]: - ty: Int(None, true) - kind: Lit: Int(42) - [6] Symbol [13-14]: - name: x - type: Int(None, false) - qsharp_type: Int - io_kind: Default - ClassicalDeclarationStmt [29-39]: - symbol_id: 7 - ty_span: [29-32] - init_expr: Expr [37-38]: - ty: Int(None, false) - kind: SymbolId(6) - [7] Symbol [33-34]: - name: y - type: Int(None, false) - qsharp_type: Int - io_kind: Default - "#]], + ClassicalDeclarationStmt [9-20]: + symbol_id: 8 + ty_span: [9-12] + init_expr: Expr [17-19]: + ty: Int(None, false) + kind: Lit: Int(42) + [8] Symbol [13-14]: + name: x + type: Int(None, false) + qsharp_type: Int + io_kind: Default + ClassicalDeclarationStmt [29-39]: + symbol_id: 9 + ty_span: [29-32] + init_expr: Expr [37-38]: + ty: Int(None, false) + kind: SymbolId(8) + [9] Symbol [33-34]: + name: y + type: Int(None, false) + qsharp_type: Int + io_kind: Default + "#]], ); } @@ -131,33 +131,33 @@ fn to_explicit_int_implicitly() { check_classical_decls( input, &expect![[r#" - ClassicalDeclarationStmt [9-20]: - symbol_id: 6 - ty_span: [9-12] - init_expr: Expr [17-19]: - ty: Int(None, true) - kind: Lit: Int(42) - [6] Symbol [13-14]: - name: x - type: Int(None, false) - qsharp_type: Int - io_kind: Default - ClassicalDeclarationStmt [29-43]: - symbol_id: 7 - ty_span: [29-36] - init_expr: Expr [41-42]: - ty: Int(Some(32), false) - kind: Cast [0-0]: + ClassicalDeclarationStmt [9-20]: + symbol_id: 8 + ty_span: [9-12] + init_expr: Expr [17-19]: + ty: Int(None, false) + kind: Lit: Int(42) + [8] Symbol [13-14]: + name: x + type: Int(None, false) + qsharp_type: Int + io_kind: Default + ClassicalDeclarationStmt [29-43]: + symbol_id: 9 + ty_span: [29-36] + init_expr: Expr [41-42]: ty: Int(Some(32), false) - expr: Expr [41-42]: - ty: Int(None, false) - kind: SymbolId(6) - [7] Symbol [37-38]: - name: y - type: Int(Some(32), false) - qsharp_type: Int - io_kind: Default - "#]], + kind: Cast [0-0]: + ty: Int(Some(32), false) + expr: Expr [41-42]: + ty: Int(None, false) + kind: SymbolId(8) + [9] Symbol [37-38]: + name: y + type: Int(Some(32), false) + qsharp_type: Int + io_kind: Default + "#]], ); } @@ -171,33 +171,33 @@ fn to_implicit_uint_implicitly() { check_classical_decls( input, &expect![[r#" - ClassicalDeclarationStmt [9-20]: - symbol_id: 6 - ty_span: [9-12] - init_expr: Expr [17-19]: - ty: Int(None, true) - kind: Lit: Int(42) - [6] Symbol [13-14]: - name: x - type: Int(None, false) - qsharp_type: Int - io_kind: Default - ClassicalDeclarationStmt [29-40]: - symbol_id: 7 - ty_span: [29-33] - init_expr: Expr [38-39]: - ty: UInt(None, false) - kind: Cast [0-0]: + ClassicalDeclarationStmt [9-20]: + symbol_id: 8 + ty_span: [9-12] + init_expr: Expr [17-19]: + ty: Int(None, false) + kind: Lit: Int(42) + [8] Symbol [13-14]: + name: x + type: Int(None, false) + qsharp_type: Int + io_kind: Default + ClassicalDeclarationStmt [29-40]: + symbol_id: 9 + ty_span: [29-33] + init_expr: Expr [38-39]: ty: UInt(None, false) - expr: Expr [38-39]: - ty: Int(None, false) - kind: SymbolId(6) - [7] Symbol [34-35]: - name: y - type: UInt(None, false) - qsharp_type: Int - io_kind: Default - "#]], + kind: Cast [0-0]: + ty: UInt(None, false) + expr: Expr [38-39]: + ty: Int(None, false) + kind: SymbolId(8) + [9] Symbol [34-35]: + name: y + type: UInt(None, false) + qsharp_type: Int + io_kind: Default + "#]], ); } @@ -211,33 +211,33 @@ fn to_explicit_uint_implicitly() { check_classical_decls( input, &expect![[r#" - ClassicalDeclarationStmt [9-20]: - symbol_id: 6 - ty_span: [9-12] - init_expr: Expr [17-19]: - ty: Int(None, true) - kind: Lit: Int(42) - [6] Symbol [13-14]: - name: x - type: Int(None, false) - qsharp_type: Int - io_kind: Default - ClassicalDeclarationStmt [29-44]: - symbol_id: 7 - ty_span: [29-37] - init_expr: Expr [42-43]: - ty: UInt(Some(32), false) - kind: Cast [0-0]: + ClassicalDeclarationStmt [9-20]: + symbol_id: 8 + ty_span: [9-12] + init_expr: Expr [17-19]: + ty: Int(None, false) + kind: Lit: Int(42) + [8] Symbol [13-14]: + name: x + type: Int(None, false) + qsharp_type: Int + io_kind: Default + ClassicalDeclarationStmt [29-44]: + symbol_id: 9 + ty_span: [29-37] + init_expr: Expr [42-43]: ty: UInt(Some(32), false) - expr: Expr [42-43]: - ty: Int(None, false) - kind: SymbolId(6) - [7] Symbol [38-39]: - name: y - type: UInt(Some(32), false) - qsharp_type: Int - io_kind: Default - "#]], + kind: Cast [0-0]: + ty: UInt(Some(32), false) + expr: Expr [42-43]: + ty: Int(None, false) + kind: SymbolId(8) + [9] Symbol [38-39]: + name: y + type: UInt(Some(32), false) + qsharp_type: Int + io_kind: Default + "#]], ); } @@ -251,33 +251,33 @@ fn to_explicit_bigint_implicitly() { check_classical_decls( input, &expect![[r#" - ClassicalDeclarationStmt [9-20]: - symbol_id: 6 - ty_span: [9-12] - init_expr: Expr [17-19]: - ty: Int(None, true) - kind: Lit: Int(42) - [6] Symbol [13-14]: - name: x - type: Int(None, false) - qsharp_type: Int - io_kind: Default - ClassicalDeclarationStmt [29-43]: - symbol_id: 7 - ty_span: [29-36] - init_expr: Expr [41-42]: - ty: Int(Some(65), false) - kind: Cast [0-0]: + ClassicalDeclarationStmt [9-20]: + symbol_id: 8 + ty_span: [9-12] + init_expr: Expr [17-19]: + ty: Int(None, false) + kind: Lit: Int(42) + [8] Symbol [13-14]: + name: x + type: Int(None, false) + qsharp_type: Int + io_kind: Default + ClassicalDeclarationStmt [29-43]: + symbol_id: 9 + ty_span: [29-36] + init_expr: Expr [41-42]: ty: Int(Some(65), false) - expr: Expr [41-42]: - ty: Int(None, false) - kind: SymbolId(6) - [7] Symbol [37-38]: - name: y - type: Int(Some(65), false) - qsharp_type: BigInt - io_kind: Default - "#]], + kind: Cast [0-0]: + ty: Int(Some(65), false) + expr: Expr [41-42]: + ty: Int(None, false) + kind: SymbolId(8) + [9] Symbol [37-38]: + name: y + type: Int(Some(65), false) + qsharp_type: BigInt + io_kind: Default + "#]], ); } @@ -291,33 +291,33 @@ fn to_implicit_float_implicitly() { check_classical_decls( input, &expect![[r#" - ClassicalDeclarationStmt [9-20]: - symbol_id: 6 - ty_span: [9-12] - init_expr: Expr [17-19]: - ty: Int(None, true) - kind: Lit: Int(42) - [6] Symbol [13-14]: - name: x - type: Int(None, false) - qsharp_type: Int - io_kind: Default - ClassicalDeclarationStmt [29-41]: - symbol_id: 7 - ty_span: [29-34] - init_expr: Expr [39-40]: - ty: Float(None, false) - kind: Cast [0-0]: + ClassicalDeclarationStmt [9-20]: + symbol_id: 8 + ty_span: [9-12] + init_expr: Expr [17-19]: + ty: Int(None, false) + kind: Lit: Int(42) + [8] Symbol [13-14]: + name: x + type: Int(None, false) + qsharp_type: Int + io_kind: Default + ClassicalDeclarationStmt [29-41]: + symbol_id: 9 + ty_span: [29-34] + init_expr: Expr [39-40]: ty: Float(None, false) - expr: Expr [39-40]: - ty: Int(None, false) - kind: SymbolId(6) - [7] Symbol [35-36]: - name: y - type: Float(None, false) - qsharp_type: Double - io_kind: Default - "#]], + kind: Cast [0-0]: + ty: Float(None, false) + expr: Expr [39-40]: + ty: Int(None, false) + kind: SymbolId(8) + [9] Symbol [35-36]: + name: y + type: Float(None, false) + qsharp_type: Double + io_kind: Default + "#]], ); } @@ -331,33 +331,33 @@ fn to_explicit_float_implicitly() { check_classical_decls( input, &expect![[r#" - ClassicalDeclarationStmt [9-20]: - symbol_id: 6 - ty_span: [9-12] - init_expr: Expr [17-19]: - ty: Int(None, true) - kind: Lit: Int(42) - [6] Symbol [13-14]: - name: x - type: Int(None, false) - qsharp_type: Int - io_kind: Default - ClassicalDeclarationStmt [29-45]: - symbol_id: 7 - ty_span: [29-38] - init_expr: Expr [43-44]: - ty: Float(Some(32), false) - kind: Cast [0-0]: + ClassicalDeclarationStmt [9-20]: + symbol_id: 8 + ty_span: [9-12] + init_expr: Expr [17-19]: + ty: Int(None, false) + kind: Lit: Int(42) + [8] Symbol [13-14]: + name: x + type: Int(None, false) + qsharp_type: Int + io_kind: Default + ClassicalDeclarationStmt [29-45]: + symbol_id: 9 + ty_span: [29-38] + init_expr: Expr [43-44]: ty: Float(Some(32), false) - expr: Expr [43-44]: - ty: Int(None, false) - kind: SymbolId(6) - [7] Symbol [39-40]: - name: y - type: Float(Some(32), false) - qsharp_type: Double - io_kind: Default - "#]], + kind: Cast [0-0]: + ty: Float(Some(32), false) + expr: Expr [43-44]: + ty: Int(None, false) + kind: SymbolId(8) + [9] Symbol [39-40]: + name: y + type: Float(Some(32), false) + qsharp_type: Double + io_kind: Default + "#]], ); } @@ -371,33 +371,33 @@ fn to_implicit_complex_implicitly() { check_classical_decls( input, &expect![[r#" - ClassicalDeclarationStmt [9-20]: - symbol_id: 6 - ty_span: [9-12] - init_expr: Expr [17-19]: - ty: Int(None, true) - kind: Lit: Int(42) - [6] Symbol [13-14]: - name: x - type: Int(None, false) - qsharp_type: Int - io_kind: Default - ClassicalDeclarationStmt [29-50]: - symbol_id: 7 - ty_span: [29-43] - init_expr: Expr [48-49]: - ty: Complex(None, false) - kind: Cast [0-0]: + ClassicalDeclarationStmt [9-20]: + symbol_id: 8 + ty_span: [9-12] + init_expr: Expr [17-19]: + ty: Int(None, false) + kind: Lit: Int(42) + [8] Symbol [13-14]: + name: x + type: Int(None, false) + qsharp_type: Int + io_kind: Default + ClassicalDeclarationStmt [29-50]: + symbol_id: 9 + ty_span: [29-43] + init_expr: Expr [48-49]: ty: Complex(None, false) - expr: Expr [48-49]: - ty: Int(None, false) - kind: SymbolId(6) - [7] Symbol [44-45]: - name: y - type: Complex(None, false) - qsharp_type: Complex - io_kind: Default - "#]], + kind: Cast [0-0]: + ty: Complex(None, false) + expr: Expr [48-49]: + ty: Int(None, false) + kind: SymbolId(8) + [9] Symbol [44-45]: + name: y + type: Complex(None, false) + qsharp_type: Complex + io_kind: Default + "#]], ); } @@ -411,32 +411,32 @@ fn to_explicit_complex_implicitly() { check_classical_decls( input, &expect![[r#" - ClassicalDeclarationStmt [9-20]: - symbol_id: 6 - ty_span: [9-12] - init_expr: Expr [17-19]: - ty: Int(None, true) - kind: Lit: Int(42) - [6] Symbol [13-14]: - name: x - type: Int(None, false) - qsharp_type: Int - io_kind: Default - ClassicalDeclarationStmt [29-54]: - symbol_id: 7 - ty_span: [29-47] - init_expr: Expr [52-53]: - ty: Complex(Some(32), false) - kind: Cast [0-0]: + ClassicalDeclarationStmt [9-20]: + symbol_id: 8 + ty_span: [9-12] + init_expr: Expr [17-19]: + ty: Int(None, false) + kind: Lit: Int(42) + [8] Symbol [13-14]: + name: x + type: Int(None, false) + qsharp_type: Int + io_kind: Default + ClassicalDeclarationStmt [29-54]: + symbol_id: 9 + ty_span: [29-47] + init_expr: Expr [52-53]: ty: Complex(Some(32), false) - expr: Expr [52-53]: - ty: Int(None, false) - kind: SymbolId(6) - [7] Symbol [48-49]: - name: y - type: Complex(Some(32), false) - qsharp_type: Complex - io_kind: Default - "#]], + kind: Cast [0-0]: + ty: Complex(Some(32), false) + expr: Expr [52-53]: + ty: Int(None, false) + kind: SymbolId(8) + [9] Symbol [48-49]: + name: y + type: Complex(Some(32), false) + qsharp_type: Complex + io_kind: Default + "#]], ); } diff --git a/compiler/qsc_qasm3/src/semantic/tests/statements/for_stmt.rs b/compiler/qsc_qasm3/src/semantic/tests/statements/for_stmt.rs index 620d87a20e..147c328b78 100644 --- a/compiler/qsc_qasm3/src/semantic/tests/statements/for_stmt.rs +++ b/compiler/qsc_qasm3/src/semantic/tests/statements/for_stmt.rs @@ -18,16 +18,16 @@ fn shadowing_loop_variable_in_single_stmt_body_fails() { Stmt [5-39]: annotations: kind: ForStmt [5-39]: - loop_variable: 6 + loop_variable: 8 iterable: DiscreteSet [18-20]: values: body: Stmt [29-39]: annotations: kind: ClassicalDeclarationStmt [29-39]: - symbol_id: 6 + symbol_id: 8 ty_span: [29-32] init_expr: Expr [37-38]: - ty: Int(None, true) + ty: Int(None, false) kind: Lit: Int(2) [Qsc.Qasm3.Compile.RedefinedSymbol @@ -53,7 +53,7 @@ fn shadowing_loop_variable_in_block_body_succeeds() { ", &expect![[r#" ForStmt [5-47]: - loop_variable: 6 + loop_variable: 8 iterable: DiscreteSet [18-20]: values: body: Stmt [21-47]: @@ -62,10 +62,10 @@ fn shadowing_loop_variable_in_block_body_succeeds() { Stmt [31-41]: annotations: kind: ClassicalDeclarationStmt [31-41]: - symbol_id: 7 + symbol_id: 9 ty_span: [31-34] init_expr: Expr [39-40]: - ty: Int(None, true) + ty: Int(None, false) kind: Lit: Int(2) "#]], ); @@ -84,22 +84,22 @@ fn loop_creates_its_own_scope() { ", &expect![[r#" ClassicalDeclarationStmt [5-15]: - symbol_id: 6 + symbol_id: 8 ty_span: [5-8] init_expr: Expr [13-14]: - ty: Int(None, true) + ty: Int(None, false) kind: Lit: Int(0) ForStmt [20-177]: - loop_variable: 7 + loop_variable: 9 iterable: DiscreteSet [33-35]: values: body: Stmt [167-177]: annotations: kind: ClassicalDeclarationStmt [167-177]: - symbol_id: 8 + symbol_id: 10 ty_span: [167-170] init_expr: Expr [175-176]: - ty: Int(None, true) + ty: Int(None, false) kind: Lit: Int(1) "#]], ); diff --git a/compiler/qsc_qasm3/src/semantic/tests/statements/if_stmt.rs b/compiler/qsc_qasm3/src/semantic/tests/statements/if_stmt.rs index 1083cbb167..850228461a 100644 --- a/compiler/qsc_qasm3/src/semantic/tests/statements/if_stmt.rs +++ b/compiler/qsc_qasm3/src/semantic/tests/statements/if_stmt.rs @@ -8,7 +8,7 @@ use expect_test::expect; fn if_branch_doesnt_create_its_own_scope() { check_stmt_kinds( " - int a = 0; + int a = 2; if (true) int a = 1; ", &expect![[r#" @@ -18,11 +18,11 @@ fn if_branch_doesnt_create_its_own_scope() { Stmt [5-15]: annotations: kind: ClassicalDeclarationStmt [5-15]: - symbol_id: 6 + symbol_id: 8 ty_span: [5-8] init_expr: Expr [13-14]: - ty: Int(None, true) - kind: Lit: Int(0) + ty: Int(None, false) + kind: Lit: Int(2) Stmt [20-40]: annotations: kind: IfStmt [20-40]: @@ -32,10 +32,10 @@ fn if_branch_doesnt_create_its_own_scope() { if_body: Stmt [30-40]: annotations: kind: ClassicalDeclarationStmt [30-40]: - symbol_id: 6 + symbol_id: 8 ty_span: [30-33] init_expr: Expr [38-39]: - ty: Int(None, true) + ty: Int(None, false) kind: Lit: Int(1) else_body: @@ -43,7 +43,7 @@ fn if_branch_doesnt_create_its_own_scope() { x Redefined symbol: a. ,-[test:3:19] - 2 | int a = 0; + 2 | int a = 2; 3 | if (true) int a = 1; : ^ 4 | @@ -56,7 +56,7 @@ fn if_branch_doesnt_create_its_own_scope() { fn else_branch_doesnt_create_its_own_scope() { check_stmt_kinds( " - int a = 0; + int a = 2; if (true) {} else int a = 1; ", @@ -67,11 +67,11 @@ fn else_branch_doesnt_create_its_own_scope() { Stmt [5-15]: annotations: kind: ClassicalDeclarationStmt [5-15]: - symbol_id: 6 + symbol_id: 8 ty_span: [5-8] init_expr: Expr [13-14]: - ty: Int(None, true) - kind: Lit: Int(0) + ty: Int(None, false) + kind: Lit: Int(2) Stmt [20-52]: annotations: kind: IfStmt [20-52]: @@ -84,10 +84,10 @@ fn else_branch_doesnt_create_its_own_scope() { else_body: Stmt [42-52]: annotations: kind: ClassicalDeclarationStmt [42-52]: - symbol_id: 6 + symbol_id: 8 ty_span: [42-45] init_expr: Expr [50-51]: - ty: Int(None, true) + ty: Int(None, false) kind: Lit: Int(1) [Qsc.Qasm3.Compile.RedefinedSymbol @@ -107,16 +107,16 @@ fn else_branch_doesnt_create_its_own_scope() { fn branch_block_creates_a_new_scope() { check_stmt_kinds( " - int a = 0; + int a = 2; if (true) { int a = 1; } ", &expect![[r#" ClassicalDeclarationStmt [5-15]: - symbol_id: 6 + symbol_id: 8 ty_span: [5-8] init_expr: Expr [13-14]: - ty: Int(None, true) - kind: Lit: Int(0) + ty: Int(None, false) + kind: Lit: Int(2) IfStmt [20-44]: condition: Expr [24-28]: ty: Bool(true) @@ -127,10 +127,10 @@ fn branch_block_creates_a_new_scope() { Stmt [32-42]: annotations: kind: ClassicalDeclarationStmt [32-42]: - symbol_id: 7 + symbol_id: 9 ty_span: [32-35] init_expr: Expr [40-41]: - ty: Int(None, true) + ty: Int(None, false) kind: Lit: Int(1) else_body: "#]], @@ -141,17 +141,17 @@ fn branch_block_creates_a_new_scope() { fn if_scope_and_else_scope_are_different() { check_stmt_kinds( " - int a = 0; + int a = 2; if (true) { int a = 1; } else { int a = 2; } ", &expect![[r#" ClassicalDeclarationStmt [5-15]: - symbol_id: 6 + symbol_id: 8 ty_span: [5-8] init_expr: Expr [13-14]: - ty: Int(None, true) - kind: Lit: Int(0) + ty: Int(None, false) + kind: Lit: Int(2) IfStmt [20-68]: condition: Expr [24-28]: ty: Bool(true) @@ -162,10 +162,10 @@ fn if_scope_and_else_scope_are_different() { Stmt [32-42]: annotations: kind: ClassicalDeclarationStmt [32-42]: - symbol_id: 7 + symbol_id: 9 ty_span: [32-35] init_expr: Expr [40-41]: - ty: Int(None, true) + ty: Int(None, false) kind: Lit: Int(1) else_body: Stmt [54-68]: annotations: @@ -173,10 +173,10 @@ fn if_scope_and_else_scope_are_different() { Stmt [56-66]: annotations: kind: ClassicalDeclarationStmt [56-66]: - symbol_id: 8 + symbol_id: 10 ty_span: [56-59] init_expr: Expr [64-65]: - ty: Int(None, true) + ty: Int(None, false) kind: Lit: Int(2) "#]], ); @@ -187,21 +187,21 @@ fn condition_cast() { check_stmt_kinds( "if (1) true;", &expect![[r#" - IfStmt [0-12]: - condition: Expr [4-5]: - ty: Bool(false) - kind: Cast [0-0]: - ty: Bool(false) - expr: Expr [4-5]: - ty: Int(None, true) - kind: Lit: Int(1) - if_body: Stmt [7-12]: - annotations: - kind: ExprStmt [7-12]: - expr: Expr [7-11]: + IfStmt [0-12]: + condition: Expr [4-5]: + ty: Bool(true) + kind: Cast [0-0]: ty: Bool(true) - kind: Lit: Bool(true) - else_body: - "#]], + expr: Expr [4-5]: + ty: Int(None, true) + kind: Lit: Int(1) + if_body: Stmt [7-12]: + annotations: + kind: ExprStmt [7-12]: + expr: Expr [7-11]: + ty: Bool(true) + kind: Lit: Bool(true) + else_body: + "#]], ); } diff --git a/compiler/qsc_qasm3/src/semantic/tests/statements/switch_stmt.rs b/compiler/qsc_qasm3/src/semantic/tests/statements/switch_stmt.rs index 6fd0950ff9..fd72b20b3b 100644 --- a/compiler/qsc_qasm3/src/semantic/tests/statements/switch_stmt.rs +++ b/compiler/qsc_qasm3/src/semantic/tests/statements/switch_stmt.rs @@ -47,7 +47,7 @@ fn not_supported_before_version_3_1() { fn cases_introduce_their_own_scope() { check_stmt_kinds( r#" - int a = 0; + int a = 3; switch (1) { case 1 { int a = 1; } case 2, 3 { int a = 2; } @@ -55,11 +55,11 @@ fn cases_introduce_their_own_scope() { "#, &expect![[r#" ClassicalDeclarationStmt [5-15]: - symbol_id: 6 + symbol_id: 8 ty_span: [5-8] init_expr: Expr [13-14]: - ty: Int(None, true) - kind: Lit: Int(0) + ty: Int(None, false) + kind: Lit: Int(3) SwitchStmt [20-101]: target: Expr [28-29]: ty: Int(None, true) @@ -74,10 +74,10 @@ fn cases_introduce_their_own_scope() { Stmt [50-60]: annotations: kind: ClassicalDeclarationStmt [50-60]: - symbol_id: 7 + symbol_id: 9 ty_span: [50-53] init_expr: Expr [58-59]: - ty: Int(None, true) + ty: Int(None, false) kind: Lit: Int(1) SwitchCase [71-95]: labels: @@ -91,10 +91,10 @@ fn cases_introduce_their_own_scope() { Stmt [83-93]: annotations: kind: ClassicalDeclarationStmt [83-93]: - symbol_id: 8 + symbol_id: 10 ty_span: [83-86] init_expr: Expr [91-92]: - ty: Int(None, true) + ty: Int(None, false) kind: Lit: Int(2) default_case: "#]], @@ -108,9 +108,9 @@ fn target_cast() { &expect![[r#" SwitchStmt [0-31]: target: Expr [8-12]: - ty: Int(None, false) + ty: Int(None, true) kind: Cast [0-0]: - ty: Int(None, false) + ty: Int(None, true) expr: Expr [8-12]: ty: Bool(true) kind: Lit: Bool(true) @@ -118,9 +118,9 @@ fn target_cast() { SwitchCase [16-29]: labels: Expr [21-26]: - ty: Int(None, false) + ty: Int(None, true) kind: Cast [0-0]: - ty: Int(None, false) + ty: Int(None, true) expr: Expr [21-26]: ty: Bool(true) kind: Lit: Bool(false) diff --git a/compiler/qsc_qasm3/src/semantic/tests/statements/while_stmt.rs b/compiler/qsc_qasm3/src/semantic/tests/statements/while_stmt.rs index 91b9fdfb3e..8d926b8974 100644 --- a/compiler/qsc_qasm3/src/semantic/tests/statements/while_stmt.rs +++ b/compiler/qsc_qasm3/src/semantic/tests/statements/while_stmt.rs @@ -8,7 +8,7 @@ use expect_test::expect; fn single_stmt_body_doesnt_creates_its_own_scope() { check_stmt_kinds( " - int a = 0; + int a = 3; while(true) int a = 1; ", &expect![[r#" @@ -18,11 +18,11 @@ fn single_stmt_body_doesnt_creates_its_own_scope() { Stmt [5-15]: annotations: kind: ClassicalDeclarationStmt [5-15]: - symbol_id: 6 + symbol_id: 8 ty_span: [5-8] init_expr: Expr [13-14]: - ty: Int(None, true) - kind: Lit: Int(0) + ty: Int(None, false) + kind: Lit: Int(3) Stmt [20-42]: annotations: kind: WhileLoop [20-42]: @@ -32,17 +32,17 @@ fn single_stmt_body_doesnt_creates_its_own_scope() { body: Stmt [32-42]: annotations: kind: ClassicalDeclarationStmt [32-42]: - symbol_id: 6 + symbol_id: 8 ty_span: [32-35] init_expr: Expr [40-41]: - ty: Int(None, true) + ty: Int(None, false) kind: Lit: Int(1) [Qsc.Qasm3.Compile.RedefinedSymbol x Redefined symbol: a. ,-[test:3:21] - 2 | int a = 0; + 2 | int a = 3; 3 | while(true) int a = 1; : ^ 4 | @@ -55,16 +55,16 @@ fn single_stmt_body_doesnt_creates_its_own_scope() { fn block_body_creates_its_own_scope() { check_stmt_kinds( " - int a = 0; + int a = 3; while(true) { int a = 1; } ", &expect![[r#" ClassicalDeclarationStmt [5-15]: - symbol_id: 6 + symbol_id: 8 ty_span: [5-8] init_expr: Expr [13-14]: - ty: Int(None, true) - kind: Lit: Int(0) + ty: Int(None, false) + kind: Lit: Int(3) WhileLoop [20-46]: condition: Expr [26-30]: ty: Bool(true) @@ -75,10 +75,10 @@ fn block_body_creates_its_own_scope() { Stmt [34-44]: annotations: kind: ClassicalDeclarationStmt [34-44]: - symbol_id: 7 + symbol_id: 9 ty_span: [34-37] init_expr: Expr [42-43]: - ty: Int(None, true) + ty: Int(None, false) kind: Lit: Int(1) "#]], ); @@ -91,9 +91,9 @@ fn condition_cast() { &expect![[r#" WhileLoop [0-15]: condition: Expr [7-8]: - ty: Bool(false) + ty: Bool(true) kind: Cast [0-0]: - ty: Bool(false) + ty: Bool(true) expr: Expr [7-8]: ty: Int(None, true) kind: Lit: Int(1) diff --git a/compiler/qsc_qasm3/src/semantic/types.rs b/compiler/qsc_qasm3/src/semantic/types.rs index 62ccc95288..aa2ed00e38 100644 --- a/compiler/qsc_qasm3/src/semantic/types.rs +++ b/compiler/qsc_qasm3/src/semantic/types.rs @@ -177,187 +177,43 @@ impl Type { #[must_use] pub fn get_indexed_type(&self) -> Option { let ty = match self { - Type::BitArray(dims, is_const) => match dims { - ArrayDimensions::One(_) => Type::Bit(*is_const), - ArrayDimensions::Two(d1, _) => Type::BitArray(ArrayDimensions::One(*d1), *is_const), - ArrayDimensions::Three(d1, d2, _) => { - Type::BitArray(ArrayDimensions::Two(*d1, *d2), *is_const) - } - ArrayDimensions::Four(d1, d2, d3, _) => { - Type::BitArray(ArrayDimensions::Three(*d1, *d2, *d3), *is_const) - } - ArrayDimensions::Five(d1, d2, d3, d4, _) => { - Type::BitArray(ArrayDimensions::Four(*d1, *d2, *d3, *d4), *is_const) - } - ArrayDimensions::Six(d1, d2, d3, d4, d5, _) => { - Type::BitArray(ArrayDimensions::Five(*d1, *d2, *d3, *d4, *d5), *is_const) - } - ArrayDimensions::Seven(d1, d2, d3, d4, d5, d6, _) => Type::BitArray( - ArrayDimensions::Six(*d1, *d2, *d3, *d4, *d5, *d6), - *is_const, - ), - ArrayDimensions::Err => Type::Err, - }, - Type::QubitArray(dims) => match dims { - ArrayDimensions::One(_) => Type::Qubit, - ArrayDimensions::Two(d1, _) => Type::QubitArray(ArrayDimensions::One(*d1)), - ArrayDimensions::Three(d1, d2, _) => { - Type::QubitArray(ArrayDimensions::Two(*d1, *d2)) - } - ArrayDimensions::Four(d1, d2, d3, _) => { - Type::QubitArray(ArrayDimensions::Three(*d1, *d2, *d3)) - } - ArrayDimensions::Five(d1, d2, d3, d4, _) => { - Type::QubitArray(ArrayDimensions::Four(*d1, *d2, *d3, *d4)) - } - ArrayDimensions::Six(d1, d2, d3, d4, d5, _) => { - Type::QubitArray(ArrayDimensions::Five(*d1, *d2, *d3, *d4, *d5)) - } - ArrayDimensions::Seven(d1, d2, d3, d4, d5, d6, _) => { - Type::QubitArray(ArrayDimensions::Six(*d1, *d2, *d3, *d4, *d5, *d6)) - } - ArrayDimensions::Err => Type::Err, - }, - Type::BoolArray(dims) => match dims { - ArrayDimensions::One(_) => Type::Bool(false), - ArrayDimensions::Two(d1, _) => Type::BoolArray(ArrayDimensions::One(*d1)), - ArrayDimensions::Three(d1, d2, _) => { - Type::BoolArray(ArrayDimensions::Two(*d1, *d2)) - } - ArrayDimensions::Four(d1, d2, d3, _) => { - Type::BoolArray(ArrayDimensions::Three(*d1, *d2, *d3)) - } - ArrayDimensions::Five(d1, d2, d3, d4, _) => { - Type::BoolArray(ArrayDimensions::Four(*d1, *d2, *d3, *d4)) - } - ArrayDimensions::Six(d1, d2, d3, d4, d5, _) => { - Type::BoolArray(ArrayDimensions::Five(*d1, *d2, *d3, *d4, *d5)) - } - ArrayDimensions::Seven(d1, d2, d3, d4, d5, d6, _) => { - Type::BoolArray(ArrayDimensions::Six(*d1, *d2, *d3, *d4, *d5, *d6)) - } - ArrayDimensions::Err => Type::Err, - }, - Type::AngleArray(size, dims) => match dims { - ArrayDimensions::One(_) => Type::Angle(*size, false), - ArrayDimensions::Two(d1, _) => Type::AngleArray(*size, ArrayDimensions::One(*d1)), - ArrayDimensions::Three(d1, d2, _) => { - Type::AngleArray(*size, ArrayDimensions::Two(*d1, *d2)) - } - ArrayDimensions::Four(d1, d2, d3, _) => { - Type::AngleArray(*size, ArrayDimensions::Three(*d1, *d2, *d3)) - } - ArrayDimensions::Five(d1, d2, d3, d4, _) => { - Type::AngleArray(*size, ArrayDimensions::Four(*d1, *d2, *d3, *d4)) - } - ArrayDimensions::Six(d1, d2, d3, d4, d5, _) => { - Type::AngleArray(*size, ArrayDimensions::Five(*d1, *d2, *d3, *d4, *d5)) - } - ArrayDimensions::Seven(d1, d2, d3, d4, d5, d6, _) => { - Type::AngleArray(*size, ArrayDimensions::Six(*d1, *d2, *d3, *d4, *d5, *d6)) - } - ArrayDimensions::Err => Type::Err, - }, - Type::ComplexArray(size, dims) => match dims { - ArrayDimensions::One(_) => Type::Complex(*size, false), - ArrayDimensions::Two(d1, _) => Type::ComplexArray(*size, ArrayDimensions::One(*d1)), - ArrayDimensions::Three(d1, d2, _) => { - Type::ComplexArray(*size, ArrayDimensions::Two(*d1, *d2)) - } - ArrayDimensions::Four(d1, d2, d3, _) => { - Type::ComplexArray(*size, ArrayDimensions::Three(*d1, *d2, *d3)) - } - ArrayDimensions::Five(d1, d2, d3, d4, _) => { - Type::ComplexArray(*size, ArrayDimensions::Four(*d1, *d2, *d3, *d4)) - } - ArrayDimensions::Six(d1, d2, d3, d4, d5, _) => { - Type::ComplexArray(*size, ArrayDimensions::Five(*d1, *d2, *d3, *d4, *d5)) - } - ArrayDimensions::Seven(d1, d2, d3, d4, d5, d6, _) => { - Type::ComplexArray(*size, ArrayDimensions::Six(*d1, *d2, *d3, *d4, *d5, *d6)) - } - ArrayDimensions::Err => Type::Err, - }, - Type::DurationArray(dims) => match dims { - ArrayDimensions::One(_) => Type::Duration(false), - ArrayDimensions::Two(d1, _) => Type::DurationArray(ArrayDimensions::One(*d1)), - ArrayDimensions::Three(d1, d2, _) => { - Type::DurationArray(ArrayDimensions::Two(*d1, *d2)) - } - ArrayDimensions::Four(d1, d2, d3, _) => { - Type::DurationArray(ArrayDimensions::Three(*d1, *d2, *d3)) - } - ArrayDimensions::Five(d1, d2, d3, d4, _) => { - Type::DurationArray(ArrayDimensions::Four(*d1, *d2, *d3, *d4)) - } - ArrayDimensions::Six(d1, d2, d3, d4, d5, _) => { - Type::DurationArray(ArrayDimensions::Five(*d1, *d2, *d3, *d4, *d5)) - } - ArrayDimensions::Seven(d1, d2, d3, d4, d5, d6, _) => { - Type::DurationArray(ArrayDimensions::Six(*d1, *d2, *d3, *d4, *d5, *d6)) - } - ArrayDimensions::Err => Type::Err, - }, - Type::FloatArray(size, dims) => match dims { - ArrayDimensions::One(_) => Type::Float(*size, false), - ArrayDimensions::Two(d1, _) => Type::FloatArray(*size, ArrayDimensions::One(*d1)), - ArrayDimensions::Three(d1, d2, _) => { - Type::FloatArray(*size, ArrayDimensions::Two(*d1, *d2)) - } - ArrayDimensions::Four(d1, d2, d3, _) => { - Type::FloatArray(*size, ArrayDimensions::Three(*d1, *d2, *d3)) - } - ArrayDimensions::Five(d1, d2, d3, d4, _) => { - Type::FloatArray(*size, ArrayDimensions::Four(*d1, *d2, *d3, *d4)) - } - ArrayDimensions::Six(d1, d2, d3, d4, d5, _) => { - Type::FloatArray(*size, ArrayDimensions::Five(*d1, *d2, *d3, *d4, *d5)) - } - ArrayDimensions::Seven(d1, d2, d3, d4, d5, d6, _) => { - Type::FloatArray(*size, ArrayDimensions::Six(*d1, *d2, *d3, *d4, *d5, *d6)) - } - ArrayDimensions::Err => Type::Err, - }, - Type::IntArray(size, dims) => match dims { - ArrayDimensions::One(_) => Type::Int(*size, false), - ArrayDimensions::Two(d1, _) => Type::IntArray(*size, ArrayDimensions::One(*d1)), - ArrayDimensions::Three(d1, d2, _) => { - Type::IntArray(*size, ArrayDimensions::Two(*d1, *d2)) - } - ArrayDimensions::Four(d1, d2, d3, _) => { - Type::IntArray(*size, ArrayDimensions::Three(*d1, *d2, *d3)) - } - ArrayDimensions::Five(d1, d2, d3, d4, _) => { - Type::IntArray(*size, ArrayDimensions::Four(*d1, *d2, *d3, *d4)) - } - ArrayDimensions::Six(d1, d2, d3, d4, d5, _) => { - Type::IntArray(*size, ArrayDimensions::Five(*d1, *d2, *d3, *d4, *d5)) - } - ArrayDimensions::Seven(d1, d2, d3, d4, d5, d6, _) => { - Type::IntArray(*size, ArrayDimensions::Six(*d1, *d2, *d3, *d4, *d5, *d6)) - } - ArrayDimensions::Err => Type::Err, - }, - Type::UIntArray(size, dims) => match dims { - ArrayDimensions::One(_) => Type::UInt(*size, false), - ArrayDimensions::Two(d1, _) => Type::UIntArray(*size, ArrayDimensions::One(*d1)), - ArrayDimensions::Three(d1, d2, _) => { - Type::UIntArray(*size, ArrayDimensions::Two(*d1, *d2)) - } - ArrayDimensions::Four(d1, d2, d3, _) => { - Type::UIntArray(*size, ArrayDimensions::Three(*d1, *d2, *d3)) - } - ArrayDimensions::Five(d1, d2, d3, d4, _) => { - Type::UIntArray(*size, ArrayDimensions::Four(*d1, *d2, *d3, *d4)) - } - ArrayDimensions::Six(d1, d2, d3, d4, d5, _) => { - Type::UIntArray(*size, ArrayDimensions::Five(*d1, *d2, *d3, *d4, *d5)) - } - ArrayDimensions::Seven(d1, d2, d3, d4, d5, d6, _) => { - Type::UIntArray(*size, ArrayDimensions::Six(*d1, *d2, *d3, *d4, *d5, *d6)) - } - ArrayDimensions::Err => Type::Err, - }, + Type::BitArray(dims, is_const) => indexed_type_builder( + || Type::Bit(*is_const), + |d| Type::BitArray(d, *is_const), + dims, + ), + Type::QubitArray(dims) => indexed_type_builder(|| Type::Qubit, Type::QubitArray, dims), + Type::BoolArray(dims) => { + indexed_type_builder(|| Type::Bool(false), Type::BoolArray, dims) + } + Type::AngleArray(size, dims) => indexed_type_builder( + || Type::Angle(*size, false), + |d| Type::AngleArray(*size, d), + dims, + ), + Type::ComplexArray(size, dims) => indexed_type_builder( + || Type::Complex(*size, false), + |d| Type::ComplexArray(*size, d), + dims, + ), + Type::DurationArray(dims) => { + indexed_type_builder(|| Type::Duration(false), Type::DurationArray, dims) + } + Type::FloatArray(size, dims) => indexed_type_builder( + || Type::Float(*size, false), + |d| Type::FloatArray(*size, d), + dims, + ), + Type::IntArray(size, dims) => indexed_type_builder( + || Type::Int(*size, false), + |d| Type::IntArray(*size, d), + dims, + ), + Type::UIntArray(size, dims) => indexed_type_builder( + || Type::UInt(*size, false), + |d| Type::UIntArray(*size, d), + dims, + ), _ => return None, }; Some(ty) @@ -379,6 +235,22 @@ impl Type { } } + pub(crate) fn as_non_const(&self) -> Type { + match self { + Type::Bit(_) => Self::Bit(false), + Type::Bool(_) => Self::Bool(false), + Type::Duration(_) => Self::Duration(false), + Type::Stretch(_) => Self::Stretch(false), + Type::Angle(w, _) => Self::Angle(*w, false), + Type::Complex(w, _) => Self::Complex(*w, false), + Type::Float(w, _) => Self::Float(*w, false), + Type::Int(w, _) => Self::Int(*w, false), + Type::UInt(w, _) => Self::UInt(*w, false), + Type::BitArray(dims, _) => Self::BitArray(dims.clone(), false), + _ => self.clone(), + } + } + pub(crate) fn is_quantum(&self) -> bool { matches!( self, @@ -387,7 +259,28 @@ impl Type { } } -#[derive(Debug, Clone, PartialEq, Eq, Hash, Default)] +fn indexed_type_builder( + ty: impl Fn() -> Type, + ty_array: impl Fn(ArrayDimensions) -> Type, + dims: &ArrayDimensions, +) -> Type { + match dims.clone() { + ArrayDimensions::One(_) => ty(), + ArrayDimensions::Two(d1, _) => ty_array(ArrayDimensions::One(d1)), + ArrayDimensions::Three(d1, d2, _) => ty_array(ArrayDimensions::Two(d1, d2)), + ArrayDimensions::Four(d1, d2, d3, _) => ty_array(ArrayDimensions::Three(d1, d2, d3)), + ArrayDimensions::Five(d1, d2, d3, d4, _) => ty_array(ArrayDimensions::Four(d1, d2, d3, d4)), + ArrayDimensions::Six(d1, d2, d3, d4, d5, _) => { + ty_array(ArrayDimensions::Five(d1, d2, d3, d4, d5)) + } + ArrayDimensions::Seven(d1, d2, d3, d4, d5, d6, _) => { + ty_array(ArrayDimensions::Six(d1, d2, d3, d4, d5, d6)) + } + ArrayDimensions::Err => Type::Err, + } +} + +#[derive(Debug, Clone, Default, Eq, Hash, PartialEq)] pub enum ArrayDimensions { One(u32), Two(u32, u32), @@ -400,6 +293,12 @@ pub enum ArrayDimensions { Err, } +impl From for ArrayDimensions { + fn from(value: u32) -> Self { + Self::One(value) + } +} + impl Display for ArrayDimensions { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { match self { @@ -499,8 +398,10 @@ pub(crate) fn promote_to_uint_ty( } fn get_uint_ty(ty: &Type) -> Option { - if matches!(ty, Type::UInt(..) | Type::Angle(..)) { + if matches!(ty, Type::Int(..) | Type::UInt(..) | Type::Angle(..)) { Some(Type::UInt(ty.width(), ty.is_const())) + } else if matches!(ty, Type::Bool(..) | Type::Bit(..)) { + Some(Type::UInt(Some(1), ty.is_const())) } else if let Type::BitArray(dims, _) = ty { match dims { ArrayDimensions::One(d) => Some(Type::UInt(Some(*d), ty.is_const())), diff --git a/compiler/qsc_qasm3/src/tests/expression/implicit_cast_from_bit.rs b/compiler/qsc_qasm3/src/tests/expression/implicit_cast_from_bit.rs index 10dd05ef3a..b08eb71371 100644 --- a/compiler/qsc_qasm3/src/tests/expression/implicit_cast_from_bit.rs +++ b/compiler/qsc_qasm3/src/tests/expression/implicit_cast_from_bit.rs @@ -199,6 +199,6 @@ fn to_implicit_float_implicitly_fails() { panic!("Expected error") }; - expect![r#"Cannot cast expression of type Bit(False) to type Float(None, False)"#] + expect!["Cannot cast expression of type Bit(false) to type Float(None, false)"] .assert_eq(&error[0].to_string()); } diff --git a/compiler/qsc_qasm3/src/tests/expression/implicit_cast_from_bitarray.rs b/compiler/qsc_qasm3/src/tests/expression/implicit_cast_from_bitarray.rs index 73489272a2..3d879ca671 100644 --- a/compiler/qsc_qasm3/src/tests/expression/implicit_cast_from_bitarray.rs +++ b/compiler/qsc_qasm3/src/tests/expression/implicit_cast_from_bitarray.rs @@ -106,7 +106,7 @@ fn to_int_with_higher_width_implicitly_fails() { panic!("Expected error") }; - expect![r#"Cannot cast expression of type BitArray(D1(5), False) to type Int(Some(6), False)"#] + expect!["Cannot cast expression of type BitArray(One(5), false) to type Int(Some(6), false)"] .assert_eq(&error[0].to_string()); } @@ -121,7 +121,7 @@ fn to_int_with_higher_width_decl_implicitly_fails() { panic!("Expected error") }; - expect![r#"Cannot cast expression of type BitArray(D1(5), False) to type Int(Some(6), False)"#] + expect!["Cannot cast expression of type BitArray(One(5), false) to type Int(Some(6), false)"] .assert_eq(&error[0].to_string()); } @@ -137,7 +137,7 @@ fn to_int_with_lower_width_implicitly_fails() { panic!("Expected error") }; - expect![r#"Cannot cast expression of type BitArray(D1(5), False) to type Int(Some(4), False)"#] + expect!["Cannot cast expression of type BitArray(One(5), false) to type Int(Some(4), false)"] .assert_eq(&error[0].to_string()); } @@ -152,6 +152,6 @@ fn to_int_with_lower_width_decl_implicitly_fails() { panic!("Expected error") }; - expect![r#"Cannot cast expression of type BitArray(D1(5), False) to type Int(Some(4), False)"#] + expect!["Cannot cast expression of type BitArray(One(5), false) to type Int(Some(4), false)"] .assert_eq(&error[0].to_string()); } diff --git a/compiler/qsc_qasm3/src/tests/expression/implicit_cast_from_float.rs b/compiler/qsc_qasm3/src/tests/expression/implicit_cast_from_float.rs index d9280621ed..721778b5f7 100644 --- a/compiler/qsc_qasm3/src/tests/expression/implicit_cast_from_float.rs +++ b/compiler/qsc_qasm3/src/tests/expression/implicit_cast_from_float.rs @@ -17,7 +17,7 @@ fn to_bit_implicitly() { panic!("Expected error") }; - expect![r#"Cannot cast expression of type Float(None, False) to type Bit(False)"#] + expect!["Cannot cast expression of type Float(None, false) to type Bit(false)"] .assert_eq(&error[0].to_string()); } diff --git a/compiler/qsc_qasm3/src/tests/statement.rs b/compiler/qsc_qasm3/src/tests/statement.rs index 9938cb4160..495d7df324 100644 --- a/compiler/qsc_qasm3/src/tests/statement.rs +++ b/compiler/qsc_qasm3/src/tests/statement.rs @@ -2,6 +2,7 @@ // Licensed under the MIT License. mod annotation; +mod const_eval; mod end; mod for_loop; mod gate_call; diff --git a/compiler/qsc_qasm3/src/tests/statement/const_eval.rs b/compiler/qsc_qasm3/src/tests/statement/const_eval.rs new file mode 100644 index 0000000000..e6daded79f --- /dev/null +++ b/compiler/qsc_qasm3/src/tests/statement/const_eval.rs @@ -0,0 +1,1732 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +//! The tests in this file need to check that const exprs are +//! evaluatable at lowering time. To do that we use them in +//! contexts where they need to be const-evaluated, like array +//! sizes or type widths. + +use crate::tests::compile_qasm_to_qsharp; +use expect_test::expect; +use miette::Report; + +#[test] +fn const_exprs_work_in_bitarray_size_position() -> miette::Result<(), Vec> { + let source = r#" + const int a = 1; + const int b = 2 + a; + const int c = a + 3; + bit[b] r1; + bit[c] r2; + "#; + + let qsharp = compile_qasm_to_qsharp(source)?; + expect![[r#" + let a = 1; + let b = 2 + a; + let c = a + 3; + mutable r1 = [Zero, Zero, Zero]; + mutable r2 = [Zero, Zero, Zero, Zero]; + "#]] + .assert_eq(&qsharp); + Ok(()) +} + +#[test] +fn const_exprs_implicit_cast_work_in_bitarray_size_position() -> miette::Result<(), Vec> { + let source = r#" + const int a = 1; + const float b = 2.0 + a; + const float c = a + 3.0; + bit[b] r1; + bit[c] r2; + "#; + + let qsharp = compile_qasm_to_qsharp(source)?; + expect![[r#" + let a = 1; + let b = 2. + Microsoft.Quantum.Convert.IntAsDouble(a); + let c = Microsoft.Quantum.Convert.IntAsDouble(a) + 3.; + mutable r1 = [Zero, Zero, Zero]; + mutable r2 = [Zero, Zero, Zero, Zero]; + "#]] + .assert_eq(&qsharp); + Ok(()) +} + +#[test] +fn non_const_exprs_fail_in_bitarray_size_position() { + let source = r#" + const int a = 1; + int b = 2 + a; + int c = a + 3; + bit[b] r1; + bit[c] r2; + "#; + + let Err(errs) = compile_qasm_to_qsharp(source) else { + panic!("should have generated an error"); + }; + let errs: Vec<_> = errs.iter().map(|e| format!("{e:?}")).collect(); + let errs_string = errs.join("\n"); + expect![[r#" + Qsc.Qasm3.Compile.ExprMustBeConst + + x designator must be a const expression + ,-[Test.qasm:5:13] + 4 | int c = a + 3; + 5 | bit[b] r1; + : ^ + 6 | bit[c] r2; + `---- + + Qsc.Qasm3.Compile.ExprMustBeConst + + x designator must be a const expression + ,-[Test.qasm:6:13] + 5 | bit[b] r1; + 6 | bit[c] r2; + : ^ + 7 | + `---- + "#]] + .assert_eq(&errs_string); +} + +#[test] +fn can_assign_const_expr_to_non_const_decl() -> miette::Result<(), Vec> { + let source = r#" + const int a = 1; + const int b = 2; + int c = a + b; + "#; + + let qsharp = compile_qasm_to_qsharp(source)?; + expect![[r#" + let a = 1; + let b = 2; + mutable c = a + b; + "#]] + .assert_eq(&qsharp); + Ok(()) +} + +#[test] +fn ident_const() -> miette::Result<(), Vec> { + let source = r#" + const uint a = 1; + bit[a] r; + "#; + + let qsharp = compile_qasm_to_qsharp(source)?; + expect![[r#" + let a = 1; + mutable r = [Zero]; + "#]] + .assert_eq(&qsharp); + Ok(()) +} + +#[test] +#[ignore = "indexed ident is not yet supported"] +fn indexed_ident() -> miette::Result<(), Vec> { + let source = r#" + const uint[2] a = {1, 2}; + bit[a[1]] r; + "#; + + let qsharp = compile_qasm_to_qsharp(source)?; + expect![[r#" + let a = 1; + let b = 2; + mutable c = a + b; + "#]] + .assert_eq(&qsharp); + Ok(()) +} + +// UnaryOp Float + +#[test] +fn unary_op_neg_float() -> miette::Result<(), Vec> { + let source = r#" + const float a = -1.0; + const float b = -a; + bit[b] r; + "#; + + let qsharp = compile_qasm_to_qsharp(source)?; + expect![[r#" + let a = -1.; + let b = -a; + mutable r = [Zero]; + "#]] + .assert_eq(&qsharp); + Ok(()) +} + +#[test] +fn unary_op_neg_int() -> miette::Result<(), Vec> { + let source = r#" + const int a = -1; + const int b = -a; + bit[b] r; + "#; + + let qsharp = compile_qasm_to_qsharp(source)?; + expect![[r#" + let a = -1; + let b = -a; + mutable r = [Zero]; + "#]] + .assert_eq(&qsharp); + Ok(()) +} + +#[test] +#[ignore = "casting float to angle is not yet supported"] +fn unary_op_neg_angle() -> miette::Result<(), Vec> { + let source = r#" + const angle a = -1.0; + const bool b = a; + bit[b] r; + "#; + + let qsharp = compile_qasm_to_qsharp(source)?; + expect![[r#""#]].assert_eq(&qsharp); + Ok(()) +} + +#[test] +fn unary_op_negb_uint() -> miette::Result<(), Vec> { + let source = r#" + const uint[3] a = 5; + const uint[3] b = ~a; + bit[b] r; + "#; + + let qsharp = compile_qasm_to_qsharp(source)?; + expect![[r#" + let a = 5; + let b = ~~~a; + mutable r = [Zero, Zero]; + "#]] + .assert_eq(&qsharp); + Ok(()) +} + +#[test] +#[ignore = "angles are not yet supported"] +fn unary_op_negb_angle() { + let source = r#" + const angle a = 1.0; + const bool b = ~a; + bit[b] r; + "#; + + let Err(errs) = compile_qasm_to_qsharp(source) else { + panic!("should have generated an error"); + }; + let errs: Vec<_> = errs.iter().map(|e| format!("{e:?}")).collect(); + let errs_string = errs.join("\n"); + expect![[r#""#]].assert_eq(&errs_string); +} + +#[test] +fn unary_op_negb_bit() -> miette::Result<(), Vec> { + let source = r#" + const bit a = 0; + const bit b = ~a; + bit[b] r; + "#; + + let qsharp = compile_qasm_to_qsharp(source)?; + expect![[r#" + let a = Zero; + let b = ~~~a; + mutable r = [Zero]; + "#]] + .assert_eq(&qsharp); + Ok(()) +} + +#[test] +fn unary_op_negb_bitarray() -> miette::Result<(), Vec> { + let source = r#" + const bit[3] a = "101"; + const uint[3] b = ~a; + bit[b] r; + "#; + + let qsharp = compile_qasm_to_qsharp(source)?; + expect![[r#" + function __ResultArrayAsIntBE__(results : Result[]) : Int { + Microsoft.Quantum.Convert.ResultArrayAsInt(Microsoft.Quantum.Arrays.Reversed(results)) + } + let a = [One, Zero, One]; + let b = __ResultArrayAsIntBE__(~~~a); + mutable r = [Zero, Zero]; + "#]] + .assert_eq(&qsharp); + Ok(()) +} + +// BinaryOp + +#[test] +fn lhs_ty_equals_rhs_ty_assumption_holds() -> miette::Result<(), Vec> { + let source = r#" + const int a = 1; + const float b = 2.0; + const uint c = a + b; + bit[c] r; + "#; + + let qsharp = compile_qasm_to_qsharp(source)?; + expect![[r#" + let a = 1; + let b = 2.; + let c = Microsoft.Quantum.Math.Truncate(Microsoft.Quantum.Convert.IntAsDouble(a) + b); + mutable r = [Zero, Zero, Zero]; + "#]] + .assert_eq(&qsharp); + Ok(()) +} + +// BinaryOp: Bit Shifts + +// Shl + +#[test] +fn binary_op_shl_uint() -> miette::Result<(), Vec> { + let source = r#" + const uint a = 1; + const uint b = a << 2; + bit[b] r; + "#; + + let qsharp = compile_qasm_to_qsharp(source)?; + expect![[r#" + let a = 1; + let b = a <<< 2; + mutable r = [Zero, Zero, Zero, Zero]; + "#]] + .assert_eq(&qsharp); + Ok(()) +} + +#[test] +#[ignore = "angles are not yet supported"] +fn binary_op_shl_angle() -> miette::Result<(), Vec> { + let source = r#" + const angle a = 1; + const angle b = a << 2; + const bool c = b; + bit[c] r; + "#; + + let qsharp = compile_qasm_to_qsharp(source)?; + expect![[r#""#]].assert_eq(&qsharp); + Ok(()) +} + +#[test] +fn binary_op_shl_bit() -> miette::Result<(), Vec> { + let source = r#" + const bit a = 1; + const bit b = a << 2; + bit[b] r; + "#; + + let qsharp = compile_qasm_to_qsharp(source)?; + expect![[r#" + function __ResultAsInt__(input : Result) : Int { + if Microsoft.Quantum.Convert.ResultAsBool(input) { + 1 + } else { + 0 + } + } + let a = One; + let b = if __ResultAsInt__(a) <<< 2 == 0 { + One + } else { + Zero + }; + mutable r = []; + "#]] + .assert_eq(&qsharp); + Ok(()) +} + +#[test] +fn binary_op_shl_bitarray() -> miette::Result<(), Vec> { + let source = r#" + const bit[3] a = "101"; + const bit[3] b = a << 2; + bit[b] r; + "#; + + let qsharp = compile_qasm_to_qsharp(source)?; + expect![[r#" + function __BoolAsResult__(input : Bool) : Result { + Microsoft.Quantum.Convert.BoolAsResult(input) + } + function __IntAsResultArrayBE__(number : Int, bits : Int) : Result[] { + mutable runningValue = number; + mutable result = []; + for _ in 1..bits { + set result += [__BoolAsResult__((runningValue &&& 1) != 0)]; + set runningValue >>>= 1; + } + Microsoft.Quantum.Arrays.Reversed(result) + } + function __ResultArrayAsIntBE__(results : Result[]) : Int { + Microsoft.Quantum.Convert.ResultArrayAsInt(Microsoft.Quantum.Arrays.Reversed(results)) + } + let a = [One, Zero, One]; + let b = __IntAsResultArrayBE__(__ResultArrayAsIntBE__(a) <<< 2, 3); + mutable r = [Zero, Zero, Zero, Zero]; + "#]] + .assert_eq(&qsharp); + Ok(()) +} + +#[test] +fn binary_op_shl_creg_fails() { + let source = r#" + const creg a[3] = "101"; + const creg b[3] = a << 2; + bit[b] r; + "#; + + let Err(errs) = compile_qasm_to_qsharp(source) else { + panic!("should have generated an error"); + }; + let errs: Vec<_> = errs.iter().map(|e| format!("{e:?}")).collect(); + let errs_string = errs.join("\n"); + expect![[r#" + Qasm3.Parse.Rule + + x expected scalar or array type, found keyword `creg` + ,-[Test.qasm:2:15] + 1 | + 2 | const creg a[3] = "101"; + : ^^^^ + 3 | const creg b[3] = a << 2; + `---- + + Qasm3.Parse.Rule + + x expected scalar or array type, found keyword `creg` + ,-[Test.qasm:3:15] + 2 | const creg a[3] = "101"; + 3 | const creg b[3] = a << 2; + : ^^^^ + 4 | bit[b] r; + `---- + + Qsc.Qasm3.Compile.UndefinedSymbol + + x Undefined symbol: b. + ,-[Test.qasm:4:13] + 3 | const creg b[3] = a << 2; + 4 | bit[b] r; + : ^ + 5 | + `---- + + Qsc.Qasm3.Compile.CannotCast + + x Cannot cast expression of type Err to type UInt(None, true) + ,-[Test.qasm:4:13] + 3 | const creg b[3] = a << 2; + 4 | bit[b] r; + : ^ + 5 | + `---- + + Qsc.Qasm3.Compile.ExprMustBeConst + + x designator must be a const expression + ,-[Test.qasm:4:13] + 3 | const creg b[3] = a << 2; + 4 | bit[b] r; + : ^ + 5 | + `---- + "#]] + .assert_eq(&errs_string); +} + +// Shr + +#[test] +fn binary_op_shr_uint() -> miette::Result<(), Vec> { + let source = r#" + const uint a = 5; + const uint b = a >> 2; + bit[b] r; + "#; + + let qsharp = compile_qasm_to_qsharp(source)?; + expect![[r#" + let a = 5; + let b = a >>> 2; + mutable r = [Zero]; + "#]] + .assert_eq(&qsharp); + Ok(()) +} + +#[test] +#[ignore = "angles are not yet supported"] +fn binary_op_shr_angle() -> miette::Result<(), Vec> { + let source = r#" + const angle a = 1; + const angle b = a >> 2; + const bool c = b; + bit[c] r; + "#; + + let qsharp = compile_qasm_to_qsharp(source)?; + expect![[r#""#]].assert_eq(&qsharp); + Ok(()) +} + +#[test] +fn binary_op_shr_bit() -> miette::Result<(), Vec> { + let source = r#" + const bit a = 1; + const bit b = a >> 2; + bit[b] r; + "#; + + let qsharp = compile_qasm_to_qsharp(source)?; + expect![[r#" + function __ResultAsInt__(input : Result) : Int { + if Microsoft.Quantum.Convert.ResultAsBool(input) { + 1 + } else { + 0 + } + } + let a = One; + let b = if __ResultAsInt__(a) >>> 2 == 0 { + One + } else { + Zero + }; + mutable r = []; + "#]] + .assert_eq(&qsharp); + Ok(()) +} + +#[test] +fn binary_op_shr_bitarray() -> miette::Result<(), Vec> { + let source = r#" + const bit[4] a = "1011"; + const bit[4] b = a >> 2; + bit[b] r; + "#; + + let qsharp = compile_qasm_to_qsharp(source)?; + expect![[r#" + function __BoolAsResult__(input : Bool) : Result { + Microsoft.Quantum.Convert.BoolAsResult(input) + } + function __IntAsResultArrayBE__(number : Int, bits : Int) : Result[] { + mutable runningValue = number; + mutable result = []; + for _ in 1..bits { + set result += [__BoolAsResult__((runningValue &&& 1) != 0)]; + set runningValue >>>= 1; + } + Microsoft.Quantum.Arrays.Reversed(result) + } + function __ResultArrayAsIntBE__(results : Result[]) : Int { + Microsoft.Quantum.Convert.ResultArrayAsInt(Microsoft.Quantum.Arrays.Reversed(results)) + } + let a = [One, Zero, One, One]; + let b = __IntAsResultArrayBE__(__ResultArrayAsIntBE__(a) >>> 2, 4); + mutable r = [Zero, Zero]; + "#]] + .assert_eq(&qsharp); + Ok(()) +} + +#[test] +fn binary_op_shr_creg_fails() { + let source = r#" + const creg a[4] = "1011"; + const creg b[4] = a >> 2; + bit[b] r; + "#; + + let Err(errs) = compile_qasm_to_qsharp(source) else { + panic!("should have generated an error"); + }; + let errs: Vec<_> = errs.iter().map(|e| format!("{e:?}")).collect(); + let errs_string = errs.join("\n"); + expect![[r#" + Qasm3.Parse.Rule + + x expected scalar or array type, found keyword `creg` + ,-[Test.qasm:2:15] + 1 | + 2 | const creg a[4] = "1011"; + : ^^^^ + 3 | const creg b[4] = a >> 2; + `---- + + Qasm3.Parse.Rule + + x expected scalar or array type, found keyword `creg` + ,-[Test.qasm:3:15] + 2 | const creg a[4] = "1011"; + 3 | const creg b[4] = a >> 2; + : ^^^^ + 4 | bit[b] r; + `---- + + Qsc.Qasm3.Compile.UndefinedSymbol + + x Undefined symbol: b. + ,-[Test.qasm:4:13] + 3 | const creg b[4] = a >> 2; + 4 | bit[b] r; + : ^ + 5 | + `---- + + Qsc.Qasm3.Compile.CannotCast + + x Cannot cast expression of type Err to type UInt(None, true) + ,-[Test.qasm:4:13] + 3 | const creg b[4] = a >> 2; + 4 | bit[b] r; + : ^ + 5 | + `---- + + Qsc.Qasm3.Compile.ExprMustBeConst + + x designator must be a const expression + ,-[Test.qasm:4:13] + 3 | const creg b[4] = a >> 2; + 4 | bit[b] r; + : ^ + 5 | + `---- + "#]] + .assert_eq(&errs_string); +} + +// BinaryOp: Bitwise + +// AndB + +#[test] +fn binary_op_andb_uint() -> miette::Result<(), Vec> { + let source = r#" + const uint a = 5; + const uint b = a & 6; + bit[b] r; + "#; + + let qsharp = compile_qasm_to_qsharp(source)?; + expect![[r#" + let a = 5; + let b = a &&& 6; + mutable r = [Zero, Zero, Zero, Zero]; + "#]] + .assert_eq(&qsharp); + Ok(()) +} + +#[test] +#[ignore = "angles are not yet supported"] +fn binary_op_andb_angle() -> miette::Result<(), Vec> { + let source = r#" + const angle a = 1; + const angle b = a & 2; + const bool c = b; + bit[c] r; + "#; + + let qsharp = compile_qasm_to_qsharp(source)?; + expect![[r#""#]].assert_eq(&qsharp); + Ok(()) +} + +#[test] +fn binary_op_andb_bit() -> miette::Result<(), Vec> { + let source = r#" + const bit a = 1; + const bit b = a & 0; + bit[b] r; + "#; + + let qsharp = compile_qasm_to_qsharp(source)?; + expect![[r#" + function __ResultAsInt__(input : Result) : Int { + if Microsoft.Quantum.Convert.ResultAsBool(input) { + 1 + } else { + 0 + } + } + let a = One; + let b = if __ResultAsInt__(a) &&& 0 == 0 { + One + } else { + Zero + }; + mutable r = []; + "#]] + .assert_eq(&qsharp); + Ok(()) +} + +#[test] +fn binary_op_andb_bitarray() -> miette::Result<(), Vec> { + let source = r#" + const bit[4] a = "1011"; + const bit[4] b = a & "0110"; + bit[b] r; + "#; + + let qsharp = compile_qasm_to_qsharp(source)?; + expect![[r#" + function __BoolAsResult__(input : Bool) : Result { + Microsoft.Quantum.Convert.BoolAsResult(input) + } + function __IntAsResultArrayBE__(number : Int, bits : Int) : Result[] { + mutable runningValue = number; + mutable result = []; + for _ in 1..bits { + set result += [__BoolAsResult__((runningValue &&& 1) != 0)]; + set runningValue >>>= 1; + } + Microsoft.Quantum.Arrays.Reversed(result) + } + function __ResultArrayAsIntBE__(results : Result[]) : Int { + Microsoft.Quantum.Convert.ResultArrayAsInt(Microsoft.Quantum.Arrays.Reversed(results)) + } + let a = [One, Zero, One, One]; + let b = __IntAsResultArrayBE__(__ResultArrayAsIntBE__(a) &&& __ResultArrayAsIntBE__([Zero, One, One, Zero]), 4); + mutable r = [Zero, Zero]; + "#]] + .assert_eq(&qsharp); + Ok(()) +} + +// OrB + +#[test] +fn binary_op_orb_uint() -> miette::Result<(), Vec> { + let source = r#" + const uint a = 5; + const uint b = a | 6; + bit[b] r; + "#; + + let qsharp = compile_qasm_to_qsharp(source)?; + expect![[r#" + let a = 5; + let b = a ||| 6; + mutable r = [Zero, Zero, Zero, Zero, Zero, Zero, Zero]; + "#]] + .assert_eq(&qsharp); + Ok(()) +} + +#[test] +#[ignore = "angles are not yet supported"] +fn binary_op_orb_angle() -> miette::Result<(), Vec> { + let source = r#" + const angle a = 1.0; + const angle b = a | 2.0; + const bool c = b; + bit[c] r; + "#; + + let qsharp = compile_qasm_to_qsharp(source)?; + expect![[r#""#]].assert_eq(&qsharp); + Ok(()) +} + +#[test] +fn binary_op_orb_bit() -> miette::Result<(), Vec> { + let source = r#" + const bit a = 1; + const bit b = a | 0; + bit[b] r; + "#; + + let qsharp = compile_qasm_to_qsharp(source)?; + expect![[r#" + function __ResultAsInt__(input : Result) : Int { + if Microsoft.Quantum.Convert.ResultAsBool(input) { + 1 + } else { + 0 + } + } + let a = One; + let b = if __ResultAsInt__(a) ||| 0 == 0 { + One + } else { + Zero + }; + mutable r = [Zero]; + "#]] + .assert_eq(&qsharp); + Ok(()) +} + +#[test] +fn binary_op_orb_bitarray() -> miette::Result<(), Vec> { + let source = r#" + const bit[3] a = "001"; + const bit[3] b = a | "100"; + bit[b] r; + "#; + + let qsharp = compile_qasm_to_qsharp(source)?; + expect![[r#" + function __BoolAsResult__(input : Bool) : Result { + Microsoft.Quantum.Convert.BoolAsResult(input) + } + function __IntAsResultArrayBE__(number : Int, bits : Int) : Result[] { + mutable runningValue = number; + mutable result = []; + for _ in 1..bits { + set result += [__BoolAsResult__((runningValue &&& 1) != 0)]; + set runningValue >>>= 1; + } + Microsoft.Quantum.Arrays.Reversed(result) + } + function __ResultArrayAsIntBE__(results : Result[]) : Int { + Microsoft.Quantum.Convert.ResultArrayAsInt(Microsoft.Quantum.Arrays.Reversed(results)) + } + let a = [Zero, Zero, One]; + let b = __IntAsResultArrayBE__(__ResultArrayAsIntBE__(a) ||| __ResultArrayAsIntBE__([One, Zero, Zero]), 3); + mutable r = [Zero, Zero, Zero, Zero, Zero]; + "#]] + .assert_eq(&qsharp); + Ok(()) +} + +// XorB + +#[test] +fn binary_op_xorb_uint() -> miette::Result<(), Vec> { + let source = r#" + const uint a = 5; + const uint b = a ^ 6; + bit[b] r; + "#; + + let qsharp = compile_qasm_to_qsharp(source)?; + expect![[r#" + let a = 5; + let b = a ^^^ 6; + mutable r = [Zero, Zero, Zero]; + "#]] + .assert_eq(&qsharp); + Ok(()) +} + +#[test] +#[ignore = "angles are not yet supported"] +fn binary_op_xorb_angle() -> miette::Result<(), Vec> { + let source = r#" + const angle a = 1; + const angle b = a ^ 2; + const bool c = b; + bit[c] r; + "#; + + let qsharp = compile_qasm_to_qsharp(source)?; + expect![[r#""#]].assert_eq(&qsharp); + Ok(()) +} + +#[test] +fn binary_op_xorb_bit() -> miette::Result<(), Vec> { + let source = r#" + const bit a = 1; + const bit b = a ^ 1; + bit[b] r; + "#; + + let qsharp = compile_qasm_to_qsharp(source)?; + expect![[r#" + function __ResultAsInt__(input : Result) : Int { + if Microsoft.Quantum.Convert.ResultAsBool(input) { + 1 + } else { + 0 + } + } + let a = One; + let b = if __ResultAsInt__(a) ^^^ 1 == 0 { + One + } else { + Zero + }; + mutable r = []; + "#]] + .assert_eq(&qsharp); + Ok(()) +} + +#[test] +fn binary_op_xorb_bitarray() -> miette::Result<(), Vec> { + let source = r#" + const bit[4] a = "1011"; + const bit[4] b = a ^ "1110"; + bit[b] r; + "#; + + let qsharp = compile_qasm_to_qsharp(source)?; + expect![[r#" + function __BoolAsResult__(input : Bool) : Result { + Microsoft.Quantum.Convert.BoolAsResult(input) + } + function __IntAsResultArrayBE__(number : Int, bits : Int) : Result[] { + mutable runningValue = number; + mutable result = []; + for _ in 1..bits { + set result += [__BoolAsResult__((runningValue &&& 1) != 0)]; + set runningValue >>>= 1; + } + Microsoft.Quantum.Arrays.Reversed(result) + } + function __ResultArrayAsIntBE__(results : Result[]) : Int { + Microsoft.Quantum.Convert.ResultArrayAsInt(Microsoft.Quantum.Arrays.Reversed(results)) + } + let a = [One, Zero, One, One]; + let b = __IntAsResultArrayBE__(__ResultArrayAsIntBE__(a) ^^^ __ResultArrayAsIntBE__([One, One, One, Zero]), 4); + mutable r = [Zero, Zero, Zero, Zero, Zero]; + "#]] + .assert_eq(&qsharp); + Ok(()) +} + +// Binary Logical + +#[test] +fn binary_op_andl_bool() -> miette::Result<(), Vec> { + let source = r#" + const bool f = false; + const bool t = true; + bit[f && f] r1; + bit[f && t] r2; + bit[t && f] r3; + bit[t && t] r4; + "#; + + let qsharp = compile_qasm_to_qsharp(source)?; + expect![[r#" + let f = false; + let t = true; + mutable r1 = []; + mutable r2 = []; + mutable r3 = []; + mutable r4 = [Zero]; + "#]] + .assert_eq(&qsharp); + Ok(()) +} + +#[test] +fn binary_op_orl_bool() -> miette::Result<(), Vec> { + let source = r#" + const bool f = false; + const bool t = true; + bit[f || f] r1; + bit[f || t] r2; + bit[t || f] r3; + bit[t || t] r4; + "#; + + let qsharp = compile_qasm_to_qsharp(source)?; + expect![[r#" + let f = false; + let t = true; + mutable r1 = []; + mutable r2 = [Zero]; + mutable r3 = [Zero]; + mutable r4 = [Zero]; + "#]] + .assert_eq(&qsharp); + Ok(()) +} + +// BinaryOp: Comparison + +// Eq + +#[test] +fn binary_op_comparison_int() -> miette::Result<(), Vec> { + let source = r#" + const int a = 2; + bit[a == a] r1; + bit[a != a] r2; + bit[a > a] r3; + bit[a >= a] r4; + bit[a < a] r5; + bit[a <= a] r6; + "#; + + let qsharp = compile_qasm_to_qsharp(source)?; + expect![[r#" + let a = 2; + mutable r1 = [Zero]; + mutable r2 = []; + mutable r3 = []; + mutable r4 = [Zero]; + mutable r5 = []; + mutable r6 = [Zero]; + "#]] + .assert_eq(&qsharp); + Ok(()) +} + +#[test] +fn binary_op_comparison_uint() -> miette::Result<(), Vec> { + let source = r#" + const uint a = 2; + bit[a == a] r1; + bit[a != a] r2; + bit[a > a] r3; + bit[a >= a] r4; + bit[a < a] r5; + bit[a <= a] r6; + "#; + + let qsharp = compile_qasm_to_qsharp(source)?; + expect![[r#" + let a = 2; + mutable r1 = [Zero]; + mutable r2 = []; + mutable r3 = []; + mutable r4 = [Zero]; + mutable r5 = []; + mutable r6 = [Zero]; + "#]] + .assert_eq(&qsharp); + Ok(()) +} + +#[test] +#[ignore = "angles are not yet supported"] +fn binary_op_comparison_angle() -> miette::Result<(), Vec> { + let source = r#" + const angle a = 2.0; + bit[a == a] r1; + bit[a != a] r2; + bit[a > a] r3; + bit[a >= a] r4; + bit[a < a] r5; + bit[a <= a] r6; + "#; + + let qsharp = compile_qasm_to_qsharp(source)?; + expect![[r#""#]].assert_eq(&qsharp); + Ok(()) +} + +#[test] +fn binary_op_comparison_bit() -> miette::Result<(), Vec> { + let source = r#" + const bit a = 1; + bit[a == a] r1; + bit[a != a] r2; + bit[a > a] r3; + bit[a >= a] r4; + bit[a < a] r5; + bit[a <= a] r6; + "#; + + let qsharp = compile_qasm_to_qsharp(source)?; + expect![[r#" + let a = One; + mutable r1 = [Zero]; + mutable r2 = []; + mutable r3 = []; + mutable r4 = [Zero]; + mutable r5 = []; + mutable r6 = [Zero]; + "#]] + .assert_eq(&qsharp); + Ok(()) +} + +#[test] +fn binary_op_comparison_bitarray() -> miette::Result<(), Vec> { + let source = r#" + const bit[2] a = "10"; + bit[a == a] r1; + bit[a != a] r2; + bit[a > a] r3; + bit[a >= a] r4; + bit[a < a] r5; + bit[a <= a] r6; + "#; + + let qsharp = compile_qasm_to_qsharp(source)?; + expect![[r#" + let a = [One, Zero]; + mutable r1 = [Zero]; + mutable r2 = []; + mutable r3 = []; + mutable r4 = [Zero]; + mutable r5 = []; + mutable r6 = [Zero]; + "#]] + .assert_eq(&qsharp); + Ok(()) +} + +// BinaryOp: Arithmetic + +// Add + +#[test] +fn binary_op_add_int() -> miette::Result<(), Vec> { + let source = r#" + const int a = 1; + const int b = 2; + bit[a + b] r; + "#; + + let qsharp = compile_qasm_to_qsharp(source)?; + expect![[r#" + let a = 1; + let b = 2; + mutable r = [Zero, Zero, Zero]; + "#]] + .assert_eq(&qsharp); + Ok(()) +} + +#[test] +fn binary_op_add_uint() -> miette::Result<(), Vec> { + let source = r#" + const uint a = 1; + const uint b = 2; + bit[a + b] r; + "#; + + let qsharp = compile_qasm_to_qsharp(source)?; + expect![[r#" + let a = 1; + let b = 2; + mutable r = [Zero, Zero, Zero]; + "#]] + .assert_eq(&qsharp); + Ok(()) +} + +#[test] +fn binary_op_add_float() -> miette::Result<(), Vec> { + let source = r#" + const float a = 1.0; + const float b = 2.0; + bit[a + b] r; + "#; + + let qsharp = compile_qasm_to_qsharp(source)?; + expect![[r#" + let a = 1.; + let b = 2.; + mutable r = [Zero, Zero, Zero]; + "#]] + .assert_eq(&qsharp); + Ok(()) +} + +#[test] +#[ignore = "angles are not yet supported"] +fn binary_op_add_angle() -> miette::Result<(), Vec> { + let source = r#" + const angle a = 1.0; + const angle b = 2.0; + bit[a + b] r; + "#; + + let qsharp = compile_qasm_to_qsharp(source)?; + expect![[r#""#]].assert_eq(&qsharp); + Ok(()) +} + +// Sub + +#[test] +fn binary_op_sub_int() -> miette::Result<(), Vec> { + let source = r#" + const int a = 3; + const int b = 2; + bit[a - b] r; + "#; + + let qsharp = compile_qasm_to_qsharp(source)?; + expect![[r#" + let a = 3; + let b = 2; + mutable r = [Zero]; + "#]] + .assert_eq(&qsharp); + Ok(()) +} + +#[test] +fn binary_op_sub_uint() -> miette::Result<(), Vec> { + let source = r#" + const uint a = 3; + const uint b = 2; + bit[a - b] r; + "#; + + let qsharp = compile_qasm_to_qsharp(source)?; + expect![[r#" + let a = 3; + let b = 2; + mutable r = [Zero]; + "#]] + .assert_eq(&qsharp); + Ok(()) +} + +#[test] +fn binary_op_sub_float() -> miette::Result<(), Vec> { + let source = r#" + const float a = 3.0; + const float b = 2.0; + bit[a - b] r; + "#; + + let qsharp = compile_qasm_to_qsharp(source)?; + expect![[r#" + let a = 3.; + let b = 2.; + mutable r = [Zero]; + "#]] + .assert_eq(&qsharp); + Ok(()) +} + +#[test] +#[ignore = "angles are not yet supported"] +fn binary_op_sub_angle() -> miette::Result<(), Vec> { + let source = r#" + const float a = 3.0; + const float b = 2.0; + bit[a - b] r; + "#; + + let qsharp = compile_qasm_to_qsharp(source)?; + expect![[r#""#]].assert_eq(&qsharp); + Ok(()) +} + +// Mul + +#[test] +fn binary_op_mul_int() -> miette::Result<(), Vec> { + let source = r#" + const int a = 3; + const int b = 2; + bit[a * b] r; + "#; + + let qsharp = compile_qasm_to_qsharp(source)?; + expect![[r#" + let a = 3; + let b = 2; + mutable r = [Zero, Zero, Zero, Zero, Zero, Zero]; + "#]] + .assert_eq(&qsharp); + Ok(()) +} + +#[test] +fn binary_op_mul_uint() -> miette::Result<(), Vec> { + let source = r#" + const uint a = 3; + const uint b = 2; + bit[a * b] r; + "#; + + let qsharp = compile_qasm_to_qsharp(source)?; + expect![[r#" + let a = 3; + let b = 2; + mutable r = [Zero, Zero, Zero, Zero, Zero, Zero]; + "#]] + .assert_eq(&qsharp); + Ok(()) +} + +#[test] +fn binary_op_mul_float() -> miette::Result<(), Vec> { + let source = r#" + const float a = 3.0; + const float b = 2.0; + bit[a * b] r; + "#; + + let qsharp = compile_qasm_to_qsharp(source)?; + expect![[r#" + let a = 3.; + let b = 2.; + mutable r = [Zero, Zero, Zero, Zero, Zero, Zero]; + "#]] + .assert_eq(&qsharp); + Ok(()) +} + +#[test] +#[ignore = "angles are not yet supported"] +fn binary_op_mul_angle() -> miette::Result<(), Vec> { + let source = r#" + const float a = 3.0; + const uint b = 2; + bit[a * b] r1; + bit[b * a] r2; + "#; + + let qsharp = compile_qasm_to_qsharp(source)?; + expect![[r#""#]].assert_eq(&qsharp); + Ok(()) +} + +// Div + +#[test] +fn binary_op_div_int() -> miette::Result<(), Vec> { + let source = r#" + const int a = 6; + const int b = 2; + bit[a / b] r; + "#; + + let qsharp = compile_qasm_to_qsharp(source)?; + expect![[r#" + let a = 6; + let b = 2; + mutable r = [Zero, Zero, Zero]; + "#]] + .assert_eq(&qsharp); + Ok(()) +} + +#[test] +fn binary_op_div_uint() -> miette::Result<(), Vec> { + let source = r#" + const uint a = 6; + const uint b = 2; + bit[a / b] r; + "#; + + let qsharp = compile_qasm_to_qsharp(source)?; + expect![[r#" + let a = 6; + let b = 2; + mutable r = [Zero, Zero, Zero]; + "#]] + .assert_eq(&qsharp); + Ok(()) +} + +#[test] +fn binary_op_div_float() -> miette::Result<(), Vec> { + let source = r#" + const float a = 6.0; + const float b = 2.0; + bit[a / b] r; + "#; + + let qsharp = compile_qasm_to_qsharp(source)?; + expect![[r#" + let a = 6.; + let b = 2.; + mutable r = [Zero, Zero, Zero]; + "#]] + .assert_eq(&qsharp); + Ok(()) +} + +#[test] +#[ignore = "angles are not yet supported"] +fn binary_op_div_angle() -> miette::Result<(), Vec> { + let source = r#" + const angle a = 6.0; + const uint b = 2; + bit[a / b] r; + "#; + + let qsharp = compile_qasm_to_qsharp(source)?; + expect![[r#""#]].assert_eq(&qsharp); + Ok(()) +} + +// Mod + +#[test] +fn binary_op_mod_int() -> miette::Result<(), Vec> { + let source = r#" + const int a = 8; + bit[a % 3] r; + "#; + + let qsharp = compile_qasm_to_qsharp(source)?; + expect![[r#" + let a = 8; + mutable r = [Zero, Zero]; + "#]] + .assert_eq(&qsharp); + Ok(()) +} + +#[test] +fn binary_op_mod_uint() -> miette::Result<(), Vec> { + let source = r#" + const uint a = 8; + bit[a % 3] r; + "#; + + let qsharp = compile_qasm_to_qsharp(source)?; + expect![[r#" + let a = 8; + mutable r = [Zero, Zero]; + "#]] + .assert_eq(&qsharp); + Ok(()) +} + +// Pow + +#[test] +fn binary_op_pow_int() -> miette::Result<(), Vec> { + let source = r#" + const int a = 2; + const int b = 3; + bit[a ** b] r; + "#; + + let qsharp = compile_qasm_to_qsharp(source)?; + expect![[r#" + let a = 2; + let b = 3; + mutable r = [Zero, Zero, Zero, Zero, Zero, Zero, Zero, Zero]; + "#]] + .assert_eq(&qsharp); + Ok(()) +} + +#[test] +fn binary_op_pow_uint() -> miette::Result<(), Vec> { + let source = r#" + const uint a = 2; + const uint b = 3; + bit[a ** b] r; + "#; + + let qsharp = compile_qasm_to_qsharp(source)?; + expect![[r#" + let a = 2; + let b = 3; + mutable r = [Zero, Zero, Zero, Zero, Zero, Zero, Zero, Zero]; + "#]] + .assert_eq(&qsharp); + Ok(()) +} + +#[test] +fn binary_op_pow_float() -> miette::Result<(), Vec> { + let source = r#" + const float a = 2.0; + const float b = 3.0; + bit[a ** b] r; + "#; + + let qsharp = compile_qasm_to_qsharp(source)?; + expect![[r#" + let a = 2.; + let b = 3.; + mutable r = [Zero, Zero, Zero, Zero, Zero, Zero, Zero, Zero]; + "#]] + .assert_eq(&qsharp); + Ok(()) +} + +// Cast + +#[test] +fn cast_to_bool() -> miette::Result<(), Vec> { + let source = r#" + const int a = 0; + const uint b = 1; + const float c = 2.0; + // const angle d = 2.0; + const bit e = 1; + + const bool s1 = a; + const bool s2 = b; + const bool s3 = c; + // const bool s4 = d; + const bool s5 = e; + + bit[s1] r1; + bit[s2] r2; + bit[s3] r3; + // bit[s4] r4; + bit[s5] r5; + "#; + + let qsharp = compile_qasm_to_qsharp(source)?; + expect![[r#" + function __ResultAsBool__(input : Result) : Bool { + Microsoft.Quantum.Convert.ResultAsBool(input) + } + let a = 0; + let b = 1; + let c = 2.; + let e = One; + let s1 = if a == 0 { + false + } else { + true + }; + let s2 = if b == 0 { + false + } else { + true + }; + let s3 = if Microsoft.Quantum.Math.Truncate(c) == 0 { + false + } else { + true + }; + let s5 = __ResultAsBool__(e); + mutable r1 = []; + mutable r2 = [Zero]; + mutable r3 = [Zero]; + mutable r5 = [Zero]; + "#]] + .assert_eq(&qsharp); + Ok(()) +} + +#[test] +fn cast_to_int() -> miette::Result<(), Vec> { + let source = r#" + const bool a = true; + const uint b = 2; + const float c = 3.0; + const bit d = 0; + + const int s1 = a; + const int s2 = b; + const int s3 = c; + const int s4 = d; + + bit[s1] r1; + bit[s2] r2; + bit[s3] r3; + bit[s4] r4; + "#; + + let qsharp = compile_qasm_to_qsharp(source)?; + expect![[r#" + function __BoolAsInt__(value : Bool) : Int { + if value { + 1 + } else { + 0 + } + } + function __ResultAsInt__(input : Result) : Int { + if Microsoft.Quantum.Convert.ResultAsBool(input) { + 1 + } else { + 0 + } + } + let a = true; + let b = 2; + let c = 3.; + let d = Zero; + let s1 = __BoolAsInt__(a); + let s2 = b; + let s3 = Microsoft.Quantum.Math.Truncate(c); + let s4 = __ResultAsInt__(d); + mutable r1 = [Zero]; + mutable r2 = [Zero, Zero]; + mutable r3 = [Zero, Zero, Zero]; + mutable r4 = []; + "#]] + .assert_eq(&qsharp); + Ok(()) +} + +#[test] +fn cast_to_uint() -> miette::Result<(), Vec> { + let source = r#" + const bool a = true; + const uint b = 2; + const float c = 3.0; + const bit d = 0; + + const uint s1 = a; + const uint s2 = b; + const uint s3 = c; + const uint s4 = d; + + bit[s1] r1; + bit[s2] r2; + bit[s3] r3; + bit[s4] r4; + "#; + + let qsharp = compile_qasm_to_qsharp(source)?; + expect![[r#" + function __BoolAsInt__(value : Bool) : Int { + if value { + 1 + } else { + 0 + } + } + function __ResultAsInt__(input : Result) : Int { + if Microsoft.Quantum.Convert.ResultAsBool(input) { + 1 + } else { + 0 + } + } + let a = true; + let b = 2; + let c = 3.; + let d = Zero; + let s1 = __BoolAsInt__(a); + let s2 = b; + let s3 = Microsoft.Quantum.Math.Truncate(c); + let s4 = __ResultAsInt__(d); + mutable r1 = [Zero]; + mutable r2 = [Zero, Zero]; + mutable r3 = [Zero, Zero, Zero]; + mutable r4 = []; + "#]] + .assert_eq(&qsharp); + Ok(()) +} + +#[test] +fn cast_to_float() -> miette::Result<(), Vec> { + let source = r#" + const bool a = true; + const int b = 2; + const uint c = 3; + + const float s1 = a; + const float s2 = b; + const float s3 = c; + + bit[s1] r1; + bit[s2] r2; + bit[s3] r3; + "#; + + let qsharp = compile_qasm_to_qsharp(source)?; + expect![[r#" + function __BoolAsDouble__(value : Bool) : Double { + if value { + 1. + } else { + 0. + } + } + let a = true; + let b = 2; + let c = 3; + let s1 = __BoolAsDouble__(a); + let s2 = Microsoft.Quantum.Convert.IntAsDouble(b); + let s3 = Microsoft.Quantum.Convert.IntAsDouble(c); + mutable r1 = [Zero]; + mutable r2 = [Zero, Zero]; + mutable r3 = [Zero, Zero, Zero]; + "#]] + .assert_eq(&qsharp); + Ok(()) +} + +#[test] +#[ignore = "angles are not yet supported"] +fn cast_to_angle() -> miette::Result<(), Vec> { + let source = r#" + const float a = 2.0; + const bit b = 1; + + const angle s1 = a; + const angle s2 = b; + + bit[s1] r1; + bit[s2] r2; + "#; + + let qsharp = compile_qasm_to_qsharp(source)?; + expect![[r#""#]].assert_eq(&qsharp); + Ok(()) +} + +#[test] +fn cast_to_bit() -> miette::Result<(), Vec> { + let source = r#" + const bool a = false; + const int b = 1; + const uint c = 2; + // const angle d = 3.0; + + const bit s1 = a; + const bit s2 = b; + const bit s3 = c; + // const bit s4 = d; + + bit[s1] r1; + bit[s2] r2; + bit[s3] r3; + // bit[s4] r4; + "#; + + let qsharp = compile_qasm_to_qsharp(source)?; + expect![[r#" + function __BoolAsResult__(input : Bool) : Result { + Microsoft.Quantum.Convert.BoolAsResult(input) + } + let a = false; + let b = 1; + let c = 2; + let s1 = __BoolAsResult__(a); + let s2 = if b == 0 { + One + } else { + Zero + }; + let s3 = if c == 0 { + One + } else { + Zero + }; + mutable r1 = []; + mutable r2 = [Zero]; + mutable r3 = [Zero]; + "#]] + .assert_eq(&qsharp); + Ok(()) +} diff --git a/compiler/qsc_qasm3/src/tests/statement/gate_call.rs b/compiler/qsc_qasm3/src/tests/statement/gate_call.rs index 3165ccd847..4bd127bae9 100644 --- a/compiler/qsc_qasm3/src/tests/statement/gate_call.rs +++ b/compiler/qsc_qasm3/src/tests/statement/gate_call.rs @@ -6,6 +6,46 @@ use expect_test::expect; use miette::Report; use qsc::target::Profile; +#[test] +fn u_gate_can_be_called() -> miette::Result<(), Vec> { + let source = r#" + qubit q; + U(1.0, 2.0, 3.0) q; + "#; + + let qsharp = compile_qasm_to_qsharp(source)?; + expect![ + r#" + operation U(theta : Double, phi : Double, lambda : Double, qubit : Qubit) : Unit is Adj + Ctl { + body ... { + Rz(lambda, qubit); + Ry(theta, qubit); + Rz(phi, qubit); + R(PauliI, -lambda - phi - theta, qubit); + } + adjoint auto; + controlled auto; + controlled adjoint auto; + } + let q = QIR.Runtime.__quantum__rt__qubit_allocate(); + U(1., 2., 3., q); + "# + ] + .assert_eq(&qsharp); + Ok(()) +} + +#[test] +fn gphase_gate_can_be_called() -> miette::Result<(), Vec> { + let source = r#" + gphase(2.0); + "#; + + let qsharp = compile_qasm_to_qsharp(source)?; + expect![r#""#].assert_eq(&qsharp); + Ok(()) +} + #[test] fn x_gate_can_be_called() -> miette::Result<(), Vec> { let source = r#" diff --git a/compiler/qsc_qasm3/src/tests/statement/if_stmt.rs b/compiler/qsc_qasm3/src/tests/statement/if_stmt.rs index 6dbd2129f0..9723bf3de8 100644 --- a/compiler/qsc_qasm3/src/tests/statement/if_stmt.rs +++ b/compiler/qsc_qasm3/src/tests/statement/if_stmt.rs @@ -147,6 +147,6 @@ fn using_cond_that_cannot_implicit_cast_to_bool_fail() { panic!("Expected error"); }; - expect![r#"Cannot cast expression of type Qubit to type Bool(False)"#] + expect!["Cannot cast expression of type Qubit to type Bool(false)"] .assert_eq(&errors[0].to_string()); } diff --git a/compiler/qsc_qasm3/src/tests/statement/measure.rs b/compiler/qsc_qasm3/src/tests/statement/measure.rs index c21b8e6a87..4072d847e7 100644 --- a/compiler/qsc_qasm3/src/tests/statement/measure.rs +++ b/compiler/qsc_qasm3/src/tests/statement/measure.rs @@ -4,6 +4,7 @@ use crate::tests::compile_qasm_to_qsharp; use expect_test::expect; use miette::Report; +use std::fmt::Write; #[test] fn single_qubit_can_be_measured_into_single_bit() -> miette::Result<(), Vec> { @@ -24,7 +25,6 @@ fn single_qubit_can_be_measured_into_single_bit() -> miette::Result<(), Vec miette::Result<(), Vec> { let source = r#" bit c; @@ -87,17 +87,29 @@ fn measuring_hardware_qubits_generates_an_error() { c = measure $2; "#; - let Err(err) = compile_qasm_to_qsharp(source) else { + let Err(errs) = compile_qasm_to_qsharp(source) else { panic!("Measuring HW qubit should have generated an error"); }; - assert!( - err.len() == 1, - "Expected a single error when measuring a HW qubit, got: {err:#?}" - ); - - assert!(err[0] - .to_string() - .contains("Hardware qubit operands are not supported")); + + let mut errs_string = String::new(); + + for err in errs { + writeln!(&mut errs_string, "{err:?}").expect(""); + } + + expect![[r#" + Qsc.Qasm3.Compile.NotSupported + + x Hardware qubit operands are not supported. + ,-[Test.qasm:3:21] + 2 | bit c; + 3 | c = measure $2; + : ^^ + 4 | + `---- + + "#]] + .assert_eq(&errs_string); } #[test] diff --git a/compiler/qsc_qasm3/src/tests/statement/switch.rs b/compiler/qsc_qasm3/src/tests/statement/switch.rs index 17aa7257bd..eaabadeb33 100644 --- a/compiler/qsc_qasm3/src/tests/statement/switch.rs +++ b/compiler/qsc_qasm3/src/tests/statement/switch.rs @@ -8,7 +8,7 @@ use miette::Report; #[test] fn default_is_optional() -> miette::Result<(), Vec> { let source = r#" - OPENQASM 3.0; + OPENQASM 3.1; int i = 15; switch (i) { case 1 { @@ -33,7 +33,7 @@ fn default_is_optional() -> miette::Result<(), Vec> { #[test] fn default_as_only_case_causes_parse_error() { let source = r#" - OPENQASM 3.0; + OPENQASM 3.1; int i = 15; switch (i) { default { @@ -53,7 +53,7 @@ fn default_as_only_case_causes_parse_error() { #[test] fn no_cases_causes_parse_error() { let source = r#" - OPENQASM 3.0; + OPENQASM 3.1; int i = 15; switch (i) { } @@ -70,7 +70,7 @@ fn no_cases_causes_parse_error() { #[test] fn spec_case_1() -> miette::Result<(), Vec> { let source = r#" - OPENQASM 3.0; + OPENQASM 3.1; include "stdgates.inc"; qubit q; @@ -115,7 +115,7 @@ fn spec_case_1() -> miette::Result<(), Vec> { #[test] fn spec_case_2() -> miette::Result<(), Vec> { let source = r#" - OPENQASM 3.0; + OPENQASM 3.1; include "stdgates.inc"; qubit q; @@ -164,7 +164,7 @@ fn spec_case_2() -> miette::Result<(), Vec> { #[test] fn spec_case_3() -> miette::Result<(), Vec> { let source = r#" - OPENQASM 3.0; + OPENQASM 3.1; include "stdgates.inc"; qubit q; bit[2] b; @@ -217,7 +217,7 @@ fn spec_case_3() -> miette::Result<(), Vec> { #[ignore = "Function decls are not supported yet"] fn spec_case_4() -> miette::Result<(), Vec> { let source = r#" - OPENQASM 3.0; + OPENQASM 3.1; include "stdgates.inc"; qubit q; bit[2] b; @@ -260,7 +260,7 @@ fn spec_case_4() -> miette::Result<(), Vec> { #[test] fn spec_case_5() -> miette::Result<(), Vec> { let source = r#" - OPENQASM 3.0; + OPENQASM 3.1; include "stdgates.inc"; diff --git a/compiler/qsc_qasm3/src/tests/statement/while_loop.rs b/compiler/qsc_qasm3/src/tests/statement/while_loop.rs index 2f92febde3..76e67f585a 100644 --- a/compiler/qsc_qasm3/src/tests/statement/while_loop.rs +++ b/compiler/qsc_qasm3/src/tests/statement/while_loop.rs @@ -55,6 +55,6 @@ fn using_cond_that_cannot_implicit_cast_to_bool_fail() { panic!("Expected error"); }; - expect![r#"Cannot cast expression of type Qubit to type Bool(False)"#] + expect!["Cannot cast expression of type Qubit to type Bool(false)"] .assert_eq(&errors[0].to_string()); } From dce297d2ce884f9f55be3c1d91c471c5fda30f46 Mon Sep 17 00:00:00 2001 From: Ian Davis Date: Mon, 24 Mar 2025 16:57:32 -0700 Subject: [PATCH 065/108] Removing old test --- .../src/semantic/tests/expression.rs | 47 ------------------- 1 file changed, 47 deletions(-) diff --git a/compiler/qsc_qasm3/src/semantic/tests/expression.rs b/compiler/qsc_qasm3/src/semantic/tests/expression.rs index 6102868912..8a2f3511ad 100644 --- a/compiler/qsc_qasm3/src/semantic/tests/expression.rs +++ b/compiler/qsc_qasm3/src/semantic/tests/expression.rs @@ -8,50 +8,3 @@ mod implicit_cast_from_bitarray; mod implicit_cast_from_bool; mod implicit_cast_from_float; mod implicit_cast_from_int; - -use expect_test::expect; - -use super::check_stmt_kinds; - -#[test] -fn a() { - check_stmt_kinds( - r#" - true && false; - false || true; - !true; - "#, - &expect![[r#" - ExprStmt [9-23]: - expr: Expr [9-22]: - ty: Bool(false) - kind: BinaryOpExpr: - op: AndL - lhs: Expr [9-13]: - ty: Bool(true) - kind: Lit: Bool(true) - rhs: Expr [17-22]: - ty: Bool(true) - kind: Lit: Bool(false) - ExprStmt [32-46]: - expr: Expr [32-45]: - ty: Bool(false) - kind: BinaryOpExpr: - op: OrL - lhs: Expr [32-37]: - ty: Bool(true) - kind: Lit: Bool(false) - rhs: Expr [41-45]: - ty: Bool(true) - kind: Lit: Bool(true) - ExprStmt [55-61]: - expr: Expr [56-60]: - ty: Bool(true) - kind: UnaryOpExpr [56-60]: - op: NotL - expr: Expr [56-60]: - ty: Bool(true) - kind: Lit: Bool(true) - "#]], - ); -} From 2249c9c8c8e4960719b1fea7bc6e272f7e2b715c Mon Sep 17 00:00:00 2001 From: Ian Davis Date: Tue, 25 Mar 2025 08:52:39 -0700 Subject: [PATCH 066/108] Parsing now bundles IO source loading errors into parse errors. (#2252) --- compiler/qsc/src/lib.rs | 1 - compiler/qsc_qasm3/src/compile/tests.rs | 4 +- compiler/qsc_qasm3/src/compiler.rs | 45 +-------- compiler/qsc_qasm3/src/io.rs | 22 ++--- compiler/qsc_qasm3/src/io/error.rs | 24 +++++ compiler/qsc_qasm3/src/lib.rs | 9 +- compiler/qsc_qasm3/src/parser.rs | 55 +++++++---- compiler/qsc_qasm3/src/parser/error.rs | 10 ++ compiler/qsc_qasm3/src/parser/prgm.rs | 10 +- compiler/qsc_qasm3/src/parser/tests.rs | 8 +- compiler/qsc_qasm3/src/semantic.rs | 25 +++-- compiler/qsc_qasm3/src/semantic/error.rs | 6 ++ compiler/qsc_qasm3/src/semantic/lowerer.rs | 9 -- compiler/qsc_qasm3/src/semantic/tests.rs | 16 ++-- compiler/qsc_qasm3/src/tests.rs | 93 +++++++++++++++---- compiler/qsc_qasm3/src/tests/declaration.rs | 7 +- compiler/qsc_qasm3/src/tests/output.rs | 11 +-- .../src/tests/sample_circuits/bell_pair.rs | 5 +- .../tests/sample_circuits/rgqft_multiplier.rs | 5 +- .../qsc_qasm3/src/tests/statement/include.rs | 6 +- .../qsc_qasm3/src/tests/statement/reset.rs | 5 +- .../qsc_qasm3/src/tests/statement/switch.rs | 4 +- pip/src/interop.rs | 8 +- 23 files changed, 235 insertions(+), 153 deletions(-) create mode 100644 compiler/qsc_qasm3/src/io/error.rs diff --git a/compiler/qsc/src/lib.rs b/compiler/qsc/src/lib.rs index 09a0c72bfd..539f54516d 100644 --- a/compiler/qsc/src/lib.rs +++ b/compiler/qsc/src/lib.rs @@ -79,7 +79,6 @@ pub mod partial_eval { } pub mod qasm3 { - pub use qsc_qasm3::io::*; pub use qsc_qasm3::parse::*; pub use qsc_qasm3::*; } diff --git a/compiler/qsc_qasm3/src/compile/tests.rs b/compiler/qsc_qasm3/src/compile/tests.rs index 8c5fb59d12..72b0210d14 100644 --- a/compiler/qsc_qasm3/src/compile/tests.rs +++ b/compiler/qsc_qasm3/src/compile/tests.rs @@ -18,7 +18,7 @@ fn programs_with_includes_with_includes_can_be_compiled() -> miette::Result<(), ("source2.qasm".into(), source2.into()), ]; - let unit = compile_all_fragments("source0.qasm", all_sources).map_err(|e| vec![e])?; + let unit = compile_all_fragments("source0.qasm", all_sources)?; print_compilation_errors(&unit); assert!(!unit.has_errors()); Ok(()) @@ -39,7 +39,7 @@ fn including_stdgates_multiple_times_causes_symbol_redifintion_errors( ("source2.qasm".into(), source2.into()), ]; - let unit = compile_all_fragments("source0.qasm", all_sources).map_err(|e| vec![e])?; + let unit = compile_all_fragments("source0.qasm", all_sources)?; assert!(unit.has_errors()); for error in unit.errors() { assert!(error.to_string().contains("Redefined symbol: ")); diff --git a/compiler/qsc_qasm3/src/compiler.rs b/compiler/qsc_qasm3/src/compiler.rs index b23563aa43..35870469ca 100644 --- a/compiler/qsc_qasm3/src/compiler.rs +++ b/compiler/qsc_qasm3/src/compiler.rs @@ -1,7 +1,7 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. -use std::{path::Path, rc::Rc, sync::Arc}; +use std::{path::Path, rc::Rc}; use num_bigint::BigInt; use qsc_data_structures::span::Span; @@ -25,7 +25,6 @@ use crate::{ build_wrapped_block_expr, managed_qubit_alloc_array, map_qsharp_type_to_ast_ty, wrap_expr_in_parens, }, - io::{InMemorySourceResolver, SourceResolver}, parser::ast::{list_from_iter, List}, runtime::{get_runtime_function_decls, RuntimeFunctions}, semantic::{ @@ -45,48 +44,12 @@ use crate::{ use crate::semantic::ast as semast; use qsc_ast::ast::{self as qsast, NodeId, Package}; -pub fn compile_anon_with_config( - source: S, - config: CompilerConfig, -) -> miette::Result -where - S: AsRef, -{ - let path = std::path::PathBuf::from("Test.qasm"); - let sources = [( - Arc::from(path.display().to_string().as_str()), - Arc::from(source.as_ref()), - )]; - let resolver = InMemorySourceResolver::from_iter(sources); - let source = resolver.resolve(&path)?.1; - compile_with_config(source, &path, &resolver, config) -} - -pub fn compile_all_with_config

( - path: P, - sources: impl IntoIterator, Arc)>, - config: CompilerConfig, -) -> miette::Result -where - P: AsRef, -{ - let resolver = InMemorySourceResolver::from_iter(sources); - let source = resolver.resolve(path.as_ref())?.1; - compile_with_config(source, path, &resolver, config) -} - -pub fn compile_with_config( - source: S, - path: P, - resolver: &R, - config: CompilerConfig, -) -> miette::Result +pub fn compile_with_config(source: S, path: P, config: CompilerConfig) -> QasmCompileUnit where S: AsRef, P: AsRef, - R: SourceResolver, { - let res = crate::semantic::parse_source(source, path, resolver)?; + let res = crate::semantic::parse(source, path); let program = res.program; let compiler = crate::compiler::QasmCompiler { @@ -98,7 +61,7 @@ where errors: res.errors, }; - Ok(compiler.compile(&program)) + compiler.compile(&program) } pub struct QasmCompiler { diff --git a/compiler/qsc_qasm3/src/io.rs b/compiler/qsc_qasm3/src/io.rs index 79df8d5608..bc705f037f 100644 --- a/compiler/qsc_qasm3/src/io.rs +++ b/compiler/qsc_qasm3/src/io.rs @@ -1,6 +1,10 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. +mod error; +pub use error::Error; +pub use error::ErrorKind; + use std::{ path::{Path, PathBuf}, sync::Arc, @@ -8,30 +12,27 @@ use std::{ use rustc_hash::FxHashMap; -use miette::IntoDiagnostic; - /// A trait for resolving include file paths to their contents. /// This is used by the parser to resolve `include` directives. /// Implementations of this trait can be provided to the parser /// to customize how include files are resolved. pub trait SourceResolver { #[cfg(feature = "fs")] - fn resolve

(&self, path: P) -> miette::Result<(PathBuf, String)> + fn resolve

(&self, path: P) -> miette::Result<(PathBuf, String), Error> where P: AsRef, { let path = std::fs::canonicalize(path).map_err(|e| { - crate::Error(crate::ErrorKind::IO(format!( + Error(ErrorKind::IO(format!( "Could not resolve include file path: {e}" ))) })?; match std::fs::read_to_string(&path) { Ok(source) => Ok((path, source)), - Err(_) => Err(crate::Error(crate::ErrorKind::NotFound(format!( + Err(_) => Err(Error(ErrorKind::NotFound(format!( "Could not resolve include file: {}", path.display() - )))) - .into_diagnostic(), + )))), } } #[cfg(not(feature = "fs"))] @@ -62,18 +63,17 @@ impl FromIterator<(Arc, Arc)> for InMemorySourceResolver { } impl SourceResolver for InMemorySourceResolver { - fn resolve

(&self, path: P) -> miette::Result<(PathBuf, String)> + fn resolve

(&self, path: P) -> miette::Result<(PathBuf, String), Error> where P: AsRef, { let path = path.as_ref(); match self.sources.get(path) { Some(source) => Ok((path.to_owned(), source.clone())), - None => Err(crate::Error(crate::ErrorKind::NotFound(format!( + None => Err(Error(ErrorKind::NotFound(format!( "Could not resolve include file: {}", path.display() - )))) - .into_diagnostic(), + )))), } } } diff --git a/compiler/qsc_qasm3/src/io/error.rs b/compiler/qsc_qasm3/src/io/error.rs new file mode 100644 index 0000000000..1a3b40ecf3 --- /dev/null +++ b/compiler/qsc_qasm3/src/io/error.rs @@ -0,0 +1,24 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +use miette::Diagnostic; +use thiserror::Error; + +#[derive(Clone, Debug, Diagnostic, Eq, Error, PartialEq)] +#[error(transparent)] +#[diagnostic(transparent)] +pub struct Error(pub ErrorKind); + +#[derive(Clone, Debug, Diagnostic, Eq, Error, PartialEq)] +pub enum ErrorKind { + #[error("Not Found {0}")] + NotFound(String), + #[error("IO Error: {0}")] + IO(String), +} + +impl From for crate::Error { + fn from(val: Error) -> Self { + crate::Error(crate::ErrorKind::IO(val)) + } +} diff --git a/compiler/qsc_qasm3/src/lib.rs b/compiler/qsc_qasm3/src/lib.rs index 0b58aa8c6f..3b44495f43 100644 --- a/compiler/qsc_qasm3/src/lib.rs +++ b/compiler/qsc_qasm3/src/lib.rs @@ -9,6 +9,7 @@ mod ast_builder; mod compile; mod compiler; pub use compile::qasm_to_program; +pub use compiler::compile_with_config; pub mod io; mod keyword; mod lex; @@ -64,6 +65,9 @@ impl Error { #[derive(Clone, Debug, Diagnostic, Eq, Error, PartialEq)] #[error(transparent)] pub enum ErrorKind { + #[error(transparent)] + #[diagnostic(transparent)] + IO(#[from] crate::io::Error), #[error("QASM3 Parse Error: {0}")] Parse(String, #[label] Span), #[error(transparent)] @@ -75,17 +79,18 @@ pub enum ErrorKind { #[error("QASM3 Parse Error: Not Found {0}")] NotFound(String), #[error("IO Error: {0}")] - IO(String), + OldIO(String), } impl ErrorKind { fn with_offset(self, offset: u32) -> Self { match self { + ErrorKind::IO(error) => Self::IO(error), ErrorKind::Parse(error, span) => Self::Parse(error, span + offset), ErrorKind::Parser(error) => Self::Parser(error.with_offset(offset)), ErrorKind::Semantic(error) => Self::Semantic(error.with_offset(offset)), ErrorKind::NotFound(error) => Self::NotFound(error), - ErrorKind::IO(error) => Self::IO(error), + ErrorKind::OldIO(error) => Self::OldIO(error), } } } diff --git a/compiler/qsc_qasm3/src/parser.rs b/compiler/qsc_qasm3/src/parser.rs index 6ec86a8472..69b9e9204b 100644 --- a/compiler/qsc_qasm3/src/parser.rs +++ b/compiler/qsc_qasm3/src/parser.rs @@ -110,14 +110,14 @@ fn update_offsets(source_map: &SourceMap, source: &mut QasmSource) { /// This function will resolve includes using the provided resolver. /// If an include file cannot be resolved, an error will be returned. /// If a file is included recursively, a stack overflow occurs. -pub fn parse_source(source: S, path: P, resolver: &R) -> miette::Result +pub fn parse_source(source: S, path: P, resolver: &R) -> QasmParseResult where S: AsRef, P: AsRef, R: SourceResolver, { - let res = parse_qasm_source(source, path, resolver)?; - Ok(QasmParseResult::new(res)) + let res = parse_qasm_source(source, path, resolver); + QasmParseResult::new(res) } /// Creates a Q# source map from a QASM parse output. The `QasmSource` @@ -230,38 +230,54 @@ impl QasmSource { /// This function is the start of a recursive process that will resolve all /// includes in the QASM file. Any includes are parsed as if their contents /// were defined where the include statement is. -fn parse_qasm_file(path: P, resolver: &R) -> miette::Result +fn parse_qasm_file(path: P, resolver: &R) -> QasmSource where P: AsRef, R: SourceResolver, { - let (path, source) = resolver.resolve(&path)?; - parse_qasm_source(source, path, resolver) + match resolver.resolve(&path) { + Ok((path, source)) => parse_qasm_source(source, path, resolver), + Err(e) => { + let error = crate::parser::error::ErrorKind::IO(e); + let error = crate::parser::Error(error, None); + QasmSource { + path: path.as_ref().to_owned(), + source: Default::default(), + program: Program { + span: Span::default(), + statements: vec![].into_boxed_slice(), + version: None, + }, + errors: vec![error], + included: vec![], + } + } + } } -fn parse_qasm_source(source: S, path: P, resolver: &R) -> miette::Result +fn parse_qasm_source(source: S, path: P, resolver: &R) -> QasmSource where S: AsRef, P: AsRef, R: SourceResolver, { - let (program, errors, includes) = parse_source_and_includes(source.as_ref(), resolver)?; - Ok(QasmSource::new(source, path, program, errors, includes)) + let (program, errors, includes) = parse_source_and_includes(source.as_ref(), resolver); + QasmSource::new(source, path, program, errors, includes) } fn parse_source_and_includes, R>( source: P, resolver: &R, -) -> miette::Result<(Program, Vec, Vec)> +) -> (Program, Vec, Vec) where R: SourceResolver, { - let (program, errors) = parse(source.as_ref())?; - let included = parse_includes(&program, resolver)?; - Ok((program, errors, included)) + let (program, errors) = parse(source.as_ref()); + let included = parse_includes(&program, resolver); + (program, errors, included) } -fn parse_includes(program: &Program, resolver: &R) -> miette::Result> +fn parse_includes(program: &Program, resolver: &R) -> Vec where R: SourceResolver, { @@ -274,12 +290,12 @@ where if file_path.to_lowercase() == "stdgates.inc" { continue; } - let source = parse_qasm_file(file_path, resolver)?; + let source = parse_qasm_file(file_path, resolver); includes.push(source); } } - Ok(includes) + includes } pub(crate) type Result = std::result::Result; @@ -288,8 +304,9 @@ pub(crate) trait Parser: FnMut(&mut ParserContext) -> Result {} impl Result> Parser for F {} -pub fn parse(input: &str) -> Result<(Program, Vec)> { +#[must_use] +pub fn parse(input: &str) -> (Program, Vec) { let mut scanner = ParserContext::new(input); - let program = prgm::parse(&mut scanner)?; - Ok((program, scanner.into_errors())) + let program = prgm::parse(&mut scanner); + (program, scanner.into_errors()) } diff --git a/compiler/qsc_qasm3/src/parser/error.rs b/compiler/qsc_qasm3/src/parser/error.rs index da16aa0353..30c4f81d55 100644 --- a/compiler/qsc_qasm3/src/parser/error.rs +++ b/compiler/qsc_qasm3/src/parser/error.rs @@ -137,6 +137,9 @@ pub enum ErrorKind { #[error("multiple index operators are only allowed in assignments")] #[diagnostic(code("Qasm3.Parse.MultipleIndexOperators"))] MultipleIndexOperators(#[label] Span), + #[error(transparent)] + #[diagnostic(transparent)] + IO(#[from] crate::io::Error), } impl ErrorKind { @@ -160,6 +163,13 @@ impl ErrorKind { Self::GPhaseInvalidArguments(span) => Self::GPhaseInvalidArguments(span + offset), Self::InvalidGateCallDesignator(span) => Self::InvalidGateCallDesignator(span + offset), Self::MultipleIndexOperators(span) => Self::MultipleIndexOperators(span + offset), + Self::IO(error) => Self::IO(error), } } } + +impl From for crate::Error { + fn from(val: Error) -> Self { + crate::Error(crate::ErrorKind::Parser(val)) + } +} diff --git a/compiler/qsc_qasm3/src/parser/prgm.rs b/compiler/qsc_qasm3/src/parser/prgm.rs index d87859a59e..b9ca8586cd 100644 --- a/compiler/qsc_qasm3/src/parser/prgm.rs +++ b/compiler/qsc_qasm3/src/parser/prgm.rs @@ -15,12 +15,12 @@ use super::ast::{Program, Stmt, StmtKind, Version}; use super::ParserContext; /// Grammar: `version? statementOrScope* EOF`. -pub(super) fn parse(s: &mut ParserContext) -> Result { +pub(super) fn parse(s: &mut ParserContext) -> Program { let lo = s.peek().span.lo; - let version = opt(s, parse_version)?; - let stmts = parse_top_level_nodes(s)?; + let version = opt(s, parse_version).unwrap_or_default(); + let stmts = parse_top_level_nodes(s).unwrap_or_default(); - Ok(Program { + Program { span: s.span(lo), version, statements: stmts @@ -28,7 +28,7 @@ pub(super) fn parse(s: &mut ParserContext) -> Result { .map(Box::new) .collect::>() .into_boxed_slice(), - }) + } } /// Grammar: `OPENQASM VersionSpecifier SEMICOLON`. diff --git a/compiler/qsc_qasm3/src/parser/tests.rs b/compiler/qsc_qasm3/src/parser/tests.rs index acf6acb26f..30bd4e45d7 100644 --- a/compiler/qsc_qasm3/src/parser/tests.rs +++ b/compiler/qsc_qasm3/src/parser/tests.rs @@ -24,8 +24,10 @@ where P: AsRef, { let resolver = InMemorySourceResolver::from_iter(sources); - let source = resolver.resolve(path.as_ref()).map_err(|e| vec![e])?.1; - let res = crate::parser::parse_source(source, path, &resolver).map_err(|e| vec![e])?; + let (path, source) = resolver + .resolve(path.as_ref()) + .map_err(|e| vec![Report::new(e)])?; + let res = crate::parser::parse_source(source, path, &resolver); if res.source.has_errors() { let errors = res .errors() @@ -43,7 +45,7 @@ where S: AsRef, { let resolver = InMemorySourceResolver::from_iter([("test".into(), source.as_ref().into())]); - let res = parse_source(source, "test", &resolver).map_err(|e| vec![e])?; + let res = parse_source(source, "test", &resolver); if res.source.has_errors() { let errors = res .errors() diff --git a/compiler/qsc_qasm3/src/semantic.rs b/compiler/qsc_qasm3/src/semantic.rs index 6b470711b6..e99dac9906 100644 --- a/compiler/qsc_qasm3/src/semantic.rs +++ b/compiler/qsc_qasm3/src/semantic.rs @@ -1,6 +1,7 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. +use crate::io::InMemorySourceResolver; use crate::io::SourceResolver; use crate::parser::QasmSource; @@ -90,29 +91,37 @@ impl QasmSemanticParseResult { } } +pub(crate) fn parse(source: S, path: P) -> QasmSemanticParseResult +where + S: AsRef, + P: AsRef, +{ + let resolver = InMemorySourceResolver::from_iter([( + path.as_ref().display().to_string().into(), + source.as_ref().into(), + )]); + parse_source(source, path, &resolver) +} + /// Parse a QASM file and return the parse result. /// This function will resolve includes using the provided resolver. /// If an include file cannot be resolved, an error will be returned. /// If a file is included recursively, a stack overflow occurs. -pub fn parse_source( - source: S, - path: P, - resolver: &R, -) -> miette::Result +pub fn parse_source(source: S, path: P, resolver: &R) -> QasmSemanticParseResult where S: AsRef, P: AsRef, R: SourceResolver, { - let res = crate::parser::parse_source(source, path, resolver)?; + let res = crate::parser::parse_source(source, path, resolver); let analyzer = Lowerer::new(res.source, res.source_map); let sem_res = analyzer.lower(); let errors = sem_res.all_errors(); - Ok(QasmSemanticParseResult { + QasmSemanticParseResult { source: sem_res.source, source_map: sem_res.source_map, symbols: sem_res.symbols, program: sem_res.program, errors, - }) + } } diff --git a/compiler/qsc_qasm3/src/semantic/error.rs b/compiler/qsc_qasm3/src/semantic/error.rs index 6a6652456a..01b800c9c9 100644 --- a/compiler/qsc_qasm3/src/semantic/error.rs +++ b/compiler/qsc_qasm3/src/semantic/error.rs @@ -380,3 +380,9 @@ impl SemanticErrorKind { } } } + +impl From for crate::Error { + fn from(val: Error) -> Self { + crate::Error(crate::ErrorKind::Semantic(val)) + } +} diff --git a/compiler/qsc_qasm3/src/semantic/lowerer.rs b/compiler/qsc_qasm3/src/semantic/lowerer.rs index dd4e054af3..7ce669bee2 100644 --- a/compiler/qsc_qasm3/src/semantic/lowerer.rs +++ b/compiler/qsc_qasm3/src/semantic/lowerer.rs @@ -1386,15 +1386,6 @@ impl Lowerer { let target_ty = Type::Int(None, target.ty.is_const()); let target = self.cast_expr_to_type(&target_ty, &target); - // It is a parse error to have a switch statement with no cases, - // even if the default block is present. Getting here means the - // parser is broken or they changed the grammar. - if cases.is_empty() { - self.push_semantic_error(SemanticErrorKind::SwitchStatementMustHaveAtLeastOneCase( - stmt.span, - )); - } - // We push a semantic error on switch statements if version is less than 3.1, // as they were introduced in 3.1. if let Some(ref version) = self.version { diff --git a/compiler/qsc_qasm3/src/semantic/tests.rs b/compiler/qsc_qasm3/src/semantic/tests.rs index ce82fc5ff6..583875c50a 100644 --- a/compiler/qsc_qasm3/src/semantic/tests.rs +++ b/compiler/qsc_qasm3/src/semantic/tests.rs @@ -30,8 +30,10 @@ where P: AsRef, { let resolver = InMemorySourceResolver::from_iter(sources); - let source = resolver.resolve(path.as_ref()).map_err(|e| vec![e])?.1; - let res = parse_source(source, path, &resolver).map_err(|e| vec![e])?; + let (path, source) = resolver + .resolve(path.as_ref()) + .map_err(|e| vec![Report::new(e)])?; + let res = parse_source(source, path, &resolver); if res.source.has_errors() { let errors = res .errors() @@ -49,7 +51,7 @@ where S: AsRef, { let resolver = InMemorySourceResolver::from_iter([("test".into(), source.as_ref().into())]); - let res = parse_source(source, "test", &resolver).map_err(|e| vec![e])?; + let res = parse_source(source, "test", &resolver); if res.source.has_errors() { let errors = res .errors() @@ -138,9 +140,7 @@ fn check_map( S: AsRef, { let resolver = InMemorySourceResolver::from_iter([("test".into(), input.as_ref().into())]); - let res = parse_source(input, "test", &resolver) - .map_err(|e| vec![e]) - .expect("failed to parse"); + let res = parse_source(input, "test", &resolver); let errors = res.all_errors(); @@ -188,9 +188,7 @@ fn check_map_all

( .map_err(|e| vec![e]) .expect("could not load source") .1; - let res = parse_source(source, path, &resolver) - .map_err(|e| vec![e]) - .expect("failed to parse"); + let res = parse_source(source, path, &resolver); let errors = res.all_errors(); diff --git a/compiler/qsc_qasm3/src/tests.rs b/compiler/qsc_qasm3/src/tests.rs index 1c2a10bee5..c98591dac4 100644 --- a/compiler/qsc_qasm3/src/tests.rs +++ b/compiler/qsc_qasm3/src/tests.rs @@ -1,7 +1,8 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. -use crate::compiler::compile_anon_with_config; +use crate::runtime::RuntimeFunctions; +use crate::semantic::symbols::SymbolTable; use crate::{CompilerConfig, OutputSemantics, ProgramType, QasmCompileUnit, QubitSemantics}; use miette::Report; use qsc::interpret::Error; @@ -67,7 +68,7 @@ pub(crate) fn generate_qir_from_ast( ) } -fn compile(source: S) -> miette::Result +fn compile(source: S) -> miette::Result> where S: AsRef, { @@ -78,13 +79,42 @@ where Some("Test".into()), None, ); - compile_anon_with_config(source, config) + compile_with_config(source, config) +} + +fn compile_with_config( + source: S, + config: CompilerConfig, +) -> miette::Result> +where + S: AsRef, +{ + let res = parse(source)?; + if res.has_syntax_errors() { + for e in res.sytax_errors() { + println!("{:?}", Report::new(e.clone())); + } + } + assert!(!res.has_syntax_errors()); + let program = res.program; + + let compiler = crate::compiler::QasmCompiler { + source_map: res.source_map, + config, + stmts: vec![], + runtime: RuntimeFunctions::empty(), + symbols: res.symbols, + errors: res.errors, + }; + + let unit = compiler.compile(&program); + Ok(unit) } pub fn compile_all

( path: P, sources: impl IntoIterator, Arc)>, -) -> miette::Result +) -> miette::Result> where P: AsRef, { @@ -95,13 +125,13 @@ where Some("Test".into()), None, ); - crate::compiler::compile_all_with_config(path, sources, config) + compile_all_with_config(path, sources, config) } pub fn compile_all_fragments

( path: P, sources: impl IntoIterator, Arc)>, -) -> miette::Result +) -> miette::Result> where P: AsRef, { @@ -112,7 +142,7 @@ where None, None, ); - crate::compiler::compile_all_with_config(path, sources, config) + compile_all_with_config(path, sources, config) } fn compile_fragments(source: S) -> miette::Result> @@ -126,11 +156,36 @@ where None, None, ); - compile_anon_with_config(source, config).map_err(|e| vec![e]) + compile_with_config(source, config) +} + +pub fn compile_all_with_config

( + path: P, + sources: impl IntoIterator, Arc)>, + config: CompilerConfig, +) -> miette::Result> +where + P: AsRef, +{ + let res = parse_all(path, sources)?; + assert!(!res.has_syntax_errors()); + let program = res.program; + + let compiler = crate::compiler::QasmCompiler { + source_map: res.source_map, + config, + stmts: vec![], + runtime: RuntimeFunctions::empty(), + symbols: SymbolTable::default(), + errors: res.errors, + }; + + let unit = compiler.compile(&program); + Ok(unit) } fn compile_qasm_to_qir(source: &str, profile: Profile) -> Result> { - let unit = compile(source).map_err(|e| vec![e])?; + let unit = compile(source)?; fail_on_compilation_errors(&unit); let package = unit.package.expect("no package found"); let qir = generate_qir_from_ast(package, unit.source_map, profile).map_err(|errors| { @@ -158,8 +213,9 @@ pub(crate) fn parse(source: S) -> miette::Result, { - let resolver = InMemorySourceResolver::from_iter([("test".into(), source.as_ref().into())]); - let res = parse_source(source, "test", &resolver).map_err(|e| vec![e])?; + let resolver = + InMemorySourceResolver::from_iter([("Test.qasm".into(), source.as_ref().into())]); + let res = parse_source(source, "Test.qasm", &resolver); if res.source.has_errors() { let errors = res .errors() @@ -179,8 +235,11 @@ where P: AsRef, { let resolver = InMemorySourceResolver::from_iter(sources); - let source = resolver.resolve(path.as_ref()).map_err(|e| vec![e])?.1; - let res = parse_source(source, path, &resolver).map_err(|e| vec![e])?; + let source = resolver + .resolve(path.as_ref()) + .map_err(|e| vec![Report::new(e)])? + .1; + let res = parse_source(source, path, &resolver); if res.source.has_errors() { let errors = res .errors() @@ -201,7 +260,7 @@ pub fn compile_qasm_to_qsharp_file(source: &str) -> miette::Result miette::Result miette::Result<(), Vec> { None, None, ); - let unit = compile_anon_with_config(source, config).expect("parse failed"); + let unit = compile_with_config(source, config).expect("parse failed"); println!("{:?}", unit.errors); assert_eq!(unit.errors.len(), 5); for error in &unit.errors { @@ -95,7 +94,7 @@ fn stretch() { None, None, ); - let unit = compile_anon_with_config(source, config).expect("parse failed"); + let unit = compile_with_config(source, config).expect("parse failed"); assert!(unit.has_errors()); println!("{:?}", unit.errors); assert!(unit.errors.len() == 2); diff --git a/compiler/qsc_qasm3/src/tests/output.rs b/compiler/qsc_qasm3/src/tests/output.rs index b991b88e51..4f6fdbd3fd 100644 --- a/compiler/qsc_qasm3/src/tests/output.rs +++ b/compiler/qsc_qasm3/src/tests/output.rs @@ -2,7 +2,6 @@ // Licensed under the MIT License. use crate::{ - compiler::compile_anon_with_config, tests::{fail_on_compilation_errors, gen_qsharp}, CompilerConfig, OutputSemantics, ProgramType, QubitSemantics, }; @@ -10,7 +9,7 @@ use expect_test::expect; use miette::Report; use qsc::target::Profile; -use super::compile_qasm_to_qir; +use super::{compile_qasm_to_qir, compile_with_config}; #[test] fn using_re_semantics_removes_output() -> miette::Result<(), Vec> { @@ -36,7 +35,7 @@ fn using_re_semantics_removes_output() -> miette::Result<(), Vec> { Some("Test".into()), None, ); - let unit = compile_anon_with_config(source, config).expect("parse failed"); + let unit = compile_with_config(source, config).expect("parse failed"); fail_on_compilation_errors(&unit); let qsharp = gen_qsharp(&unit.package.expect("no package found")); expect![[r#" @@ -84,7 +83,7 @@ fn using_qasm_semantics_captures_all_classical_decls_as_output() -> miette::Resu Some("Test".into()), None, ); - let unit = compile_anon_with_config(source, config).expect("parse failed"); + let unit = compile_with_config(source, config).expect("parse failed"); fail_on_compilation_errors(&unit); let qsharp = gen_qsharp(&unit.package.expect("no package found")); expect![[r#" @@ -132,7 +131,7 @@ fn using_qiskit_semantics_only_bit_array_is_captured_and_reversed( Some("Test".into()), None, ); - let unit = compile_anon_with_config(source, config).expect("parse failed"); + let unit = compile_with_config(source, config).expect("parse failed"); fail_on_compilation_errors(&unit); let qsharp = gen_qsharp(&unit.package.expect("no package found")); expect![[r#" @@ -187,7 +186,7 @@ c2[2] = measure q[4]; Some("Test".into()), None, ); - let unit = compile_anon_with_config(source, config).expect("parse failed"); + let unit = compile_with_config(source, config).expect("parse failed"); fail_on_compilation_errors(&unit); let package = unit.package.expect("no package found"); let qsharp = gen_qsharp(&package.clone()); diff --git a/compiler/qsc_qasm3/src/tests/sample_circuits/bell_pair.rs b/compiler/qsc_qasm3/src/tests/sample_circuits/bell_pair.rs index e0643fb9c2..2726225072 100644 --- a/compiler/qsc_qasm3/src/tests/sample_circuits/bell_pair.rs +++ b/compiler/qsc_qasm3/src/tests/sample_circuits/bell_pair.rs @@ -2,8 +2,7 @@ // Licensed under the MIT License. use crate::{ - compiler::compile_anon_with_config, - tests::{gen_qsharp, print_compilation_errors}, + tests::{compile_with_config, gen_qsharp, print_compilation_errors}, CompilerConfig, OutputSemantics, ProgramType, QubitSemantics, }; @@ -30,7 +29,7 @@ fn it_compiles() { Some("Test".into()), None, ); - let unit = compile_anon_with_config(source, config).expect("parse failed"); + let unit = compile_with_config(source, config).expect("parse failed"); print_compilation_errors(&unit); assert!(!unit.has_errors()); diff --git a/compiler/qsc_qasm3/src/tests/sample_circuits/rgqft_multiplier.rs b/compiler/qsc_qasm3/src/tests/sample_circuits/rgqft_multiplier.rs index e4ca4ace56..ee0f9387a7 100644 --- a/compiler/qsc_qasm3/src/tests/sample_circuits/rgqft_multiplier.rs +++ b/compiler/qsc_qasm3/src/tests/sample_circuits/rgqft_multiplier.rs @@ -2,8 +2,7 @@ // Licensed under the MIT License. use crate::{ - compiler::compile_anon_with_config, - tests::{gen_qsharp, print_compilation_errors}, + tests::{compile_with_config, gen_qsharp, print_compilation_errors}, CompilerConfig, OutputSemantics, ProgramType, QubitSemantics, }; @@ -18,7 +17,7 @@ fn it_compiles() { Some("Test".into()), None, ); - let unit = compile_anon_with_config(source, config).expect("parse failed"); + let unit = compile_with_config(source, config).expect("parse failed"); print_compilation_errors(&unit); assert!(!unit.has_errors()); diff --git a/compiler/qsc_qasm3/src/tests/statement/include.rs b/compiler/qsc_qasm3/src/tests/statement/include.rs index 6d3879f262..fc21457b00 100644 --- a/compiler/qsc_qasm3/src/tests/statement/include.rs +++ b/compiler/qsc_qasm3/src/tests/statement/include.rs @@ -2,8 +2,8 @@ // Licensed under the MIT License. use crate::{ - compiler::compile_all_with_config, tests::qsharp_from_qasm_compilation, CompilerConfig, - OutputSemantics, ProgramType, QubitSemantics, + tests::{compile_all_with_config, qsharp_from_qasm_compilation}, + CompilerConfig, OutputSemantics, ProgramType, QubitSemantics, }; use expect_test::expect; use miette::Report; @@ -36,7 +36,7 @@ fn programs_with_includes_can_be_parsed() -> miette::Result<(), Vec> { Some("Test".into()), None, ); - let r = compile_all_with_config("source0.qasm", all_sources, config).map_err(|e| vec![e])?; + let r = compile_all_with_config("source0.qasm", all_sources, config)?; let qsharp = qsharp_from_qasm_compilation(r)?; expect![[r#" namespace qasm3_import { diff --git a/compiler/qsc_qasm3/src/tests/statement/reset.rs b/compiler/qsc_qasm3/src/tests/statement/reset.rs index 48ffa34f49..97698f9e2d 100644 --- a/compiler/qsc_qasm3/src/tests/statement/reset.rs +++ b/compiler/qsc_qasm3/src/tests/statement/reset.rs @@ -2,8 +2,7 @@ // Licensed under the MIT License. use crate::{ - compiler::compile_anon_with_config, - tests::{fail_on_compilation_errors, gen_qsharp}, + tests::{compile_with_config, fail_on_compilation_errors, gen_qsharp}, CompilerConfig, OutputSemantics, ProgramType, QubitSemantics, }; use expect_test::expect; @@ -31,7 +30,7 @@ fn reset_calls_are_generated_from_qasm() -> miette::Result<(), Vec> { Some("Test".into()), None, ); - let unit = compile_anon_with_config(source, config).map_err(|e| vec![e])?; + let unit = compile_with_config(source, config)?; fail_on_compilation_errors(&unit); let qsharp = gen_qsharp(&unit.package.expect("no package found")); expect![ diff --git a/compiler/qsc_qasm3/src/tests/statement/switch.rs b/compiler/qsc_qasm3/src/tests/statement/switch.rs index eaabadeb33..81083c13f8 100644 --- a/compiler/qsc_qasm3/src/tests/statement/switch.rs +++ b/compiler/qsc_qasm3/src/tests/statement/switch.rs @@ -47,7 +47,7 @@ fn default_as_only_case_causes_parse_error() { panic!("Expected an error, got {res:?}"); }; assert_eq!(errors.len(), 1); - expect![r#"QASM3 Parse Error: expecting `case` keyword"#].assert_eq(&errors[0].to_string()); + expect!["missing switch statement cases"].assert_eq(&errors[0].to_string()); } #[test] @@ -64,7 +64,7 @@ fn no_cases_causes_parse_error() { panic!("Expected an error, got {res:?}"); }; assert_eq!(errors.len(), 1); - expect![r#"QASM3 Parse Error: expecting `case` keyword"#].assert_eq(&errors[0].to_string()); + expect!["missing switch statement cases"].assert_eq(&errors[0].to_string()); } #[test] diff --git a/pip/src/interop.rs b/pip/src/interop.rs index 985159d95a..29847eff37 100644 --- a/pip/src/interop.rs +++ b/pip/src/interop.rs @@ -11,6 +11,7 @@ use pyo3::types::{PyDict, PyList}; use qsc::interpret::output::Receiver; use qsc::interpret::{into_errors, Interpreter}; use qsc::qasm3::io::SourceResolver; +use qsc::qasm3::io::{Error, ErrorKind}; use qsc::qasm3::{ qasm_to_program, CompilerConfig, OperationSignature, QasmCompileUnit, QubitSemantics, }; @@ -55,12 +56,15 @@ impl SourceResolver for ImportResolver where T: FileSystem, { - fn resolve

(&self, path: P) -> miette::Result<(PathBuf, String)> + fn resolve

(&self, path: P) -> miette::Result<(PathBuf, String), Error> where P: AsRef, { let path = self.path.join(path); - let (path, source) = self.fs.read_file(path.as_ref())?; + let (path, source) = self + .fs + .read_file(path.as_ref()) + .map_err(|e| Error(ErrorKind::IO(format!("{e}"))))?; Ok(( PathBuf::from(path.as_ref().to_owned()), source.as_ref().to_owned(), From 475c559f1d9bf2bbe25d4c5b8e4605cc3ed251e2 Mon Sep 17 00:00:00 2001 From: orpuente-MS <156957451+orpuente-MS@users.noreply.github.com> Date: Thu, 27 Mar 2025 09:25:25 -0700 Subject: [PATCH 067/108] Compile gphase, gatedef, and control flow statements (#2254) This PR completes the lowering and compilation for: - [x] gate definitions - [x] gphase - [x] for stmt - [x] if stmt - [x] switch stmt - [x] return stmt - [x] classical function calls - [x] classical function decls It also does some partial work to make the compiler infallible, by returning `ExprKind::Err` instead of None when compiling expressions. --- compiler/qsc_qasm3/src/ast_builder.rs | 78 ++- compiler/qsc_qasm3/src/compile.rs | 33 +- compiler/qsc_qasm3/src/compiler.rs | 653 +++++++++++------- compiler/qsc_qasm3/src/parser/ast.rs | 2 + compiler/qsc_qasm3/src/parser/mut_visit.rs | 1 + compiler/qsc_qasm3/src/parser/stmt.rs | 3 + .../qsc_qasm3/src/parser/stmt/tests/gphase.rs | 8 + compiler/qsc_qasm3/src/semantic/ast.rs | 99 +-- compiler/qsc_qasm3/src/semantic/error.rs | 10 + compiler/qsc_qasm3/src/semantic/lowerer.rs | 356 ++++++++-- compiler/qsc_qasm3/src/semantic/types.rs | 2 + compiler/qsc_qasm3/src/tests/declaration.rs | 1 + .../qsc_qasm3/src/tests/declaration/def.rs | 72 ++ compiler/qsc_qasm3/src/tests/expression.rs | 1 + .../src/tests/expression/function_call.rs | 229 ++++++ .../qsc_qasm3/src/tests/expression/ident.rs | 30 +- .../src/tests/statement/gate_call.rs | 134 +++- 17 files changed, 1309 insertions(+), 403 deletions(-) create mode 100644 compiler/qsc_qasm3/src/tests/declaration/def.rs create mode 100644 compiler/qsc_qasm3/src/tests/expression/function_call.rs diff --git a/compiler/qsc_qasm3/src/ast_builder.rs b/compiler/qsc_qasm3/src/ast_builder.rs index 5f65251db3..8cc6f70b74 100644 --- a/compiler/qsc_qasm3/src/ast_builder.rs +++ b/compiler/qsc_qasm3/src/ast_builder.rs @@ -810,7 +810,7 @@ pub(crate) fn build_call_with_param( operand: Expr, name_span: Span, operand_span: Span, - stmt_span: Span, + call_span: Span, ) -> Expr { let segments = build_idents(idents); let fn_name = Ident { @@ -838,7 +838,38 @@ pub(crate) fn build_call_with_param( Expr { id: NodeId::default(), - span: stmt_span, + span: call_span, + kind: Box::new(call), + } +} + +pub(crate) fn build_call_with_params( + name: &str, + idents: &[&str], + operands: Vec, + name_span: Span, + call_span: Span, +) -> Expr { + let segments = build_idents(idents); + let fn_name = Ident { + name: Rc::from(name), + span: name_span, + ..Default::default() + }; + let path_expr = Expr { + kind: Box::new(ExprKind::Path(PathKind::Ok(Box::new(Path { + segments, + name: Box::new(fn_name), + id: NodeId::default(), + span: Span::default(), + })))), + ..Default::default() + }; + let call = ExprKind::Call(Box::new(path_expr), Box::new(build_tuple_expr(operands))); + + Expr { + id: NodeId::default(), + span: call_span, kind: Box::new(call), } } @@ -1186,8 +1217,10 @@ fn wrap_ty_in_array(ty: Ty) -> Ty { } pub(crate) fn build_for_stmt( - loop_var: &crate::symbols::Symbol, - iter: crate::types::QasmTypedExpr, + loop_var_name: &str, + loop_var_span: Span, + loop_var_qsharp_ty: &crate::types::Type, + iter: Expr, body: Block, stmt_span: Span, ) -> Stmt { @@ -1197,15 +1230,15 @@ pub(crate) fn build_for_stmt( Box::new(Pat { kind: Box::new(PatKind::Bind( Box::new(Ident { - name: loop_var.name.clone().into(), - span: loop_var.span, + name: loop_var_name.into(), + span: loop_var_span, ..Default::default() }), - Some(Box::new(map_qsharp_type_to_ast_ty(&loop_var.qsharp_ty))), + Some(Box::new(map_qsharp_type_to_ast_ty(loop_var_qsharp_ty))), )), ..Default::default() }), - Box::new(iter.expr), + Box::new(iter), Box::new(body), )), span: stmt_span, @@ -1371,7 +1404,8 @@ pub(crate) fn build_gate_decl( } } -pub(crate) fn build_gate_decl_lambda>( +#[allow(clippy::too_many_arguments, clippy::too_many_lines)] +pub(crate) fn build_lambda>( name: S, cargs: Vec<(String, Ty, Pat)>, qargs: Vec<(String, Ty, Pat)>, @@ -1379,6 +1413,8 @@ pub(crate) fn build_gate_decl_lambda>( name_span: Span, body_span: Span, gate_span: Span, + return_type: Option, + kind: CallableKind, ) -> Stmt { let args = cargs .into_iter() @@ -1413,15 +1449,15 @@ pub(crate) fn build_gate_decl_lambda>( }) .map(Box::new) .collect::>(); - let input_pat = if args.len() > 1 { + let input_pat = if args.len() == 1 { ast::Pat { - kind: Box::new(PatKind::Tuple(name_args.into_boxed_slice())), + kind: Box::new(ast::PatKind::Paren(name_args[0].clone())), span: Span { lo, hi }, ..Default::default() } } else { ast::Pat { - kind: Box::new(ast::PatKind::Paren(name_args[0].clone())), + kind: Box::new(PatKind::Tuple(name_args.into_boxed_slice())), span: Span { lo, hi }, ..Default::default() } @@ -1438,29 +1474,35 @@ pub(crate) fn build_gate_decl_lambda>( let lambda_expr = Expr { id: NodeId::default(), kind: Box::new(ExprKind::Lambda( - CallableKind::Operation, + kind, Box::new(input_pat), Box::new(block_expr), )), span: gate_span, }; let ty_args = args.iter().map(|(_, ty, _)| ty.clone()).collect::>(); - let input_ty = if args.len() > 1 { + let input_ty = if args.len() == 1 { ast::Ty { - kind: Box::new(ast::TyKind::Tuple(ty_args.into_boxed_slice())), + kind: Box::new(ast::TyKind::Paren(Box::new(ty_args[0].clone()))), ..Default::default() } } else { ast::Ty { - kind: Box::new(ast::TyKind::Paren(Box::new(ty_args[0].clone()))), + kind: Box::new(ast::TyKind::Tuple(ty_args.into_boxed_slice())), ..Default::default() } }; + let return_type = if let Some(ty) = return_type { + ty + } else { + build_path_ident_ty("Unit") + }; + let lambda_ty = ast::Ty { kind: Box::new(ast::TyKind::Arrow( - CallableKind::Operation, + kind, Box::new(input_ty), - Box::new(build_path_ident_ty("Unit")), + Box::new(return_type), None, )), ..Default::default() diff --git a/compiler/qsc_qasm3/src/compile.rs b/compiler/qsc_qasm3/src/compile.rs index 49b189d2b0..35e2762b5e 100644 --- a/compiler/qsc_qasm3/src/compile.rs +++ b/compiler/qsc_qasm3/src/compile.rs @@ -9,18 +9,17 @@ use crate::ast_builder::{ build_barrier_call, build_binary_expr, build_cast_call, build_cast_call_two_params, build_classical_decl, build_complex_binary_expr, build_complex_from_expr, build_convert_call_expr, build_default_result_array_expr, build_expr_array_expr, - build_gate_call_param_expr, build_gate_decl_lambda, build_if_expr_then_block, - build_if_expr_then_block_else_block, build_if_expr_then_block_else_expr, - build_if_expr_then_expr_else_expr, build_implicit_return_stmt, - build_indexed_assignment_statement, build_lit_bigint_expr, build_lit_bool_expr, - build_lit_complex_expr, build_lit_double_expr, build_lit_int_expr, - build_lit_result_array_expr_from_bitstring, build_lit_result_expr, build_managed_qubit_alloc, - build_math_call_no_params, build_measure_call, build_operation_with_stmts, - build_path_ident_expr, build_range_expr, build_reset_call, build_stmt_semi_from_expr, - build_stmt_wrapped_block_expr, build_top_level_ns_with_item, build_tuple_expr, - build_unary_op_expr, build_unmanaged_qubit_alloc, build_unmanaged_qubit_alloc_array, - build_wrapped_block_expr, is_complex_binop_supported, managed_qubit_alloc_array, - map_qsharp_type_to_ast_ty, + build_gate_call_param_expr, build_if_expr_then_block, build_if_expr_then_block_else_block, + build_if_expr_then_block_else_expr, build_if_expr_then_expr_else_expr, + build_implicit_return_stmt, build_indexed_assignment_statement, build_lambda, + build_lit_bigint_expr, build_lit_bool_expr, build_lit_complex_expr, build_lit_double_expr, + build_lit_int_expr, build_lit_result_array_expr_from_bitstring, build_lit_result_expr, + build_managed_qubit_alloc, build_math_call_no_params, build_measure_call, + build_operation_with_stmts, build_path_ident_expr, build_range_expr, build_reset_call, + build_stmt_semi_from_expr, build_stmt_wrapped_block_expr, build_top_level_ns_with_item, + build_tuple_expr, build_unary_op_expr, build_unmanaged_qubit_alloc, + build_unmanaged_qubit_alloc_array, build_wrapped_block_expr, is_complex_binop_supported, + managed_qubit_alloc_array, map_qsharp_type_to_ast_ty, }; use crate::oqasm_helpers::{ @@ -2317,8 +2316,10 @@ impl QasmCompiler { }; Some(ast_builder::build_for_stmt( - &loop_var_symbol, - iterable, + &loop_var_symbol.name, + loop_var_symbol.span, + &loop_var_symbol.qsharp_ty, + iterable.expr, body, stmt_span, )) @@ -2471,7 +2472,7 @@ impl QasmCompiler { gate_span, )) } else { - Some(build_gate_decl_lambda( + Some(build_lambda( name.to_string(), cargs, qargs, @@ -2479,6 +2480,8 @@ impl QasmCompiler { name_span, body_span, gate_span, + None, + ast::CallableKind::Operation, )) } } diff --git a/compiler/qsc_qasm3/src/compiler.rs b/compiler/qsc_qasm3/src/compiler.rs index 35870469ca..d4cd79fa3d 100644 --- a/compiler/qsc_qasm3/src/compiler.rs +++ b/compiler/qsc_qasm3/src/compiler.rs @@ -10,28 +10,29 @@ use qsc_frontend::{compile::SourceMap, error::WithSource}; use crate::{ ast_builder::{ build_arg_pat, build_array_reverse_expr, build_assignment_statement, build_barrier_call, - build_binary_expr, build_cast_call, build_cast_call_two_params, build_classical_decl, - build_complex_from_expr, build_convert_call_expr, build_expr_array_expr, - build_gate_call_param_expr, build_gate_call_with_params_and_callee, + build_binary_expr, build_call_no_params, build_call_with_param, build_call_with_params, + build_cast_call, build_cast_call_two_params, build_classical_decl, build_complex_from_expr, + build_convert_call_expr, build_expr_array_expr, build_for_stmt, build_gate_call_param_expr, + build_gate_call_with_params_and_callee, build_if_expr_then_block, + build_if_expr_then_block_else_block, build_if_expr_then_block_else_expr, build_if_expr_then_expr_else_expr, build_implicit_return_stmt, - build_indexed_assignment_statement, build_lit_bigint_expr, build_lit_bool_expr, - build_lit_complex_expr, build_lit_double_expr, build_lit_int_expr, + build_indexed_assignment_statement, build_lambda, build_lit_bigint_expr, + build_lit_bool_expr, build_lit_complex_expr, build_lit_double_expr, build_lit_int_expr, build_lit_result_array_expr_from_bitstring, build_lit_result_expr, build_managed_qubit_alloc, build_math_call_from_exprs, build_math_call_no_params, - build_measure_call, build_operation_with_stmts, build_path_ident_expr, build_reset_call, - build_stmt_semi_from_expr, build_stmt_semi_from_expr_with_span, - build_top_level_ns_with_item, build_tuple_expr, build_unary_op_expr, - build_unmanaged_qubit_alloc, build_unmanaged_qubit_alloc_array, build_while_stmt, - build_wrapped_block_expr, managed_qubit_alloc_array, map_qsharp_type_to_ast_ty, - wrap_expr_in_parens, + build_measure_call, build_operation_with_stmts, build_path_ident_expr, build_range_expr, + build_reset_call, build_return_expr, build_return_unit, build_stmt_semi_from_expr, + build_stmt_semi_from_expr_with_span, build_top_level_ns_with_item, build_tuple_expr, + build_unary_op_expr, build_unmanaged_qubit_alloc, build_unmanaged_qubit_alloc_array, + build_while_stmt, build_wrapped_block_expr, managed_qubit_alloc_array, + map_qsharp_type_to_ast_ty, wrap_expr_in_parens, }, parser::ast::{list_from_iter, List}, runtime::{get_runtime_function_decls, RuntimeFunctions}, semantic::{ ast::{ - BinaryOpExpr, Cast, DiscreteSet, Expr, FunctionCall, GateOperand, GateOperandKind, - IndexElement, IndexExpr, IndexSet, IndexedIdent, LiteralKind, MeasureExpr, TimeUnit, - UnaryOpExpr, + BinaryOpExpr, Cast, DiscreteSet, Expr, GateOperand, GateOperandKind, IndexElement, + IndexExpr, IndexSet, IndexedIdent, LiteralKind, MeasureExpr, TimeUnit, UnaryOpExpr, }, symbols::{IOKind, Symbol, SymbolId, SymbolTable}, types::{ArrayDimensions, Type}, @@ -44,6 +45,16 @@ use crate::{ use crate::semantic::ast as semast; use qsc_ast::ast::{self as qsast, NodeId, Package}; +/// Helper to create an error expression. Used when we fail to +/// compile an expression. It is assumed that an error was +/// already reported. +fn err_expr(span: Span) -> qsast::Expr { + qsast::Expr { + span, + ..Default::default() + } +} + pub fn compile_with_config(source: S, path: P, config: CompilerConfig) -> QasmCompileUnit where S: AsRef, @@ -322,7 +333,6 @@ impl QasmCompiler { semast::StmtKind::For(stmt) => self.compile_for_stmt(stmt), semast::StmtKind::If(stmt) => self.compile_if_stmt(stmt), semast::StmtKind::GateCall(stmt) => self.compile_gate_call_stmt(stmt), - semast::StmtKind::GPhase(stmt) => self.compile_gphase_stmt(stmt), semast::StmtKind::Include(stmt) => self.compile_include_stmt(stmt), semast::StmtKind::InputDeclaration(stmt) => self.compile_input_decl_stmt(stmt), semast::StmtKind::OutputDeclaration(stmt) => self.compile_output_decl_stmt(stmt), @@ -353,9 +363,9 @@ impl QasmCompiler { let name = &symbol.name; let stmt_span = stmt.span; - let name_span = stmt.name_span; + let name_span = stmt.lhs_span; - let rhs = self.compile_expr(&stmt.rhs)?; + let rhs = self.compile_expr(&stmt.rhs); let stmt = build_assignment_statement(name_span, name, rhs, stmt_span); Some(stmt) @@ -370,7 +380,7 @@ impl QasmCompiler { let indices: Vec<_> = stmt .indices .iter() - .filter_map(|elem| self.compile_index_element(elem)) + .map(|elem| self.compile_index_element(elem)) .collect(); let rhs = self.compile_expr(&stmt.rhs); @@ -383,12 +393,7 @@ impl QasmCompiler { return None; } - if indices.len() != stmt.indices.len() { - return None; - } - - // Use the `?` operator after compiling checking all other errors. - let (rhs, index_expr) = (rhs?, indices[0].clone()); + let index_expr = indices[0].clone(); let stmt = build_indexed_assignment_statement( symbol.span, @@ -402,23 +407,16 @@ impl QasmCompiler { } fn compile_assign_op_stmt(&mut self, stmt: &semast::AssignOpStmt) -> Option { - self.push_unimplemented_error_message("assignment op statements", stmt.span); - None + let lhs = self.compile_expr(&stmt.lhs); + let rhs = self.compile_expr(&stmt.rhs); + let qsop = Self::map_bin_op(stmt.op); + let expr = build_binary_expr(true, qsop, lhs, rhs, stmt.span); + Some(build_stmt_semi_from_expr(expr)) } fn compile_barrier_stmt(&mut self, stmt: &semast::BarrierStmt) -> Option { - let qubits: Vec<_> = stmt - .qubits - .iter() - .filter_map(|q| self.compile_gate_operand(q)) - .collect(); - - if stmt.qubits.len() != qubits.len() { - // if any of the qubit arguments failed to compile we can't proceed. - // This can happen if the qubit is not defined. - return None; - } - + // we don't support barrier, but we can insert a runtime function + // which will generate a barrier call in QIR self.runtime.insert(RuntimeFunctions::Barrier); Some(build_barrier_call(stmt.span)) } @@ -467,7 +465,7 @@ impl QasmCompiler { let qsharp_ty = &symbol.qsharp_ty; let expr = decl.init_expr.as_ref(); - let expr = self.compile_expr(expr)?; + let expr = self.compile_expr(expr); let stmt = build_classical_decl( name, is_const, ty_span, decl_span, name_span, qsharp_ty, expr, ); @@ -476,8 +474,45 @@ impl QasmCompiler { } fn compile_def_stmt(&mut self, stmt: &semast::DefStmt) -> Option { - self.push_unimplemented_error_message("def statements", stmt.span); - None + let symbol = self.symbols[stmt.symbol_id].clone(); + let name = symbol.name.clone(); + + let cargs: Vec<_> = stmt + .params + .iter() + .map(|arg| { + let symbol = self.symbols[*arg].clone(); + let name = symbol.name.clone(); + let ast_type = map_qsharp_type_to_ast_ty(&symbol.qsharp_ty); + ( + name.clone(), + ast_type.clone(), + build_arg_pat(name, symbol.span, ast_type), + ) + }) + .collect(); + + let body = Some(self.compile_block(&stmt.body)); + let return_type = stmt.return_type.as_ref().map(map_qsharp_type_to_ast_ty); + let kind = if stmt.has_qubit_params { + qsast::CallableKind::Operation + } else { + qsast::CallableKind::Function + }; + + // We use the same primitives used for declaring gates, because def declarations + // in QASM3 can take qubits as arguments and call quantum gates. + Some(build_lambda( + name, + cargs, + vec![], + body, + symbol.span, + stmt.body.span, + stmt.span, + return_type, + kind, + )) } fn compile_def_cal_stmt(&mut self, stmt: &semast::DefCalStmt) -> Option { @@ -496,7 +531,7 @@ impl QasmCompiler { } fn compile_expr_stmt(&mut self, stmt: &semast::ExprStmt) -> Option { - let expr = self.compile_expr(&stmt.expr)?; + let expr = self.compile_expr(&stmt.expr); Some(build_stmt_semi_from_expr_with_span(expr, stmt.span)) } @@ -506,13 +541,68 @@ impl QasmCompiler { } fn compile_for_stmt(&mut self, stmt: &semast::ForStmt) -> Option { - self.push_unimplemented_error_message("for statements", stmt.span); - None + let loop_var = self.symbols[stmt.loop_variable].clone(); + let iterable = self.compile_enumerable_set(&stmt.set_declaration); + let body = self.compile_block(&Self::stmt_as_block(&stmt.body)); + + Some(build_for_stmt( + &loop_var.name, + loop_var.span, + &loop_var.qsharp_ty, + iterable, + body, + stmt.span, + )) } fn compile_if_stmt(&mut self, stmt: &semast::IfStmt) -> Option { - self.push_unimplemented_error_message("if statements", stmt.span); - None + let condition = self.compile_expr(&stmt.condition); + let then_block = self.compile_block(&Self::stmt_as_block(&stmt.if_body)); + let else_block = stmt + .else_body + .as_ref() + .map(|stmt| self.compile_block(&Self::stmt_as_block(stmt))); + + let if_expr = if let Some(else_block) = else_block { + build_if_expr_then_block_else_block(condition, then_block, else_block, stmt.span) + } else { + build_if_expr_then_block(condition, then_block, stmt.span) + }; + + Some(build_stmt_semi_from_expr(if_expr)) + } + + fn stmt_as_block(stmt: &semast::Stmt) -> semast::Block { + match &*stmt.kind { + semast::StmtKind::Block(block) => *block.to_owned(), + _ => semast::Block { + span: stmt.span, + stmts: list_from_iter([stmt.clone()]), + }, + } + } + + fn compile_function_call_expr(&mut self, expr: &semast::FunctionCall) -> qsast::Expr { + let symbol = self.symbols[expr.symbol_id].clone(); + let name = &symbol.name; + let name_span = symbol.span; + if expr.args.len() > 0 { + let args: Vec<_> = expr + .args + .iter() + .map(|expr| self.compile_expr(expr)) + .collect(); + + if args.len() == 1 { + let operand_span = expr.args[0].span; + let operand = args.into_iter().next().expect("there is one argument"); + build_call_with_param(name, &[], operand, name_span, operand_span, expr.span) + } else { + build_call_with_params(name, &[], args, name_span, expr.span) + } + } else { + build_call_no_params(name, &[], expr.span) + } } fn compile_gate_call_stmt(&mut self, stmt: &semast::GateCall) -> Option { @@ -520,23 +610,19 @@ impl QasmCompiler { if symbol.name == "U" { self.runtime |= RuntimeFunctions::U; } + if symbol.name == "gphase" { + self.runtime |= RuntimeFunctions::Gphase; + } let mut qubits: Vec<_> = stmt .qubits .iter() - .filter_map(|q| self.compile_gate_operand(q)) + .map(|q| self.compile_gate_operand(q)) .collect(); - let args: Vec<_> = stmt - .args - .iter() - .filter_map(|arg| self.compile_expr(arg)) - .collect(); - - if qubits.len() != stmt.qubits.len() || args.len() != stmt.args.len() { - return None; - } + let args: Vec<_> = stmt.args.iter().map(|arg| self.compile_expr(arg)).collect(); // Take the number of qubit args that the gates expects from the source qubits. - let gate_qubits = qubits.split_off(qubits.len() - stmt.quantum_arity as usize); + let gate_qubits = + qubits.split_off(qubits.len().saturating_sub(stmt.quantum_arity as usize)); // Then merge the classical args with the qubit args. This will give // us the args for the call prior to wrapping in tuples for controls. let args: Vec<_> = args.into_iter().chain(gate_qubits).collect(); @@ -553,7 +639,7 @@ impl QasmCompiler { ); } semast::GateModifierKind::Pow(expr) => { - let exponent_expr = self.compile_expr(expr)?; + let exponent_expr = self.compile_expr(expr); self.runtime |= RuntimeFunctions::Pow; args = build_tuple_expr(vec![exponent_expr, callee, args]); callee = build_path_ident_expr("__Pow__", modifier.span, stmt.span); @@ -569,7 +655,7 @@ impl QasmCompiler { self.push_semantic_error(kind); return None; } - let ctrl = qubits.split_off(qubits.len() - *num_ctrls as usize); + let ctrl = qubits.split_off(qubits.len().saturating_sub(*num_ctrls as usize)); let ctrls = build_expr_array_expr(ctrl, modifier.span); args = build_tuple_expr(vec![ctrls, args]); callee = build_unary_op_expr( @@ -589,7 +675,7 @@ impl QasmCompiler { self.push_semantic_error(kind); return None; } - let ctrl = qubits.split_off(qubits.len() - *num_ctrls as usize); + let ctrl = qubits.split_off(qubits.len().saturating_sub(*num_ctrls as usize)); let ctrls = build_expr_array_expr(ctrl, modifier.span); let lit_0 = build_lit_int_expr(0, Span::default()); args = build_tuple_expr(vec![lit_0, callee, ctrls, args]); @@ -599,22 +685,10 @@ impl QasmCompiler { } } - // This should never be reached, since semantic analysis during lowering - // makes sure the arities match. - if !qubits.is_empty() { - return None; - } - let expr = build_gate_call_with_params_and_callee(args, callee, stmt.span); Some(build_stmt_semi_from_expr(expr)) } - fn compile_gphase_stmt(&mut self, stmt: &semast::GPhase) -> Option { - self.runtime |= RuntimeFunctions::Gphase; - self.push_unimplemented_error_message("gphase statements", stmt.span); - None - } - fn compile_include_stmt(&mut self, stmt: &semast::IncludeStmt) -> Option { self.push_unimplemented_error_message("include statements", stmt.span); None @@ -650,7 +724,7 @@ impl QasmCompiler { let expr = stmt.init_expr.as_ref(); - let expr = self.compile_expr(expr)?; + let expr = self.compile_expr(expr); let stmt = build_classical_decl( name, is_const, ty_span, decl_span, name_span, qsharp_ty, expr, ); @@ -672,8 +746,52 @@ impl QasmCompiler { &mut self, stmt: &semast::QuantumGateDefinition, ) -> Option { - self.push_unimplemented_error_message("gate decl statements", stmt.span); - None + let symbol = self.symbols[stmt.symbol_id].clone(); + let name = symbol.name.clone(); + + let cargs: Vec<_> = stmt + .params + .iter() + .map(|arg| { + let symbol = self.symbols[*arg].clone(); + let name = symbol.name.clone(); + let ast_type = map_qsharp_type_to_ast_ty(&symbol.qsharp_ty); + ( + name.clone(), + ast_type.clone(), + build_arg_pat(name, symbol.span, ast_type), + ) + }) + .collect(); + + let qargs: Vec<_> = stmt + .qubits + .iter() + .map(|arg| { + let symbol = self.symbols[*arg].clone(); + let name = symbol.name.clone(); + let ast_type = map_qsharp_type_to_ast_ty(&symbol.qsharp_ty); + ( + name.clone(), + ast_type.clone(), + build_arg_pat(name, symbol.span, ast_type), + ) + }) + .collect(); + + let body = Some(self.compile_block(&stmt.body)); + + Some(build_lambda( + name, + cargs, + qargs, + body, + symbol.span, + stmt.body.span, + stmt.span, + None, + qsast::CallableKind::Operation, + )) } fn compile_qubit_decl_stmt(&mut self, stmt: &semast::QubitDeclaration) -> Option { @@ -712,24 +830,82 @@ impl QasmCompiler { } fn compile_reset_stmt(&mut self, stmt: &semast::ResetStmt) -> Option { - let operand = self.compile_gate_operand(&stmt.operand)?; + let operand = self.compile_gate_operand(&stmt.operand); let operand_span = operand.span; let expr = build_reset_call(operand, stmt.reset_token_span, operand_span); Some(build_stmt_semi_from_expr(expr)) } fn compile_return_stmt(&mut self, stmt: &semast::ReturnStmt) -> Option { - self.push_unimplemented_error_message("return statements", stmt.span); - None + let expr = stmt.expr.as_ref().map(|expr| self.compile_expr(expr)); + + let expr = if let Some(expr) = expr { + build_return_expr(expr, stmt.span) + } else { + build_return_unit(stmt.span) + }; + + Some(build_stmt_semi_from_expr(expr)) } fn compile_switch_stmt(&mut self, stmt: &semast::SwitchStmt) -> Option { - self.push_unimplemented_error_message("switch statements", stmt.span); - None + // For each case, convert the lhs into a sequence of equality checks + // and then fold them into a single expression of logical ors for + // the if expr + let control = self.compile_expr(&stmt.target); + let cases: Vec<(qsast::Expr, qsast::Block)> = stmt + .cases + .iter() + .map(|case| { + let block = self.compile_block(&case.block); + + let case = case + .labels + .iter() + .map(|label| { + let lhs = control.clone(); + let rhs = self.compile_expr(label); + build_binary_expr(false, qsast::BinOp::Eq, lhs, rhs, label.span) + }) + .fold(None, |acc, expr| match acc { + None => Some(expr), + Some(acc) => { + let qsop = qsast::BinOp::OrL; + let span = Span { + lo: acc.span.lo, + hi: expr.span.hi, + }; + Some(build_binary_expr(false, qsop, acc, expr, span)) + } + }); + // The type checker doesn't know that we have at least one case + // so we have to unwrap here since the accumulation is guaranteed + // to have Some(value) + let case = case.expect("Case must have at least one expression"); + (case, block) + }) + .collect(); + + let default_block = stmt.default.as_ref().map(|block| self.compile_block(block)); + + let default_expr = default_block.map(build_wrapped_block_expr); + let if_expr = cases + .into_iter() + .rev() + .fold(default_expr, |else_expr, (cond, block)| { + let span = Span { + lo: cond.span.lo, + hi: block.span.hi, + }; + Some(build_if_expr_then_block_else_expr( + cond, block, else_expr, span, + )) + }); + if_expr.map(build_stmt_semi_from_expr) } fn compile_while_stmt(&mut self, stmt: &semast::WhileLoop) -> Option { - let condition = self.compile_expr(&stmt.condition)?; + let condition = self.compile_expr(&stmt.condition); match &*stmt.body.kind { semast::StmtKind::Block(block) => { let block = self.compile_block(block); @@ -752,13 +928,12 @@ impl QasmCompiler { } } - fn compile_expr(&mut self, expr: &semast::Expr) -> Option { + fn compile_expr(&mut self, expr: &semast::Expr) -> qsast::Expr { match expr.kind.as_ref() { - semast::ExprKind::Err => { - // todo: determine if we should push an error here - // Are we going to allow trying to compile a program with semantic errors? - None - } + semast::ExprKind::Err => qsast::Expr { + span: expr.span, + ..Default::default() + }, semast::ExprKind::Ident(symbol_id) => self.compile_ident_expr(*symbol_id), semast::ExprKind::IndexedIdentifier(indexed_ident) => { self.compile_indexed_ident_expr(indexed_ident) @@ -770,7 +945,9 @@ impl QasmCompiler { semast::ExprKind::Lit(literal_kind) => { self.compile_literal_expr(literal_kind, expr.span) } - semast::ExprKind::FunctionCall(function_call) => self.compile_call_expr(function_call), + semast::ExprKind::FunctionCall(function_call) => { + self.compile_function_call_expr(function_call) + } semast::ExprKind::Cast(cast) => self.compile_cast_expr(cast), semast::ExprKind::IndexExpr(index_expr) => self.compile_index_expr(index_expr), semast::ExprKind::Paren(pexpr) => self.compile_paren_expr(pexpr, expr.span), @@ -778,10 +955,10 @@ impl QasmCompiler { } } - fn compile_ident_expr(&mut self, symbol_id: SymbolId) -> Option { + fn compile_ident_expr(&mut self, symbol_id: SymbolId) -> qsast::Expr { let symbol = &self.symbols[symbol_id]; let span = symbol.span; - let expr = match symbol.name.as_str() { + match symbol.name.as_str() { "euler" | "ℇ" => build_math_call_no_params("E", span), "pi" | "π" => build_math_call_no_params("PI", span), "tau" | "τ" => { @@ -797,18 +974,17 @@ impl QasmCompiler { } } _ => build_path_ident_expr(&symbol.name, span, span), - }; - Some(expr) + } } /// The lowerer eliminated indexed identifiers with zero indices. /// So we are safe to assume that the indices are non-empty. - fn compile_indexed_ident_expr(&mut self, indexed_ident: &IndexedIdent) -> Option { + fn compile_indexed_ident_expr(&mut self, indexed_ident: &IndexedIdent) -> qsast::Expr { let span = indexed_ident.span; let index: Vec<_> = indexed_ident .indices .iter() - .filter_map(|elem| self.compile_index_element(elem)) + .map(|elem| self.compile_index_element(elem)) .collect(); if index.len() != 1 { @@ -816,62 +992,54 @@ impl QasmCompiler { "multi-dimensional array index expressions", span, ); - return None; + return err_expr(indexed_ident.span); } let symbol = &self.symbols[indexed_ident.symbol_id]; let ident = build_path_ident_expr(&symbol.name, indexed_ident.name_span, indexed_ident.span); - let expr = qsast::Expr { + qsast::Expr { id: qsast::NodeId::default(), span, kind: Box::new(qsast::ExprKind::Index( Box::new(ident), Box::new(index[0].clone()), )), - }; - Some(expr) + } } - fn compile_unary_op_expr(&mut self, unary: &UnaryOpExpr) -> Option { + fn compile_unary_op_expr(&mut self, unary: &UnaryOpExpr) -> qsast::Expr { match unary.op { semast::UnaryOp::Neg => self.compile_neg_expr(&unary.expr, unary.span), semast::UnaryOp::NotB => self.compile_bitwise_not_expr(&unary.expr, unary.span), semast::UnaryOp::NotL => self.compile_logical_not_expr(&unary.expr, unary.span), } } - fn compile_neg_expr(&mut self, expr: &Expr, span: Span) -> Option { - let expr = self.compile_expr(expr)?; - Some(build_unary_op_expr(qsast::UnOp::Neg, expr, span)) + fn compile_neg_expr(&mut self, expr: &Expr, span: Span) -> qsast::Expr { + let expr = self.compile_expr(expr); + build_unary_op_expr(qsast::UnOp::Neg, expr, span) } - fn compile_bitwise_not_expr(&mut self, expr: &Expr, span: Span) -> Option { - let expr = self.compile_expr(expr)?; - Some(build_unary_op_expr(qsast::UnOp::NotB, expr, span)) + fn compile_bitwise_not_expr(&mut self, expr: &Expr, span: Span) -> qsast::Expr { + let expr = self.compile_expr(expr); + build_unary_op_expr(qsast::UnOp::NotB, expr, span) } - fn compile_logical_not_expr(&mut self, expr: &Expr, span: Span) -> Option { - let expr = self.compile_expr(expr)?; - Some(build_unary_op_expr(qsast::UnOp::NotL, expr, span)) + fn compile_logical_not_expr(&mut self, expr: &Expr, span: Span) -> qsast::Expr { + let expr = self.compile_expr(expr); + build_unary_op_expr(qsast::UnOp::NotL, expr, span) } - fn compile_binary_op_expr(&mut self, binary: &BinaryOpExpr) -> Option { + fn compile_binary_op_expr(&mut self, binary: &BinaryOpExpr) -> qsast::Expr { let lhs = self.compile_expr(&binary.lhs); let rhs = self.compile_expr(&binary.rhs); - let (lhs, rhs) = (lhs?, rhs?); let op = Self::map_bin_op(binary.op); let is_assignment = false; - Some(build_binary_expr( - is_assignment, - op, - lhs, - rhs, - binary.span(), - )) + build_binary_expr(is_assignment, op, lhs, rhs, binary.span()) } - fn compile_literal_expr(&mut self, lit: &LiteralKind, span: Span) -> Option { + fn compile_literal_expr(&mut self, lit: &LiteralKind, span: Span) -> qsast::Expr { match lit { LiteralKind::Array(value) => self.compile_array_literal(value, span), LiteralKind::Bitstring(big_int, width) => { @@ -890,13 +1058,8 @@ impl QasmCompiler { } } - fn compile_call_expr(&mut self, call: &FunctionCall) -> Option { - self.push_unimplemented_error_message("function call expresssions", call.span); - None - } - - fn compile_cast_expr(&mut self, cast: &Cast) -> Option { - let expr = self.compile_expr(&cast.expr)?; + fn compile_cast_expr(&mut self, cast: &Cast) -> qsast::Expr { + let expr = self.compile_expr(&cast.expr); let cast_expr = match cast.expr.ty { crate::semantic::types::Type::Bit(_) => { self.cast_bit_expr_to_ty(expr, &cast.expr.ty, &cast.ty, cast.span) @@ -922,9 +1085,9 @@ impl QasmCompiler { crate::semantic::types::Type::BitArray(ArrayDimensions::One(size), _) => { self.cast_bit_array_expr_to_ty(expr, &cast.expr.ty, &cast.ty, size, cast.span) } - _ => None, + _ => err_expr(cast.span), }; - if cast_expr.is_none() { + if matches!(*cast_expr.kind, qsast::ExprKind::Err) { self.push_unsupported_error_message( format!("casting {} to {} type", cast.expr.ty, cast.ty), cast.span, @@ -933,26 +1096,25 @@ impl QasmCompiler { cast_expr } - fn compile_index_expr(&mut self, index: &IndexExpr) -> Option { + fn compile_index_expr(&mut self, index: &IndexExpr) -> qsast::Expr { self.push_unimplemented_error_message("index expressions", index.span); - None + err_expr(index.span) } - fn compile_paren_expr(&mut self, paren: &Expr, span: Span) -> Option { - let expr = self.compile_expr(paren)?; - Some(wrap_expr_in_parens(expr, span)) + fn compile_paren_expr(&mut self, paren: &Expr, span: Span) -> qsast::Expr { + let expr = self.compile_expr(paren); + wrap_expr_in_parens(expr, span) } - fn compile_measure_expr(&mut self, expr: &MeasureExpr) -> Option { + fn compile_measure_expr(&mut self, expr: &MeasureExpr) -> qsast::Expr { let call_span = expr.span; let name_span = expr.measure_token_span; - let arg = self.compile_gate_operand(&expr.operand)?; + let arg = self.compile_gate_operand(&expr.operand); let operand_span = expr.operand.span; - let expr = build_measure_call(arg, name_span, operand_span, call_span); - Some(expr) + build_measure_call(arg, name_span, operand_span, call_span) } - fn compile_gate_operand(&mut self, op: &GateOperand) -> Option { + fn compile_gate_operand(&mut self, op: &GateOperand) -> qsast::Expr { match &op.kind { GateOperandKind::HardwareQubit(hw) => { // We don't support hardware qubits, so we need to push an error @@ -961,27 +1123,31 @@ impl QasmCompiler { // catch any other errors let message = "Hardware qubit operands"; self.push_unsupported_error_message(message, op.span); - let ident = build_path_ident_expr(hw.name.clone(), hw.span, op.span); - Some(ident) + build_path_ident_expr(hw.name.clone(), hw.span, op.span) } GateOperandKind::Expr(expr) => self.compile_expr(expr), - GateOperandKind::Err => None, + GateOperandKind::Err => err_expr(op.span), } } - fn compile_index_element(&mut self, elem: &IndexElement) -> Option { + fn compile_index_element(&mut self, elem: &IndexElement) -> qsast::Expr { match elem { IndexElement::DiscreteSet(discrete_set) => self.compile_discrete_set(discrete_set), IndexElement::IndexSet(index_set) => self.compile_index_set(index_set), } } - fn compile_discrete_set(&mut self, set: &DiscreteSet) -> Option { - self.push_unimplemented_error_message("discrete set expressions", set.span); - None + fn compile_discrete_set(&mut self, set: &DiscreteSet) -> qsast::Expr { + let expr_list: Vec<_> = set + .values + .iter() + .map(|expr| self.compile_expr(expr)) + .collect(); + + build_expr_array_expr(expr_list, set.span) } - fn compile_index_set(&mut self, set: &IndexSet) -> Option { + fn compile_index_set(&mut self, set: &IndexSet) -> qsast::Expr { // This is a temporary limitation. We can only handle // single index expressions for now. if set.values.len() == 1 { @@ -991,20 +1157,44 @@ impl QasmCompiler { } self.push_unsupported_error_message("index set expressions with multiple values", set.span); - None + err_expr(set.span) + } + + fn compile_enumerable_set(&mut self, set: &semast::EnumerableSet) -> qsast::Expr { + match set { + semast::EnumerableSet::DiscreteSet(set) => self.compile_discrete_set(set), + semast::EnumerableSet::Expr(expr) => self.compile_expr(expr), + semast::EnumerableSet::RangeDefinition(range) => self.compile_range_expr(range), + } + } + + fn compile_range_expr(&mut self, range: &semast::RangeDefinition) -> qsast::Expr { + let Some(start) = &range.start else { + self.push_unimplemented_error_message("omitted range start", range.span); + return err_expr(range.span); + }; + let Some(end) = &range.end else { + self.push_unimplemented_error_message("omitted range end", range.span); + return err_expr(range.span); + }; + + let start = self.compile_expr(start); + let end = self.compile_expr(end); + let step = range.step.as_ref().map(|expr| self.compile_expr(expr)); + build_range_expr(start, end, step, range.span) } - fn compile_array_literal(&mut self, _value: &List, span: Span) -> Option { + fn compile_array_literal(&mut self, _value: &List, span: Span) -> qsast::Expr { self.push_unimplemented_error_message("array literals", span); - None + err_expr(span) } - fn compile_bit_literal(value: bool, span: Span) -> Option { - Some(build_lit_result_expr(value.into(), span)) + fn compile_bit_literal(value: bool, span: Span) -> qsast::Expr { + build_lit_result_expr(value.into(), span) } - fn compile_bool_literal(value: bool, span: Span) -> Option { - Some(build_lit_bool_expr(value, span)) + fn compile_bool_literal(value: bool, span: Span) -> qsast::Expr { + build_lit_bool_expr(value, span) } fn compile_duration_literal( @@ -1012,43 +1202,40 @@ impl QasmCompiler { _value: f64, _unit: TimeUnit, span: Span, - ) -> Option { + ) -> qsast::Expr { self.push_unsupported_error_message("timing literals", span); - None + err_expr(span) } - fn compile_bitstring_literal(value: &BigInt, width: u32, span: Span) -> Option { + fn compile_bitstring_literal(value: &BigInt, width: u32, span: Span) -> qsast::Expr { let width = width as usize; let bitstring = if value == &BigInt::ZERO && width == 0 { "Bitstring(\"\")".to_string() } else { format!("Bitstring(\"{:0>width$}\")", value.to_str_radix(2)) }; - Some(build_lit_result_array_expr_from_bitstring(bitstring, span)) + build_lit_result_array_expr_from_bitstring(bitstring, span) } - fn compile_complex_literal(real: f64, imag: f64, span: Span) -> Option { - Some(build_lit_complex_expr( - crate::types::Complex::new(real, imag), - span, - )) + fn compile_complex_literal(real: f64, imag: f64, span: Span) -> qsast::Expr { + build_lit_complex_expr(crate::types::Complex::new(real, imag), span) } - fn compile_float_literal(value: f64, span: Span) -> Option { - Some(build_lit_double_expr(value, span)) + fn compile_float_literal(value: f64, span: Span) -> qsast::Expr { + build_lit_double_expr(value, span) } - fn compile_int_literal(value: i64, span: Span) -> Option { - Some(build_lit_int_expr(value, span)) + fn compile_int_literal(value: i64, span: Span) -> qsast::Expr { + build_lit_int_expr(value, span) } - fn compile_bigint_literal(value: &BigInt, span: Span) -> Option { - Some(build_lit_bigint_expr(value.clone(), span)) + fn compile_bigint_literal(value: &BigInt, span: Span) -> qsast::Expr { + build_lit_bigint_expr(value.clone(), span) } - fn compile_string_literal(&mut self, _value: &Rc, span: Span) -> Option { + fn compile_string_literal(&mut self, _value: &Rc, span: Span) -> qsast::Expr { self.push_unimplemented_error_message("string literal expressions", span); - None + err_expr(span) } /// Pushes an unsupported error with the supplied message. @@ -1083,33 +1270,33 @@ impl QasmCompiler { expr: &qsast::Expr, expr_ty: &crate::semantic::types::Type, ty: &crate::semantic::types::Type, - _span: Span, - ) -> Option { + span: Span, + ) -> qsast::Expr { assert!(matches!(expr_ty, Type::Angle(..))); // https://openqasm.com/language/types.html#casting-from-angle match ty { Type::Angle(..) => { let msg = "Cast angle to angle"; self.push_unimplemented_error_message(msg, expr.span); - None + err_expr(span) } Type::Bit(..) => { let msg = "Cast angle to bit"; self.push_unimplemented_error_message(msg, expr.span); - None + err_expr(span) } Type::BitArray(..) => { let msg = "Cast angle to bit array"; self.push_unimplemented_error_message(msg, expr.span); - None + err_expr(span) } Type::Bool(..) => { let msg = "Cast angle to bool"; self.push_unimplemented_error_message(msg, expr.span); - None + err_expr(span) } - _ => None, + _ => err_expr(span), } } @@ -1126,32 +1313,27 @@ impl QasmCompiler { expr_ty: &crate::semantic::types::Type, ty: &crate::semantic::types::Type, span: Span, - ) -> Option { + ) -> qsast::Expr { assert!(matches!(expr_ty, Type::Bit(..))); // There is no operand, choosing the span of the node // but we could use the expr span as well. let operand_span = expr.span; let name_span = span; match ty { - &Type::Angle(..) => { - let msg = "Cast bit to angle"; - self.push_unimplemented_error_message(msg, expr.span); - None - } &Type::Bool(..) => { self.runtime |= RuntimeFunctions::ResultAsBool; - Some(build_cast_call( + build_cast_call( RuntimeFunctions::ResultAsBool, expr, name_span, operand_span, - )) + ) } &Type::Float(..) => { // The spec says that this cast isn't supported, but it // casts to other types that case to float. For now, we'll // say it is invalid like the spec. - None + err_expr(span) } &Type::Int(w, _) | &Type::UInt(w, _) => { let function = if let Some(width) = w { @@ -1164,11 +1346,9 @@ impl QasmCompiler { RuntimeFunctions::ResultAsInt }; self.runtime |= function; - let expr = build_cast_call(function, expr, name_span, operand_span); - Some(expr) + build_cast_call(function, expr, name_span, operand_span) } - - _ => None, + _ => err_expr(span), } } @@ -1179,7 +1359,7 @@ impl QasmCompiler { ty: &crate::semantic::types::Type, size: u32, span: Span, - ) -> Option { + ) -> qsast::Expr { assert!(matches!( expr_ty, Type::BitArray(ArrayDimensions::One(_), _) @@ -1189,7 +1369,7 @@ impl QasmCompiler { let operand_span = span; if !matches!(ty, Type::Int(..) | Type::UInt(..)) { - return None; + return err_expr(span); } // we know we have a bit array being cast to an int/uint // verfiy widths @@ -1197,15 +1377,14 @@ impl QasmCompiler { if int_width.is_none() || (int_width == Some(size)) { self.runtime |= RuntimeFunctions::ResultArrayAsIntBE; - let expr = build_cast_call( + build_cast_call( RuntimeFunctions::ResultArrayAsIntBE, expr, name_span, operand_span, - ); - Some(expr) + ) } else { - None + err_expr(span) } } @@ -1222,31 +1401,28 @@ impl QasmCompiler { expr_ty: &crate::semantic::types::Type, ty: &crate::semantic::types::Type, span: Span, - ) -> Option { + ) -> qsast::Expr { assert!(matches!(expr_ty, Type::Bool(..))); - let name_span = expr.span; let operand_span = span; match ty { Type::Bit(..) => { self.runtime |= RuntimeFunctions::BoolAsResult; - let expr = build_cast_call( + build_cast_call( RuntimeFunctions::BoolAsResult, expr, name_span, operand_span, - ); - Some(expr) + ) } Type::Float(..) => { self.runtime |= RuntimeFunctions::BoolAsDouble; - let expr = build_cast_call( + build_cast_call( RuntimeFunctions::BoolAsDouble, expr, name_span, operand_span, - ); - Some(expr) + ) } Type::Int(w, _) | Type::UInt(w, _) => { let function = if let Some(width) = w { @@ -1259,10 +1435,9 @@ impl QasmCompiler { RuntimeFunctions::BoolAsInt }; self.runtime |= function; - let expr = build_cast_call(function, expr, name_span, operand_span); - Some(expr) + build_cast_call(function, expr, name_span, operand_span) } - _ => None, + _ => err_expr(span), } } @@ -1272,9 +1447,9 @@ impl QasmCompiler { _expr_ty: &crate::semantic::types::Type, _ty: &crate::semantic::types::Type, span: Span, - ) -> Option { + ) -> qsast::Expr { self.push_unimplemented_error_message("cast complex expressions", span); - None + err_expr(span) } fn cast_duration_expr_to_ty( @@ -1283,9 +1458,9 @@ impl QasmCompiler { _expr_ty: &crate::semantic::types::Type, _ty: &crate::semantic::types::Type, span: Span, - ) -> Option { + ) -> qsast::Expr { self.push_unimplemented_error_message("cast duration expressions", span); - None + err_expr(span) } /// +----------------+-------------------------------------------------------------+ @@ -1303,21 +1478,18 @@ impl QasmCompiler { expr_ty: &crate::semantic::types::Type, ty: &crate::semantic::types::Type, span: Span, - ) -> Option { + ) -> qsast::Expr { assert!(matches!(expr_ty, Type::Float(..))); match ty { - &Type::Complex(..) => { - let expr = build_complex_from_expr(expr); - Some(expr) - } + &Type::Complex(..) => build_complex_from_expr(expr), &Type::Angle(..) => { let msg = "Cast float to angle"; self.push_unimplemented_error_message(msg, expr.span); - None + err_expr(span) } &Type::Int(w, _) | &Type::UInt(w, _) => { let expr = build_math_call_from_exprs("Truncate", vec![expr], span); - let expr = if let Some(w) = w { + if let Some(w) = w { if w > 64 { build_convert_call_expr(expr, "IntAsBigInt") } else { @@ -1325,9 +1497,7 @@ impl QasmCompiler { } } else { expr - }; - - Some(expr) + } } &Type::Bool(..) => { let span = expr.span; @@ -1335,15 +1505,14 @@ impl QasmCompiler { let const_int_zero_expr = build_lit_int_expr(0, span); let qsop = qsast::BinOp::Eq; let cond = build_binary_expr(false, qsop, expr, const_int_zero_expr, span); - let coerce_expr = build_if_expr_then_expr_else_expr( + build_if_expr_then_expr_else_expr( cond, build_lit_bool_expr(false, span), build_lit_bool_expr(true, span), span, - ); - Some(coerce_expr) + ) } - _ => None, + _ => err_expr(span), } } @@ -1368,7 +1537,7 @@ impl QasmCompiler { expr_ty: &crate::semantic::types::Type, ty: &crate::semantic::types::Type, span: Span, - ) -> Option { + ) -> qsast::Expr { assert!(matches!(expr_ty, Type::Int(..) | Type::UInt(..))); let name_span = expr.span; let operand_span = span; @@ -1376,35 +1545,30 @@ impl QasmCompiler { Type::BitArray(dims, _) => { self.runtime |= RuntimeFunctions::IntAsResultArrayBE; let ArrayDimensions::One(size) = dims else { - return None; + return err_expr(span); }; let size = i64::from(*size); let size_expr = build_lit_int_expr(size, Span::default()); - let expr = build_cast_call_two_params( + build_cast_call_two_params( RuntimeFunctions::IntAsResultArrayBE, expr, size_expr, name_span, operand_span, - ); - Some(expr) - } - Type::Float(..) => { - let expr = build_convert_call_expr(expr, "IntAsDouble"); - Some(expr) + ) } + Type::Float(..) => build_convert_call_expr(expr, "IntAsDouble"), Type::Int(tw, _) | Type::UInt(tw, _) => { // uint to int, or int/uint to BigInt if let Some(tw) = tw { if *tw > 64 { - let expr = build_convert_call_expr(expr, "IntAsBigInt"); - Some(expr) + build_convert_call_expr(expr, "IntAsBigInt") } else { - Some(expr) + expr } } else { - Some(expr) + expr } } Type::Bool(..) => { @@ -1412,33 +1576,30 @@ impl QasmCompiler { let const_int_zero_expr = build_lit_int_expr(0, expr.span); let qsop = qsast::BinOp::Eq; let cond = build_binary_expr(false, qsop, expr, const_int_zero_expr, expr_span); - let coerce_expr = build_if_expr_then_expr_else_expr( + build_if_expr_then_expr_else_expr( cond, build_lit_bool_expr(false, expr_span), build_lit_bool_expr(true, expr_span), expr_span, - ); - Some(coerce_expr) + ) } Type::Bit(..) => { let expr_span = expr.span; let const_int_zero_expr = build_lit_int_expr(0, expr.span); let qsop = qsast::BinOp::Eq; let cond = build_binary_expr(false, qsop, expr, const_int_zero_expr, expr_span); - let coerce_expr = build_if_expr_then_expr_else_expr( + build_if_expr_then_expr_else_expr( cond, build_lit_result_expr(qsast::Result::One, expr_span), build_lit_result_expr(qsast::Result::Zero, expr_span), expr_span, - ); - Some(coerce_expr) + ) } Type::Complex(..) => { let expr = build_convert_call_expr(expr, "IntAsDouble"); - let expr = build_complex_from_expr(expr); - Some(expr) + build_complex_from_expr(expr) } - _ => None, + _ => err_expr(span), } } diff --git a/compiler/qsc_qasm3/src/parser/ast.rs b/compiler/qsc_qasm3/src/parser/ast.rs index cdcd499e30..442a13e7aa 100644 --- a/compiler/qsc_qasm3/src/parser/ast.rs +++ b/compiler/qsc_qasm3/src/parser/ast.rs @@ -1090,6 +1090,7 @@ impl Display for GateCall { #[derive(Clone, Debug)] pub struct GPhase { pub span: Span, + pub gphase_token_span: Span, pub modifiers: List, pub args: List, pub qubits: List, @@ -1099,6 +1100,7 @@ pub struct GPhase { impl Display for GPhase { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { writeln_header(f, "GPhase", self.span)?; + writeln_field(f, "gphase_token_span", &self.gphase_token_span)?; writeln_list_field(f, "modifiers", &self.modifiers)?; writeln_list_field(f, "args", &self.args)?; writeln_opt_field(f, "duration", self.duration.as_ref())?; diff --git a/compiler/qsc_qasm3/src/parser/mut_visit.rs b/compiler/qsc_qasm3/src/parser/mut_visit.rs index 838523708f..f14543d1ad 100644 --- a/compiler/qsc_qasm3/src/parser/mut_visit.rs +++ b/compiler/qsc_qasm3/src/parser/mut_visit.rs @@ -515,6 +515,7 @@ fn walk_gate_call_stmt(vis: &mut impl MutVisitor, stmt: &mut GateCall) { fn walk_gphase_stmt(vis: &mut impl MutVisitor, stmt: &mut GPhase) { vis.visit_span(&mut stmt.span); + vis.visit_span(&mut stmt.gphase_token_span); stmt.modifiers .iter_mut() .for_each(|m| vis.visit_gate_modifier(m)); diff --git a/compiler/qsc_qasm3/src/parser/stmt.rs b/compiler/qsc_qasm3/src/parser/stmt.rs index 27be880edc..a172af1be2 100644 --- a/compiler/qsc_qasm3/src/parser/stmt.rs +++ b/compiler/qsc_qasm3/src/parser/stmt.rs @@ -1540,7 +1540,9 @@ fn parse_gphase( lo: u32, modifiers: List, ) -> Result { + let gphase_token_lo = s.peek().span.lo; token(s, TokenKind::GPhase)?; + let gphase_token_span = s.span(gphase_token_lo); let args_lo = s.peek().span.lo; let args = opt(s, |s| { @@ -1563,6 +1565,7 @@ fn parse_gphase( Ok(GPhase { span: s.span(lo), + gphase_token_span, modifiers, args, qubits, diff --git a/compiler/qsc_qasm3/src/parser/stmt/tests/gphase.rs b/compiler/qsc_qasm3/src/parser/stmt/tests/gphase.rs index f2b5cb94f0..ca70bdef4f 100644 --- a/compiler/qsc_qasm3/src/parser/stmt/tests/gphase.rs +++ b/compiler/qsc_qasm3/src/parser/stmt/tests/gphase.rs @@ -14,6 +14,7 @@ fn gphase() { Stmt [0-15]: annotations: kind: GPhase [0-15]: + gphase_token_span: [0-6] modifiers: args: Expr [7-13]: BinaryOpExpr: @@ -34,6 +35,7 @@ fn gphase_qubit_ident() { Stmt [0-13]: annotations: kind: GPhase [0-13]: + gphase_token_span: [0-6] modifiers: args: Expr [7-8]: Ident [7-8] "a" @@ -56,6 +58,7 @@ fn gphase_qubit_register() { Stmt [0-15]: annotations: kind: GPhase [0-15]: + gphase_token_span: [0-6] modifiers: args: Expr [7-8]: Ident [7-8] "a" @@ -81,6 +84,7 @@ fn gphase_multiple_qubits() { Stmt [0-19]: annotations: kind: GPhase [0-19]: + gphase_token_span: [0-6] modifiers: args: Expr [7-8]: Ident [7-8] "a" @@ -111,6 +115,7 @@ fn gphase_no_arguments() { Stmt [0-7]: annotations: kind: GPhase [0-7]: + gphase_token_span: [0-6] modifiers: args: duration: @@ -138,6 +143,7 @@ fn gphase_with_parameters() { Stmt [0-15]: annotations: kind: GPhase [0-15]: + gphase_token_span: [0-6] modifiers: args: Expr [7-13]: BinaryOpExpr: @@ -158,6 +164,7 @@ fn gphase_inv_modifier() { Stmt [0-16]: annotations: kind: GPhase [0-16]: + gphase_token_span: [6-12] modifiers: QuantumGateModifier [0-5]: Inv args: @@ -176,6 +183,7 @@ fn gphase_ctrl_inv_modifiers() { Stmt [0-31]: annotations: kind: GPhase [0-31]: + gphase_token_span: [13-19] modifiers: QuantumGateModifier [0-6]: Ctrl None QuantumGateModifier [7-12]: Inv diff --git a/compiler/qsc_qasm3/src/semantic/ast.rs b/compiler/qsc_qasm3/src/semantic/ast.rs index f1bde908fe..af17ba9830 100644 --- a/compiler/qsc_qasm3/src/semantic/ast.rs +++ b/compiler/qsc_qasm3/src/semantic/ast.rs @@ -357,7 +357,6 @@ pub enum StmtKind { For(ForStmt), If(IfStmt), GateCall(GateCall), - GPhase(GPhase), Include(IncludeStmt), InputDeclaration(InputDeclaration), OutputDeclaration(OutputDeclaration), @@ -394,7 +393,6 @@ impl Display for StmtKind { StmtKind::ExternDecl(decl) => write!(f, "{decl}"), StmtKind::For(for_stmt) => write!(f, "{for_stmt}"), StmtKind::GateCall(gate_call) => write!(f, "{gate_call}"), - StmtKind::GPhase(gphase) => write!(f, "{gphase}"), StmtKind::If(if_stmt) => write!(f, "{if_stmt}"), StmtKind::Include(include) => write!(f, "{include}"), StmtKind::IndexedAssign(assign) => write!(f, "{assign}"), @@ -1043,16 +1041,16 @@ impl Display for QubitArrayDeclaration { #[derive(Clone, Debug)] pub struct QuantumGateDefinition { pub span: Span, - pub ident: Box, - pub params: List, - pub qubits: List, - pub body: Box, + pub symbol_id: SymbolId, + pub params: Box<[SymbolId]>, + pub qubits: Box<[SymbolId]>, + pub body: Block, } impl Display for QuantumGateDefinition { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { writeln_header(f, "Gate", self.span)?; - writeln_field(f, "ident", &self.ident)?; + writeln_field(f, "symbol_id", &self.symbol_id)?; writeln_list_field(f, "parameters", &self.params)?; writeln_list_field(f, "qubits", &self.qubits)?; write_field(f, "body", &self.body) @@ -1084,8 +1082,8 @@ pub struct GateCall { pub args: List, pub qubits: List, pub duration: Option, + pub classical_arity: u32, pub quantum_arity: u32, - pub quantum_arity_with_modifiers: u32, } impl Display for GateCall { @@ -1094,33 +1092,10 @@ impl Display for GateCall { writeln_list_field(f, "modifiers", &self.modifiers)?; writeln_field(f, "symbol_id", &self.symbol_id)?; writeln_list_field(f, "args", &self.args)?; - writeln_opt_field(f, "duration", self.duration.as_ref())?; writeln_list_field(f, "qubits", &self.qubits)?; - writeln_field(f, "quantum_arity", &self.quantum_arity)?; - write_field( - f, - "quantum_arity_with_modifiers", - &self.quantum_arity_with_modifiers, - ) - } -} - -#[derive(Clone, Debug)] -pub struct GPhase { - pub span: Span, - pub modifiers: List, - pub args: List, - pub qubits: List, - pub duration: Option, -} - -impl Display for GPhase { - fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { - writeln_header(f, "GPhase", self.span)?; - writeln_list_field(f, "modifiers", &self.modifiers)?; - writeln_list_field(f, "args", &self.args)?; writeln_opt_field(f, "duration", self.duration.as_ref())?; - write_list_field(f, "qubits", &self.qubits) + writeln_field(f, "classical_arity", &self.classical_arity)?; + write_field(f, "quantum_arity", &self.quantum_arity) } } @@ -1216,42 +1191,6 @@ impl Display for OutputDeclaration { } } -#[derive(Clone, Debug)] -pub enum TypedParameter { - Scalar(ScalarTypedParameter), - Quantum(QuantumTypedParameter), - ArrayReference(ArrayTypedParameter), -} - -impl WithSpan for TypedParameter { - fn with_span(self, span: Span) -> Self { - match self { - Self::Scalar(param) => Self::Scalar(param.with_span(span)), - Self::Quantum(param) => Self::Quantum(param.with_span(span)), - Self::ArrayReference(param) => Self::ArrayReference(param.with_span(span)), - } - } -} - -impl Display for TypedParameter { - fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { - match self { - Self::Scalar(param) => write!(f, "{param}"), - Self::Quantum(param) => write!(f, "{param}"), - Self::ArrayReference(param) => write!(f, "{param}"), - } - } -} -impl Default for TypedParameter { - fn default() -> Self { - Self::Scalar(ScalarTypedParameter { - span: Span::default(), - ident: Ident::default(), - ty: Box::default(), - }) - } -} - #[derive(Clone, Debug)] pub struct ScalarTypedParameter { pub span: Span, @@ -1321,16 +1260,18 @@ impl WithSpan for ArrayTypedParameter { #[derive(Clone, Debug)] pub struct DefStmt { pub span: Span, - pub name: Box, - pub params: List, - pub body: Box, - pub return_type: Option, + pub symbol_id: SymbolId, + pub has_qubit_params: bool, + pub params: Box<[SymbolId]>, + pub body: Block, + pub return_type: Option, } impl Display for DefStmt { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { writeln_header(f, "DefStmt", self.span)?; - writeln_field(f, "ident", &self.name)?; + writeln_field(f, "symbol_id", &self.symbol_id)?; + writeln_field(f, "has_qubit_params", &self.has_qubit_params)?; writeln_list_field(f, "parameters", &self.params)?; writeln_opt_field(f, "return_type", self.return_type.as_ref())?; write_field(f, "body", &self.body) @@ -1472,8 +1413,8 @@ impl Display for ExprKind { #[derive(Clone, Debug)] pub struct AssignStmt { pub span: Span, - pub name_span: Span, pub symbol_id: SymbolId, + pub lhs_span: Span, pub rhs: Expr, } @@ -1481,7 +1422,7 @@ impl Display for AssignStmt { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { writeln_header(f, "AssignStmt", self.span)?; writeln_field(f, "symbol_id", &self.symbol_id)?; - writeln_field(f, "name_span", &self.name_span)?; + writeln_field(f, "lhs_span", &self.lhs_span)?; write_field(f, "rhs", &self.rhs) } } @@ -1507,6 +1448,7 @@ impl Display for IndexedAssignStmt { pub struct AssignOpStmt { pub span: Span, pub symbol_id: SymbolId, + pub indices: List, pub op: BinOp, pub lhs: Expr, pub rhs: Expr, @@ -1516,6 +1458,7 @@ impl Display for AssignOpStmt { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { writeln_header(f, "AssignOpStmt", self.span)?; writeln_field(f, "symbol_id", &self.symbol_id)?; + writeln_list_field(f, "indices", &self.indices)?; writeln_field(f, "op", &self.op)?; writeln_field(f, "lhs", &self.rhs)?; write_field(f, "rhs", &self.rhs) @@ -1565,14 +1508,14 @@ impl Display for BinaryOpExpr { #[derive(Clone, Debug)] pub struct FunctionCall { pub span: Span, - pub name: Ident, + pub symbol_id: SymbolId, pub args: List, } impl Display for FunctionCall { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { writeln_header(f, "FunctionCall", self.span)?; - writeln_field(f, "name", &self.name)?; + writeln_field(f, "symbol_id", &self.symbol_id)?; write_list_field(f, "args", &self.args) } } diff --git a/compiler/qsc_qasm3/src/semantic/error.rs b/compiler/qsc_qasm3/src/semantic/error.rs index 01b800c9c9..75ee54ff56 100644 --- a/compiler/qsc_qasm3/src/semantic/error.rs +++ b/compiler/qsc_qasm3/src/semantic/error.rs @@ -50,6 +50,9 @@ pub enum SemanticErrorKind { #[error("Cannot assign a value of {0} type to a classical variable of {1} type.")] #[diagnostic(code("Qsc.Qasm3.Compile.CannotAssignToType"))] CannotAssignToType(String, String, #[label] Span), + #[error("Cannot call an expression that is not a function.")] + #[diagnostic(code("Qsc.Qasm3.Compile.CannotCallNonFunction"))] + CannotCallNonFunction(#[label] Span), #[error("Cannot call a gate that is not a gate.")] #[diagnostic(code("Qsc.Qasm3.Compile.CannotCallNonGate"))] CannotCallNonGate(#[label] Span), @@ -84,6 +87,9 @@ pub enum SemanticErrorKind { #[error("Array size must be a non-negative integer const expression.")] #[diagnostic(code("Qsc.Qasm3.Compile.ArraySizeMustBeNonNegativeConstExpr"))] ArraySizeMustBeNonNegativeConstExpr(#[label] Span), + #[error("Def declarations must be done in global scope.")] + #[diagnostic(code("Qsc.Qasm3.Compile.DefDeclarationInNonGlobalScope"))] + DefDeclarationInNonGlobalScope(#[label] Span), #[error("Designator is too large.")] #[diagnostic(code("Qsc.Qasm3.Compile.DesignatorTooLarge"))] DesignatorTooLarge(#[label] Span), @@ -258,6 +264,7 @@ impl SemanticErrorKind { Self::CannotAssignToType(lhs, rhs, span) => { Self::CannotAssignToType(lhs, rhs, span + offset) } + Self::CannotCallNonFunction(span) => Self::CannotCallNonFunction(span + offset), Self::CannotCallNonGate(span) => Self::CannotCallNonGate(span + offset), Self::CannotIndexType(name, span) => Self::CannotIndexType(name, span + offset), Self::CannotUpdateConstVariable(name, span) => { @@ -273,6 +280,9 @@ impl SemanticErrorKind { Self::ArraySizeMustBeNonNegativeConstExpr(span) => { Self::ArraySizeMustBeNonNegativeConstExpr(span + offset) } + Self::DefDeclarationInNonGlobalScope(span) => { + Self::DefDeclarationInNonGlobalScope(span + offset) + } Self::DesignatorTooLarge(span) => Self::DesignatorTooLarge(span + offset), Self::FailedToCompileExpressionList(span) => { Self::FailedToCompileExpressionList(span + offset) diff --git a/compiler/qsc_qasm3/src/semantic/lowerer.rs b/compiler/qsc_qasm3/src/semantic/lowerer.rs index 7ce669bee2..afd39c6f5e 100644 --- a/compiler/qsc_qasm3/src/semantic/lowerer.rs +++ b/compiler/qsc_qasm3/src/semantic/lowerer.rs @@ -201,8 +201,8 @@ impl Lowerer { syntax::StmtKind::ExternDecl(extern_decl) => self.lower_extern(extern_decl), syntax::StmtKind::For(stmt) => self.lower_for_stmt(stmt), syntax::StmtKind::If(stmt) => self.lower_if_stmt(stmt), - syntax::StmtKind::GateCall(stmt) => self.lower_gate_call(stmt), - syntax::StmtKind::GPhase(stmt) => self.lower_gphase(stmt), + syntax::StmtKind::GateCall(stmt) => self.lower_gate_call_stmt(stmt), + syntax::StmtKind::GPhase(stmt) => self.lower_gphase_stmt(stmt), syntax::StmtKind::Include(stmt) => self.lower_include(stmt), syntax::StmtKind::IODeclaration(stmt) => self.lower_io_decl(stmt), syntax::StmtKind::Measure(stmt) => self.lower_measure(stmt), @@ -269,15 +269,15 @@ impl Lowerer { &mut self, name: S, symbol: Symbol, - span: Span, ) -> super::symbols::SymbolId where S: AsRef, { + let symbol_span = symbol.span; let symbol_id = match self.symbols.try_insert_or_get_existing(symbol) { Ok(symbol_id) => symbol_id, Err(symbol_id) => { - self.push_redefined_symbol_error(name.as_ref(), span); + self.push_redefined_symbol_error(name.as_ref(), symbol_span); symbol_id } }; @@ -325,7 +325,7 @@ impl Lowerer { IOKind::Default, ); - let symbol_id = self.try_insert_or_get_existing_symbol_id(name, symbol, alias.ident.span()); + let symbol_id = self.try_insert_or_get_existing_symbol_id(name, symbol); if rhs.iter().any(|expr| expr.ty != first.ty) { let tys = rhs @@ -382,7 +382,7 @@ impl Lowerer { semantic::StmtKind::Assign(semantic::AssignStmt { symbol_id, - name_span: ident.span, + lhs_span: ident.span, rhs, span, }) @@ -398,16 +398,15 @@ impl Lowerer { let (symbol_id, symbol) = self.try_get_existing_or_insert_err_symbol(&ident.name, ident.span); - let indexed_ty = &symbol - .ty - .get_indexed_type() - .expect("we only get here if there is at least one index"); + let indexed_ty = + &self.get_indexed_type(&symbol.ty, index_expr.name.span, index_expr.indices.len()); - let indices = index_expr - .indices - .iter() - .map(|index| self.lower_index_element(index)); - let indices = list_from_iter(indices); + let indices = list_from_iter( + index_expr + .indices + .iter() + .map(|index| self.lower_index_element(index)), + ); let rhs = match rhs { syntax::ValueExpr::Expr(expr) => { @@ -443,7 +442,16 @@ impl Lowerer { let (symbol_id, symbol) = self.try_get_existing_or_insert_err_symbol(&ident.name, ident.span); - let ty = &symbol.ty; + let ty = if lhs.indices.len() == 0 { + &symbol.ty + } else { + &self.get_indexed_type(&symbol.ty, lhs.name.span, lhs.indices.len()) + }; + let indices = list_from_iter( + lhs.indices + .iter() + .map(|index| self.lower_index_element(index)), + ); if ty.is_const() { let kind = @@ -466,6 +474,7 @@ impl Lowerer { semantic::StmtKind::AssignOp(semantic::AssignOpStmt { span: stmt.span, symbol_id, + indices, op, lhs, rhs, @@ -484,10 +493,7 @@ impl Lowerer { err_expr!(Type::Err, expr.span) } syntax::ExprKind::Err => err_expr!(Type::Err, expr.span), - syntax::ExprKind::FunctionCall(_) => { - self.push_unimplemented_error_message("function call expr", expr.span); - err_expr!(Type::Err, expr.span) - } + syntax::ExprKind::FunctionCall(expr) => self.lower_function_call_expr(expr), syntax::ExprKind::Ident(ident) => self.lower_ident_expr(ident), syntax::ExprKind::IndexExpr(expr) => self.lower_index_expr(expr), @@ -886,8 +892,7 @@ impl Lowerer { None => self.cast_expr_with_target_type_or_default(None, &ty, stmt_span), }; - let symbol_id = - self.try_insert_or_get_existing_symbol_id(name, symbol, stmt.identifier.span); + let symbol_id = self.try_insert_or_get_existing_symbol_id(name, symbol); semantic::StmtKind::ClassicalDecl(semantic::ClassicalDeclarationStmt { span: stmt_span, @@ -923,8 +928,7 @@ impl Lowerer { symbol = symbol.with_const_expr(Rc::new(init_expr.clone())); } - let symbol_id = - self.try_insert_or_get_existing_symbol_id(name, symbol, stmt.identifier.span); + let symbol_id = self.try_insert_or_get_existing_symbol_id(name, symbol); if !init_expr.ty.is_const() { self.push_semantic_error(SemanticErrorKind::ExprMustBeConst( @@ -947,8 +951,143 @@ impl Lowerer { } fn lower_def(&mut self, stmt: &syntax::DefStmt) -> semantic::StmtKind { - self.push_unimplemented_error_message("def stmt", stmt.span); - semantic::StmtKind::Err + // 1. Check that we are in the global scope. QASM3 semantics + // only allow def declarations in the global scope. + if !self.symbols.is_current_scope_global() { + let kind = SemanticErrorKind::DefDeclarationInNonGlobalScope(stmt.span); + self.push_semantic_error(kind); + } + + let (return_ty, qsharp_return_ty) = if let Some(ty) = &stmt.return_type { + let ty_span = ty.span; + let tydef = syntax::TypeDef::Scalar(*ty.clone()); + let ty = self.get_semantic_type_from_tydef(&tydef, false); + let qsharp_ty = self.convert_semantic_type_to_qsharp_type(&ty, ty_span); + (Some(ty), Some(qsharp_ty)) + } else { + (None, None) + }; + + // 2. Push the function symbol to the symbol table. + #[allow(clippy::cast_possible_truncation)] + let arity = stmt.params.len() as u32; + let name = stmt.name.name.clone(); + let name_span = stmt.name.span; + let ty = crate::semantic::types::Type::Function(arity, return_ty.map(Box::new)); + + let has_qubit_params = stmt + .params + .iter() + .any(|arg| matches!(&**arg, syntax::TypedParameter::Quantum(..))); + + let kind = if has_qubit_params { + crate::types::CallableKind::Operation + } else { + crate::types::CallableKind::Function + }; + + let qsharp_ty = crate::types::Type::Callable(kind, arity, 0); + + let symbol = Symbol::new(&name, name_span, ty, qsharp_ty, IOKind::Default); + let symbol_id = self.try_insert_or_get_existing_symbol_id(name, symbol); + + // Push the scope where the def lives. + self.symbols.push_scope(ScopeKind::Function); + + let params = stmt + .params + .iter() + .map(|param| { + let symbol = self.lower_typed_parameter(param); + let name = symbol.name.clone(); + self.try_insert_or_get_existing_symbol_id(name, symbol) + }) + .collect(); + + let body = semantic::Block { + span: stmt.span, + stmts: list_from_iter(stmt.body.stmts.iter().map(|stmt| self.lower_stmt(stmt))), + }; + + // Pop the scope where the def lives. + self.symbols.pop_scope(); + + semantic::StmtKind::Def(semantic::DefStmt { + span: stmt.span, + symbol_id, + has_qubit_params, + params, + body, + return_type: qsharp_return_ty, + }) + } + + fn lower_typed_parameter(&mut self, typed_param: &syntax::TypedParameter) -> Symbol { + match typed_param { + syntax::TypedParameter::ArrayReference(param) => { + self.lower_array_reference_parameter(param) + } + syntax::TypedParameter::Quantum(param) => self.lower_quantum_parameter(param), + syntax::TypedParameter::Scalar(param) => self.lower_scalar_parameter(param), + } + } + + fn lower_array_reference_parameter( + &mut self, + typed_param: &syntax::ArrayTypedParameter, + ) -> Symbol { + let tydef = syntax::TypeDef::ArrayReference(*typed_param.ty.clone()); + let ty = self.get_semantic_type_from_tydef(&tydef, false); + let qsharp_ty = self.convert_semantic_type_to_qsharp_type(&ty, typed_param.ty.span); + + Symbol::new( + &typed_param.ident.name, + typed_param.ident.span, + ty, + qsharp_ty, + IOKind::Default, + ) + } + + fn lower_quantum_parameter(&mut self, typed_param: &syntax::QuantumTypedParameter) -> Symbol { + let (ty, qsharp_ty) = if let Some(size) = &typed_param.size { + if let Some(size) = self.const_eval_array_size_designator_from_expr(size) { + let ty = crate::semantic::types::Type::QubitArray(ArrayDimensions::One(size)); + let qsharp_ty = crate::types::Type::QubitArray(crate::types::ArrayDimensions::One( + size as usize, + )); + (ty, qsharp_ty) + } else { + (crate::semantic::types::Type::Err, crate::types::Type::Err) + } + } else { + ( + crate::semantic::types::Type::Qubit, + crate::types::Type::Qubit, + ) + }; + + Symbol::new( + &typed_param.ident.name, + typed_param.ident.span, + ty, + qsharp_ty, + IOKind::Default, + ) + } + + fn lower_scalar_parameter(&mut self, typed_param: &syntax::ScalarTypedParameter) -> Symbol { + let tydef = syntax::TypeDef::Scalar(*typed_param.ty.clone()); + let ty = self.get_semantic_type_from_tydef(&tydef, false); + let qsharp_ty = self.convert_semantic_type_to_qsharp_type(&ty, typed_param.ty.span); + + Symbol::new( + &typed_param.ident.name, + typed_param.ident.span, + ty, + qsharp_ty, + IOKind::Default, + ) } fn lower_def_cal(&mut self, stmt: &syntax::DefCalStmt) -> semantic::StmtKind { @@ -1008,7 +1147,7 @@ impl Lowerer { // symbol error. let body = self.lower_stmt(&stmt.body); - // pop the scope where the loop variable lives + // Pop the scope where the loop variable lives. self.symbols.pop_scope(); semantic::StmtKind::For(semantic::ForStmt { @@ -1037,7 +1176,51 @@ impl Lowerer { }) } - fn lower_gate_call(&mut self, stmt: &syntax::GateCall) -> semantic::StmtKind { + fn lower_function_call_expr(&mut self, expr: &syntax::FunctionCall) -> semantic::Expr { + // 1. Lower the args. + let args = expr.args.iter().map(|arg| self.lower_expr(arg)); + let args = list_from_iter(args); + + // 2. Check that the function name actually refers to a function + // in the symbol table and get its symbol_id & symbol. + let name = expr.name.name.clone(); + let name_span = expr.name.span; + let (symbol_id, symbol) = self.try_get_existing_or_insert_err_symbol(name, name_span); + + let return_ty = if let Type::Function(arity, return_ty) = &symbol.ty { + // 2. Check that function classical arity matches the number of classical args. + if *arity as usize != args.len() { + self.push_semantic_error(SemanticErrorKind::InvalidNumberOfClassicalArgs( + *arity as usize, + args.len(), + expr.span, + )); + } + + if let Some(ty) = return_ty { + *ty.clone() + } else { + crate::semantic::types::Type::Err + } + } else { + self.push_semantic_error(SemanticErrorKind::CannotCallNonFunction(symbol.span)); + crate::semantic::types::Type::Err + }; + + let kind = Box::new(semantic::ExprKind::FunctionCall(semantic::FunctionCall { + span: expr.span, + symbol_id, + args, + })); + + semantic::Expr { + span: expr.span, + kind, + ty: return_ty, + } + } + + fn lower_gate_call_stmt(&mut self, stmt: &syntax::GateCall) -> semantic::StmtKind { // 1. Lower all the fields: // 1.1. Lower the modifiers. let mut modifiers = stmt @@ -1135,8 +1318,8 @@ impl Lowerer { args, qubits, duration, + classical_arity, quantum_arity, - quantum_arity_with_modifiers, }) // The compiler will be left to do all things that need explicit Q# knowledge. @@ -1146,6 +1329,23 @@ impl Lowerer { // by all the semantic analysis. } + /// This is just syntax sugar around a gate call. + fn lower_gphase_stmt(&mut self, stmt: &syntax::GPhase) -> semantic::StmtKind { + let name = syntax::Ident { + span: stmt.gphase_token_span, + name: "gphase".into(), + }; + let gate_call_stmt = syntax::GateCall { + span: stmt.span, + modifiers: stmt.modifiers.clone(), + name, + args: stmt.args.clone(), + qubits: stmt.qubits.clone(), + duration: stmt.duration.clone(), + }; + self.lower_gate_call_stmt(&gate_call_stmt) + } + fn lower_modifier( &mut self, modifier: &syntax::QuantumGateModifier, @@ -1206,11 +1406,6 @@ impl Lowerer { Some(n) } - fn lower_gphase(&mut self, stmt: &syntax::GPhase) -> semantic::StmtKind { - self.push_unimplemented_error_message("gphase stmt", stmt.span); - semantic::StmtKind::Err - } - /// This function is always a indication of a error. Either the /// program is declaring include in a non-global scope or the /// include is not handled in `self.lower_source` properly. @@ -1243,7 +1438,7 @@ impl Lowerer { let qsharp_ty = self.convert_semantic_type_to_qsharp_type(&ty, ty_span); let symbol = Symbol::new(&name, stmt.ident.span, ty.clone(), qsharp_ty, io_kind); - let symbol_id = self.try_insert_or_get_existing_symbol_id(name, symbol, stmt.ident.span); + let symbol_id = self.try_insert_or_get_existing_symbol_id(name, symbol); if io_kind == IOKind::Input { return semantic::StmtKind::InputDeclaration(semantic::InputDeclaration { @@ -1287,8 +1482,69 @@ impl Lowerer { } fn lower_gate_def(&mut self, stmt: &syntax::QuantumGateDefinition) -> semantic::StmtKind { - self.push_unimplemented_error_message("gate def stmt", stmt.span); - semantic::StmtKind::Err + // 1. Check that we are in the global scope. QASM3 semantics + // only allow gate definitions in the global scope. + if !self.symbols.is_current_scope_global() { + let kind = SemanticErrorKind::QuantumDeclarationInNonGlobalScope(stmt.span); + self.push_semantic_error(kind); + } + + // 2. Push the gate symbol to the symbol table. + #[allow(clippy::cast_possible_truncation)] + let classical_arity = stmt.params.len() as u32; + #[allow(clippy::cast_possible_truncation)] + let quantum_arity = stmt.qubits.len() as u32; + let name = stmt.ident.name.clone(); + let ty = crate::semantic::types::Type::Gate(classical_arity, quantum_arity); + let qsharp_ty = crate::types::Type::Callable( + crate::types::CallableKind::Operation, + classical_arity, + quantum_arity, + ); + let symbol = Symbol::new(&name, stmt.ident.span, ty, qsharp_ty, IOKind::Default); + let symbol_id = self.try_insert_or_get_existing_symbol_id(name, symbol); + + // Push the scope where the gate definition lives. + self.symbols.push_scope(ScopeKind::Gate); + + let params = stmt + .params + .iter() + .map(|arg| { + let ty = crate::semantic::types::Type::Angle(None, false); + let qsharp_ty = self.convert_semantic_type_to_qsharp_type(&ty, Span::default()); + let symbol = Symbol::new(&arg.name, arg.span, ty, qsharp_ty, IOKind::Default); + self.try_insert_or_get_existing_symbol_id(&arg.name, symbol) + }) + .collect(); + + let qubits = stmt + .qubits + .iter() + .map(|arg| { + let ty = crate::semantic::types::Type::Qubit; + let qsharp_ty = self.convert_semantic_type_to_qsharp_type(&ty, Span::default()); + let symbol = + Symbol::new(&arg.name, stmt.ident.span, ty, qsharp_ty, IOKind::Default); + self.try_insert_or_get_existing_symbol_id(&arg.name, symbol) + }) + .collect(); + + let body = semantic::Block { + span: stmt.span, + stmts: list_from_iter(stmt.body.stmts.iter().map(|stmt| self.lower_stmt(stmt))), + }; + + // Pop the scope where the gate definition lives. + self.symbols.pop_scope(); + + semantic::StmtKind::QuantumGateDefinition(semantic::QuantumGateDefinition { + span: stmt.span, + symbol_id, + params, + qubits, + body, + }) } fn lower_quantum_decl(&mut self, stmt: &syntax::QubitDeclaration) -> semantic::StmtKind { @@ -1331,7 +1587,7 @@ impl Lowerer { IOKind::Default, ); - let symbol_id = self.try_insert_or_get_existing_symbol_id(name, symbol, stmt.qubit.span); + let symbol_id = self.try_insert_or_get_existing_symbol_id(name, symbol); if let Some((size, size_span)) = size_and_span { semantic::StmtKind::QubitArrayDecl(semantic::QubitArrayDeclaration { @@ -1358,8 +1614,19 @@ impl Lowerer { } fn lower_return(&mut self, stmt: &syntax::ReturnStmt) -> semantic::StmtKind { - self.push_unimplemented_error_message("return stmt", stmt.span); - semantic::StmtKind::Err + let expr = stmt + .expr + .as_ref() + .map(|expr| match &**expr { + syntax::ValueExpr::Expr(expr) => self.lower_expr(expr), + syntax::ValueExpr::Measurement(expr) => self.lower_measure_expr(expr), + }) + .map(Box::new); + + semantic::StmtKind::Return(semantic::ReturnStmt { + span: stmt.span, + expr, + }) } fn lower_switch(&mut self, stmt: &syntax::SwitchStmt) -> semantic::StmtKind { @@ -1775,7 +2042,12 @@ impl Lowerer { self.push_unimplemented_error_message("uint array default value", span); None } - Type::Duration(_) | Type::Gate(_, _) | Type::Range | Type::Set | Type::Void => { + Type::Duration(_) + | Type::Gate(_, _) + | Type::Function(..) + | Type::Range + | Type::Set + | Type::Void => { let message = format!("Default values for {ty:?} are unsupported."); self.push_unsupported_error_message(message, span); None @@ -2802,6 +3074,8 @@ fn try_get_qsharp_name_and_implicit_modifiers>( // ch, crx, cry, crz, sdg, and tdg match gate_name.as_ref() { + "cy" => Some(("Y".to_string(), make_modifier(Ctrl(1)))), + "cz" => Some(("Z".to_string(), make_modifier(Ctrl(1)))), "ch" => Some(("H".to_string(), make_modifier(Ctrl(1)))), "crx" => Some(("Rx".to_string(), make_modifier(Ctrl(1)))), "cry" => Some(("Ry".to_string(), make_modifier(Ctrl(1)))), diff --git a/compiler/qsc_qasm3/src/semantic/types.rs b/compiler/qsc_qasm3/src/semantic/types.rs index aa2ed00e38..1728165e69 100644 --- a/compiler/qsc_qasm3/src/semantic/types.rs +++ b/compiler/qsc_qasm3/src/semantic/types.rs @@ -43,6 +43,7 @@ pub enum Type { // realistically the sizes could be u3 Gate(u32, u32), + Function(u32, Option>), Range, Set, Void, @@ -74,6 +75,7 @@ impl Display for Type { Type::IntArray(width, dims) => write!(f, "IntArray({width:?}, {dims:?})"), Type::UIntArray(width, dims) => write!(f, "UIntArray({width:?}, {dims:?})"), Type::Gate(cargs, qargs) => write!(f, "Gate({cargs}, {qargs})"), + Type::Function(cargs, return_ty) => write!(f, "Function({cargs}) -> {return_ty:?}"), Type::Range => write!(f, "Range"), Type::Set => write!(f, "Set"), Type::Void => write!(f, "Void"), diff --git a/compiler/qsc_qasm3/src/tests/declaration.rs b/compiler/qsc_qasm3/src/tests/declaration.rs index 6d742ed4e3..1c189a4482 100644 --- a/compiler/qsc_qasm3/src/tests/declaration.rs +++ b/compiler/qsc_qasm3/src/tests/declaration.rs @@ -5,6 +5,7 @@ mod array; mod bit; mod bool; mod complex; +mod def; mod float; mod gate; mod integer; diff --git a/compiler/qsc_qasm3/src/tests/declaration/def.rs b/compiler/qsc_qasm3/src/tests/declaration/def.rs new file mode 100644 index 0000000000..6fa2418435 --- /dev/null +++ b/compiler/qsc_qasm3/src/tests/declaration/def.rs @@ -0,0 +1,72 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +use crate::tests::compile_qasm_stmt_to_qsharp; +use expect_test::expect; +use miette::Report; + +#[test] +fn no_parameters_no_return() -> miette::Result<(), Vec> { + let source = "def empty() {}"; + + let qsharp = compile_qasm_stmt_to_qsharp(source)?; + expect![[r#" + let empty : () -> Unit = () -> {}; + "#]] + .assert_eq(&qsharp); + Ok(()) +} + +#[test] +fn single_parameter() -> miette::Result<(), Vec> { + let source = r#" + def square(int x) -> int { + return x * x; + } + "#; + + let qsharp = compile_qasm_stmt_to_qsharp(source)?; + expect![[r#" + let square : (Int) -> Int = (x) -> { + return x * x; + }; + "#]] + .assert_eq(&qsharp); + Ok(()) +} + +#[test] +fn qubit_parameter() -> miette::Result<(), Vec> { + let source = r#" + def square(qubit q) -> uint { + return 1; + } + "#; + + let qsharp = compile_qasm_stmt_to_qsharp(source)?; + expect![[r#" + let square : (Qubit) => Int = (q) => { + return 1; + }; + "#]] + .assert_eq(&qsharp); + Ok(()) +} + +#[test] +fn qubit_array_parameter() -> miette::Result<(), Vec> { + let source = r#" + def square(qubit[3] qs) -> uint { + return 1; + } + "#; + + let qsharp = compile_qasm_stmt_to_qsharp(source)?; + expect![[r#" + let square : (Qubit[]) => Int = (qs) => { + return 1; + }; + "#]] + .assert_eq(&qsharp); + Ok(()) +} diff --git a/compiler/qsc_qasm3/src/tests/expression.rs b/compiler/qsc_qasm3/src/tests/expression.rs index 8ee361df0c..b8803d23e9 100644 --- a/compiler/qsc_qasm3/src/tests/expression.rs +++ b/compiler/qsc_qasm3/src/tests/expression.rs @@ -3,6 +3,7 @@ mod binary; mod bits; +mod function_call; mod ident; mod implicit_cast_from_bit; mod implicit_cast_from_bitarray; diff --git a/compiler/qsc_qasm3/src/tests/expression/function_call.rs b/compiler/qsc_qasm3/src/tests/expression/function_call.rs new file mode 100644 index 0000000000..aa74fcc2cf --- /dev/null +++ b/compiler/qsc_qasm3/src/tests/expression/function_call.rs @@ -0,0 +1,229 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +use crate::tests::compile_qasm_to_qsharp; +use expect_test::expect; +use miette::Report; + +#[test] +fn funcall_with_no_arguments_generates_correct_qsharp() -> miette::Result<(), Vec> { + let source = r#" + def empty() {} + empty(); + "#; + + let qsharp = compile_qasm_to_qsharp(source)?; + expect![[r#" + let empty : () -> Unit = () -> {}; + empty(); + "#]] + .assert_eq(&qsharp); + Ok(()) +} + +#[test] +fn funcall_with_one_argument_generates_correct_qsharp() -> miette::Result<(), Vec> { + let source = r#" + def square(int x) -> int { + return x * x; + } + + square(2); + "#; + + let qsharp = compile_qasm_to_qsharp(source)?; + expect![[r#" + let square : (Int) -> Int = (x) -> { + return x * x; + }; + square(2); + "#]] + .assert_eq(&qsharp); + Ok(()) +} + +#[test] +fn funcall_with_two_arguments_generates_correct_qsharp() -> miette::Result<(), Vec> { + let source = r#" + def sum(int x, int y) -> int { + return x + y; + } + + sum(2, 3); + "#; + + let qsharp = compile_qasm_to_qsharp(source)?; + expect![[r#" + let sum : (Int, Int) -> Int = (x, y) -> { + return x + y; + }; + sum(2, 3); + "#]] + .assert_eq(&qsharp); + Ok(()) +} + +#[test] +fn funcall_with_qubit_argument() -> miette::Result<(), Vec> { + let source = r#" + def parity(qubit[2] qs) -> bit { + bit a = measure qs[0]; + bit b = measure qs[1]; + return a ^ b; + } + + bit p = parity(2); + "#; + + let qsharp = compile_qasm_to_qsharp(source)?; + expect![[r#" + function __ResultAsInt__(input : Result) : Int { + if Microsoft.Quantum.Convert.ResultAsBool(input) { + 1 + } else { + 0 + } + } + let parity : (Qubit[]) => Result = (qs) => { + mutable a = QIR.Intrinsic.__quantum__qis__m__body(qs[0]); + mutable b = QIR.Intrinsic.__quantum__qis__m__body(qs[1]); + return if __ResultAsInt__(a) ^^^ __ResultAsInt__(b) == 0 { + One + } else { + Zero + }; + }; + mutable p = parity(2); + "#]] + .assert_eq(&qsharp); + Ok(()) +} + +#[test] +fn funcall_with_too_few_arguments_generates_error() { + let source = r#" + def square(int x) -> int { + return x * x; + } + + square(); + "#; + + let Err(errors) = compile_qasm_to_qsharp(source) else { + panic!("Expected error"); + }; + + expect![[r#" + [Qsc.Qasm3.Compile.InvalidNumberOfClassicalArgs + + x Gate expects 1 classical arguments, but 0 were provided. + ,-[Test.qasm:6:9] + 5 | + 6 | square(); + : ^^^^^^^^ + 7 | + `---- + ]"#]] + .assert_eq(&format!("{errors:?}")); +} + +#[test] +fn funcall_with_too_many_arguments_generates_error() { + let source = r#" + def square(int x) -> int { + return x * x; + } + + square(2, 3); + "#; + + let Err(errors) = compile_qasm_to_qsharp(source) else { + panic!("Expected error"); + }; + + expect![[r#" + [Qsc.Qasm3.Compile.InvalidNumberOfClassicalArgs + + x Gate expects 1 classical arguments, but 2 were provided. + ,-[Test.qasm:6:9] + 5 | + 6 | square(2, 3); + : ^^^^^^^^^^^^ + 7 | + `---- + ]"#]] + .assert_eq(&format!("{errors:?}")); +} + +#[test] +fn funcall_accepts_qubit_argument() -> miette::Result<(), Vec> { + let source = r#" + include "stdgates.inc"; + def h_wrapper(qubit q) { + h q; + } + + qubit q; + h_wrapper(q); + "#; + + let qsharp = compile_qasm_to_qsharp(source)?; + expect![[r#" + let h_wrapper : (Qubit) => Unit = (q) => { + H(q); + }; + let q = QIR.Runtime.__quantum__rt__qubit_allocate(); + h_wrapper(q); + "#]] + .assert_eq(&qsharp); + Ok(()) +} + +#[test] +fn classical_decl_initialized_with_funcall() -> miette::Result<(), Vec> { + let source = r#" + def square(int x) -> int { + return x * x; + } + + int a = square(2); + "#; + + let qsharp = compile_qasm_to_qsharp(source)?; + expect![[r#" + let square : (Int) -> Int = (x) -> { + return x * x; + }; + mutable a = square(2); + "#]] + .assert_eq(&qsharp); + Ok(()) +} + +#[test] +fn classical_decl_initialized_with_incompatible_funcall_errors() { + let source = r#" + def square(float x) -> float { + return x * x; + } + + bit a = square(2.0); + "#; + + let Err(errors) = compile_qasm_to_qsharp(source) else { + panic!("Expected error"); + }; + + expect![[r#" + [Qsc.Qasm3.Compile.CannotCast + + x Cannot cast expression of type Float(None, false) to type Bit(false) + ,-[Test.qasm:6:17] + 5 | + 6 | bit a = square(2.0); + : ^^^^^^^^^^^ + 7 | + `---- + ]"#]] + .assert_eq(&format!("{errors:?}")); +} diff --git a/compiler/qsc_qasm3/src/tests/expression/ident.rs b/compiler/qsc_qasm3/src/tests/expression/ident.rs index 86d689015b..f17a9d86f2 100644 --- a/compiler/qsc_qasm3/src/tests/expression/ident.rs +++ b/compiler/qsc_qasm3/src/tests/expression/ident.rs @@ -12,11 +12,33 @@ fn unresolved_idenfiers_raise_symbol_error() { float x = t; "; - let Err(errors) = compile_qasm_stmt_to_qsharp(source) else { - panic!("Expected an error"); + let Err(errors) = compile_qasm_to_qsharp(source) else { + panic!("should have generated an error"); }; - assert_eq!(1, errors.len(), "Expected one error"); - expect![r#"Undefined symbol: t."#].assert_eq(&errors[0].to_string()); + let errs: Vec<_> = errors.iter().map(|e| format!("{e:?}")).collect(); + let errs_string = errs.join("\n"); + expect![[r#" + Qsc.Qasm3.Compile.UndefinedSymbol + + x Undefined symbol: t. + ,-[Test.qasm:2:19] + 1 | + 2 | float x = t; + : ^ + 3 | + `---- + + Qsc.Qasm3.Compile.CannotCast + + x Cannot cast expression of type Err to type Float(None, false) + ,-[Test.qasm:2:19] + 1 | + 2 | float x = t; + : ^ + 3 | + `---- + "#]] + .assert_eq(&errs_string); } // this test verifies QASM behavior that would normally be allowed diff --git a/compiler/qsc_qasm3/src/tests/statement/gate_call.rs b/compiler/qsc_qasm3/src/tests/statement/gate_call.rs index 4bd127bae9..353556c7e6 100644 --- a/compiler/qsc_qasm3/src/tests/statement/gate_call.rs +++ b/compiler/qsc_qasm3/src/tests/statement/gate_call.rs @@ -42,7 +42,18 @@ fn gphase_gate_can_be_called() -> miette::Result<(), Vec> { "#; let qsharp = compile_qasm_to_qsharp(source)?; - expect![r#""#].assert_eq(&qsharp); + expect![[r#" + operation gphase(theta : Double) : Unit is Adj + Ctl { + body ... { + Exp([], theta, []) + } + adjoint auto; + controlled auto; + controlled adjoint auto; + } + gphase(2.); + "#]] + .assert_eq(&qsharp); Ok(()) } @@ -184,3 +195,124 @@ fn barrier_can_be_called_on_two_qubit() -> miette::Result<(), Vec> { .assert_eq(&qsharp); Ok(()) } + +#[test] +fn cx_called_with_one_qubit_generates_error() { + let source = r#" + include "stdgates.inc"; + qubit[2] q; + cx q[0]; + "#; + + let Err(errors) = compile_qasm_to_qsharp(source) else { + panic!("Expected error"); + }; + + expect![[r#" + [Qsc.Qasm3.Compile.InvalidNumberOfQubitArgs + + x Gate expects 2 qubit arguments, but 1 were provided. + ,-[Test.qasm:4:9] + 3 | qubit[2] q; + 4 | cx q[0]; + : ^^^^^^^^ + 5 | + `---- + ]"#]] + .assert_eq(&format!("{errors:?}")); +} + +#[test] +fn cx_called_with_too_many_qubits_generates_error() { + let source = r#" + include "stdgates.inc"; + qubit[3] q; + cx q[0], q[1], q[2]; + "#; + + let Err(errors) = compile_qasm_to_qsharp(source) else { + panic!("Expected error"); + }; + + expect![[r#" + [Qsc.Qasm3.Compile.InvalidNumberOfQubitArgs + + x Gate expects 2 qubit arguments, but 3 were provided. + ,-[Test.qasm:4:9] + 3 | qubit[3] q; + 4 | cx q[0], q[1], q[2]; + : ^^^^^^^^^^^^^^^^^^^^ + 5 | + `---- + ]"#]] + .assert_eq(&format!("{errors:?}")); +} + +#[test] +fn rx_gate_with_no_angles_generates_error() { + let source = r#" + include "stdgates.inc"; + qubit q; + rx q; + "#; + + let Err(errors) = compile_qasm_to_qsharp(source) else { + panic!("Expected error"); + }; + + expect![[r#" + [Qsc.Qasm3.Compile.InvalidNumberOfClassicalArgs + + x Gate expects 1 classical arguments, but 0 were provided. + ,-[Test.qasm:4:9] + 3 | qubit q; + 4 | rx q; + : ^^^^^ + 5 | + `---- + ]"#]] + .assert_eq(&format!("{errors:?}")); +} + +#[test] +fn rx_gate_with_one_angle_can_be_called() -> miette::Result<(), Vec> { + let source = r#" + include "stdgates.inc"; + qubit q; + rx(2.0) q; + "#; + + let qsharp = compile_qasm_to_qsharp(source)?; + expect![[r#" + let q = QIR.Runtime.__quantum__rt__qubit_allocate(); + Rx(2., q); + "#]] + .assert_eq(&qsharp); + Ok(()) +} + +#[test] +fn rx_gate_with_too_many_angles_generates_error() { + let source = r#" + include "stdgates.inc"; + qubit q; + rx(2.0, 3.0) q; + "#; + + let Err(errors) = compile_qasm_to_qsharp(source) else { + panic!("Expected error"); + }; + + expect![[r#" + [Qsc.Qasm3.Compile.InvalidNumberOfClassicalArgs + + x Gate expects 1 classical arguments, but 2 were provided. + ,-[Test.qasm:4:9] + 3 | qubit q; + 4 | rx(2.0, 3.0) q; + : ^^^^^^^^^^^^^^^ + 5 | + `---- + ]"#]] + .assert_eq(&format!("{errors:?}")); +} From e182a8c37bc4eaffd66ab451d639fc587bc78f53 Mon Sep 17 00:00:00 2001 From: orpuente-MS <156957451+orpuente-MS@users.noreply.github.com> Date: Thu, 27 Mar 2025 11:37:37 -0700 Subject: [PATCH 068/108] QASM3 - Add implicit casts to function and gate arguments (#2257) Add implicit casts to function and gate arguments. --- compiler/qsc_qasm3/src/semantic/lowerer.rs | 60 ++++++++++---- compiler/qsc_qasm3/src/semantic/types.rs | 12 +-- .../src/tests/expression/function_call.rs | 83 ++++++++++++++++++- .../src/tests/statement/gate_call.rs | 19 +++++ 4 files changed, 151 insertions(+), 23 deletions(-) diff --git a/compiler/qsc_qasm3/src/semantic/lowerer.rs b/compiler/qsc_qasm3/src/semantic/lowerer.rs index afd39c6f5e..28337a3e01 100644 --- a/compiler/qsc_qasm3/src/semantic/lowerer.rs +++ b/compiler/qsc_qasm3/src/semantic/lowerer.rs @@ -958,6 +958,17 @@ impl Lowerer { self.push_semantic_error(kind); } + // 2. Build the parameter's type. + let mut param_types = Vec::with_capacity(stmt.params.len()); + let mut param_symbols = Vec::with_capacity(stmt.params.len()); + + for param in &stmt.params { + let symbol = self.lower_typed_parameter(param); + param_types.push(symbol.ty.clone()); + param_symbols.push(symbol); + } + + // 3. Build the return type. let (return_ty, qsharp_return_ty) = if let Some(ty) = &stmt.return_type { let ty_span = ty.span; let tydef = syntax::TypeDef::Scalar(*ty.clone()); @@ -973,7 +984,7 @@ impl Lowerer { let arity = stmt.params.len() as u32; let name = stmt.name.name.clone(); let name_span = stmt.name.span; - let ty = crate::semantic::types::Type::Function(arity, return_ty.map(Box::new)); + let ty = crate::semantic::types::Type::Function(param_types.into(), return_ty.map(Rc::new)); let has_qubit_params = stmt .params @@ -1177,36 +1188,51 @@ impl Lowerer { } fn lower_function_call_expr(&mut self, expr: &syntax::FunctionCall) -> semantic::Expr { - // 1. Lower the args. - let args = expr.args.iter().map(|arg| self.lower_expr(arg)); - let args = list_from_iter(args); - - // 2. Check that the function name actually refers to a function + // 1. Check that the function name actually refers to a function // in the symbol table and get its symbol_id & symbol. let name = expr.name.name.clone(); let name_span = expr.name.span; let (symbol_id, symbol) = self.try_get_existing_or_insert_err_symbol(name, name_span); - let return_ty = if let Type::Function(arity, return_ty) = &symbol.ty { + let (params_ty, return_ty) = if let Type::Function(params_ty, return_ty) = &symbol.ty { + let arity = params_ty.len(); + // 2. Check that function classical arity matches the number of classical args. - if *arity as usize != args.len() { + if arity != expr.args.len() { self.push_semantic_error(SemanticErrorKind::InvalidNumberOfClassicalArgs( - *arity as usize, - args.len(), + arity, + expr.args.len(), expr.span, )); } - if let Some(ty) = return_ty { - *ty.clone() + if let Some(return_ty) = return_ty { + (params_ty.clone(), (**return_ty).clone()) } else { - crate::semantic::types::Type::Err + (Rc::default(), crate::semantic::types::Type::Err) } } else { self.push_semantic_error(SemanticErrorKind::CannotCallNonFunction(symbol.span)); - crate::semantic::types::Type::Err + (Rc::default(), crate::semantic::types::Type::Err) }; + // 3. Lower the args. There are three cases. + // 3.1 If there are fewer args than the arity of the function. + + // 3.2 If the number of args and the arity match. + + // 3.3 If there are more args than the arity of the function. + let mut params_ty_iter = params_ty.iter(); + let args = expr.args.iter().map(|arg| { + let arg = self.lower_expr(arg); + if let Some(ty) = params_ty_iter.next() { + self.cast_expr_to_type(ty, &arg) + } else { + arg + } + }); + let args = list_from_iter(args); + let kind = Box::new(semantic::ExprKind::FunctionCall(semantic::FunctionCall { span: expr.span, symbol_id, @@ -1235,7 +1261,10 @@ impl Lowerer { } // 1.3. Lower the args. - let args = stmt.args.iter().map(|arg| self.lower_expr(arg)); + let args = stmt.args.iter().map(|arg| { + let arg = self.lower_expr(arg); + self.cast_expr_to_type(&crate::semantic::types::Type::Angle(None, false), &arg) + }); let args = list_from_iter(args); // 1.4. Lower the qubits. let qubits = stmt.qubits.iter().map(|q| self.lower_gate_operand(q)); @@ -2831,7 +2860,6 @@ impl Lowerer { fn lower_index_expr(&mut self, expr: &syntax::IndexExpr) -> semantic::Expr { let collection = self.lower_expr(&expr.collection); let index = self.lower_index_element(&expr.index); - let indexed_ty = self.get_indexed_type(&collection.ty, expr.span, 1); semantic::Expr { diff --git a/compiler/qsc_qasm3/src/semantic/types.rs b/compiler/qsc_qasm3/src/semantic/types.rs index 1728165e69..2187a37442 100644 --- a/compiler/qsc_qasm3/src/semantic/types.rs +++ b/compiler/qsc_qasm3/src/semantic/types.rs @@ -1,7 +1,7 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. -use std::cmp::max; +use std::{cmp::max, rc::Rc}; use core::fmt; use std::fmt::{Display, Formatter}; @@ -43,7 +43,7 @@ pub enum Type { // realistically the sizes could be u3 Gate(u32, u32), - Function(u32, Option>), + Function(Rc<[Type]>, Option>), Range, Set, Void, @@ -75,7 +75,9 @@ impl Display for Type { Type::IntArray(width, dims) => write!(f, "IntArray({width:?}, {dims:?})"), Type::UIntArray(width, dims) => write!(f, "UIntArray({width:?}, {dims:?})"), Type::Gate(cargs, qargs) => write!(f, "Gate({cargs}, {qargs})"), - Type::Function(cargs, return_ty) => write!(f, "Function({cargs}) -> {return_ty:?}"), + Type::Function(params_ty, return_ty) => { + write!(f, "Function({params_ty:?}) -> {return_ty:?}") + } Type::Range => write!(f, "Range"), Type::Set => write!(f, "Set"), Type::Void => write!(f, "Void"), @@ -719,7 +721,7 @@ fn try_promote_bitarray_to_int(left_type: &Type, right_type: &Type) -> Option Option miette::Result<(), Ve Ok(()) } +#[test] +fn void_function_with_one_argument_generates_correct_qsharp() -> miette::Result<(), Vec> { + let source = r#" + def f(int x) {} + f(2); + "#; + + let qsharp = compile_qasm_to_qsharp(source)?; + expect![[r#" + let f : (Int) -> Unit = (x) -> {}; + f(2); + "#]] + .assert_eq(&qsharp); + Ok(()) +} + #[test] fn funcall_with_one_argument_generates_correct_qsharp() -> miette::Result<(), Vec> { let source = r#" @@ -72,7 +88,8 @@ fn funcall_with_qubit_argument() -> miette::Result<(), Vec> { return a ^ b; } - bit p = parity(2); + qubit[2] qs; + bit p = parity(qs); "#; let qsharp = compile_qasm_to_qsharp(source)?; @@ -93,7 +110,8 @@ fn funcall_with_qubit_argument() -> miette::Result<(), Vec> { Zero }; }; - mutable p = parity(2); + let qs = QIR.Runtime.AllocateQubitArray(2); + mutable p = parity(qs); "#]] .assert_eq(&qsharp); Ok(()) @@ -227,3 +245,64 @@ fn classical_decl_initialized_with_incompatible_funcall_errors() { ]"#]] .assert_eq(&format!("{errors:?}")); } + +#[test] +fn funcall_implicit_arg_cast_uint_to_bitarray() -> miette::Result<(), Vec> { + let source = r#" + def parity(bit[2] arr) -> bit { + return 1; + } + + bit p = parity(2); + "#; + + let qsharp = compile_qasm_to_qsharp(source)?; + expect![[r#" + function __BoolAsResult__(input : Bool) : Result { + Microsoft.Quantum.Convert.BoolAsResult(input) + } + function __IntAsResultArrayBE__(number : Int, bits : Int) : Result[] { + mutable runningValue = number; + mutable result = []; + for _ in 1..bits { + set result += [__BoolAsResult__((runningValue &&& 1) != 0)]; + set runningValue >>>= 1; + } + Microsoft.Quantum.Arrays.Reversed(result) + } + let parity : (Result[]) -> Result = (arr) -> { + return 1; + }; + mutable p = parity(__IntAsResultArrayBE__(2, 2)); + "#]] + .assert_eq(&qsharp); + Ok(()) +} + +#[test] +fn funcall_implicit_arg_cast_uint_to_qubit_errors() { + let source = r#" + def parity(qubit[2] arr) -> bit { + return 1; + } + + bit p = parity(2); + "#; + + let Err(errors) = compile_qasm_to_qsharp(source) else { + panic!("Expected error"); + }; + + expect![[r#" + [Qsc.Qasm3.Compile.CannotCast + + x Cannot cast expression of type Int(None, true) to type QubitArray(One(2)) + ,-[Test.qasm:6:24] + 5 | + 6 | bit p = parity(2); + : ^ + 7 | + `---- + ]"#]] + .assert_eq(&format!("{errors:?}")); +} diff --git a/compiler/qsc_qasm3/src/tests/statement/gate_call.rs b/compiler/qsc_qasm3/src/tests/statement/gate_call.rs index 353556c7e6..b2941fdf86 100644 --- a/compiler/qsc_qasm3/src/tests/statement/gate_call.rs +++ b/compiler/qsc_qasm3/src/tests/statement/gate_call.rs @@ -316,3 +316,22 @@ fn rx_gate_with_too_many_angles_generates_error() { ]"#]] .assert_eq(&format!("{errors:?}")); } + +#[test] +fn implicit_cast_to_angle_works() -> miette::Result<(), Vec> { + let source = r#" + include "stdgates.inc"; + qubit q; + float a = 2.0; + rx(a) q; + "#; + + let qsharp = compile_qasm_to_qsharp(source)?; + expect![[r#" + let q = QIR.Runtime.__quantum__rt__qubit_allocate(); + let a = 2.0; + Rx(2., q); + "#]] + .assert_eq(&qsharp); + Ok(()) +} From cf4e508cab7903497e721af82a789e52b6bb7688 Mon Sep 17 00:00:00 2001 From: orpuente-MS <156957451+orpuente-MS@users.noreply.github.com> Date: Thu, 27 Mar 2025 13:42:13 -0700 Subject: [PATCH 069/108] QASM3 - simple index expr (#2258) --- compiler/qsc_qasm3/src/compiler.rs | 12 +++-- .../qsc_qasm3/src/tests/expression/indexed.rs | 49 ++++++++++++++++++- 2 files changed, 56 insertions(+), 5 deletions(-) diff --git a/compiler/qsc_qasm3/src/compiler.rs b/compiler/qsc_qasm3/src/compiler.rs index d4cd79fa3d..b453d9c568 100644 --- a/compiler/qsc_qasm3/src/compiler.rs +++ b/compiler/qsc_qasm3/src/compiler.rs @@ -1096,9 +1096,15 @@ impl QasmCompiler { cast_expr } - fn compile_index_expr(&mut self, index: &IndexExpr) -> qsast::Expr { - self.push_unimplemented_error_message("index expressions", index.span); - err_expr(index.span) + fn compile_index_expr(&mut self, index_expr: &IndexExpr) -> qsast::Expr { + let expr = self.compile_expr(&index_expr.collection); + let index = self.compile_index_element(&index_expr.index); + + qsast::Expr { + id: qsast::NodeId::default(), + span: index_expr.span, + kind: Box::new(qsast::ExprKind::Index(Box::new(expr), Box::new(index))), + } } fn compile_paren_expr(&mut self, paren: &Expr, span: Span) -> qsast::Expr { diff --git a/compiler/qsc_qasm3/src/tests/expression/indexed.rs b/compiler/qsc_qasm3/src/tests/expression/indexed.rs index 6ca7873af5..c76ee6b0ff 100644 --- a/compiler/qsc_qasm3/src/tests/expression/indexed.rs +++ b/compiler/qsc_qasm3/src/tests/expression/indexed.rs @@ -19,7 +19,7 @@ fn indexed_bit_cannot_be_implicitly_converted_to_float() { }; assert_eq!(1, errors.len(), "Expected one error"); - expect![r#"Cannot cast expression of type Bit(False) to type Float(None, False)"#] + expect![r#"Cannot cast expression of type Bit(false) to type Float(None, false)"#] .assert_eq(&errors[0].to_string()); } @@ -99,7 +99,7 @@ fn bit_indexed_ty_is_same_as_element_ty() -> miette::Result<(), Vec> { #[ignore = "Not yet implemented"] fn bool_indexed_ty_is_same_as_element_ty() -> miette::Result<(), Vec> { let source = " - bool[5] x; + array[bool, 5] x; bool y = x[0]; "; @@ -113,3 +113,48 @@ fn bool_indexed_ty_is_same_as_element_ty() -> miette::Result<(), Vec> { .assert_eq(&qsharp); Ok(()) } + +#[test] +#[ignore = "Not yet implemented"] +fn bitstring_slicing() -> miette::Result<(), Vec> { + let source = r#" + include "stdgates.inc"; + bit[5] ans = "10101"; + qubit qq; + if(ans[0:3] == 4) x qq; + "#; + + let qsharp = compile_qasm_to_qsharp(source)?; + expect![r#""#].assert_eq(&qsharp); + Ok(()) +} + +#[test] +#[ignore = "Not yet implemented"] +fn bitstring_slicing_with_step() -> miette::Result<(), Vec> { + let source = r#" + include "stdgates.inc"; + bit[5] ans = "10101"; + qubit qq; + if(ans[0:3:2] == 4) x qq; + "#; + + let qsharp = compile_qasm_to_qsharp(source)?; + expect![r#""#].assert_eq(&qsharp); + Ok(()) +} + +#[test] +#[ignore = "Not yet implemented"] +fn bitstring_index_set() -> miette::Result<(), Vec> { + let source = r#" + include "stdgates.inc"; + bit[5] ans = "10101"; + qubit qq; + if(ans[{1, 3}] == 4) x qq; + "#; + + let qsharp = compile_qasm_to_qsharp(source)?; + expect![r#""#].assert_eq(&qsharp); + Ok(()) +} From 404dd4d6d62ba34e40dc455e510b41e8091ed4f3 Mon Sep 17 00:00:00 2001 From: orpuente-MS <156957451+orpuente-MS@users.noreply.github.com> Date: Mon, 31 Mar 2025 14:29:31 -0700 Subject: [PATCH 070/108] Add angle support to the new compiler (#2267) This PR adds angle support to the new compiler. --------- Co-authored-by: Ian Davis --- Cargo.lock | 2 + compiler/qsc/src/codegen.rs | 1 + compiler/qsc_codegen/src/qsharp.rs | 16 + compiler/qsc_qasm3/Cargo.toml | 2 + compiler/qsc_qasm3/src/ast_builder.rs | 160 ++++- compiler/qsc_qasm3/src/compile.rs | 33 +- compiler/qsc_qasm3/src/compiler.rs | 342 ++++++--- compiler/qsc_qasm3/src/lib.rs | 3 +- compiler/qsc_qasm3/src/runtime.rs | 93 +-- compiler/qsc_qasm3/src/semantic/ast.rs | 13 +- .../qsc_qasm3/src/semantic/ast/const_eval.rs | 246 +++++-- compiler/qsc_qasm3/src/semantic/error.rs | 6 + compiler/qsc_qasm3/src/semantic/lowerer.rs | 194 +++-- compiler/qsc_qasm3/src/semantic/tests.rs | 48 +- .../qsc_qasm3/src/semantic/tests/decls.rs | 2 +- .../src/semantic/tests/decls/angle.rs | 146 +++- .../expression/implicit_cast_from_angle.rs | 111 ++- .../expression/implicit_cast_from_bit.rs | 66 +- .../expression/implicit_cast_from_float.rs | 6 +- compiler/qsc_qasm3/src/semantic/types.rs | 32 +- compiler/qsc_qasm3/src/stdlib.rs | 7 + .../qsc_qasm3/src/stdlib/QasmStd/qsharp.json | 9 + .../src/stdlib/QasmStd/src/QasmStd/Angle.qs | 273 +++++++ .../src/stdlib/QasmStd/src/QasmStd/Convert.qs | 100 +++ .../stdlib/QasmStd/src/QasmStd/Intrinsic.qs | 150 ++++ compiler/qsc_qasm3/src/{ => stdlib}/angle.rs | 154 +++- .../qsc_qasm3/src/{ => stdlib}/angle/tests.rs | 101 ++- compiler/qsc_qasm3/src/stdlib/compile.rs | 149 ++++ compiler/qsc_qasm3/src/tests.rs | 35 +- compiler/qsc_qasm3/src/tests/declaration.rs | 1 - .../qsc_qasm3/src/tests/declaration/gate.rs | 28 +- .../tests/declaration/io/explicit_input.rs | 144 ++-- .../tests/declaration/io/explicit_output.rs | 246 ++++--- .../tests/declaration/io/implicit_output.rs | 244 ++++--- .../src/tests/declaration/unsigned_integer.rs | 4 +- .../qsc_qasm3/src/tests/expression/binary.rs | 1 + .../src/tests/expression/binary/angle.rs | 270 +++++++ .../binary/arithmetic_conversions.rs | 54 +- .../src/tests/expression/binary/comparison.rs | 69 +- .../src/tests/expression/binary/ident.rs | 27 +- .../binary/literal/multiplication.rs | 10 +- .../qsc_qasm3/src/tests/expression/bits.rs | 42 +- .../src/tests/expression/function_call.rs | 45 +- .../qsc_qasm3/src/tests/expression/ident.rs | 3 + .../expression/implicit_cast_from_bit.rs | 109 +-- .../expression/implicit_cast_from_bitarray.rs | 48 +- .../expression/implicit_cast_from_bool.rs | 139 ++-- .../expression/implicit_cast_from_float.rs | 94 +-- .../expression/implicit_cast_from_int.rs | 99 +-- .../qsc_qasm3/src/tests/expression/indexed.rs | 51 +- .../qsc_qasm3/src/tests/expression/unary.rs | 103 ++- compiler/qsc_qasm3/src/tests/output.rs | 130 ++-- compiler/qsc_qasm3/src/tests/scopes.rs | 11 +- .../src/tests/statement/annotation.rs | 3 + .../src/tests/statement/const_eval.rs | 674 ++++++++++++------ .../qsc_qasm3/src/tests/statement/for_loop.rs | 60 +- .../src/tests/statement/gate_call.rs | 95 ++- .../qsc_qasm3/src/tests/statement/if_stmt.rs | 16 +- .../statement/implicit_modified_gate_call.rs | 206 ++++-- .../qsc_qasm3/src/tests/statement/include.rs | 10 +- .../qsc_qasm3/src/tests/statement/measure.rs | 23 +- .../src/tests/statement/modified_gate_call.rs | 141 ++-- .../qsc_qasm3/src/tests/statement/reset.rs | 11 +- .../qsc_qasm3/src/tests/statement/switch.rs | 103 +-- .../src/tests/statement/while_loop.rs | 8 +- compiler/qsc_qasm3/src/types.rs | 2 + pip/src/interop.rs | 14 +- 67 files changed, 3987 insertions(+), 1851 deletions(-) create mode 100644 compiler/qsc_qasm3/src/stdlib.rs create mode 100644 compiler/qsc_qasm3/src/stdlib/QasmStd/qsharp.json create mode 100644 compiler/qsc_qasm3/src/stdlib/QasmStd/src/QasmStd/Angle.qs create mode 100644 compiler/qsc_qasm3/src/stdlib/QasmStd/src/QasmStd/Convert.qs create mode 100644 compiler/qsc_qasm3/src/stdlib/QasmStd/src/QasmStd/Intrinsic.qs rename compiler/qsc_qasm3/src/{ => stdlib}/angle.rs (57%) rename compiler/qsc_qasm3/src/{ => stdlib}/angle/tests.rs (59%) create mode 100644 compiler/qsc_qasm3/src/stdlib/compile.rs create mode 100644 compiler/qsc_qasm3/src/tests/expression/binary/angle.rs diff --git a/Cargo.lock b/Cargo.lock index e4520e48a1..f3bc3bb375 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1493,7 +1493,9 @@ dependencies = [ "qsc_ast", "qsc_data_structures", "qsc_frontend", + "qsc_hir", "qsc_parse", + "qsc_passes", "qsc_qasm3", "rustc-hash", "thiserror", diff --git a/compiler/qsc/src/codegen.rs b/compiler/qsc/src/codegen.rs index 54ad8388c7..3b284f9e86 100644 --- a/compiler/qsc/src/codegen.rs +++ b/compiler/qsc/src/codegen.rs @@ -5,6 +5,7 @@ mod tests; pub mod qsharp { + pub use qsc_codegen::qsharp::write_item_string; pub use qsc_codegen::qsharp::write_package_string; pub use qsc_codegen::qsharp::write_stmt_string; } diff --git a/compiler/qsc_codegen/src/qsharp.rs b/compiler/qsc_codegen/src/qsharp.rs index 63636c1d61..c845e53227 100644 --- a/compiler/qsc_codegen/src/qsharp.rs +++ b/compiler/qsc_codegen/src/qsharp.rs @@ -61,6 +61,22 @@ pub fn write_package_string(package: &Package) -> String { format_str(&s) } +#[must_use] +pub fn write_item_string(item: &Item) -> String { + let mut output = Vec::new(); + let mut gen = QSharpGen::new(&mut output); + + gen.visit_item(item); + + let s = match std::str::from_utf8(&output) { + Ok(v) => v.to_owned(), + Err(e) => format!("Invalid UTF-8 sequence: {e}"), + }; + + output.clear(); + format_str(&s) +} + #[must_use] pub fn write_stmt_string(stmt: &ast::Stmt) -> String { let mut output = Vec::new(); diff --git a/compiler/qsc_qasm3/Cargo.toml b/compiler/qsc_qasm3/Cargo.toml index 0884792b61..8f95afc821 100644 --- a/compiler/qsc_qasm3/Cargo.toml +++ b/compiler/qsc_qasm3/Cargo.toml @@ -17,7 +17,9 @@ miette = { workspace = true } qsc_ast = { path = "../qsc_ast" } qsc_data_structures = { path = "../qsc_data_structures" } qsc_frontend = { path = "../qsc_frontend" } +qsc_hir = { path = "../qsc_hir" } qsc_parse = { path = "../qsc_parse" } +qsc_passes = { path = "../qsc_passes" } rustc-hash = { workspace = true } thiserror = { workspace = true } oq3_source_file = { workspace = true } diff --git a/compiler/qsc_qasm3/src/ast_builder.rs b/compiler/qsc_qasm3/src/ast_builder.rs index 8cc6f70b74..f69686fe17 100644 --- a/compiler/qsc_qasm3/src/ast_builder.rs +++ b/compiler/qsc_qasm3/src/ast_builder.rs @@ -6,14 +6,17 @@ use std::rc::Rc; use num_bigint::BigInt; use qsc_ast::ast::{ - self, Attr, Block, CallableBody, CallableDecl, CallableKind, Expr, ExprKind, Ident, Item, Lit, - Mutability, NodeId, Pat, PatKind, Path, PathKind, QubitInit, QubitInitKind, QubitSource, Stmt, - StmtKind, TopLevelNode, Ty, TyKind, + self, Attr, Block, CallableBody, CallableDecl, CallableKind, Expr, ExprKind, FieldAssign, + Ident, ImportOrExportDecl, ImportOrExportItem, Item, ItemKind, Lit, Mutability, NodeId, Pat, + PatKind, Path, PathKind, QubitInit, QubitInitKind, QubitSource, Stmt, StmtKind, TopLevelNode, + Ty, TyKind, }; use qsc_data_structures::span::Span; use crate::{ + parser::ast::list_from_iter, runtime::RuntimeFunctions, + stdlib::angle::Angle, types::{ArrayDimensions, Complex}, }; @@ -281,6 +284,55 @@ pub(crate) fn build_lit_int_expr(value: i64, span: Span) -> Expr { } } +fn build_ident(name: &str) -> Ident { + Ident { + name: Rc::from(name), + ..Default::default() + } +} + +pub(crate) fn build_lit_angle_expr(angle: Angle, span: Span) -> Expr { + let alloc_ident = build_ident("__Angle__"); + let path_kind = PathKind::Ok(Box::new(Path { + segments: None, + name: Box::new(alloc_ident), + id: NodeId::default(), + span: Span::default(), + })); + let value_expr = Box::new(Expr { + #[allow(clippy::cast_possible_wrap)] + kind: Box::new(ExprKind::Lit(Box::new(Lit::Int(angle.value as i64)))), + ..Default::default() + }); + let size_expr = Box::new(Expr { + kind: Box::new(ExprKind::Lit(Box::new(Lit::Int(i64::from(angle.size))))), + ..Default::default() + }); + + let fields = list_from_iter([ + FieldAssign { + span, + field: Box::new(build_ident("Value")), + value: value_expr, + ..Default::default() + }, + FieldAssign { + span, + field: Box::new(build_ident("Size")), + value: size_expr, + ..Default::default() + }, + ]); + + let kind = Box::new(ExprKind::Struct(path_kind, None, fields)); + + Expr { + id: NodeId::default(), + span, + kind, + } +} + pub(crate) fn build_lit_complex_expr(value: Complex, span: Span) -> Expr { let real = build_lit_double_expr(value.real, Span::default()); let img = build_lit_double_expr(value.imaginary, Span::default()); @@ -631,6 +683,15 @@ pub(crate) fn build_cast_call_two_params( build_global_call_with_two_params(name, fst, snd, name_span, operand_span) } +pub(crate) fn build_cast_call_by_name( + name: &str, + expr: ast::Expr, + name_span: Span, + operand_span: Span, +) -> ast::Expr { + build_global_call_with_one_param(name, expr, name_span, operand_span) +} + pub(crate) fn build_cast_call( function: RuntimeFunctions, expr: ast::Expr, @@ -926,6 +987,98 @@ pub(crate) fn build_expr_wrapped_block_expr(expr: Expr) -> Block { } } +pub(crate) fn build_qasm_import_decl() -> Vec { + vec![ + build_qasm_import_decl_angle(), + build_qasm_import_decl_convert(), + build_qasm_import_decl_intrinsic(), + ] +} + +pub(crate) fn build_qasm_import_decl_angle() -> Stmt { + let path_kind = Path { + segments: Some(Box::new([build_ident("QasmStd")])), + name: Box::new(build_ident("Angle")), + id: NodeId::default(), + span: Span::default(), + }; + let items = vec![ImportOrExportItem { + span: Span::default(), + path: PathKind::Ok(Box::new(path_kind)), + alias: None, + is_glob: true, + }]; + let decl = ImportOrExportDecl::new(Span::default(), items.into_boxed_slice(), false); + let item = Item { + id: NodeId::default(), + span: Span::default(), + kind: Box::new(ItemKind::ImportOrExport(decl)), + doc: "".into(), + attrs: Box::new([]), + }; + Stmt { + kind: Box::new(StmtKind::Item(Box::new(item))), + span: Span::default(), + id: NodeId::default(), + } +} + +pub(crate) fn build_qasm_import_decl_convert() -> Stmt { + let path_kind = Path { + segments: Some(Box::new([build_ident("QasmStd")])), + name: Box::new(build_ident("Convert")), + id: NodeId::default(), + span: Span::default(), + }; + let items = vec![ImportOrExportItem { + span: Span::default(), + path: PathKind::Ok(Box::new(path_kind)), + alias: None, + is_glob: true, + }]; + let decl = ImportOrExportDecl::new(Span::default(), items.into_boxed_slice(), false); + let item = Item { + id: NodeId::default(), + span: Span::default(), + kind: Box::new(ItemKind::ImportOrExport(decl)), + doc: "".into(), + attrs: Box::new([]), + }; + Stmt { + kind: Box::new(StmtKind::Item(Box::new(item))), + span: Span::default(), + id: NodeId::default(), + } +} + +pub(crate) fn build_qasm_import_decl_intrinsic() -> Stmt { + let path_kind = Path { + segments: Some(Box::new([build_ident("QasmStd")])), + name: Box::new(build_ident("Intrinsic")), + id: NodeId::default(), + span: Span::default(), + }; + let items = vec![ImportOrExportItem { + span: Span::default(), + path: PathKind::Ok(Box::new(path_kind)), + alias: None, + is_glob: true, + }]; + let decl = ImportOrExportDecl::new(Span::default(), items.into_boxed_slice(), false); + let item = Item { + id: NodeId::default(), + span: Span::default(), + kind: Box::new(ItemKind::ImportOrExport(decl)), + doc: "".into(), + attrs: Box::new([]), + }; + Stmt { + kind: Box::new(StmtKind::Item(Box::new(item))), + span: Span::default(), + id: NodeId::default(), + } +} + pub(crate) fn build_classical_decl( name: S, is_const: bool, @@ -1154,6 +1307,7 @@ pub(crate) fn build_unary_op_expr(op: ast::UnOp, expr: ast::Expr, prefix_span: S pub(crate) fn map_qsharp_type_to_ast_ty(output_ty: &crate::types::Type) -> Ty { match output_ty { + crate::types::Type::Angle(_) => build_path_ident_ty("__Angle__"), crate::types::Type::Result(_) => build_path_ident_ty("Result"), crate::types::Type::Qubit => build_path_ident_ty("Qubit"), crate::types::Type::BigInt(_) => build_path_ident_ty("BigInt"), diff --git a/compiler/qsc_qasm3/src/compile.rs b/compiler/qsc_qasm3/src/compile.rs index 35e2762b5e..65781508f1 100644 --- a/compiler/qsc_qasm3/src/compile.rs +++ b/compiler/qsc_qasm3/src/compile.rs @@ -6,20 +6,21 @@ use std::path::PathBuf; use crate::ast_builder::{ self, build_arg_pat, build_array_reverse_expr, build_assignment_statement, build_attr, - build_barrier_call, build_binary_expr, build_cast_call, build_cast_call_two_params, - build_classical_decl, build_complex_binary_expr, build_complex_from_expr, - build_convert_call_expr, build_default_result_array_expr, build_expr_array_expr, - build_gate_call_param_expr, build_if_expr_then_block, build_if_expr_then_block_else_block, - build_if_expr_then_block_else_expr, build_if_expr_then_expr_else_expr, - build_implicit_return_stmt, build_indexed_assignment_statement, build_lambda, - build_lit_bigint_expr, build_lit_bool_expr, build_lit_complex_expr, build_lit_double_expr, - build_lit_int_expr, build_lit_result_array_expr_from_bitstring, build_lit_result_expr, - build_managed_qubit_alloc, build_math_call_no_params, build_measure_call, - build_operation_with_stmts, build_path_ident_expr, build_range_expr, build_reset_call, - build_stmt_semi_from_expr, build_stmt_wrapped_block_expr, build_top_level_ns_with_item, - build_tuple_expr, build_unary_op_expr, build_unmanaged_qubit_alloc, - build_unmanaged_qubit_alloc_array, build_wrapped_block_expr, is_complex_binop_supported, - managed_qubit_alloc_array, map_qsharp_type_to_ast_ty, + build_barrier_call, build_binary_expr, build_cast_call, build_classical_decl, + build_complex_binary_expr, build_complex_from_expr, build_convert_call_expr, + build_default_result_array_expr, build_expr_array_expr, build_gate_call_param_expr, + build_global_call_with_two_params, build_if_expr_then_block, + build_if_expr_then_block_else_block, build_if_expr_then_block_else_expr, + build_if_expr_then_expr_else_expr, build_implicit_return_stmt, + build_indexed_assignment_statement, build_lambda, build_lit_bigint_expr, build_lit_bool_expr, + build_lit_complex_expr, build_lit_double_expr, build_lit_int_expr, + build_lit_result_array_expr_from_bitstring, build_lit_result_expr, build_managed_qubit_alloc, + build_math_call_no_params, build_measure_call, build_operation_with_stmts, + build_path_ident_expr, build_range_expr, build_reset_call, build_stmt_semi_from_expr, + build_stmt_wrapped_block_expr, build_top_level_ns_with_item, build_tuple_expr, + build_unary_op_expr, build_unmanaged_qubit_alloc, build_unmanaged_qubit_alloc_array, + build_wrapped_block_expr, is_complex_binop_supported, managed_qubit_alloc_array, + map_qsharp_type_to_ast_ty, }; use crate::oqasm_helpers::{ @@ -3784,8 +3785,8 @@ impl QasmCompiler { }; let size_expr = build_lit_int_expr(size, Span::default()); - let expr = build_cast_call_two_params( - RuntimeFunctions::IntAsResultArrayBE, + let expr = build_global_call_with_two_params( + "__IntAsResultArrayBE__", rhs.expr.clone(), size_expr, name_span, diff --git a/compiler/qsc_qasm3/src/compiler.rs b/compiler/qsc_qasm3/src/compiler.rs index b453d9c568..d3f36066cc 100644 --- a/compiler/qsc_qasm3/src/compiler.rs +++ b/compiler/qsc_qasm3/src/compiler.rs @@ -1,6 +1,7 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. +use core::f64; use std::{path::Path, rc::Rc}; use num_bigint::BigInt; @@ -11,31 +12,31 @@ use crate::{ ast_builder::{ build_arg_pat, build_array_reverse_expr, build_assignment_statement, build_barrier_call, build_binary_expr, build_call_no_params, build_call_with_param, build_call_with_params, - build_cast_call, build_cast_call_two_params, build_classical_decl, build_complex_from_expr, + build_cast_call_by_name, build_classical_decl, build_complex_from_expr, build_convert_call_expr, build_expr_array_expr, build_for_stmt, build_gate_call_param_expr, - build_gate_call_with_params_and_callee, build_if_expr_then_block, - build_if_expr_then_block_else_block, build_if_expr_then_block_else_expr, - build_if_expr_then_expr_else_expr, build_implicit_return_stmt, - build_indexed_assignment_statement, build_lambda, build_lit_bigint_expr, - build_lit_bool_expr, build_lit_complex_expr, build_lit_double_expr, build_lit_int_expr, - build_lit_result_array_expr_from_bitstring, build_lit_result_expr, - build_managed_qubit_alloc, build_math_call_from_exprs, build_math_call_no_params, - build_measure_call, build_operation_with_stmts, build_path_ident_expr, build_range_expr, - build_reset_call, build_return_expr, build_return_unit, build_stmt_semi_from_expr, + build_gate_call_with_params_and_callee, build_global_call_with_two_params, + build_if_expr_then_block, build_if_expr_then_block_else_block, + build_if_expr_then_block_else_expr, build_if_expr_then_expr_else_expr, + build_implicit_return_stmt, build_indexed_assignment_statement, build_lambda, + build_lit_angle_expr, build_lit_bigint_expr, build_lit_bool_expr, build_lit_complex_expr, + build_lit_double_expr, build_lit_int_expr, build_lit_result_array_expr_from_bitstring, + build_lit_result_expr, build_managed_qubit_alloc, build_math_call_from_exprs, + build_math_call_no_params, build_measure_call, build_operation_with_stmts, + build_path_ident_expr, build_qasm_import_decl, build_range_expr, build_reset_call, + build_return_expr, build_return_unit, build_stmt_semi_from_expr, build_stmt_semi_from_expr_with_span, build_top_level_ns_with_item, build_tuple_expr, build_unary_op_expr, build_unmanaged_qubit_alloc, build_unmanaged_qubit_alloc_array, build_while_stmt, build_wrapped_block_expr, managed_qubit_alloc_array, map_qsharp_type_to_ast_ty, wrap_expr_in_parens, }, parser::ast::{list_from_iter, List}, - runtime::{get_runtime_function_decls, RuntimeFunctions}, semantic::{ ast::{ BinaryOpExpr, Cast, DiscreteSet, Expr, GateOperand, GateOperandKind, IndexElement, IndexExpr, IndexSet, IndexedIdent, LiteralKind, MeasureExpr, TimeUnit, UnaryOpExpr, }, symbols::{IOKind, Symbol, SymbolId, SymbolTable}, - types::{ArrayDimensions, Type}, + types::{promote_types, ArrayDimensions, Type}, SemanticErrorKind, }, CompilerConfig, OperationSignature, OutputSemantics, ProgramType, QasmCompileUnit, @@ -67,7 +68,6 @@ where source_map: res.source_map, config, stmts: vec![], - runtime: RuntimeFunctions::empty(), symbols: res.symbols, errors: res.errors, }; @@ -85,9 +85,6 @@ pub struct QasmCompiler { pub config: CompilerConfig, /// The compiled statments accumulated during compilation. pub stmts: Vec, - /// The runtime functions that need to be included at the end of - /// compilation - pub runtime: RuntimeFunctions, pub symbols: SymbolTable, pub errors: Vec>, } @@ -97,8 +94,8 @@ impl QasmCompiler { /// source file and build the appropriate package based on the /// configuration. pub fn compile(mut self, program: &crate::semantic::ast::Program) -> QasmCompileUnit { + self.append_runtime_import_decls(); self.compile_stmts(&program.statements); - self.prepend_runtime_decls(); let program_ty = self.config.program_ty.clone(); let (package, signature) = match program_ty { ProgramType::File => self.build_file(), @@ -290,16 +287,11 @@ impl QasmCompiler { ) } - /// Prepends the runtime declarations to the beginning of the statements. - /// Any runtime functions that are required by the compiled code are set - /// in the `self.runtime` field during compilation. - /// - /// We could declare these as top level functions when compiling to - /// `ProgramType::File`, but prepending them to the statements is the - /// most flexible approach. - fn prepend_runtime_decls(&mut self) { - let mut runtime = get_runtime_function_decls(self.runtime); - self.stmts.splice(0..0, runtime.drain(..)); + /// Appends the runtime imports to the compiled statements. + fn append_runtime_import_decls(&mut self) { + for stmt in build_qasm_import_decl() { + self.stmts.push(stmt); + } } fn compile_stmts(&mut self, smtms: &[Box]) { @@ -317,7 +309,7 @@ impl QasmCompiler { semast::StmtKind::Assign(stmt) => self.compile_assign_stmt(stmt), semast::StmtKind::IndexedAssign(stmt) => self.compile_indexed_assign_stmt(stmt), semast::StmtKind::AssignOp(stmt) => self.compile_assign_op_stmt(stmt), - semast::StmtKind::Barrier(stmt) => self.compile_barrier_stmt(stmt), + semast::StmtKind::Barrier(stmt) => Self::compile_barrier_stmt(stmt), semast::StmtKind::Box(stmt) => self.compile_box_stmt(stmt), semast::StmtKind::Block(stmt) => self.compile_block_stmt(stmt), semast::StmtKind::CalibrationGrammar(stmt) => { @@ -407,17 +399,77 @@ impl QasmCompiler { } fn compile_assign_op_stmt(&mut self, stmt: &semast::AssignOpStmt) -> Option { + // If the lhs is of type Angle, we call compile_assign_stmt with the rhs = lhs + rhs. + // This will call compile_binary_expr which handles angles correctly. + if matches!(&stmt.lhs.ty, Type::Angle(..)) { + if stmt.indices.is_empty() { + let rhs = semast::Expr { + span: stmt.span, + ty: stmt.lhs.ty.clone(), + kind: Box::new(semast::ExprKind::BinaryOp(semast::BinaryOpExpr { + op: stmt.op, + lhs: stmt.lhs.clone(), + rhs: stmt.rhs.clone(), + })), + }; + + let stmt = semast::AssignStmt { + span: stmt.span, + symbol_id: stmt.symbol_id, + lhs_span: stmt.lhs.span, + rhs, + }; + + return self.compile_assign_stmt(&stmt); + } + + if stmt.indices.len() != 1 { + self.push_unimplemented_error_message( + "multi-dimensional array index expressions", + stmt.span, + ); + return None; + } + + let lhs = semast::Expr { + span: stmt.span, + ty: stmt.lhs.ty.clone(), + kind: Box::new(semast::ExprKind::IndexExpr(semast::IndexExpr { + span: stmt.lhs.span, + collection: stmt.lhs.clone(), + index: *stmt.indices[0].clone(), + })), + }; + + let rhs = semast::Expr { + span: stmt.span, + ty: stmt.lhs.ty.clone(), + kind: Box::new(semast::ExprKind::BinaryOp(semast::BinaryOpExpr { + op: stmt.op, + lhs, + rhs: stmt.rhs.clone(), + })), + }; + + let stmt = semast::IndexedAssignStmt { + span: stmt.span, + symbol_id: stmt.symbol_id, + indices: stmt.indices.clone(), + rhs, + }; + + return self.compile_indexed_assign_stmt(&stmt); + } + let lhs = self.compile_expr(&stmt.lhs); let rhs = self.compile_expr(&stmt.rhs); let qsop = Self::map_bin_op(stmt.op); + let expr = build_binary_expr(true, qsop, lhs, rhs, stmt.span); Some(build_stmt_semi_from_expr(expr)) } - fn compile_barrier_stmt(&mut self, stmt: &semast::BarrierStmt) -> Option { - // we don't support barrier, but we can insert a runtime function - // which will generate a barrier call in QIR - self.runtime.insert(RuntimeFunctions::Barrier); + fn compile_barrier_stmt(stmt: &semast::BarrierStmt) -> Option { Some(build_barrier_call(stmt.span)) } @@ -607,12 +659,6 @@ impl QasmCompiler { fn compile_gate_call_stmt(&mut self, stmt: &semast::GateCall) -> Option { let symbol = self.symbols[stmt.symbol_id].clone(); - if symbol.name == "U" { - self.runtime |= RuntimeFunctions::U; - } - if symbol.name == "gphase" { - self.runtime |= RuntimeFunctions::Gphase; - } let mut qubits: Vec<_> = stmt .qubits .iter() @@ -640,7 +686,6 @@ impl QasmCompiler { } semast::GateModifierKind::Pow(expr) => { let exponent_expr = self.compile_expr(expr); - self.runtime |= RuntimeFunctions::Pow; args = build_tuple_expr(vec![exponent_expr, callee, args]); callee = build_path_ident_expr("__Pow__", modifier.span, stmt.span); } @@ -1017,13 +1062,23 @@ impl QasmCompiler { } } fn compile_neg_expr(&mut self, expr: &Expr, span: Span) -> qsast::Expr { - let expr = self.compile_expr(expr); - build_unary_op_expr(qsast::UnOp::Neg, expr, span) + let compiled_expr = self.compile_expr(expr); + + if matches!(expr.ty, Type::Angle(..)) { + build_call_with_param("__NegAngle__", &[], compiled_expr, span, expr.span, span) + } else { + build_unary_op_expr(qsast::UnOp::Neg, compiled_expr, span) + } } fn compile_bitwise_not_expr(&mut self, expr: &Expr, span: Span) -> qsast::Expr { - let expr = self.compile_expr(expr); - build_unary_op_expr(qsast::UnOp::NotB, expr, span) + let compiled_expr = self.compile_expr(expr); + + if matches!(expr.ty, Type::Angle(..)) { + build_call_with_param("__AngleNotB__", &[], compiled_expr, span, expr.span, span) + } else { + build_unary_op_expr(qsast::UnOp::NotB, compiled_expr, span) + } } fn compile_logical_not_expr(&mut self, expr: &Expr, span: Span) -> qsast::Expr { @@ -1032,15 +1087,84 @@ impl QasmCompiler { } fn compile_binary_op_expr(&mut self, binary: &BinaryOpExpr) -> qsast::Expr { + let op = Self::map_bin_op(binary.op); let lhs = self.compile_expr(&binary.lhs); let rhs = self.compile_expr(&binary.rhs); - let op = Self::map_bin_op(binary.op); + + if matches!(&binary.lhs.ty, Type::Angle(..)) || matches!(&binary.rhs.ty, Type::Angle(..)) { + return self.compile_angle_binary_op(op, lhs, rhs, &binary.lhs.ty, &binary.rhs.ty); + } + let is_assignment = false; build_binary_expr(is_assignment, op, lhs, rhs, binary.span()) } + fn compile_angle_binary_op( + &mut self, + op: qsast::BinOp, + lhs: qsast::Expr, + rhs: qsast::Expr, + lhs_ty: &crate::semantic::types::Type, + rhs_ty: &crate::semantic::types::Type, + ) -> qsast::Expr { + let span = Span { + lo: lhs.span.lo, + hi: rhs.span.hi, + }; + + let mut operands = vec![lhs, rhs]; + + let fn_name: &str = match op { + // Bit shift + qsast::BinOp::Shl => "__AngleShl__", + qsast::BinOp::Shr => "__AngleShr__", + + // Bitwise + qsast::BinOp::AndB => "__AngleAndB__", + qsast::BinOp::OrB => "__AngleOrB__", + qsast::BinOp::XorB => "__AngleXorB__", + + // Comparison + qsast::BinOp::Eq => "__AngleEq__", + qsast::BinOp::Neq => "__AngleNeq__", + qsast::BinOp::Gt => "__AngleGt__", + qsast::BinOp::Gte => "__AngleGte__", + qsast::BinOp::Lt => "__AngleLt__", + qsast::BinOp::Lte => "__AngleLte__", + + // Arithmetic + qsast::BinOp::Add => "__AddAngles__", + qsast::BinOp::Sub => "__SubtractAngles__", + qsast::BinOp::Mul => { + // if we are doing `int * angle` we need to + // reverse the order of the args to __MultiplyAngleByInt__ + if matches!(lhs_ty, Type::Int(..) | Type::UInt(..)) { + operands.reverse(); + } + "__MultiplyAngleByInt__" + } + qsast::BinOp::Div => { + if matches!(lhs_ty, Type::Angle(..)) + && matches!(rhs_ty, Type::Int(..) | Type::UInt(..)) + { + "__DivideAngleByInt__" + } else { + "__DivideAngleByAngle__" + } + } + + _ => { + self.push_unsupported_error_message("angle binary operation", span); + return err_expr(span); + } + }; + + build_call_with_params(fn_name, &[], operands, span, span) + } + fn compile_literal_expr(&mut self, lit: &LiteralKind, span: Span) -> qsast::Expr { match lit { + LiteralKind::Angle(value) => build_lit_angle_expr(*value, span), LiteralKind::Array(value) => self.compile_array_literal(value, span), LiteralKind::Bitstring(big_int, width) => { Self::compile_bitstring_literal(big_int, *width, span) @@ -1062,28 +1186,28 @@ impl QasmCompiler { let expr = self.compile_expr(&cast.expr); let cast_expr = match cast.expr.ty { crate::semantic::types::Type::Bit(_) => { - self.cast_bit_expr_to_ty(expr, &cast.expr.ty, &cast.ty, cast.span) + Self::cast_bit_expr_to_ty(expr, &cast.expr.ty, &cast.ty, cast.span) } crate::semantic::types::Type::Bool(_) => { - self.cast_bool_expr_to_ty(expr, &cast.expr.ty, &cast.ty, cast.span) + Self::cast_bool_expr_to_ty(expr, &cast.expr.ty, &cast.ty, cast.span) } crate::semantic::types::Type::Duration(_) => { self.cast_duration_expr_to_ty(expr, &cast.expr.ty, &cast.ty, cast.span) } crate::semantic::types::Type::Angle(_, _) => { - self.cast_angle_expr_to_ty(&expr, &cast.expr.ty, &cast.ty, cast.span) + Self::cast_angle_expr_to_ty(expr, &cast.expr.ty, &cast.ty, cast.span) } crate::semantic::types::Type::Complex(_, _) => { self.cast_complex_expr_to_ty(expr, &cast.expr.ty, &cast.ty, cast.span) } crate::semantic::types::Type::Float(_, _) => { - self.cast_float_expr_to_ty(expr, &cast.expr.ty, &cast.ty, cast.span) + Self::cast_float_expr_to_ty(expr, &cast.expr.ty, &cast.ty, cast.span) } crate::semantic::types::Type::Int(_, _) | crate::semantic::types::Type::UInt(_, _) => { - self.cast_int_expr_to_ty(expr, &cast.expr.ty, &cast.ty, cast.span) + Self::cast_int_expr_to_ty(expr, &cast.expr.ty, &cast.ty, cast.span) } crate::semantic::types::Type::BitArray(ArrayDimensions::One(size), _) => { - self.cast_bit_array_expr_to_ty(expr, &cast.expr.ty, &cast.ty, size, cast.span) + Self::cast_bit_array_expr_to_ty(expr, &cast.expr.ty, &cast.ty, size, cast.span) } _ => err_expr(cast.span), }; @@ -1272,8 +1396,7 @@ impl QasmCompiler { /// | angle | Yes | No | No | No | - | Yes | No | No | /// +----------------+-------+-----+------+-------+-------+-----+----------+-------+ fn cast_angle_expr_to_ty( - &mut self, - expr: &qsast::Expr, + expr: qsast::Expr, expr_ty: &crate::semantic::types::Type, ty: &crate::semantic::types::Type, span: Span, @@ -1282,26 +1405,29 @@ impl QasmCompiler { // https://openqasm.com/language/types.html#casting-from-angle match ty { Type::Angle(..) => { - let msg = "Cast angle to angle"; - self.push_unimplemented_error_message(msg, expr.span); - err_expr(span) + // we know they are both angles, here we promote the width. + let promoted_ty = promote_types(expr_ty, ty); + if promoted_ty.width().is_some() && promoted_ty.width() != expr_ty.width() { + // we need to convert the angle to a different width + let width = promoted_ty.width().expect("width should be set"); + build_global_call_with_two_params( + "__ConvertAngleToWidthNoTrunc__", + expr, + build_lit_int_expr(width.into(), span), + span, + span, + ) + } else { + expr + } } Type::Bit(..) => { - let msg = "Cast angle to bit"; - self.push_unimplemented_error_message(msg, expr.span); - err_expr(span) + build_call_with_param("__AngleAsResult__", &[], expr, span, span, span) } Type::BitArray(..) => { - let msg = "Cast angle to bit array"; - self.push_unimplemented_error_message(msg, expr.span); - err_expr(span) - } - Type::Bool(..) => { - let msg = "Cast angle to bool"; - self.push_unimplemented_error_message(msg, expr.span); - err_expr(span) + build_call_with_param("__AngleAsResultArray__", &[], expr, span, span, span) } - + Type::Bool(..) => build_call_with_param("__AngleAsBool__", &[], expr, span, span, span), _ => err_expr(span), } } @@ -1314,7 +1440,6 @@ impl QasmCompiler { /// | bit | Yes | Yes | Yes | No | Yes | - | No | No | /// +----------------+-------+-----+------+-------+-------+-----+----------+-------+ fn cast_bit_expr_to_ty( - &mut self, expr: qsast::Expr, expr_ty: &crate::semantic::types::Type, ty: &crate::semantic::types::Type, @@ -1326,14 +1451,11 @@ impl QasmCompiler { let operand_span = expr.span; let name_span = span; match ty { + &Type::Angle(..) => { + build_cast_call_by_name("__ResultAsAngle__", expr, name_span, operand_span) + } &Type::Bool(..) => { - self.runtime |= RuntimeFunctions::ResultAsBool; - build_cast_call( - RuntimeFunctions::ResultAsBool, - expr, - name_span, - operand_span, - ) + build_cast_call_by_name("__ResultAsBool__", expr, name_span, operand_span) } &Type::Float(..) => { // The spec says that this cast isn't supported, but it @@ -1344,22 +1466,21 @@ impl QasmCompiler { &Type::Int(w, _) | &Type::UInt(w, _) => { let function = if let Some(width) = w { if width > 64 { - RuntimeFunctions::ResultAsBigInt + "__ResultAsBigInt__" } else { - RuntimeFunctions::ResultAsInt + "__ResultAsInt__" } } else { - RuntimeFunctions::ResultAsInt + "__ResultAsInt__" }; - self.runtime |= function; - build_cast_call(function, expr, name_span, operand_span) + + build_cast_call_by_name(function, expr, name_span, operand_span) } _ => err_expr(span), } } fn cast_bit_array_expr_to_ty( - &mut self, expr: qsast::Expr, expr_ty: &crate::semantic::types::Type, ty: &crate::semantic::types::Type, @@ -1382,13 +1503,7 @@ impl QasmCompiler { let int_width = ty.width(); if int_width.is_none() || (int_width == Some(size)) { - self.runtime |= RuntimeFunctions::ResultArrayAsIntBE; - build_cast_call( - RuntimeFunctions::ResultArrayAsIntBE, - expr, - name_span, - operand_span, - ) + build_cast_call_by_name("__ResultArrayAsIntBE__", expr, name_span, operand_span) } else { err_expr(span) } @@ -1402,7 +1517,6 @@ impl QasmCompiler { /// | bool | - | Yes | Yes | Yes | No | Yes | No | No | /// +----------------+-------+-----+------+-------+-------+-----+----------+-------+ fn cast_bool_expr_to_ty( - &mut self, expr: qsast::Expr, expr_ty: &crate::semantic::types::Type, ty: &crate::semantic::types::Type, @@ -1413,35 +1527,22 @@ impl QasmCompiler { let operand_span = span; match ty { Type::Bit(..) => { - self.runtime |= RuntimeFunctions::BoolAsResult; - build_cast_call( - RuntimeFunctions::BoolAsResult, - expr, - name_span, - operand_span, - ) + build_cast_call_by_name("__BoolAsResult__", expr, name_span, operand_span) } Type::Float(..) => { - self.runtime |= RuntimeFunctions::BoolAsDouble; - build_cast_call( - RuntimeFunctions::BoolAsDouble, - expr, - name_span, - operand_span, - ) + build_cast_call_by_name("__BoolAsDouble__", expr, name_span, operand_span) } Type::Int(w, _) | Type::UInt(w, _) => { let function = if let Some(width) = w { if *width > 64 { - RuntimeFunctions::BoolAsBigInt + "__BoolAsBigInt__" } else { - RuntimeFunctions::BoolAsInt + "__BoolAsInt__" } } else { - RuntimeFunctions::BoolAsInt + "__BoolAsInt__" }; - self.runtime |= function; - build_cast_call(function, expr, name_span, operand_span) + build_cast_call_by_name(function, expr, name_span, operand_span) } _ => err_expr(span), } @@ -1479,19 +1580,26 @@ impl QasmCompiler { /// /// Additional cast to complex fn cast_float_expr_to_ty( - &mut self, expr: qsast::Expr, expr_ty: &crate::semantic::types::Type, ty: &crate::semantic::types::Type, span: Span, ) -> qsast::Expr { assert!(matches!(expr_ty, Type::Float(..))); + match ty { &Type::Complex(..) => build_complex_from_expr(expr), - &Type::Angle(..) => { - let msg = "Cast float to angle"; - self.push_unimplemented_error_message(msg, expr.span); - err_expr(span) + &Type::Angle(width, _) => { + let expr_span = expr.span; + let width = + build_lit_int_expr(width.unwrap_or(f64::MANTISSA_DIGITS).into(), expr_span); + build_call_with_params( + "__DoubleAsAngle__", + &[], + vec![expr, width], + expr_span, + expr_span, + ) } &Type::Int(w, _) | &Type::UInt(w, _) => { let expr = build_math_call_from_exprs("Truncate", vec![expr], span); @@ -1505,6 +1613,8 @@ impl QasmCompiler { expr } } + // This is a width promotion, but it is a no-op in Q#. + &Type::Float(..) => expr, &Type::Bool(..) => { let span = expr.span; let expr = build_math_call_from_exprs("Truncate", vec![expr], span); @@ -1538,7 +1648,6 @@ impl QasmCompiler { /// `OpenQASM` support this will need to be fleshed out. #[allow(clippy::too_many_lines)] fn cast_int_expr_to_ty( - &mut self, expr: qsast::Expr, expr_ty: &crate::semantic::types::Type, ty: &crate::semantic::types::Type, @@ -1549,15 +1658,14 @@ impl QasmCompiler { let operand_span = span; match ty { Type::BitArray(dims, _) => { - self.runtime |= RuntimeFunctions::IntAsResultArrayBE; let ArrayDimensions::One(size) = dims else { return err_expr(span); }; let size = i64::from(*size); let size_expr = build_lit_int_expr(size, Span::default()); - build_cast_call_two_params( - RuntimeFunctions::IntAsResultArrayBE, + build_global_call_with_two_params( + "__IntAsResultArrayBE__", expr, size_expr, name_span, diff --git a/compiler/qsc_qasm3/src/lib.rs b/compiler/qsc_qasm3/src/lib.rs index 3b44495f43..1ad5ee67d0 100644 --- a/compiler/qsc_qasm3/src/lib.rs +++ b/compiler/qsc_qasm3/src/lib.rs @@ -4,12 +4,13 @@ // while we work through the conversion, allow dead code to avoid warnings #![allow(dead_code)] -mod angle; mod ast_builder; mod compile; mod compiler; +mod stdlib; pub use compile::qasm_to_program; pub use compiler::compile_with_config; +pub use stdlib::package_store_with_qasm; pub mod io; mod keyword; mod lex; diff --git a/compiler/qsc_qasm3/src/runtime.rs b/compiler/qsc_qasm3/src/runtime.rs index 0f1b0a4541..d1bf332285 100644 --- a/compiler/qsc_qasm3/src/runtime.rs +++ b/compiler/qsc_qasm3/src/runtime.rs @@ -17,38 +17,6 @@ use bitflags::bitflags; use qsc_ast::ast::{Stmt, TopLevelNode}; use qsc_data_structures::language_features::LanguageFeatures; -/// Implement the `gphase` operation for QASM3. -const GPHASE_GATE: &str = " -operation gphase(theta : Double) : Unit is Adj + Ctl { - body ... { - Exp([], theta, []) - } - adjoint auto; - controlled auto; - controlled adjoint auto; -} -"; - -/// Implement the `U` operation for QASM3. -/// We need to apply a global phase, but rather than require gphase to be called, -/// we can use the `R` gate since we have a qubit parameter (though it is ignored). -/// `R(PauliI, 4. * PI() - (lambda + phi + theta), qubit);` -/// Since `U` is periodic to `2pi`, we can use the following: -/// `R(PauliI, -(lambda + phi + theta), qubit);` -const U_GATE: &str = " -operation U(theta : Double, phi : Double, lambda : Double, qubit : Qubit) : Unit is Adj + Ctl { - body ... { - Rz(lambda, qubit); - Ry(theta, qubit); - Rz(phi, qubit); - R(PauliI, -lambda - phi - theta, qubit); - } - adjoint auto; - controlled auto; - controlled adjoint auto; -} -"; - /// The POW function is used to implement the `pow` modifier in QASM3 for integers. const POW: &str = " operation __Pow__<'T>(N: Int, op: ('T => Unit is Adj), target : 'T) : Unit is Adj { @@ -170,20 +138,19 @@ pub struct RuntimeFunctions(u16); bitflags! { impl RuntimeFunctions: u16 { - const Pow = 0b1; - const Barrier = 0b10; - const BoolAsResult = 0b100; - const BoolAsInt = 0b1_000; - const BoolAsBigInt = 0b10_000; - const BoolAsDouble = 0b100_000; - const ResultAsBool = 0b1_000_000; - const ResultAsInt = 0b10_000_000; - const ResultAsBigInt = 0b100_000_000; + const Pow = 0b1; + const Barrier = 0b10; + const BoolAsResult = 0b100; + const BoolAsInt = 0b1000; + const BoolAsBigInt = 0b1_0000; + const BoolAsDouble = 0b10_0000; + const ResultAsBool = 0b100_0000; + const ResultAsInt = 0b1000_0000; + const ResultAsBigInt = 0b1_0000_0000; /// IntAsResultArray requires BoolAsResult to be included. - const IntAsResultArrayBE = 0b1_000_000_000 | 0b100; - const ResultArrayAsIntBE = 0b10_000_000_000; - const Gphase = 0b100_000_000_000; - const U = 0b1_000_000_000_000; + const IntAsResultArrayBE = 0b10_0000_0000 | 0b100; + const ResultArrayAsIntBE = 0b100_0000_0000; + const GATES = 0b1000_0000_0000; } } @@ -237,20 +204,12 @@ pub(crate) fn get_result_array_as_int_be_decl() -> Stmt { parse_stmt(RESULT_ARRAY_AS_INT_BE) } -pub(crate) fn get_gphase_decl() -> Stmt { - parse_stmt(GPHASE_GATE) -} - -pub(crate) fn get_u_decl() -> Stmt { - parse_stmt(U_GATE) -} - /// As we are trying to add statements to the AST, we parse the Q# implementations /// of the runtime functions and return the AST nodes. This saves us a lot of time /// in writing the AST nodes manually. fn parse_stmt(name: &str) -> Stmt { let (nodes, errors) = qsc_parse::top_level_nodes(name, LanguageFeatures::default()); - assert!(errors.is_empty(), "Failed to parse POW: {errors:?}"); + assert!(errors.is_empty(), "Failed to parse: {errors:?}"); assert!( nodes.len() == 1, "Expected one top-level node, found {:?}", @@ -258,12 +217,28 @@ fn parse_stmt(name: &str) -> Stmt { ); match nodes.into_iter().next().expect("no top-level nodes found") { TopLevelNode::Namespace(..) => { - panic!("Expected operation, got Namespace") + panic!("Expected stmt, got Namespace") } TopLevelNode::Stmt(stmt) => *stmt, } } +fn parse_stmts(name: &str) -> Vec { + let (nodes, errors) = qsc_parse::top_level_nodes(name, LanguageFeatures::default()); + assert!(errors.is_empty(), "Failed to parse: {errors:?}"); + let mut stmts = vec![]; + for stmt in nodes { + match stmt { + TopLevelNode::Namespace(..) => { + panic!("Expected stmt, got Namespace") + } + TopLevelNode::Stmt(stmt) => stmts.push(*stmt), + } + } + + stmts +} + /// Get the runtime function declarations for the given runtime functions. pub(crate) fn get_runtime_function_decls(runtime: RuntimeFunctions) -> Vec { let mut stmts = vec![]; @@ -311,13 +286,5 @@ pub(crate) fn get_runtime_function_decls(runtime: RuntimeFunctions) -> Vec let stmt = crate::runtime::get_result_array_as_int_be_decl(); stmts.push(stmt); } - if runtime.contains(RuntimeFunctions::Gphase) { - let stmt = crate::runtime::get_gphase_decl(); - stmts.push(stmt); - } - if runtime.contains(RuntimeFunctions::U) { - let stmt = crate::runtime::get_u_decl(); - stmts.push(stmt); - } stmts } diff --git a/compiler/qsc_qasm3/src/semantic/ast.rs b/compiler/qsc_qasm3/src/semantic/ast.rs index af17ba9830..625a7e91e5 100644 --- a/compiler/qsc_qasm3/src/semantic/ast.rs +++ b/compiler/qsc_qasm3/src/semantic/ast.rs @@ -21,6 +21,7 @@ use crate::{ List, }, semantic::symbols::SymbolId, + stdlib::angle::Angle, }; use crate::parser::ast as syntax; @@ -1552,6 +1553,7 @@ impl Display for IndexExpr { #[derive(Clone, Debug)] pub enum LiteralKind { + Angle(Angle), Array(List), Bitstring(BigInt, u32), Bool(bool), @@ -1572,6 +1574,7 @@ impl Display for LiteralKind { let width = *width as usize; write!(f, "Bitstring(\"{:0>width$}\")", value.to_str_radix(2)) } + LiteralKind::Angle(a) => write!(f, "Angle({a})"), LiteralKind::Bit(b) => write!(f, "Bit({:?})", u8::from(*b)), LiteralKind::Bool(b) => write!(f, "Bool({b:?})"), LiteralKind::Complex(real, imag) => write!(f, "Complex({real:?}, {imag:?})"), @@ -1579,7 +1582,6 @@ impl Display for LiteralKind { write!(f, "Duration({value:?}, {unit:?})") } LiteralKind::Float(value) => write!(f, "Float({value:?})"), - LiteralKind::Int(i) => write!(f, "Int({i:?})"), LiteralKind::BigInt(i) => write!(f, "BigInt({i:?})"), LiteralKind::String(s) => write!(f, "String({s:?})"), @@ -1639,6 +1641,15 @@ pub enum IndexElement { IndexSet(IndexSet), } +impl IndexElement { + pub fn span(&self) -> Span { + match self { + IndexElement::DiscreteSet(discrete_set) => discrete_set.span, + IndexElement::IndexSet(index_set) => index_set.span, + } + } +} + impl Display for IndexElement { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { match self { diff --git a/compiler/qsc_qasm3/src/semantic/ast/const_eval.rs b/compiler/qsc_qasm3/src/semantic/ast/const_eval.rs index 15f4ca2344..a899186e55 100644 --- a/compiler/qsc_qasm3/src/semantic/ast/const_eval.rs +++ b/compiler/qsc_qasm3/src/semantic/ast/const_eval.rs @@ -6,15 +6,17 @@ //! and sizes of arrays. Therefore, those are the only const evaluation //! paths that are implemented. -use std::f64; - use super::{ BinOp, BinaryOpExpr, Cast, Expr, ExprKind, FunctionCall, IndexExpr, IndexedIdent, LiteralKind, SymbolId, UnaryOp, UnaryOpExpr, }; -use crate::semantic::{ - symbols::SymbolTable, - types::{ArrayDimensions, Type}, +use crate::stdlib::angle; +use crate::{ + oqasm_helpers::safe_i64_to_f64, + semantic::{ + symbols::SymbolTable, + types::{ArrayDimensions, Type}, + }, }; use num_bigint::BigInt; @@ -74,7 +76,7 @@ macro_rules! rewrap_lit { impl UnaryOpExpr { fn const_eval(&self, symbols: &SymbolTable) -> Option { - use LiteralKind::{Bit, Bitstring, Bool, Float, Int}; + use LiteralKind::{Angle, Bit, Bitstring, Bool, Float, Int}; let operand_ty = &self.expr.ty; let lit = self.expr.const_eval(symbols)?; @@ -82,7 +84,7 @@ impl UnaryOpExpr { UnaryOp::Neg => match operand_ty { Type::Int(..) => rewrap_lit!(lit, Int(val), Int(-val)), Type::Float(..) => rewrap_lit!(lit, Float(val), Float(-val)), - Type::Angle(..) => rewrap_lit!(lit, Float(val), Float(f64::consts::TAU - val)), + Type::Angle(..) => rewrap_lit!(lit, Angle(val), Angle(-val)), _ => None, }, UnaryOp::NotB => match operand_ty { @@ -90,6 +92,7 @@ impl UnaryOpExpr { let mask = (1 << (*size)?) - 1; Int(!val & mask) }), + Type::Angle(..) => rewrap_lit!(lit, Angle(val), Angle(!val)), Type::Bit(..) => rewrap_lit!(lit, Bit(val), Bit(!val)), Type::BitArray(..) => { rewrap_lit!(lit, Bitstring(val, size), { @@ -136,7 +139,7 @@ fn assert_binary_op_ty_invariant(op: BinOp, lhs_ty: &Type, rhs_ty: &Type) { impl BinaryOpExpr { #[allow(clippy::too_many_lines)] fn const_eval(&self, symbols: &SymbolTable) -> Option { - use LiteralKind::{Bit, Bitstring, Bool, Float, Int}; + use LiteralKind::{Angle, Bit, Bitstring, Bool, Float, Int}; assert_binary_op_ty_invariant(self.op, &self.lhs.ty, &self.rhs.ty); let lhs = self.lhs.const_eval(symbols)?; @@ -147,29 +150,70 @@ impl BinaryOpExpr { // Bit Shifts BinOp::Shl => match lhs_ty { Type::UInt(Some(size), _) => rewrap_lit!((lhs, rhs), (Int(lhs), Int(rhs)), { + if rhs < 0 { + return None; + } let mask = (1 << size) - 1; Int((lhs << rhs) & mask) }), - Type::UInt(..) => rewrap_lit!((lhs, rhs), (Int(lhs), Int(rhs)), Int(lhs << rhs)), + Type::UInt(..) => rewrap_lit!((lhs, rhs), (Int(lhs), Int(rhs)), { + if rhs < 0 { + return None; + } + Int(lhs << rhs) + }), + Type::Angle(..) => { + rewrap_lit!((lhs, rhs), (Angle(lhs), Int(rhs)), { + if rhs < 0 { + return None; + } + Angle(lhs << rhs) + }) + } Type::Bit(..) => rewrap_lit!((lhs, rhs), (Bit(lhs), Int(rhs)), { + if rhs < 0 { + return None; + } // The Spec says "The shift operators shift bits off the end." // Therefore if the rhs is > 0 the value becomes zero. Bit(rhs == 0 && lhs) }), Type::BitArray(..) => rewrap_lit!((lhs, rhs), (Bitstring(lhs, size), Int(rhs)), { + if rhs < 0 { + return None; + } let mask = BigInt::from((1 << size) - 1); Bitstring((lhs << rhs) & mask, size) }), _ => None, }, BinOp::Shr => match lhs_ty { - Type::UInt(..) => rewrap_lit!((lhs, rhs), (Int(lhs), Int(rhs)), Int(lhs >> rhs)), + Type::UInt(..) => rewrap_lit!((lhs, rhs), (Int(lhs), Int(rhs)), { + if rhs < 0 { + return None; + } + Int(lhs >> rhs) + }), + Type::Angle(..) => { + rewrap_lit!((lhs, rhs), (Angle(lhs), Int(rhs)), { + if rhs < 0 { + return None; + } + Angle(lhs >> rhs) + }) + } Type::Bit(..) => rewrap_lit!((lhs, rhs), (Bit(lhs), Int(rhs)), { + if rhs < 0 { + return None; + } // The Spec says "The shift operators shift bits off the end." // Therefore if the rhs is > 0 the value becomes zero. Bit(rhs == 0 && lhs) }), Type::BitArray(..) => rewrap_lit!((lhs, rhs), (Bitstring(lhs, size), Int(rhs)), { + if rhs < 0 { + return None; + } Bitstring(lhs >> rhs, size) }), _ => None, @@ -178,6 +222,9 @@ impl BinaryOpExpr { // Bitwise BinOp::AndB => match lhs_ty { Type::UInt(..) => rewrap_lit!((lhs, rhs), (Int(lhs), Int(rhs)), Int(lhs & rhs)), + Type::Angle(..) => { + rewrap_lit!((lhs, rhs), (Angle(lhs), Angle(rhs)), Angle(lhs & rhs)) + } Type::Bit(..) => rewrap_lit!((lhs, rhs), (Bit(lhs), Bit(rhs)), Bit(lhs & rhs)), Type::BitArray(..) => rewrap_lit!( (lhs, rhs), @@ -188,6 +235,9 @@ impl BinaryOpExpr { }, BinOp::OrB => match lhs_ty { Type::UInt(..) => rewrap_lit!((lhs, rhs), (Int(lhs), Int(rhs)), Int(lhs | rhs)), + Type::Angle(..) => { + rewrap_lit!((lhs, rhs), (Angle(lhs), Angle(rhs)), Angle(lhs | rhs)) + } Type::Bit(..) => rewrap_lit!((lhs, rhs), (Bit(lhs), Bit(rhs)), Bit(lhs | rhs)), Type::BitArray(..) => rewrap_lit!( (lhs, rhs), @@ -198,6 +248,9 @@ impl BinaryOpExpr { }, BinOp::XorB => match lhs_ty { Type::UInt(..) => rewrap_lit!((lhs, rhs), (Int(lhs), Int(rhs)), Int(lhs ^ rhs)), + Type::Angle(..) => { + rewrap_lit!((lhs, rhs), (Angle(lhs), Angle(rhs)), Angle(lhs ^ rhs)) + } Type::Bit(..) => rewrap_lit!((lhs, rhs), (Bit(lhs), Bit(rhs)), Bit(lhs ^ rhs)), Type::BitArray(..) => rewrap_lit!( (lhs, rhs), @@ -222,13 +275,16 @@ impl BinaryOpExpr { Type::Int(..) | Type::UInt(..) => { rewrap_lit!((lhs, rhs), (Int(lhs), Int(rhs)), Bool(lhs == rhs)) } - Type::Float(..) | Type::Angle(..) => { + Type::Float(..) => { rewrap_lit!((lhs, rhs), (Float(lhs), Float(rhs)), { // TODO: we need to issue the same lint in Q#. #[allow(clippy::float_cmp)] Bool(lhs == rhs) }) } + Type::Angle(..) => { + rewrap_lit!((lhs, rhs), (Angle(lhs), Angle(rhs)), Bool(lhs == rhs)) + } Type::Bit(..) => rewrap_lit!((lhs, rhs), (Bit(lhs), Bit(rhs)), Bool(lhs == rhs)), Type::BitArray(..) => rewrap_lit!( (lhs, rhs), @@ -241,13 +297,16 @@ impl BinaryOpExpr { Type::Int(..) | Type::UInt(..) => { rewrap_lit!((lhs, rhs), (Int(lhs), Int(rhs)), Bool(lhs != rhs)) } - Type::Float(..) | Type::Angle(..) => { + Type::Float(..) => { rewrap_lit!((lhs, rhs), (Float(lhs), Float(rhs)), { // TODO: we need to issue the same lint in Q#. #[allow(clippy::float_cmp)] Bool(lhs != rhs) }) } + Type::Angle(..) => { + rewrap_lit!((lhs, rhs), (Angle(lhs), Angle(rhs)), Bool(lhs != rhs)) + } Type::Bit(..) => rewrap_lit!((lhs, rhs), (Bit(lhs), Bit(rhs)), Bool(lhs != rhs)), Type::BitArray(..) => rewrap_lit!( (lhs, rhs), @@ -260,9 +319,12 @@ impl BinaryOpExpr { Type::Int(..) | Type::UInt(..) => { rewrap_lit!((lhs, rhs), (Int(lhs), Int(rhs)), Bool(lhs > rhs)) } - Type::Float(..) | Type::Angle(..) => { + Type::Float(..) => { rewrap_lit!((lhs, rhs), (Float(lhs), Float(rhs)), Bool(lhs > rhs)) } + Type::Angle(..) => { + rewrap_lit!((lhs, rhs), (Angle(lhs), Angle(rhs)), Bool(lhs > rhs)) + } // This was originally `lhs > rhs` but clippy suggested this expression. Type::Bit(..) => rewrap_lit!((lhs, rhs), (Bit(lhs), Bit(rhs)), Bool(lhs && !rhs)), Type::BitArray(..) => rewrap_lit!( @@ -276,9 +338,12 @@ impl BinaryOpExpr { Type::Int(..) | Type::UInt(..) => { rewrap_lit!((lhs, rhs), (Int(lhs), Int(rhs)), Bool(lhs >= rhs)) } - Type::Float(..) | Type::Angle(..) => { + Type::Float(..) => { rewrap_lit!((lhs, rhs), (Float(lhs), Float(rhs)), Bool(lhs >= rhs)) } + Type::Angle(..) => { + rewrap_lit!((lhs, rhs), (Angle(lhs), Angle(rhs)), Bool(lhs >= rhs)) + } Type::Bit(..) => rewrap_lit!((lhs, rhs), (Bit(lhs), Bit(rhs)), Bool(lhs >= rhs)), Type::BitArray(..) => rewrap_lit!( (lhs, rhs), @@ -291,9 +356,12 @@ impl BinaryOpExpr { Type::Int(..) | Type::UInt(..) => { rewrap_lit!((lhs, rhs), (Int(lhs), Int(rhs)), Bool(lhs < rhs)) } - Type::Float(..) | Type::Angle(..) => { + Type::Float(..) => { rewrap_lit!((lhs, rhs), (Float(lhs), Float(rhs)), Bool(lhs < rhs)) } + Type::Angle(..) => { + rewrap_lit!((lhs, rhs), (Angle(lhs), Angle(rhs)), Bool(lhs < rhs)) + } // This was originally `lhs < rhs` but clippy suggested this expression. Type::Bit(..) => rewrap_lit!((lhs, rhs), (Bit(lhs), Bit(rhs)), Bool(!lhs & rhs)), Type::BitArray(..) => rewrap_lit!( @@ -307,9 +375,12 @@ impl BinaryOpExpr { Type::Int(..) | Type::UInt(..) => { rewrap_lit!((lhs, rhs), (Int(lhs), Int(rhs)), Bool(lhs <= rhs)) } - Type::Float(..) | Type::Angle(..) => { + Type::Float(..) => { rewrap_lit!((lhs, rhs), (Float(lhs), Float(rhs)), Bool(lhs <= rhs)) } + Type::Angle(..) => { + rewrap_lit!((lhs, rhs), (Angle(lhs), Angle(rhs)), Bool(lhs <= rhs)) + } Type::Bit(..) => rewrap_lit!((lhs, rhs), (Bit(lhs), Bit(rhs)), Bool(lhs <= rhs)), Type::BitArray(..) => rewrap_lit!( (lhs, rhs), @@ -327,13 +398,9 @@ impl BinaryOpExpr { Type::Float(..) => { rewrap_lit!((lhs, rhs), (Float(lhs), Float(rhs)), Float(lhs + rhs)) } - Type::Angle(..) => rewrap_lit!((lhs, rhs), (Float(lhs), Float(rhs)), { - let mut ans = lhs + rhs; - if ans >= f64::consts::TAU { - ans -= f64::consts::TAU; - } - Float(ans) - }), + Type::Angle(..) => { + rewrap_lit!((lhs, rhs), (Angle(lhs), Angle(rhs)), Angle(lhs + rhs)) + } _ => None, }, BinOp::Sub => match lhs_ty { @@ -343,42 +410,37 @@ impl BinaryOpExpr { Type::Float(..) => { rewrap_lit!((lhs, rhs), (Float(lhs), Float(rhs)), Float(lhs - rhs)) } - Type::Angle(..) => rewrap_lit!((lhs, rhs), (Float(lhs), Float(rhs)), { - let mut ans = lhs - rhs; - if ans < 0.0 { - ans += f64::consts::TAU; - } - Float(ans) - }), + Type::Angle(..) => { + rewrap_lit!((lhs, rhs), (Angle(lhs), Angle(rhs)), Angle(lhs - rhs)) + } _ => None, }, BinOp::Mul => match lhs_ty { Type::Int(..) => rewrap_lit!((lhs, rhs), (Int(lhs), Int(rhs)), Int(lhs * rhs)), Type::UInt(..) => match &self.rhs.ty { - Type::UInt(..) => rewrap_lit!((lhs, rhs), (Int(lhs), Int(rhs)), Int(lhs * rhs)), - Type::Angle(..) => rewrap_lit!((lhs, rhs), (Int(lhs), Float(rhs)), { - // allow reason: angles are in [0, 2π) - #[allow(clippy::cast_precision_loss)] - let mut ans = (lhs as f64) * rhs; - while ans >= f64::consts::TAU { - ans -= f64::consts::TAU; + Type::UInt(..) => { + rewrap_lit!((lhs, rhs), (Int(lhs), Int(rhs)), Int(lhs * rhs)) + } + Type::Angle(..) => rewrap_lit!((lhs, rhs), (Int(lhs), Angle(rhs)), { + if lhs < 0 { + return None; } - Float(ans) + #[allow(clippy::cast_sign_loss)] + Angle(rhs * u64::try_from(lhs).ok()?) }), + _ => None, }, Type::Float(..) => { rewrap_lit!((lhs, rhs), (Float(lhs), Float(rhs)), Float(lhs * rhs)) } - Type::Angle(..) => rewrap_lit!((lhs, rhs), (Float(lhs), Int(rhs)), { - // allow reason: angles are in [0, 2π) - #[allow(clippy::cast_precision_loss)] - let mut ans = lhs * (rhs as f64); - while ans >= f64::consts::TAU { - ans -= f64::consts::TAU; - } - Float(ans) - }), + Type::Angle(..) => { + rewrap_lit!( + (lhs, rhs), + (Angle(lhs), Int(rhs)), + Angle(lhs * u64::try_from(rhs).ok()?) + ) + } _ => None, }, BinOp::Div => match lhs_ty { @@ -388,11 +450,23 @@ impl BinaryOpExpr { Type::Float(..) => { rewrap_lit!((lhs, rhs), (Float(lhs), Float(rhs)), Float(lhs / rhs)) } - Type::Angle(..) => rewrap_lit!((lhs, rhs), (Float(lhs), Int(rhs)), { - // allow reason: angles are in [0, 2π) - #[allow(clippy::cast_precision_loss)] - Float(lhs / (rhs as f64)) - }), + Type::Angle(..) => match &self.rhs.ty { + Type::UInt(..) => { + rewrap_lit!( + (lhs, rhs), + (Angle(lhs), Int(rhs)), + Angle(lhs / u64::try_from(rhs).ok()?) + ) + } + Type::Angle(..) => { + rewrap_lit!( + (lhs, rhs), + (Angle(lhs), Angle(rhs)), + Int((lhs / rhs).try_into().ok()?) + ) + } + _ => None, + }, _ => None, }, BinOp::Mod => match lhs_ty { @@ -435,16 +509,16 @@ impl IndexExpr { impl Cast { fn const_eval(&self, symbols: &SymbolTable, ty: &Type) -> Option { let lit = self.expr.const_eval(symbols)?; - let from_ty = &self.expr.ty; + let lit_ty = &self.expr.ty; match ty { - Type::Bool(_) => cast_to_bool(from_ty, lit), - Type::Int(_, _) => cast_to_int(from_ty, lit), - Type::UInt(_, _) => cast_to_uint(from_ty, lit), - Type::Float(_, _) => cast_to_float(from_ty, lit), - Type::Angle(_, _) => cast_to_angle(from_ty, lit), - Type::Bit(_) => cast_to_bit(from_ty, lit), - Type::BitArray(dims, _) => cast_to_bitarray(from_ty, lit, dims), + Type::Bool(_) => cast_to_bool(lit_ty, lit), + Type::Int(_, _) => cast_to_int(lit_ty, lit), + Type::UInt(_, _) => cast_to_uint(lit_ty, lit), + Type::Float(_, _) => cast_to_float(lit_ty, lit), + Type::Angle(_, _) => cast_to_angle(lit, lit_ty, ty), + Type::Bit(_) => cast_to_bit(lit_ty, lit), + Type::BitArray(dims, _) => cast_to_bitarray(lit_ty, lit, dims), _ => None, } } @@ -458,14 +532,15 @@ impl Cast { /// | bool | - | Yes | Yes | Yes | Yes | Yes | /// +---------------+------+-----+------+-------+-------+-----+ fn cast_to_bool(ty: &Type, lit: LiteralKind) -> Option { - use LiteralKind::{Bit, Bitstring, Bool, Float, Int}; + use LiteralKind::{Angle, Bit, Bitstring, Bool, Float, Int}; match ty { Type::Bool(..) => Some(lit), Type::Bit(..) => rewrap_lit!(lit, Bit(val), Bool(val)), Type::BitArray(..) => rewrap_lit!(lit, Bitstring(val, _), Bool(val != BigInt::ZERO)), Type::Int(..) | Type::UInt(..) => rewrap_lit!(lit, Int(val), Bool(val != 0)), - Type::Float(..) | Type::Angle(..) => rewrap_lit!(lit, Float(val), Bool(val != 0.0)), + Type::Float(..) => rewrap_lit!(lit, Float(val), Bool(val != 0.0)), + Type::Angle(..) => rewrap_lit!(lit, Angle(val), Bool(val.into())), _ => None, } } @@ -502,7 +577,7 @@ fn cast_to_int(ty: &Type, lit: LiteralKind) -> Option { /// +---------------+-----------------------------------------+ /// | Allowed casts | Casting from | /// +---------------+------+-----+------+-------+-------+-----+ -/// | Casting from | bool | int | uint | float | angle | bit | +/// | Casting to | bool | int | uint | float | angle | bit | /// +---------------+------+-----+------+-------+-------+-----+ /// | uint | Yes | Yes | - | Yes | No | Yes | /// +---------------+------+-----+------+-------+-------+-----+ @@ -544,7 +619,7 @@ fn cast_to_float(ty: &Type, lit: LiteralKind) -> Option { Type::Int(..) | Type::UInt(..) => rewrap_lit!(lit, Int(val), { // TODO: we need to issue the same lint in Q#. #[allow(clippy::cast_precision_loss)] - Float(val as f64) + Float(safe_i64_to_f64(val)?) }), Type::Float(..) => Some(lit), _ => None, @@ -558,9 +633,35 @@ fn cast_to_float(ty: &Type, lit: LiteralKind) -> Option { /// +---------------+------+-----+------+-------+-------+-----+ /// | angle | No | No | No | Yes | - | Yes | /// +---------------+------+-----+------+-------+-------+-----+ -fn cast_to_angle(ty: &Type, lit: LiteralKind) -> Option { - match ty { - Type::Float(..) | Type::Angle(..) => Some(lit), +fn cast_to_angle(lit: LiteralKind, lit_ty: &Type, target_ty: &Type) -> Option { + use LiteralKind::{Angle, Bit, Bitstring, Float}; + match lit_ty { + Type::Float(size, _) => rewrap_lit!( + lit, + Float(val), + Angle(angle::Angle::from_f64_maybe_sized(val, *size)) + ), + Type::Angle(..) => rewrap_lit!( + lit, + Angle(val), + Angle(val.cast_to_maybe_sized(target_ty.width())) + ), + Type::Bit(..) => rewrap_lit!( + lit, + Bit(val), + Angle(angle::Angle { + value: val.into(), + size: 1 + }) + ), + Type::BitArray(..) => rewrap_lit!( + lit, + Bitstring(val, size), + Angle(angle::Angle { + value: val.try_into().ok()?, + size + }) + ), _ => None, } } @@ -573,12 +674,13 @@ fn cast_to_angle(ty: &Type, lit: LiteralKind) -> Option { /// | bit | Yes | Yes | Yes | No | Yes | - | /// +---------------+------+-----+------+-------+-------+-----+ fn cast_to_bit(ty: &Type, lit: LiteralKind) -> Option { - use LiteralKind::{Bit, Bool, Int}; + use LiteralKind::{Angle, Bit, Bool, Int}; match ty { Type::Bool(..) => rewrap_lit!(lit, Bool(val), Bit(val)), Type::Bit(..) => Some(lit), Type::Int(..) | Type::UInt(..) => rewrap_lit!(lit, Int(val), Bit(val != 0)), + Type::Angle(..) => rewrap_lit!(lit, Angle(val), Bit(val.value != 0)), _ => None, } } @@ -591,7 +693,7 @@ fn cast_to_bit(ty: &Type, lit: LiteralKind) -> Option { /// | bitarray | Yes | Yes | Yes | No | Yes | - | /// +---------------+------+-----+------+-------+-------+-----+ fn cast_to_bitarray(ty: &Type, lit: LiteralKind, dims: &ArrayDimensions) -> Option { - use LiteralKind::{Bit, Bitstring, Bool, Int}; + use LiteralKind::{Angle, Bit, Bitstring, Bool, Int}; let ArrayDimensions::One(size) = dims else { return None; @@ -600,9 +702,13 @@ fn cast_to_bitarray(ty: &Type, lit: LiteralKind, dims: &ArrayDimensions) -> Opti match ty { Type::Bool(..) => rewrap_lit!(lit, Bool(val), Bitstring(BigInt::from(val), size)), + Type::Angle(..) => rewrap_lit!(lit, Angle(val), { + let new_val = val.cast_to_maybe_sized(Some(size)); + Bitstring(new_val.value.into(), size) + }), Type::Bit(..) => rewrap_lit!(lit, Bit(val), Bitstring(BigInt::from(val), size)), Type::BitArray(..) => rewrap_lit!(lit, Bitstring(val, rhs_size), { - if rhs_size < size { + if rhs_size > size { return None; } Bitstring(val, size) diff --git a/compiler/qsc_qasm3/src/semantic/error.rs b/compiler/qsc_qasm3/src/semantic/error.rs index 75ee54ff56..404ae890b3 100644 --- a/compiler/qsc_qasm3/src/semantic/error.rs +++ b/compiler/qsc_qasm3/src/semantic/error.rs @@ -204,6 +204,9 @@ pub enum SemanticErrorKind { #[error("Unary negation is not allowed for instances of {0}.")] #[diagnostic(code("Qsc.Qasm3.Compile.TypeDoesNotSupportedUnaryNegation"))] TypeDoesNotSupportedUnaryNegation(String, #[label] Span), + #[error("{0} max width is {1} but {2} was provided.")] + #[diagnostic(code("Qsc.Qasm3.Compile.TypeMaxWidthExceeded"))] + TypeMaxWidthExceeded(String, usize, usize, #[label] Span), #[error("Types differ by dimensions and are incompatible.")] #[diagnostic(code("Qsc.Qasm3.Compile.TypeRankError"))] TypeRankError(#[label] Span), @@ -371,6 +374,9 @@ impl SemanticErrorKind { Self::TypeDoesNotSupportedUnaryNegation(name, span) => { Self::TypeDoesNotSupportedUnaryNegation(name, span + offset) } + Self::TypeMaxWidthExceeded(name, max_width, provided_width, span) => { + Self::TypeMaxWidthExceeded(name, max_width, provided_width, span + offset) + } Self::TypeRankError(span) => Self::TypeRankError(span + offset), Self::UndefinedSymbol(name, span) => Self::UndefinedSymbol(name, span + offset), Self::UnexpectedParserError(error, span) => { diff --git a/compiler/qsc_qasm3/src/semantic/lowerer.rs b/compiler/qsc_qasm3/src/semantic/lowerer.rs index 28337a3e01..f9d2755877 100644 --- a/compiler/qsc_qasm3/src/semantic/lowerer.rs +++ b/compiler/qsc_qasm3/src/semantic/lowerer.rs @@ -5,10 +5,12 @@ use std::ops::ShlAssign; use std::rc::Rc; use super::symbols::ScopeKind; +use super::types::binop_requires_asymmetric_angle_op; use super::types::binop_requires_int_conversion_for_type; use super::types::binop_requires_symmetric_int_conversion; use super::types::is_complex_binop_supported; use super::types::promote_to_uint_ty; +use super::types::promote_width; use super::types::requires_symmetric_conversion; use super::types::try_promote_with_casting; use super::types::types_equal_except_const; @@ -28,7 +30,7 @@ use crate::parser::QasmSource; use crate::semantic::types::can_cast_literal; use crate::semantic::types::can_cast_literal_with_value_knowledge; use crate::semantic::types::ArrayDimensions; -use crate::types::get_qsharp_gate_name; +use crate::stdlib::angle::Angle; use super::ast as semantic; use crate::parser::ast as syntax; @@ -238,24 +240,33 @@ impl Lowerer { ) } let gates = vec![ - gate_symbol("X", 0, 1), - gate_symbol("Y", 0, 1), - gate_symbol("Z", 0, 1), - gate_symbol("H", 0, 1), - gate_symbol("S", 0, 1), - gate_symbol("T", 0, 1), - gate_symbol("Rx", 1, 1), - gate_symbol("Rxx", 1, 2), - gate_symbol("Ry", 1, 1), - gate_symbol("Ryy", 1, 2), - gate_symbol("Rz", 1, 1), - gate_symbol("Rzz", 1, 2), - gate_symbol("CNOT", 0, 2), - gate_symbol("CY", 0, 2), - gate_symbol("CZ", 0, 2), - gate_symbol("I", 0, 1), - gate_symbol("SWAP", 0, 2), - gate_symbol("CCNOT", 0, 3), + gate_symbol("p", 1, 1), + gate_symbol("x", 0, 1), + gate_symbol("y", 0, 1), + gate_symbol("z", 0, 1), + gate_symbol("h", 0, 1), + gate_symbol("s", 0, 1), + gate_symbol("t", 0, 1), + gate_symbol("sx", 0, 1), + gate_symbol("rx", 1, 1), + gate_symbol("rxx", 1, 2), + gate_symbol("ry", 1, 1), + gate_symbol("ryy", 1, 2), + gate_symbol("rz", 1, 1), + gate_symbol("rzz", 1, 2), + gate_symbol("cx", 0, 2), + gate_symbol("cy", 0, 2), + gate_symbol("cz", 0, 2), + gate_symbol("cp", 0, 2), + gate_symbol("swap", 0, 2), + gate_symbol("ccx", 0, 3), + gate_symbol("cu", 4, 2), + gate_symbol("CX", 0, 2), + gate_symbol("phase", 1, 1), + gate_symbol("id", 0, 1), + gate_symbol("u1", 1, 1), + gate_symbol("u2", 2, 1), + gate_symbol("u3", 3, 1), ]; for gate in gates { let name = gate.name.clone(); @@ -739,7 +750,8 @@ impl Lowerer { crate::types::Type::Int(is_const) } } - Type::Float(_, _) | Type::Angle(_, _) => crate::types::Type::Double(is_const), + Type::Float(_, _) => crate::types::Type::Double(is_const), + Type::Angle(_, _) => crate::types::Type::Angle(is_const), Type::Complex(_, _) => crate::types::Type::Complex(is_const), Type::Bool(_) => crate::types::Type::Bool(is_const), Type::Duration(_) => { @@ -1005,11 +1017,9 @@ impl Lowerer { // Push the scope where the def lives. self.symbols.push_scope(ScopeKind::Function); - let params = stmt - .params - .iter() - .map(|param| { - let symbol = self.lower_typed_parameter(param); + let params = param_symbols + .into_iter() + .map(|symbol| { let name = symbol.name.clone(); self.try_insert_or_get_existing_symbol_id(name, symbol) }) @@ -1287,8 +1297,6 @@ impl Lowerer { // Q: Do we need this during lowering? // A: Yes, we need it to check the gate_call arity. modifiers.push(implicit_modifier); - } else { - name = get_qsharp_gate_name(&name).unwrap_or(&name).to_string(); } // 3. Check that the gate_name actually refers to a gate in the symbol table @@ -1408,7 +1416,7 @@ impl Lowerer { let expr = self.lower_expr(expr); let target_ty = &Type::UInt(None, true); - let Some(expr) = self.try_cast_expr_to_type(target_ty, &expr) else { + let Some(expr) = Self::try_cast_expr_to_type(target_ty, &expr) else { self.push_invalid_cast_error(target_ty, &expr.ty, expr.span); return None; }; @@ -1581,7 +1589,7 @@ impl Lowerer { let (ty, size_and_span) = if let Some(size_expr) = &stmt.size { let size_expr = self.lower_expr(size_expr); let span = size_expr.span; - let size_expr = self.try_cast_expr_to_type(&Type::UInt(None, true), &size_expr); + let size_expr = Self::try_cast_expr_to_type(&Type::UInt(None, true), &size_expr); if let Some(Some(semantic::LiteralKind::Int(val))) = size_expr.map(|expr| expr.const_eval(&self.symbols)) @@ -1872,7 +1880,17 @@ impl Lowerer { let Some(size) = self.const_eval_type_width_designator_from_expr(size) else { return crate::semantic::types::Type::Err; }; - crate::semantic::types::Type::Float(Some(size), is_const) + if size > 64 { + self.push_semantic_error(SemanticErrorKind::TypeMaxWidthExceeded( + "float".to_string(), + 64, + size as usize, + float_type.span, + )); + crate::semantic::types::Type::Err + } else { + crate::semantic::types::Type::Float(Some(size), is_const) + } } None => crate::semantic::types::Type::Float(None, is_const), }, @@ -1894,7 +1912,18 @@ impl Lowerer { let Some(size) = self.const_eval_type_width_designator_from_expr(size) else { return crate::semantic::types::Type::Err; }; - crate::semantic::types::Type::Angle(Some(size), is_const) + + if size > 64 { + self.push_semantic_error(SemanticErrorKind::TypeMaxWidthExceeded( + "angle".to_string(), + 64, + size as usize, + angle_type.span, + )); + crate::semantic::types::Type::Err + } else { + crate::semantic::types::Type::Angle(Some(size), is_const) + } } None => crate::semantic::types::Type::Angle(None, is_const), }, @@ -2005,10 +2034,11 @@ impl Lowerer { } }; let expr = match ty { + Type::Angle(_, _) => Some(from_lit_kind(LiteralKind::Angle(Default::default()))), Type::Bit(_) => Some(from_lit_kind(LiteralKind::Bit(false))), Type::Int(_, _) | Type::UInt(_, _) => Some(from_lit_kind(LiteralKind::Int(0))), Type::Bool(_) => Some(from_lit_kind(LiteralKind::Bool(false))), - Type::Angle(_, _) | Type::Float(_, _) => Some(from_lit_kind(LiteralKind::Float(0.0))), + Type::Float(_, _) => Some(from_lit_kind(LiteralKind::Float(0.0))), Type::Complex(_, _) => Some(from_lit_kind(LiteralKind::Complex(0.0, 0.0))), Type::Stretch(_) => { let message = "Stretch default values"; @@ -2211,7 +2241,19 @@ impl Lowerer { } None } - (Type::Angle(..) | Type::Float(..), Type::Float(..)) => { + (Type::Angle(width, _), Type::Float(..)) => { + if let semantic::LiteralKind::Float(value) = kind { + return Some(semantic::Expr { + span, + kind: Box::new(semantic::ExprKind::Lit(semantic::LiteralKind::Angle( + Angle::from_f64_maybe_sized(*value, *width), + ))), + ty: lhs_ty.as_const(), + }); + } + None + } + (Type::Float(..), Type::Float(..)) => { if let semantic::LiteralKind::Float(value) = kind { return Some(semantic::Expr { span, @@ -2335,18 +2377,14 @@ impl Lowerer { } fn cast_expr_to_type(&mut self, ty: &Type, expr: &semantic::Expr) -> semantic::Expr { - let Some(cast_expr) = self.try_cast_expr_to_type(ty, expr) else { + let Some(cast_expr) = Self::try_cast_expr_to_type(ty, expr) else { self.push_invalid_cast_error(ty, &expr.ty, expr.span); return expr.clone(); }; cast_expr } - fn try_cast_expr_to_type( - &mut self, - ty: &Type, - expr: &semantic::Expr, - ) -> Option { + fn try_cast_expr_to_type(ty: &Type, expr: &semantic::Expr) -> Option { if *ty == expr.ty { // Base case, we shouldn't have gotten here // but if we did, we can just return the rhs @@ -2372,19 +2410,11 @@ impl Lowerer { | (Type::Float(w1, _), Type::Float(w2, _)) | (Type::Complex(w1, _), Type::Complex(w2, _)) => { if w1.is_none() && w2.is_some() { - return Some(semantic::Expr { - span: expr.span, - kind: expr.kind.clone(), - ty: ty.clone(), - }); + return Some(wrap_expr_in_implicit_cast_expr(ty.clone(), expr.clone())); } if *w1 >= *w2 { - return Some(semantic::Expr { - span: expr.span, - kind: expr.kind.clone(), - ty: ty.clone(), - }); + return Some(wrap_expr_in_implicit_cast_expr(ty.clone(), expr.clone())); } } _ => {} @@ -2394,7 +2424,7 @@ impl Lowerer { // the standard library. match &expr.ty { Type::Angle(_, _) => Self::cast_angle_expr_to_type(ty, expr), - Type::Bit(_) => self.cast_bit_expr_to_type(ty, expr), + Type::Bit(_) => Self::cast_bit_expr_to_type(ty, expr), Type::Bool(_) => Self::cast_bool_expr_to_type(ty, expr), Type::Complex(_, _) => cast_complex_expr_to_type(ty, expr), Type::Float(_, _) => Self::cast_float_expr_to_type(ty, expr), @@ -2428,23 +2458,18 @@ impl Lowerer { /// +----------------+-------+-----+------+-------+-------+-----+----------+-------+ /// | bit | Yes | Yes | Yes | No | Yes | - | No | No | /// +----------------+-------+-----+------+-------+-------+-----+----------+-------+ - fn cast_bit_expr_to_type(&mut self, ty: &Type, rhs: &semantic::Expr) -> Option { + fn cast_bit_expr_to_type(ty: &Type, rhs: &semantic::Expr) -> Option { assert!(matches!(rhs.ty, Type::Bit(..))); // There is no operand, choosing the span of the node // but we could use the expr span as well. match ty { - &Type::Angle(..) => { - let msg = "Cast bit to angle"; - self.push_unimplemented_error_message(msg, rhs.span); - None - } &Type::Float(..) => { // The spec says that this cast isn't supported, but it // casts to other types that cast to float. For now, we'll // say it is invalid like the spec. None } - &Type::Bool(_) | &Type::Int(_, _) | &Type::UInt(_, _) => { + &Type::Angle(..) | &Type::Bool(_) | &Type::Int(_, _) | &Type::UInt(_, _) => { Some(wrap_expr_in_implicit_cast_expr(ty.clone(), rhs.clone())) } @@ -2642,6 +2667,30 @@ impl Lowerer { let new_lhs = self.cast_expr_to_type(&ty, &lhs); let new_rhs = self.cast_expr_to_type(&ty, &rhs); (new_lhs, new_rhs, ty) + } else if binop_requires_asymmetric_angle_op(op, &left_type, &rhs.ty) { + if matches!(op, syntax::BinOp::Div) + && matches!(left_type, Type::Angle(..)) + && matches!(right_type, Type::Angle(..)) + { + // result is uint, we need to promote both sides to match width. + let angle_ty = Type::Angle(promote_width(&left_type, &right_type), ty_constness); + let new_lhs = self.cast_expr_to_type(&angle_ty, &lhs); + let new_rhs = self.cast_expr_to_type(&angle_ty, &rhs); + let int_ty = Type::UInt(angle_ty.width(), ty_constness); + (new_lhs, new_rhs, int_ty) + } else if matches!(left_type, Type::Angle(..)) { + let ty = Type::Angle(left_type.width(), ty_constness); + let new_lhs = self.cast_expr_to_type(&ty, &lhs); + let rhs_ty = Type::UInt(ty.width(), ty_constness); + let new_rhs = self.cast_expr_to_type(&rhs_ty, &rhs); + (new_lhs, new_rhs, ty) + } else { + let lhs_ty = Type::UInt(rhs.ty.width(), ty_constness); + let new_lhs = self.cast_expr_to_type(&lhs_ty, &lhs); + let ty = Type::Angle(rhs.ty.width(), ty_constness); + let new_rhs = self.cast_expr_to_type(&ty, &rhs); + (new_lhs, new_rhs, ty) + } } else if binop_requires_int_conversion_for_type(op, &left_type, &rhs.ty) { let ty = Type::Int(None, ty_constness); let new_lhs = self.cast_expr_to_type(&ty, &lhs); @@ -2747,20 +2796,14 @@ impl Lowerer { syntax::BinOp::AndB | syntax::BinOp::OrB | syntax::BinOp::XorB ) && matches!( left_type, - Type::Bit(..) - | Type::UInt(..) - | Type::Angle(..) - | Type::BitArray(ArrayDimensions::One(_), _) + Type::Bit(..) | Type::UInt(..) | Type::BitArray(ArrayDimensions::One(_), _) )) { return true; } if (matches!(op, syntax::BinOp::Shl | syntax::BinOp::Shr) && matches!( left_type, - Type::Bit(..) - | Type::UInt(..) - | Type::Angle(..) - | Type::BitArray(ArrayDimensions::One(_), _) + Type::Bit(..) | Type::UInt(..) | Type::BitArray(ArrayDimensions::One(_), _) )) { return true; @@ -3100,16 +3143,19 @@ fn try_get_qsharp_name_and_implicit_modifiers>( kind, }; - // ch, crx, cry, crz, sdg, and tdg match gate_name.as_ref() { - "cy" => Some(("Y".to_string(), make_modifier(Ctrl(1)))), - "cz" => Some(("Z".to_string(), make_modifier(Ctrl(1)))), - "ch" => Some(("H".to_string(), make_modifier(Ctrl(1)))), - "crx" => Some(("Rx".to_string(), make_modifier(Ctrl(1)))), - "cry" => Some(("Ry".to_string(), make_modifier(Ctrl(1)))), - "crz" => Some(("Rz".to_string(), make_modifier(Ctrl(1)))), - "sdg" => Some(("S".to_string(), make_modifier(Inv))), - "tdg" => Some(("T".to_string(), make_modifier(Inv))), + "cy" => Some(("y".to_string(), make_modifier(Ctrl(1)))), + "cz" => Some(("z".to_string(), make_modifier(Ctrl(1)))), + "ch" => Some(("h".to_string(), make_modifier(Ctrl(1)))), + "crx" => Some(("rx".to_string(), make_modifier(Ctrl(1)))), + "cry" => Some(("ry".to_string(), make_modifier(Ctrl(1)))), + "crz" => Some(("rz".to_string(), make_modifier(Ctrl(1)))), + "cswap" => Some(("swap".to_string(), make_modifier(Ctrl(1)))), + "sdg" => Some(("s".to_string(), make_modifier(Inv))), + "tdg" => Some(("t".to_string(), make_modifier(Inv))), + // Gates for OpenQASM 2 backwards compatibility + "CX" => Some(("x".to_string(), make_modifier(Ctrl(1)))), + "cphase" => Some(("phase".to_string(), make_modifier(Ctrl(1)))), _ => None, } } diff --git a/compiler/qsc_qasm3/src/semantic/tests.rs b/compiler/qsc_qasm3/src/semantic/tests.rs index 583875c50a..d2eb3f29a0 100644 --- a/compiler/qsc_qasm3/src/semantic/tests.rs +++ b/compiler/qsc_qasm3/src/semantic/tests.rs @@ -221,10 +221,10 @@ fn semantic_errors_map_to_their_corresponding_file_specific_spans() { bit c = r; // undefined symbol r "#; let source1 = r#"include "source2.qasm"; - angle z = 7.0; - float k = z + false; // invalid cast"#; - let source2 = "bit x = 1; - bool x = y && x; // undefined y, redefine x"; + angle j = 7.0; + float k = j + false; // invalid cast"#; + let source2 = "bit l = 1; + bool l = v && l; // undefined y, redefine l"; let all_sources = [ ("source0.qasm".into(), source0.into()), ("source1.qasm".into(), source1.into()), @@ -241,7 +241,7 @@ fn semantic_errors_map_to_their_corresponding_file_specific_spans() { Stmt [196-206]: annotations: kind: ClassicalDeclarationStmt [196-206]: - symbol_id: 26 + symbol_id: 35 ty_span: [196-199] init_expr: Expr [204-205]: ty: Bit(true) @@ -249,7 +249,7 @@ fn semantic_errors_map_to_their_corresponding_file_specific_spans() { Stmt [211-227]: annotations: kind: ClassicalDeclarationStmt [211-227]: - symbol_id: 26 + symbol_id: 35 ty_span: [211-215] init_expr: Expr [220-226]: ty: Bool(false) @@ -257,26 +257,26 @@ fn semantic_errors_map_to_their_corresponding_file_specific_spans() { op: AndL lhs: Expr [220-221]: ty: Err - kind: SymbolId(27) + kind: SymbolId(36) rhs: Expr [225-226]: ty: Bool(false) kind: Cast [0-0]: ty: Bool(false) expr: Expr [225-226]: ty: Bit(false) - kind: SymbolId(26) + kind: SymbolId(35) Stmt [140-154]: annotations: kind: ClassicalDeclarationStmt [140-154]: - symbol_id: 28 + symbol_id: 37 ty_span: [140-145] init_expr: Expr [150-153]: ty: Angle(None, true) - kind: Lit: Float(7.0) + kind: Lit: Angle(0.7168146928204138) Stmt [159-179]: annotations: kind: ClassicalDeclarationStmt [159-179]: - symbol_id: 29 + symbol_id: 38 ty_span: [159-164] init_expr: Expr [169-178]: ty: Float(None, false) @@ -284,7 +284,7 @@ fn semantic_errors_map_to_their_corresponding_file_specific_spans() { op: Add lhs: Expr [169-170]: ty: Angle(None, false) - kind: SymbolId(28) + kind: SymbolId(37) rhs: Expr [173-178]: ty: Float(None, false) kind: Cast [0-0]: @@ -295,34 +295,34 @@ fn semantic_errors_map_to_their_corresponding_file_specific_spans() { Stmt [74-84]: annotations: kind: ClassicalDeclarationStmt [74-84]: - symbol_id: 31 + symbol_id: 40 ty_span: [74-77] init_expr: Expr [82-83]: ty: Err - kind: SymbolId(30) + kind: SymbolId(39) [Qsc.Qasm3.Compile.UndefinedSymbol - x Undefined symbol: y. + x Undefined symbol: v. ,-[source2.qasm:2:14] - 1 | bit x = 1; - 2 | bool x = y && x; // undefined y, redefine x + 1 | bit l = 1; + 2 | bool l = v && l; // undefined y, redefine l : ^ `---- , Qsc.Qasm3.Compile.CannotCast x Cannot cast expression of type Err to type Bool(false) ,-[source2.qasm:2:14] - 1 | bit x = 1; - 2 | bool x = y && x; // undefined y, redefine x + 1 | bit l = 1; + 2 | bool l = v && l; // undefined y, redefine l : ^ `---- , Qsc.Qasm3.Compile.RedefinedSymbol - x Redefined symbol: x. + x Redefined symbol: l. ,-[source2.qasm:2:10] - 1 | bit x = 1; - 2 | bool x = y && x; // undefined y, redefine x + 1 | bit l = 1; + 2 | bool l = v && l; // undefined y, redefine l : ^ `---- , Qsc.Qasm3.Compile.CannotCast @@ -330,8 +330,8 @@ fn semantic_errors_map_to_their_corresponding_file_specific_spans() { x Cannot cast expression of type Angle(None, false) to type Float(None, | false) ,-[source1.qasm:3:15] - 2 | angle z = 7.0; - 3 | float k = z + false; // invalid cast + 2 | angle j = 7.0; + 3 | float k = j + false; // invalid cast : ^ `---- , Qsc.Qasm3.Compile.UndefinedSymbol diff --git a/compiler/qsc_qasm3/src/semantic/tests/decls.rs b/compiler/qsc_qasm3/src/semantic/tests/decls.rs index daf0381d85..94629a0e1a 100644 --- a/compiler/qsc_qasm3/src/semantic/tests/decls.rs +++ b/compiler/qsc_qasm3/src/semantic/tests/decls.rs @@ -93,7 +93,7 @@ fn scalar_ty_designator_must_be_castable_to_const_int() { ty_span: [6-11] init_expr: Expr [19-22]: ty: Angle(None, true) - kind: Lit: Float(2.0) + kind: Lit: Angle(2.0000000000000004) Stmt [24-36]: annotations: kind: ClassicalDeclarationStmt [24-36]: diff --git a/compiler/qsc_qasm3/src/semantic/tests/decls/angle.rs b/compiler/qsc_qasm3/src/semantic/tests/decls/angle.rs index 74363762e3..e64a0ff65a 100644 --- a/compiler/qsc_qasm3/src/semantic/tests/decls/angle.rs +++ b/compiler/qsc_qasm3/src/semantic/tests/decls/angle.rs @@ -15,11 +15,11 @@ fn implicit_bitness_default() { ty_span: [0-5] init_expr: Expr [0-0]: ty: Angle(None, true) - kind: Lit: Float(0.0) + kind: Lit: Angle(0) [8] Symbol [6-7]: name: x type: Angle(None, false) - qsharp_type: Double + qsharp_type: Angle io_kind: Default"#]], ); } @@ -34,11 +34,11 @@ fn lit() { ty_span: [0-5] init_expr: Expr [10-14]: ty: Angle(None, true) - kind: Lit: Float(42.1) + kind: Lit: Angle(4.400888156922484) [8] Symbol [6-7]: name: x type: Angle(None, false) - qsharp_type: Double + qsharp_type: Angle io_kind: Default"#]], ); } @@ -53,11 +53,11 @@ fn const_lit() { ty_span: [6-11] init_expr: Expr [16-20]: ty: Angle(None, true) - kind: Lit: Float(42.1) + kind: Lit: Angle(4.400888156922484) [8] Symbol [12-13]: name: x type: Angle(None, true) - qsharp_type: Double + qsharp_type: Angle io_kind: Default"#]], ); } @@ -72,11 +72,11 @@ fn lit_explicit_width() { ty_span: [0-9] init_expr: Expr [14-18]: ty: Angle(Some(64), true) - kind: Lit: Float(42.1) + kind: Lit: Angle(4.400888156922484) [8] Symbol [10-11]: name: x type: Angle(Some(64), false) - qsharp_type: Double + qsharp_type: Angle io_kind: Default"#]], ); } @@ -91,11 +91,11 @@ fn const_explicit_width_lit() { ty_span: [6-15] init_expr: Expr [20-24]: ty: Angle(Some(64), true) - kind: Lit: Float(42.1) + kind: Lit: Angle(4.400888156922484) [8] Symbol [16-17]: name: x type: Angle(Some(64), true) - qsharp_type: Double + qsharp_type: Angle io_kind: Default"#]], ); } @@ -110,11 +110,11 @@ fn lit_decl_leading_dot() { ty_span: [0-5] init_expr: Expr [10-14]: ty: Angle(None, true) - kind: Lit: Float(0.421) + kind: Lit: Angle(0.4210000000000001) [8] Symbol [6-7]: name: x type: Angle(None, false) - qsharp_type: Double + qsharp_type: Angle io_kind: Default"#]], ); } @@ -129,11 +129,11 @@ fn const_lit_decl_leading_dot() { ty_span: [6-11] init_expr: Expr [16-20]: ty: Angle(None, true) - kind: Lit: Float(0.421) + kind: Lit: Angle(0.4210000000000001) [8] Symbol [12-13]: name: x type: Angle(None, true) - qsharp_type: Double + qsharp_type: Angle io_kind: Default"#]], ); } @@ -148,11 +148,11 @@ fn const_lit_decl_leading_dot_scientific() { ty_span: [6-11] init_expr: Expr [16-22]: ty: Angle(None, true) - kind: Lit: Float(42.1) + kind: Lit: Angle(4.400888156922484) [8] Symbol [12-13]: name: x type: Angle(None, true) - qsharp_type: Double + qsharp_type: Angle io_kind: Default"#]], ); } @@ -167,11 +167,11 @@ fn lit_decl_trailing_dot() { ty_span: [0-5] init_expr: Expr [10-14]: ty: Angle(None, true) - kind: Lit: Float(421.0) + kind: Lit: Angle(0.02658441896772248) [8] Symbol [6-7]: name: x type: Angle(None, false) - qsharp_type: Double + qsharp_type: Angle io_kind: Default"#]], ); } @@ -186,11 +186,11 @@ fn const_lit_decl_trailing_dot() { ty_span: [6-11] init_expr: Expr [16-20]: ty: Angle(None, true) - kind: Lit: Float(421.0) + kind: Lit: Angle(0.02658441896772248) [8] Symbol [12-13]: name: x type: Angle(None, true) - qsharp_type: Double + qsharp_type: Angle io_kind: Default"#]], ); } @@ -205,11 +205,11 @@ fn lit_decl_scientific() { ty_span: [0-5] init_expr: Expr [10-16]: ty: Angle(None, true) - kind: Lit: Float(42.1) + kind: Lit: Angle(4.400888156922484) [8] Symbol [6-7]: name: x type: Angle(None, false) - qsharp_type: Double + qsharp_type: Angle io_kind: Default"#]], ); } @@ -224,11 +224,11 @@ fn const_lit_decl_scientific() { ty_span: [6-11] init_expr: Expr [16-22]: ty: Angle(None, true) - kind: Lit: Float(42.1) + kind: Lit: Angle(4.400888156922484) [8] Symbol [12-13]: name: x type: Angle(None, true) - qsharp_type: Double + qsharp_type: Angle io_kind: Default"#]], ); } @@ -243,11 +243,11 @@ fn lit_decl_scientific_signed_pos() { ty_span: [0-5] init_expr: Expr [10-17]: ty: Angle(None, true) - kind: Lit: Float(42.1) + kind: Lit: Angle(4.400888156922484) [8] Symbol [6-7]: name: x type: Angle(None, false) - qsharp_type: Double + qsharp_type: Angle io_kind: Default"#]], ); } @@ -262,11 +262,11 @@ fn const_lit_decl_scientific_signed_pos() { ty_span: [6-11] init_expr: Expr [16-23]: ty: Angle(None, true) - kind: Lit: Float(42.1) + kind: Lit: Angle(4.400888156922484) [8] Symbol [12-13]: name: x type: Angle(None, true) - qsharp_type: Double + qsharp_type: Angle io_kind: Default"#]], ); } @@ -281,11 +281,11 @@ fn lit_decl_scientific_cap_e() { ty_span: [0-5] init_expr: Expr [10-16]: ty: Angle(None, true) - kind: Lit: Float(42.1) + kind: Lit: Angle(4.400888156922484) [8] Symbol [6-7]: name: x type: Angle(None, false) - qsharp_type: Double + qsharp_type: Angle io_kind: Default"#]], ); } @@ -300,11 +300,11 @@ fn const_lit_decl_scientific_cap_e() { ty_span: [6-11] init_expr: Expr [16-22]: ty: Angle(None, true) - kind: Lit: Float(42.1) + kind: Lit: Angle(4.400888156922484) [8] Symbol [12-13]: name: x type: Angle(None, true) - qsharp_type: Double + qsharp_type: Angle io_kind: Default"#]], ); } @@ -319,11 +319,11 @@ fn lit_decl_scientific_signed_neg() { ty_span: [0-5] init_expr: Expr [10-18]: ty: Angle(None, true) - kind: Lit: Float(42.1) + kind: Lit: Angle(4.400888156922484) [8] Symbol [6-7]: name: x type: Angle(None, false) - qsharp_type: Double + qsharp_type: Angle io_kind: Default"#]], ); } @@ -338,11 +338,11 @@ fn const_lit_decl_scientific_signed_neg() { ty_span: [6-11] init_expr: Expr [16-24]: ty: Angle(None, true) - kind: Lit: Float(42.1) + kind: Lit: Angle(4.400888156922484) [8] Symbol [12-13]: name: x type: Angle(None, true) - qsharp_type: Double + qsharp_type: Angle io_kind: Default"#]], ); } @@ -369,7 +369,7 @@ fn const_lit_decl_signed_float_lit_cast_neg() { [8] Symbol [12-13]: name: x type: Angle(None, true) - qsharp_type: Double + qsharp_type: Angle io_kind: Default"#]], ); } @@ -405,3 +405,75 @@ fn const_lit_decl_signed_int_lit_cast_neg_fails() { ]"#]], ); } + +#[test] +fn explicit_zero_width_fails() { + check_classical_decl( + "angle[0] x = 42.1;", + &expect![[r#" + Program: + version: + statements: + Stmt [0-18]: + annotations: + kind: ClassicalDeclarationStmt [0-18]: + symbol_id: 8 + ty_span: [0-8] + init_expr: Expr [13-17]: + ty: Float(None, true) + kind: Lit: Float(42.1) + + [Qsc.Qasm3.Compile.TypeWidthMustBePositiveIntConstExpr + + x Type width must be a positive integer const expression. + ,-[test:1:7] + 1 | angle[0] x = 42.1; + : ^ + `---- + , Qsc.Qasm3.Compile.CannotAssignToType + + x Cannot assign a value of Float(None, true) type to a classical variable of + | Err type. + ,-[test:1:1] + 1 | angle[0] x = 42.1; + : ^^^^^^^^^^^^^^^^^^ + `---- + ]"#]], + ); +} + +#[test] +fn explicit_width_over_64_fails() { + check_classical_decl( + "const angle[65] x = 42.1;", + &expect![[r#" + Program: + version: + statements: + Stmt [0-25]: + annotations: + kind: ClassicalDeclarationStmt [0-25]: + symbol_id: 8 + ty_span: [6-15] + init_expr: Expr [20-24]: + ty: Float(None, true) + kind: Lit: Float(42.1) + + [Qsc.Qasm3.Compile.TypeMaxWidthExceeded + + x angle max width is 64 but 65 was provided. + ,-[test:1:7] + 1 | const angle[65] x = 42.1; + : ^^^^^^^^^ + `---- + , Qsc.Qasm3.Compile.CannotAssignToType + + x Cannot assign a value of Float(None, true) type to a classical variable of + | Err type. + ,-[test:1:1] + 1 | const angle[65] x = 42.1; + : ^^^^^^^^^^^^^^^^^^^^^^^^^ + `---- + ]"#]], + ); +} diff --git a/compiler/qsc_qasm3/src/semantic/tests/expression/implicit_cast_from_angle.rs b/compiler/qsc_qasm3/src/semantic/tests/expression/implicit_cast_from_angle.rs index fb6601199e..3936536762 100644 --- a/compiler/qsc_qasm3/src/semantic/tests/expression/implicit_cast_from_angle.rs +++ b/compiler/qsc_qasm3/src/semantic/tests/expression/implicit_cast_from_angle.rs @@ -20,11 +20,11 @@ fn to_bit_implicitly() { ty_span: [9-14] init_expr: Expr [19-22]: ty: Angle(None, true) - kind: Lit: Float(42.0) + kind: Lit: Angle(4.300888156922483) [8] Symbol [15-16]: name: x type: Angle(None, false) - qsharp_type: Double + qsharp_type: Angle io_kind: Default ClassicalDeclarationStmt [32-42]: symbol_id: 9 @@ -60,11 +60,11 @@ fn explicit_width_to_bit_implicitly_fails() { ty_span: [9-18] init_expr: Expr [23-26]: ty: Angle(Some(64), true) - kind: Lit: Float(42.0) + kind: Lit: Angle(4.300888156922483) [8] Symbol [19-20]: name: x type: Angle(Some(64), false) - qsharp_type: Double + qsharp_type: Angle io_kind: Default ClassicalDeclarationStmt [36-46]: symbol_id: 9 @@ -99,11 +99,11 @@ fn to_bool_implicitly() { ty_span: [9-14] init_expr: Expr [19-22]: ty: Angle(None, true) - kind: Lit: Float(42.0) + kind: Lit: Angle(4.300888156922483) [8] Symbol [15-16]: name: x type: Angle(None, false) - qsharp_type: Double + qsharp_type: Angle io_kind: Default ClassicalDeclarationStmt [32-43]: symbol_id: 9 @@ -144,7 +144,7 @@ fn to_implicit_int_implicitly_fails() { ty_span: [9-14] init_expr: Expr [19-22]: ty: Angle(None, true) - kind: Lit: Float(42.0) + kind: Lit: Angle(4.300888156922483) Stmt [32-42]: annotations: kind: ClassicalDeclarationStmt [32-42]: @@ -187,7 +187,7 @@ fn to_explicit_int_implicitly_fails() { ty_span: [9-14] init_expr: Expr [19-22]: ty: Angle(None, true) - kind: Lit: Float(42.0) + kind: Lit: Angle(4.300888156922483) Stmt [32-46]: annotations: kind: ClassicalDeclarationStmt [32-46]: @@ -231,7 +231,7 @@ fn to_implicit_uint_implicitly_fails() { ty_span: [9-14] init_expr: Expr [19-22]: ty: Angle(None, true) - kind: Lit: Float(42.0) + kind: Lit: Angle(4.300888156922483) Stmt [32-43]: annotations: kind: ClassicalDeclarationStmt [32-43]: @@ -327,7 +327,7 @@ fn to_explicit_uint_implicitly_fails() { ty_span: [9-14] init_expr: Expr [19-22]: ty: Angle(None, true) - kind: Lit: Float(42.0) + kind: Lit: Angle(4.300888156922483) Stmt [32-47]: annotations: kind: ClassicalDeclarationStmt [32-47]: @@ -371,7 +371,7 @@ fn to_explicit_bigint_implicitly_fails() { ty_span: [9-14] init_expr: Expr [19-22]: ty: Angle(None, true) - kind: Lit: Float(42.0) + kind: Lit: Angle(4.300888156922483) Stmt [32-46]: annotations: kind: ClassicalDeclarationStmt [32-46]: @@ -415,7 +415,7 @@ fn to_implicit_float_implicitly_fails() { ty_span: [9-14] init_expr: Expr [19-22]: ty: Angle(None, true) - kind: Lit: Float(42.0) + kind: Lit: Angle(4.300888156922483) Stmt [32-44]: annotations: kind: ClassicalDeclarationStmt [32-44]: @@ -459,7 +459,7 @@ fn to_explicit_float_implicitly_fails() { ty_span: [9-14] init_expr: Expr [19-22]: ty: Angle(None, true) - kind: Lit: Float(42.0) + kind: Lit: Angle(4.300888156922483) Stmt [32-48]: annotations: kind: ClassicalDeclarationStmt [32-48]: @@ -503,7 +503,7 @@ fn to_implicit_complex_implicitly_fails() { ty_span: [9-14] init_expr: Expr [19-22]: ty: Angle(None, true) - kind: Lit: Float(42.0) + kind: Lit: Angle(4.300888156922483) Stmt [32-53]: annotations: kind: ClassicalDeclarationStmt [32-53]: @@ -547,7 +547,7 @@ fn to_explicit_complex_implicitly_fails() { ty_span: [9-14] init_expr: Expr [19-22]: ty: Angle(None, true) - kind: Lit: Float(42.0) + kind: Lit: Angle(4.300888156922483) Stmt [32-57]: annotations: kind: ClassicalDeclarationStmt [32-57]: @@ -586,11 +586,11 @@ fn to_angle_implicitly() { ty_span: [9-14] init_expr: Expr [19-22]: ty: Angle(None, true) - kind: Lit: Float(42.0) + kind: Lit: Angle(4.300888156922483) [8] Symbol [15-16]: name: x type: Angle(None, false) - qsharp_type: Double + qsharp_type: Angle io_kind: Default ClassicalDeclarationStmt [32-44]: symbol_id: 9 @@ -601,7 +601,7 @@ fn to_angle_implicitly() { [9] Symbol [38-39]: name: y type: Angle(None, false) - qsharp_type: Double + qsharp_type: Angle io_kind: Default "#]], ); @@ -622,22 +622,89 @@ fn to_explicit_angle_implicitly() { ty_span: [9-14] init_expr: Expr [19-22]: ty: Angle(None, true) - kind: Lit: Float(42.0) + kind: Lit: Angle(4.300888156922483) [8] Symbol [15-16]: name: x type: Angle(None, false) - qsharp_type: Double + qsharp_type: Angle io_kind: Default ClassicalDeclarationStmt [32-47]: symbol_id: 9 ty_span: [32-40] init_expr: Expr [45-46]: ty: Angle(Some(4), false) - kind: SymbolId(8) + kind: Cast [0-0]: + ty: Angle(Some(4), false) + expr: Expr [45-46]: + ty: Angle(None, false) + kind: SymbolId(8) [9] Symbol [41-42]: name: y type: Angle(Some(4), false) - qsharp_type: Double + qsharp_type: Angle + io_kind: Default + "#]], + ); +} + +#[test] +fn width_promotion() { + let input = " + angle[32] x = 1.0; + angle[48] y = 2.0; + bit z = x / y; + "; + + check_classical_decls( + input, + &expect![[r#" + ClassicalDeclarationStmt [9-27]: + symbol_id: 8 + ty_span: [9-18] + init_expr: Expr [23-26]: + ty: Angle(Some(32), true) + kind: Lit: Angle(1.000000000619646) + [8] Symbol [19-20]: + name: x + type: Angle(Some(32), false) + qsharp_type: Angle + io_kind: Default + ClassicalDeclarationStmt [36-54]: + symbol_id: 9 + ty_span: [36-45] + init_expr: Expr [50-53]: + ty: Angle(Some(48), true) + kind: Lit: Angle(1.999999999999999) + [9] Symbol [46-47]: + name: y + type: Angle(Some(48), false) + qsharp_type: Angle + io_kind: Default + ClassicalDeclarationStmt [63-77]: + symbol_id: 10 + ty_span: [63-66] + init_expr: Expr [71-76]: + ty: Bit(false) + kind: Cast [0-0]: + ty: Bit(false) + expr: Expr [71-76]: + ty: UInt(Some(48), false) + kind: BinaryOpExpr: + op: Div + lhs: Expr [71-72]: + ty: Angle(Some(48), false) + kind: Cast [0-0]: + ty: Angle(Some(48), false) + expr: Expr [71-72]: + ty: Angle(Some(32), false) + kind: SymbolId(8) + rhs: Expr [75-76]: + ty: Angle(Some(48), false) + kind: SymbolId(9) + [10] Symbol [67-68]: + name: z + type: Bit(false) + qsharp_type: Result io_kind: Default "#]], ); diff --git a/compiler/qsc_qasm3/src/semantic/tests/expression/implicit_cast_from_bit.rs b/compiler/qsc_qasm3/src/semantic/tests/expression/implicit_cast_from_bit.rs index 9e207073e3..b0f0e96648 100644 --- a/compiler/qsc_qasm3/src/semantic/tests/expression/implicit_cast_from_bit.rs +++ b/compiler/qsc_qasm3/src/semantic/tests/expression/implicit_cast_from_bit.rs @@ -6,25 +6,83 @@ use expect_test::expect; use crate::semantic::tests::check_classical_decls; #[test] -#[ignore = "not yet implemented"] fn to_angle_implicitly() { let input = r#" bit x = 1; angle y = x; "#; - check_classical_decls(input, &expect![[r#""#]]); + check_classical_decls( + input, + &expect![[r#" + ClassicalDeclarationStmt [10-20]: + symbol_id: 8 + ty_span: [10-13] + init_expr: Expr [18-19]: + ty: Bit(true) + kind: Lit: Bit(1) + [8] Symbol [14-15]: + name: x + type: Bit(false) + qsharp_type: Result + io_kind: Default + ClassicalDeclarationStmt [30-42]: + symbol_id: 9 + ty_span: [30-35] + init_expr: Expr [40-41]: + ty: Angle(None, false) + kind: Cast [0-0]: + ty: Angle(None, false) + expr: Expr [40-41]: + ty: Bit(false) + kind: SymbolId(8) + [9] Symbol [36-37]: + name: y + type: Angle(None, false) + qsharp_type: Angle + io_kind: Default + "#]], + ); } #[test] -#[ignore = "not yet implemented"] fn to_explicit_angle_implicitly() { let input = r#" bit x = 1; angle[4] y = x; "#; - check_classical_decls(input, &expect![[r#""#]]); + check_classical_decls( + input, + &expect![[r#" + ClassicalDeclarationStmt [10-20]: + symbol_id: 8 + ty_span: [10-13] + init_expr: Expr [18-19]: + ty: Bit(true) + kind: Lit: Bit(1) + [8] Symbol [14-15]: + name: x + type: Bit(false) + qsharp_type: Result + io_kind: Default + ClassicalDeclarationStmt [30-45]: + symbol_id: 9 + ty_span: [30-38] + init_expr: Expr [43-44]: + ty: Angle(Some(4), false) + kind: Cast [0-0]: + ty: Angle(Some(4), false) + expr: Expr [43-44]: + ty: Bit(false) + kind: SymbolId(8) + [9] Symbol [39-40]: + name: y + type: Angle(Some(4), false) + qsharp_type: Angle + io_kind: Default + "#]], + ); } #[test] diff --git a/compiler/qsc_qasm3/src/semantic/tests/expression/implicit_cast_from_float.rs b/compiler/qsc_qasm3/src/semantic/tests/expression/implicit_cast_from_float.rs index 78ab02b6b9..706646a53b 100644 --- a/compiler/qsc_qasm3/src/semantic/tests/expression/implicit_cast_from_float.rs +++ b/compiler/qsc_qasm3/src/semantic/tests/expression/implicit_cast_from_float.rs @@ -436,7 +436,11 @@ fn to_explicit_float_implicitly() { ty_span: [32-41] init_expr: Expr [46-47]: ty: Float(Some(32), false) - kind: SymbolId(8) + kind: Cast [0-0]: + ty: Float(Some(32), false) + expr: Expr [46-47]: + ty: Float(None, false) + kind: SymbolId(8) [9] Symbol [42-43]: name: y type: Float(Some(32), false) diff --git a/compiler/qsc_qasm3/src/semantic/types.rs b/compiler/qsc_qasm3/src/semantic/types.rs index 2187a37442..e05dadc203 100644 --- a/compiler/qsc_qasm3/src/semantic/types.rs +++ b/compiler/qsc_qasm3/src/semantic/types.rs @@ -623,6 +623,34 @@ pub(crate) fn unary_op_can_be_applied_to_type(op: syntax::UnaryOp, ty: &Type) -> } } +pub(crate) fn binop_requires_asymmetric_angle_op( + op: syntax::BinOp, + lhs: &Type, + rhs: &Type, +) -> bool { + match op { + syntax::BinOp::Div => { + matches!( + (lhs, rhs), + ( + Type::Angle(_, _), + Type::Int(_, _) | Type::UInt(_, _) | Type::Angle(_, _) + ) + ) + } + syntax::BinOp::Mul => { + matches!( + (lhs, rhs), + (Type::Angle(_, _), Type::Int(_, _) | Type::UInt(_, _)) + ) || matches!( + (lhs, rhs), + (Type::Int(_, _) | Type::UInt(_, _), Type::Angle(_, _)) + ) + } + _ => false, + } +} + /// Bit arrays can be compared, but need to be converted to int first pub(crate) fn binop_requires_int_conversion_for_type( op: syntax::BinOp, @@ -688,8 +716,8 @@ pub(crate) fn try_promote_with_casting(left_type: &Type, right_type: &Type) -> T } // simple promotion failed, try a lossless cast // each side to double - let promoted_rhs = promote_types(&Type::Float(None, false), right_type); - let promoted_lhs = promote_types(left_type, &Type::Float(None, false)); + let promoted_rhs = promote_types(&Type::Float(None, right_type.is_const()), right_type); + let promoted_lhs = promote_types(left_type, &Type::Float(None, left_type.is_const())); match (promoted_lhs, promoted_rhs) { (Type::Void, Type::Void) => Type::Float(None, false), diff --git a/compiler/qsc_qasm3/src/stdlib.rs b/compiler/qsc_qasm3/src/stdlib.rs new file mode 100644 index 0000000000..ff466a50e6 --- /dev/null +++ b/compiler/qsc_qasm3/src/stdlib.rs @@ -0,0 +1,7 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +pub(crate) mod angle; +pub(crate) mod compile; + +pub use compile::package_store_with_qasm; diff --git a/compiler/qsc_qasm3/src/stdlib/QasmStd/qsharp.json b/compiler/qsc_qasm3/src/stdlib/QasmStd/qsharp.json new file mode 100644 index 0000000000..a29a2e746f --- /dev/null +++ b/compiler/qsc_qasm3/src/stdlib/QasmStd/qsharp.json @@ -0,0 +1,9 @@ +{ + "author": "Microsoft", + "license": "MIT", + "files": [ + "src/QasmStd/Angle.qs", + "src/QasmStd/Convert.qs", + "src/QasmStd/Intrinsic.qs" + ] +} diff --git a/compiler/qsc_qasm3/src/stdlib/QasmStd/src/QasmStd/Angle.qs b/compiler/qsc_qasm3/src/stdlib/QasmStd/src/QasmStd/Angle.qs new file mode 100644 index 0000000000..d3926410ce --- /dev/null +++ b/compiler/qsc_qasm3/src/stdlib/QasmStd/src/QasmStd/Angle.qs @@ -0,0 +1,273 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +import Std.Arrays.Reversed; +import Std.Convert.BigIntAsBoolArray; +import Std.Convert.BoolArrayAsInt; +import Std.Convert.IntAsBigInt; +import Std.Convert.IntAsBoolArray; +import Std.Convert.IntAsDouble; +import Std.Diagnostics.Fact; +import Convert.__IntAsResultArrayBE__; +import Convert.__ResultAsInt__; + +export __Angle__, __AngleAsBoolArrayBE__, __AngleAsResultArray__, __AngleAsDouble__, __AngleAsBool__, __AngleAsResult__, __IntAsAngle__, __DoubleAsAngle__, __ConvertAngleToWidth__, __ConvertAngleToWidthNoTrunc__, __AngleShl__, __AngleShr__, __AngleNotB__, __AngleAndB__, __AngleOrB__, __AngleXorB__, __AngleEq__, __AngleNeq__, __AngleGt__, __AngleGte__, __AngleLt__, __AngleLte__, __AddAngles__, __SubtractAngles__, __MultiplyAngleByInt__, __MultiplyAngleByBigInt__, __DivideAngleByInt__, __DivideAngleByAngle__, __NegAngle__, __ResultAsAngle__; + + +struct __Angle__ { + Value : Int, + Size : Int +} + +function __AngleAsBoolArrayBE__(angle : __Angle__) : Bool[] { + Reversed(IntAsBoolArray(angle.Value, angle.Size)) +} + +function __AngleAsResultArray__(angle : __Angle__) : Result[] { + let (number, bits) = angle!; + __IntAsResultArrayBE__(number, bits) +} + +function __AngleAsDouble__(angle : __Angle__) : Double { + let F64_MANTISSA_DIGITS = 53; + let (value, size) = if angle.Size > F64_MANTISSA_DIGITS { + __ConvertAngleToWidth__(angle, F64_MANTISSA_DIGITS, false)! + } else { + angle! + }; + let denom = IntAsDouble(1 <<< size); + let value = IntAsDouble(value); + let factor = (2.0 * Std.Math.PI()) / denom; + value * factor +} + +function __AngleAsBool__(angle : __Angle__) : Bool { + return angle.Value != 0; +} + +function __ResultAsAngle__(result: Result) : __Angle__ { + new __Angle__ { Value = __ResultAsInt__(result), Size = 1 } +} + +function __AngleAsResult__(angle : __Angle__) : Result { + Microsoft.Quantum.Convert.BoolAsResult(angle.Value != 0) +} + +function __IntAsAngle__(value : Int, size : Int) : __Angle__ { + Fact(value >= 0, "Value must be >= 0"); + Fact(size > 0, "Size must be > 0"); + new __Angle__ { Value = value, Size = size } +} + +function __DoubleAsAngle__(value : Double, size : Int) : __Angle__ { + let tau : Double = 2. * Std.Math.PI(); + + mutable value = value % tau; + if value < 0. { + value = value + tau; + } + + Fact(value >= 0., "Value must be >= 0."); + Fact(value < tau, "Value must be < tau."); + Fact(size > 0, "Size must be > 0"); + + + let factor = tau / Std.Convert.IntAsDouble(1 <<< size); + let value = RoundHalfAwayFromZero(value / factor); + new __Angle__ { Value = value, Size = size } +} + +function __ConvertAngleToWidthNoTrunc__(angle : __Angle__, new_size : Int) : __Angle__ { + __ConvertAngleToWidth__(angle, new_size, false) +} + +function __ConvertAngleToWidth__(angle : __Angle__, new_size : Int, truncate : Bool) : __Angle__ { + let (value, size) = angle!; + if new_size < size { + let value = if truncate { + let shift_amount = size - new_size; + value >>> shift_amount + } else { + // Rounding + let shift_amount = size - new_size; + let half = 1 <<< (shift_amount - 1); + let mask = (1 <<< shift_amount) - 1; + let lower_bits = value &&& mask; + let upper_bits = value >>> shift_amount; + if lower_bits > half or (lower_bits == half and (upper_bits &&& 1) == 1) { + upper_bits + 1 + } else { + upper_bits + } + }; + new __Angle__ { Value = value, Size = size } + } elif new_size == size { + // Same size, no change + angle + } else { + // Padding with zeros + let value = value <<< (new_size - size); + new __Angle__ { Value = value, Size = size } + } +} + +// Bit shift + +function __AngleShl__(lhs : __Angle__, rhs : Int) : __Angle__ { + let (lhs_value, lhs_size) = lhs!; + let mask = (1 <<< lhs_size) - 1; + let value = (lhs_value <<< rhs) &&& mask; + new __Angle__ { Value = value, Size = lhs_size } +} + +function __AngleShr__(lhs : __Angle__, rhs : Int) : __Angle__ { + let (lhs_value, lhs_size) = lhs!; + let value = (lhs_value >>> rhs); + new __Angle__ { Value = value, Size = lhs_size } +} + +// Bitwise + +function __AngleNotB__(angle : __Angle__) : __Angle__ { + let (value, size) = angle!; + let mask = (1 <<< size) - 1; + let value = (~~~value) &&& mask; + new __Angle__ { Value = value, Size = size } +} + +function __AngleAndB__(lhs : __Angle__, rhs : __Angle__) : __Angle__ { + let (lhs_value, lhs_size) = lhs!; + let (rhs_value, rhs_size) = rhs!; + Fact(lhs_size == rhs_size, "Angle sizes must be the same"); + let value = lhs_value &&& rhs_value; + new __Angle__ { Value = value, Size = lhs_size } +} + +function __AngleOrB__(lhs : __Angle__, rhs : __Angle__) : __Angle__ { + let (lhs_value, lhs_size) = lhs!; + let (rhs_value, rhs_size) = rhs!; + Fact(lhs_size == rhs_size, "Angle sizes must be the same"); + let value = lhs_value ||| rhs_value; + new __Angle__ { Value = value, Size = lhs_size } +} + +function __AngleXorB__(lhs : __Angle__, rhs : __Angle__) : __Angle__ { + let (lhs_value, lhs_size) = lhs!; + let (rhs_value, rhs_size) = rhs!; + Fact(lhs_size == rhs_size, "Angle sizes must be the same"); + let value = lhs_value ^^^ rhs_value; + new __Angle__ { Value = value, Size = lhs_size } +} + +// Comparison + +function __AngleEq__(lhs: __Angle__, rhs: __Angle__) : Bool { + let (lhs_value, lhs_size) = lhs!; + let (rhs_value, rhs_size) = rhs!; + Fact(lhs_size == rhs_size, "Angle sizes must be the same"); + lhs_value == rhs_value +} + +function __AngleNeq__(lhs: __Angle__, rhs: __Angle__) : Bool { + let (lhs_value, lhs_size) = lhs!; + let (rhs_value, rhs_size) = rhs!; + Fact(lhs_size == rhs_size, "Angle sizes must be the same"); + lhs_value != rhs_value +} + +function __AngleGt__(lhs: __Angle__, rhs: __Angle__) : Bool { + let (lhs_value, lhs_size) = lhs!; + let (rhs_value, rhs_size) = rhs!; + Fact(lhs_size == rhs_size, "Angle sizes must be the same"); + lhs_value > rhs_value +} + +function __AngleGte__(lhs: __Angle__, rhs: __Angle__) : Bool { + let (lhs_value, lhs_size) = lhs!; + let (rhs_value, rhs_size) = rhs!; + Fact(lhs_size == rhs_size, "Angle sizes must be the same"); + lhs_value >= rhs_value +} + +function __AngleLt__(lhs: __Angle__, rhs: __Angle__) : Bool { + let (lhs_value, lhs_size) = lhs!; + let (rhs_value, rhs_size) = rhs!; + Fact(lhs_size == rhs_size, "Angle sizes must be the same"); + lhs_value < rhs_value +} + +function __AngleLte__(lhs: __Angle__, rhs: __Angle__) : Bool { + let (lhs_value, lhs_size) = lhs!; + let (rhs_value, rhs_size) = rhs!; + Fact(lhs_size == rhs_size, "Angle sizes must be the same"); + lhs_value <= rhs_value +} + +// Arithmetic + +function __AddAngles__(lhs : __Angle__, rhs : __Angle__) : __Angle__ { + let (lhs_value, lhs_size) = lhs!; + let (rhs_value, rhs_size) = rhs!; + Fact(lhs_size == rhs_size, "Angle sizes must be the same"); + let value = (lhs_value + rhs_value) % (1 <<< lhs_size); + new __Angle__ { Value = value, Size = lhs_size } +} + +function __SubtractAngles__(lhs : __Angle__, rhs : __Angle__) : __Angle__ { + let (lhs_value, lhs_size) = lhs!; + let (rhs_value, rhs_size) = rhs!; + Fact(lhs_size == rhs_size, "Angle sizes must be the same"); + let value = (lhs_value + ((1 <<< lhs_size) - rhs_value)) % (1 <<< lhs_size); + new __Angle__ { Value = value, Size = lhs_size } +} + +function __MultiplyAngleByInt__(angle : __Angle__, factor : Int) : __Angle__ { + let (value, size) = angle!; + let value = (value * factor) % (1 <<< size); + new __Angle__ { Value = value, Size = size } +} + +function __MultiplyAngleByBigInt__(angle : __Angle__, factor : BigInt) : __Angle__ { + let (value, size) = angle!; + let value : BigInt = Std.Convert.IntAsBigInt(value); + let value = (value * factor) % Std.Convert.IntAsBigInt(1 <<< size); + let value = Std.Convert.BoolArrayAsInt(Std.Convert.BigIntAsBoolArray(value, size)); + new __Angle__ { Value = value, Size = size } +} + +function __DivideAngleByAngle__(lhs : __Angle__, rhs : __Angle__) : Int { + let (lhs_value, lhs_size) = lhs!; + let (rhs_value, rhs_size) = rhs!; + Fact(lhs_size == rhs_size, "Angle sizes must be the same"); + let value = lhs_value / rhs_value; + value +} + +function __DivideAngleByInt__(angle : __Angle__, divisor : Int) : __Angle__ { + let (value, size) = angle!; + let value = value / divisor; + new __Angle__ { Value = value, Size = size } +} + +function __NegAngle__(angle : __Angle__) : __Angle__ { + let (value, size) = angle!; + let value = (1 <<< size) - value; + new __Angle__ { Value = value, Size = size } +} + +// not exported +function RoundHalfAwayFromZero(value : Double) : Int { + let roundedValue = Microsoft.Quantum.Math.Round(value); + let EPSILON = 2.2204460492503131e-16; + let diff = Std.Math.AbsD(value - Std.Convert.IntAsDouble(roundedValue)); + if (Std.Math.AbsD(diff - 0.5) < EPSILON) { + if (value > 0.0) { + return roundedValue + 1; + } else { + return roundedValue - 1; + } + } else { + return roundedValue; + } +} + diff --git a/compiler/qsc_qasm3/src/stdlib/QasmStd/src/QasmStd/Convert.qs b/compiler/qsc_qasm3/src/stdlib/QasmStd/src/QasmStd/Convert.qs new file mode 100644 index 0000000000..eec43ab655 --- /dev/null +++ b/compiler/qsc_qasm3/src/stdlib/QasmStd/src/QasmStd/Convert.qs @@ -0,0 +1,100 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +import Std.Math.AbsI; + +/// The POW function is used to implement the `pow` modifier in QASM3 for integers. +operation __Pow__<'T>(N: Int, op: ('T => Unit is Adj + Ctl), target : 'T) : Unit { + let op = if N > 0 { () => op(target) } else { () => Adjoint op(target) }; + for _ in 1..AbsI(N) { + op() + } +} + +/// The ``BARRIER`` function is used to implement the `barrier` statement in QASM3. +/// The `@SimulatableIntrinsic` attribute is used to mark the operation for QIR +/// generation. +/// Q# doesn't support barriers, so this is a no-op. We need to figure out what +/// barriers mean in the context of QIR in the future for better support. +@SimulatableIntrinsic() +operation __quantum__qis__barrier__body() : Unit {} + + +/// The ``BOOL_AS_RESULT`` function is used to implement the cast expr in QASM3 for bool to bit. +/// This already exists in the Q# library, but is defined as a marker for casts from QASM3. +function __BoolAsResult__(input: Bool) : Result { + Microsoft.Quantum.Convert.BoolAsResult(input) +} + +/// The ``BOOL_AS_INT`` function is used to implement the cast expr in QASM3 for bool to int. +function __BoolAsInt__(value: Bool) : Int { + if value { + 1 + } else { + 0 + } +} + +/// The ``BOOL_AS_BIGINT`` function is used to implement the cast expr in QASM3 for bool to big int. + +function __BoolAsBigInt__(value: Bool) : BigInt { + if value { + 1L + } else { + 0L + } +} + +/// The ``BOOL_AS_DOUBLE`` function is used to implement the cast expr in QASM3 for bool to int. + +function __BoolAsDouble__(value: Bool) : Double { + if value { + 1. + } else { + 0. + } +} + +/// The ``RESULT_AS_BOOL`` function is used to implement the cast expr in QASM3 for bit to bool. +/// This already exists in the Q# library, but is defined as a marker for casts from QASM3. +function __ResultAsBool__(input: Result) : Bool { + Microsoft.Quantum.Convert.ResultAsBool(input) +} + +/// The ``RESULT_AS_INT`` function is used to implement the cast expr in QASM3 for bit to bool. +function __ResultAsInt__(input: Result) : Int { + if Microsoft.Quantum.Convert.ResultAsBool(input) { + 1 + } else { + 0 + } +} + +/// The ``RESULT_AS_BIGINT`` function is used to implement the cast expr in QASM3 for bit to bool. +function __ResultAsBigInt__(input: Result) : BigInt { + if Microsoft.Quantum.Convert.ResultAsBool(input) { + 1L + } else { + 0L + } +} + +/// The ``INT_AS_RESULT_ARRAY_BE`` function is used to implement the cast expr in QASM3 for int to bit[]. +/// with big-endian order. This is needed for round-trip conversion for bin ops. +function __IntAsResultArrayBE__(number : Int, bits : Int) : Result[] { + mutable runningValue = number; + mutable result = []; + for _ in 1..bits { + set result += [__BoolAsResult__((runningValue &&& 1) != 0)]; + set runningValue >>>= 1; + } + Microsoft.Quantum.Arrays.Reversed(result) +} + +/// The ``RESULT_ARRAY_AS_INT_BE`` function is used to implement the cast expr in QASM3 for bit[] to uint. +/// with big-endian order. This is needed for round-trip conversion for bin ops. +function __ResultArrayAsIntBE__(results : Result[]) : Int { + Microsoft.Quantum.Convert.ResultArrayAsInt(Microsoft.Quantum.Arrays.Reversed(results)) +} + +export __Pow__, __quantum__qis__barrier__body, __BoolAsResult__, __BoolAsInt__, __BoolAsBigInt__, __BoolAsDouble__, __ResultAsBool__, __ResultAsInt__, __ResultAsBigInt__, __IntAsResultArrayBE__, __ResultArrayAsIntBE__, ; diff --git a/compiler/qsc_qasm3/src/stdlib/QasmStd/src/QasmStd/Intrinsic.qs b/compiler/qsc_qasm3/src/stdlib/QasmStd/src/QasmStd/Intrinsic.qs new file mode 100644 index 0000000000..8c99eb8374 --- /dev/null +++ b/compiler/qsc_qasm3/src/stdlib/QasmStd/src/QasmStd/Intrinsic.qs @@ -0,0 +1,150 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +export gphase, U, p, x, y, z, h, s, sdg, t, tdg, sx, rx, ry, rz, cx, cp, swap, ccx, cu, phase, id, u1, u2, u3; + +import Angle.*; + +import Std.Intrinsic.*; + + +operation gphase(theta : __Angle__) : Unit is Adj + Ctl { + body ... { + Exp([], __AngleAsDouble__(theta), []) + } + adjoint auto; + controlled auto; + controlled adjoint auto; +} + +operation U(theta : __Angle__, phi : __Angle__, lambda : __Angle__, qubit : Qubit) : Unit is Adj + Ctl { + body ... { + let theta = __AngleAsDouble__(theta); + let phi = __AngleAsDouble__(phi); + let lambda = __AngleAsDouble__(lambda); + + Rz(lambda, qubit); + Ry(theta, qubit); + Rz(phi, qubit); + R(PauliI, -lambda - phi - theta, qubit); + } + adjoint auto; + controlled auto; + controlled adjoint auto; +} + +operation p(lambda : __Angle__, qubit : Qubit) : Unit is Adj + Ctl { + Controlled gphase([qubit], lambda); +} + +operation x(qubit : Qubit) : Unit is Adj + Ctl { + X(qubit); +} + +operation y(qubit : Qubit) : Unit is Adj + Ctl { + Y(qubit); +} + +operation z(qubit : Qubit) : Unit is Adj + Ctl { + Z(qubit); +} + +operation h(qubit : Qubit) : Unit is Adj + Ctl { + H(qubit); +} + +operation s(qubit : Qubit) : Unit is Adj + Ctl { + S(qubit); +} + +operation sdg(qubit : Qubit) : Unit is Adj + Ctl { + Adjoint S(qubit); +} + +operation t(qubit : Qubit) : Unit is Adj + Ctl { + T(qubit); +} + +operation tdg(qubit : Qubit) : Unit is Adj + Ctl { + Adjoint T(qubit); +} + +operation sx(qubit : Qubit) : Unit is Adj + Ctl { + Rx(Std.Math.PI() / 2., qubit); +} + +operation rx(theta : __Angle__, qubit : Qubit) : Unit is Adj + Ctl { + let theta = __AngleAsDouble__(theta); + Rx(theta, qubit); +} + +operation ry(theta : __Angle__, qubit : Qubit) : Unit is Adj + Ctl { + let theta = __AngleAsDouble__(theta); + Ry(theta, qubit); +} + +operation rz(theta : __Angle__, qubit : Qubit) : Unit is Adj + Ctl { + let theta = __AngleAsDouble__(theta); + Rz(theta, qubit); +} + +operation cx(ctrl : Qubit, qubit : Qubit) : Unit is Adj + Ctl { + CNOT(ctrl, qubit); +} + +operation cp(lambda : __Angle__, ctrl : Qubit, qubit : Qubit) : Unit is Adj + Ctl { + Controlled p([ctrl], (lambda, qubit)); +} + +operation swap(qubit1 : Qubit, qubit2 : Qubit) : Unit is Adj + Ctl { + SWAP(qubit1, qubit2); +} + +operation ccx(ctrl1 : Qubit, ctrl2 : Qubit, target : Qubit) : Unit is Adj + Ctl { + CCNOT(ctrl1, ctrl2, target); +} + +operation cu(theta : __Angle__, phi : __Angle__, lambda : __Angle__, gamma : __Angle__, qubit1 : Qubit, qubit2 : Qubit) : Unit is Adj + Ctl { + p(__SubtractAngles__(gamma, __DivideAngleByInt__(theta, 2)), qubit1); + Controlled U([qubit2], (theta, phi, lambda, qubit1)); +} + +// Gates for OpenQASM 2 backwards compatibility +operation phase(lambda : __Angle__, qubit : Qubit) : Unit is Adj + Ctl { + U(__DoubleAsAngle__(0., 1), __DoubleAsAngle__(0., 1), lambda, qubit); +} + +operation id(qubit : Qubit) : Unit is Adj + Ctl { + I(qubit) +} + +operation u1(lambda : __Angle__, qubit : Qubit) : Unit is Adj + Ctl { + U(__DoubleAsAngle__(0., 1), __DoubleAsAngle__(0., 1), lambda, qubit); +} + +operation u2(phi : __Angle__, lambda : __Angle__, qubit : Qubit) : Unit is Adj + Ctl { + let half_pi = __DivideAngleByInt__(__DoubleAsAngle__(Std.Math.PI(), 53), 2); + + gphase(__NegAngle__(__DivideAngleByInt__(__AddAngles__( + phi, + __AddAngles__( + lambda, + half_pi + ) + ), 2))); + + U(half_pi, phi, lambda, qubit); +} + +operation u3(theta : __Angle__, phi : __Angle__, lambda : __Angle__, qubit : Qubit) : Unit is Adj + Ctl { + gphase(__NegAngle__(__DivideAngleByInt__(__AddAngles__( + phi, + __AddAngles__( + lambda, + theta + ) + ), 2))); + + U(theta, phi, lambda, qubit); +} + diff --git a/compiler/qsc_qasm3/src/angle.rs b/compiler/qsc_qasm3/src/stdlib/angle.rs similarity index 57% rename from compiler/qsc_qasm3/src/angle.rs rename to compiler/qsc_qasm3/src/stdlib/angle.rs index 44de3cf37a..63bd90463b 100644 --- a/compiler/qsc_qasm3/src/angle.rs +++ b/compiler/qsc_qasm3/src/stdlib/angle.rs @@ -7,37 +7,75 @@ pub(crate) mod tests; use num_bigint::BigInt; use crate::oqasm_helpers::safe_u64_to_f64; +use core::f64; use std::convert::TryInto; -use std::f64::consts::PI; +use std::f64::consts::TAU; use std::fmt; -use std::ops::{Add, AddAssign, Div, DivAssign, Mul, MulAssign, Neg, Sub, SubAssign}; +use std::ops::{ + Add, AddAssign, BitAnd, BitOr, BitXor, Div, DivAssign, Mul, MulAssign, Neg, Not, Shl, Shr, Sub, + SubAssign, +}; /// A fixed-point angle type with a specified number of bits. -#[derive(Debug, Clone, Copy, PartialEq, Eq)] -struct Angle { - value: u64, - size: u32, +#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)] +pub struct Angle { + pub value: u64, + pub size: u32, } #[allow(dead_code)] impl Angle { - fn new(value: u64, size: u32) -> Self { + pub fn new(value: u64, size: u32) -> Self { Angle { value, size } } - fn from_f64(val: f64, size: u32) -> Self { + pub fn from_f64_maybe_sized(val: f64, size: Option) -> Angle { + Self::from_f64_sized(val, size.unwrap_or(f64::MANTISSA_DIGITS)) + } + + /// Takes an `f64` representing angle and: + /// 1. Wraps it around so that it is in the range [0, TAU). + /// 2. Encodes it as a binary number between 0 and (1 << size) - 1. + pub fn from_f64_sized(mut val: f64, size: u32) -> Angle { + // First, we need to convert the angle to the `[0, TAU)` range. + val %= TAU; + + // The modulus operator leaves negative numbers as negative. + // So, in this case we need to add an extra `TAU`. + if val < 0. { + val += TAU; + } + + // If the size is > f64::MANTISSA_DIGITS, the cast to f64 + // on the next lines will loose precission. + if size > f64::MANTISSA_DIGITS { + return Self::from_f64_sized_edge_case(val, size); + } + #[allow(clippy::cast_precision_loss)] - let factor = (2.0 * PI) / (1u64 << size) as f64; + let factor = TAU / (1u64 << size) as f64; #[allow(clippy::cast_possible_truncation)] #[allow(clippy::cast_sign_loss)] let value = (val / factor).round() as u64; - Angle { value, size } + Angle::new(value, size) + } + + /// This function handles the edge case when size > `f64::MANTISSA_DIGITS`. + fn from_f64_sized_edge_case(val: f64, size: u32) -> Angle { + let angle = Self::from_f64_sized(val, f64::MANTISSA_DIGITS); + angle.cast(size, false) } fn to_bitstring(self) -> String { format!("{:0width$b}", self.value, width = self.size as usize) } + pub fn cast_to_maybe_sized(self, new_size: Option) -> Angle { + match new_size { + Some(size) => self.cast(size, false), + None => self, + } + } fn cast(&self, new_size: u32, truncate: bool) -> Self { match new_size.cmp(&self.size) { std::cmp::Ordering::Less => { @@ -79,6 +117,91 @@ impl Angle { } } +impl Default for Angle { + fn default() -> Self { + Self { + value: 0, + size: f64::MANTISSA_DIGITS, + } + } +} + +// Bit shift +impl Shl for Angle { + type Output = Self; + + fn shl(self, rhs: i64) -> Self::Output { + let mask = (1 << self.size) - 1; + Self { + value: (self.value << rhs) & mask, + size: self.size, + } + } +} + +impl Shr for Angle { + type Output = Self; + + fn shr(self, rhs: i64) -> Self::Output { + Self { + value: self.value >> rhs, + size: self.size, + } + } +} + +// Bitwise + +impl Not for Angle { + type Output = Self; + + fn not(self) -> Self::Output { + let mask = (1 << self.size) - 1; + Self { + value: !self.value & mask, + size: self.size, + } + } +} + +impl BitAnd for Angle { + type Output = Self; + + fn bitand(self, rhs: Self) -> Self::Output { + assert_eq!(self.size, rhs.size); + Self { + value: self.value & rhs.value, + size: self.size, + } + } +} + +impl BitOr for Angle { + type Output = Self; + + fn bitor(self, rhs: Self) -> Self::Output { + assert_eq!(self.size, rhs.size); + Self { + value: self.value | rhs.value, + size: self.size, + } + } +} + +impl BitXor for Angle { + type Output = Self; + + fn bitxor(self, rhs: Self) -> Self::Output { + assert_eq!(self.size, rhs.size); + Self { + value: self.value ^ rhs.value, + size: self.size, + } + } +} + +// Arithmetic + impl Add for Angle { type Output = Self; @@ -185,17 +308,26 @@ impl DivAssign for Angle { impl TryInto for Angle { type Error = &'static str; + /// Angle to float cast is not allowed in QASM3. + /// This function is only meant to be used in unit tests. fn try_into(self) -> Result { if self.size > 64 { return Err("Size exceeds 64 bits"); } + + // Edge case handling. + if self.size > f64::MANTISSA_DIGITS { + let angle = self.cast(f64::MANTISSA_DIGITS, false); + return angle.try_into(); + } + let Some(denom) = safe_u64_to_f64(1u64 << self.size) else { return Err("Denominator is too large"); }; let Some(value) = safe_u64_to_f64(self.value) else { return Err("Value is too large"); }; - let factor = (2.0 * PI) / denom; + let factor = TAU / denom; Ok(value * factor) } } diff --git a/compiler/qsc_qasm3/src/angle/tests.rs b/compiler/qsc_qasm3/src/stdlib/angle/tests.rs similarity index 59% rename from compiler/qsc_qasm3/src/angle/tests.rs rename to compiler/qsc_qasm3/src/stdlib/angle/tests.rs index 783d8dc3e2..9c1142b8e0 100644 --- a/compiler/qsc_qasm3/src/angle/tests.rs +++ b/compiler/qsc_qasm3/src/stdlib/angle/tests.rs @@ -5,15 +5,46 @@ #![allow(clippy::unreadable_literal)] use std::convert::TryInto; -use std::f64::consts::PI; +use std::f64::consts::{PI, TAU}; use super::Angle; +#[test] +fn test_angle_domain() { + let angle_0000 = Angle::from_f64_sized(0.0, 4); + let angle_0001 = Angle::from_f64_sized(PI / 8.0, 4); + let angle_0010 = Angle::from_f64_sized(PI / 4.0, 4); + let angle_0100 = Angle::from_f64_sized(PI / 2.0, 4); + let angle_1000 = Angle::from_f64_sized(PI, 4); + + assert_eq!(angle_0000.to_bitstring(), "0000"); + assert_eq!(angle_0001.to_bitstring(), "0001"); + assert_eq!(angle_0010.to_bitstring(), "0010"); + assert_eq!(angle_0100.to_bitstring(), "0100"); + assert_eq!(angle_1000.to_bitstring(), "1000"); +} + +#[test] +fn tau_wraps_around() { + let angle_0 = Angle::from_f64_sized(0.0, 4); + let angle_tau = Angle::from_f64_sized(TAU, 4); + assert_eq!(angle_0.to_bitstring(), "0000"); + assert_eq!(angle_tau.to_bitstring(), "0000"); +} + +#[test] +fn angle_float_invariant() { + let angle = Angle::from_f64_sized(PI, 4); + assert_eq!(angle.to_bitstring(), "1000"); + let pi: f64 = angle.try_into().unwrap(); + assert!(dbg!((pi - PI).abs()) <= f64::EPSILON); +} + #[test] fn test_angle() { - let angle1 = Angle::from_f64(PI, 4); - let angle2 = Angle::from_f64(PI / 2.0, 6); - let angle3 = Angle::from_f64(7.0 * (PI / 8.0), 8); + let angle1 = Angle::from_f64_sized(PI, 4); + let angle2 = Angle::from_f64_sized(PI / 2.0, 6); + let angle3 = Angle::from_f64_sized(7.0 * (PI / 8.0), 8); assert_eq!(angle1.to_bitstring(), "1000"); assert_eq!(angle2.to_bitstring(), "010000"); @@ -22,132 +53,132 @@ fn test_angle() { #[test] fn test_angle_creation() { - let angle = Angle::from_f64(PI, 4); + let angle = Angle::from_f64_sized(PI, 4); assert_eq!(angle.value, 8); assert_eq!(angle.size, 4); } #[test] fn test_angle_addition() { - let angle1 = Angle::from_f64(PI / 2.0, 4); - let angle2 = Angle::from_f64(PI / 2.0, 4); + let angle1 = Angle::from_f64_sized(PI / 2.0, 4); + let angle2 = Angle::from_f64_sized(PI / 2.0, 4); let result = angle1 + angle2; assert_eq!(result.value, 8); let angle: f64 = result.try_into().unwrap(); - assert!((angle - PI).abs() < f64::EPSILON); + assert!((angle - PI).abs() <= f64::EPSILON); } #[test] fn test_angle_multiplication() { - let angle = Angle::from_f64(PI / 4.0, 4); + let angle = Angle::from_f64_sized(PI / 4.0, 4); let result: Angle = angle * 2u64; assert_eq!(result.value, 4); let angle: f64 = result.try_into().unwrap(); - assert!((angle - PI / 2.0).abs() < f64::EPSILON); + assert!((angle - PI / 2.0).abs() <= f64::EPSILON); } #[test] fn test_angle_multiplication_bigint() { - let angle = Angle::from_f64(PI / 4.0, 4); + let angle = Angle::from_f64_sized(PI / 4.0, 4); let result: Angle = angle * 18446744073709551616u128; assert_eq!(result.value, 0); let angle: f64 = result.try_into().unwrap(); - assert!((angle - 0.0).abs() < f64::EPSILON); + assert!((angle - 0.0).abs() <= f64::EPSILON); } #[test] fn test_angle_multiplication_bigint2() { - let angle = Angle::from_f64(PI / 4.0, 4); + let angle = Angle::from_f64_sized(PI / 4.0, 4); let result: Angle = angle * 9223372036854775806u128; assert_eq!(result.value, 12); let angle: f64 = result.try_into().unwrap(); - assert!((angle - ((3. * PI) / 2.)).abs() < f64::EPSILON); + assert!((angle - ((3. * PI) / 2.)).abs() <= f64::EPSILON); } #[test] fn test_angle_division_int() { - let angle = Angle::from_f64(PI / 2.0, 4); + let angle = Angle::from_f64_sized(PI / 2.0, 4); let result = angle / 2; assert_eq!(result.value, 2); let angle: f64 = result.try_into().unwrap(); - assert!((angle - PI / 4.0).abs() < f64::EPSILON); + assert!((angle - PI / 4.0).abs() <= f64::EPSILON); } #[test] fn test_angle_division_by_angle() { - let angle1 = Angle::from_f64(PI, 4); - let angle2 = Angle::from_f64(PI / 4.0, 4); + let angle1 = Angle::from_f64_sized(PI, 4); + let angle2 = Angle::from_f64_sized(PI / 4.0, 4); let result = angle1 / angle2; assert_eq!(result, 4); } #[test] fn test_angle_unary_negation() { - let angle = Angle::from_f64(PI / 4.0, 4); + let angle = Angle::from_f64_sized(PI / 4.0, 4); let result = -angle; // "0010" assert_eq!(result.value, 14); // 7*(pi/4) │ "1110" } #[test] fn test_angle_compound_addition() { - let mut angle1 = Angle::from_f64(PI / 2.0, 4); - let angle2 = Angle::from_f64(PI / 2.0, 4); + let mut angle1 = Angle::from_f64_sized(PI / 2.0, 4); + let angle2 = Angle::from_f64_sized(PI / 2.0, 4); angle1 += angle2; assert_eq!(angle1.value, 8); let angle: f64 = angle1.try_into().unwrap(); - assert!((angle - PI).abs() < f64::EPSILON); + assert!((angle - PI).abs() <= f64::EPSILON); } #[test] fn test_angle_compound_subtraction() { - let mut angle1 = Angle::from_f64(PI, 4); - let angle2 = Angle::from_f64(PI / 2.0, 4); + let mut angle1 = Angle::from_f64_sized(PI, 4); + let angle2 = Angle::from_f64_sized(PI / 2.0, 4); angle1 -= angle2; assert_eq!(angle1.value, 4); let angle: f64 = angle1.try_into().unwrap(); - assert!((angle - PI / 2.0).abs() < f64::EPSILON); + assert!((angle - PI / 2.0).abs() <= f64::EPSILON); } #[test] fn test_angle_compound_multiplication() { - let mut angle = Angle::from_f64(PI / 4.0, 4); + let mut angle = Angle::from_f64_sized(PI / 4.0, 4); angle *= 2; assert_eq!(angle.value, 4); let angle: f64 = angle.try_into().unwrap(); - assert!((angle - PI / 2.0).abs() < f64::EPSILON); + assert!((angle - PI / 2.0).abs() <= f64::EPSILON); } #[test] fn test_angle_compound_division() { - let mut angle = Angle::from_f64(PI / 2.0, 4); + let mut angle = Angle::from_f64_sized(PI / 2.0, 4); angle /= 2; assert_eq!(angle.value, 2); let angle: f64 = angle.try_into().unwrap(); - assert!((angle - PI / 4.0).abs() < f64::EPSILON); + assert!((angle - PI / 4.0).abs() <= f64::EPSILON); } #[test] fn test_angle_bitstring() { - let angle = Angle::from_f64(PI, 4); + let angle = Angle::from_f64_sized(PI, 4); assert_eq!(angle.to_bitstring(), "1000"); } #[test] fn test_angle_try_into_f64() { - let angle: Angle = Angle::from_f64(PI, 4); + let angle: Angle = Angle::from_f64_sized(PI, 4); let angle_f64: f64 = angle.try_into().unwrap(); - assert!((angle_f64 - PI).abs() < f64::EPSILON); + assert!((angle_f64 - PI).abs() <= f64::EPSILON); } #[test] fn test_angle_display() { - let angle = Angle::from_f64(PI, 4); + let angle = Angle::from_f64_sized(PI, 4); assert_eq!(format!("{angle}"), format!("{PI}")); } #[test] fn from_f64_round_to_the_nearest_ties_to_even() { - let angle = Angle::from_f64(2.0 * PI * (127. / 512.), 8); + let angle = Angle::from_f64_sized(2.0 * PI * (127. / 512.), 8); // 00111111 is equally close, but even rounds to 01000000 assert_eq!(angle.to_bitstring(), "01000000"); } @@ -178,7 +209,7 @@ fn test_angle_cast_round_padding() { assert!( (TryInto::::try_into(angle).unwrap() - TryInto::::try_into(new_angle).unwrap()) .abs() - < f64::EPSILON + <= f64::EPSILON ); } diff --git a/compiler/qsc_qasm3/src/stdlib/compile.rs b/compiler/qsc_qasm3/src/stdlib/compile.rs new file mode 100644 index 0000000000..af801d567a --- /dev/null +++ b/compiler/qsc_qasm3/src/stdlib/compile.rs @@ -0,0 +1,149 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +use miette::Report; + +use qsc_data_structures::{language_features::LanguageFeatures, target::TargetCapabilityFlags}; +use qsc_frontend::{ + compile::{compile, CompileUnit, PackageStore, SourceContents, SourceMap, SourceName}, + error::WithSource, +}; +use qsc_hir::hir::PackageId; +use qsc_passes::{run_core_passes, run_default_passes, PackageType}; + +#[test] +fn compiles_with_base_profile() { + let _ = package_store_with_qasm(TargetCapabilityFlags::empty()); +} + +#[must_use] +pub fn package_store_with_qasm( + capabilities: TargetCapabilityFlags, +) -> ( + qsc_hir::hir::PackageId, + qsc_hir::hir::PackageId, + PackageStore, +) { + let (std_package_id, mut store) = package_store_with_stdlib(capabilities); + let mut unit = compile_qasm_std(&store, std_package_id, capabilities); + + let pass_errors = run_default_passes(store.core(), &mut unit, PackageType::Lib); + if pass_errors.is_empty() { + //unit.expose(); + let package_id = store.insert(unit); + (std_package_id, package_id, store) + } else { + for error in pass_errors { + let report = Report::new(WithSource::from_map(&unit.sources, error)); + eprintln!("{report:?}"); + } + + panic!("could not compile qasm standard library") + } +} + +pub const OPENQASM_LIBRARY_URI_SCHEME: &str = "openqasm-library-source"; + +pub const STD_LIB: &[(&str, &str)] = &[ + ( + "openqasm-library-source:QasmStd/Angle.qs", + include_str!("QasmStd/src/QasmStd/Angle.qs"), + ), + ( + "openqasm-library-source:QasmStd/Convert.qs", + include_str!("QasmStd/src/QasmStd/Convert.qs"), + ), + ( + "openqasm-library-source:QasmStd/Intrinsic.qs", + include_str!("QasmStd/src/QasmStd/Intrinsic.qs"), + ), +]; + +/// Compiles the standard library. +/// +/// # Panics +/// +/// Panics if the standard library does not compile without errors. +#[must_use] +pub fn compile_qasm_std( + store: &PackageStore, + std_id: PackageId, + capabilities: TargetCapabilityFlags, +) -> CompileUnit { + let std: Vec<(SourceName, SourceContents)> = STD_LIB + .iter() + .map(|(name, contents)| ((*name).into(), (*contents).into())) + .collect(); + let sources = SourceMap::new(std, None); + + let mut unit = compile( + store, + &[(PackageId::CORE, None), (std_id, None)], + sources, + capabilities, + LanguageFeatures::default(), + ); + assert_no_errors(&unit.sources, &mut unit.errors); + unit +} + +fn assert_no_errors(sources: &SourceMap, errors: &mut Vec) { + if !errors.is_empty() { + for error in errors.drain(..) { + eprintln!("{:?}", Report::new(WithSource::from_map(sources, error))); + } + + panic!("could not compile package"); + } +} + +#[must_use] +pub fn package_store_with_stdlib( + capabilities: TargetCapabilityFlags, +) -> (qsc_hir::hir::PackageId, PackageStore) { + let mut store = PackageStore::new(core()); + let std_id = store.insert(std(&store, capabilities)); + (std_id, store) +} + +/// Compiles the core library. +/// +/// # Panics +/// +/// Panics if the core library compiles with errors. +#[must_use] +fn core() -> CompileUnit { + let mut unit = qsc_frontend::compile::core(); + let pass_errors = run_core_passes(&mut unit); + if pass_errors.is_empty() { + unit + } else { + for error in pass_errors { + let report = Report::new(WithSource::from_map(&unit.sources, error)); + eprintln!("{report:?}"); + } + + panic!("could not compile core library") + } +} + +/// Compiles the standard library. +/// +/// # Panics +/// +/// Panics if the standard library does not compile without errors. +#[must_use] +fn std(store: &PackageStore, capabilities: TargetCapabilityFlags) -> CompileUnit { + let mut unit = qsc_frontend::compile::std(store, capabilities); + let pass_errors = run_default_passes(store.core(), &mut unit, PackageType::Lib); + if pass_errors.is_empty() { + unit + } else { + for error in pass_errors { + let report = Report::new(WithSource::from_map(&unit.sources, error)); + eprintln!("{report:?}"); + } + + panic!("could not compile standard library") + } +} diff --git a/compiler/qsc_qasm3/src/tests.rs b/compiler/qsc_qasm3/src/tests.rs index c98591dac4..e47f0d4a30 100644 --- a/compiler/qsc_qasm3/src/tests.rs +++ b/compiler/qsc_qasm3/src/tests.rs @@ -1,16 +1,16 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. -use crate::runtime::RuntimeFunctions; -use crate::semantic::symbols::SymbolTable; +use crate::stdlib::compile::package_store_with_qasm; use crate::{CompilerConfig, OutputSemantics, ProgramType, QasmCompileUnit, QubitSemantics}; use miette::Report; use qsc::interpret::Error; use qsc::{ ast::{mut_visit::MutVisitor, Package, Stmt, TopLevelNode}, target::Profile, - PackageStore, SourceMap, Span, + SourceMap, Span, }; +use qsc_hir::hir::PackageId; use std::{path::Path, sync::Arc}; use crate::io::{InMemorySourceResolver, SourceResolver}; @@ -54,11 +54,13 @@ pub(crate) fn generate_qir_from_ast( source_map: SourceMap, profile: Profile, ) -> Result> { - let mut store = PackageStore::new(qsc::compile::core()); - let mut dependencies = Vec::new(); let capabilities = profile.into(); - dependencies.push((store.insert(qsc::compile::std(&store, capabilities)), None)); - + let (stdid, qasmid, mut store) = package_store_with_qasm(capabilities); + let dependencies = vec![ + (PackageId::CORE, None), + (stdid, None), + (qasmid, Some("QasmStd".into())), + ]; qsc::codegen::qir::get_qir_from_ast( &mut store, &dependencies, @@ -102,7 +104,6 @@ where source_map: res.source_map, config, stmts: vec![], - runtime: RuntimeFunctions::empty(), symbols: res.symbols, errors: res.errors, }; @@ -175,8 +176,7 @@ where source_map: res.source_map, config, stmts: vec![], - runtime: RuntimeFunctions::empty(), - symbols: SymbolTable::default(), + symbols: res.symbols, errors: res.errors, }; @@ -346,12 +346,23 @@ pub fn compile_qasm_stmt_to_qsharp_with_semantics( let Some(package) = unit.package else { panic!("Expected package, got None"); }; - let qsharp = get_first_statement_as_qsharp(&package); + let qsharp = get_last_statement_as_qsharp(&package); Ok(qsharp) } +fn get_last_statement_as_qsharp(package: &Package) -> String { + let qsharp = match package.nodes.iter().last() { + Some(i) => match i { + TopLevelNode::Namespace(_) => panic!("Expected Stmt, got Namespace"), + TopLevelNode::Stmt(stmt) => gen_qsharp_stmt(stmt.as_ref()), + }, + None => panic!("Expected Stmt, got None"), + }; + qsharp +} + fn get_first_statement_as_qsharp(package: &Package) -> String { - let qsharp = match package.nodes.first() { + let qsharp = match package.nodes.get(1) { Some(i) => match i { TopLevelNode::Namespace(_) => panic!("Expected Stmt, got Namespace"), TopLevelNode::Stmt(stmt) => gen_qsharp_stmt(stmt.as_ref()), diff --git a/compiler/qsc_qasm3/src/tests/declaration.rs b/compiler/qsc_qasm3/src/tests/declaration.rs index 1c189a4482..c23f0af07d 100644 --- a/compiler/qsc_qasm3/src/tests/declaration.rs +++ b/compiler/qsc_qasm3/src/tests/declaration.rs @@ -35,7 +35,6 @@ fn classical() -> miette::Result<(), Vec> { qubit q2; bit[4] b1 = "0100"; bit[8] b2 = "1001_0100"; - bit b3 = "1"; bool i = true; bool j = false; const float[64] k = 5.5e3; diff --git a/compiler/qsc_qasm3/src/tests/declaration/gate.rs b/compiler/qsc_qasm3/src/tests/declaration/gate.rs index de4614d2d8..7795720a1b 100644 --- a/compiler/qsc_qasm3/src/tests/declaration/gate.rs +++ b/compiler/qsc_qasm3/src/tests/declaration/gate.rs @@ -18,7 +18,7 @@ fn single_qubit() -> miette::Result<(), Vec> { expect![ r#" let my_h : (Qubit) => Unit = (q) => { - H(q); + h(q); }; "# ] @@ -40,8 +40,8 @@ fn two_qubits() -> miette::Result<(), Vec> { expect![ r#" let my_h : (Qubit, Qubit) => Unit = (q, q2) => { - H(q2); - H(q); + h(q2); + h(q); }; "# ] @@ -59,13 +59,11 @@ fn single_angle_single_qubit() -> miette::Result<(), Vec> { "#; let qsharp = compile_qasm_stmt_to_qsharp(source)?; - expect![ - r#" - let my_h : (Double, Qubit) => Unit = (θ, q) => { - Rx(θ, q); + expect![[r#" + let my_h : (__Angle__, Qubit) => Unit = (θ, q) => { + rx(θ, q); }; - "# - ] + "#]] .assert_eq(&qsharp); Ok(()) } @@ -81,14 +79,12 @@ fn two_angles_two_qubits() -> miette::Result<(), Vec> { "#; let qsharp = compile_qasm_stmt_to_qsharp(source)?; - expect![ - r#" - let my_h : (Double, Double, Qubit, Qubit) => Unit = (θ, φ, q, q2) => { - Rx(θ, q2); - Ry(φ, q); + expect![[r#" + let my_h : (__Angle__, __Angle__, Qubit, Qubit) => Unit = (θ, φ, q, q2) => { + rx(θ, q2); + ry(φ, q); }; - "# - ] + "#]] .assert_eq(&qsharp); Ok(()) } diff --git a/compiler/qsc_qasm3/src/tests/declaration/io/explicit_input.rs b/compiler/qsc_qasm3/src/tests/declaration/io/explicit_input.rs index 38cd18c777..39c4fe404d 100644 --- a/compiler/qsc_qasm3/src/tests/declaration/io/explicit_input.rs +++ b/compiler/qsc_qasm3/src/tests/declaration/io/explicit_input.rs @@ -13,11 +13,13 @@ input bit[2] c; "#; let qsharp = compile_qasm_to_qsharp_operation(source)?; - expect![ - r#" -operation Test(c : Result[]) : Unit {} -"# - ] + expect![[r#" + operation Test(c : Result[]) : Unit { + import QasmStd.Angle.*; + import QasmStd.Convert.*; + import QasmStd.Intrinsic.*; + } + "#]] .assert_eq(&qsharp); Ok(()) } @@ -30,11 +32,13 @@ input bit c; "#; let qsharp = compile_qasm_to_qsharp_operation(source)?; - expect![ - r#" -operation Test(c : Result) : Unit {} -"# - ] + expect![[r#" + operation Test(c : Result) : Unit { + import QasmStd.Angle.*; + import QasmStd.Convert.*; + import QasmStd.Intrinsic.*; + } + "#]] .assert_eq(&qsharp); Ok(()) } @@ -46,11 +50,13 @@ input bool c; "#; let qsharp = compile_qasm_to_qsharp_operation(source)?; - expect![ - r#" -operation Test(c : Bool) : Unit {} -"# - ] + expect![[r#" + operation Test(c : Bool) : Unit { + import QasmStd.Angle.*; + import QasmStd.Convert.*; + import QasmStd.Intrinsic.*; + } + "#]] .assert_eq(&qsharp); Ok(()) } @@ -62,11 +68,13 @@ input complex[float] c; "#; let qsharp = compile_qasm_to_qsharp_operation(source)?; - expect![ - r#" -operation Test(c : Microsoft.Quantum.Math.Complex) : Unit {} -"# - ] + expect![[r#" + operation Test(c : Microsoft.Quantum.Math.Complex) : Unit { + import QasmStd.Angle.*; + import QasmStd.Convert.*; + import QasmStd.Intrinsic.*; + } + "#]] .assert_eq(&qsharp); Ok(()) } @@ -78,11 +86,13 @@ input float f; "#; let qsharp = compile_qasm_to_qsharp_operation(source)?; - expect![ - r#" -operation Test(f : Double) : Unit {} -"# - ] + expect![[r#" + operation Test(f : Double) : Unit { + import QasmStd.Angle.*; + import QasmStd.Convert.*; + import QasmStd.Intrinsic.*; + } + "#]] .assert_eq(&qsharp); Ok(()) } @@ -94,11 +104,13 @@ input float[60] f; "#; let qsharp = compile_qasm_to_qsharp_operation(source)?; - expect![ - r#" -operation Test(f : Double) : Unit {} -"# - ] + expect![[r#" + operation Test(f : Double) : Unit { + import QasmStd.Angle.*; + import QasmStd.Convert.*; + import QasmStd.Intrinsic.*; + } + "#]] .assert_eq(&qsharp); Ok(()) } @@ -110,11 +122,13 @@ input int i; "#; let qsharp = compile_qasm_to_qsharp_operation(source)?; - expect![ - r#" -operation Test(i : Int) : Unit {} -"# - ] + expect![[r#" + operation Test(i : Int) : Unit { + import QasmStd.Angle.*; + import QasmStd.Convert.*; + import QasmStd.Intrinsic.*; + } + "#]] .assert_eq(&qsharp); Ok(()) } @@ -126,11 +140,13 @@ input int[60] i; "#; let qsharp = compile_qasm_to_qsharp_operation(source)?; - expect![ - r#" -operation Test(i : Int) : Unit {} -"# - ] + expect![[r#" + operation Test(i : Int) : Unit { + import QasmStd.Angle.*; + import QasmStd.Convert.*; + import QasmStd.Intrinsic.*; + } + "#]] .assert_eq(&qsharp); Ok(()) } @@ -142,11 +158,13 @@ input uint i; "#; let qsharp = compile_qasm_to_qsharp_operation(source)?; - expect![ - r#" -operation Test(i : Int) : Unit {} -"# - ] + expect![[r#" + operation Test(i : Int) : Unit { + import QasmStd.Angle.*; + import QasmStd.Convert.*; + import QasmStd.Intrinsic.*; + } + "#]] .assert_eq(&qsharp); Ok(()) } @@ -158,11 +176,13 @@ input uint[60] i; "#; let qsharp = compile_qasm_to_qsharp_operation(source)?; - expect![ - r#" -operation Test(i : Int) : Unit {} -"# - ] + expect![[r#" + operation Test(i : Int) : Unit { + import QasmStd.Angle.*; + import QasmStd.Convert.*; + import QasmStd.Intrinsic.*; + } + "#]] .assert_eq(&qsharp); Ok(()) } @@ -174,11 +194,13 @@ input int[65] i; "#; let qsharp = compile_qasm_to_qsharp_operation(source)?; - expect![ - r#" -operation Test(i : BigInt) : Unit {} -"# - ] + expect![[r#" + operation Test(i : BigInt) : Unit { + import QasmStd.Angle.*; + import QasmStd.Convert.*; + import QasmStd.Intrinsic.*; + } + "#]] .assert_eq(&qsharp); Ok(()) } @@ -213,11 +235,13 @@ input bit[2] b2; "#; let qsharp = compile_qasm_to_qsharp_operation(source)?; - expect![ - r#" -operation Test(bi : BigInt, i : Int, ui : Int, u : Int, f : Double, b : Bool, c : Result, cf : Microsoft.Quantum.Math.Complex, b2 : Result[]) : Unit {} -"# - ] + expect![[r#" + operation Test(bi : BigInt, i : Int, ui : Int, u : Int, f : Double, b : Bool, c : Result, cf : Microsoft.Quantum.Math.Complex, b2 : Result[]) : Unit { + import QasmStd.Angle.*; + import QasmStd.Convert.*; + import QasmStd.Intrinsic.*; + } + "#]] .assert_eq(&qsharp); Ok(()) } diff --git a/compiler/qsc_qasm3/src/tests/declaration/io/explicit_output.rs b/compiler/qsc_qasm3/src/tests/declaration/io/explicit_output.rs index 92e186465e..ce241820d9 100644 --- a/compiler/qsc_qasm3/src/tests/declaration/io/explicit_output.rs +++ b/compiler/qsc_qasm3/src/tests/declaration/io/explicit_output.rs @@ -12,15 +12,16 @@ output bit[2] c; "#; let qsharp = compile_qasm_to_qsharp_operation(source)?; - expect![ - r#" -@EntryPoint() -operation Test() : Result[] { - mutable c = [Zero, Zero]; - c -} -"# - ] + expect![[r#" + @EntryPoint() + operation Test() : Result[] { + import QasmStd.Angle.*; + import QasmStd.Convert.*; + import QasmStd.Intrinsic.*; + mutable c = [Zero, Zero]; + c + } + "#]] .assert_eq(&qsharp); Ok(()) } @@ -32,15 +33,16 @@ output bit c; "#; let qsharp = compile_qasm_to_qsharp_operation(source)?; - expect![ - r#" -@EntryPoint() -operation Test() : Result { - mutable c = Zero; - c -} -"# - ] + expect![[r#" + @EntryPoint() + operation Test() : Result { + import QasmStd.Angle.*; + import QasmStd.Convert.*; + import QasmStd.Intrinsic.*; + mutable c = Zero; + c + } + "#]] .assert_eq(&qsharp); Ok(()) } @@ -52,15 +54,16 @@ output bool c; "#; let qsharp = compile_qasm_to_qsharp_operation(source)?; - expect![ - r#" -@EntryPoint() -operation Test() : Bool { - mutable c = false; - c -} -"# - ] + expect![[r#" + @EntryPoint() + operation Test() : Bool { + import QasmStd.Angle.*; + import QasmStd.Convert.*; + import QasmStd.Intrinsic.*; + mutable c = false; + c + } + "#]] .assert_eq(&qsharp); Ok(()) } @@ -72,15 +75,16 @@ output complex[float] c; "#; let qsharp = compile_qasm_to_qsharp_operation(source)?; - expect![ - r#" -@EntryPoint() -operation Test() : Microsoft.Quantum.Math.Complex { - mutable c = Microsoft.Quantum.Math.Complex(0., 0.); - c -} -"# - ] + expect![[r#" + @EntryPoint() + operation Test() : Microsoft.Quantum.Math.Complex { + import QasmStd.Angle.*; + import QasmStd.Convert.*; + import QasmStd.Intrinsic.*; + mutable c = Microsoft.Quantum.Math.Complex(0., 0.); + c + } + "#]] .assert_eq(&qsharp); Ok(()) } @@ -92,15 +96,16 @@ output float f; "#; let qsharp = compile_qasm_to_qsharp_operation(source)?; - expect![ - r#" -@EntryPoint() -operation Test() : Double { - mutable f = 0.; - f -} -"# - ] + expect![[r#" + @EntryPoint() + operation Test() : Double { + import QasmStd.Angle.*; + import QasmStd.Convert.*; + import QasmStd.Intrinsic.*; + mutable f = 0.; + f + } + "#]] .assert_eq(&qsharp); Ok(()) } @@ -112,15 +117,16 @@ output float[42] f; "#; let qsharp = compile_qasm_to_qsharp_operation(source)?; - expect![ - r#" -@EntryPoint() -operation Test() : Double { - mutable f = 0.; - f -} -"# - ] + expect![[r#" + @EntryPoint() + operation Test() : Double { + import QasmStd.Angle.*; + import QasmStd.Convert.*; + import QasmStd.Intrinsic.*; + mutable f = 0.; + f + } + "#]] .assert_eq(&qsharp); Ok(()) } @@ -132,15 +138,16 @@ output int[42] i; "#; let qsharp = compile_qasm_to_qsharp_operation(source)?; - expect![ - r#" -@EntryPoint() -operation Test() : Int { - mutable i = 0; - i -} -"# - ] + expect![[r#" + @EntryPoint() + operation Test() : Int { + import QasmStd.Angle.*; + import QasmStd.Convert.*; + import QasmStd.Intrinsic.*; + mutable i = 0; + i + } + "#]] .assert_eq(&qsharp); Ok(()) } @@ -152,15 +159,16 @@ output int i; "#; let qsharp = compile_qasm_to_qsharp_operation(source)?; - expect![ - r#" -@EntryPoint() -operation Test() : Int { - mutable i = 0; - i -} -"# - ] + expect![[r#" + @EntryPoint() + operation Test() : Int { + import QasmStd.Angle.*; + import QasmStd.Convert.*; + import QasmStd.Intrinsic.*; + mutable i = 0; + i + } + "#]] .assert_eq(&qsharp); Ok(()) } @@ -172,15 +180,16 @@ output uint i; "#; let qsharp = compile_qasm_to_qsharp_operation(source)?; - expect![ - r#" -@EntryPoint() -operation Test() : Int { - mutable i = 0; - i -} -"# - ] + expect![[r#" + @EntryPoint() + operation Test() : Int { + import QasmStd.Angle.*; + import QasmStd.Convert.*; + import QasmStd.Intrinsic.*; + mutable i = 0; + i + } + "#]] .assert_eq(&qsharp); Ok(()) } @@ -192,15 +201,16 @@ output uint[42] i; "#; let qsharp = compile_qasm_to_qsharp_operation(source)?; - expect![ - r#" -@EntryPoint() -operation Test() : Int { - mutable i = 0; - i -} -"# - ] + expect![[r#" + @EntryPoint() + operation Test() : Int { + import QasmStd.Angle.*; + import QasmStd.Convert.*; + import QasmStd.Intrinsic.*; + mutable i = 0; + i + } + "#]] .assert_eq(&qsharp); Ok(()) } @@ -212,15 +222,16 @@ output int[65] i; "#; let qsharp = compile_qasm_to_qsharp_operation(source)?; - expect![ - r#" -@EntryPoint() -operation Test() : BigInt { - mutable i = 0L; - i -} -"# - ] + expect![[r#" + @EntryPoint() + operation Test() : BigInt { + import QasmStd.Angle.*; + import QasmStd.Convert.*; + import QasmStd.Intrinsic.*; + mutable i = 0; + i + } + "#]] .assert_eq(&qsharp); Ok(()) } @@ -237,7 +248,7 @@ output qubit q; assert!(error[0] .to_string() - .contains("QASM3 Parse Error: Quantum type found in input/output declaration.")); + .contains("expected scalar or array type, found keyword `qubit")); } #[test] @@ -255,23 +266,24 @@ output bit[2] b2; "#; let qsharp = compile_qasm_to_qsharp_operation(source)?; - expect![ - r#" -@EntryPoint() -operation Test() : (BigInt, Int, Int, Int, Double, Bool, Result, Microsoft.Quantum.Math.Complex, Result[]) { - mutable bi = 0L; - mutable i = 0; - mutable ui = 0; - mutable u = 0; - mutable f = 0.; - mutable b = false; - mutable c = Zero; - mutable cf = Microsoft.Quantum.Math.Complex(0., 0.); - mutable b2 = [Zero, Zero]; - (bi, i, ui, u, f, b, c, cf, b2) -} -"# - ] + expect![[r#" + @EntryPoint() + operation Test() : (BigInt, Int, Int, Int, Double, Bool, Result, Microsoft.Quantum.Math.Complex, Result[]) { + import QasmStd.Angle.*; + import QasmStd.Convert.*; + import QasmStd.Intrinsic.*; + mutable bi = 0; + mutable i = 0; + mutable ui = 0; + mutable u = 0; + mutable f = 0.; + mutable b = false; + mutable c = Zero; + mutable cf = Microsoft.Quantum.Math.Complex(0., 0.); + mutable b2 = [Zero, Zero]; + (bi, i, ui, u, f, b, c, cf, b2) + } + "#]] .assert_eq(&qsharp); Ok(()) } diff --git a/compiler/qsc_qasm3/src/tests/declaration/io/implicit_output.rs b/compiler/qsc_qasm3/src/tests/declaration/io/implicit_output.rs index 2a927e45e4..88db764e6d 100644 --- a/compiler/qsc_qasm3/src/tests/declaration/io/implicit_output.rs +++ b/compiler/qsc_qasm3/src/tests/declaration/io/implicit_output.rs @@ -12,15 +12,16 @@ bit[2] c; "#; let qsharp = compile_qasm_to_qsharp_operation(source)?; - expect![ - r#" -@EntryPoint() -operation Test() : Result[] { - mutable c = [Zero, Zero]; - c -} -"# - ] + expect![[r#" + @EntryPoint() + operation Test() : Result[] { + import QasmStd.Angle.*; + import QasmStd.Convert.*; + import QasmStd.Intrinsic.*; + mutable c = [Zero, Zero]; + c + } + "#]] .assert_eq(&qsharp); Ok(()) } @@ -32,15 +33,16 @@ bit c; "#; let qsharp = compile_qasm_to_qsharp_operation(source)?; - expect![ - r#" -@EntryPoint() -operation Test() : Result { - mutable c = Zero; - c -} -"# - ] + expect![[r#" + @EntryPoint() + operation Test() : Result { + import QasmStd.Angle.*; + import QasmStd.Convert.*; + import QasmStd.Intrinsic.*; + mutable c = Zero; + c + } + "#]] .assert_eq(&qsharp); Ok(()) } @@ -52,15 +54,16 @@ bool c; "#; let qsharp = compile_qasm_to_qsharp_operation(source)?; - expect![ - r#" -@EntryPoint() -operation Test() : Bool { - mutable c = false; - c -} -"# - ] + expect![[r#" + @EntryPoint() + operation Test() : Bool { + import QasmStd.Angle.*; + import QasmStd.Convert.*; + import QasmStd.Intrinsic.*; + mutable c = false; + c + } + "#]] .assert_eq(&qsharp); Ok(()) } @@ -72,15 +75,16 @@ complex[float] c; "#; let qsharp = compile_qasm_to_qsharp_operation(source)?; - expect![ - r#" -@EntryPoint() -operation Test() : Microsoft.Quantum.Math.Complex { - mutable c = Microsoft.Quantum.Math.Complex(0., 0.); - c -} -"# - ] + expect![[r#" + @EntryPoint() + operation Test() : Microsoft.Quantum.Math.Complex { + import QasmStd.Angle.*; + import QasmStd.Convert.*; + import QasmStd.Intrinsic.*; + mutable c = Microsoft.Quantum.Math.Complex(0., 0.); + c + } + "#]] .assert_eq(&qsharp); Ok(()) } @@ -92,15 +96,16 @@ float f; "#; let qsharp = compile_qasm_to_qsharp_operation(source)?; - expect![ - r#" -@EntryPoint() -operation Test() : Double { - mutable f = 0.; - f -} -"# - ] + expect![[r#" + @EntryPoint() + operation Test() : Double { + import QasmStd.Angle.*; + import QasmStd.Convert.*; + import QasmStd.Intrinsic.*; + mutable f = 0.; + f + } + "#]] .assert_eq(&qsharp); Ok(()) } @@ -112,15 +117,16 @@ float[42] f; "#; let qsharp = compile_qasm_to_qsharp_operation(source)?; - expect![ - r#" -@EntryPoint() -operation Test() : Double { - mutable f = 0.; - f -} -"# - ] + expect![[r#" + @EntryPoint() + operation Test() : Double { + import QasmStd.Angle.*; + import QasmStd.Convert.*; + import QasmStd.Intrinsic.*; + mutable f = 0.; + f + } + "#]] .assert_eq(&qsharp); Ok(()) } @@ -132,15 +138,16 @@ int[42] i; "#; let qsharp = compile_qasm_to_qsharp_operation(source)?; - expect![ - r#" -@EntryPoint() -operation Test() : Int { - mutable i = 0; - i -} -"# - ] + expect![[r#" + @EntryPoint() + operation Test() : Int { + import QasmStd.Angle.*; + import QasmStd.Convert.*; + import QasmStd.Intrinsic.*; + mutable i = 0; + i + } + "#]] .assert_eq(&qsharp); Ok(()) } @@ -152,15 +159,16 @@ int i; "#; let qsharp = compile_qasm_to_qsharp_operation(source)?; - expect![ - r#" -@EntryPoint() -operation Test() : Int { - mutable i = 0; - i -} -"# - ] + expect![[r#" + @EntryPoint() + operation Test() : Int { + import QasmStd.Angle.*; + import QasmStd.Convert.*; + import QasmStd.Intrinsic.*; + mutable i = 0; + i + } + "#]] .assert_eq(&qsharp); Ok(()) } @@ -172,15 +180,16 @@ uint i; "#; let qsharp = compile_qasm_to_qsharp_operation(source)?; - expect![ - r#" -@EntryPoint() -operation Test() : Int { - mutable i = 0; - i -} -"# - ] + expect![[r#" + @EntryPoint() + operation Test() : Int { + import QasmStd.Angle.*; + import QasmStd.Convert.*; + import QasmStd.Intrinsic.*; + mutable i = 0; + i + } + "#]] .assert_eq(&qsharp); Ok(()) } @@ -192,15 +201,16 @@ uint[42] i; "#; let qsharp = compile_qasm_to_qsharp_operation(source)?; - expect![ - r#" -@EntryPoint() -operation Test() : Int { - mutable i = 0; - i -} -"# - ] + expect![[r#" + @EntryPoint() + operation Test() : Int { + import QasmStd.Angle.*; + import QasmStd.Convert.*; + import QasmStd.Intrinsic.*; + mutable i = 0; + i + } + "#]] .assert_eq(&qsharp); Ok(()) } @@ -212,15 +222,16 @@ int[65] i; "#; let qsharp = compile_qasm_to_qsharp_operation(source)?; - expect![ - r#" -@EntryPoint() -operation Test() : BigInt { - mutable i = 0L; - i -} -"# - ] + expect![[r#" + @EntryPoint() + operation Test() : BigInt { + import QasmStd.Angle.*; + import QasmStd.Convert.*; + import QasmStd.Intrinsic.*; + mutable i = 0; + i + } + "#]] .assert_eq(&qsharp); Ok(()) } @@ -240,23 +251,24 @@ bit[2] b2; "#; let qsharp = compile_qasm_to_qsharp_operation(source)?; - expect![ - r#" -@EntryPoint() -operation Test() : (BigInt, Int, Int, Int, Double, Bool, Result, Microsoft.Quantum.Math.Complex, Result[]) { - mutable bi = 0L; - mutable i = 0; - mutable ui = 0; - mutable u = 0; - mutable f = 0.; - mutable b = false; - mutable c = Zero; - mutable cf = Microsoft.Quantum.Math.Complex(0., 0.); - mutable b2 = [Zero, Zero]; - (bi, i, ui, u, f, b, c, cf, b2) -} -"# - ] + expect![[r#" + @EntryPoint() + operation Test() : (BigInt, Int, Int, Int, Double, Bool, Result, Microsoft.Quantum.Math.Complex, Result[]) { + import QasmStd.Angle.*; + import QasmStd.Convert.*; + import QasmStd.Intrinsic.*; + mutable bi = 0; + mutable i = 0; + mutable ui = 0; + mutable u = 0; + mutable f = 0.; + mutable b = false; + mutable c = Zero; + mutable cf = Microsoft.Quantum.Math.Complex(0., 0.); + mutable b2 = [Zero, Zero]; + (bi, i, ui, u, f, b, c, cf, b2) + } + "#]] .assert_eq(&qsharp); Ok(()) } diff --git a/compiler/qsc_qasm3/src/tests/declaration/unsigned_integer.rs b/compiler/qsc_qasm3/src/tests/declaration/unsigned_integer.rs index 0d4f1ba594..58a93ab28e 100644 --- a/compiler/qsc_qasm3/src/tests/declaration/unsigned_integer.rs +++ b/compiler/qsc_qasm3/src/tests/declaration/unsigned_integer.rs @@ -243,8 +243,8 @@ fn assigning_uint_to_negative_lit_results_in_semantic_error() { let Err(errors) = compile_qasm_stmt_to_qsharp(source) else { panic!("Expected error"); }; - expect![ + expect![[ r#"Cannot assign a value of Negative Int type to a classical variable of UInt(Some(10), True) type."# - ] + ]] .assert_eq(&errors[0].to_string()); } diff --git a/compiler/qsc_qasm3/src/tests/expression/binary.rs b/compiler/qsc_qasm3/src/tests/expression/binary.rs index 533e9323c5..dc676059b5 100644 --- a/compiler/qsc_qasm3/src/tests/expression/binary.rs +++ b/compiler/qsc_qasm3/src/tests/expression/binary.rs @@ -5,6 +5,7 @@ use expect_test::expect; use crate::tests::compile_qasm_to_qsharp; +mod angle; mod arithmetic_conversions; mod comparison; mod complex; diff --git a/compiler/qsc_qasm3/src/tests/expression/binary/angle.rs b/compiler/qsc_qasm3/src/tests/expression/binary/angle.rs new file mode 100644 index 0000000000..42a38fd02f --- /dev/null +++ b/compiler/qsc_qasm3/src/tests/expression/binary/angle.rs @@ -0,0 +1,270 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +use crate::tests::compile_qasm_stmt_to_qsharp; + +use expect_test::expect; +use miette::Report; + +// Bit shift +#[test] +fn shl() -> miette::Result<(), Vec> { + let source = " + angle[32] a = 1.0; + uint b = 2; + angle x = a << b; + "; + + let qsharp = compile_qasm_stmt_to_qsharp(source)?; + expect![[r#" + mutable x = __AngleShl__(a, b); + "#]] + .assert_eq(&qsharp); + Ok(()) +} + +#[test] +fn shr() -> miette::Result<(), Vec> { + let source = " + angle[32] a = 1.0; + uint b = 2; + angle x = a >> b; + "; + + let qsharp = compile_qasm_stmt_to_qsharp(source)?; + expect![[r#" + mutable x = __AngleShr__(a, b); + "#]] + .assert_eq(&qsharp); + Ok(()) +} + +// Bitwise + +#[test] +fn andb() -> miette::Result<(), Vec> { + let source = " + angle[32] a = 1.0; + angle[32] b = 2.0; + angle x = a & b; + "; + + let qsharp = compile_qasm_stmt_to_qsharp(source)?; + expect![[r#" + mutable x = __AngleAndB__(a, b); + "#]] + .assert_eq(&qsharp); + Ok(()) +} + +#[test] +fn orb() -> miette::Result<(), Vec> { + let source = " + angle[32] a = 1.0; + angle[32] b = 2.0; + angle x = a | b; + "; + + let qsharp = compile_qasm_stmt_to_qsharp(source)?; + expect![[r#" + mutable x = __AngleOrB__(a, b); + "#]] + .assert_eq(&qsharp); + Ok(()) +} + +#[test] +fn xorb() -> miette::Result<(), Vec> { + let source = " + angle[32] a = 1.0; + angle[32] b = 2.0; + angle x = a ^ b; + "; + + let qsharp = compile_qasm_stmt_to_qsharp(source)?; + expect![[r#" + mutable x = __AngleXorB__(a, b); + "#]] + .assert_eq(&qsharp); + Ok(()) +} + +// Comparison + +#[test] +fn eq() -> miette::Result<(), Vec> { + let source = " + angle[32] a = 1.0; + angle[32] b = 2.0; + bool x = a == b; + "; + + let qsharp = compile_qasm_stmt_to_qsharp(source)?; + expect![[r#" + mutable x = __AngleEq__(a, b); + "#]] + .assert_eq(&qsharp); + Ok(()) +} + +#[test] +fn neq() -> miette::Result<(), Vec> { + let source = " + angle[32] a = 1.0; + angle[32] b = 2.0; + bool x = a != b; + "; + + let qsharp = compile_qasm_stmt_to_qsharp(source)?; + expect![[r#" + mutable x = __AngleNeq__(a, b); + "#]] + .assert_eq(&qsharp); + Ok(()) +} + +#[test] +fn gt() -> miette::Result<(), Vec> { + let source = " + angle[32] a = 1.0; + angle[32] b = 2.0; + bool x = a > b; + "; + + let qsharp = compile_qasm_stmt_to_qsharp(source)?; + expect![[r#" + mutable x = __AngleGt__(a, b); + "#]] + .assert_eq(&qsharp); + Ok(()) +} + +#[test] +fn gte() -> miette::Result<(), Vec> { + let source = " + angle[32] a = 1.0; + angle[32] b = 2.0; + bool x = a >= b; + "; + + let qsharp = compile_qasm_stmt_to_qsharp(source)?; + expect![[r#" + mutable x = __AngleGte__(a, b); + "#]] + .assert_eq(&qsharp); + Ok(()) +} + +#[test] +fn lt() -> miette::Result<(), Vec> { + let source = " + angle[32] a = 1.0; + angle[32] b = 2.0; + bool x = a < b; + "; + + let qsharp = compile_qasm_stmt_to_qsharp(source)?; + expect![[r#" + mutable x = __AngleLt__(a, b); + "#]] + .assert_eq(&qsharp); + Ok(()) +} + +#[test] +fn lte() -> miette::Result<(), Vec> { + let source = " + angle[32] a = 1.0; + angle[32] b = 2.0; + bool x = a <= b; + "; + + let qsharp = compile_qasm_stmt_to_qsharp(source)?; + expect![[r#" + mutable x = __AngleLte__(a, b); + "#]] + .assert_eq(&qsharp); + Ok(()) +} + +// Arithmetic + +#[test] +fn addition() -> miette::Result<(), Vec> { + let source = " + angle[32] a = 1.0; + angle[32] b = 2.0; + angle x = a + b; + "; + + let qsharp = compile_qasm_stmt_to_qsharp(source)?; + expect![[r#" + mutable x = __AddAngles__(a, b); + "#]] + .assert_eq(&qsharp); + Ok(()) +} + +#[test] +fn subtraction() -> miette::Result<(), Vec> { + let source = " + angle[32] a = 1.0; + angle[32] b = 2.0; + angle x = a - b; + "; + + let qsharp = compile_qasm_stmt_to_qsharp(source)?; + expect![[r#" + mutable x = __SubtractAngles__(a, b); + "#]] + .assert_eq(&qsharp); + Ok(()) +} + +#[test] +fn multiplication_by_uint() -> miette::Result<(), Vec> { + let source = " + angle[32] a = 1.0; + uint[32] b = 2; + angle[32] x = a * b; + "; + + let qsharp = compile_qasm_stmt_to_qsharp(source)?; + expect![[r#" + mutable x = __MultiplyAngleByInt__(a, b); + "#]] + .assert_eq(&qsharp); + Ok(()) +} + +#[test] +fn division_by_uint() -> miette::Result<(), Vec> { + let source = " + angle[32] a = 1.0; + uint[32] b = 2; + angle x = a / b; + "; + + let qsharp = compile_qasm_stmt_to_qsharp(source)?; + expect![[r#" + mutable x = __DivideAngleByInt__(a, b); + "#]] + .assert_eq(&qsharp); + Ok(()) +} + +#[test] +fn division_by_angle() -> miette::Result<(), Vec> { + let source = " + angle[32] a = 1.0; + angle[32] b = 2.0; + uint x = a / b; + "; + + let qsharp = compile_qasm_stmt_to_qsharp(source)?; + expect![[r#" + mutable x = __DivideAngleByAngle__(a, b); + "#]] + .assert_eq(&qsharp); + Ok(()) +} diff --git a/compiler/qsc_qasm3/src/tests/expression/binary/arithmetic_conversions.rs b/compiler/qsc_qasm3/src/tests/expression/binary/arithmetic_conversions.rs index a570787372..eddae61f83 100644 --- a/compiler/qsc_qasm3/src/tests/expression/binary/arithmetic_conversions.rs +++ b/compiler/qsc_qasm3/src/tests/expression/binary/arithmetic_conversions.rs @@ -15,13 +15,14 @@ fn int_idents_without_width_can_be_multiplied() -> miette::Result<(), Vec miette::Result<(), Vec miette::Result<(), Vec "; let qsharp = compile_qasm_to_qsharp(source)?; - expect![ - r#" + expect![[r#" + import QasmStd.Angle.*; + import QasmStd.Convert.*; + import QasmStd.Intrinsic.*; mutable x = 5; mutable y = 3; x * y; - "# - ] + "#]] .assert_eq(&qsharp); Ok(()) } @@ -76,13 +79,14 @@ fn multiplying_int_idents_with_different_width_result_in_higher_width_result( "; let qsharp = compile_qasm_to_qsharp(source)?; - expect![ - r#" + expect![[r#" + import QasmStd.Angle.*; + import QasmStd.Convert.*; + import QasmStd.Intrinsic.*; mutable x = 5; mutable y = 3; mutable z = x * y; - "# - ] + "#]] .assert_eq(&qsharp); Ok(()) } @@ -97,13 +101,14 @@ fn multiplying_int_idents_with_different_width_result_in_no_width_result( "; let qsharp = compile_qasm_to_qsharp(source)?; - expect![ - r#" + expect![[r#" + import QasmStd.Angle.*; + import QasmStd.Convert.*; + import QasmStd.Intrinsic.*; mutable x = 5; mutable y = 3; mutable z = x * y; - "# - ] + "#]] .assert_eq(&qsharp); Ok(()) } @@ -118,13 +123,14 @@ fn multiplying_int_idents_with_width_greater_than_64_result_in_bigint_result( "; let qsharp = compile_qasm_to_qsharp(source)?; - expect![ - r#" + expect![[r#" + import QasmStd.Angle.*; + import QasmStd.Convert.*; + import QasmStd.Intrinsic.*; mutable x = 5; mutable y = 3; mutable z = Microsoft.Quantum.Convert.IntAsBigInt(x * y); - "# - ] + "#]] .assert_eq(&qsharp); Ok(()) } diff --git a/compiler/qsc_qasm3/src/tests/expression/binary/comparison.rs b/compiler/qsc_qasm3/src/tests/expression/binary/comparison.rs index 5bbb6ba08a..e07e28d457 100644 --- a/compiler/qsc_qasm3/src/tests/expression/binary/comparison.rs +++ b/compiler/qsc_qasm3/src/tests/expression/binary/comparison.rs @@ -23,11 +23,13 @@ fn int_var_comparisons_can_be_translated() -> miette::Result<(), Vec> { "; let qsharp = compile_qasm_to_qsharp_file(source)?; - expect![ - r#" + expect![[r#" namespace qasm3_import { @EntryPoint() operation Test() : (Int, Int, Bool, Bool, Bool, Bool, Bool, Bool) { + import QasmStd.Angle.*; + import QasmStd.Convert.*; + import QasmStd.Intrinsic.*; mutable x = 5; mutable y = 3; mutable f = (x > y); @@ -38,8 +40,7 @@ fn int_var_comparisons_can_be_translated() -> miette::Result<(), Vec> { mutable d = (x != y); (x, y, f, e, a, c, b, d) } - }"# - ] + }"#]] .assert_eq(&qsharp); Ok(()) } @@ -58,11 +59,13 @@ fn uint_var_comparisons_can_be_translated() -> miette::Result<(), Vec> { "; let qsharp = compile_qasm_to_qsharp_file(source)?; - expect![ - r#" + expect![[r#" namespace qasm3_import { @EntryPoint() operation Test() : (Int, Int, Bool, Bool, Bool, Bool, Bool, Bool) { + import QasmStd.Angle.*; + import QasmStd.Convert.*; + import QasmStd.Intrinsic.*; mutable x = 5; mutable y = 3; mutable f = (x > y); @@ -73,8 +76,7 @@ fn uint_var_comparisons_can_be_translated() -> miette::Result<(), Vec> { mutable d = (x != y); (x, y, f, e, a, c, b, d) } - }"# - ] + }"#]] .assert_eq(&qsharp); Ok(()) } @@ -93,11 +95,13 @@ fn bit_var_comparisons_can_be_translated() -> miette::Result<(), Vec> { "; let qsharp = compile_qasm_to_qsharp_file(source)?; - expect![ - r#" + expect![[r#" namespace qasm3_import { @EntryPoint() operation Test() : (Result, Result, Bool, Bool, Bool, Bool, Bool, Bool) { + import QasmStd.Angle.*; + import QasmStd.Convert.*; + import QasmStd.Intrinsic.*; mutable x = One; mutable y = Zero; mutable f = (x > y); @@ -108,8 +112,7 @@ fn bit_var_comparisons_can_be_translated() -> miette::Result<(), Vec> { mutable d = (x != y); (x, y, f, e, a, c, b, d) } - }"# - ] + }"#]] .assert_eq(&qsharp); Ok(()) } @@ -128,14 +131,13 @@ fn bitarray_var_comparisons_can_be_translated() -> miette::Result<(), Vec __ResultArrayAsIntBE__(y)); @@ -146,8 +148,7 @@ fn bitarray_var_comparisons_can_be_translated() -> miette::Result<(), Vec miette::Result<(), Vec< "#; let qsharp = compile_qasm_to_qsharp_file(source)?; - expect![ - r#" + expect![[r#" namespace qasm3_import { operation Test(y : Int) : (Result[], Bool, Bool, Bool, Bool, Bool, Bool, Bool, Bool, Bool, Bool, Bool, Bool) { - function __ResultArrayAsIntBE__(results : Result[]) : Int { - Microsoft.Quantum.Convert.ResultArrayAsInt(Microsoft.Quantum.Arrays.Reversed(results)) - } + import QasmStd.Angle.*; + import QasmStd.Convert.*; + import QasmStd.Intrinsic.*; mutable x = [One]; mutable a = (__ResultArrayAsIntBE__(x) > y); mutable b = (__ResultArrayAsIntBE__(x) >= y); @@ -194,8 +194,7 @@ fn bitarray_var_comparison_to_int_can_be_translated() -> miette::Result<(), Vec< mutable l = (y != __ResultArrayAsIntBE__(x)); (x, a, b, c, d, e, f, g, h, i, j, k, l) } - }"# - ] + }"#]] .assert_eq(&qsharp); Ok(()) } @@ -214,11 +213,13 @@ fn float_var_comparisons_can_be_translated() -> miette::Result<(), Vec> "; let qsharp = compile_qasm_to_qsharp_file(source)?; - expect![ - r#" + expect![[r#" namespace qasm3_import { @EntryPoint() operation Test() : (Double, Double, Bool, Bool, Bool, Bool, Bool, Bool) { + import QasmStd.Angle.*; + import QasmStd.Convert.*; + import QasmStd.Intrinsic.*; mutable x = 5.; mutable y = 3.; mutable f = (x > y); @@ -229,8 +230,7 @@ fn float_var_comparisons_can_be_translated() -> miette::Result<(), Vec> mutable d = (x != y); (x, y, f, e, a, c, b, d) } - }"# - ] + }"#]] .assert_eq(&qsharp); Ok(()) } @@ -251,11 +251,13 @@ fn bool_var_comparisons_can_be_translated() -> miette::Result<(), Vec> { "; let qsharp = compile_qasm_to_qsharp_file(source)?; - expect![ - r#" + expect![[r#" namespace qasm3_import { @EntryPoint() operation Test() : (Bool, Bool, Bool, Bool, Bool, Bool, Bool, Bool, Bool, Bool) { + import QasmStd.Angle.*; + import QasmStd.Convert.*; + import QasmStd.Intrinsic.*; mutable x = true; mutable y = false; mutable a = (x and y); @@ -268,8 +270,7 @@ fn bool_var_comparisons_can_be_translated() -> miette::Result<(), Vec> { mutable h = (x or not y); (x, y, a, b, c, d, e, f, g, h) } - }"# - ] + }"#]] .assert_eq(&qsharp); Ok(()) } diff --git a/compiler/qsc_qasm3/src/tests/expression/binary/ident.rs b/compiler/qsc_qasm3/src/tests/expression/binary/ident.rs index 5e6dee5f9c..a6224743b5 100644 --- a/compiler/qsc_qasm3/src/tests/expression/binary/ident.rs +++ b/compiler/qsc_qasm3/src/tests/expression/binary/ident.rs @@ -15,13 +15,14 @@ fn mutable_int_idents_without_width_can_be_multiplied() -> miette::Result<(), Ve "; let qsharp = compile_qasm_to_qsharp(source)?; - expect![ - r#" + expect![[r#" + import QasmStd.Angle.*; + import QasmStd.Convert.*; + import QasmStd.Intrinsic.*; mutable x = 5; mutable y = 3; x * y; - "# - ] + "#]] .assert_eq(&qsharp); Ok(()) } @@ -35,13 +36,14 @@ fn const_int_idents_without_width_can_be_multiplied() -> miette::Result<(), Vec< "; let qsharp = compile_qasm_to_qsharp(source)?; - expect![ - r#" + expect![[r#" + import QasmStd.Angle.*; + import QasmStd.Convert.*; + import QasmStd.Intrinsic.*; let x = 5; let y = 3; x * y; - "# - ] + "#]] .assert_eq(&qsharp); Ok(()) } @@ -56,13 +58,14 @@ fn const_int_idents_widthless_lhs_can_be_multiplied_by_explicit_width_int( "; let qsharp = compile_qasm_to_qsharp(source)?; - expect![ - r#" + expect![[r#" + import QasmStd.Angle.*; + import QasmStd.Convert.*; + import QasmStd.Intrinsic.*; let x = 5; let y = 3; x * y; - "# - ] + "#]] .assert_eq(&qsharp); Ok(()) } diff --git a/compiler/qsc_qasm3/src/tests/expression/binary/literal/multiplication.rs b/compiler/qsc_qasm3/src/tests/expression/binary/literal/multiplication.rs index 4fb67ca1f0..7e112df8e5 100644 --- a/compiler/qsc_qasm3/src/tests/expression/binary/literal/multiplication.rs +++ b/compiler/qsc_qasm3/src/tests/expression/binary/literal/multiplication.rs @@ -4,7 +4,7 @@ use expect_test::expect; use miette::Report; -use crate::tests::{compile_qasm_stmt_to_qsharp, compile_qasm_to_qsharp}; +use crate::tests::compile_qasm_stmt_to_qsharp; #[test] fn int_float_lhs_promoted_to_float() -> miette::Result<(), Vec> { @@ -12,12 +12,10 @@ fn int_float_lhs_promoted_to_float() -> miette::Result<(), Vec> { 5 * 0.3; "; - let qsharp = compile_qasm_to_qsharp(source)?; - expect![ - r#" + let qsharp = compile_qasm_stmt_to_qsharp(source)?; + expect![[r#" 5. * 0.3; - "# - ] + "#]] .assert_eq(&qsharp); Ok(()) } diff --git a/compiler/qsc_qasm3/src/tests/expression/bits.rs b/compiler/qsc_qasm3/src/tests/expression/bits.rs index c3c9cddf7a..21e138626b 100644 --- a/compiler/qsc_qasm3/src/tests/expression/bits.rs +++ b/compiler/qsc_qasm3/src/tests/expression/bits.rs @@ -30,21 +30,9 @@ fn bit_array_bits_and_register_ops() -> miette::Result<(), Vec> { namespace qasm3_import { @EntryPoint() operation Test() : (Result[], Result[], Result[], Result[], Result[]) { - function __BoolAsResult__(input : Bool) : Result { - Microsoft.Quantum.Convert.BoolAsResult(input) - } - function __IntAsResultArrayBE__(number : Int, bits : Int) : Result[] { - mutable runningValue = number; - mutable result = []; - for _ in 1..bits { - set result += [__BoolAsResult__((runningValue &&& 1) != 0)]; - set runningValue >>>= 1; - } - Microsoft.Quantum.Arrays.Reversed(result) - } - function __ResultArrayAsIntBE__(results : Result[]) : Int { - Microsoft.Quantum.Convert.ResultArrayAsInt(Microsoft.Quantum.Arrays.Reversed(results)) - } + import QasmStd.Angle.*; + import QasmStd.Convert.*; + import QasmStd.Intrinsic.*; mutable a = [One, Zero, Zero, Zero, One, One, One, One]; mutable b = [Zero, One, One, One, Zero, Zero, Zero, Zero]; mutable ls_a_1 = [Zero, Zero, Zero, Zero, Zero, Zero, Zero, Zero]; @@ -74,28 +62,14 @@ fn bit_array_left_shift() -> miette::Result<(), Vec> { "#; let qsharp = compile_qasm_to_qsharp(source)?; - expect![ - r#" - function __BoolAsResult__(input : Bool) : Result { - Microsoft.Quantum.Convert.BoolAsResult(input) - } - function __IntAsResultArrayBE__(number : Int, bits : Int) : Result[] { - mutable runningValue = number; - mutable result = []; - for _ in 1..bits { - set result += [__BoolAsResult__((runningValue &&& 1) != 0)]; - set runningValue >>>= 1; - } - Microsoft.Quantum.Arrays.Reversed(result) - } - function __ResultArrayAsIntBE__(results : Result[]) : Int { - Microsoft.Quantum.Convert.ResultArrayAsInt(Microsoft.Quantum.Arrays.Reversed(results)) - } + expect![[r#" + import QasmStd.Angle.*; + import QasmStd.Convert.*; + import QasmStd.Intrinsic.*; mutable a = [One, Zero, Zero, Zero, One, One, One, One]; mutable ls_a_1 = [Zero, Zero, Zero, Zero, Zero, Zero, Zero, Zero]; set ls_a_1 = (__IntAsResultArrayBE__(__ResultArrayAsIntBE__(a) <<< 1, 8)); - "# - ] + "#]] .assert_eq(&qsharp); Ok(()) } diff --git a/compiler/qsc_qasm3/src/tests/expression/function_call.rs b/compiler/qsc_qasm3/src/tests/expression/function_call.rs index 8c5628378c..8347947792 100644 --- a/compiler/qsc_qasm3/src/tests/expression/function_call.rs +++ b/compiler/qsc_qasm3/src/tests/expression/function_call.rs @@ -14,6 +14,9 @@ fn funcall_with_no_arguments_generates_correct_qsharp() -> miette::Result<(), Ve let qsharp = compile_qasm_to_qsharp(source)?; expect![[r#" + import QasmStd.Angle.*; + import QasmStd.Convert.*; + import QasmStd.Intrinsic.*; let empty : () -> Unit = () -> {}; empty(); "#]] @@ -30,6 +33,9 @@ fn void_function_with_one_argument_generates_correct_qsharp() -> miette::Result< let qsharp = compile_qasm_to_qsharp(source)?; expect![[r#" + import QasmStd.Angle.*; + import QasmStd.Convert.*; + import QasmStd.Intrinsic.*; let f : (Int) -> Unit = (x) -> {}; f(2); "#]] @@ -49,6 +55,9 @@ fn funcall_with_one_argument_generates_correct_qsharp() -> miette::Result<(), Ve let qsharp = compile_qasm_to_qsharp(source)?; expect![[r#" + import QasmStd.Angle.*; + import QasmStd.Convert.*; + import QasmStd.Intrinsic.*; let square : (Int) -> Int = (x) -> { return x * x; }; @@ -70,6 +79,9 @@ fn funcall_with_two_arguments_generates_correct_qsharp() -> miette::Result<(), V let qsharp = compile_qasm_to_qsharp(source)?; expect![[r#" + import QasmStd.Angle.*; + import QasmStd.Convert.*; + import QasmStd.Intrinsic.*; let sum : (Int, Int) -> Int = (x, y) -> { return x + y; }; @@ -94,13 +106,9 @@ fn funcall_with_qubit_argument() -> miette::Result<(), Vec> { let qsharp = compile_qasm_to_qsharp(source)?; expect![[r#" - function __ResultAsInt__(input : Result) : Int { - if Microsoft.Quantum.Convert.ResultAsBool(input) { - 1 - } else { - 0 - } - } + import QasmStd.Angle.*; + import QasmStd.Convert.*; + import QasmStd.Intrinsic.*; let parity : (Qubit[]) => Result = (qs) => { mutable a = QIR.Intrinsic.__quantum__qis__m__body(qs[0]); mutable b = QIR.Intrinsic.__quantum__qis__m__body(qs[1]); @@ -187,8 +195,11 @@ fn funcall_accepts_qubit_argument() -> miette::Result<(), Vec> { let qsharp = compile_qasm_to_qsharp(source)?; expect![[r#" + import QasmStd.Angle.*; + import QasmStd.Convert.*; + import QasmStd.Intrinsic.*; let h_wrapper : (Qubit) => Unit = (q) => { - H(q); + h(q); }; let q = QIR.Runtime.__quantum__rt__qubit_allocate(); h_wrapper(q); @@ -209,6 +220,9 @@ fn classical_decl_initialized_with_funcall() -> miette::Result<(), Vec> let qsharp = compile_qasm_to_qsharp(source)?; expect![[r#" + import QasmStd.Angle.*; + import QasmStd.Convert.*; + import QasmStd.Intrinsic.*; let square : (Int) -> Int = (x) -> { return x * x; }; @@ -258,18 +272,9 @@ fn funcall_implicit_arg_cast_uint_to_bitarray() -> miette::Result<(), Vec>>= 1; - } - Microsoft.Quantum.Arrays.Reversed(result) - } + import QasmStd.Angle.*; + import QasmStd.Convert.*; + import QasmStd.Intrinsic.*; let parity : (Result[]) -> Result = (arr) -> { return 1; }; diff --git a/compiler/qsc_qasm3/src/tests/expression/ident.rs b/compiler/qsc_qasm3/src/tests/expression/ident.rs index f17a9d86f2..b7a5b1ce6b 100644 --- a/compiler/qsc_qasm3/src/tests/expression/ident.rs +++ b/compiler/qsc_qasm3/src/tests/expression/ident.rs @@ -67,6 +67,9 @@ fn resolved_idenfiers_are_compiled_as_refs() -> miette::Result<(), Vec> let qsharp = compile_qasm_to_qsharp(source)?; expect![ r#" + import QasmStd.Angle.*; + import QasmStd.Convert.*; + import QasmStd.Intrinsic.*; mutable p = Microsoft.Quantum.Math.PI(); mutable x = p; "# diff --git a/compiler/qsc_qasm3/src/tests/expression/implicit_cast_from_bit.rs b/compiler/qsc_qasm3/src/tests/expression/implicit_cast_from_bit.rs index b08eb71371..9ff05cade3 100644 --- a/compiler/qsc_qasm3/src/tests/expression/implicit_cast_from_bit.rs +++ b/compiler/qsc_qasm3/src/tests/expression/implicit_cast_from_bit.rs @@ -21,14 +21,10 @@ fn to_bool_and_back_implicitly() -> miette::Result<(), Vec> { "#; let qsharp = compile_qasm_to_qsharp(source)?; - expect![ - r#" - function __BoolAsResult__(input : Bool) : Result { - Microsoft.Quantum.Convert.BoolAsResult(input) - } - function __ResultAsBool__(input : Result) : Bool { - Microsoft.Quantum.Convert.ResultAsBool(input) - } + expect![[r#" + import QasmStd.Angle.*; + import QasmStd.Convert.*; + import QasmStd.Intrinsic.*; mutable _bool0 = false; mutable _bool1 = false; set _bool0 = true; @@ -36,8 +32,7 @@ fn to_bool_and_back_implicitly() -> miette::Result<(), Vec> { set _bool0 = _bool1; set _bool0 = _bool1; set a = __BoolAsResult__(_bool1); - "# - ] + "#]] .assert_eq(&qsharp); Ok(()) } @@ -50,15 +45,13 @@ fn to_bool_implicitly() -> miette::Result<(), Vec> { "; let qsharp = compile_qasm_to_qsharp(source)?; - expect![ - r#" - function __ResultAsBool__(input : Result) : Bool { - Microsoft.Quantum.Convert.ResultAsBool(input) - } + expect![[r#" + import QasmStd.Angle.*; + import QasmStd.Convert.*; + import QasmStd.Intrinsic.*; mutable x = One; mutable y = __ResultAsBool__(x); - "# - ] + "#]] .assert_eq(&qsharp); Ok(()) } @@ -71,19 +64,13 @@ fn to_implicit_int_implicitly() -> miette::Result<(), Vec> { "; let qsharp = compile_qasm_to_qsharp(source)?; - expect![ - r#" - function __ResultAsInt__(input : Result) : Int { - if Microsoft.Quantum.Convert.ResultAsBool(input) { - 1 - } else { - 0 - } - } + expect![[r#" + import QasmStd.Angle.*; + import QasmStd.Convert.*; + import QasmStd.Intrinsic.*; mutable x = One; mutable y = __ResultAsInt__(x); - "# - ] + "#]] .assert_eq(&qsharp); Ok(()) } @@ -96,19 +83,13 @@ fn to_explicit_int_implicitly() -> miette::Result<(), Vec> { "; let qsharp = compile_qasm_to_qsharp(source)?; - expect![ - r#" - function __ResultAsInt__(input : Result) : Int { - if Microsoft.Quantum.Convert.ResultAsBool(input) { - 1 - } else { - 0 - } - } + expect![[r#" + import QasmStd.Angle.*; + import QasmStd.Convert.*; + import QasmStd.Intrinsic.*; mutable x = One; mutable y = __ResultAsInt__(x); - "# - ] + "#]] .assert_eq(&qsharp); Ok(()) } @@ -121,19 +102,13 @@ fn to_implicit_uint_implicitly() -> miette::Result<(), Vec> { "; let qsharp = compile_qasm_to_qsharp(source)?; - expect![ - r#" - function __ResultAsInt__(input : Result) : Int { - if Microsoft.Quantum.Convert.ResultAsBool(input) { - 1 - } else { - 0 - } - } + expect![[r#" + import QasmStd.Angle.*; + import QasmStd.Convert.*; + import QasmStd.Intrinsic.*; mutable x = One; mutable y = __ResultAsInt__(x); - "# - ] + "#]] .assert_eq(&qsharp); Ok(()) } @@ -146,19 +121,13 @@ fn to_explicit_uint_implicitly() -> miette::Result<(), Vec> { "; let qsharp = compile_qasm_to_qsharp(source)?; - expect![ - r#" - function __ResultAsInt__(input : Result) : Int { - if Microsoft.Quantum.Convert.ResultAsBool(input) { - 1 - } else { - 0 - } - } + expect![[r#" + import QasmStd.Angle.*; + import QasmStd.Convert.*; + import QasmStd.Intrinsic.*; mutable x = One; mutable y = __ResultAsInt__(x); - "# - ] + "#]] .assert_eq(&qsharp); Ok(()) } @@ -171,19 +140,13 @@ fn to_explicit_bigint_implicitly() -> miette::Result<(), Vec> { "; let qsharp = compile_qasm_to_qsharp(source)?; - expect![ - r#" - function __ResultAsBigInt__(input : Result) : BigInt { - if Microsoft.Quantum.Convert.ResultAsBool(input) { - 1L - } else { - 0L - } - } + expect![[r#" + import QasmStd.Angle.*; + import QasmStd.Convert.*; + import QasmStd.Intrinsic.*; mutable x = One; mutable y = __ResultAsBigInt__(x); - "# - ] + "#]] .assert_eq(&qsharp); Ok(()) } @@ -199,6 +162,6 @@ fn to_implicit_float_implicitly_fails() { panic!("Expected error") }; - expect!["Cannot cast expression of type Bit(false) to type Float(None, false)"] + expect![["Cannot cast expression of type Bit(false) to type Float(None, false)"]] .assert_eq(&error[0].to_string()); } diff --git a/compiler/qsc_qasm3/src/tests/expression/implicit_cast_from_bitarray.rs b/compiler/qsc_qasm3/src/tests/expression/implicit_cast_from_bitarray.rs index 3d879ca671..c76bffefd4 100644 --- a/compiler/qsc_qasm3/src/tests/expression/implicit_cast_from_bitarray.rs +++ b/compiler/qsc_qasm3/src/tests/expression/implicit_cast_from_bitarray.rs @@ -14,15 +14,13 @@ fn to_int_decl_implicitly() -> miette::Result<(), Vec> { "#; let qsharp = compile_qasm_to_qsharp(source)?; - expect![ - r#" - function __ResultArrayAsIntBE__(results : Result[]) : Int { - Microsoft.Quantum.Convert.ResultArrayAsInt(Microsoft.Quantum.Arrays.Reversed(results)) - } + expect![[r#" + import QasmStd.Angle.*; + import QasmStd.Convert.*; + import QasmStd.Intrinsic.*; mutable reg = [Zero, Zero, Zero, Zero, Zero]; mutable b = __ResultArrayAsIntBE__(reg); - "# - ] + "#]] .assert_eq(&qsharp); Ok(()) } @@ -36,16 +34,14 @@ fn to_int_assignment_implicitly() -> miette::Result<(), Vec> { "#; let qsharp = compile_qasm_to_qsharp(source)?; - expect![ - r#" - function __ResultArrayAsIntBE__(results : Result[]) : Int { - Microsoft.Quantum.Convert.ResultArrayAsInt(Microsoft.Quantum.Arrays.Reversed(results)) - } + expect![[r#" + import QasmStd.Angle.*; + import QasmStd.Convert.*; + import QasmStd.Intrinsic.*; mutable reg = [Zero, Zero, Zero, Zero, Zero]; mutable a = 0; set a = __ResultArrayAsIntBE__(reg); - "# - ] + "#]] .assert_eq(&qsharp); Ok(()) } @@ -59,16 +55,14 @@ fn to_int_with_equal_width_in_assignment_implicitly() -> miette::Result<(), Vec< "#; let qsharp = compile_qasm_to_qsharp(source)?; - expect![ - r#" - function __ResultArrayAsIntBE__(results : Result[]) : Int { - Microsoft.Quantum.Convert.ResultArrayAsInt(Microsoft.Quantum.Arrays.Reversed(results)) - } + expect![[r#" + import QasmStd.Angle.*; + import QasmStd.Convert.*; + import QasmStd.Intrinsic.*; mutable reg = [Zero, Zero, Zero, Zero, Zero]; mutable a = 0; set a = __ResultArrayAsIntBE__(reg); - "# - ] + "#]] .assert_eq(&qsharp); Ok(()) } @@ -81,15 +75,13 @@ fn to_int_with_equal_width_in_decl_implicitly() -> miette::Result<(), Vec miette::Result<(), Vec> { "#; let qsharp = compile_qasm_to_qsharp(source)?; - expect![ - r#" - function __BoolAsResult__(input : Bool) : Result { - Microsoft.Quantum.Convert.BoolAsResult(input) - } - function __ResultAsBool__(input : Result) : Bool { - Microsoft.Quantum.Convert.ResultAsBool(input) - } + expect![[r#" + import QasmStd.Angle.*; + import QasmStd.Convert.*; + import QasmStd.Intrinsic.*; mutable _bit0 = Zero; mutable _bit1 = Zero; set _bit0 = One; @@ -36,8 +32,7 @@ fn to_bit_and_back_implicitly() -> miette::Result<(), Vec> { set _bit0 = _bit1; set _bit0 = _bit1; set a = __ResultAsBool__(_bit1); - "# - ] + "#]] .assert_eq(&qsharp); Ok(()) } @@ -50,15 +45,13 @@ fn to_bit_implicitly() -> miette::Result<(), Vec> { "; let qsharp = compile_qasm_to_qsharp(source)?; - expect![ - r#" - function __BoolAsResult__(input : Bool) : Result { - Microsoft.Quantum.Convert.BoolAsResult(input) - } + expect![[r#" + import QasmStd.Angle.*; + import QasmStd.Convert.*; + import QasmStd.Intrinsic.*; mutable x = true; mutable y = __BoolAsResult__(x); - "# - ] + "#]] .assert_eq(&qsharp); Ok(()) } @@ -71,19 +64,13 @@ fn to_implicit_int_implicitly() -> miette::Result<(), Vec> { "; let qsharp = compile_qasm_to_qsharp(source)?; - expect![ - r#" - function __BoolAsInt__(value : Bool) : Int { - if value { - 1 - } else { - 0 - } - } + expect![[r#" + import QasmStd.Angle.*; + import QasmStd.Convert.*; + import QasmStd.Intrinsic.*; mutable x = true; mutable y = __BoolAsInt__(x); - "# - ] + "#]] .assert_eq(&qsharp); Ok(()) } @@ -96,19 +83,13 @@ fn to_explicit_int_implicitly() -> miette::Result<(), Vec> { "; let qsharp = compile_qasm_to_qsharp(source)?; - expect![ - r#" - function __BoolAsInt__(value : Bool) : Int { - if value { - 1 - } else { - 0 - } - } + expect![[r#" + import QasmStd.Angle.*; + import QasmStd.Convert.*; + import QasmStd.Intrinsic.*; mutable x = true; mutable y = __BoolAsInt__(x); - "# - ] + "#]] .assert_eq(&qsharp); Ok(()) } @@ -121,19 +102,13 @@ fn to_implicit_uint_implicitly() -> miette::Result<(), Vec> { "; let qsharp = compile_qasm_to_qsharp(source)?; - expect![ - r#" - function __BoolAsInt__(value : Bool) : Int { - if value { - 1 - } else { - 0 - } - } + expect![[r#" + import QasmStd.Angle.*; + import QasmStd.Convert.*; + import QasmStd.Intrinsic.*; mutable x = true; mutable y = __BoolAsInt__(x); - "# - ] + "#]] .assert_eq(&qsharp); Ok(()) } @@ -146,19 +121,13 @@ fn to_explicit_uint_implicitly() -> miette::Result<(), Vec> { "; let qsharp = compile_qasm_to_qsharp(source)?; - expect![ - r#" - function __BoolAsInt__(value : Bool) : Int { - if value { - 1 - } else { - 0 - } - } + expect![[r#" + import QasmStd.Angle.*; + import QasmStd.Convert.*; + import QasmStd.Intrinsic.*; mutable x = true; mutable y = __BoolAsInt__(x); - "# - ] + "#]] .assert_eq(&qsharp); Ok(()) } @@ -171,19 +140,13 @@ fn to_explicit_bigint_implicitly() -> miette::Result<(), Vec> { "; let qsharp = compile_qasm_to_qsharp(source)?; - expect![ - r#" - function __BoolAsBigInt__(value : Bool) : BigInt { - if value { - 1L - } else { - 0L - } - } + expect![[r#" + import QasmStd.Angle.*; + import QasmStd.Convert.*; + import QasmStd.Intrinsic.*; mutable x = true; mutable y = __BoolAsBigInt__(x); - "# - ] + "#]] .assert_eq(&qsharp); Ok(()) } @@ -196,19 +159,13 @@ fn to_implicit_float_implicitly() -> miette::Result<(), Vec> { "; let qsharp = compile_qasm_to_qsharp(source)?; - expect![ - r#" - function __BoolAsDouble__(value : Bool) : Double { - if value { - 1. - } else { - 0. - } - } + expect![[r#" + import QasmStd.Angle.*; + import QasmStd.Convert.*; + import QasmStd.Intrinsic.*; mutable x = true; mutable y = __BoolAsDouble__(x); - "# - ] + "#]] .assert_eq(&qsharp); Ok(()) } @@ -221,19 +178,13 @@ fn to_explicit_float_implicitly() -> miette::Result<(), Vec> { "; let qsharp = compile_qasm_to_qsharp(source)?; - expect![ - r#" - function __BoolAsDouble__(value : Bool) : Double { - if value { - 1. - } else { - 0. - } - } + expect![[r#" + import QasmStd.Angle.*; + import QasmStd.Convert.*; + import QasmStd.Intrinsic.*; mutable x = true; mutable y = __BoolAsDouble__(x); - "# - ] + "#]] .assert_eq(&qsharp); Ok(()) } diff --git a/compiler/qsc_qasm3/src/tests/expression/implicit_cast_from_float.rs b/compiler/qsc_qasm3/src/tests/expression/implicit_cast_from_float.rs index 721778b5f7..a1de1bd0e0 100644 --- a/compiler/qsc_qasm3/src/tests/expression/implicit_cast_from_float.rs +++ b/compiler/qsc_qasm3/src/tests/expression/implicit_cast_from_float.rs @@ -17,7 +17,7 @@ fn to_bit_implicitly() { panic!("Expected error") }; - expect!["Cannot cast expression of type Float(None, false) to type Bit(false)"] + expect![["Cannot cast expression of type Float(None, false) to type Bit(false)"]] .assert_eq(&error[0].to_string()); } @@ -32,7 +32,7 @@ fn explicit_width_to_bit_implicitly() { panic!("Expected error") }; - expect![r#"Cannot cast expression of type Float(Some(64), false) to type Bit(false)"#] + expect![[r#"Cannot cast expression of type Float(Some(64), false) to type Bit(false)"#]] .assert_eq(&error[0].to_string()); } @@ -44,16 +44,17 @@ fn to_bool_implicitly() -> miette::Result<(), Vec> { "; let qsharp = compile_qasm_to_qsharp(source)?; - expect![ - r#" + expect![[r#" + import QasmStd.Angle.*; + import QasmStd.Convert.*; + import QasmStd.Intrinsic.*; mutable x = 42.; mutable y = if Microsoft.Quantum.Math.Truncate(x) == 0 { false } else { true }; - "# - ] + "#]] .assert_eq(&qsharp); Ok(()) } @@ -66,12 +67,13 @@ fn to_implicit_int_implicitly() -> miette::Result<(), Vec> { "; let qsharp = compile_qasm_to_qsharp(source)?; - expect![ - r#" + expect![[r#" + import QasmStd.Angle.*; + import QasmStd.Convert.*; + import QasmStd.Intrinsic.*; mutable x = 42.; mutable y = Microsoft.Quantum.Math.Truncate(x); - "# - ] + "#]] .assert_eq(&qsharp); Ok(()) } @@ -84,12 +86,13 @@ fn to_explicit_int_implicitly() -> miette::Result<(), Vec> { "; let qsharp = compile_qasm_to_qsharp(source)?; - expect![ - r#" + expect![[r#" + import QasmStd.Angle.*; + import QasmStd.Convert.*; + import QasmStd.Intrinsic.*; mutable x = 42.; mutable y = Microsoft.Quantum.Math.Truncate(x); - "# - ] + "#]] .assert_eq(&qsharp); Ok(()) } @@ -102,12 +105,13 @@ fn to_implicit_uint_implicitly() -> miette::Result<(), Vec> { "; let qsharp = compile_qasm_to_qsharp(source)?; - expect![ - r#" + expect![[r#" + import QasmStd.Angle.*; + import QasmStd.Convert.*; + import QasmStd.Intrinsic.*; mutable x = 42.; mutable y = Microsoft.Quantum.Math.Truncate(x); - "# - ] + "#]] .assert_eq(&qsharp); Ok(()) } @@ -120,12 +124,13 @@ fn to_explicit_uint_implicitly() -> miette::Result<(), Vec> { "; let qsharp = compile_qasm_to_qsharp(source)?; - expect![ - r#" + expect![[r#" + import QasmStd.Angle.*; + import QasmStd.Convert.*; + import QasmStd.Intrinsic.*; mutable x = 42.; mutable y = Microsoft.Quantum.Math.Truncate(x); - "# - ] + "#]] .assert_eq(&qsharp); Ok(()) } @@ -138,12 +143,13 @@ fn to_explicit_bigint_implicitly() -> miette::Result<(), Vec> { "; let qsharp = compile_qasm_to_qsharp(source)?; - expect![ - r#" + expect![[r#" + import QasmStd.Angle.*; + import QasmStd.Convert.*; + import QasmStd.Intrinsic.*; mutable x = 42.; mutable y = Microsoft.Quantum.Convert.IntAsBigInt(Microsoft.Quantum.Math.Truncate(x)); - "# - ] + "#]] .assert_eq(&qsharp); Ok(()) } @@ -156,12 +162,13 @@ fn to_implicit_float_implicitly() -> miette::Result<(), Vec> { "; let qsharp = compile_qasm_to_qsharp(source)?; - expect![ - r#" + expect![[r#" + import QasmStd.Angle.*; + import QasmStd.Convert.*; + import QasmStd.Intrinsic.*; mutable x = 42.; mutable y = x; - "# - ] + "#]] .assert_eq(&qsharp); Ok(()) } @@ -174,12 +181,13 @@ fn to_explicit_float_implicitly() -> miette::Result<(), Vec> { "; let qsharp = compile_qasm_to_qsharp(source)?; - expect![ - r#" + expect![[r#" + import QasmStd.Angle.*; + import QasmStd.Convert.*; + import QasmStd.Intrinsic.*; mutable x = 42.; mutable y = x; - "# - ] + "#]] .assert_eq(&qsharp); Ok(()) } @@ -192,12 +200,13 @@ fn to_implicit_complex_implicitly() -> miette::Result<(), Vec> { "; let qsharp = compile_qasm_to_qsharp(source)?; - expect![ - r#" + expect![[r#" + import QasmStd.Angle.*; + import QasmStd.Convert.*; + import QasmStd.Intrinsic.*; mutable x = 42.; mutable y = Microsoft.Quantum.Math.Complex(x, 0.); - "# - ] + "#]] .assert_eq(&qsharp); Ok(()) } @@ -210,12 +219,13 @@ fn to_explicit_complex_implicitly() -> miette::Result<(), Vec> { "; let qsharp = compile_qasm_to_qsharp(source)?; - expect![ - r#" + expect![[r#" + import QasmStd.Angle.*; + import QasmStd.Convert.*; + import QasmStd.Intrinsic.*; mutable x = 42.; mutable y = Microsoft.Quantum.Math.Complex(x, 0.); - "# - ] + "#]] .assert_eq(&qsharp); Ok(()) } diff --git a/compiler/qsc_qasm3/src/tests/expression/implicit_cast_from_int.rs b/compiler/qsc_qasm3/src/tests/expression/implicit_cast_from_int.rs index 862fb74526..fe71800c3c 100644 --- a/compiler/qsc_qasm3/src/tests/expression/implicit_cast_from_int.rs +++ b/compiler/qsc_qasm3/src/tests/expression/implicit_cast_from_int.rs @@ -14,16 +14,17 @@ fn to_bit_implicitly() -> miette::Result<(), Vec> { "; let qsharp = compile_qasm_to_qsharp(source)?; - expect![ - r#" + expect![[r#" + import QasmStd.Angle.*; + import QasmStd.Convert.*; + import QasmStd.Intrinsic.*; mutable x = 42; mutable y = if x == 0 { One } else { Zero }; - "# - ] + "#]] .assert_eq(&qsharp); Ok(()) } @@ -36,16 +37,17 @@ fn to_bool_implicitly() -> miette::Result<(), Vec> { "; let qsharp = compile_qasm_to_qsharp(source)?; - expect![ - r#" + expect![[r#" + import QasmStd.Angle.*; + import QasmStd.Convert.*; + import QasmStd.Intrinsic.*; mutable x = 42; mutable y = if x == 0 { false } else { true }; - "# - ] + "#]] .assert_eq(&qsharp); Ok(()) } @@ -58,12 +60,13 @@ fn to_implicit_int_implicitly() -> miette::Result<(), Vec> { "; let qsharp = compile_qasm_to_qsharp(source)?; - expect![ - r#" + expect![[r#" + import QasmStd.Angle.*; + import QasmStd.Convert.*; + import QasmStd.Intrinsic.*; mutable x = 42; mutable y = x; - "# - ] + "#]] .assert_eq(&qsharp); Ok(()) } @@ -76,12 +79,13 @@ fn to_explicit_int_implicitly() -> miette::Result<(), Vec> { "; let qsharp = compile_qasm_to_qsharp(source)?; - expect![ - r#" + expect![[r#" + import QasmStd.Angle.*; + import QasmStd.Convert.*; + import QasmStd.Intrinsic.*; mutable x = 42; mutable y = x; - "# - ] + "#]] .assert_eq(&qsharp); Ok(()) } @@ -94,12 +98,13 @@ fn to_implicit_uint_implicitly() -> miette::Result<(), Vec> { "; let qsharp = compile_qasm_to_qsharp(source)?; - expect![ - r#" + expect![[r#" + import QasmStd.Angle.*; + import QasmStd.Convert.*; + import QasmStd.Intrinsic.*; mutable x = 42; mutable y = x; - "# - ] + "#]] .assert_eq(&qsharp); Ok(()) } @@ -112,12 +117,13 @@ fn to_explicit_uint_implicitly() -> miette::Result<(), Vec> { "; let qsharp = compile_qasm_to_qsharp(source)?; - expect![ - r#" + expect![[r#" + import QasmStd.Angle.*; + import QasmStd.Convert.*; + import QasmStd.Intrinsic.*; mutable x = 42; mutable y = x; - "# - ] + "#]] .assert_eq(&qsharp); Ok(()) } @@ -130,12 +136,13 @@ fn to_explicit_bigint_implicitly() -> miette::Result<(), Vec> { "; let qsharp = compile_qasm_to_qsharp(source)?; - expect![ - r#" + expect![[r#" + import QasmStd.Angle.*; + import QasmStd.Convert.*; + import QasmStd.Intrinsic.*; mutable x = 42; mutable y = Microsoft.Quantum.Convert.IntAsBigInt(x); - "# - ] + "#]] .assert_eq(&qsharp); Ok(()) } @@ -148,12 +155,13 @@ fn to_implicit_float_implicitly() -> miette::Result<(), Vec> { "; let qsharp = compile_qasm_to_qsharp(source)?; - expect![ - r#" + expect![[r#" + import QasmStd.Angle.*; + import QasmStd.Convert.*; + import QasmStd.Intrinsic.*; mutable x = 42; mutable y = Microsoft.Quantum.Convert.IntAsDouble(x); - "# - ] + "#]] .assert_eq(&qsharp); Ok(()) } @@ -166,12 +174,13 @@ fn to_explicit_float_implicitly() -> miette::Result<(), Vec> { "; let qsharp = compile_qasm_to_qsharp(source)?; - expect![ - r#" + expect![[r#" + import QasmStd.Angle.*; + import QasmStd.Convert.*; + import QasmStd.Intrinsic.*; mutable x = 42; mutable y = Microsoft.Quantum.Convert.IntAsDouble(x); - "# - ] + "#]] .assert_eq(&qsharp); Ok(()) } @@ -184,12 +193,13 @@ fn to_implicit_complex_implicitly() -> miette::Result<(), Vec> { "; let qsharp = compile_qasm_to_qsharp(source)?; - expect![ - r#" + expect![[r#" + import QasmStd.Angle.*; + import QasmStd.Convert.*; + import QasmStd.Intrinsic.*; mutable x = 42; mutable y = Microsoft.Quantum.Math.Complex(Microsoft.Quantum.Convert.IntAsDouble(x), 0.); - "# - ] + "#]] .assert_eq(&qsharp); Ok(()) } @@ -202,12 +212,13 @@ fn to_explicit_complex_implicitly() -> miette::Result<(), Vec> { "; let qsharp = compile_qasm_to_qsharp(source)?; - expect![ - r#" + expect![[r#" + import QasmStd.Angle.*; + import QasmStd.Convert.*; + import QasmStd.Intrinsic.*; mutable x = 42; mutable y = Microsoft.Quantum.Math.Complex(Microsoft.Quantum.Convert.IntAsDouble(x), 0.); - "# - ] + "#]] .assert_eq(&qsharp); Ok(()) } diff --git a/compiler/qsc_qasm3/src/tests/expression/indexed.rs b/compiler/qsc_qasm3/src/tests/expression/indexed.rs index c76ee6b0ff..ab308947d3 100644 --- a/compiler/qsc_qasm3/src/tests/expression/indexed.rs +++ b/compiler/qsc_qasm3/src/tests/expression/indexed.rs @@ -19,7 +19,7 @@ fn indexed_bit_cannot_be_implicitly_converted_to_float() { }; assert_eq!(1, errors.len(), "Expected one error"); - expect![r#"Cannot cast expression of type Bit(false) to type Float(None, false)"#] + expect!["Cannot cast expression of type Bit(false) to type Float(None, true)"] .assert_eq(&errors[0].to_string()); } @@ -33,21 +33,15 @@ fn indexed_bit_can_implicitly_convert_to_int() -> miette::Result<(), Vec "; let qsharp = compile_qasm_to_qsharp(source)?; - expect![ - r#" - function __ResultAsInt__(input : Result) : Int { - if Microsoft.Quantum.Convert.ResultAsBool(input) { - 1 - } else { - 0 - } - } + expect![[r#" + import QasmStd.Angle.*; + import QasmStd.Convert.*; + import QasmStd.Intrinsic.*; mutable x = [Zero, Zero, Zero, Zero, Zero]; if __ResultAsInt__(x[0]) == 1 { set x w/= 1 <- One; }; - "# - ] + "#]] .assert_eq(&qsharp); Ok(()) } @@ -62,17 +56,15 @@ fn indexed_bit_can_implicitly_convert_to_bool() -> miette::Result<(), Vec miette::Result<(), Vec> { "; let qsharp = compile_qasm_to_qsharp(source)?; - expect![ - r#" + expect![[r#" + import QasmStd.Angle.*; + import QasmStd.Convert.*; + import QasmStd.Intrinsic.*; mutable x = [Zero, Zero, Zero, Zero, Zero]; mutable y = x[0]; - "# - ] + "#]] .assert_eq(&qsharp); Ok(()) } @@ -104,12 +97,10 @@ fn bool_indexed_ty_is_same_as_element_ty() -> miette::Result<(), Vec> { "; let qsharp = compile_qasm_to_qsharp(source)?; - expect![ - r#" + expect![[r#" mutable x = [false, false, false, false, false]; mutable y = x[0]; - "# - ] + "#]] .assert_eq(&qsharp); Ok(()) } @@ -125,7 +116,7 @@ fn bitstring_slicing() -> miette::Result<(), Vec> { "#; let qsharp = compile_qasm_to_qsharp(source)?; - expect![r#""#].assert_eq(&qsharp); + expect![[r#""#]].assert_eq(&qsharp); Ok(()) } @@ -140,7 +131,7 @@ fn bitstring_slicing_with_step() -> miette::Result<(), Vec> { "#; let qsharp = compile_qasm_to_qsharp(source)?; - expect![r#""#].assert_eq(&qsharp); + expect![[r#""#]].assert_eq(&qsharp); Ok(()) } @@ -155,6 +146,6 @@ fn bitstring_index_set() -> miette::Result<(), Vec> { "#; let qsharp = compile_qasm_to_qsharp(source)?; - expect![r#""#].assert_eq(&qsharp); + expect![[r#""#]].assert_eq(&qsharp); Ok(()) } diff --git a/compiler/qsc_qasm3/src/tests/expression/unary.rs b/compiler/qsc_qasm3/src/tests/expression/unary.rs index f4accf3eaf..cb5f05270a 100644 --- a/compiler/qsc_qasm3/src/tests/expression/unary.rs +++ b/compiler/qsc_qasm3/src/tests/expression/unary.rs @@ -15,12 +15,10 @@ fn bitwise_not_int() -> miette::Result<(), Vec> { "; let qsharp = compile_qasm_to_qsharp(source)?; - expect![ - r#" + expect![[r#" mutable x = 5; mutable y = ~~~x; - "# - ] + "#]] .assert_eq(&qsharp); Ok(()) } @@ -33,12 +31,13 @@ fn not_bool() -> miette::Result<(), Vec> { "; let qsharp = compile_qasm_to_qsharp(source)?; - expect![ - r#" + expect![[r#" + import QasmStd.Angle.*; + import QasmStd.Convert.*; + import QasmStd.Intrinsic.*; mutable x = true; mutable y = not x; - "# - ] + "#]] .assert_eq(&qsharp); Ok(()) } @@ -51,18 +50,13 @@ fn not_result() -> miette::Result<(), Vec> { "; let qsharp = compile_qasm_to_qsharp(source)?; - expect![ - r#" - function __BoolAsResult__(input : Bool) : Result { - Microsoft.Quantum.Convert.BoolAsResult(input) - } - function __ResultAsBool__(input : Result) : Bool { - Microsoft.Quantum.Convert.ResultAsBool(input) - } + expect![[r#" + import QasmStd.Angle.*; + import QasmStd.Convert.*; + import QasmStd.Intrinsic.*; mutable x = One; mutable y = __BoolAsResult__(not __ResultAsBool__(x)); - "# - ] + "#]] .assert_eq(&qsharp); Ok(()) } @@ -75,22 +69,23 @@ fn logical_not_int() -> miette::Result<(), Vec> { "; let qsharp = compile_qasm_to_qsharp(source)?; - expect![ - r#" + expect![[r#" + import QasmStd.Angle.*; + import QasmStd.Convert.*; + import QasmStd.Intrinsic.*; mutable x = 159; mutable y = not if x == 0 { false } else { true }; - "# - ] + "#]] .assert_eq(&qsharp); Ok(()) } #[test] -#[ignore = "OPENQASM 3.0 parser bug"] +#[ignore = "negating a Result type is an invalid Q# operation"] fn bitwise_not_result() -> miette::Result<(), Vec> { let source = " bit[1] x; @@ -98,10 +93,8 @@ fn bitwise_not_result() -> miette::Result<(), Vec> { "; let qsharp = compile_qasm_to_qsharp(source)?; - expect![ - r#" - "# - ] + expect![[r#" + "#]] .assert_eq(&qsharp); Ok(()) } @@ -116,17 +109,59 @@ fn logical_not_indexed_bit_array_in_if_cond() -> miette::Result<(), Vec> "; let qsharp = compile_qasm_to_qsharp(source)?; - expect![ - r#" - function __ResultAsBool__(input : Result) : Bool { - Microsoft.Quantum.Convert.ResultAsBool(input) - } + expect![[r#" + import QasmStd.Angle.*; + import QasmStd.Convert.*; + import QasmStd.Intrinsic.*; mutable Classical = [Zero, Zero, Zero, Zero, Zero, Zero, Zero, Zero, Zero, Zero]; if not __ResultAsBool__(Classical[1]) { set Classical w/= 0 <- One; }; - "# - ] + "#]] + .assert_eq(&qsharp); + Ok(()) +} + +#[test] +fn neg_angle() -> miette::Result<(), Vec> { + let source = " + angle[4] x = 1.0; + angle[4] y = -x; + "; + + let qsharp = compile_qasm_to_qsharp(source)?; + expect![[r#" + import QasmStd.Angle.*; + import QasmStd.Convert.*; + import QasmStd.Intrinsic.*; + mutable x = new __Angle__ { + Value = 3, + Size = 4 + }; + mutable y = __NegAngle__(x); + "#]] + .assert_eq(&qsharp); + Ok(()) +} + +#[test] +fn notb_angle() -> miette::Result<(), Vec> { + let source = " + angle[4] x = 1.0; + angle[4] y = ~x; + "; + + let qsharp = compile_qasm_to_qsharp(source)?; + expect![[r#" + import QasmStd.Angle.*; + import QasmStd.Convert.*; + import QasmStd.Intrinsic.*; + mutable x = new __Angle__ { + Value = 3, + Size = 4 + }; + mutable y = __AngleNotB__(x); + "#]] .assert_eq(&qsharp); Ok(()) } diff --git a/compiler/qsc_qasm3/src/tests/output.rs b/compiler/qsc_qasm3/src/tests/output.rs index 4f6fdbd3fd..0e9bcac15b 100644 --- a/compiler/qsc_qasm3/src/tests/output.rs +++ b/compiler/qsc_qasm3/src/tests/output.rs @@ -41,13 +41,16 @@ fn using_re_semantics_removes_output() -> miette::Result<(), Vec> { expect![[r#" namespace qasm3_import { operation Test(theta : Double, beta : Int) : Unit { + import QasmStd.Angle.*; + import QasmStd.Convert.*; + import QasmStd.Intrinsic.*; mutable c = [Zero, Zero]; let q = QIR.Runtime.AllocateQubitArray(2); mutable gamma = 0.; mutable delta = 0.; - Rz(theta, q[0]); - H(q[0]); - CNOT(q[0], q[1]); + rz(__DoubleAsAngle__(theta, 53), q[0]); + h(q[0]); + cx(q[0], q[1]); set c w/= 0 <- QIR.Intrinsic.__quantum__qis__m__body(q[0]); set c w/= 1 <- QIR.Intrinsic.__quantum__qis__m__body(q[1]); } @@ -89,13 +92,16 @@ fn using_qasm_semantics_captures_all_classical_decls_as_output() -> miette::Resu expect![[r#" namespace qasm3_import { operation Test(theta : Double, beta : Int) : (Result[], Double, Double) { + import QasmStd.Angle.*; + import QasmStd.Convert.*; + import QasmStd.Intrinsic.*; mutable c = [Zero, Zero]; let q = QIR.Runtime.AllocateQubitArray(2); mutable gamma = 0.; mutable delta = 0.; - Rz(theta, q[0]); - H(q[0]); - CNOT(q[0], q[1]); + rz(__DoubleAsAngle__(theta, 53), q[0]); + h(q[0]); + cx(q[0], q[1]); set c w/= 0 <- QIR.Intrinsic.__quantum__qis__m__body(q[0]); set c w/= 1 <- QIR.Intrinsic.__quantum__qis__m__body(q[1]); (c, gamma, delta) @@ -137,13 +143,16 @@ fn using_qiskit_semantics_only_bit_array_is_captured_and_reversed( expect![[r#" namespace qasm3_import { operation Test(theta : Double, beta : Int) : Result[] { + import QasmStd.Angle.*; + import QasmStd.Convert.*; + import QasmStd.Intrinsic.*; mutable c = [Zero, Zero]; let q = QIR.Runtime.AllocateQubitArray(2); mutable gamma = 0.; mutable delta = 0.; - Rz(theta, q[0]); - H(q[0]); - CNOT(q[0], q[1]); + rz(__DoubleAsAngle__(theta, 53), q[0]); + h(q[0]); + cx(q[0], q[1]); set c w/= 0 <- QIR.Intrinsic.__quantum__qis__m__body(q[0]); set c w/= 1 <- QIR.Intrinsic.__quantum__qis__m__body(q[1]); Microsoft.Quantum.Arrays.Reversed(c) @@ -193,17 +202,20 @@ c2[2] = measure q[4]; expect![[r#" namespace qasm3_import { operation Test(theta : Double, beta : Int) : (Result[], Result[]) { + import QasmStd.Angle.*; + import QasmStd.Convert.*; + import QasmStd.Intrinsic.*; mutable c = [Zero, Zero]; mutable c2 = [Zero, Zero, Zero]; let q = QIR.Runtime.AllocateQubitArray(5); mutable gamma = 0.; mutable delta = 0.; - Rz(theta, q[0]); - H(q[0]); - CNOT(q[0], q[1]); - X(q[2]); - I(q[3]); - X(q[4]); + rz(__DoubleAsAngle__(theta, 53), q[0]); + h(q[0]); + cx(q[0], q[1]); + x(q[2]); + id(q[3]); + x(q[4]); set c w/= 0 <- QIR.Intrinsic.__quantum__qis__m__body(q[0]); set c w/= 1 <- QIR.Intrinsic.__quantum__qis__m__body(q[1]); set c2 w/= 0 <- QIR.Intrinsic.__quantum__qis__m__body(q[2]); @@ -246,64 +258,64 @@ c2[2] = measure q[4]; let qir = compile_qasm_to_qir(source, Profile::AdaptiveRI)?; expect![[r#" -%Result = type opaque -%Qubit = type opaque + %Result = type opaque + %Qubit = type opaque -define void @ENTRYPOINT__main() #0 { -block_0: - call void @__quantum__qis__rz__body(double 0.5, %Qubit* inttoptr (i64 0 to %Qubit*)) - call void @__quantum__qis__h__body(%Qubit* inttoptr (i64 0 to %Qubit*)) - call void @__quantum__qis__cx__body(%Qubit* inttoptr (i64 0 to %Qubit*), %Qubit* inttoptr (i64 1 to %Qubit*)) - call void @__quantum__qis__x__body(%Qubit* inttoptr (i64 2 to %Qubit*)) - call void @__quantum__qis__x__body(%Qubit* inttoptr (i64 4 to %Qubit*)) - call void @__quantum__qis__barrier__body() - call void @__quantum__qis__m__body(%Qubit* inttoptr (i64 0 to %Qubit*), %Result* inttoptr (i64 0 to %Result*)) - call void @__quantum__qis__m__body(%Qubit* inttoptr (i64 1 to %Qubit*), %Result* inttoptr (i64 1 to %Result*)) - call void @__quantum__qis__m__body(%Qubit* inttoptr (i64 2 to %Qubit*), %Result* inttoptr (i64 2 to %Result*)) - call void @__quantum__qis__m__body(%Qubit* inttoptr (i64 3 to %Qubit*), %Result* inttoptr (i64 3 to %Result*)) - call void @__quantum__qis__m__body(%Qubit* inttoptr (i64 4 to %Qubit*), %Result* inttoptr (i64 4 to %Result*)) - call void @__quantum__rt__tuple_record_output(i64 2, i8* null) - call void @__quantum__rt__array_record_output(i64 3, i8* null) - call void @__quantum__rt__result_record_output(%Result* inttoptr (i64 4 to %Result*), i8* null) - call void @__quantum__rt__result_record_output(%Result* inttoptr (i64 3 to %Result*), i8* null) - call void @__quantum__rt__result_record_output(%Result* inttoptr (i64 2 to %Result*), i8* null) - call void @__quantum__rt__array_record_output(i64 2, i8* null) - call void @__quantum__rt__result_record_output(%Result* inttoptr (i64 1 to %Result*), i8* null) - call void @__quantum__rt__result_record_output(%Result* inttoptr (i64 0 to %Result*), i8* null) - ret void -} + define void @ENTRYPOINT__main() #0 { + block_0: + call void @__quantum__qis__rz__body(double 0.4999999999999997, %Qubit* inttoptr (i64 0 to %Qubit*)) + call void @__quantum__qis__h__body(%Qubit* inttoptr (i64 0 to %Qubit*)) + call void @__quantum__qis__cx__body(%Qubit* inttoptr (i64 0 to %Qubit*), %Qubit* inttoptr (i64 1 to %Qubit*)) + call void @__quantum__qis__x__body(%Qubit* inttoptr (i64 2 to %Qubit*)) + call void @__quantum__qis__x__body(%Qubit* inttoptr (i64 4 to %Qubit*)) + call void @__quantum__qis__barrier__body() + call void @__quantum__qis__m__body(%Qubit* inttoptr (i64 0 to %Qubit*), %Result* inttoptr (i64 0 to %Result*)) + call void @__quantum__qis__m__body(%Qubit* inttoptr (i64 1 to %Qubit*), %Result* inttoptr (i64 1 to %Result*)) + call void @__quantum__qis__m__body(%Qubit* inttoptr (i64 2 to %Qubit*), %Result* inttoptr (i64 2 to %Result*)) + call void @__quantum__qis__m__body(%Qubit* inttoptr (i64 3 to %Qubit*), %Result* inttoptr (i64 3 to %Result*)) + call void @__quantum__qis__m__body(%Qubit* inttoptr (i64 4 to %Qubit*), %Result* inttoptr (i64 4 to %Result*)) + call void @__quantum__rt__tuple_record_output(i64 2, i8* null) + call void @__quantum__rt__array_record_output(i64 3, i8* null) + call void @__quantum__rt__result_record_output(%Result* inttoptr (i64 4 to %Result*), i8* null) + call void @__quantum__rt__result_record_output(%Result* inttoptr (i64 3 to %Result*), i8* null) + call void @__quantum__rt__result_record_output(%Result* inttoptr (i64 2 to %Result*), i8* null) + call void @__quantum__rt__array_record_output(i64 2, i8* null) + call void @__quantum__rt__result_record_output(%Result* inttoptr (i64 1 to %Result*), i8* null) + call void @__quantum__rt__result_record_output(%Result* inttoptr (i64 0 to %Result*), i8* null) + ret void + } -declare void @__quantum__qis__rz__body(double, %Qubit*) + declare void @__quantum__qis__rz__body(double, %Qubit*) -declare void @__quantum__qis__h__body(%Qubit*) + declare void @__quantum__qis__h__body(%Qubit*) -declare void @__quantum__qis__cx__body(%Qubit*, %Qubit*) + declare void @__quantum__qis__cx__body(%Qubit*, %Qubit*) -declare void @__quantum__qis__x__body(%Qubit*) + declare void @__quantum__qis__x__body(%Qubit*) -declare void @__quantum__qis__barrier__body() + declare void @__quantum__qis__barrier__body() -declare void @__quantum__qis__m__body(%Qubit*, %Result*) #1 + declare void @__quantum__qis__m__body(%Qubit*, %Result*) #1 -declare void @__quantum__rt__tuple_record_output(i64, i8*) + declare void @__quantum__rt__tuple_record_output(i64, i8*) -declare void @__quantum__rt__array_record_output(i64, i8*) + declare void @__quantum__rt__array_record_output(i64, i8*) -declare void @__quantum__rt__result_record_output(%Result*, i8*) + declare void @__quantum__rt__result_record_output(%Result*, i8*) -attributes #0 = { "entry_point" "output_labeling_schema" "qir_profiles"="adaptive_profile" "required_num_qubits"="5" "required_num_results"="5" } -attributes #1 = { "irreversible" } + attributes #0 = { "entry_point" "output_labeling_schema" "qir_profiles"="adaptive_profile" "required_num_qubits"="5" "required_num_results"="5" } + attributes #1 = { "irreversible" } -; module flags + ; module flags -!llvm.module.flags = !{!0, !1, !2, !3, !4} + !llvm.module.flags = !{!0, !1, !2, !3, !4} -!0 = !{i32 1, !"qir_major_version", i32 1} -!1 = !{i32 7, !"qir_minor_version", i32 0} -!2 = !{i32 1, !"dynamic_qubit_management", i1 false} -!3 = !{i32 1, !"dynamic_result_management", i1 false} -!4 = !{i32 1, !"int_computations", !"i64"} -"#]] + !0 = !{i32 1, !"qir_major_version", i32 1} + !1 = !{i32 7, !"qir_minor_version", i32 0} + !2 = !{i32 1, !"dynamic_qubit_management", i1 false} + !3 = !{i32 1, !"dynamic_result_management", i1 false} + !4 = !{i32 1, !"int_computations", !"i64"} + "#]] .assert_eq(&qir); Ok(()) diff --git a/compiler/qsc_qasm3/src/tests/scopes.rs b/compiler/qsc_qasm3/src/tests/scopes.rs index 5480fca9f1..7651ee223d 100644 --- a/compiler/qsc_qasm3/src/tests/scopes.rs +++ b/compiler/qsc_qasm3/src/tests/scopes.rs @@ -20,18 +20,19 @@ fn can_access_const_decls_from_global_scope() -> miette::Result<(), Vec> "#; let qsharp = compile_qasm_to_qsharp(source)?; - expect![ - r#" + expect![[r#" + import QasmStd.Angle.*; + import QasmStd.Convert.*; + import QasmStd.Intrinsic.*; let i = 7; let my_h : (Qubit) => Unit = (q) => { if i == 0 { - H(q); + h(q); }; }; let q = QIR.Runtime.__quantum__rt__qubit_allocate(); my_h(q); - "# - ] + "#]] .assert_eq(&qsharp); Ok(()) } diff --git a/compiler/qsc_qasm3/src/tests/statement/annotation.rs b/compiler/qsc_qasm3/src/tests/statement/annotation.rs index 64155978ba..1ad3d5ecb7 100644 --- a/compiler/qsc_qasm3/src/tests/statement/annotation.rs +++ b/compiler/qsc_qasm3/src/tests/statement/annotation.rs @@ -18,6 +18,9 @@ fn simulatable_intrinsic_can_be_applied_to_gate() -> miette::Result<(), Vec miette::Result<(), Vec miette::Result< let qsharp = compile_qasm_to_qsharp(source)?; expect![[r#" + import QasmStd.Angle.*; + import QasmStd.Convert.*; + import QasmStd.Intrinsic.*; let a = 1; let b = 2. + Microsoft.Quantum.Convert.IntAsDouble(a); let c = Microsoft.Quantum.Convert.IntAsDouble(a) + 3.; @@ -103,6 +109,9 @@ fn can_assign_const_expr_to_non_const_decl() -> miette::Result<(), Vec> let qsharp = compile_qasm_to_qsharp(source)?; expect![[r#" + import QasmStd.Angle.*; + import QasmStd.Convert.*; + import QasmStd.Intrinsic.*; let a = 1; let b = 2; mutable c = a + b; @@ -120,6 +129,9 @@ fn ident_const() -> miette::Result<(), Vec> { let qsharp = compile_qasm_to_qsharp(source)?; expect![[r#" + import QasmStd.Angle.*; + import QasmStd.Convert.*; + import QasmStd.Intrinsic.*; let a = 1; mutable r = [Zero]; "#]] @@ -157,6 +169,9 @@ fn unary_op_neg_float() -> miette::Result<(), Vec> { let qsharp = compile_qasm_to_qsharp(source)?; expect![[r#" + import QasmStd.Angle.*; + import QasmStd.Convert.*; + import QasmStd.Intrinsic.*; let a = -1.; let b = -a; mutable r = [Zero]; @@ -175,6 +190,9 @@ fn unary_op_neg_int() -> miette::Result<(), Vec> { let qsharp = compile_qasm_to_qsharp(source)?; expect![[r#" + import QasmStd.Angle.*; + import QasmStd.Convert.*; + import QasmStd.Intrinsic.*; let a = -1; let b = -a; mutable r = [Zero]; @@ -184,16 +202,23 @@ fn unary_op_neg_int() -> miette::Result<(), Vec> { } #[test] -#[ignore = "casting float to angle is not yet supported"] fn unary_op_neg_angle() -> miette::Result<(), Vec> { let source = r#" - const angle a = -1.0; - const bool b = a; + const angle[32] a = -1.0; + const bit b = a; bit[b] r; "#; let qsharp = compile_qasm_to_qsharp(source)?; - expect![[r#""#]].assert_eq(&qsharp); + expect![[r#" + import QasmStd.Angle.*; + import QasmStd.Convert.*; + import QasmStd.Intrinsic.*; + let a = __DoubleAsAngle__(-1., 32); + let b = __AngleAsResult__(a); + mutable r = [Zero]; + "#]] + .assert_eq(&qsharp); Ok(()) } @@ -207,6 +232,9 @@ fn unary_op_negb_uint() -> miette::Result<(), Vec> { let qsharp = compile_qasm_to_qsharp(source)?; expect![[r#" + import QasmStd.Angle.*; + import QasmStd.Convert.*; + import QasmStd.Intrinsic.*; let a = 5; let b = ~~~a; mutable r = [Zero, Zero]; @@ -216,20 +244,28 @@ fn unary_op_negb_uint() -> miette::Result<(), Vec> { } #[test] -#[ignore = "angles are not yet supported"] -fn unary_op_negb_angle() { + +fn unary_op_negb_angle() -> miette::Result<(), Vec> { let source = r#" - const angle a = 1.0; - const bool b = ~a; + const angle[32] a = 1.0; + const bit b = ~a; bit[b] r; "#; - let Err(errs) = compile_qasm_to_qsharp(source) else { - panic!("should have generated an error"); - }; - let errs: Vec<_> = errs.iter().map(|e| format!("{e:?}")).collect(); - let errs_string = errs.join("\n"); - expect![[r#""#]].assert_eq(&errs_string); + let qsharp = compile_qasm_to_qsharp(source)?; + expect![[r#" + import QasmStd.Angle.*; + import QasmStd.Convert.*; + import QasmStd.Intrinsic.*; + let a = new __Angle__ { + Value = 683565276, + Size = 32 + }; + let b = __AngleAsResult__(__AngleNotB__(a)); + mutable r = [Zero]; + "#]] + .assert_eq(&qsharp); + Ok(()) } #[test] @@ -242,6 +278,9 @@ fn unary_op_negb_bit() -> miette::Result<(), Vec> { let qsharp = compile_qasm_to_qsharp(source)?; expect![[r#" + import QasmStd.Angle.*; + import QasmStd.Convert.*; + import QasmStd.Intrinsic.*; let a = Zero; let b = ~~~a; mutable r = [Zero]; @@ -260,9 +299,9 @@ fn unary_op_negb_bitarray() -> miette::Result<(), Vec> { let qsharp = compile_qasm_to_qsharp(source)?; expect![[r#" - function __ResultArrayAsIntBE__(results : Result[]) : Int { - Microsoft.Quantum.Convert.ResultArrayAsInt(Microsoft.Quantum.Arrays.Reversed(results)) - } + import QasmStd.Angle.*; + import QasmStd.Convert.*; + import QasmStd.Intrinsic.*; let a = [One, Zero, One]; let b = __ResultArrayAsIntBE__(~~~a); mutable r = [Zero, Zero]; @@ -284,6 +323,9 @@ fn lhs_ty_equals_rhs_ty_assumption_holds() -> miette::Result<(), Vec> { let qsharp = compile_qasm_to_qsharp(source)?; expect![[r#" + import QasmStd.Angle.*; + import QasmStd.Convert.*; + import QasmStd.Intrinsic.*; let a = 1; let b = 2.; let c = Microsoft.Quantum.Math.Truncate(Microsoft.Quantum.Convert.IntAsDouble(a) + b); @@ -307,6 +349,9 @@ fn binary_op_shl_uint() -> miette::Result<(), Vec> { let qsharp = compile_qasm_to_qsharp(source)?; expect![[r#" + import QasmStd.Angle.*; + import QasmStd.Convert.*; + import QasmStd.Intrinsic.*; let a = 1; let b = a <<< 2; mutable r = [Zero, Zero, Zero, Zero]; @@ -316,17 +361,29 @@ fn binary_op_shl_uint() -> miette::Result<(), Vec> { } #[test] -#[ignore = "angles are not yet supported"] + fn binary_op_shl_angle() -> miette::Result<(), Vec> { let source = r#" - const angle a = 1; - const angle b = a << 2; - const bool c = b; + const angle[32] a = 1.0; + const angle[32] b = a << 2; + const bit c = b; bit[c] r; "#; let qsharp = compile_qasm_to_qsharp(source)?; - expect![[r#""#]].assert_eq(&qsharp); + expect![[r#" + import QasmStd.Angle.*; + import QasmStd.Convert.*; + import QasmStd.Intrinsic.*; + let a = new __Angle__ { + Value = 683565276, + Size = 32 + }; + let b = __AngleShl__(a, 2); + let c = __AngleAsResult__(b); + mutable r = [Zero]; + "#]] + .assert_eq(&qsharp); Ok(()) } @@ -340,13 +397,9 @@ fn binary_op_shl_bit() -> miette::Result<(), Vec> { let qsharp = compile_qasm_to_qsharp(source)?; expect![[r#" - function __ResultAsInt__(input : Result) : Int { - if Microsoft.Quantum.Convert.ResultAsBool(input) { - 1 - } else { - 0 - } - } + import QasmStd.Angle.*; + import QasmStd.Convert.*; + import QasmStd.Intrinsic.*; let a = One; let b = if __ResultAsInt__(a) <<< 2 == 0 { One @@ -369,21 +422,9 @@ fn binary_op_shl_bitarray() -> miette::Result<(), Vec> { let qsharp = compile_qasm_to_qsharp(source)?; expect![[r#" - function __BoolAsResult__(input : Bool) : Result { - Microsoft.Quantum.Convert.BoolAsResult(input) - } - function __IntAsResultArrayBE__(number : Int, bits : Int) : Result[] { - mutable runningValue = number; - mutable result = []; - for _ in 1..bits { - set result += [__BoolAsResult__((runningValue &&& 1) != 0)]; - set runningValue >>>= 1; - } - Microsoft.Quantum.Arrays.Reversed(result) - } - function __ResultArrayAsIntBE__(results : Result[]) : Int { - Microsoft.Quantum.Convert.ResultArrayAsInt(Microsoft.Quantum.Arrays.Reversed(results)) - } + import QasmStd.Angle.*; + import QasmStd.Convert.*; + import QasmStd.Intrinsic.*; let a = [One, Zero, One]; let b = __IntAsResultArrayBE__(__ResultArrayAsIntBE__(a) <<< 2, 3); mutable r = [Zero, Zero, Zero, Zero]; @@ -471,6 +512,9 @@ fn binary_op_shr_uint() -> miette::Result<(), Vec> { let qsharp = compile_qasm_to_qsharp(source)?; expect![[r#" + import QasmStd.Angle.*; + import QasmStd.Convert.*; + import QasmStd.Intrinsic.*; let a = 5; let b = a >>> 2; mutable r = [Zero]; @@ -480,17 +524,29 @@ fn binary_op_shr_uint() -> miette::Result<(), Vec> { } #[test] -#[ignore = "angles are not yet supported"] + fn binary_op_shr_angle() -> miette::Result<(), Vec> { let source = r#" - const angle a = 1; - const angle b = a >> 2; - const bool c = b; + const angle[32] a = 1.0; + const angle[32] b = a >> 2; + const bit c = b; bit[c] r; "#; let qsharp = compile_qasm_to_qsharp(source)?; - expect![[r#""#]].assert_eq(&qsharp); + expect![[r#" + import QasmStd.Angle.*; + import QasmStd.Convert.*; + import QasmStd.Intrinsic.*; + let a = new __Angle__ { + Value = 683565276, + Size = 32 + }; + let b = __AngleShr__(a, 2); + let c = __AngleAsResult__(b); + mutable r = [Zero]; + "#]] + .assert_eq(&qsharp); Ok(()) } @@ -504,13 +560,9 @@ fn binary_op_shr_bit() -> miette::Result<(), Vec> { let qsharp = compile_qasm_to_qsharp(source)?; expect![[r#" - function __ResultAsInt__(input : Result) : Int { - if Microsoft.Quantum.Convert.ResultAsBool(input) { - 1 - } else { - 0 - } - } + import QasmStd.Angle.*; + import QasmStd.Convert.*; + import QasmStd.Intrinsic.*; let a = One; let b = if __ResultAsInt__(a) >>> 2 == 0 { One @@ -533,21 +585,9 @@ fn binary_op_shr_bitarray() -> miette::Result<(), Vec> { let qsharp = compile_qasm_to_qsharp(source)?; expect![[r#" - function __BoolAsResult__(input : Bool) : Result { - Microsoft.Quantum.Convert.BoolAsResult(input) - } - function __IntAsResultArrayBE__(number : Int, bits : Int) : Result[] { - mutable runningValue = number; - mutable result = []; - for _ in 1..bits { - set result += [__BoolAsResult__((runningValue &&& 1) != 0)]; - set runningValue >>>= 1; - } - Microsoft.Quantum.Arrays.Reversed(result) - } - function __ResultArrayAsIntBE__(results : Result[]) : Int { - Microsoft.Quantum.Convert.ResultArrayAsInt(Microsoft.Quantum.Arrays.Reversed(results)) - } + import QasmStd.Angle.*; + import QasmStd.Convert.*; + import QasmStd.Intrinsic.*; let a = [One, Zero, One, One]; let b = __IntAsResultArrayBE__(__ResultArrayAsIntBE__(a) >>> 2, 4); mutable r = [Zero, Zero]; @@ -637,6 +677,9 @@ fn binary_op_andb_uint() -> miette::Result<(), Vec> { let qsharp = compile_qasm_to_qsharp(source)?; expect![[r#" + import QasmStd.Angle.*; + import QasmStd.Convert.*; + import QasmStd.Intrinsic.*; let a = 5; let b = a &&& 6; mutable r = [Zero, Zero, Zero, Zero]; @@ -646,17 +689,34 @@ fn binary_op_andb_uint() -> miette::Result<(), Vec> { } #[test] -#[ignore = "angles are not yet supported"] + fn binary_op_andb_angle() -> miette::Result<(), Vec> { let source = r#" - const angle a = 1; - const angle b = a & 2; - const bool c = b; - bit[c] r; + const angle[32] a = 1.0; + const angle[32] b = 2.0; + const angle[32] c = a & b; + const bit d = c; + bit[d] r; "#; let qsharp = compile_qasm_to_qsharp(source)?; - expect![[r#""#]].assert_eq(&qsharp); + expect![[r#" + import QasmStd.Angle.*; + import QasmStd.Convert.*; + import QasmStd.Intrinsic.*; + let a = new __Angle__ { + Value = 683565276, + Size = 32 + }; + let b = new __Angle__ { + Value = 1367130551, + Size = 32 + }; + let c = __AngleAndB__(a, b); + let d = __AngleAsResult__(c); + mutable r = [Zero]; + "#]] + .assert_eq(&qsharp); Ok(()) } @@ -670,13 +730,9 @@ fn binary_op_andb_bit() -> miette::Result<(), Vec> { let qsharp = compile_qasm_to_qsharp(source)?; expect![[r#" - function __ResultAsInt__(input : Result) : Int { - if Microsoft.Quantum.Convert.ResultAsBool(input) { - 1 - } else { - 0 - } - } + import QasmStd.Angle.*; + import QasmStd.Convert.*; + import QasmStd.Intrinsic.*; let a = One; let b = if __ResultAsInt__(a) &&& 0 == 0 { One @@ -699,21 +755,9 @@ fn binary_op_andb_bitarray() -> miette::Result<(), Vec> { let qsharp = compile_qasm_to_qsharp(source)?; expect![[r#" - function __BoolAsResult__(input : Bool) : Result { - Microsoft.Quantum.Convert.BoolAsResult(input) - } - function __IntAsResultArrayBE__(number : Int, bits : Int) : Result[] { - mutable runningValue = number; - mutable result = []; - for _ in 1..bits { - set result += [__BoolAsResult__((runningValue &&& 1) != 0)]; - set runningValue >>>= 1; - } - Microsoft.Quantum.Arrays.Reversed(result) - } - function __ResultArrayAsIntBE__(results : Result[]) : Int { - Microsoft.Quantum.Convert.ResultArrayAsInt(Microsoft.Quantum.Arrays.Reversed(results)) - } + import QasmStd.Angle.*; + import QasmStd.Convert.*; + import QasmStd.Intrinsic.*; let a = [One, Zero, One, One]; let b = __IntAsResultArrayBE__(__ResultArrayAsIntBE__(a) &&& __ResultArrayAsIntBE__([Zero, One, One, Zero]), 4); mutable r = [Zero, Zero]; @@ -734,6 +778,9 @@ fn binary_op_orb_uint() -> miette::Result<(), Vec> { let qsharp = compile_qasm_to_qsharp(source)?; expect![[r#" + import QasmStd.Angle.*; + import QasmStd.Convert.*; + import QasmStd.Intrinsic.*; let a = 5; let b = a ||| 6; mutable r = [Zero, Zero, Zero, Zero, Zero, Zero, Zero]; @@ -743,17 +790,34 @@ fn binary_op_orb_uint() -> miette::Result<(), Vec> { } #[test] -#[ignore = "angles are not yet supported"] + fn binary_op_orb_angle() -> miette::Result<(), Vec> { let source = r#" - const angle a = 1.0; - const angle b = a | 2.0; - const bool c = b; - bit[c] r; + const angle[32] a = 1.0; + const angle[32] b = 2.0; + const angle[32] c = a | b; + const bool d = c; + bit[d] r; "#; let qsharp = compile_qasm_to_qsharp(source)?; - expect![[r#""#]].assert_eq(&qsharp); + expect![[r#" + import QasmStd.Angle.*; + import QasmStd.Convert.*; + import QasmStd.Intrinsic.*; + let a = new __Angle__ { + Value = 683565276, + Size = 32 + }; + let b = new __Angle__ { + Value = 1367130551, + Size = 32 + }; + let c = __AngleOrB__(a, b); + let d = __AngleAsBool__(c); + mutable r = [Zero]; + "#]] + .assert_eq(&qsharp); Ok(()) } @@ -767,13 +831,9 @@ fn binary_op_orb_bit() -> miette::Result<(), Vec> { let qsharp = compile_qasm_to_qsharp(source)?; expect![[r#" - function __ResultAsInt__(input : Result) : Int { - if Microsoft.Quantum.Convert.ResultAsBool(input) { - 1 - } else { - 0 - } - } + import QasmStd.Angle.*; + import QasmStd.Convert.*; + import QasmStd.Intrinsic.*; let a = One; let b = if __ResultAsInt__(a) ||| 0 == 0 { One @@ -796,21 +856,9 @@ fn binary_op_orb_bitarray() -> miette::Result<(), Vec> { let qsharp = compile_qasm_to_qsharp(source)?; expect![[r#" - function __BoolAsResult__(input : Bool) : Result { - Microsoft.Quantum.Convert.BoolAsResult(input) - } - function __IntAsResultArrayBE__(number : Int, bits : Int) : Result[] { - mutable runningValue = number; - mutable result = []; - for _ in 1..bits { - set result += [__BoolAsResult__((runningValue &&& 1) != 0)]; - set runningValue >>>= 1; - } - Microsoft.Quantum.Arrays.Reversed(result) - } - function __ResultArrayAsIntBE__(results : Result[]) : Int { - Microsoft.Quantum.Convert.ResultArrayAsInt(Microsoft.Quantum.Arrays.Reversed(results)) - } + import QasmStd.Angle.*; + import QasmStd.Convert.*; + import QasmStd.Intrinsic.*; let a = [Zero, Zero, One]; let b = __IntAsResultArrayBE__(__ResultArrayAsIntBE__(a) ||| __ResultArrayAsIntBE__([One, Zero, Zero]), 3); mutable r = [Zero, Zero, Zero, Zero, Zero]; @@ -831,6 +879,9 @@ fn binary_op_xorb_uint() -> miette::Result<(), Vec> { let qsharp = compile_qasm_to_qsharp(source)?; expect![[r#" + import QasmStd.Angle.*; + import QasmStd.Convert.*; + import QasmStd.Intrinsic.*; let a = 5; let b = a ^^^ 6; mutable r = [Zero, Zero, Zero]; @@ -840,17 +891,34 @@ fn binary_op_xorb_uint() -> miette::Result<(), Vec> { } #[test] -#[ignore = "angles are not yet supported"] + fn binary_op_xorb_angle() -> miette::Result<(), Vec> { let source = r#" - const angle a = 1; - const angle b = a ^ 2; - const bool c = b; - bit[c] r; + const angle[32] a = 1.0; + const angle[32] b = 2.0; + const angle[32] c = a ^ b; + const bit d = c; + bit[d] r; "#; let qsharp = compile_qasm_to_qsharp(source)?; - expect![[r#""#]].assert_eq(&qsharp); + expect![[r#" + import QasmStd.Angle.*; + import QasmStd.Convert.*; + import QasmStd.Intrinsic.*; + let a = new __Angle__ { + Value = 683565276, + Size = 32 + }; + let b = new __Angle__ { + Value = 1367130551, + Size = 32 + }; + let c = __AngleXorB__(a, b); + let d = __AngleAsResult__(c); + mutable r = [Zero]; + "#]] + .assert_eq(&qsharp); Ok(()) } @@ -864,13 +932,9 @@ fn binary_op_xorb_bit() -> miette::Result<(), Vec> { let qsharp = compile_qasm_to_qsharp(source)?; expect![[r#" - function __ResultAsInt__(input : Result) : Int { - if Microsoft.Quantum.Convert.ResultAsBool(input) { - 1 - } else { - 0 - } - } + import QasmStd.Angle.*; + import QasmStd.Convert.*; + import QasmStd.Intrinsic.*; let a = One; let b = if __ResultAsInt__(a) ^^^ 1 == 0 { One @@ -893,21 +957,9 @@ fn binary_op_xorb_bitarray() -> miette::Result<(), Vec> { let qsharp = compile_qasm_to_qsharp(source)?; expect![[r#" - function __BoolAsResult__(input : Bool) : Result { - Microsoft.Quantum.Convert.BoolAsResult(input) - } - function __IntAsResultArrayBE__(number : Int, bits : Int) : Result[] { - mutable runningValue = number; - mutable result = []; - for _ in 1..bits { - set result += [__BoolAsResult__((runningValue &&& 1) != 0)]; - set runningValue >>>= 1; - } - Microsoft.Quantum.Arrays.Reversed(result) - } - function __ResultArrayAsIntBE__(results : Result[]) : Int { - Microsoft.Quantum.Convert.ResultArrayAsInt(Microsoft.Quantum.Arrays.Reversed(results)) - } + import QasmStd.Angle.*; + import QasmStd.Convert.*; + import QasmStd.Intrinsic.*; let a = [One, Zero, One, One]; let b = __IntAsResultArrayBE__(__ResultArrayAsIntBE__(a) ^^^ __ResultArrayAsIntBE__([One, One, One, Zero]), 4); mutable r = [Zero, Zero, Zero, Zero, Zero]; @@ -931,6 +983,9 @@ fn binary_op_andl_bool() -> miette::Result<(), Vec> { let qsharp = compile_qasm_to_qsharp(source)?; expect![[r#" + import QasmStd.Angle.*; + import QasmStd.Convert.*; + import QasmStd.Intrinsic.*; let f = false; let t = true; mutable r1 = []; @@ -955,6 +1010,9 @@ fn binary_op_orl_bool() -> miette::Result<(), Vec> { let qsharp = compile_qasm_to_qsharp(source)?; expect![[r#" + import QasmStd.Angle.*; + import QasmStd.Convert.*; + import QasmStd.Intrinsic.*; let f = false; let t = true; mutable r1 = []; @@ -984,6 +1042,9 @@ fn binary_op_comparison_int() -> miette::Result<(), Vec> { let qsharp = compile_qasm_to_qsharp(source)?; expect![[r#" + import QasmStd.Angle.*; + import QasmStd.Convert.*; + import QasmStd.Intrinsic.*; let a = 2; mutable r1 = [Zero]; mutable r2 = []; @@ -1010,6 +1071,9 @@ fn binary_op_comparison_uint() -> miette::Result<(), Vec> { let qsharp = compile_qasm_to_qsharp(source)?; expect![[r#" + import QasmStd.Angle.*; + import QasmStd.Convert.*; + import QasmStd.Intrinsic.*; let a = 2; mutable r1 = [Zero]; mutable r2 = []; @@ -1023,10 +1087,10 @@ fn binary_op_comparison_uint() -> miette::Result<(), Vec> { } #[test] -#[ignore = "angles are not yet supported"] + fn binary_op_comparison_angle() -> miette::Result<(), Vec> { let source = r#" - const angle a = 2.0; + const angle[32] a = 2.0; bit[a == a] r1; bit[a != a] r2; bit[a > a] r3; @@ -1036,7 +1100,22 @@ fn binary_op_comparison_angle() -> miette::Result<(), Vec> { "#; let qsharp = compile_qasm_to_qsharp(source)?; - expect![[r#""#]].assert_eq(&qsharp); + expect![[r#" + import QasmStd.Angle.*; + import QasmStd.Convert.*; + import QasmStd.Intrinsic.*; + let a = new __Angle__ { + Value = 1367130551, + Size = 32 + }; + mutable r1 = [Zero]; + mutable r2 = []; + mutable r3 = []; + mutable r4 = [Zero]; + mutable r5 = []; + mutable r6 = [Zero]; + "#]] + .assert_eq(&qsharp); Ok(()) } @@ -1054,6 +1133,9 @@ fn binary_op_comparison_bit() -> miette::Result<(), Vec> { let qsharp = compile_qasm_to_qsharp(source)?; expect![[r#" + import QasmStd.Angle.*; + import QasmStd.Convert.*; + import QasmStd.Intrinsic.*; let a = One; mutable r1 = [Zero]; mutable r2 = []; @@ -1080,6 +1162,9 @@ fn binary_op_comparison_bitarray() -> miette::Result<(), Vec> { let qsharp = compile_qasm_to_qsharp(source)?; expect![[r#" + import QasmStd.Angle.*; + import QasmStd.Convert.*; + import QasmStd.Intrinsic.*; let a = [One, Zero]; mutable r1 = [Zero]; mutable r2 = []; @@ -1106,6 +1191,9 @@ fn binary_op_add_int() -> miette::Result<(), Vec> { let qsharp = compile_qasm_to_qsharp(source)?; expect![[r#" + import QasmStd.Angle.*; + import QasmStd.Convert.*; + import QasmStd.Intrinsic.*; let a = 1; let b = 2; mutable r = [Zero, Zero, Zero]; @@ -1124,6 +1212,9 @@ fn binary_op_add_uint() -> miette::Result<(), Vec> { let qsharp = compile_qasm_to_qsharp(source)?; expect![[r#" + import QasmStd.Angle.*; + import QasmStd.Convert.*; + import QasmStd.Intrinsic.*; let a = 1; let b = 2; mutable r = [Zero, Zero, Zero]; @@ -1142,6 +1233,9 @@ fn binary_op_add_float() -> miette::Result<(), Vec> { let qsharp = compile_qasm_to_qsharp(source)?; expect![[r#" + import QasmStd.Angle.*; + import QasmStd.Convert.*; + import QasmStd.Intrinsic.*; let a = 1.; let b = 2.; mutable r = [Zero, Zero, Zero]; @@ -1151,16 +1245,32 @@ fn binary_op_add_float() -> miette::Result<(), Vec> { } #[test] -#[ignore = "angles are not yet supported"] + fn binary_op_add_angle() -> miette::Result<(), Vec> { let source = r#" - const angle a = 1.0; - const angle b = 2.0; - bit[a + b] r; + const angle[32] a = 1.0; + const angle[32] b = 2.0; + const bit c = a + b; + bit[c] r; "#; let qsharp = compile_qasm_to_qsharp(source)?; - expect![[r#""#]].assert_eq(&qsharp); + expect![[r#" + import QasmStd.Angle.*; + import QasmStd.Convert.*; + import QasmStd.Intrinsic.*; + let a = new __Angle__ { + Value = 683565276, + Size = 32 + }; + let b = new __Angle__ { + Value = 1367130551, + Size = 32 + }; + let c = __AngleAsResult__(__AddAngles__(a, b)); + mutable r = [Zero]; + "#]] + .assert_eq(&qsharp); Ok(()) } @@ -1176,6 +1286,9 @@ fn binary_op_sub_int() -> miette::Result<(), Vec> { let qsharp = compile_qasm_to_qsharp(source)?; expect![[r#" + import QasmStd.Angle.*; + import QasmStd.Convert.*; + import QasmStd.Intrinsic.*; let a = 3; let b = 2; mutable r = [Zero]; @@ -1194,6 +1307,9 @@ fn binary_op_sub_uint() -> miette::Result<(), Vec> { let qsharp = compile_qasm_to_qsharp(source)?; expect![[r#" + import QasmStd.Angle.*; + import QasmStd.Convert.*; + import QasmStd.Intrinsic.*; let a = 3; let b = 2; mutable r = [Zero]; @@ -1212,6 +1328,9 @@ fn binary_op_sub_float() -> miette::Result<(), Vec> { let qsharp = compile_qasm_to_qsharp(source)?; expect![[r#" + import QasmStd.Angle.*; + import QasmStd.Convert.*; + import QasmStd.Intrinsic.*; let a = 3.; let b = 2.; mutable r = [Zero]; @@ -1221,16 +1340,32 @@ fn binary_op_sub_float() -> miette::Result<(), Vec> { } #[test] -#[ignore = "angles are not yet supported"] + fn binary_op_sub_angle() -> miette::Result<(), Vec> { let source = r#" - const float a = 3.0; - const float b = 2.0; - bit[a - b] r; + const angle[32] a = 1.0; + const angle[32] b = 2.0; + const bit c = a - b; + bit[c] r; "#; let qsharp = compile_qasm_to_qsharp(source)?; - expect![[r#""#]].assert_eq(&qsharp); + expect![[r#" + import QasmStd.Angle.*; + import QasmStd.Convert.*; + import QasmStd.Intrinsic.*; + let a = new __Angle__ { + Value = 683565276, + Size = 32 + }; + let b = new __Angle__ { + Value = 1367130551, + Size = 32 + }; + let c = __AngleAsResult__(__SubtractAngles__(a, b)); + mutable r = [Zero]; + "#]] + .assert_eq(&qsharp); Ok(()) } @@ -1246,6 +1381,9 @@ fn binary_op_mul_int() -> miette::Result<(), Vec> { let qsharp = compile_qasm_to_qsharp(source)?; expect![[r#" + import QasmStd.Angle.*; + import QasmStd.Convert.*; + import QasmStd.Intrinsic.*; let a = 3; let b = 2; mutable r = [Zero, Zero, Zero, Zero, Zero, Zero]; @@ -1264,6 +1402,9 @@ fn binary_op_mul_uint() -> miette::Result<(), Vec> { let qsharp = compile_qasm_to_qsharp(source)?; expect![[r#" + import QasmStd.Angle.*; + import QasmStd.Convert.*; + import QasmStd.Intrinsic.*; let a = 3; let b = 2; mutable r = [Zero, Zero, Zero, Zero, Zero, Zero]; @@ -1282,6 +1423,9 @@ fn binary_op_mul_float() -> miette::Result<(), Vec> { let qsharp = compile_qasm_to_qsharp(source)?; expect![[r#" + import QasmStd.Angle.*; + import QasmStd.Convert.*; + import QasmStd.Intrinsic.*; let a = 3.; let b = 2.; mutable r = [Zero, Zero, Zero, Zero, Zero, Zero]; @@ -1291,17 +1435,33 @@ fn binary_op_mul_float() -> miette::Result<(), Vec> { } #[test] -#[ignore = "angles are not yet supported"] + fn binary_op_mul_angle() -> miette::Result<(), Vec> { let source = r#" - const float a = 3.0; + const angle[32] a = 1.0; const uint b = 2; - bit[a * b] r1; - bit[b * a] r2; + const bit c1 = a * b; + const bit c2 = b * a; + bit[c1] r1; + bit[c2] r2; "#; let qsharp = compile_qasm_to_qsharp(source)?; - expect![[r#""#]].assert_eq(&qsharp); + expect![[r#" + import QasmStd.Angle.*; + import QasmStd.Convert.*; + import QasmStd.Intrinsic.*; + let a = new __Angle__ { + Value = 683565276, + Size = 32 + }; + let b = 2; + let c1 = __AngleAsResult__(__MultiplyAngleByInt__(a, b)); + let c2 = __AngleAsResult__(__MultiplyAngleByInt__(a, b)); + mutable r1 = [Zero]; + mutable r2 = [Zero]; + "#]] + .assert_eq(&qsharp); Ok(()) } @@ -1317,6 +1477,9 @@ fn binary_op_div_int() -> miette::Result<(), Vec> { let qsharp = compile_qasm_to_qsharp(source)?; expect![[r#" + import QasmStd.Angle.*; + import QasmStd.Convert.*; + import QasmStd.Intrinsic.*; let a = 6; let b = 2; mutable r = [Zero, Zero, Zero]; @@ -1335,6 +1498,9 @@ fn binary_op_div_uint() -> miette::Result<(), Vec> { let qsharp = compile_qasm_to_qsharp(source)?; expect![[r#" + import QasmStd.Angle.*; + import QasmStd.Convert.*; + import QasmStd.Intrinsic.*; let a = 6; let b = 2; mutable r = [Zero, Zero, Zero]; @@ -1353,6 +1519,9 @@ fn binary_op_div_float() -> miette::Result<(), Vec> { let qsharp = compile_qasm_to_qsharp(source)?; expect![[r#" + import QasmStd.Angle.*; + import QasmStd.Convert.*; + import QasmStd.Intrinsic.*; let a = 6.; let b = 2.; mutable r = [Zero, Zero, Zero]; @@ -1362,16 +1531,42 @@ fn binary_op_div_float() -> miette::Result<(), Vec> { } #[test] -#[ignore = "angles are not yet supported"] + fn binary_op_div_angle() -> miette::Result<(), Vec> { let source = r#" - const angle a = 6.0; - const uint b = 2; - bit[a / b] r; + const angle[32] a = 12.0; + const angle[48] b = 6.0; + const uint c = 2; + const bit d = a / b; + const bit e = a / c; + bit[d] r1; + bit[e] r2; "#; let qsharp = compile_qasm_to_qsharp(source)?; - expect![[r#""#]].assert_eq(&qsharp); + expect![[r#" + import QasmStd.Angle.*; + import QasmStd.Convert.*; + import QasmStd.Intrinsic.*; + let a = new __Angle__ { + Value = 3907816011, + Size = 32 + }; + let b = new __Angle__ { + Value = 268788803401062, + Size = 48 + }; + let c = 2; + let d = if __DivideAngleByAngle__(__ConvertAngleToWidthNoTrunc__(a, 48), b) == 0 { + One + } else { + Zero + }; + let e = __AngleAsResult__(__DivideAngleByInt__(a, c)); + mutable r1 = []; + mutable r2 = [Zero]; + "#]] + .assert_eq(&qsharp); Ok(()) } @@ -1386,6 +1581,9 @@ fn binary_op_mod_int() -> miette::Result<(), Vec> { let qsharp = compile_qasm_to_qsharp(source)?; expect![[r#" + import QasmStd.Angle.*; + import QasmStd.Convert.*; + import QasmStd.Intrinsic.*; let a = 8; mutable r = [Zero, Zero]; "#]] @@ -1402,6 +1600,9 @@ fn binary_op_mod_uint() -> miette::Result<(), Vec> { let qsharp = compile_qasm_to_qsharp(source)?; expect![[r#" + import QasmStd.Angle.*; + import QasmStd.Convert.*; + import QasmStd.Intrinsic.*; let a = 8; mutable r = [Zero, Zero]; "#]] @@ -1421,6 +1622,9 @@ fn binary_op_pow_int() -> miette::Result<(), Vec> { let qsharp = compile_qasm_to_qsharp(source)?; expect![[r#" + import QasmStd.Angle.*; + import QasmStd.Convert.*; + import QasmStd.Intrinsic.*; let a = 2; let b = 3; mutable r = [Zero, Zero, Zero, Zero, Zero, Zero, Zero, Zero]; @@ -1439,6 +1643,9 @@ fn binary_op_pow_uint() -> miette::Result<(), Vec> { let qsharp = compile_qasm_to_qsharp(source)?; expect![[r#" + import QasmStd.Angle.*; + import QasmStd.Convert.*; + import QasmStd.Intrinsic.*; let a = 2; let b = 3; mutable r = [Zero, Zero, Zero, Zero, Zero, Zero, Zero, Zero]; @@ -1457,6 +1664,9 @@ fn binary_op_pow_float() -> miette::Result<(), Vec> { let qsharp = compile_qasm_to_qsharp(source)?; expect![[r#" + import QasmStd.Angle.*; + import QasmStd.Convert.*; + import QasmStd.Intrinsic.*; let a = 2.; let b = 3.; mutable r = [Zero, Zero, Zero, Zero, Zero, Zero, Zero, Zero]; @@ -1473,30 +1683,34 @@ fn cast_to_bool() -> miette::Result<(), Vec> { const int a = 0; const uint b = 1; const float c = 2.0; - // const angle d = 2.0; + const angle[32] d = 2.0; const bit e = 1; const bool s1 = a; const bool s2 = b; const bool s3 = c; - // const bool s4 = d; + const bool s4 = d; const bool s5 = e; bit[s1] r1; bit[s2] r2; bit[s3] r3; - // bit[s4] r4; + bit[s4] r4; bit[s5] r5; "#; let qsharp = compile_qasm_to_qsharp(source)?; expect![[r#" - function __ResultAsBool__(input : Result) : Bool { - Microsoft.Quantum.Convert.ResultAsBool(input) - } + import QasmStd.Angle.*; + import QasmStd.Convert.*; + import QasmStd.Intrinsic.*; let a = 0; let b = 1; let c = 2.; + let d = new __Angle__ { + Value = 1367130551, + Size = 32 + }; let e = One; let s1 = if a == 0 { false @@ -1513,10 +1727,12 @@ fn cast_to_bool() -> miette::Result<(), Vec> { } else { true }; + let s4 = __AngleAsBool__(d); let s5 = __ResultAsBool__(e); mutable r1 = []; mutable r2 = [Zero]; mutable r3 = [Zero]; + mutable r4 = [Zero]; mutable r5 = [Zero]; "#]] .assert_eq(&qsharp); @@ -1544,20 +1760,9 @@ fn cast_to_int() -> miette::Result<(), Vec> { let qsharp = compile_qasm_to_qsharp(source)?; expect![[r#" - function __BoolAsInt__(value : Bool) : Int { - if value { - 1 - } else { - 0 - } - } - function __ResultAsInt__(input : Result) : Int { - if Microsoft.Quantum.Convert.ResultAsBool(input) { - 1 - } else { - 0 - } - } + import QasmStd.Angle.*; + import QasmStd.Convert.*; + import QasmStd.Intrinsic.*; let a = true; let b = 2; let c = 3.; @@ -1596,20 +1801,9 @@ fn cast_to_uint() -> miette::Result<(), Vec> { let qsharp = compile_qasm_to_qsharp(source)?; expect![[r#" - function __BoolAsInt__(value : Bool) : Int { - if value { - 1 - } else { - 0 - } - } - function __ResultAsInt__(input : Result) : Int { - if Microsoft.Quantum.Convert.ResultAsBool(input) { - 1 - } else { - 0 - } - } + import QasmStd.Angle.*; + import QasmStd.Convert.*; + import QasmStd.Intrinsic.*; let a = true; let b = 2; let c = 3.; @@ -1645,13 +1839,9 @@ fn cast_to_float() -> miette::Result<(), Vec> { let qsharp = compile_qasm_to_qsharp(source)?; expect![[r#" - function __BoolAsDouble__(value : Bool) : Double { - if value { - 1. - } else { - 0. - } - } + import QasmStd.Angle.*; + import QasmStd.Convert.*; + import QasmStd.Intrinsic.*; let a = true; let b = 2; let c = 3; @@ -1667,21 +1857,37 @@ fn cast_to_float() -> miette::Result<(), Vec> { } #[test] -#[ignore = "angles are not yet supported"] + fn cast_to_angle() -> miette::Result<(), Vec> { let source = r#" - const float a = 2.0; - const bit b = 1; + const float a1 = 2.0; + const bit a2 = 1; - const angle s1 = a; - const angle s2 = b; + const angle[32] b1 = a1; + const angle[32] b2 = a2; + + const bit s1 = b1; + const bit s2 = b2; bit[s1] r1; bit[s2] r2; "#; let qsharp = compile_qasm_to_qsharp(source)?; - expect![[r#""#]].assert_eq(&qsharp); + expect![[r#" + import QasmStd.Angle.*; + import QasmStd.Convert.*; + import QasmStd.Intrinsic.*; + let a1 = 2.; + let a2 = One; + let b1 = __DoubleAsAngle__(a1, 32); + let b2 = __ResultAsAngle__(a2); + let s1 = __AngleAsResult__(b1); + let s2 = __AngleAsResult__(b2); + mutable r1 = [Zero]; + mutable r2 = [Zero]; + "#]] + .assert_eq(&qsharp); Ok(()) } @@ -1691,27 +1897,31 @@ fn cast_to_bit() -> miette::Result<(), Vec> { const bool a = false; const int b = 1; const uint c = 2; - // const angle d = 3.0; + const angle[32] d = 3.0; const bit s1 = a; const bit s2 = b; const bit s3 = c; - // const bit s4 = d; + const bit s4 = d; bit[s1] r1; bit[s2] r2; bit[s3] r3; - // bit[s4] r4; + bit[s4] r4; "#; let qsharp = compile_qasm_to_qsharp(source)?; expect![[r#" - function __BoolAsResult__(input : Bool) : Result { - Microsoft.Quantum.Convert.BoolAsResult(input) - } + import QasmStd.Angle.*; + import QasmStd.Convert.*; + import QasmStd.Intrinsic.*; let a = false; let b = 1; let c = 2; + let d = new __Angle__ { + Value = 2050695827, + Size = 32 + }; let s1 = __BoolAsResult__(a); let s2 = if b == 0 { One @@ -1723,9 +1933,11 @@ fn cast_to_bit() -> miette::Result<(), Vec> { } else { Zero }; + let s4 = __AngleAsResult__(d); mutable r1 = []; mutable r2 = [Zero]; mutable r3 = [Zero]; + mutable r4 = [Zero]; "#]] .assert_eq(&qsharp); Ok(()) diff --git a/compiler/qsc_qasm3/src/tests/statement/for_loop.rs b/compiler/qsc_qasm3/src/tests/statement/for_loop.rs index 4c476bdf96..ea708fccef 100644 --- a/compiler/qsc_qasm3/src/tests/statement/for_loop.rs +++ b/compiler/qsc_qasm3/src/tests/statement/for_loop.rs @@ -15,14 +15,15 @@ fn for_loops_can_iterate_over_discrete_set() -> miette::Result<(), Vec> "#; let qsharp = compile_qasm_to_qsharp(source)?; - expect![ - r#" + expect![[r#" + import QasmStd.Angle.*; + import QasmStd.Convert.*; + import QasmStd.Intrinsic.*; mutable sum = 0; for i : Int in [1, 5, 10] { set sum += i; } - "# - ] + "#]] .assert_eq(&qsharp); Ok(()) } @@ -36,14 +37,15 @@ fn for_loops_can_have_stmt_bodies() -> miette::Result<(), Vec> { "#; let qsharp = compile_qasm_to_qsharp(source)?; - expect![ - r#" + expect![[r#" + import QasmStd.Angle.*; + import QasmStd.Convert.*; + import QasmStd.Intrinsic.*; mutable sum = 0; for i : Int in [1, 5, 10] { set sum += i; } - "# - ] + "#]] .assert_eq(&qsharp); Ok(()) } @@ -58,14 +60,15 @@ fn for_loops_can_iterate_over_range() -> miette::Result<(), Vec> { "#; let qsharp = compile_qasm_to_qsharp(source)?; - expect![ - r#" + expect![[r#" + import QasmStd.Angle.*; + import QasmStd.Convert.*; + import QasmStd.Intrinsic.*; mutable sum = 0; for i : Int in 0..2..20 { set sum += i; } - "# - ] + "#]] .assert_eq(&qsharp); Ok(()) } @@ -80,14 +83,15 @@ fn for_loops_can_iterate_float_set() -> miette::Result<(), Vec> { "#; let qsharp = compile_qasm_to_qsharp(source)?; - expect![ - r#" + expect![[r#" + import QasmStd.Angle.*; + import QasmStd.Convert.*; + import QasmStd.Intrinsic.*; mutable sum = 0.; for f : Double in [1.2, -3.4, 0.5, 9.8] { set sum += f; } - "# - ] + "#]] .assert_eq(&qsharp); Ok(()) } @@ -104,15 +108,13 @@ fn for_loops_can_iterate_float_array_symbol() -> miette::Result<(), Vec> "#; let qsharp = compile_qasm_to_qsharp(source)?; - expect![ - r#" + expect![[r#" mutable sum = 0.; let my_floats = [1.2, -3.4, 0.5, 9.8]; for f : Double in my_floats { set sum += f; } - "# - ] + "#]] .assert_eq(&qsharp); Ok(()) } @@ -133,22 +135,16 @@ fn for_loops_can_iterate_bit_register() -> miette::Result<(), Vec> { "#; let qsharp = compile_qasm_to_qsharp(source)?; - expect![ - r#" - function __ResultAsInt__(input : Result) : Int { - if Microsoft.Quantum.Convert.ResultAsBool(input) { - 1 - } else { - 0 - } - } + expect![[r#" + import QasmStd.Angle.*; + import QasmStd.Convert.*; + import QasmStd.Intrinsic.*; mutable sum = 0; let reg = [One, Zero, One, Zero, One]; for b : Result in reg { set sum += __ResultAsInt__(b); } - "# - ] + "#]] .assert_eq(&qsharp); Ok(()) } @@ -165,5 +161,5 @@ fn loop_variables_should_be_scoped_to_for_loop() { panic!("Expected error"); }; - expect![r#"Undefined symbol: i."#].assert_eq(&errors[0].to_string()); + expect![[r#"Undefined symbol: i."#]].assert_eq(&errors[0].to_string()); } diff --git a/compiler/qsc_qasm3/src/tests/statement/gate_call.rs b/compiler/qsc_qasm3/src/tests/statement/gate_call.rs index b2941fdf86..3e7fecced1 100644 --- a/compiler/qsc_qasm3/src/tests/statement/gate_call.rs +++ b/compiler/qsc_qasm3/src/tests/statement/gate_call.rs @@ -14,23 +14,13 @@ fn u_gate_can_be_called() -> miette::Result<(), Vec> { "#; let qsharp = compile_qasm_to_qsharp(source)?; - expect![ - r#" - operation U(theta : Double, phi : Double, lambda : Double, qubit : Qubit) : Unit is Adj + Ctl { - body ... { - Rz(lambda, qubit); - Ry(theta, qubit); - Rz(phi, qubit); - R(PauliI, -lambda - phi - theta, qubit); - } - adjoint auto; - controlled auto; - controlled adjoint auto; - } + expect![[r#" + import QasmStd.Angle.*; + import QasmStd.Convert.*; + import QasmStd.Intrinsic.*; let q = QIR.Runtime.__quantum__rt__qubit_allocate(); - U(1., 2., 3., q); - "# - ] + U(__DoubleAsAngle__(1., 53), __DoubleAsAngle__(2., 53), __DoubleAsAngle__(3., 53), q); + "#]] .assert_eq(&qsharp); Ok(()) } @@ -43,15 +33,10 @@ fn gphase_gate_can_be_called() -> miette::Result<(), Vec> { let qsharp = compile_qasm_to_qsharp(source)?; expect![[r#" - operation gphase(theta : Double) : Unit is Adj + Ctl { - body ... { - Exp([], theta, []) - } - adjoint auto; - controlled auto; - controlled adjoint auto; - } - gphase(2.); + import QasmStd.Angle.*; + import QasmStd.Convert.*; + import QasmStd.Intrinsic.*; + gphase(__DoubleAsAngle__(2., 53)); "#]] .assert_eq(&qsharp); Ok(()) @@ -66,12 +51,13 @@ fn x_gate_can_be_called() -> miette::Result<(), Vec> { "#; let qsharp = compile_qasm_to_qsharp(source)?; - expect![ - r#" + expect![[r#" + import QasmStd.Angle.*; + import QasmStd.Convert.*; + import QasmStd.Intrinsic.*; let q = QIR.Runtime.__quantum__rt__qubit_allocate(); - X(q); - "# - ] + x(q); + "#]] .assert_eq(&qsharp); Ok(()) } @@ -85,14 +71,13 @@ fn barrier_can_be_called_on_single_qubit() -> miette::Result<(), Vec> { "#; let qsharp = compile_qasm_to_qsharp(source)?; - expect![ - r#" - @SimulatableIntrinsic() - operation __quantum__qis__barrier__body() : Unit {} + expect![[r#" + import QasmStd.Angle.*; + import QasmStd.Convert.*; + import QasmStd.Intrinsic.*; let q = QIR.Runtime.__quantum__rt__qubit_allocate(); __quantum__qis__barrier__body(); - "# - ] + "#]] .assert_eq(&qsharp); Ok(()) } @@ -106,14 +91,13 @@ fn barrier_can_be_called_without_qubits() -> miette::Result<(), Vec> { "#; let qsharp = compile_qasm_to_qsharp(source)?; - expect![ - r#" - @SimulatableIntrinsic() - operation __quantum__qis__barrier__body() : Unit {} + expect![[r#" + import QasmStd.Angle.*; + import QasmStd.Convert.*; + import QasmStd.Intrinsic.*; let q = QIR.Runtime.__quantum__rt__qubit_allocate(); __quantum__qis__barrier__body(); - "# - ] + "#]] .assert_eq(&qsharp); Ok(()) } @@ -132,7 +116,7 @@ fn barrier_generates_qir() -> miette::Result<(), Vec> { "#; let qsharp = compile_qasm_to_qir(source, Profile::AdaptiveRI)?; - expect![ + expect![[ r#" %Result = type opaque %Qubit = type opaque @@ -170,7 +154,7 @@ fn barrier_generates_qir() -> miette::Result<(), Vec> { !3 = !{i32 1, !"dynamic_result_management", i1 false} !4 = !{i32 1, !"int_computations", !"i64"} "# - ] + ]] .assert_eq(&qsharp); Ok(()) } @@ -184,14 +168,13 @@ fn barrier_can_be_called_on_two_qubit() -> miette::Result<(), Vec> { "#; let qsharp = compile_qasm_to_qsharp(source)?; - expect![ - r#" - @SimulatableIntrinsic() - operation __quantum__qis__barrier__body() : Unit {} + expect![[r#" + import QasmStd.Angle.*; + import QasmStd.Convert.*; + import QasmStd.Intrinsic.*; let q = QIR.Runtime.AllocateQubitArray(2); __quantum__qis__barrier__body(); - "# - ] + "#]] .assert_eq(&qsharp); Ok(()) } @@ -284,8 +267,11 @@ fn rx_gate_with_one_angle_can_be_called() -> miette::Result<(), Vec> { let qsharp = compile_qasm_to_qsharp(source)?; expect![[r#" + import QasmStd.Angle.*; + import QasmStd.Convert.*; + import QasmStd.Intrinsic.*; let q = QIR.Runtime.__quantum__rt__qubit_allocate(); - Rx(2., q); + rx(__DoubleAsAngle__(2., 53), q); "#]] .assert_eq(&qsharp); Ok(()) @@ -328,9 +314,12 @@ fn implicit_cast_to_angle_works() -> miette::Result<(), Vec> { let qsharp = compile_qasm_to_qsharp(source)?; expect![[r#" + import QasmStd.Angle.*; + import QasmStd.Convert.*; + import QasmStd.Intrinsic.*; let q = QIR.Runtime.__quantum__rt__qubit_allocate(); - let a = 2.0; - Rx(2., q); + mutable a = 2.; + rx(__DoubleAsAngle__(a, 53), q); "#]] .assert_eq(&qsharp); Ok(()) diff --git a/compiler/qsc_qasm3/src/tests/statement/if_stmt.rs b/compiler/qsc_qasm3/src/tests/statement/if_stmt.rs index 9723bf3de8..3f551c4b7b 100644 --- a/compiler/qsc_qasm3/src/tests/statement/if_stmt.rs +++ b/compiler/qsc_qasm3/src/tests/statement/if_stmt.rs @@ -20,11 +20,11 @@ fn can_use_cond_with_implicit_cast_to_bool() -> miette::Result<(), Vec> let qsharp = compile_qasm_to_qsharp(source)?; expect![[r#" - function __ResultAsBool__(input : Result) : Bool { - Microsoft.Quantum.Convert.ResultAsBool(input) - } + import QasmStd.Angle.*; + import QasmStd.Convert.*; + import QasmStd.Intrinsic.*; let q = QIR.Runtime.__quantum__rt__qubit_allocate(); - H(q); + h(q); mutable result = QIR.Intrinsic.__quantum__qis__m__body(q); if __ResultAsBool__(result) { Reset(q); @@ -49,11 +49,11 @@ fn can_use_negated_cond_with_implicit_cast_to_bool() -> miette::Result<(), Vec miette::Result<(), Vec> { + let source = r#" + include "stdgates.inc"; + qubit ctl; + qubit target; + cy ctl, target; + "#; + + let qsharp = compile_qasm_to_qsharp(source)?; + expect![[r#" + import QasmStd.Angle.*; + import QasmStd.Convert.*; + import QasmStd.Intrinsic.*; + let ctl = QIR.Runtime.__quantum__rt__qubit_allocate(); + let target = QIR.Runtime.__quantum__rt__qubit_allocate(); + Controlled y([ctl], target); + "#]] + .assert_eq(&qsharp); + Ok(()) +} + +#[test] +fn cz_gate_can_be_called() -> miette::Result<(), Vec> { + let source = r#" + include "stdgates.inc"; + qubit ctl; + qubit target; + cz ctl, target; + "#; + + let qsharp = compile_qasm_to_qsharp(source)?; + expect![[r#" + import QasmStd.Angle.*; + import QasmStd.Convert.*; + import QasmStd.Intrinsic.*; + let ctl = QIR.Runtime.__quantum__rt__qubit_allocate(); + let target = QIR.Runtime.__quantum__rt__qubit_allocate(); + Controlled z([ctl], target); + "#]] + .assert_eq(&qsharp); + Ok(()) +} + +#[test] +fn ch_gate_can_be_called() -> miette::Result<(), Vec> { + let source = r#" + include "stdgates.inc"; + qubit ctl; + qubit target; + ch ctl, target; + "#; + + let qsharp = compile_qasm_to_qsharp(source)?; + expect![[r#" + import QasmStd.Angle.*; + import QasmStd.Convert.*; + import QasmStd.Intrinsic.*; + let ctl = QIR.Runtime.__quantum__rt__qubit_allocate(); + let target = QIR.Runtime.__quantum__rt__qubit_allocate(); + Controlled h([ctl], target); + "#]] + .assert_eq(&qsharp); + Ok(()) +} + #[test] fn sdg_gate_can_be_called() -> miette::Result<(), Vec> { let source = r#" @@ -14,12 +80,13 @@ fn sdg_gate_can_be_called() -> miette::Result<(), Vec> { "#; let qsharp = compile_qasm_to_qsharp(source)?; - expect![ - r#" + expect![[r#" + import QasmStd.Angle.*; + import QasmStd.Convert.*; + import QasmStd.Intrinsic.*; let q = QIR.Runtime.__quantum__rt__qubit_allocate(); - Adjoint S(q); - "# - ] + Adjoint s(q); + "#]] .assert_eq(&qsharp); Ok(()) } @@ -33,12 +100,13 @@ fn tdg_gate_can_be_called() -> miette::Result<(), Vec> { "#; let qsharp = compile_qasm_to_qsharp(source)?; - expect![ - r#" + expect![[r#" + import QasmStd.Angle.*; + import QasmStd.Convert.*; + import QasmStd.Intrinsic.*; let q = QIR.Runtime.__quantum__rt__qubit_allocate(); - Adjoint T(q); - "# - ] + Adjoint t(q); + "#]] .assert_eq(&qsharp); Ok(()) } @@ -47,17 +115,20 @@ fn tdg_gate_can_be_called() -> miette::Result<(), Vec> { fn crx_gate_can_be_called() -> miette::Result<(), Vec> { let source = r#" include "stdgates.inc"; - qubit[2] q; - crx(0.5) q[1], q[0]; + qubit ctl; + qubit target; + crx(0.5) ctl, target; "#; let qsharp = compile_qasm_to_qsharp(source)?; - expect![ - r#" - let q = QIR.Runtime.AllocateQubitArray(2); - Controlled Rx([q[1]], (0.5, q[0])); - "# - ] + expect![[r#" + import QasmStd.Angle.*; + import QasmStd.Convert.*; + import QasmStd.Intrinsic.*; + let ctl = QIR.Runtime.__quantum__rt__qubit_allocate(); + let target = QIR.Runtime.__quantum__rt__qubit_allocate(); + Controlled rx([ctl], (__DoubleAsAngle__(0.5, 53), target)); + "#]] .assert_eq(&qsharp); Ok(()) } @@ -66,17 +137,20 @@ fn crx_gate_can_be_called() -> miette::Result<(), Vec> { fn cry_gate_can_be_called() -> miette::Result<(), Vec> { let source = r#" include "stdgates.inc"; - qubit[2] q; - cry(0.5) q[1], q[0]; + qubit ctl; + qubit target; + cry(0.5) ctl, target; "#; let qsharp = compile_qasm_to_qsharp(source)?; - expect![ - r#" - let q = QIR.Runtime.AllocateQubitArray(2); - Controlled Ry([q[1]], (0.5, q[0])); - "# - ] + expect![[r#" + import QasmStd.Angle.*; + import QasmStd.Convert.*; + import QasmStd.Intrinsic.*; + let ctl = QIR.Runtime.__quantum__rt__qubit_allocate(); + let target = QIR.Runtime.__quantum__rt__qubit_allocate(); + Controlled ry([ctl], (__DoubleAsAngle__(0.5, 53), target)); + "#]] .assert_eq(&qsharp); Ok(()) } @@ -85,36 +159,86 @@ fn cry_gate_can_be_called() -> miette::Result<(), Vec> { fn crz_gate_can_be_called() -> miette::Result<(), Vec> { let source = r#" include "stdgates.inc"; - qubit[2] q; - crz(0.5) q[1], q[0]; + qubit ctl; + qubit target; + crz(0.5) ctl, target; "#; let qsharp = compile_qasm_to_qsharp(source)?; - expect![ - r#" - let q = QIR.Runtime.AllocateQubitArray(2); - Controlled Rz([q[1]], (0.5, q[0])); - "# - ] + expect![[r#" + import QasmStd.Angle.*; + import QasmStd.Convert.*; + import QasmStd.Intrinsic.*; + let ctl = QIR.Runtime.__quantum__rt__qubit_allocate(); + let target = QIR.Runtime.__quantum__rt__qubit_allocate(); + Controlled rz([ctl], (__DoubleAsAngle__(0.5, 53), target)); + "#]] .assert_eq(&qsharp); Ok(()) } #[test] -fn ch_gate_can_be_called() -> miette::Result<(), Vec> { +fn cswap_gate_can_be_called() -> miette::Result<(), Vec> { let source = r#" include "stdgates.inc"; + qubit ctl; qubit[2] q; - ch q[1], q[0]; + cswap ctl, q[0], q[1]; "#; let qsharp = compile_qasm_to_qsharp(source)?; - expect![ - r#" + expect![[r#" + import QasmStd.Angle.*; + import QasmStd.Convert.*; + import QasmStd.Intrinsic.*; + let ctl = QIR.Runtime.__quantum__rt__qubit_allocate(); let q = QIR.Runtime.AllocateQubitArray(2); - Controlled H([q[1]], q[0]); - "# - ] + Controlled swap([ctl], (q[0], q[1])); + "#]] + .assert_eq(&qsharp); + Ok(()) +} + +#[test] +fn legacy_cx_gate_can_be_called() -> miette::Result<(), Vec> { + let source = r#" + include "stdgates.inc"; + qubit ctl; + qubit target; + CX ctl, target; + "#; + + let qsharp = compile_qasm_to_qsharp(source)?; + expect![[r#" + import QasmStd.Angle.*; + import QasmStd.Convert.*; + import QasmStd.Intrinsic.*; + let ctl = QIR.Runtime.__quantum__rt__qubit_allocate(); + let target = QIR.Runtime.__quantum__rt__qubit_allocate(); + Controlled x([ctl], target); + "#]] + .assert_eq(&qsharp); + Ok(()) +} + +#[test] +fn legacy_cphase_gate_can_be_called() -> miette::Result<(), Vec> { + let source = r#" + include "stdgates.inc"; + qubit ctl; + qubit target; + cphase(1.0) ctl, target; + "#; + + let qsharp = compile_qasm_to_qsharp(source)?; + expect![[r#" + import QasmStd.Angle.*; + import QasmStd.Convert.*; + import QasmStd.Intrinsic.*; + let ctl = QIR.Runtime.__quantum__rt__qubit_allocate(); + let target = QIR.Runtime.__quantum__rt__qubit_allocate(); + Controlled phase([ctl], (__DoubleAsAngle__(1., 53), target)); + "#]] .assert_eq(&qsharp); Ok(()) } diff --git a/compiler/qsc_qasm3/src/tests/statement/include.rs b/compiler/qsc_qasm3/src/tests/statement/include.rs index fc21457b00..3c95d89622 100644 --- a/compiler/qsc_qasm3/src/tests/statement/include.rs +++ b/compiler/qsc_qasm3/src/tests/statement/include.rs @@ -42,10 +42,12 @@ fn programs_with_includes_can_be_parsed() -> miette::Result<(), Vec> { namespace qasm3_import { @EntryPoint() operation Test() : Result[] { - @SimulatableIntrinsic() - operation my_gate(q : Qubit) : Unit { - X(q); - } + import QasmStd.Angle.*; + import QasmStd.Convert.*; + import QasmStd.Intrinsic.*; + let my_gate : (Qubit) => Unit = (q) => { + x(q); + }; mutable c = [Zero]; let q = QIR.Runtime.AllocateQubitArray(1); my_gate(q[0]); diff --git a/compiler/qsc_qasm3/src/tests/statement/measure.rs b/compiler/qsc_qasm3/src/tests/statement/measure.rs index 4072d847e7..f3695d51e6 100644 --- a/compiler/qsc_qasm3/src/tests/statement/measure.rs +++ b/compiler/qsc_qasm3/src/tests/statement/measure.rs @@ -16,6 +16,9 @@ fn single_qubit_can_be_measured_into_single_bit() -> miette::Result<(), Vec miette::Result<(), Ve let qsharp = compile_qasm_to_qsharp(source)?; expect![[r#" - mutable c = Zero; - let q = QIR.Runtime.__quantum__rt__qubit_allocate(); - set c = QIR.Intrinsic.__quantum__qis__m__body(q); - "#]] + import QasmStd.Angle.*; + import QasmStd.Convert.*; + import QasmStd.Intrinsic.*; + mutable c = Zero; + let q = QIR.Runtime.__quantum__rt__qubit_allocate(); + set c = QIR.Intrinsic.__quantum__qis__m__body(q); + "#]] .assert_eq(&qsharp); Ok(()) } @@ -53,6 +59,9 @@ fn indexed_single_qubit_can_be_measured_into_indexed_bit_register( let qsharp = compile_qasm_to_qsharp(source)?; expect![[r#" + import QasmStd.Angle.*; + import QasmStd.Convert.*; + import QasmStd.Intrinsic.*; mutable c = [Zero]; let q = QIR.Runtime.AllocateQubitArray(1); set c w/= 0 <- QIR.Intrinsic.__quantum__qis__m__body(q[0]); @@ -72,6 +81,9 @@ fn indexed_single_qubit_can_be_measured_into_single_bit_register() -> miette::Re let qsharp = compile_qasm_to_qsharp(source)?; expect![[r#" + import QasmStd.Angle.*; + import QasmStd.Convert.*; + import QasmStd.Intrinsic.*; mutable c = Zero; let q = QIR.Runtime.AllocateQubitArray(1); set c = QIR.Intrinsic.__quantum__qis__m__body(q[0]); @@ -121,6 +133,9 @@ fn value_from_measurement_can_be_dropped() -> miette::Result<(), Vec> { let qsharp = compile_qasm_to_qsharp(source)?; expect![[r#" + import QasmStd.Angle.*; + import QasmStd.Convert.*; + import QasmStd.Intrinsic.*; let q = QIR.Runtime.__quantum__rt__qubit_allocate(); QIR.Intrinsic.__quantum__qis__m__body(q); "#]] diff --git a/compiler/qsc_qasm3/src/tests/statement/modified_gate_call.rs b/compiler/qsc_qasm3/src/tests/statement/modified_gate_call.rs index 0f535f8385..9872fe4fcc 100644 --- a/compiler/qsc_qasm3/src/tests/statement/modified_gate_call.rs +++ b/compiler/qsc_qasm3/src/tests/statement/modified_gate_call.rs @@ -14,12 +14,13 @@ fn adj_x_gate_can_be_called() -> miette::Result<(), Vec> { "#; let qsharp = compile_qasm_to_qsharp(source)?; - expect![ - r#" + expect![[r#" + import QasmStd.Angle.*; + import QasmStd.Convert.*; + import QasmStd.Intrinsic.*; let q = QIR.Runtime.__quantum__rt__qubit_allocate(); - Adjoint X(q); - "# - ] + Adjoint x(q); + "#]] .assert_eq(&qsharp); Ok(()) } @@ -33,12 +34,13 @@ fn adj_adj_x_gate_can_be_called() -> miette::Result<(), Vec> { "#; let qsharp = compile_qasm_to_qsharp(source)?; - expect![ - r#" + expect![[r#" + import QasmStd.Angle.*; + import QasmStd.Convert.*; + import QasmStd.Intrinsic.*; let q = QIR.Runtime.__quantum__rt__qubit_allocate(); - Adjoint Adjoint X(q); - "# - ] + Adjoint Adjoint x(q); + "#]] .assert_eq(&qsharp); Ok(()) } @@ -53,13 +55,14 @@ fn multiple_controls_on_x_gate_can_be_called() -> miette::Result<(), Vec "#; let qsharp = compile_qasm_to_qsharp(source)?; - expect![ - r#" + expect![[r#" + import QasmStd.Angle.*; + import QasmStd.Convert.*; + import QasmStd.Intrinsic.*; let q = QIR.Runtime.AllocateQubitArray(3); let f = QIR.Runtime.__quantum__rt__qubit_allocate(); - Controlled X([q[1], q[0], q[2]], f); - "# - ] + Controlled x([q[1], q[0], q[2]], f); + "#]] .assert_eq(&qsharp); Ok(()) } @@ -75,14 +78,15 @@ fn repeated_multi_controls_on_x_gate_can_be_called() -> miette::Result<(), Vec miette::Result<( "#; let qsharp = compile_qasm_to_qsharp(source)?; - expect![ - r#" + expect![[r#" + import QasmStd.Angle.*; + import QasmStd.Convert.*; + import QasmStd.Intrinsic.*; let q = QIR.Runtime.AllocateQubitArray(2); let r = QIR.Runtime.AllocateQubitArray(3); let f = QIR.Runtime.__quantum__rt__qubit_allocate(); - Controlled Adjoint Controlled Adjoint X([q[1], r[0]], ([q[0], f, r[1]], r[2])); - "# - ] + Controlled Adjoint Controlled Adjoint x([q[1], r[0]], ([q[0], f, r[1]], r[2])); + "#]] .assert_eq(&qsharp); Ok(()) } @@ -120,13 +125,14 @@ fn multiple_controls_on_cx_gate_can_be_called() -> miette::Result<(), Vec miette::Result<(), Vec miette::Result<( "#; let qsharp = compile_qasm_to_qsharp(source)?; - expect![ - r#" + expect![[r#" + import QasmStd.Angle.*; + import QasmStd.Convert.*; + import QasmStd.Intrinsic.*; let q = QIR.Runtime.AllocateQubitArray(4); let f = QIR.Runtime.__quantum__rt__qubit_allocate(); - Adjoint ApplyControlledOnInt(0, Adjoint Controlled Rx, [q[1], q[0], q[2]], ([f], (0.5, q[3]))); - "# - ] + Adjoint ApplyControlledOnInt(0, Adjoint Controlled rx, [q[1], q[0], q[2]], ([f], (__DoubleAsAngle__(0.5, 53), q[3]))); + "#]] .assert_eq(&qsharp); Ok(()) } @@ -183,13 +191,14 @@ fn neg_ctrl_can_wrap_another_neg_crtl_modifier() -> miette::Result<(), Vec miette::Result<(), Vec> { "#; let qsharp = compile_qasm_to_qsharp(source)?; - expect![ - r#" - operation __Pow__ < 'T > (N : Int, op : ('T => Unit is Adj), target : 'T) : Unit is Adj { - let op = if N > 0 { - () => op(target) - } else { - () => Adjoint op(target) - }; - for _ in 1..Microsoft.Quantum.Math.AbsI(N) { - op() - } - } + expect![[r#" + import QasmStd.Angle.*; + import QasmStd.Convert.*; + import QasmStd.Intrinsic.*; let q = QIR.Runtime.AllocateQubitArray(6); let f = QIR.Runtime.__quantum__rt__qubit_allocate(); - __Pow__(1, __Pow__, (1, __Pow__, (1, Controlled Rx, ([f], (0.5, q[5]))))); - "# - ] + __Pow__(1, __Pow__, (1, __Pow__, (1, Controlled rx, ([f], (__DoubleAsAngle__(0.5, 53), q[5]))))); + "#]] .assert_eq(&qsharp); Ok(()) } @@ -234,22 +234,13 @@ fn pow_can_be_applied_on_a_simple_gate() -> miette::Result<(), Vec> { "#; let qsharp = compile_qasm_to_qsharp(source)?; - expect![ - r#" - operation __Pow__ < 'T > (N : Int, op : ('T => Unit is Adj), target : 'T) : Unit is Adj { - let op = if N > 0 { - () => op(target) - } else { - () => Adjoint op(target) - }; - for _ in 1..Microsoft.Quantum.Math.AbsI(N) { - op() - } - } + expect![[r#" + import QasmStd.Angle.*; + import QasmStd.Convert.*; + import QasmStd.Intrinsic.*; let f = QIR.Runtime.__quantum__rt__qubit_allocate(); - __Pow__(2, X, (f)); - "# - ] + __Pow__(2, x, (f)); + "#]] .assert_eq(&qsharp); Ok(()) } diff --git a/compiler/qsc_qasm3/src/tests/statement/reset.rs b/compiler/qsc_qasm3/src/tests/statement/reset.rs index 97698f9e2d..fa4689dd25 100644 --- a/compiler/qsc_qasm3/src/tests/statement/reset.rs +++ b/compiler/qsc_qasm3/src/tests/statement/reset.rs @@ -33,20 +33,21 @@ fn reset_calls_are_generated_from_qasm() -> miette::Result<(), Vec> { let unit = compile_with_config(source, config)?; fail_on_compilation_errors(&unit); let qsharp = gen_qsharp(&unit.package.expect("no package found")); - expect![ - r#" + expect![[r#" namespace qasm3_import { @EntryPoint() operation Test() : Result[] { + import QasmStd.Angle.*; + import QasmStd.Convert.*; + import QasmStd.Intrinsic.*; mutable meas = [Zero]; let q = QIR.Runtime.AllocateQubitArray(1); Reset(q[0]); - H(q[0]); + h(q[0]); set meas w/= 0 <- QIR.Intrinsic.__quantum__qis__m__body(q[0]); Microsoft.Quantum.Arrays.Reversed(meas) } - }"# - ] + }"#]] .assert_eq(&qsharp); Ok(()) diff --git a/compiler/qsc_qasm3/src/tests/statement/switch.rs b/compiler/qsc_qasm3/src/tests/statement/switch.rs index 81083c13f8..874d753bdb 100644 --- a/compiler/qsc_qasm3/src/tests/statement/switch.rs +++ b/compiler/qsc_qasm3/src/tests/statement/switch.rs @@ -18,14 +18,15 @@ fn default_is_optional() -> miette::Result<(), Vec> { "#; let qsharp = compile_qasm_to_qsharp(source)?; - expect![ - r#" + expect![[r#" + import QasmStd.Angle.*; + import QasmStd.Convert.*; + import QasmStd.Intrinsic.*; mutable i = 15; if i == 1 { set i = 2; }; - "# - ] + "#]] .assert_eq(&qsharp); Ok(()) } @@ -47,7 +48,7 @@ fn default_as_only_case_causes_parse_error() { panic!("Expected an error, got {res:?}"); }; assert_eq!(errors.len(), 1); - expect!["missing switch statement cases"].assert_eq(&errors[0].to_string()); + expect![["missing switch statement cases"]].assert_eq(&errors[0].to_string()); } #[test] @@ -64,7 +65,7 @@ fn no_cases_causes_parse_error() { panic!("Expected an error, got {res:?}"); }; assert_eq!(errors.len(), 1); - expect!["missing switch statement cases"].assert_eq(&errors[0].to_string()); + expect![["missing switch statement cases"]].assert_eq(&errors[0].to_string()); } #[test] @@ -93,21 +94,22 @@ fn spec_case_1() -> miette::Result<(), Vec> { "#; let qsharp = compile_qasm_to_qsharp(source)?; - expect![ - r#" + expect![[r#" + import QasmStd.Angle.*; + import QasmStd.Convert.*; + import QasmStd.Intrinsic.*; let q = QIR.Runtime.__quantum__rt__qubit_allocate(); mutable i = 15; if i == 1 or i == 3 or i == 5 { - H(q); + h(q); } elif i == 2 or i == 4 or i == 6 { - X(q); + x(q); } elif i == -1 { - Y(q); + y(q); } else { - Z(q); + z(q); }; - "# - ] + "#]] .assert_eq(&qsharp); Ok(()) } @@ -140,23 +142,24 @@ fn spec_case_2() -> miette::Result<(), Vec> { "#; let qsharp = compile_qasm_to_qsharp(source)?; - expect![ - r#" + expect![[r#" + import QasmStd.Angle.*; + import QasmStd.Convert.*; + import QasmStd.Intrinsic.*; let q = QIR.Runtime.__quantum__rt__qubit_allocate(); let A = 0; let B = 1; mutable i = 15; if i == A { - H(q); + h(q); } elif i == B { - X(q); + x(q); } elif i == B + 1 { - Y(q); + y(q); } else { - Z(q); + z(q); }; - "# - ] + "#]] .assert_eq(&qsharp); Ok(()) } @@ -186,35 +189,32 @@ fn spec_case_3() -> miette::Result<(), Vec> { "#; let qsharp = compile_qasm_to_qsharp_file(source)?; - expect![ - r#" + expect![[r#" namespace qasm3_import { @EntryPoint() operation Test() : Result[] { - function __ResultArrayAsIntBE__(results : Result[]) : Int { - Microsoft.Quantum.Convert.ResultArrayAsInt(Microsoft.Quantum.Arrays.Reversed(results)) - } + import QasmStd.Angle.*; + import QasmStd.Convert.*; + import QasmStd.Intrinsic.*; let q = QIR.Runtime.__quantum__rt__qubit_allocate(); mutable b = [Zero, Zero]; if __ResultArrayAsIntBE__(b) == 0 { - H(q); + h(q); } elif __ResultArrayAsIntBE__(b) == 1 { - X(q); + x(q); } elif __ResultArrayAsIntBE__(b) == 2 { - Y(q); + y(q); } elif __ResultArrayAsIntBE__(b) == 3 { - Z(q); + z(q); }; b } - }"# - ] + }"#]] .assert_eq(&qsharp); Ok(()) } #[test] -#[ignore = "Function decls are not supported yet"] fn spec_case_4() -> miette::Result<(), Vec> { let source = r#" OPENQASM 3.1; @@ -249,10 +249,26 @@ fn spec_case_4() -> miette::Result<(), Vec> { "#; let qsharp = compile_qasm_to_qsharp(source)?; - expect![ - r#" - "# - ] + expect![[r#" + import QasmStd.Angle.*; + import QasmStd.Convert.*; + import QasmStd.Intrinsic.*; + let q = QIR.Runtime.__quantum__rt__qubit_allocate(); + mutable b = [Zero, Zero]; + let foo : (Int, Qubit[]) => Result = (i, d) => { + return QIR.Intrinsic.__quantum__qis__m__body(d[i]); + }; + mutable i = 15; + mutable j = 1; + mutable k = 2; + mutable c1 = Zero; + let q0 = QIR.Runtime.AllocateQubitArray(8); + if i == 1 { + set j = k + __ResultAsInt__(foo(k, q0)); + } elif i == 2 { + mutable d = Microsoft.Quantum.Convert.IntAsDouble(j / k); + } elif i == 3 {} else {}; + "#]] .assert_eq(&qsharp); Ok(()) } @@ -283,18 +299,19 @@ fn spec_case_5() -> miette::Result<(), Vec> { "#; let qsharp = compile_qasm_to_qsharp(source)?; - expect![ - r#" + expect![[r#" + import QasmStd.Angle.*; + import QasmStd.Convert.*; + import QasmStd.Intrinsic.*; let q = QIR.Runtime.AllocateQubitArray(8); mutable j = 30; mutable i = 0; if i == 1 or i == 2 or i == 5 or i == 12 {} elif i == 3 { if j == 10 or j == 15 or j == 20 { - H(q); + h(q); }; }; - "# - ] + "#]] .assert_eq(&qsharp); Ok(()) } diff --git a/compiler/qsc_qasm3/src/tests/statement/while_loop.rs b/compiler/qsc_qasm3/src/tests/statement/while_loop.rs index 76e67f585a..e2910a871b 100644 --- a/compiler/qsc_qasm3/src/tests/statement/while_loop.rs +++ b/compiler/qsc_qasm3/src/tests/statement/while_loop.rs @@ -24,14 +24,14 @@ fn can_iterate_over_mutable_var_cmp_expr() -> miette::Result<(), Vec> { let qsharp = compile_qasm_to_qsharp(source)?; expect![[r#" - function __ResultAsBool__(input : Result) : Bool { - Microsoft.Quantum.Convert.ResultAsBool(input) - } + import QasmStd.Angle.*; + import QasmStd.Convert.*; + import QasmStd.Intrinsic.*; let q = QIR.Runtime.__quantum__rt__qubit_allocate(); mutable result = Zero; mutable i = 0; while i < 10 { - H(q); + h(q); set result = QIR.Intrinsic.__quantum__qis__m__body(q); if __ResultAsBool__(result) { set i += 1; diff --git a/compiler/qsc_qasm3/src/types.rs b/compiler/qsc_qasm3/src/types.rs index 046ba921d0..d8f607001b 100644 --- a/compiler/qsc_qasm3/src/types.rs +++ b/compiler/qsc_qasm3/src/types.rs @@ -99,6 +99,7 @@ impl Complex { #[derive(Debug, Clone, Default, PartialEq, Eq)] pub enum Type { + Angle(bool), Bool(bool), BigInt(bool), Complex(bool), @@ -177,6 +178,7 @@ impl From<&crate::semantic::types::ArrayDimensions> for ArrayDimensions { impl Display for Type { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { match self { + Type::Angle(_) => write!(f, "Angle"), Type::Bool(_) => write!(f, "bool"), Type::BigInt(_) => write!(f, "BigInt"), Type::Complex(_) => write!(f, "Complex"), diff --git a/pip/src/interop.rs b/pip/src/interop.rs index 29847eff37..40f6c65a31 100644 --- a/pip/src/interop.rs +++ b/pip/src/interop.rs @@ -8,6 +8,7 @@ use std::fmt::Write; use pyo3::exceptions::PyException; use pyo3::prelude::*; use pyo3::types::{PyDict, PyList}; +use qsc::hir::PackageId; use qsc::interpret::output::Receiver; use qsc::interpret::{into_errors, Interpreter}; use qsc::qasm3::io::SourceResolver; @@ -17,8 +18,7 @@ use qsc::qasm3::{ }; use qsc::target::Profile; use qsc::{ - ast::Package, error::WithSource, interpret, project::FileSystem, LanguageFeatures, - PackageStore, SourceMap, + ast::Package, error::WithSource, interpret, project::FileSystem, LanguageFeatures, SourceMap, }; use qsc::{Backend, PackageType, SparseSim}; @@ -541,12 +541,14 @@ fn create_interpreter_from_ast( language_features: LanguageFeatures, package_type: PackageType, ) -> Result> { - let mut store = PackageStore::new(qsc::compile::core()); - let mut dependencies = Vec::new(); - let capabilities = profile.into(); + let (stdid, qasmid, mut store) = qsc::qasm3::package_store_with_qasm(capabilities); + let dependencies = vec![ + (PackageId::CORE, None), + (stdid, None), + (qasmid, Some("QasmStd".into())), + ]; - dependencies.push((store.insert(qsc::compile::std(&store, capabilities)), None)); let (mut unit, errors) = qsc::compile::compile_ast( &store, &dependencies, From 50216b32850a3f023b62c07ba7b903877384eb9e Mon Sep 17 00:00:00 2001 From: orpuente-MS <156957451+orpuente-MS@users.noreply.github.com> Date: Tue, 1 Apr 2025 06:11:15 -0700 Subject: [PATCH 071/108] Lower and compile complex binary ops (#2268) --- compiler/qsc_qasm3/src/compiler.rs | 38 +++++- compiler/qsc_qasm3/src/semantic/lowerer.rs | 16 ++- .../src/tests/expression/binary/complex.rs | 120 ++++++++++++++---- 3 files changed, 141 insertions(+), 33 deletions(-) diff --git a/compiler/qsc_qasm3/src/compiler.rs b/compiler/qsc_qasm3/src/compiler.rs index d3f36066cc..c997016539 100644 --- a/compiler/qsc_qasm3/src/compiler.rs +++ b/compiler/qsc_qasm3/src/compiler.rs @@ -400,8 +400,8 @@ impl QasmCompiler { fn compile_assign_op_stmt(&mut self, stmt: &semast::AssignOpStmt) -> Option { // If the lhs is of type Angle, we call compile_assign_stmt with the rhs = lhs + rhs. - // This will call compile_binary_expr which handles angles correctly. - if matches!(&stmt.lhs.ty, Type::Angle(..)) { + // This will call compile_binary_expr which handles angle & complex correctly. + if matches!(&stmt.lhs.ty, Type::Angle(..) | Type::Complex(..)) { if stmt.indices.is_empty() { let rhs = semast::Expr { span: stmt.span, @@ -1095,6 +1095,12 @@ impl QasmCompiler { return self.compile_angle_binary_op(op, lhs, rhs, &binary.lhs.ty, &binary.rhs.ty); } + if matches!(&binary.lhs.ty, Type::Complex(..)) + || matches!(&binary.rhs.ty, Type::Complex(..)) + { + return Self::compile_complex_binary_op(op, lhs, rhs); + } + let is_assignment = false; build_binary_expr(is_assignment, op, lhs, rhs, binary.span()) } @@ -1162,6 +1168,34 @@ impl QasmCompiler { build_call_with_params(fn_name, &[], operands, span, span) } + fn compile_complex_binary_op( + op: qsast::BinOp, + lhs: qsast::Expr, + rhs: qsast::Expr, + ) -> qsast::Expr { + let span = Span { + lo: lhs.span.lo, + hi: rhs.span.hi, + }; + + let fn_name: &str = match op { + // Arithmetic + qsast::BinOp::Add => "PlusC", + qsast::BinOp::Sub => "MinusC", + qsast::BinOp::Mul => "TimesC", + qsast::BinOp::Div => "DividedByC", + qsast::BinOp::Exp => "PowC", + _ => { + // we are already pushing a semantic error in the lowerer + // if the operation is not supported. So, we just return + // an Expr::Err here. + return err_expr(span); + } + }; + + build_math_call_from_exprs(fn_name, vec![lhs, rhs], span) + } + fn compile_literal_expr(&mut self, lit: &LiteralKind, span: Span) -> qsast::Expr { match lit { LiteralKind::Angle(value) => build_lit_angle_expr(*value, span), diff --git a/compiler/qsc_qasm3/src/semantic/lowerer.rs b/compiler/qsc_qasm3/src/semantic/lowerer.rs index f9d2755877..14071415a1 100644 --- a/compiler/qsc_qasm3/src/semantic/lowerer.rs +++ b/compiler/qsc_qasm3/src/semantic/lowerer.rs @@ -2745,11 +2745,17 @@ impl Lowerer { let expr = if matches!(ty, Type::Complex(..)) { if is_complex_binop_supported(op) { - // TODO: How do we handle complex binary expressions? - // this is going to be a call to a built-in function - // that doesn't map to qasm def semantics - self.push_unimplemented_error_message("complex binary exprs", span); - err_expr!(ty.clone(), span) + let bin_expr = semantic::BinaryOpExpr { + op: op.into(), + lhs, + rhs, + }; + let kind = semantic::ExprKind::BinaryOp(bin_expr); + semantic::Expr { + span, + kind: Box::new(kind), + ty: ty.clone(), + } } else { let kind = SemanticErrorKind::OperatorNotSupportedForTypes( format!("{op:?}"), diff --git a/compiler/qsc_qasm3/src/tests/expression/binary/complex.rs b/compiler/qsc_qasm3/src/tests/expression/binary/complex.rs index 869e58d936..fb70121884 100644 --- a/compiler/qsc_qasm3/src/tests/expression/binary/complex.rs +++ b/compiler/qsc_qasm3/src/tests/expression/binary/complex.rs @@ -2,10 +2,41 @@ // Licensed under the MIT License. use crate::tests::compile_qasm_stmt_to_qsharp; - use expect_test::expect; use miette::Report; +#[test] +fn addition() -> miette::Result<(), Vec> { + let source = " + input complex[float] a; + input complex[float] b; + complex x = (a + b); + "; + + let qsharp = compile_qasm_stmt_to_qsharp(source)?; + expect![[r#" + mutable x = (Microsoft.Quantum.Math.PlusC(a, b)); + "#]] + .assert_eq(&qsharp); + Ok(()) +} + +#[test] +fn addition_assign_op() -> miette::Result<(), Vec> { + let source = " + input complex[float] a; + complex x = 0.0; + x += a; + "; + + let qsharp = compile_qasm_stmt_to_qsharp(source)?; + expect![[r#" + set x = Microsoft.Quantum.Math.PlusC(x, a); + "#]] + .assert_eq(&qsharp); + Ok(()) +} + #[test] fn subtraction() -> miette::Result<(), Vec> { let source = " @@ -15,29 +46,25 @@ fn subtraction() -> miette::Result<(), Vec> { "; let qsharp = compile_qasm_stmt_to_qsharp(source)?; - expect![ - r#" + expect![[r#" mutable x = (Microsoft.Quantum.Math.MinusC(a, b)); - "# - ] + "#]] .assert_eq(&qsharp); Ok(()) } #[test] -fn addition() -> miette::Result<(), Vec> { +fn subtraction_assign_op() -> miette::Result<(), Vec> { let source = " input complex[float] a; - input complex[float] b; - complex x = (a + b); + complex x = 0.0; + x -= a; "; let qsharp = compile_qasm_stmt_to_qsharp(source)?; - expect![ - r#" - mutable x = (Microsoft.Quantum.Math.PlusC(a, b)); - "# - ] + expect![[r#" + set x = Microsoft.Quantum.Math.MinusC(x, a); + "#]] .assert_eq(&qsharp); Ok(()) } @@ -51,11 +78,25 @@ fn multiplication() -> miette::Result<(), Vec> { "; let qsharp = compile_qasm_stmt_to_qsharp(source)?; - expect![ - r#" + expect![[r#" mutable x = (Microsoft.Quantum.Math.TimesC(a, b)); - "# - ] + "#]] + .assert_eq(&qsharp); + Ok(()) +} + +#[test] +fn multiplication_assign_op() -> miette::Result<(), Vec> { + let source = " + input complex[float] a; + complex x = 0.0; + x *= a; + "; + + let qsharp = compile_qasm_stmt_to_qsharp(source)?; + expect![[r#" + set x = Microsoft.Quantum.Math.TimesC(x, a); + "#]] .assert_eq(&qsharp); Ok(()) } @@ -69,17 +110,30 @@ fn division() -> miette::Result<(), Vec> { "; let qsharp = compile_qasm_stmt_to_qsharp(source)?; - expect![ - r#" + expect![[r#" mutable x = (Microsoft.Quantum.Math.DividedByC(a, b)); - "# - ] + "#]] + .assert_eq(&qsharp); + Ok(()) +} + +#[test] +fn division_assign_op() -> miette::Result<(), Vec> { + let source = " + input complex[float] a; + complex x = 0.0; + x /= a; + "; + + let qsharp = compile_qasm_stmt_to_qsharp(source)?; + expect![[r#" + set x = Microsoft.Quantum.Math.DividedByC(x, a); + "#]] .assert_eq(&qsharp); Ok(()) } #[test] -#[ignore = "QASM3 parser bug"] fn power() -> miette::Result<(), Vec> { let source = " input complex[float] a; @@ -88,11 +142,25 @@ fn power() -> miette::Result<(), Vec> { "; let qsharp = compile_qasm_stmt_to_qsharp(source)?; - expect![ - r#" + expect![[r#" mutable x = (Microsoft.Quantum.Math.PowC(a, b)); - "# - ] + "#]] + .assert_eq(&qsharp); + Ok(()) +} + +#[test] +fn power_assign_op() -> miette::Result<(), Vec> { + let source = " + input complex[float] a; + complex x = 0.0; + x **= a; + "; + + let qsharp = compile_qasm_stmt_to_qsharp(source)?; + expect![[r#" + set x = Microsoft.Quantum.Math.PowC(x, a); + "#]] .assert_eq(&qsharp); Ok(()) } From dec185a04bed0645da6e28a270c4415c878264b9 Mon Sep 17 00:00:00 2001 From: Ian Davis Date: Tue, 1 Apr 2025 06:16:30 -0700 Subject: [PATCH 072/108] Fixing angle input/output (#2269) --- compiler/qsc_qasm3/src/ast_builder.rs | 59 ++++--- compiler/qsc_qasm3/src/compile.rs | 2 +- compiler/qsc_qasm3/src/compiler.rs | 149 +++++++++++++----- .../tests/declaration/io/explicit_input.rs | 16 ++ .../tests/declaration/io/explicit_output.rs | 35 ++-- .../tests/declaration/io/implicit_output.rs | 35 ++-- .../src/tests/expression/binary/comparison.rs | 42 ++--- .../qsc_qasm3/src/tests/expression/bits.rs | 12 +- compiler/qsc_qasm3/src/tests/output.rs | 24 +-- .../qsc_qasm3/src/tests/statement/include.rs | 6 +- .../qsc_qasm3/src/tests/statement/reset.rs | 6 +- .../qsc_qasm3/src/tests/statement/switch.rs | 6 +- 12 files changed, 255 insertions(+), 137 deletions(-) diff --git a/compiler/qsc_qasm3/src/ast_builder.rs b/compiler/qsc_qasm3/src/ast_builder.rs index f69686fe17..ccdf6e92c2 100644 --- a/compiler/qsc_qasm3/src/ast_builder.rs +++ b/compiler/qsc_qasm3/src/ast_builder.rs @@ -988,6 +988,17 @@ pub(crate) fn build_expr_wrapped_block_expr(expr: Expr) -> Block { } pub(crate) fn build_qasm_import_decl() -> Vec { + build_qasm_import_items() + .into_iter() + .map(|item| Stmt { + kind: Box::new(StmtKind::Item(Box::new(item))), + span: Span::default(), + id: NodeId::default(), + }) + .collect() +} + +pub(crate) fn build_qasm_import_items() -> Vec { vec![ build_qasm_import_decl_angle(), build_qasm_import_decl_convert(), @@ -995,7 +1006,7 @@ pub(crate) fn build_qasm_import_decl() -> Vec { ] } -pub(crate) fn build_qasm_import_decl_angle() -> Stmt { +pub(crate) fn build_qasm_import_decl_angle() -> Item { let path_kind = Path { segments: Some(Box::new([build_ident("QasmStd")])), name: Box::new(build_ident("Angle")), @@ -1009,21 +1020,16 @@ pub(crate) fn build_qasm_import_decl_angle() -> Stmt { is_glob: true, }]; let decl = ImportOrExportDecl::new(Span::default(), items.into_boxed_slice(), false); - let item = Item { + Item { id: NodeId::default(), span: Span::default(), kind: Box::new(ItemKind::ImportOrExport(decl)), doc: "".into(), attrs: Box::new([]), - }; - Stmt { - kind: Box::new(StmtKind::Item(Box::new(item))), - span: Span::default(), - id: NodeId::default(), } } -pub(crate) fn build_qasm_import_decl_convert() -> Stmt { +pub(crate) fn build_qasm_import_decl_convert() -> Item { let path_kind = Path { segments: Some(Box::new([build_ident("QasmStd")])), name: Box::new(build_ident("Convert")), @@ -1037,21 +1043,16 @@ pub(crate) fn build_qasm_import_decl_convert() -> Stmt { is_glob: true, }]; let decl = ImportOrExportDecl::new(Span::default(), items.into_boxed_slice(), false); - let item = Item { + Item { id: NodeId::default(), span: Span::default(), kind: Box::new(ItemKind::ImportOrExport(decl)), doc: "".into(), attrs: Box::new([]), - }; - Stmt { - kind: Box::new(StmtKind::Item(Box::new(item))), - span: Span::default(), - id: NodeId::default(), } } -pub(crate) fn build_qasm_import_decl_intrinsic() -> Stmt { +pub(crate) fn build_qasm_import_decl_intrinsic() -> Item { let path_kind = Path { segments: Some(Box::new([build_ident("QasmStd")])), name: Box::new(build_ident("Intrinsic")), @@ -1065,17 +1066,12 @@ pub(crate) fn build_qasm_import_decl_intrinsic() -> Stmt { is_glob: true, }]; let decl = ImportOrExportDecl::new(Span::default(), items.into_boxed_slice(), false); - let item = Item { + Item { id: NodeId::default(), span: Span::default(), kind: Box::new(ItemKind::ImportOrExport(decl)), doc: "".into(), attrs: Box::new([]), - }; - Stmt { - kind: Box::new(StmtKind::Item(Box::new(item))), - span: Span::default(), - id: NodeId::default(), } } @@ -1190,10 +1186,10 @@ pub(crate) fn build_complex_ty_ident() -> Ty { } } -pub(crate) fn build_top_level_ns_with_item>( +pub(crate) fn build_top_level_ns_with_items>( whole_span: Span, ns: S, - entry: ast::Item, + items: Vec, ) -> TopLevelNode { TopLevelNode::Namespace(qsc_ast::ast::Namespace { id: NodeId::default(), @@ -1204,23 +1200,36 @@ pub(crate) fn build_top_level_ns_with_item>( id: NodeId::default(), }] .into(), - items: Box::new([Box::new(entry)]), + items: items + .into_iter() + .map(Box::new) + .collect::>() + .into_boxed_slice(), doc: "".into(), }) } +pub(crate) fn build_top_level_ns_with_item>( + whole_span: Span, + ns: S, + entry: ast::Item, +) -> TopLevelNode { + build_top_level_ns_with_items(whole_span, ns, vec![entry]) +} + pub(crate) fn build_operation_with_stmts>( name: S, input_pats: Vec, output_ty: Ty, stmts: Vec, whole_span: Span, + add_entry_point: bool, ) -> ast::Item { let mut attrs = vec![]; // If there are no input parameters, add an attribute to mark this // as an entry point. We will get a Q# compilation error if we // attribute an operation with EntryPoint and it has input parameters. - if input_pats.is_empty() { + if input_pats.is_empty() && add_entry_point { attrs.push(Box::new(qsc_ast::ast::Attr { id: NodeId::default(), span: Span::default(), diff --git a/compiler/qsc_qasm3/src/compile.rs b/compiler/qsc_qasm3/src/compile.rs index 65781508f1..4610a4a8e1 100644 --- a/compiler/qsc_qasm3/src/compile.rs +++ b/compiler/qsc_qasm3/src/compile.rs @@ -3696,7 +3696,7 @@ impl QasmCompiler { .collect::>(); ( - build_operation_with_stmts(name, input_pats, ast_ty, stmts, whole_span), + build_operation_with_stmts(name, input_pats, ast_ty, stmts, whole_span, true), signature, ) } diff --git a/compiler/qsc_qasm3/src/compiler.rs b/compiler/qsc_qasm3/src/compiler.rs index c997016539..90b10ac1a4 100644 --- a/compiler/qsc_qasm3/src/compiler.rs +++ b/compiler/qsc_qasm3/src/compiler.rs @@ -22,9 +22,9 @@ use crate::{ build_lit_double_expr, build_lit_int_expr, build_lit_result_array_expr_from_bitstring, build_lit_result_expr, build_managed_qubit_alloc, build_math_call_from_exprs, build_math_call_no_params, build_measure_call, build_operation_with_stmts, - build_path_ident_expr, build_qasm_import_decl, build_range_expr, build_reset_call, - build_return_expr, build_return_unit, build_stmt_semi_from_expr, - build_stmt_semi_from_expr_with_span, build_top_level_ns_with_item, build_tuple_expr, + build_path_ident_expr, build_qasm_import_decl, build_qasm_import_items, build_range_expr, + build_reset_call, build_return_expr, build_return_unit, build_stmt_semi_from_expr, + build_stmt_semi_from_expr_with_span, build_top_level_ns_with_items, build_tuple_expr, build_unary_op_expr, build_unmanaged_qubit_alloc, build_unmanaged_qubit_alloc_array, build_while_stmt, build_wrapped_block_expr, managed_qubit_alloc_array, map_qsharp_type_to_ast_ty, wrap_expr_in_parens, @@ -94,9 +94,18 @@ impl QasmCompiler { /// source file and build the appropriate package based on the /// configuration. pub fn compile(mut self, program: &crate::semantic::ast::Program) -> QasmCompileUnit { - self.append_runtime_import_decls(); - self.compile_stmts(&program.statements); + // in non-file mode we need the runtime imports in the body let program_ty = self.config.program_ty.clone(); + + // If we are compiling for operation/fragments, we need to + // prepend to the list of statements. + // In file mode we need to add top level imports which are + // handled in the `build_file` method. + if !matches!(program_ty, ProgramType::File) { + self.append_runtime_import_decls(); + } + + self.compile_stmts(&program.statements); let (package, signature) = match program_ty { ProgramType::File => self.build_file(), ProgramType::Operation => self.build_operation(), @@ -114,7 +123,9 @@ impl QasmCompiler { let (operation, mut signature) = self.create_entry_operation(operation_name, whole_span); let ns = self.config.namespace(); signature.ns = Some(ns.to_string()); - let top = build_top_level_ns_with_item(whole_span, ns, operation); + let mut items = build_qasm_import_items(); + items.push(operation); + let top = build_top_level_ns_with_items(whole_span, ns, items); ( Package { nodes: Box::new([top]), @@ -164,6 +175,22 @@ impl QasmCompiler { ) -> (qsast::Item, OperationSignature) { let stmts = self.stmts.drain(..).collect::>(); let input = self.symbols.get_input(); + + // Analyze input for `Angle` types which we can't support as it would require + // passing a struct from Python. So we need to raise an error saying to use `float` + // which will preserve the angle type semantics via implicit conversion to angle + // in the qasm program. + if let Some(inputs) = &input { + for input in inputs { + if matches!(input.qsharp_ty, crate::types::Type::Angle(..)) { + let message = + "use `float` types for passing input, using `angle` types".to_string(); + let kind = SemanticErrorKind::NotSupported(message, input.span); + self.push_semantic_error(kind); + } + } + } + let output = self.symbols.get_output(); self.create_entry_item( name, @@ -175,6 +202,7 @@ impl QasmCompiler { ) } + #[allow(clippy::too_many_lines)] fn create_entry_item>( &mut self, name: S, @@ -192,6 +220,60 @@ impl QasmCompiler { name: name.as_ref().to_string(), ns: None, }; + let output_ty = self.apply_output_semantics( + output, + whole_span, + output_semantics, + &mut stmts, + is_qiskit, + ); + + let ast_ty = map_qsharp_type_to_ast_ty(&output_ty); + signature.output = format!("{output_ty}"); + // TODO: This can create a collision on multiple compiles when interactive + // We also have issues with the new entry point inference logic + let input_desc = input + .iter() + .flat_map(|s| { + s.iter() + .map(|s| (s.name.to_string(), format!("{}", s.qsharp_ty))) + }) + .collect::>(); + signature.input = input_desc; + let input_pats = input + .into_iter() + .flat_map(|s| { + s.into_iter().map(|s| { + build_arg_pat( + s.name.clone(), + s.span, + map_qsharp_type_to_ast_ty(&s.qsharp_ty), + ) + }) + }) + .collect::>(); + let add_entry_point_attr = matches!(self.config.program_ty, ProgramType::File); + ( + build_operation_with_stmts( + name, + input_pats, + ast_ty, + stmts, + whole_span, + add_entry_point_attr, + ), + signature, + ) + } + + fn apply_output_semantics( + &mut self, + output: Option>>, + whole_span: Span, + output_semantics: OutputSemantics, + stmts: &mut Vec, + is_qiskit: bool, + ) -> crate::types::Type { let output_ty = if matches!(output_semantics, OutputSemantics::ResourceEstimation) { // we have no output, but need to set the entry point return type crate::types::Type::Tuple(vec![]) @@ -214,7 +296,21 @@ impl QasmCompiler { output .iter() .map(|symbol| { - build_path_ident_expr(symbol.name.as_str(), symbol.span, symbol.span) + let ident = + build_path_ident_expr(symbol.name.as_str(), symbol.span, symbol.span); + if matches!(symbol.ty, Type::Angle(..)) { + // we can't output a struct, so we need to convert it to a double + build_call_with_param( + "__AngleAsDouble__", + &[], + ident, + symbol.span, + symbol.span, + symbol.span, + ) + } else { + ident + } }) .collect::>() }; @@ -233,7 +329,13 @@ impl QasmCompiler { } else { output .iter() - .map(|symbol| symbol.qsharp_ty.clone()) + .map(|symbol| { + if matches!(symbol.qsharp_ty, crate::types::Type::Angle(..)) { + crate::types::Type::Double(symbol.ty.is_const()) + } else { + symbol.qsharp_ty.clone() + } + }) .collect::>() }; @@ -255,36 +357,7 @@ impl QasmCompiler { } crate::types::Type::Tuple(vec![]) }; - - let ast_ty = map_qsharp_type_to_ast_ty(&output_ty); - signature.output = format!("{output_ty}"); - // TODO: This can create a collision on multiple compiles when interactive - // We also have issues with the new entry point inference logic - let input_desc = input - .iter() - .flat_map(|s| { - s.iter() - .map(|s| (s.name.to_string(), format!("{}", s.qsharp_ty))) - }) - .collect::>(); - signature.input = input_desc; - let input_pats = input - .into_iter() - .flat_map(|s| { - s.into_iter().map(|s| { - build_arg_pat( - s.name.clone(), - s.span, - map_qsharp_type_to_ast_ty(&s.qsharp_ty), - ) - }) - }) - .collect::>(); - - ( - build_operation_with_stmts(name, input_pats, ast_ty, stmts, whole_span), - signature, - ) + output_ty } /// Appends the runtime imports to the compiled statements. diff --git a/compiler/qsc_qasm3/src/tests/declaration/io/explicit_input.rs b/compiler/qsc_qasm3/src/tests/declaration/io/explicit_input.rs index 39c4fe404d..7224dc346f 100644 --- a/compiler/qsc_qasm3/src/tests/declaration/io/explicit_input.rs +++ b/compiler/qsc_qasm3/src/tests/declaration/io/explicit_input.rs @@ -220,6 +220,22 @@ input qubit q; .contains("expected scalar or array type, found keyword `qubit`")); } +#[test] +fn lifting_angle_raises_compile_error() { + let source = r#" +input angle a; +"#; + + let Err(error) = compile_qasm_to_qsharp_operation(source) else { + panic!("Expected error") + }; + + assert_eq!( + error[0].to_string(), + "use `float` types for passing input, using `angle` types are not supported." + ); +} + #[test] fn order_is_preserved_with_multiple_inputs() -> miette::Result<(), Vec> { let source = r#" diff --git a/compiler/qsc_qasm3/src/tests/declaration/io/explicit_output.rs b/compiler/qsc_qasm3/src/tests/declaration/io/explicit_output.rs index ce241820d9..ea8ac75890 100644 --- a/compiler/qsc_qasm3/src/tests/declaration/io/explicit_output.rs +++ b/compiler/qsc_qasm3/src/tests/declaration/io/explicit_output.rs @@ -13,7 +13,6 @@ output bit[2] c; let qsharp = compile_qasm_to_qsharp_operation(source)?; expect![[r#" - @EntryPoint() operation Test() : Result[] { import QasmStd.Angle.*; import QasmStd.Convert.*; @@ -34,7 +33,6 @@ output bit c; let qsharp = compile_qasm_to_qsharp_operation(source)?; expect![[r#" - @EntryPoint() operation Test() : Result { import QasmStd.Angle.*; import QasmStd.Convert.*; @@ -55,7 +53,6 @@ output bool c; let qsharp = compile_qasm_to_qsharp_operation(source)?; expect![[r#" - @EntryPoint() operation Test() : Bool { import QasmStd.Angle.*; import QasmStd.Convert.*; @@ -76,7 +73,6 @@ output complex[float] c; let qsharp = compile_qasm_to_qsharp_operation(source)?; expect![[r#" - @EntryPoint() operation Test() : Microsoft.Quantum.Math.Complex { import QasmStd.Angle.*; import QasmStd.Convert.*; @@ -97,7 +93,6 @@ output float f; let qsharp = compile_qasm_to_qsharp_operation(source)?; expect![[r#" - @EntryPoint() operation Test() : Double { import QasmStd.Angle.*; import QasmStd.Convert.*; @@ -118,7 +113,6 @@ output float[42] f; let qsharp = compile_qasm_to_qsharp_operation(source)?; expect![[r#" - @EntryPoint() operation Test() : Double { import QasmStd.Angle.*; import QasmStd.Convert.*; @@ -139,7 +133,6 @@ output int[42] i; let qsharp = compile_qasm_to_qsharp_operation(source)?; expect![[r#" - @EntryPoint() operation Test() : Int { import QasmStd.Angle.*; import QasmStd.Convert.*; @@ -160,7 +153,6 @@ output int i; let qsharp = compile_qasm_to_qsharp_operation(source)?; expect![[r#" - @EntryPoint() operation Test() : Int { import QasmStd.Angle.*; import QasmStd.Convert.*; @@ -181,7 +173,6 @@ output uint i; let qsharp = compile_qasm_to_qsharp_operation(source)?; expect![[r#" - @EntryPoint() operation Test() : Int { import QasmStd.Angle.*; import QasmStd.Convert.*; @@ -202,7 +193,6 @@ output uint[42] i; let qsharp = compile_qasm_to_qsharp_operation(source)?; expect![[r#" - @EntryPoint() operation Test() : Int { import QasmStd.Angle.*; import QasmStd.Convert.*; @@ -223,7 +213,6 @@ output int[65] i; let qsharp = compile_qasm_to_qsharp_operation(source)?; expect![[r#" - @EntryPoint() operation Test() : BigInt { import QasmStd.Angle.*; import QasmStd.Convert.*; @@ -267,7 +256,6 @@ output bit[2] b2; let qsharp = compile_qasm_to_qsharp_operation(source)?; expect![[r#" - @EntryPoint() operation Test() : (BigInt, Int, Int, Int, Double, Bool, Result, Microsoft.Quantum.Math.Complex, Result[]) { import QasmStd.Angle.*; import QasmStd.Convert.*; @@ -287,3 +275,26 @@ output bit[2] b2; .assert_eq(&qsharp); Ok(()) } + +#[test] +fn angle_explicit_returned_as_double() -> miette::Result<(), Vec> { + let source = r#" +output angle c; +"#; + + let qsharp = compile_qasm_to_qsharp_operation(source)?; + expect![[r#" + operation Test() : Double { + import QasmStd.Angle.*; + import QasmStd.Convert.*; + import QasmStd.Intrinsic.*; + mutable c = new __Angle__ { + Value = 0, + Size = 53 + }; + __AngleAsDouble__(c) + } + "#]] + .assert_eq(&qsharp); + Ok(()) +} diff --git a/compiler/qsc_qasm3/src/tests/declaration/io/implicit_output.rs b/compiler/qsc_qasm3/src/tests/declaration/io/implicit_output.rs index 88db764e6d..79b7291ec8 100644 --- a/compiler/qsc_qasm3/src/tests/declaration/io/implicit_output.rs +++ b/compiler/qsc_qasm3/src/tests/declaration/io/implicit_output.rs @@ -13,7 +13,6 @@ bit[2] c; let qsharp = compile_qasm_to_qsharp_operation(source)?; expect![[r#" - @EntryPoint() operation Test() : Result[] { import QasmStd.Angle.*; import QasmStd.Convert.*; @@ -34,7 +33,6 @@ bit c; let qsharp = compile_qasm_to_qsharp_operation(source)?; expect![[r#" - @EntryPoint() operation Test() : Result { import QasmStd.Angle.*; import QasmStd.Convert.*; @@ -55,7 +53,6 @@ bool c; let qsharp = compile_qasm_to_qsharp_operation(source)?; expect![[r#" - @EntryPoint() operation Test() : Bool { import QasmStd.Angle.*; import QasmStd.Convert.*; @@ -76,7 +73,6 @@ complex[float] c; let qsharp = compile_qasm_to_qsharp_operation(source)?; expect![[r#" - @EntryPoint() operation Test() : Microsoft.Quantum.Math.Complex { import QasmStd.Angle.*; import QasmStd.Convert.*; @@ -97,7 +93,6 @@ float f; let qsharp = compile_qasm_to_qsharp_operation(source)?; expect![[r#" - @EntryPoint() operation Test() : Double { import QasmStd.Angle.*; import QasmStd.Convert.*; @@ -118,7 +113,6 @@ float[42] f; let qsharp = compile_qasm_to_qsharp_operation(source)?; expect![[r#" - @EntryPoint() operation Test() : Double { import QasmStd.Angle.*; import QasmStd.Convert.*; @@ -139,7 +133,6 @@ int[42] i; let qsharp = compile_qasm_to_qsharp_operation(source)?; expect![[r#" - @EntryPoint() operation Test() : Int { import QasmStd.Angle.*; import QasmStd.Convert.*; @@ -160,7 +153,6 @@ int i; let qsharp = compile_qasm_to_qsharp_operation(source)?; expect![[r#" - @EntryPoint() operation Test() : Int { import QasmStd.Angle.*; import QasmStd.Convert.*; @@ -181,7 +173,6 @@ uint i; let qsharp = compile_qasm_to_qsharp_operation(source)?; expect![[r#" - @EntryPoint() operation Test() : Int { import QasmStd.Angle.*; import QasmStd.Convert.*; @@ -202,7 +193,6 @@ uint[42] i; let qsharp = compile_qasm_to_qsharp_operation(source)?; expect![[r#" - @EntryPoint() operation Test() : Int { import QasmStd.Angle.*; import QasmStd.Convert.*; @@ -223,7 +213,6 @@ int[65] i; let qsharp = compile_qasm_to_qsharp_operation(source)?; expect![[r#" - @EntryPoint() operation Test() : BigInt { import QasmStd.Angle.*; import QasmStd.Convert.*; @@ -252,7 +241,6 @@ bit[2] b2; let qsharp = compile_qasm_to_qsharp_operation(source)?; expect![[r#" - @EntryPoint() operation Test() : (BigInt, Int, Int, Int, Double, Bool, Result, Microsoft.Quantum.Math.Complex, Result[]) { import QasmStd.Angle.*; import QasmStd.Convert.*; @@ -272,3 +260,26 @@ bit[2] b2; .assert_eq(&qsharp); Ok(()) } + +#[test] +fn angle_is_inferred_and_returned_as_double() -> miette::Result<(), Vec> { + let source = r#" +angle c; +"#; + + let qsharp = compile_qasm_to_qsharp_operation(source)?; + expect![[r#" + operation Test() : Double { + import QasmStd.Angle.*; + import QasmStd.Convert.*; + import QasmStd.Intrinsic.*; + mutable c = new __Angle__ { + Value = 0, + Size = 53 + }; + __AngleAsDouble__(c) + } + "#]] + .assert_eq(&qsharp); + Ok(()) +} diff --git a/compiler/qsc_qasm3/src/tests/expression/binary/comparison.rs b/compiler/qsc_qasm3/src/tests/expression/binary/comparison.rs index e07e28d457..a14625c864 100644 --- a/compiler/qsc_qasm3/src/tests/expression/binary/comparison.rs +++ b/compiler/qsc_qasm3/src/tests/expression/binary/comparison.rs @@ -25,11 +25,11 @@ fn int_var_comparisons_can_be_translated() -> miette::Result<(), Vec> { let qsharp = compile_qasm_to_qsharp_file(source)?; expect![[r#" namespace qasm3_import { + import QasmStd.Angle.*; + import QasmStd.Convert.*; + import QasmStd.Intrinsic.*; @EntryPoint() operation Test() : (Int, Int, Bool, Bool, Bool, Bool, Bool, Bool) { - import QasmStd.Angle.*; - import QasmStd.Convert.*; - import QasmStd.Intrinsic.*; mutable x = 5; mutable y = 3; mutable f = (x > y); @@ -61,11 +61,11 @@ fn uint_var_comparisons_can_be_translated() -> miette::Result<(), Vec> { let qsharp = compile_qasm_to_qsharp_file(source)?; expect![[r#" namespace qasm3_import { + import QasmStd.Angle.*; + import QasmStd.Convert.*; + import QasmStd.Intrinsic.*; @EntryPoint() operation Test() : (Int, Int, Bool, Bool, Bool, Bool, Bool, Bool) { - import QasmStd.Angle.*; - import QasmStd.Convert.*; - import QasmStd.Intrinsic.*; mutable x = 5; mutable y = 3; mutable f = (x > y); @@ -97,11 +97,11 @@ fn bit_var_comparisons_can_be_translated() -> miette::Result<(), Vec> { let qsharp = compile_qasm_to_qsharp_file(source)?; expect![[r#" namespace qasm3_import { + import QasmStd.Angle.*; + import QasmStd.Convert.*; + import QasmStd.Intrinsic.*; @EntryPoint() operation Test() : (Result, Result, Bool, Bool, Bool, Bool, Bool, Bool) { - import QasmStd.Angle.*; - import QasmStd.Convert.*; - import QasmStd.Intrinsic.*; mutable x = One; mutable y = Zero; mutable f = (x > y); @@ -133,11 +133,11 @@ fn bitarray_var_comparisons_can_be_translated() -> miette::Result<(), Vec __ResultArrayAsIntBE__(y)); @@ -175,10 +175,10 @@ fn bitarray_var_comparison_to_int_can_be_translated() -> miette::Result<(), Vec< let qsharp = compile_qasm_to_qsharp_file(source)?; expect![[r#" namespace qasm3_import { + import QasmStd.Angle.*; + import QasmStd.Convert.*; + import QasmStd.Intrinsic.*; operation Test(y : Int) : (Result[], Bool, Bool, Bool, Bool, Bool, Bool, Bool, Bool, Bool, Bool, Bool, Bool) { - import QasmStd.Angle.*; - import QasmStd.Convert.*; - import QasmStd.Intrinsic.*; mutable x = [One]; mutable a = (__ResultArrayAsIntBE__(x) > y); mutable b = (__ResultArrayAsIntBE__(x) >= y); @@ -215,11 +215,11 @@ fn float_var_comparisons_can_be_translated() -> miette::Result<(), Vec> let qsharp = compile_qasm_to_qsharp_file(source)?; expect![[r#" namespace qasm3_import { + import QasmStd.Angle.*; + import QasmStd.Convert.*; + import QasmStd.Intrinsic.*; @EntryPoint() operation Test() : (Double, Double, Bool, Bool, Bool, Bool, Bool, Bool) { - import QasmStd.Angle.*; - import QasmStd.Convert.*; - import QasmStd.Intrinsic.*; mutable x = 5.; mutable y = 3.; mutable f = (x > y); @@ -253,11 +253,11 @@ fn bool_var_comparisons_can_be_translated() -> miette::Result<(), Vec> { let qsharp = compile_qasm_to_qsharp_file(source)?; expect![[r#" namespace qasm3_import { + import QasmStd.Angle.*; + import QasmStd.Convert.*; + import QasmStd.Intrinsic.*; @EntryPoint() operation Test() : (Bool, Bool, Bool, Bool, Bool, Bool, Bool, Bool, Bool, Bool) { - import QasmStd.Angle.*; - import QasmStd.Convert.*; - import QasmStd.Intrinsic.*; mutable x = true; mutable y = false; mutable a = (x and y); diff --git a/compiler/qsc_qasm3/src/tests/expression/bits.rs b/compiler/qsc_qasm3/src/tests/expression/bits.rs index 21e138626b..6751290904 100644 --- a/compiler/qsc_qasm3/src/tests/expression/bits.rs +++ b/compiler/qsc_qasm3/src/tests/expression/bits.rs @@ -25,14 +25,13 @@ fn bit_array_bits_and_register_ops() -> miette::Result<(), Vec> { rs_a_1 = (a >> 1); // Bit shift right produces "01000111" "#; let qsharp = compile_qasm_to_qsharp_file(source)?; - expect![ - r#" + expect![[r#" namespace qasm3_import { + import QasmStd.Angle.*; + import QasmStd.Convert.*; + import QasmStd.Intrinsic.*; @EntryPoint() operation Test() : (Result[], Result[], Result[], Result[], Result[]) { - import QasmStd.Angle.*; - import QasmStd.Convert.*; - import QasmStd.Intrinsic.*; mutable a = [One, Zero, Zero, Zero, One, One, One, One]; mutable b = [Zero, One, One, One, Zero, Zero, Zero, Zero]; mutable ls_a_1 = [Zero, Zero, Zero, Zero, Zero, Zero, Zero, Zero]; @@ -47,8 +46,7 @@ fn bit_array_bits_and_register_ops() -> miette::Result<(), Vec> { set rs_a_1 = (__IntAsResultArrayBE__(__ResultArrayAsIntBE__(a) >>> 1, 8)); (ls_a_1, a_or_b, a_and_b, a_xor_b, rs_a_1) } - }"# - ] + }"#]] .assert_eq(&qsharp); Ok(()) } diff --git a/compiler/qsc_qasm3/src/tests/output.rs b/compiler/qsc_qasm3/src/tests/output.rs index 0e9bcac15b..f3cd433dbc 100644 --- a/compiler/qsc_qasm3/src/tests/output.rs +++ b/compiler/qsc_qasm3/src/tests/output.rs @@ -40,10 +40,10 @@ fn using_re_semantics_removes_output() -> miette::Result<(), Vec> { let qsharp = gen_qsharp(&unit.package.expect("no package found")); expect![[r#" namespace qasm3_import { + import QasmStd.Angle.*; + import QasmStd.Convert.*; + import QasmStd.Intrinsic.*; operation Test(theta : Double, beta : Int) : Unit { - import QasmStd.Angle.*; - import QasmStd.Convert.*; - import QasmStd.Intrinsic.*; mutable c = [Zero, Zero]; let q = QIR.Runtime.AllocateQubitArray(2); mutable gamma = 0.; @@ -91,10 +91,10 @@ fn using_qasm_semantics_captures_all_classical_decls_as_output() -> miette::Resu let qsharp = gen_qsharp(&unit.package.expect("no package found")); expect![[r#" namespace qasm3_import { + import QasmStd.Angle.*; + import QasmStd.Convert.*; + import QasmStd.Intrinsic.*; operation Test(theta : Double, beta : Int) : (Result[], Double, Double) { - import QasmStd.Angle.*; - import QasmStd.Convert.*; - import QasmStd.Intrinsic.*; mutable c = [Zero, Zero]; let q = QIR.Runtime.AllocateQubitArray(2); mutable gamma = 0.; @@ -142,10 +142,10 @@ fn using_qiskit_semantics_only_bit_array_is_captured_and_reversed( let qsharp = gen_qsharp(&unit.package.expect("no package found")); expect![[r#" namespace qasm3_import { + import QasmStd.Angle.*; + import QasmStd.Convert.*; + import QasmStd.Intrinsic.*; operation Test(theta : Double, beta : Int) : Result[] { - import QasmStd.Angle.*; - import QasmStd.Convert.*; - import QasmStd.Intrinsic.*; mutable c = [Zero, Zero]; let q = QIR.Runtime.AllocateQubitArray(2); mutable gamma = 0.; @@ -201,10 +201,10 @@ c2[2] = measure q[4]; let qsharp = gen_qsharp(&package.clone()); expect![[r#" namespace qasm3_import { + import QasmStd.Angle.*; + import QasmStd.Convert.*; + import QasmStd.Intrinsic.*; operation Test(theta : Double, beta : Int) : (Result[], Result[]) { - import QasmStd.Angle.*; - import QasmStd.Convert.*; - import QasmStd.Intrinsic.*; mutable c = [Zero, Zero]; mutable c2 = [Zero, Zero, Zero]; let q = QIR.Runtime.AllocateQubitArray(5); diff --git a/compiler/qsc_qasm3/src/tests/statement/include.rs b/compiler/qsc_qasm3/src/tests/statement/include.rs index 3c95d89622..20f1abb5db 100644 --- a/compiler/qsc_qasm3/src/tests/statement/include.rs +++ b/compiler/qsc_qasm3/src/tests/statement/include.rs @@ -40,11 +40,11 @@ fn programs_with_includes_can_be_parsed() -> miette::Result<(), Vec> { let qsharp = qsharp_from_qasm_compilation(r)?; expect![[r#" namespace qasm3_import { + import QasmStd.Angle.*; + import QasmStd.Convert.*; + import QasmStd.Intrinsic.*; @EntryPoint() operation Test() : Result[] { - import QasmStd.Angle.*; - import QasmStd.Convert.*; - import QasmStd.Intrinsic.*; let my_gate : (Qubit) => Unit = (q) => { x(q); }; diff --git a/compiler/qsc_qasm3/src/tests/statement/reset.rs b/compiler/qsc_qasm3/src/tests/statement/reset.rs index fa4689dd25..d0ef520368 100644 --- a/compiler/qsc_qasm3/src/tests/statement/reset.rs +++ b/compiler/qsc_qasm3/src/tests/statement/reset.rs @@ -35,11 +35,11 @@ fn reset_calls_are_generated_from_qasm() -> miette::Result<(), Vec> { let qsharp = gen_qsharp(&unit.package.expect("no package found")); expect![[r#" namespace qasm3_import { + import QasmStd.Angle.*; + import QasmStd.Convert.*; + import QasmStd.Intrinsic.*; @EntryPoint() operation Test() : Result[] { - import QasmStd.Angle.*; - import QasmStd.Convert.*; - import QasmStd.Intrinsic.*; mutable meas = [Zero]; let q = QIR.Runtime.AllocateQubitArray(1); Reset(q[0]); diff --git a/compiler/qsc_qasm3/src/tests/statement/switch.rs b/compiler/qsc_qasm3/src/tests/statement/switch.rs index 874d753bdb..1c8674c2d8 100644 --- a/compiler/qsc_qasm3/src/tests/statement/switch.rs +++ b/compiler/qsc_qasm3/src/tests/statement/switch.rs @@ -191,11 +191,11 @@ fn spec_case_3() -> miette::Result<(), Vec> { let qsharp = compile_qasm_to_qsharp_file(source)?; expect![[r#" namespace qasm3_import { + import QasmStd.Angle.*; + import QasmStd.Convert.*; + import QasmStd.Intrinsic.*; @EntryPoint() operation Test() : Result[] { - import QasmStd.Angle.*; - import QasmStd.Convert.*; - import QasmStd.Intrinsic.*; let q = QIR.Runtime.__quantum__rt__qubit_allocate(); mutable b = [Zero, Zero]; if __ResultArrayAsIntBE__(b) == 0 { From 483d384e870a9cb63b97f7ff7317587e90b9f61c Mon Sep 17 00:00:00 2001 From: Ian Davis Date: Tue, 1 Apr 2025 07:35:34 -0700 Subject: [PATCH 073/108] Fix scoping resolution for defs --- compiler/qsc_qasm3/src/semantic/symbols.rs | 6 +- compiler/qsc_qasm3/src/tests/scopes.rs | 170 +++++++++++++++++++++ 2 files changed, 174 insertions(+), 2 deletions(-) diff --git a/compiler/qsc_qasm3/src/semantic/symbols.rs b/compiler/qsc_qasm3/src/semantic/symbols.rs index 11a0a01dd6..b954a1bdd8 100644 --- a/compiler/qsc_qasm3/src/semantic/symbols.rs +++ b/compiler/qsc_qasm3/src/semantic/symbols.rs @@ -447,7 +447,7 @@ impl SymbolTable { if let Some(scope) = last_false { if let Some((id, symbol)) = scope.get_symbol_by_name(name.as_ref()) { if symbol.ty.is_const() - || matches!(symbol.ty, Type::Gate(..) | Type::Void) + || matches!(symbol.ty, Type::Gate(..) | Type::Void | Type::Function(..)) || self.is_scope_rooted_in_global() { return Some((id, symbol)); @@ -457,7 +457,9 @@ impl SymbolTable { // we should be at the global, function, or gate scope now for scope in scopes { if let Some((id, symbol)) = scope.get_symbol_by_name(name.as_ref()) { - if symbol.ty.is_const() || matches!(symbol.ty, Type::Gate(..) | Type::Void) { + if symbol.ty.is_const() + || matches!(symbol.ty, Type::Gate(..) | Type::Void | Type::Function(..)) + { return Some((id, symbol)); } } diff --git a/compiler/qsc_qasm3/src/tests/scopes.rs b/compiler/qsc_qasm3/src/tests/scopes.rs index 7651ee223d..4f5bbd49ff 100644 --- a/compiler/qsc_qasm3/src/tests/scopes.rs +++ b/compiler/qsc_qasm3/src/tests/scopes.rs @@ -54,3 +54,173 @@ fn cannot_access_mutable_decls_from_global_scope() { }; expect![r#"Undefined symbol: i."#].assert_eq(&errors[0].to_string()); } + +#[test] +fn gates_can_call_previously_declared_gates() -> miette::Result<(), Vec> { + let source = r#" + include "stdgates.inc"; + gate my_h q { + h q; + } + gate my_hx q { + my_h q; + x q; + } + qubit q; + my_hx q; + "#; + + let qsharp = compile_qasm_to_qsharp(source)?; + expect![[r#" + import QasmStd.Angle.*; + import QasmStd.Convert.*; + import QasmStd.Intrinsic.*; + let my_h : (Qubit) => Unit = (q) => { + h(q); + }; + let my_hx : (Qubit) => Unit = (q) => { + my_h(q); + x(q); + }; + let q = QIR.Runtime.__quantum__rt__qubit_allocate(); + my_hx(q); + "#]] + .assert_eq(&qsharp); + Ok(()) +} + +#[test] +fn def_can_call_previously_declared_def() -> miette::Result<(), Vec> { + let source = r#" + include "stdgates.inc"; + def apply_h(qubit q) { + h q; + } + def apply_hx(qubit q) { + apply_h(q); + x q; + } + qubit q; + apply_hx(q); + "#; + + let qsharp = compile_qasm_to_qsharp(source)?; + expect![[r#" + import QasmStd.Angle.*; + import QasmStd.Convert.*; + import QasmStd.Intrinsic.*; + let apply_h : (Qubit) => Unit = (q) => { + h(q); + }; + let apply_hx : (Qubit) => Unit = (q) => { + apply_h(q); + x(q); + }; + let q = QIR.Runtime.__quantum__rt__qubit_allocate(); + apply_hx(q); + "#]] + .assert_eq(&qsharp); + Ok(()) +} + +#[test] +fn gate_can_call_previously_declared_def() -> miette::Result<(), Vec> { + let source = r#" + include "stdgates.inc"; + def apply_h(qubit q) { + h q; + } + gate my_hx q { + apply_h(q); + x q; + } + qubit q; + my_hx q; + "#; + + let qsharp = compile_qasm_to_qsharp(source)?; + expect![[r#" + import QasmStd.Angle.*; + import QasmStd.Convert.*; + import QasmStd.Intrinsic.*; + let apply_h : (Qubit) => Unit = (q) => { + h(q); + }; + let my_hx : (Qubit) => Unit = (q) => { + apply_h(q); + x(q); + }; + let q = QIR.Runtime.__quantum__rt__qubit_allocate(); + my_hx(q); + "#]] + .assert_eq(&qsharp); + Ok(()) +} + +#[test] +fn def_can_call_previously_declared_gate() -> miette::Result<(), Vec> { + let source = r#" + include "stdgates.inc"; + gate my_h q { + h q; + } + def apply_hx(qubit q) { + my_h q; + x q; + } + qubit q; + apply_hx(q); + "#; + + let qsharp = compile_qasm_to_qsharp(source)?; + expect![[r#" + import QasmStd.Angle.*; + import QasmStd.Convert.*; + import QasmStd.Intrinsic.*; + let my_h : (Qubit) => Unit = (q) => { + h(q); + }; + let apply_hx : (Qubit) => Unit = (q) => { + my_h(q); + x(q); + }; + let q = QIR.Runtime.__quantum__rt__qubit_allocate(); + apply_hx(q); + "#]] + .assert_eq(&qsharp); + Ok(()) +} + +#[test] +fn def_can_call_itself_recursively() -> miette::Result<(), Vec> { + let source = r#" + include "stdgates.inc"; + def apply_hx(int limit, qubit q) { + if (limit > 0) { + apply_hx(limit - 1, q); + x q; + } + h q; + } + qubit q; + apply_hx(2, q); + "#; + + let qsharp = compile_qasm_to_qsharp(source)?; + expect![[r#" + import QasmStd.Angle.*; + import QasmStd.Convert.*; + import QasmStd.Intrinsic.*; + let apply_hx : (Int, Qubit) => Unit = (limit, q) => { + if limit > 0 { + apply_hx(limit - 1, q); + x(q); + }; + h(q); + }; + let q = QIR.Runtime.__quantum__rt__qubit_allocate(); + apply_hx(2, q); + "#]] + .assert_eq(&qsharp); + Ok(()) +} From a8022c8f12db7c7252cfb88c81d3e6fc18b5c744 Mon Sep 17 00:00:00 2001 From: orpuente-MS <156957451+orpuente-MS@users.noreply.github.com> Date: Tue, 1 Apr 2025 10:44:41 -0700 Subject: [PATCH 074/108] functions and operations with const eval (#2270) Compile functions and operations const evaluating any references to symbol in an external scope. --- compiler/qsc_qasm3/src/ast_builder.rs | 108 ++++++++++++- compiler/qsc_qasm3/src/compiler.rs | 36 +++-- compiler/qsc_qasm3/src/semantic/lowerer.rs | 42 ++++- compiler/qsc_qasm3/src/semantic/symbols.rs | 33 +++- .../qsc_qasm3/src/tests/declaration/def.rs | 14 +- .../qsc_qasm3/src/tests/declaration/gate.rs | 28 ++-- .../src/tests/expression/function_call.rs | 28 ++-- compiler/qsc_qasm3/src/tests/scopes.rs | 42 ++--- .../src/tests/statement/annotation.rs | 4 +- .../src/tests/statement/gate_call.rs | 149 ++++++++++++++++++ .../qsc_qasm3/src/tests/statement/include.rs | 4 +- .../qsc_qasm3/src/tests/statement/switch.rs | 4 +- 12 files changed, 405 insertions(+), 87 deletions(-) diff --git a/compiler/qsc_qasm3/src/ast_builder.rs b/compiler/qsc_qasm3/src/ast_builder.rs index ccdf6e92c2..ce7eed4c2d 100644 --- a/compiler/qsc_qasm3/src/ast_builder.rs +++ b/compiler/qsc_qasm3/src/ast_builder.rs @@ -7,9 +7,9 @@ use num_bigint::BigInt; use qsc_ast::ast::{ self, Attr, Block, CallableBody, CallableDecl, CallableKind, Expr, ExprKind, FieldAssign, - Ident, ImportOrExportDecl, ImportOrExportItem, Item, ItemKind, Lit, Mutability, NodeId, Pat, - PatKind, Path, PathKind, QubitInit, QubitInitKind, QubitSource, Stmt, StmtKind, TopLevelNode, - Ty, TyKind, + FunctorExpr, FunctorExprKind, Ident, ImportOrExportDecl, ImportOrExportItem, Item, ItemKind, + Lit, Mutability, NodeId, Pat, PatKind, Path, PathKind, QubitInit, QubitInitKind, QubitSource, + Stmt, StmtKind, TopLevelNode, Ty, TyKind, }; use qsc_data_structures::span::Span; @@ -1691,6 +1691,108 @@ pub(crate) fn build_lambda>( } } +#[allow(clippy::too_many_arguments, clippy::too_many_lines)] +pub(crate) fn build_function_or_operation( + name: String, + cargs: Vec<(String, Ty, Pat)>, + qargs: Vec<(String, Ty, Pat)>, + body: Option, + name_span: Span, + body_span: Span, + gate_span: Span, + return_type: Option, + kind: CallableKind, + functors: Option, +) -> Stmt { + let args = cargs + .into_iter() + .chain(qargs) + .map(|(_, _, pat)| Box::new(pat)) + .collect::>(); + + let lo = args + .iter() + .min_by_key(|x| x.span.lo) + .map(|x| x.span.lo) + .unwrap_or_default(); + + let hi = args + .iter() + .max_by_key(|x| x.span.hi) + .map(|x| x.span.hi) + .unwrap_or_default(); + + let input_pat_kind = if args.len() == 1 { + PatKind::Paren(args[0].clone()) + } else { + PatKind::Tuple(args.into_boxed_slice()) + }; + + let input_pat = Pat { + kind: Box::new(input_pat_kind), + span: Span { lo, hi }, + ..Default::default() + }; + + let return_type = if let Some(ty) = return_type { + ty + } else { + build_path_ident_ty("Unit") + }; + + let body = CallableBody::Block(Box::new(body.unwrap_or_else(|| Block { + id: NodeId::default(), + span: body_span, + stmts: Box::new([]), + }))); + + let decl = CallableDecl { + id: NodeId::default(), + span: name_span, + kind, + name: Box::new(Ident { + name: name.into(), + ..Default::default() + }), + generics: Box::new([]), + input: Box::new(input_pat), + output: Box::new(return_type), + functors: functors.map(Box::new), + body: Box::new(body), + }; + let item = Item { + span: gate_span, + kind: Box::new(ast::ItemKind::Callable(Box::new(decl))), + ..Default::default() + }; + + Stmt { + kind: Box::new(StmtKind::Item(Box::new(item))), + span: gate_span, + ..Default::default() + } +} + +pub(crate) fn build_adj_plus_ctl_functor() -> FunctorExpr { + let adj = Box::new(FunctorExpr { + kind: Box::new(FunctorExprKind::Lit(ast::Functor::Adj)), + id: Default::default(), + span: Default::default(), + }); + + let ctl = Box::new(FunctorExpr { + kind: Box::new(FunctorExprKind::Lit(ast::Functor::Ctl)), + id: Default::default(), + span: Default::default(), + }); + + FunctorExpr { + kind: Box::new(FunctorExprKind::BinOp(ast::SetOp::Union, adj, ctl)), + id: Default::default(), + span: Default::default(), + } +} + fn build_idents(idents: &[&str]) -> Option> { let idents = idents .iter() diff --git a/compiler/qsc_qasm3/src/compiler.rs b/compiler/qsc_qasm3/src/compiler.rs index 90b10ac1a4..7b99e2b040 100644 --- a/compiler/qsc_qasm3/src/compiler.rs +++ b/compiler/qsc_qasm3/src/compiler.rs @@ -10,20 +10,22 @@ use qsc_frontend::{compile::SourceMap, error::WithSource}; use crate::{ ast_builder::{ - build_arg_pat, build_array_reverse_expr, build_assignment_statement, build_barrier_call, - build_binary_expr, build_call_no_params, build_call_with_param, build_call_with_params, - build_cast_call_by_name, build_classical_decl, build_complex_from_expr, - build_convert_call_expr, build_expr_array_expr, build_for_stmt, build_gate_call_param_expr, - build_gate_call_with_params_and_callee, build_global_call_with_two_params, - build_if_expr_then_block, build_if_expr_then_block_else_block, - build_if_expr_then_block_else_expr, build_if_expr_then_expr_else_expr, - build_implicit_return_stmt, build_indexed_assignment_statement, build_lambda, - build_lit_angle_expr, build_lit_bigint_expr, build_lit_bool_expr, build_lit_complex_expr, - build_lit_double_expr, build_lit_int_expr, build_lit_result_array_expr_from_bitstring, - build_lit_result_expr, build_managed_qubit_alloc, build_math_call_from_exprs, - build_math_call_no_params, build_measure_call, build_operation_with_stmts, - build_path_ident_expr, build_qasm_import_decl, build_qasm_import_items, build_range_expr, - build_reset_call, build_return_expr, build_return_unit, build_stmt_semi_from_expr, + build_adj_plus_ctl_functor, build_arg_pat, build_array_reverse_expr, + build_assignment_statement, build_barrier_call, build_binary_expr, build_call_no_params, + build_call_with_param, build_call_with_params, build_cast_call_by_name, + build_classical_decl, build_complex_from_expr, build_convert_call_expr, + build_expr_array_expr, build_for_stmt, build_function_or_operation, + build_gate_call_param_expr, build_gate_call_with_params_and_callee, + build_global_call_with_two_params, build_if_expr_then_block, + build_if_expr_then_block_else_block, build_if_expr_then_block_else_expr, + build_if_expr_then_expr_else_expr, build_implicit_return_stmt, + build_indexed_assignment_statement, build_lit_angle_expr, build_lit_bigint_expr, + build_lit_bool_expr, build_lit_complex_expr, build_lit_double_expr, build_lit_int_expr, + build_lit_result_array_expr_from_bitstring, build_lit_result_expr, + build_managed_qubit_alloc, build_math_call_from_exprs, build_math_call_no_params, + build_measure_call, build_operation_with_stmts, build_path_ident_expr, + build_qasm_import_decl, build_qasm_import_items, build_range_expr, build_reset_call, + build_return_expr, build_return_unit, build_stmt_semi_from_expr, build_stmt_semi_from_expr_with_span, build_top_level_ns_with_items, build_tuple_expr, build_unary_op_expr, build_unmanaged_qubit_alloc, build_unmanaged_qubit_alloc_array, build_while_stmt, build_wrapped_block_expr, managed_qubit_alloc_array, @@ -627,7 +629,7 @@ impl QasmCompiler { // We use the same primitives used for declaring gates, because def declarations // in QASM3 can take qubits as arguments and call quantum gates. - Some(build_lambda( + Some(build_function_or_operation( name, cargs, vec![], @@ -637,6 +639,7 @@ impl QasmCompiler { stmt.span, return_type, kind, + None, )) } @@ -899,7 +902,7 @@ impl QasmCompiler { let body = Some(self.compile_block(&stmt.body)); - Some(build_lambda( + Some(build_function_or_operation( name, cargs, qargs, @@ -909,6 +912,7 @@ impl QasmCompiler { stmt.span, None, qsast::CallableKind::Operation, + Some(build_adj_plus_ctl_functor()), )) } diff --git a/compiler/qsc_qasm3/src/semantic/lowerer.rs b/compiler/qsc_qasm3/src/semantic/lowerer.rs index 14071415a1..c34b73dcb5 100644 --- a/compiler/qsc_qasm3/src/semantic/lowerer.rs +++ b/compiler/qsc_qasm3/src/semantic/lowerer.rs @@ -257,7 +257,7 @@ impl Lowerer { gate_symbol("cx", 0, 2), gate_symbol("cy", 0, 2), gate_symbol("cz", 0, 2), - gate_symbol("cp", 0, 2), + gate_symbol("cp", 1, 2), gate_symbol("swap", 0, 2), gate_symbol("ccx", 0, 3), gate_symbol("cu", 4, 2), @@ -520,7 +520,45 @@ impl Lowerer { let (symbol_id, symbol) = self.try_get_existing_or_insert_err_symbol(&name, ident.span); - let kind = semantic::ExprKind::Ident(symbol_id); + // Design Note: The end goal of this const evaluation is to be able to compile qasm + // annotations as Q# attributes like `@SimulatableIntrinsic()`. + // + // QASM3 subroutines and gates can be recursive and capture const symbols + // outside their scope. In Q#, only lambdas can capture symbols, but only + // proper functions and operations can be recursive or have attributes on + // them. To get both, annotations & recursive gates/functions and the + // ability to capture const symbols outside the gate/function scope, we + // decided to compile the gates/functions as proper Q# operations/functions + // and evaluate at lowering-time all references to const symbols outside + // the current gate/function scope. + + // This is true if we are inside any gate or function scope. + let is_symbol_inside_gate_or_function_scope = + self.symbols.is_scope_rooted_in_gate_or_subroutine(); + + // This is true if the symbol is outside the most inner gate or function scope. + let is_symbol_outside_most_inner_gate_or_function_scope = self + .symbols + .is_symbol_outside_most_inner_gate_or_function_scope(symbol_id); + + let is_const_evaluation_necessary = symbol.is_const() + && is_symbol_inside_gate_or_function_scope + && is_symbol_outside_most_inner_gate_or_function_scope; + + let kind = if is_const_evaluation_necessary { + if let Some(val) = symbol.get_const_expr().const_eval(&self.symbols) { + semantic::ExprKind::Lit(val) + } else { + self.push_semantic_error(SemanticErrorKind::ExprMustBeConst( + ident.name.to_string(), + ident.span, + )); + semantic::ExprKind::Err + } + } else { + semantic::ExprKind::Ident(symbol_id) + }; + semantic::Expr { span: ident.span, kind: Box::new(kind), diff --git a/compiler/qsc_qasm3/src/semantic/symbols.rs b/compiler/qsc_qasm3/src/semantic/symbols.rs index b954a1bdd8..0da5f644e7 100644 --- a/compiler/qsc_qasm3/src/semantic/symbols.rs +++ b/compiler/qsc_qasm3/src/semantic/symbols.rs @@ -2,10 +2,9 @@ // Licensed under the MIT License. use core::f64; -use std::rc::Rc; - use qsc_data_structures::{index_map::IndexMap, span::Span}; use rustc_hash::FxHashMap; +use std::rc::Rc; use super::{ ast::{Expr, ExprKind, LiteralKind}, @@ -137,6 +136,12 @@ impl Symbol { } } + /// Returns true if they symbol's value is a const expr. + #[must_use] + pub fn is_const(&self) -> bool { + self.const_expr.is_some() + } + /// Returns the value of the symbol. #[must_use] pub fn get_const_expr(&self) -> Rc { @@ -468,6 +473,22 @@ impl SymbolTable { None } + #[must_use] + pub fn is_symbol_outside_most_inner_gate_or_function_scope(&self, symbol_id: SymbolId) -> bool { + for scope in self.scopes.iter().rev() { + if scope.id_to_symbol.contains_key(&symbol_id) { + return false; + } + if matches!( + scope.kind, + ScopeKind::Gate | ScopeKind::Function | ScopeKind::Global + ) { + return true; + } + } + unreachable!("when the loop ends we will have visited at least the Global scope"); + } + #[must_use] pub fn is_current_scope_global(&self) -> bool { matches!(self.scopes.last(), Some(scope) if scope.kind == ScopeKind::Global) @@ -481,6 +502,14 @@ impl SymbolTable { .any(|scope| scope.kind == ScopeKind::Function) } + #[must_use] + pub fn is_scope_rooted_in_gate_or_subroutine(&self) -> bool { + self.scopes + .iter() + .rev() + .any(|scope| matches!(scope.kind, ScopeKind::Gate | ScopeKind::Function)) + } + #[must_use] pub fn is_scope_rooted_in_global(&self) -> bool { for scope in self.scopes.iter().rev() { diff --git a/compiler/qsc_qasm3/src/tests/declaration/def.rs b/compiler/qsc_qasm3/src/tests/declaration/def.rs index 6fa2418435..6f30a22b97 100644 --- a/compiler/qsc_qasm3/src/tests/declaration/def.rs +++ b/compiler/qsc_qasm3/src/tests/declaration/def.rs @@ -11,7 +11,7 @@ fn no_parameters_no_return() -> miette::Result<(), Vec> { let qsharp = compile_qasm_stmt_to_qsharp(source)?; expect![[r#" - let empty : () -> Unit = () -> {}; + function empty() : Unit {} "#]] .assert_eq(&qsharp); Ok(()) @@ -27,9 +27,9 @@ fn single_parameter() -> miette::Result<(), Vec> { let qsharp = compile_qasm_stmt_to_qsharp(source)?; expect![[r#" - let square : (Int) -> Int = (x) -> { + function square(x : Int) : Int { return x * x; - }; + } "#]] .assert_eq(&qsharp); Ok(()) @@ -45,9 +45,9 @@ fn qubit_parameter() -> miette::Result<(), Vec> { let qsharp = compile_qasm_stmt_to_qsharp(source)?; expect![[r#" - let square : (Qubit) => Int = (q) => { + operation square(q : Qubit) : Int { return 1; - }; + } "#]] .assert_eq(&qsharp); Ok(()) @@ -63,9 +63,9 @@ fn qubit_array_parameter() -> miette::Result<(), Vec> { let qsharp = compile_qasm_stmt_to_qsharp(source)?; expect![[r#" - let square : (Qubit[]) => Int = (qs) => { + operation square(qs : Qubit[]) : Int { return 1; - }; + } "#]] .assert_eq(&qsharp); Ok(()) diff --git a/compiler/qsc_qasm3/src/tests/declaration/gate.rs b/compiler/qsc_qasm3/src/tests/declaration/gate.rs index 7795720a1b..5f3e98d5bd 100644 --- a/compiler/qsc_qasm3/src/tests/declaration/gate.rs +++ b/compiler/qsc_qasm3/src/tests/declaration/gate.rs @@ -15,13 +15,11 @@ fn single_qubit() -> miette::Result<(), Vec> { "#; let qsharp = compile_qasm_stmt_to_qsharp(source)?; - expect![ - r#" - let my_h : (Qubit) => Unit = (q) => { + expect![[r#" + operation my_h(q : Qubit) : Unit is Adj + Ctl { h(q); - }; - "# - ] + } + "#]] .assert_eq(&qsharp); Ok(()) } @@ -37,14 +35,12 @@ fn two_qubits() -> miette::Result<(), Vec> { "#; let qsharp = compile_qasm_stmt_to_qsharp(source)?; - expect![ - r#" - let my_h : (Qubit, Qubit) => Unit = (q, q2) => { + expect![[r#" + operation my_h(q : Qubit, q2 : Qubit) : Unit is Adj + Ctl { h(q2); h(q); - }; - "# - ] + } + "#]] .assert_eq(&qsharp); Ok(()) } @@ -60,9 +56,9 @@ fn single_angle_single_qubit() -> miette::Result<(), Vec> { let qsharp = compile_qasm_stmt_to_qsharp(source)?; expect![[r#" - let my_h : (__Angle__, Qubit) => Unit = (θ, q) => { + operation my_h(θ : __Angle__, q : Qubit) : Unit is Adj + Ctl { rx(θ, q); - }; + } "#]] .assert_eq(&qsharp); Ok(()) @@ -80,10 +76,10 @@ fn two_angles_two_qubits() -> miette::Result<(), Vec> { let qsharp = compile_qasm_stmt_to_qsharp(source)?; expect![[r#" - let my_h : (__Angle__, __Angle__, Qubit, Qubit) => Unit = (θ, φ, q, q2) => { + operation my_h(θ : __Angle__, φ : __Angle__, q : Qubit, q2 : Qubit) : Unit is Adj + Ctl { rx(θ, q2); ry(φ, q); - }; + } "#]] .assert_eq(&qsharp); Ok(()) diff --git a/compiler/qsc_qasm3/src/tests/expression/function_call.rs b/compiler/qsc_qasm3/src/tests/expression/function_call.rs index 8347947792..a679d76f82 100644 --- a/compiler/qsc_qasm3/src/tests/expression/function_call.rs +++ b/compiler/qsc_qasm3/src/tests/expression/function_call.rs @@ -17,7 +17,7 @@ fn funcall_with_no_arguments_generates_correct_qsharp() -> miette::Result<(), Ve import QasmStd.Angle.*; import QasmStd.Convert.*; import QasmStd.Intrinsic.*; - let empty : () -> Unit = () -> {}; + function empty() : Unit {} empty(); "#]] .assert_eq(&qsharp); @@ -36,7 +36,7 @@ fn void_function_with_one_argument_generates_correct_qsharp() -> miette::Result< import QasmStd.Angle.*; import QasmStd.Convert.*; import QasmStd.Intrinsic.*; - let f : (Int) -> Unit = (x) -> {}; + function f(x : Int) : Unit {} f(2); "#]] .assert_eq(&qsharp); @@ -58,9 +58,9 @@ fn funcall_with_one_argument_generates_correct_qsharp() -> miette::Result<(), Ve import QasmStd.Angle.*; import QasmStd.Convert.*; import QasmStd.Intrinsic.*; - let square : (Int) -> Int = (x) -> { + function square(x : Int) : Int { return x * x; - }; + } square(2); "#]] .assert_eq(&qsharp); @@ -82,9 +82,9 @@ fn funcall_with_two_arguments_generates_correct_qsharp() -> miette::Result<(), V import QasmStd.Angle.*; import QasmStd.Convert.*; import QasmStd.Intrinsic.*; - let sum : (Int, Int) -> Int = (x, y) -> { + function sum(x : Int, y : Int) : Int { return x + y; - }; + } sum(2, 3); "#]] .assert_eq(&qsharp); @@ -109,7 +109,7 @@ fn funcall_with_qubit_argument() -> miette::Result<(), Vec> { import QasmStd.Angle.*; import QasmStd.Convert.*; import QasmStd.Intrinsic.*; - let parity : (Qubit[]) => Result = (qs) => { + operation parity(qs : Qubit[]) : Result { mutable a = QIR.Intrinsic.__quantum__qis__m__body(qs[0]); mutable b = QIR.Intrinsic.__quantum__qis__m__body(qs[1]); return if __ResultAsInt__(a) ^^^ __ResultAsInt__(b) == 0 { @@ -117,7 +117,7 @@ fn funcall_with_qubit_argument() -> miette::Result<(), Vec> { } else { Zero }; - }; + } let qs = QIR.Runtime.AllocateQubitArray(2); mutable p = parity(qs); "#]] @@ -198,9 +198,9 @@ fn funcall_accepts_qubit_argument() -> miette::Result<(), Vec> { import QasmStd.Angle.*; import QasmStd.Convert.*; import QasmStd.Intrinsic.*; - let h_wrapper : (Qubit) => Unit = (q) => { + operation h_wrapper(q : Qubit) : Unit { h(q); - }; + } let q = QIR.Runtime.__quantum__rt__qubit_allocate(); h_wrapper(q); "#]] @@ -223,9 +223,9 @@ fn classical_decl_initialized_with_funcall() -> miette::Result<(), Vec> import QasmStd.Angle.*; import QasmStd.Convert.*; import QasmStd.Intrinsic.*; - let square : (Int) -> Int = (x) -> { + function square(x : Int) : Int { return x * x; - }; + } mutable a = square(2); "#]] .assert_eq(&qsharp); @@ -275,9 +275,9 @@ fn funcall_implicit_arg_cast_uint_to_bitarray() -> miette::Result<(), Vec Result = (arr) -> { + function parity(arr : Result[]) : Result { return 1; - }; + } mutable p = parity(__IntAsResultArrayBE__(2, 2)); "#]] .assert_eq(&qsharp); diff --git a/compiler/qsc_qasm3/src/tests/scopes.rs b/compiler/qsc_qasm3/src/tests/scopes.rs index 4f5bbd49ff..c200ad0ffd 100644 --- a/compiler/qsc_qasm3/src/tests/scopes.rs +++ b/compiler/qsc_qasm3/src/tests/scopes.rs @@ -25,11 +25,11 @@ fn can_access_const_decls_from_global_scope() -> miette::Result<(), Vec> import QasmStd.Convert.*; import QasmStd.Intrinsic.*; let i = 7; - let my_h : (Qubit) => Unit = (q) => { - if i == 0 { + operation my_h(q : Qubit) : Unit is Adj + Ctl { + if 7 == 0 { h(q); }; - }; + } let q = QIR.Runtime.__quantum__rt__qubit_allocate(); my_h(q); "#]] @@ -75,13 +75,13 @@ fn gates_can_call_previously_declared_gates() -> miette::Result<(), Vec> import QasmStd.Angle.*; import QasmStd.Convert.*; import QasmStd.Intrinsic.*; - let my_h : (Qubit) => Unit = (q) => { + operation my_h(q : Qubit) : Unit is Adj + Ctl { h(q); - }; - let my_hx : (Qubit) => Unit = (q) => { + } + operation my_hx(q : Qubit) : Unit is Adj + Ctl { my_h(q); x(q); - }; + } let q = QIR.Runtime.__quantum__rt__qubit_allocate(); my_hx(q); "#]] @@ -109,13 +109,13 @@ fn def_can_call_previously_declared_def() -> miette::Result<(), Vec> { import QasmStd.Angle.*; import QasmStd.Convert.*; import QasmStd.Intrinsic.*; - let apply_h : (Qubit) => Unit = (q) => { + operation apply_h(q : Qubit) : Unit { h(q); - }; - let apply_hx : (Qubit) => Unit = (q) => { + } + operation apply_hx(q : Qubit) : Unit { apply_h(q); x(q); - }; + } let q = QIR.Runtime.__quantum__rt__qubit_allocate(); apply_hx(q); "#]] @@ -143,13 +143,13 @@ fn gate_can_call_previously_declared_def() -> miette::Result<(), Vec> { import QasmStd.Angle.*; import QasmStd.Convert.*; import QasmStd.Intrinsic.*; - let apply_h : (Qubit) => Unit = (q) => { + operation apply_h(q : Qubit) : Unit { h(q); - }; - let my_hx : (Qubit) => Unit = (q) => { + } + operation my_hx(q : Qubit) : Unit is Adj + Ctl { apply_h(q); x(q); - }; + } let q = QIR.Runtime.__quantum__rt__qubit_allocate(); my_hx(q); "#]] @@ -177,13 +177,13 @@ fn def_can_call_previously_declared_gate() -> miette::Result<(), Vec> { import QasmStd.Angle.*; import QasmStd.Convert.*; import QasmStd.Intrinsic.*; - let my_h : (Qubit) => Unit = (q) => { + operation my_h(q : Qubit) : Unit is Adj + Ctl { h(q); - }; - let apply_hx : (Qubit) => Unit = (q) => { + } + operation apply_hx(q : Qubit) : Unit { my_h(q); x(q); - }; + } let q = QIR.Runtime.__quantum__rt__qubit_allocate(); apply_hx(q); "#]] @@ -211,13 +211,13 @@ fn def_can_call_itself_recursively() -> miette::Result<(), Vec> { import QasmStd.Angle.*; import QasmStd.Convert.*; import QasmStd.Intrinsic.*; - let apply_hx : (Int, Qubit) => Unit = (limit, q) => { + operation apply_hx(limit : Int, q : Qubit) : Unit { if limit > 0 { apply_hx(limit - 1, q); x(q); }; h(q); - }; + } let q = QIR.Runtime.__quantum__rt__qubit_allocate(); apply_hx(2, q); "#]] diff --git a/compiler/qsc_qasm3/src/tests/statement/annotation.rs b/compiler/qsc_qasm3/src/tests/statement/annotation.rs index 1ad3d5ecb7..373002d9f7 100644 --- a/compiler/qsc_qasm3/src/tests/statement/annotation.rs +++ b/compiler/qsc_qasm3/src/tests/statement/annotation.rs @@ -22,8 +22,8 @@ fn simulatable_intrinsic_can_be_applied_to_gate() -> miette::Result<(), Vec miette::Result<(), Vec> { .assert_eq(&qsharp); Ok(()) } + +#[test] +fn custom_gate_can_be_called() -> miette::Result<(), Vec> { + let source = r#" + include "stdgates.inc"; + gate my_gate q1, q2 { + h q1; + h q2; + } + + qubit[2] q; + my_gate q[0], q[1]; + "#; + + let qsharp = compile_qasm_to_qsharp(source)?; + expect![[r#" + import QasmStd.Angle.*; + import QasmStd.Convert.*; + import QasmStd.Intrinsic.*; + operation my_gate(q1 : Qubit, q2 : Qubit) : Unit is Adj + Ctl { + h(q1); + h(q2); + } + let q = QIR.Runtime.AllocateQubitArray(2); + my_gate(q[0], q[1]); + "#]] + .assert_eq(&qsharp); + Ok(()) +} + +#[test] +fn custom_gate_can_be_called_with_inv_modifier() -> miette::Result<(), Vec> { + let source = r#" + include "stdgates.inc"; + gate my_gate q1, q2 { + h q1; + h q2; + } + + qubit[2] q; + inv @ my_gate q[0], q[1]; + "#; + + let qsharp = compile_qasm_to_qsharp(source)?; + expect![[r#" + import QasmStd.Angle.*; + import QasmStd.Convert.*; + import QasmStd.Intrinsic.*; + operation my_gate(q1 : Qubit, q2 : Qubit) : Unit is Adj + Ctl { + h(q1); + h(q2); + } + let q = QIR.Runtime.AllocateQubitArray(2); + Adjoint my_gate(q[0], q[1]); + "#]] + .assert_eq(&qsharp); + Ok(()) +} + +#[test] +fn custom_gate_can_be_called_with_ctrl_modifier() -> miette::Result<(), Vec> { + let source = r#" + include "stdgates.inc"; + gate my_gate q1, q2 { + h q1; + h q2; + } + + qubit[2] ctl; + qubit[2] q; + ctrl(2) @ my_gate ctl[0], ctl[1], q[0], q[1]; + "#; + + let qsharp = compile_qasm_to_qsharp(source)?; + expect![[r#" + import QasmStd.Angle.*; + import QasmStd.Convert.*; + import QasmStd.Intrinsic.*; + operation my_gate(q1 : Qubit, q2 : Qubit) : Unit is Adj + Ctl { + h(q1); + h(q2); + } + let ctl = QIR.Runtime.AllocateQubitArray(2); + let q = QIR.Runtime.AllocateQubitArray(2); + Controlled my_gate([ctl[0], ctl[1]], (q[0], q[1])); + "#]] + .assert_eq(&qsharp); + Ok(()) +} + +#[test] +fn custom_gate_can_be_called_with_negctrl_modifier() -> miette::Result<(), Vec> { + let source = r#" + include "stdgates.inc"; + gate my_gate q1, q2 { + h q1; + h q2; + } + + qubit[2] ctl; + qubit[2] q; + negctrl(2) @ my_gate ctl[0], ctl[1], q[0], q[1]; + "#; + + let qsharp = compile_qasm_to_qsharp(source)?; + expect![[r#" + import QasmStd.Angle.*; + import QasmStd.Convert.*; + import QasmStd.Intrinsic.*; + operation my_gate(q1 : Qubit, q2 : Qubit) : Unit is Adj + Ctl { + h(q1); + h(q2); + } + let ctl = QIR.Runtime.AllocateQubitArray(2); + let q = QIR.Runtime.AllocateQubitArray(2); + ApplyControlledOnInt(0, my_gate, [ctl[0], ctl[1]], (q[0], q[1])); + "#]] + .assert_eq(&qsharp); + Ok(()) +} + +#[test] +fn custom_gate_can_be_called_with_pow_modifier() -> miette::Result<(), Vec> { + let source = r#" + include "stdgates.inc"; + gate my_gate q1, q2 { + h q1; + h q2; + } + + qubit[2] q; + pow(2) @ my_gate q[0], q[1]; + "#; + + let qsharp = compile_qasm_to_qsharp(source)?; + expect![[r#" + import QasmStd.Angle.*; + import QasmStd.Convert.*; + import QasmStd.Intrinsic.*; + operation my_gate(q1 : Qubit, q2 : Qubit) : Unit is Adj + Ctl { + h(q1); + h(q2); + } + let q = QIR.Runtime.AllocateQubitArray(2); + __Pow__(2, my_gate, (q[0], q[1])); + "#]] + .assert_eq(&qsharp); + Ok(()) +} diff --git a/compiler/qsc_qasm3/src/tests/statement/include.rs b/compiler/qsc_qasm3/src/tests/statement/include.rs index 20f1abb5db..383082d39b 100644 --- a/compiler/qsc_qasm3/src/tests/statement/include.rs +++ b/compiler/qsc_qasm3/src/tests/statement/include.rs @@ -45,9 +45,9 @@ fn programs_with_includes_can_be_parsed() -> miette::Result<(), Vec> { import QasmStd.Intrinsic.*; @EntryPoint() operation Test() : Result[] { - let my_gate : (Qubit) => Unit = (q) => { + operation my_gate(q : Qubit) : Unit is Adj + Ctl { x(q); - }; + } mutable c = [Zero]; let q = QIR.Runtime.AllocateQubitArray(1); my_gate(q[0]); diff --git a/compiler/qsc_qasm3/src/tests/statement/switch.rs b/compiler/qsc_qasm3/src/tests/statement/switch.rs index 1c8674c2d8..cafd8d2627 100644 --- a/compiler/qsc_qasm3/src/tests/statement/switch.rs +++ b/compiler/qsc_qasm3/src/tests/statement/switch.rs @@ -255,9 +255,9 @@ fn spec_case_4() -> miette::Result<(), Vec> { import QasmStd.Intrinsic.*; let q = QIR.Runtime.__quantum__rt__qubit_allocate(); mutable b = [Zero, Zero]; - let foo : (Int, Qubit[]) => Result = (i, d) => { + operation foo(i : Int, d : Qubit[]) : Result { return QIR.Intrinsic.__quantum__qis__m__body(d[i]); - }; + } mutable i = 15; mutable j = 1; mutable k = 2; From d24e88bc4b613164418a6a890c22079f76b550bb Mon Sep 17 00:00:00 2001 From: orpuente-MS <156957451+orpuente-MS@users.noreply.github.com> Date: Tue, 1 Apr 2025 12:13:14 -0700 Subject: [PATCH 075/108] Compile annotations into attributes (#2271) --- compiler/qsc_qasm3/src/ast_builder.rs | 75 ++++++++++++----- compiler/qsc_qasm3/src/compile.rs | 2 +- compiler/qsc_qasm3/src/compiler.rs | 82 +++++++++++++++---- compiler/qsc_qasm3/src/parser/error.rs | 2 +- compiler/qsc_qasm3/src/parser/stmt.rs | 15 ++-- compiler/qsc_qasm3/src/semantic/error.rs | 2 +- compiler/qsc_qasm3/src/semantic/lowerer.rs | 43 ++-------- .../src/tests/statement/annotation.rs | 78 +++++++++++++++++- compiler/qsc_qasm3/src/tests/statement/end.rs | 19 +++-- .../qsc_qasm3/src/tests/statement/include.rs | 1 + 10 files changed, 226 insertions(+), 93 deletions(-) diff --git a/compiler/qsc_qasm3/src/ast_builder.rs b/compiler/qsc_qasm3/src/ast_builder.rs index ce7eed4c2d..e0cb35e55f 100644 --- a/compiler/qsc_qasm3/src/ast_builder.rs +++ b/compiler/qsc_qasm3/src/ast_builder.rs @@ -14,7 +14,7 @@ use qsc_ast::ast::{ use qsc_data_structures::span::Span; use crate::{ - parser::ast::list_from_iter, + parser::ast::{list_from_iter, List}, runtime::RuntimeFunctions, stdlib::angle::Angle, types::{ArrayDimensions, Complex}, @@ -1459,15 +1459,14 @@ pub(crate) fn build_end_stmt(span: Span) -> Stmt { }; let kind = ExprKind::Fail(Box::new(message)); - Stmt { - kind: Box::new(StmtKind::Expr(Box::new(Expr { - kind: Box::new(kind), - span, - ..Default::default() - }))), + + let expr = Expr { + kind: Box::new(kind), span, ..Default::default() - } + }; + + build_stmt_semi_from_expr_with_span(expr, span) } pub(crate) fn build_index_expr(expr: Expr, index_expr: Expr, span: Span) -> Expr { @@ -1484,19 +1483,6 @@ pub(crate) fn build_barrier_call(span: Span) -> Stmt { build_stmt_semi_from_expr(expr) } -pub(crate) fn build_attr(text: String, span: Span) -> Attr { - Attr { - id: NodeId::default(), - span, - name: Box::new(Ident { - name: Rc::from(text), - span, - ..Default::default() - }), - arg: Box::new(create_unit_expr(span)), - } -} - pub(crate) fn build_gate_decl( name: String, cargs: Vec<(String, Ty, Pat)>, @@ -1703,6 +1689,7 @@ pub(crate) fn build_function_or_operation( return_type: Option, kind: CallableKind, functors: Option, + attrs: List, ) -> Stmt { let args = cargs .into_iter() @@ -1763,6 +1750,7 @@ pub(crate) fn build_function_or_operation( let item = Item { span: gate_span, kind: Box::new(ast::ItemKind::Callable(Box::new(decl))), + attrs, ..Default::default() }; @@ -1807,3 +1795,48 @@ fn build_idents(idents: &[&str]) -> Option> { Some(idents.into()) } } + +pub(crate) fn build_attr(name: S, value: Option, span: Span) -> Attr +where + S: AsRef, +{ + let name = Box::new(Ident { + span, + name: name.as_ref().into(), + ..Default::default() + }); + + let arg = if let Some(value) = value { + Box::new(Expr { + span, + kind: Box::new(ExprKind::Paren(Box::new(Expr { + span, + kind: Box::new(ExprKind::Path(PathKind::Ok(Box::new(Path { + id: Default::default(), + span, + segments: None, + name: Box::new(Ident { + span, + name: value.as_ref().into(), + ..Default::default() + }), + })))), + id: Default::default(), + }))), + id: Default::default(), + }) + } else { + Box::new(Expr { + span, + kind: Box::new(ExprKind::Tuple(Box::default())), + id: Default::default(), + }) + }; + + Attr { + span, + name, + arg, + id: Default::default(), + } +} diff --git a/compiler/qsc_qasm3/src/compile.rs b/compiler/qsc_qasm3/src/compile.rs index 4610a4a8e1..bcfecbaefb 100644 --- a/compiler/qsc_qasm3/src/compile.rs +++ b/compiler/qsc_qasm3/src/compile.rs @@ -415,7 +415,7 @@ impl QasmCompiler { let span = span_for_syntax_node(stmt.syntax()); if let "@SimulatableIntrinsic" = text.as_str() { let (_at, name) = text.split_at(1); - Some(build_attr(name.to_string(), span)) + Some(build_attr(name.to_string(), None, span)) } else { let span = span_for_syntax_node(stmt.syntax()); let kind = SemanticErrorKind::UnknownAnnotation(text.to_string(), span); diff --git a/compiler/qsc_qasm3/src/compiler.rs b/compiler/qsc_qasm3/src/compiler.rs index 7b99e2b040..eb709e4361 100644 --- a/compiler/qsc_qasm3/src/compiler.rs +++ b/compiler/qsc_qasm3/src/compiler.rs @@ -11,17 +11,17 @@ use qsc_frontend::{compile::SourceMap, error::WithSource}; use crate::{ ast_builder::{ build_adj_plus_ctl_functor, build_arg_pat, build_array_reverse_expr, - build_assignment_statement, build_barrier_call, build_binary_expr, build_call_no_params, - build_call_with_param, build_call_with_params, build_cast_call_by_name, - build_classical_decl, build_complex_from_expr, build_convert_call_expr, - build_expr_array_expr, build_for_stmt, build_function_or_operation, - build_gate_call_param_expr, build_gate_call_with_params_and_callee, - build_global_call_with_two_params, build_if_expr_then_block, - build_if_expr_then_block_else_block, build_if_expr_then_block_else_expr, - build_if_expr_then_expr_else_expr, build_implicit_return_stmt, - build_indexed_assignment_statement, build_lit_angle_expr, build_lit_bigint_expr, - build_lit_bool_expr, build_lit_complex_expr, build_lit_double_expr, build_lit_int_expr, - build_lit_result_array_expr_from_bitstring, build_lit_result_expr, + build_assignment_statement, build_attr, build_barrier_call, build_binary_expr, + build_call_no_params, build_call_with_param, build_call_with_params, + build_cast_call_by_name, build_classical_decl, build_complex_from_expr, + build_convert_call_expr, build_end_stmt, build_expr_array_expr, build_for_stmt, + build_function_or_operation, build_gate_call_param_expr, + build_gate_call_with_params_and_callee, build_global_call_with_two_params, + build_if_expr_then_block, build_if_expr_then_block_else_block, + build_if_expr_then_block_else_expr, build_if_expr_then_expr_else_expr, + build_implicit_return_stmt, build_indexed_assignment_statement, build_lit_angle_expr, + build_lit_bigint_expr, build_lit_bool_expr, build_lit_complex_expr, build_lit_double_expr, + build_lit_int_expr, build_lit_result_array_expr_from_bitstring, build_lit_result_expr, build_managed_qubit_alloc, build_math_call_from_exprs, build_math_call_no_params, build_measure_call, build_operation_with_stmts, build_path_ident_expr, build_qasm_import_decl, build_qasm_import_items, build_range_expr, build_reset_call, @@ -379,6 +379,19 @@ impl QasmCompiler { } fn compile_stmt(&mut self, stmt: &crate::semantic::ast::Stmt) -> Option { + if !stmt.annotations.is_empty() + && !matches!( + stmt.kind.as_ref(), + semast::StmtKind::QuantumGateDefinition(..) | semast::StmtKind::Def(..) + ) + { + for annotation in &stmt.annotations { + self.push_semantic_error(SemanticErrorKind::InvalidAnnotationTarget( + annotation.span, + )); + } + } + match stmt.kind.as_ref() { semast::StmtKind::Alias(stmt) => self.compile_alias_decl_stmt(stmt), semast::StmtKind::Assign(stmt) => self.compile_assign_stmt(stmt), @@ -391,10 +404,10 @@ impl QasmCompiler { self.compile_calibration_grammar_stmt(stmt) } semast::StmtKind::ClassicalDecl(stmt) => self.compile_classical_decl(stmt), - semast::StmtKind::Def(stmt) => self.compile_def_stmt(stmt), + semast::StmtKind::Def(def_stmt) => self.compile_def_stmt(def_stmt, &stmt.annotations), semast::StmtKind::DefCal(stmt) => self.compile_def_cal_stmt(stmt), semast::StmtKind::Delay(stmt) => self.compile_delay_stmt(stmt), - semast::StmtKind::End(stmt) => self.compile_end_stmt(stmt), + semast::StmtKind::End(stmt) => Self::compile_end_stmt(stmt), semast::StmtKind::ExprStmt(stmt) => self.compile_expr_stmt(stmt), semast::StmtKind::ExternDecl(stmt) => self.compile_extern_stmt(stmt), semast::StmtKind::For(stmt) => self.compile_for_stmt(stmt), @@ -405,7 +418,9 @@ impl QasmCompiler { semast::StmtKind::OutputDeclaration(stmt) => self.compile_output_decl_stmt(stmt), semast::StmtKind::MeasureArrow(stmt) => self.compile_measure_stmt(stmt), semast::StmtKind::Pragma(stmt) => self.compile_pragma_stmt(stmt), - semast::StmtKind::QuantumGateDefinition(stmt) => self.compile_gate_decl_stmt(stmt), + semast::StmtKind::QuantumGateDefinition(gate_stmt) => { + self.compile_gate_decl_stmt(gate_stmt, &stmt.annotations) + } semast::StmtKind::QubitDecl(stmt) => self.compile_qubit_decl_stmt(stmt), semast::StmtKind::QubitArrayDecl(stmt) => self.compile_qubit_array_decl_stmt(stmt), semast::StmtKind::Reset(stmt) => self.compile_reset_stmt(stmt), @@ -600,7 +615,11 @@ impl QasmCompiler { Some(stmt) } - fn compile_def_stmt(&mut self, stmt: &semast::DefStmt) -> Option { + fn compile_def_stmt( + &mut self, + stmt: &semast::DefStmt, + annotations: &List, + ) -> Option { let symbol = self.symbols[stmt.symbol_id].clone(); let name = symbol.name.clone(); @@ -627,6 +646,10 @@ impl QasmCompiler { qsast::CallableKind::Function }; + let attrs = annotations + .iter() + .filter_map(|annotation| self.compile_annotation(annotation)); + // We use the same primitives used for declaring gates, because def declarations // in QASM3 can take qubits as arguments and call quantum gates. Some(build_function_or_operation( @@ -640,6 +663,7 @@ impl QasmCompiler { return_type, kind, None, + list_from_iter(attrs), )) } @@ -653,9 +677,8 @@ impl QasmCompiler { None } - fn compile_end_stmt(&mut self, stmt: &semast::EndStmt) -> Option { - self.push_unimplemented_error_message("end statements", stmt.span); - None + fn compile_end_stmt(stmt: &semast::EndStmt) -> Option { + Some(build_end_stmt(stmt.span)) } fn compile_expr_stmt(&mut self, stmt: &semast::ExprStmt) -> Option { @@ -866,6 +889,7 @@ impl QasmCompiler { fn compile_gate_decl_stmt( &mut self, stmt: &semast::QuantumGateDefinition, + annotations: &List, ) -> Option { let symbol = self.symbols[stmt.symbol_id].clone(); let name = symbol.name.clone(); @@ -902,6 +926,10 @@ impl QasmCompiler { let body = Some(self.compile_block(&stmt.body)); + let attrs = annotations + .iter() + .filter_map(|annotation| self.compile_annotation(annotation)); + Some(build_function_or_operation( name, cargs, @@ -913,9 +941,27 @@ impl QasmCompiler { None, qsast::CallableKind::Operation, Some(build_adj_plus_ctl_functor()), + list_from_iter(attrs), )) } + fn compile_annotation(&mut self, annotation: &semast::Annotation) -> Option { + match annotation.identifier.as_ref() { + "SimulatableIntrinsic" | "Config" => Some(build_attr( + &annotation.identifier, + annotation.value.as_ref(), + annotation.span, + )), + _ => { + self.push_semantic_error(SemanticErrorKind::UnknownAnnotation( + format!("@{}", annotation.identifier), + annotation.span, + )); + None + } + } + } + fn compile_qubit_decl_stmt(&mut self, stmt: &semast::QubitDeclaration) -> Option { let symbol = self.symbols[stmt.symbol_id].clone(); let name = &symbol.name; diff --git a/compiler/qsc_qasm3/src/parser/error.rs b/compiler/qsc_qasm3/src/parser/error.rs index 30c4f81d55..99042af191 100644 --- a/compiler/qsc_qasm3/src/parser/error.rs +++ b/compiler/qsc_qasm3/src/parser/error.rs @@ -98,7 +98,7 @@ pub enum ErrorKind { #[error("Empty statements are not supported")] #[diagnostic(code("Qasm3.Parse.EmptyStatement"))] EmptyStatement(#[label] Span), - #[error("expected statement after annotation")] + #[error("Annotation missing target statement.")] #[diagnostic(code("Qasm3.Parse.FloatingAnnotation"))] FloatingAnnotation(#[label] Span), #[error("expected {0}, found {1}")] diff --git a/compiler/qsc_qasm3/src/parser/stmt.rs b/compiler/qsc_qasm3/src/parser/stmt.rs index a172af1be2..b909ef427a 100644 --- a/compiler/qsc_qasm3/src/parser/stmt.rs +++ b/compiler/qsc_qasm3/src/parser/stmt.rs @@ -156,11 +156,16 @@ pub(super) fn parse(s: &mut ParserContext) -> Result { } else if let Some(stmt) = opt(s, parse_measure_stmt)? { StmtKind::Measure(stmt) } else { - return Err(Error::new(ErrorKind::Rule( - "statement", - s.peek().kind, - s.peek().span, - ))); + return if attrs.is_empty() { + Err(Error::new(ErrorKind::Rule( + "statement", + s.peek().kind, + s.peek().span, + ))) + } else { + let span = attrs.last().expect("there is at least one annotation").span; + Err(Error::new(ErrorKind::FloatingAnnotation(span))) + }; }; Ok(Stmt { diff --git a/compiler/qsc_qasm3/src/semantic/error.rs b/compiler/qsc_qasm3/src/semantic/error.rs index 404ae890b3..b9ed712721 100644 --- a/compiler/qsc_qasm3/src/semantic/error.rs +++ b/compiler/qsc_qasm3/src/semantic/error.rs @@ -120,7 +120,7 @@ pub enum SemanticErrorKind { #[error("Indexed must be a single expression.")] #[diagnostic(code("Qsc.Qasm3.Compile.IndexMustBeSingleExpr"))] IndexMustBeSingleExpr(#[label] Span), - #[error("Annotations only valid on gate definitions.")] + #[error("Annotations only valid on def and gate statements.")] #[diagnostic(code("Qsc.Qasm3.Compile.InvalidAnnotationTarget"))] InvalidAnnotationTarget(#[label] Span), #[error("Assigning {0} values to {1} must be in a range that be converted to {1}.")] diff --git a/compiler/qsc_qasm3/src/semantic/lowerer.rs b/compiler/qsc_qasm3/src/semantic/lowerer.rs index c34b73dcb5..d476177763 100644 --- a/compiler/qsc_qasm3/src/semantic/lowerer.rs +++ b/compiler/qsc_qasm3/src/semantic/lowerer.rs @@ -198,7 +198,7 @@ impl Lowerer { syntax::StmtKind::Def(stmt) => self.lower_def(stmt), syntax::StmtKind::DefCal(stmt) => self.lower_def_cal(stmt), syntax::StmtKind::Delay(stmt) => self.lower_delay(stmt), - syntax::StmtKind::End(stmt) => self.lower_end_stmt(stmt), + syntax::StmtKind::End(stmt) => Self::lower_end_stmt(stmt), syntax::StmtKind::ExprStmt(stmt) => self.lower_expr_stmt(stmt), syntax::StmtKind::ExternDecl(extern_decl) => self.lower_extern(extern_decl), syntax::StmtKind::For(stmt) => self.lower_for_stmt(stmt), @@ -217,7 +217,7 @@ impl Lowerer { syntax::StmtKind::WhileLoop(stmt) => self.lower_while_stmt(stmt), syntax::StmtKind::Err => semantic::StmtKind::Err, }; - let annotations = self.lower_annotations(&stmt.annotations, &stmt.kind); + let annotations = Self::lower_annotations(&stmt.annotations); semantic::Stmt { span: stmt.span, annotations: syntax::list_from_iter(annotations), @@ -720,42 +720,14 @@ impl Lowerer { } } - fn lower_annotations( - &mut self, - annotations: &[Box], - kind: &syntax::StmtKind, - ) -> Vec { + fn lower_annotations(annotations: &[Box]) -> Vec { annotations .iter() - .map(|annotation| self.lower_annotation(annotation, kind)) + .map(|annotation| Self::lower_annotation(annotation)) .collect::>() } - fn lower_annotation( - &mut self, - annotation: &syntax::Annotation, - kind: &syntax::StmtKind, - ) -> semantic::Annotation { - if !matches!( - annotation.identifier.to_string().as_str(), - "SimulatableIntrinsic" | "Config" - ) { - self.push_unsupported_error_message( - format!("Annotation {}.", annotation.identifier), - annotation.span, - ); - } - - if let syntax::StmtKind::GateCall(_) = &kind { - self.push_unsupported_error_message( - format!( - "Annotation {} is only allowed on gate definitions.", - annotation.identifier - ), - annotation.span, - ); - } - + fn lower_annotation(annotation: &syntax::Annotation) -> semantic::Annotation { semantic::Annotation { span: annotation.span, identifier: annotation.identifier.clone(), @@ -1159,9 +1131,8 @@ impl Lowerer { semantic::StmtKind::Err } - fn lower_end_stmt(&mut self, stmt: &syntax::EndStmt) -> semantic::StmtKind { - self.push_unimplemented_error_message("end stmt", stmt.span); - semantic::StmtKind::Err + fn lower_end_stmt(stmt: &syntax::EndStmt) -> semantic::StmtKind { + semantic::StmtKind::End(semantic::EndStmt { span: stmt.span }) } fn lower_expr_stmt(&mut self, stmt: &syntax::ExprStmt) -> semantic::StmtKind { diff --git a/compiler/qsc_qasm3/src/tests/statement/annotation.rs b/compiler/qsc_qasm3/src/tests/statement/annotation.rs index 373002d9f7..bbedda3b53 100644 --- a/compiler/qsc_qasm3/src/tests/statement/annotation.rs +++ b/compiler/qsc_qasm3/src/tests/statement/annotation.rs @@ -16,8 +16,7 @@ fn simulatable_intrinsic_can_be_applied_to_gate() -> miette::Result<(), Vec miette::Result<(), Vec miette::Result<(), Vec> { + let source = r#" + include "stdgates.inc"; + @SimulatableIntrinsic + def my_h(qubit q) { + h q; + } + "#; + + let qsharp = compile_qasm_to_qsharp(source)?; + expect![[r#" + import QasmStd.Angle.*; + import QasmStd.Convert.*; + import QasmStd.Intrinsic.*; + @SimulatableIntrinsic() + operation my_h(q : Qubit) : Unit { + h(q); + } + "#]] + .assert_eq(&qsharp); + Ok(()) +} + +#[test] +fn config_can_be_applied_to_gate() -> miette::Result<(), Vec> { + let source = r#" + include "stdgates.inc"; + @Config Base + gate my_h q { + h q; + } + "#; + + let qsharp = compile_qasm_to_qsharp(source)?; + expect![[r#" + import QasmStd.Angle.*; + import QasmStd.Convert.*; + import QasmStd.Intrinsic.*; + @Config(Base) + operation my_h(q : Qubit) : Unit is Adj + Ctl { + h(q); + } + "#]] + .assert_eq(&qsharp); + Ok(()) +} + +#[test] +fn config_can_be_applied_to_def() -> miette::Result<(), Vec> { + let source = r#" + include "stdgates.inc"; + @Config Base + def my_h(qubit q) { + h q; + } + "#; + + let qsharp = compile_qasm_to_qsharp(source)?; + expect![[r#" + import QasmStd.Angle.*; + import QasmStd.Convert.*; + import QasmStd.Intrinsic.*; + @Config(Base) + operation my_h(q : Qubit) : Unit { + h(q); + } + "#]] .assert_eq(&qsharp); Ok(()) } diff --git a/compiler/qsc_qasm3/src/tests/statement/end.rs b/compiler/qsc_qasm3/src/tests/statement/end.rs index 411f9fe7ea..61a8a58703 100644 --- a/compiler/qsc_qasm3/src/tests/statement/end.rs +++ b/compiler/qsc_qasm3/src/tests/statement/end.rs @@ -15,14 +15,15 @@ fn end_can_be_in_nested_scope() -> miette::Result<(), Vec> { "#; let qsharp = compile_qasm_to_qsharp(source)?; - expect![ - r#" + expect![[r#" + import QasmStd.Angle.*; + import QasmStd.Convert.*; + import QasmStd.Intrinsic.*; mutable sum = 0; for i : Int in [1, 5, 10] { - fail "end" + fail "end"; } - "# - ] + "#]] .assert_eq(&qsharp); Ok(()) } @@ -34,6 +35,12 @@ fn end_can_be_in_global_scope() -> miette::Result<(), Vec> { "#; let qsharp = compile_qasm_to_qsharp(source)?; - expect![r#"fail "end""#].assert_eq(&qsharp); + expect![[r#" + import QasmStd.Angle.*; + import QasmStd.Convert.*; + import QasmStd.Intrinsic.*; + fail "end"; + "#]] + .assert_eq(&qsharp); Ok(()) } diff --git a/compiler/qsc_qasm3/src/tests/statement/include.rs b/compiler/qsc_qasm3/src/tests/statement/include.rs index 383082d39b..5a7da1a6e8 100644 --- a/compiler/qsc_qasm3/src/tests/statement/include.rs +++ b/compiler/qsc_qasm3/src/tests/statement/include.rs @@ -45,6 +45,7 @@ fn programs_with_includes_can_be_parsed() -> miette::Result<(), Vec> { import QasmStd.Intrinsic.*; @EntryPoint() operation Test() : Result[] { + @SimulatableIntrinsic() operation my_gate(q : Qubit) : Unit is Adj + Ctl { x(q); } From 9b14f31b8f2a92466f6398c5c0bf67165948ae3e Mon Sep 17 00:00:00 2001 From: Oscar Puente Date: Tue, 1 Apr 2025 13:07:28 -0700 Subject: [PATCH 076/108] unignore tests --- compiler/qsc_qasm3/src/ast_builder.rs | 3 + .../src/semantic/tests/decls/bool.rs | 20 +- .../src/semantic/tests/decls/complex.rs | 171 +++++---- .../tests/expression/binary/comparison.rs | 358 +++++++++++++++++- .../tests/expression/binary/complex.rs | 115 +++++- .../expression/implicit_cast_from_bitarray.rs | 293 +++++++++++++- .../expression/implicit_cast_from_float.rs | 122 +++--- .../qsc_qasm3/src/tests/declaration/float.rs | 3 - .../src/tests/declaration/integer.rs | 6 +- .../src/tests/declaration/unsigned_integer.rs | 6 +- .../qsc_qasm3/src/tests/expression/unary.rs | 24 +- .../src/tests/statement/const_eval.rs | 2 +- .../qsc_qasm3/src/tests/statement/if_stmt.rs | 28 +- 13 files changed, 949 insertions(+), 202 deletions(-) diff --git a/compiler/qsc_qasm3/src/ast_builder.rs b/compiler/qsc_qasm3/src/ast_builder.rs index e0cb35e55f..a4b23ac8a6 100644 --- a/compiler/qsc_qasm3/src/ast_builder.rs +++ b/compiler/qsc_qasm3/src/ast_builder.rs @@ -738,6 +738,7 @@ pub(crate) fn build_global_call_with_one_param>( name_span: Span, operand_span: Span, ) -> ast::Expr { + let expr_span = expr.span; let ident = ast::Ident { id: NodeId::default(), span: name_span, @@ -764,6 +765,7 @@ pub(crate) fn build_global_call_with_one_param>( let call_kind = ast::ExprKind::Call(Box::new(callee_expr), Box::new(param_expr)); ast::Expr { kind: Box::new(call_kind), + span: expr_span, ..Default::default() } } @@ -801,6 +803,7 @@ pub(crate) fn build_global_call_with_two_params>( let call_kind = ast::ExprKind::Call(Box::new(callee_expr), Box::new(param_expr)); ast::Expr { kind: Box::new(call_kind), + span: name_span, ..Default::default() } } diff --git a/compiler/qsc_qasm3/src/semantic/tests/decls/bool.rs b/compiler/qsc_qasm3/src/semantic/tests/decls/bool.rs index 895c0a6942..f7792018c6 100644 --- a/compiler/qsc_qasm3/src/semantic/tests/decls/bool.rs +++ b/compiler/qsc_qasm3/src/semantic/tests/decls/bool.rs @@ -28,19 +28,27 @@ fn with_no_init_expr_has_generated_lit_expr() { #[ignore = "Unimplemented"] fn array_with_no_init_expr_has_generated_lit_expr() { check_classical_decl( - "bool[4] a;", + "array[bool, 4] a;", &expect![[r#" Program: version: - statements: + statements: + Stmt [0-17]: + annotations: + kind: ClassicalDeclarationStmt [0-17]: + symbol_id: 8 + ty_span: [0-14] + init_expr: Expr [0-0]: + ty: Err + kind: Err [Qsc.Qasm3.Compile.Unimplemented - x this statement is not yet handled during OpenQASM 3 import: bool array - | default value + x this statement is not yet handled during OpenQASM 3 import: semantic type + | from array type ,-[test:1:1] - 1 | bool[4] a; - : ^^^^^^^^^ + 1 | array[bool, 4] a; + : ^^^^^^^^^^^^^^ `---- ]"#]], ); diff --git a/compiler/qsc_qasm3/src/semantic/tests/decls/complex.rs b/compiler/qsc_qasm3/src/semantic/tests/decls/complex.rs index 6435cfa507..6cf6676fdb 100644 --- a/compiler/qsc_qasm3/src/semantic/tests/decls/complex.rs +++ b/compiler/qsc_qasm3/src/semantic/tests/decls/complex.rs @@ -234,106 +234,147 @@ fn implicit_bitness_int_real_only() { } #[test] -#[ignore = "Requires support for binary operators"] fn implicit_bitness_simple_double_pos_im() { check_classical_decl( "complex[float] x = 1.1 + 2.2im;", &expect![[r#" - Program: - version: - statements: - - [Qsc.Qasm3.Compile.Unimplemented - - x this statement is not yet handled during OpenQASM 3 import: binary op expr - ,-[test:1:20] - 1 | complex[float] x = 1.1 + 2.2im; - : ^^^^^^^^^^^ - `---- - ]"#]], + ClassicalDeclarationStmt [0-31]: + symbol_id: 8 + ty_span: [0-14] + init_expr: Expr [19-30]: + ty: Complex(None, false) + kind: BinaryOpExpr: + op: Add + lhs: Expr [19-22]: + ty: Complex(None, true) + kind: Lit: Complex(1.1, 0.0) + rhs: Expr [25-30]: + ty: Complex(None, true) + kind: Lit: Complex(0.0, 2.2) + [8] Symbol [15-16]: + name: x + type: Complex(None, false) + qsharp_type: Complex + io_kind: Default"#]], ); } #[test] -#[ignore = "Requires support for binary operators"] fn implicit_bitness_simple_double_neg_im() { check_classical_decl( "complex[float] x = 1.1 - 2.2im;", &expect![[r#" - Program: - version: - statements: - - [Qsc.Qasm3.Compile.Unimplemented - - x this statement is not yet handled during OpenQASM 3 import: binary op expr - ,-[test:1:20] - 1 | complex[float] x = 1.1 - 2.2im; - : ^^^^^^^^^^^ - `---- - ]"#]], + ClassicalDeclarationStmt [0-31]: + symbol_id: 8 + ty_span: [0-14] + init_expr: Expr [19-30]: + ty: Complex(None, false) + kind: BinaryOpExpr: + op: Sub + lhs: Expr [19-22]: + ty: Complex(None, true) + kind: Lit: Complex(1.1, 0.0) + rhs: Expr [25-30]: + ty: Complex(None, true) + kind: Lit: Complex(0.0, 2.2) + [8] Symbol [15-16]: + name: x + type: Complex(None, false) + qsharp_type: Complex + io_kind: Default"#]], ); } #[test] -#[ignore = "Requires support for binary operators"] fn const_implicit_bitness_simple_double_neg_im() { check_classical_decl( "const complex[float] x = 1.1 - 2.2im;", &expect![[r#" - Program: - version: - statements: - - [Qsc.Qasm3.Compile.Unimplemented - - x this statement is not yet handled during OpenQASM 3 import: binary op expr - ,-[test:1:26] - 1 | const complex[float] x = 1.1 - 2.2im; - : ^^^^^^^^^^^ - `---- - ]"#]], + ClassicalDeclarationStmt [0-37]: + symbol_id: 8 + ty_span: [6-20] + init_expr: Expr [25-36]: + ty: Complex(None, true) + kind: BinaryOpExpr: + op: Sub + lhs: Expr [25-28]: + ty: Complex(None, true) + kind: Lit: Complex(1.1, 0.0) + rhs: Expr [31-36]: + ty: Complex(None, true) + kind: Lit: Complex(0.0, 2.2) + [8] Symbol [21-22]: + name: x + type: Complex(None, true) + qsharp_type: Complex + io_kind: Default"#]], ); } #[test] -#[ignore = "Requires support for binary operators"] fn implicit_bitness_simple_double_neg_real() { check_classical_decl( "complex[float] x = -1.1 + 2.2im;", &expect![[r#" - Program: - version: - statements: - - [Qsc.Qasm3.Compile.Unimplemented - - x this statement is not yet handled during OpenQASM 3 import: binary op expr - ,-[test:1:20] - 1 | complex[float] x = -1.1 + 2.2im; - : ^^^^^^^^^^^^ - `---- - ]"#]], + ClassicalDeclarationStmt [0-32]: + symbol_id: 8 + ty_span: [0-14] + init_expr: Expr [19-31]: + ty: Complex(None, false) + kind: BinaryOpExpr: + op: Add + lhs: Expr [20-23]: + ty: Complex(None, true) + kind: Cast [0-0]: + ty: Complex(None, true) + expr: Expr [20-23]: + ty: Float(None, true) + kind: UnaryOpExpr [20-23]: + op: Neg + expr: Expr [20-23]: + ty: Float(None, true) + kind: Lit: Float(1.1) + rhs: Expr [26-31]: + ty: Complex(None, true) + kind: Lit: Complex(0.0, 2.2) + [8] Symbol [15-16]: + name: x + type: Complex(None, false) + qsharp_type: Complex + io_kind: Default"#]], ); } #[test] -#[ignore = "Requires support for binary operators"] fn const_implicit_bitness_simple_double_neg_real() { check_classical_decl( "const complex[float] x = -1.1 + 2.2im;", &expect![[r#" - Program: - version: - statements: - - [Qsc.Qasm3.Compile.Unimplemented - - x this statement is not yet handled during OpenQASM 3 import: binary op expr - ,-[test:1:26] - 1 | const complex[float] x = -1.1 + 2.2im; - : ^^^^^^^^^^^^ - `---- - ]"#]], + ClassicalDeclarationStmt [0-38]: + symbol_id: 8 + ty_span: [6-20] + init_expr: Expr [25-37]: + ty: Complex(None, true) + kind: BinaryOpExpr: + op: Add + lhs: Expr [26-29]: + ty: Complex(None, true) + kind: Cast [0-0]: + ty: Complex(None, true) + expr: Expr [26-29]: + ty: Float(None, true) + kind: UnaryOpExpr [26-29]: + op: Neg + expr: Expr [26-29]: + ty: Float(None, true) + kind: Lit: Float(1.1) + rhs: Expr [32-37]: + ty: Complex(None, true) + kind: Lit: Complex(0.0, 2.2) + [8] Symbol [21-22]: + name: x + type: Complex(None, true) + qsharp_type: Complex + io_kind: Default"#]], ); } diff --git a/compiler/qsc_qasm3/src/semantic/tests/expression/binary/comparison.rs b/compiler/qsc_qasm3/src/semantic/tests/expression/binary/comparison.rs index 1743c4c3ec..36c2847de0 100644 --- a/compiler/qsc_qasm3/src/semantic/tests/expression/binary/comparison.rs +++ b/compiler/qsc_qasm3/src/semantic/tests/expression/binary/comparison.rs @@ -13,7 +13,6 @@ use expect_test::expect; use crate::semantic::tests::check_stmt_kinds; #[test] -#[ignore = "not yet implemented"] fn bitarray_var_comparisons_can_be_translated() { let input = r#" bit[1] x = "1"; @@ -26,11 +25,149 @@ fn bitarray_var_comparisons_can_be_translated() { bool d = x != y; "#; - check_stmt_kinds(input, &expect![[r#""#]]); + check_stmt_kinds(input, &expect![[r#" + ClassicalDeclarationStmt [9-24]: + symbol_id: 8 + ty_span: [9-15] + init_expr: Expr [20-23]: + ty: BitArray(One(1), false) + kind: Lit: Bitstring("1") + ClassicalDeclarationStmt [33-48]: + symbol_id: 9 + ty_span: [33-39] + init_expr: Expr [44-47]: + ty: BitArray(One(1), false) + kind: Lit: Bitstring("0") + ClassicalDeclarationStmt [57-72]: + symbol_id: 10 + ty_span: [57-61] + init_expr: Expr [66-71]: + ty: Bool(false) + kind: BinaryOpExpr: + op: Gt + lhs: Expr [66-67]: + ty: Int(None, false) + kind: Cast [0-0]: + ty: Int(None, false) + expr: Expr [66-67]: + ty: BitArray(One(1), false) + kind: SymbolId(8) + rhs: Expr [70-71]: + ty: Int(None, false) + kind: Cast [0-0]: + ty: Int(None, false) + expr: Expr [70-71]: + ty: BitArray(One(1), false) + kind: SymbolId(9) + ClassicalDeclarationStmt [81-97]: + symbol_id: 11 + ty_span: [81-85] + init_expr: Expr [90-96]: + ty: Bool(false) + kind: BinaryOpExpr: + op: Gte + lhs: Expr [90-91]: + ty: Int(None, false) + kind: Cast [0-0]: + ty: Int(None, false) + expr: Expr [90-91]: + ty: BitArray(One(1), false) + kind: SymbolId(8) + rhs: Expr [95-96]: + ty: Int(None, false) + kind: Cast [0-0]: + ty: Int(None, false) + expr: Expr [95-96]: + ty: BitArray(One(1), false) + kind: SymbolId(9) + ClassicalDeclarationStmt [106-121]: + symbol_id: 12 + ty_span: [106-110] + init_expr: Expr [115-120]: + ty: Bool(false) + kind: BinaryOpExpr: + op: Lt + lhs: Expr [115-116]: + ty: Int(None, false) + kind: Cast [0-0]: + ty: Int(None, false) + expr: Expr [115-116]: + ty: BitArray(One(1), false) + kind: SymbolId(8) + rhs: Expr [119-120]: + ty: Int(None, false) + kind: Cast [0-0]: + ty: Int(None, false) + expr: Expr [119-120]: + ty: BitArray(One(1), false) + kind: SymbolId(9) + ClassicalDeclarationStmt [130-146]: + symbol_id: 13 + ty_span: [130-134] + init_expr: Expr [139-145]: + ty: Bool(false) + kind: BinaryOpExpr: + op: Lte + lhs: Expr [139-140]: + ty: Int(None, false) + kind: Cast [0-0]: + ty: Int(None, false) + expr: Expr [139-140]: + ty: BitArray(One(1), false) + kind: SymbolId(8) + rhs: Expr [144-145]: + ty: Int(None, false) + kind: Cast [0-0]: + ty: Int(None, false) + expr: Expr [144-145]: + ty: BitArray(One(1), false) + kind: SymbolId(9) + ClassicalDeclarationStmt [155-171]: + symbol_id: 14 + ty_span: [155-159] + init_expr: Expr [164-170]: + ty: Bool(false) + kind: BinaryOpExpr: + op: Eq + lhs: Expr [164-165]: + ty: Int(None, false) + kind: Cast [0-0]: + ty: Int(None, false) + expr: Expr [164-165]: + ty: BitArray(One(1), false) + kind: SymbolId(8) + rhs: Expr [169-170]: + ty: Int(None, false) + kind: Cast [0-0]: + ty: Int(None, false) + expr: Expr [169-170]: + ty: BitArray(One(1), false) + kind: SymbolId(9) + ClassicalDeclarationStmt [180-196]: + symbol_id: 15 + ty_span: [180-184] + init_expr: Expr [189-195]: + ty: Bool(false) + kind: BinaryOpExpr: + op: Neq + lhs: Expr [189-190]: + ty: Int(None, false) + kind: Cast [0-0]: + ty: Int(None, false) + expr: Expr [189-190]: + ty: BitArray(One(1), false) + kind: SymbolId(8) + rhs: Expr [194-195]: + ty: Int(None, false) + kind: Cast [0-0]: + ty: Int(None, false) + expr: Expr [194-195]: + ty: BitArray(One(1), false) + kind: SymbolId(9) + "#]]); } #[test] -#[ignore = "not yet implemented"] fn bitarray_var_comparison_to_int_can_be_translated() { let input = r#" bit[1] x = "1"; @@ -49,5 +186,218 @@ fn bitarray_var_comparison_to_int_can_be_translated() { bool l = y != x; "#; - check_stmt_kinds(input, &expect![[r#""#]]); + check_stmt_kinds(input, &expect![[r#" + ClassicalDeclarationStmt [9-24]: + symbol_id: 8 + ty_span: [9-15] + init_expr: Expr [20-23]: + ty: BitArray(One(1), false) + kind: Lit: Bitstring("1") + InputDeclaration [33-45]: + symbol_id: 9 + ClassicalDeclarationStmt [54-69]: + symbol_id: 10 + ty_span: [54-58] + init_expr: Expr [63-68]: + ty: Bool(false) + kind: BinaryOpExpr: + op: Gt + lhs: Expr [63-64]: + ty: Int(None, false) + kind: Cast [0-0]: + ty: Int(None, false) + expr: Expr [63-64]: + ty: BitArray(One(1), false) + kind: SymbolId(8) + rhs: Expr [67-68]: + ty: Int(None, false) + kind: SymbolId(9) + ClassicalDeclarationStmt [78-94]: + symbol_id: 11 + ty_span: [78-82] + init_expr: Expr [87-93]: + ty: Bool(false) + kind: BinaryOpExpr: + op: Gte + lhs: Expr [87-88]: + ty: Int(None, false) + kind: Cast [0-0]: + ty: Int(None, false) + expr: Expr [87-88]: + ty: BitArray(One(1), false) + kind: SymbolId(8) + rhs: Expr [92-93]: + ty: Int(None, false) + kind: SymbolId(9) + ClassicalDeclarationStmt [103-118]: + symbol_id: 12 + ty_span: [103-107] + init_expr: Expr [112-117]: + ty: Bool(false) + kind: BinaryOpExpr: + op: Lt + lhs: Expr [112-113]: + ty: Int(None, false) + kind: Cast [0-0]: + ty: Int(None, false) + expr: Expr [112-113]: + ty: BitArray(One(1), false) + kind: SymbolId(8) + rhs: Expr [116-117]: + ty: Int(None, false) + kind: SymbolId(9) + ClassicalDeclarationStmt [127-143]: + symbol_id: 13 + ty_span: [127-131] + init_expr: Expr [136-142]: + ty: Bool(false) + kind: BinaryOpExpr: + op: Lte + lhs: Expr [136-137]: + ty: Int(None, false) + kind: Cast [0-0]: + ty: Int(None, false) + expr: Expr [136-137]: + ty: BitArray(One(1), false) + kind: SymbolId(8) + rhs: Expr [141-142]: + ty: Int(None, false) + kind: SymbolId(9) + ClassicalDeclarationStmt [152-168]: + symbol_id: 14 + ty_span: [152-156] + init_expr: Expr [161-167]: + ty: Bool(false) + kind: BinaryOpExpr: + op: Eq + lhs: Expr [161-162]: + ty: Int(None, false) + kind: Cast [0-0]: + ty: Int(None, false) + expr: Expr [161-162]: + ty: BitArray(One(1), false) + kind: SymbolId(8) + rhs: Expr [166-167]: + ty: Int(None, false) + kind: SymbolId(9) + ClassicalDeclarationStmt [177-193]: + symbol_id: 15 + ty_span: [177-181] + init_expr: Expr [186-192]: + ty: Bool(false) + kind: BinaryOpExpr: + op: Neq + lhs: Expr [186-187]: + ty: Int(None, false) + kind: Cast [0-0]: + ty: Int(None, false) + expr: Expr [186-187]: + ty: BitArray(One(1), false) + kind: SymbolId(8) + rhs: Expr [191-192]: + ty: Int(None, false) + kind: SymbolId(9) + ClassicalDeclarationStmt [202-217]: + symbol_id: 16 + ty_span: [202-206] + init_expr: Expr [211-216]: + ty: Bool(false) + kind: BinaryOpExpr: + op: Gt + lhs: Expr [211-212]: + ty: Int(None, false) + kind: SymbolId(9) + rhs: Expr [215-216]: + ty: Int(None, false) + kind: Cast [0-0]: + ty: Int(None, false) + expr: Expr [215-216]: + ty: BitArray(One(1), false) + kind: SymbolId(8) + ClassicalDeclarationStmt [226-242]: + symbol_id: 17 + ty_span: [226-230] + init_expr: Expr [235-241]: + ty: Bool(false) + kind: BinaryOpExpr: + op: Gte + lhs: Expr [235-236]: + ty: Int(None, false) + kind: SymbolId(9) + rhs: Expr [240-241]: + ty: Int(None, false) + kind: Cast [0-0]: + ty: Int(None, false) + expr: Expr [240-241]: + ty: BitArray(One(1), false) + kind: SymbolId(8) + ClassicalDeclarationStmt [251-266]: + symbol_id: 18 + ty_span: [251-255] + init_expr: Expr [260-265]: + ty: Bool(false) + kind: BinaryOpExpr: + op: Lt + lhs: Expr [260-261]: + ty: Int(None, false) + kind: SymbolId(9) + rhs: Expr [264-265]: + ty: Int(None, false) + kind: Cast [0-0]: + ty: Int(None, false) + expr: Expr [264-265]: + ty: BitArray(One(1), false) + kind: SymbolId(8) + ClassicalDeclarationStmt [275-291]: + symbol_id: 19 + ty_span: [275-279] + init_expr: Expr [284-290]: + ty: Bool(false) + kind: BinaryOpExpr: + op: Lte + lhs: Expr [284-285]: + ty: Int(None, false) + kind: SymbolId(9) + rhs: Expr [289-290]: + ty: Int(None, false) + kind: Cast [0-0]: + ty: Int(None, false) + expr: Expr [289-290]: + ty: BitArray(One(1), false) + kind: SymbolId(8) + ClassicalDeclarationStmt [300-316]: + symbol_id: 20 + ty_span: [300-304] + init_expr: Expr [309-315]: + ty: Bool(false) + kind: BinaryOpExpr: + op: Eq + lhs: Expr [309-310]: + ty: Int(None, false) + kind: SymbolId(9) + rhs: Expr [314-315]: + ty: Int(None, false) + kind: Cast [0-0]: + ty: Int(None, false) + expr: Expr [314-315]: + ty: BitArray(One(1), false) + kind: SymbolId(8) + ClassicalDeclarationStmt [325-341]: + symbol_id: 21 + ty_span: [325-329] + init_expr: Expr [334-340]: + ty: Bool(false) + kind: BinaryOpExpr: + op: Neq + lhs: Expr [334-335]: + ty: Int(None, false) + kind: SymbolId(9) + rhs: Expr [339-340]: + ty: Int(None, false) + kind: Cast [0-0]: + ty: Int(None, false) + expr: Expr [339-340]: + ty: BitArray(One(1), false) + kind: SymbolId(8) + "#]]); } diff --git a/compiler/qsc_qasm3/src/semantic/tests/expression/binary/complex.rs b/compiler/qsc_qasm3/src/semantic/tests/expression/binary/complex.rs index 8e3f78604f..66fc8ce939 100644 --- a/compiler/qsc_qasm3/src/semantic/tests/expression/binary/complex.rs +++ b/compiler/qsc_qasm3/src/semantic/tests/expression/binary/complex.rs @@ -6,7 +6,6 @@ use expect_test::expect; use crate::semantic::tests::check_stmt_kinds; #[test] -#[ignore = "not yet implemented"] fn subtraction() { let input = " input complex[float] a; @@ -14,11 +13,30 @@ fn subtraction() { complex x = (a - b); "; - check_stmt_kinds(input, &expect![[r#""#]]); + check_stmt_kinds(input, &expect![[r#" + InputDeclaration [9-32]: + symbol_id: 8 + InputDeclaration [41-64]: + symbol_id: 9 + ClassicalDeclarationStmt [73-93]: + symbol_id: 10 + ty_span: [73-80] + init_expr: Expr [85-92]: + ty: Complex(None, false) + kind: Paren Expr [86-91]: + ty: Complex(None, false) + kind: BinaryOpExpr: + op: Sub + lhs: Expr [86-87]: + ty: Complex(None, false) + kind: SymbolId(8) + rhs: Expr [90-91]: + ty: Complex(None, false) + kind: SymbolId(9) + "#]]); } #[test] -#[ignore = "not yet implemented"] fn addition() { let input = " input complex[float] a; @@ -26,11 +44,30 @@ fn addition() { complex x = (a + b); "; - check_stmt_kinds(input, &expect![[r#""#]]); + check_stmt_kinds(input, &expect![[r#" + InputDeclaration [9-32]: + symbol_id: 8 + InputDeclaration [41-64]: + symbol_id: 9 + ClassicalDeclarationStmt [73-93]: + symbol_id: 10 + ty_span: [73-80] + init_expr: Expr [85-92]: + ty: Complex(None, false) + kind: Paren Expr [86-91]: + ty: Complex(None, false) + kind: BinaryOpExpr: + op: Add + lhs: Expr [86-87]: + ty: Complex(None, false) + kind: SymbolId(8) + rhs: Expr [90-91]: + ty: Complex(None, false) + kind: SymbolId(9) + "#]]); } #[test] -#[ignore = "not yet implemented"] fn multiplication() { let input = " input complex[float] a; @@ -38,11 +75,30 @@ fn multiplication() { complex x = (a * b); "; - check_stmt_kinds(input, &expect![[r#""#]]); + check_stmt_kinds(input, &expect![[r#" + InputDeclaration [9-32]: + symbol_id: 8 + InputDeclaration [41-64]: + symbol_id: 9 + ClassicalDeclarationStmt [73-93]: + symbol_id: 10 + ty_span: [73-80] + init_expr: Expr [85-92]: + ty: Complex(None, false) + kind: Paren Expr [86-91]: + ty: Complex(None, false) + kind: BinaryOpExpr: + op: Mul + lhs: Expr [86-87]: + ty: Complex(None, false) + kind: SymbolId(8) + rhs: Expr [90-91]: + ty: Complex(None, false) + kind: SymbolId(9) + "#]]); } #[test] -#[ignore = "not yet implemented"] fn division() { let input = " input complex[float] a; @@ -50,11 +106,30 @@ fn division() { complex x = (a / b); "; - check_stmt_kinds(input, &expect![[r#""#]]); + check_stmt_kinds(input, &expect![[r#" + InputDeclaration [9-32]: + symbol_id: 8 + InputDeclaration [41-64]: + symbol_id: 9 + ClassicalDeclarationStmt [73-93]: + symbol_id: 10 + ty_span: [73-80] + init_expr: Expr [85-92]: + ty: Complex(None, false) + kind: Paren Expr [86-91]: + ty: Complex(None, false) + kind: BinaryOpExpr: + op: Div + lhs: Expr [86-87]: + ty: Complex(None, false) + kind: SymbolId(8) + rhs: Expr [90-91]: + ty: Complex(None, false) + kind: SymbolId(9) + "#]]); } #[test] -#[ignore = "not yet implemented"] fn power() { let input = " input complex[float] a; @@ -62,5 +137,25 @@ fn power() { complex x = (a ** b); "; - check_stmt_kinds(input, &expect![[r#""#]]); + check_stmt_kinds(input, &expect![[r#" + InputDeclaration [9-32]: + symbol_id: 8 + InputDeclaration [41-64]: + symbol_id: 9 + ClassicalDeclarationStmt [73-94]: + symbol_id: 10 + ty_span: [73-80] + init_expr: Expr [85-93]: + ty: Complex(None, false) + kind: Paren Expr [86-92]: + ty: Complex(None, false) + kind: BinaryOpExpr: + op: Exp + lhs: Expr [86-87]: + ty: Complex(None, false) + kind: SymbolId(8) + rhs: Expr [91-92]: + ty: Complex(None, false) + kind: SymbolId(9) + "#]]); } diff --git a/compiler/qsc_qasm3/src/semantic/tests/expression/implicit_cast_from_bitarray.rs b/compiler/qsc_qasm3/src/semantic/tests/expression/implicit_cast_from_bitarray.rs index 019a219e04..b20511e089 100644 --- a/compiler/qsc_qasm3/src/semantic/tests/expression/implicit_cast_from_bitarray.rs +++ b/compiler/qsc_qasm3/src/semantic/tests/expression/implicit_cast_from_bitarray.rs @@ -6,18 +6,46 @@ use expect_test::expect; use crate::semantic::tests::check_classical_decls; #[test] -#[ignore = "not yet implemented"] fn to_int_decl_implicitly() { let input = r#" bit[5] reg; int b = reg; "#; - check_classical_decls(input, &expect![[r#""#]]); + check_classical_decls( + input, + &expect![[r#" + ClassicalDeclarationStmt [9-20]: + symbol_id: 8 + ty_span: [9-15] + init_expr: Expr [0-0]: + ty: BitArray(One(5), true) + kind: Lit: Bitstring("00000") + [8] Symbol [16-19]: + name: reg + type: BitArray(One(5), false) + qsharp_type: Result[] + io_kind: Default + ClassicalDeclarationStmt [29-41]: + symbol_id: 9 + ty_span: [29-32] + init_expr: Expr [37-40]: + ty: Int(None, false) + kind: Cast [0-0]: + ty: Int(None, false) + expr: Expr [37-40]: + ty: BitArray(One(5), false) + kind: SymbolId(8) + [9] Symbol [33-34]: + name: b + type: Int(None, false) + qsharp_type: Int + io_kind: Default + "#]], + ); } #[test] -#[ignore = "not yet implemented"] fn to_int_assignment_implicitly() { let input = r#" bit[5] reg; @@ -25,11 +53,48 @@ fn to_int_assignment_implicitly() { a = reg; "#; - check_classical_decls(input, &expect![[r#""#]]); + check_classical_decls(input, &expect![[r#" + ClassicalDeclarationStmt [9-20]: + symbol_id: 8 + ty_span: [9-15] + init_expr: Expr [0-0]: + ty: BitArray(One(5), true) + kind: Lit: Bitstring("00000") + [8] Symbol [16-19]: + name: reg + type: BitArray(One(5), false) + qsharp_type: Result[] + io_kind: Default + ClassicalDeclarationStmt [29-35]: + symbol_id: 9 + ty_span: [29-32] + init_expr: Expr [0-0]: + ty: Int(None, true) + kind: Lit: Int(0) + [9] Symbol [33-34]: + name: a + type: Int(None, false) + qsharp_type: Int + io_kind: Default + AssignStmt [44-52]: + symbol_id: 9 + lhs_span: [44-45] + rhs: Expr [48-51]: + ty: Int(None, false) + kind: Cast [0-0]: + ty: Int(None, false) + expr: Expr [48-51]: + ty: BitArray(One(5), false) + kind: SymbolId(8) + [9] Symbol [33-34]: + name: a + type: Int(None, false) + qsharp_type: Int + io_kind: Default + "#]]); } #[test] -#[ignore = "not yet implemented"] fn to_int_with_equal_width_in_assignment_implicitly() { let input = r#" bit[5] reg; @@ -37,22 +102,85 @@ fn to_int_with_equal_width_in_assignment_implicitly() { a = reg; "#; - check_classical_decls(input, &expect![[r#""#]]); + check_classical_decls(input, &expect![[r#" + ClassicalDeclarationStmt [9-20]: + symbol_id: 8 + ty_span: [9-15] + init_expr: Expr [0-0]: + ty: BitArray(One(5), true) + kind: Lit: Bitstring("00000") + [8] Symbol [16-19]: + name: reg + type: BitArray(One(5), false) + qsharp_type: Result[] + io_kind: Default + ClassicalDeclarationStmt [29-38]: + symbol_id: 9 + ty_span: [29-35] + init_expr: Expr [0-0]: + ty: Int(Some(5), true) + kind: Lit: Int(0) + [9] Symbol [36-37]: + name: a + type: Int(Some(5), false) + qsharp_type: Int + io_kind: Default + AssignStmt [47-55]: + symbol_id: 9 + lhs_span: [47-48] + rhs: Expr [51-54]: + ty: Int(Some(5), false) + kind: Cast [0-0]: + ty: Int(Some(5), false) + expr: Expr [51-54]: + ty: BitArray(One(5), false) + kind: SymbolId(8) + [9] Symbol [36-37]: + name: a + type: Int(Some(5), false) + qsharp_type: Int + io_kind: Default + "#]]); } #[test] -#[ignore = "not yet implemented"] fn to_int_with_equal_width_in_decl_implicitly() { let input = r#" bit[5] reg; int[5] a = reg; "#; - check_classical_decls(input, &expect![[r#""#]]); + check_classical_decls(input, &expect![[r#" + ClassicalDeclarationStmt [9-20]: + symbol_id: 8 + ty_span: [9-15] + init_expr: Expr [0-0]: + ty: BitArray(One(5), true) + kind: Lit: Bitstring("00000") + [8] Symbol [16-19]: + name: reg + type: BitArray(One(5), false) + qsharp_type: Result[] + io_kind: Default + ClassicalDeclarationStmt [29-44]: + symbol_id: 9 + ty_span: [29-35] + init_expr: Expr [40-43]: + ty: Int(Some(5), false) + kind: Cast [0-0]: + ty: Int(Some(5), false) + expr: Expr [40-43]: + ty: BitArray(One(5), false) + kind: SymbolId(8) + [9] Symbol [36-37]: + name: a + type: Int(Some(5), false) + qsharp_type: Int + io_kind: Default + "#]]); } #[test] -#[ignore = "not yet implemented"] fn to_int_with_higher_width_implicitly_fails() { let input = " int[6] a; @@ -60,21 +188,89 @@ fn to_int_with_higher_width_implicitly_fails() { a = reg; "; - check_classical_decls(input, &expect![[r#""#]]); + check_classical_decls(input, &expect![[r#" + Program: + version: + statements: + Stmt [9-18]: + annotations: + kind: ClassicalDeclarationStmt [9-18]: + symbol_id: 8 + ty_span: [9-15] + init_expr: Expr [0-0]: + ty: Int(Some(6), true) + kind: Lit: Int(0) + Stmt [27-38]: + annotations: + kind: ClassicalDeclarationStmt [27-38]: + symbol_id: 9 + ty_span: [27-33] + init_expr: Expr [0-0]: + ty: BitArray(One(5), true) + kind: Lit: Bitstring("00000") + Stmt [47-55]: + annotations: + kind: AssignStmt [47-55]: + symbol_id: 8 + lhs_span: [47-48] + rhs: Expr [51-54]: + ty: BitArray(One(5), false) + kind: SymbolId(9) + + [Qsc.Qasm3.Compile.CannotCast + + x Cannot cast expression of type BitArray(One(5), false) to type + | Int(Some(6), false) + ,-[test:4:13] + 3 | bit[5] reg; + 4 | a = reg; + : ^^^ + 5 | + `---- + ]"#]]); } #[test] -#[ignore = "not yet implemented"] fn to_int_with_higher_width_decl_implicitly_fails() { let input = " bit[5] reg; int[6] a = reg; "; - check_classical_decls(input, &expect![[r#""#]]); + check_classical_decls(input, &expect![[r#" + Program: + version: + statements: + Stmt [9-20]: + annotations: + kind: ClassicalDeclarationStmt [9-20]: + symbol_id: 8 + ty_span: [9-15] + init_expr: Expr [0-0]: + ty: BitArray(One(5), true) + kind: Lit: Bitstring("00000") + Stmt [29-44]: + annotations: + kind: ClassicalDeclarationStmt [29-44]: + symbol_id: 9 + ty_span: [29-35] + init_expr: Expr [40-43]: + ty: BitArray(One(5), false) + kind: SymbolId(8) + + [Qsc.Qasm3.Compile.CannotCast + + x Cannot cast expression of type BitArray(One(5), false) to type + | Int(Some(6), false) + ,-[test:3:20] + 2 | bit[5] reg; + 3 | int[6] a = reg; + : ^^^ + 4 | + `---- + ]"#]]); } #[test] -#[ignore = "not yet implemented"] fn to_int_with_lower_width_implicitly_fails() { let input = " input int[4] a; @@ -82,16 +278,81 @@ fn to_int_with_lower_width_implicitly_fails() { a = reg; "; - check_classical_decls(input, &expect![[r#""#]]); + check_classical_decls(input, &expect![[r#" + Program: + version: + statements: + Stmt [9-24]: + annotations: + kind: InputDeclaration [9-24]: + symbol_id: 8 + Stmt [33-44]: + annotations: + kind: ClassicalDeclarationStmt [33-44]: + symbol_id: 9 + ty_span: [33-39] + init_expr: Expr [0-0]: + ty: BitArray(One(5), true) + kind: Lit: Bitstring("00000") + Stmt [53-61]: + annotations: + kind: AssignStmt [53-61]: + symbol_id: 8 + lhs_span: [53-54] + rhs: Expr [57-60]: + ty: BitArray(One(5), false) + kind: SymbolId(9) + + [Qsc.Qasm3.Compile.CannotCast + + x Cannot cast expression of type BitArray(One(5), false) to type + | Int(Some(4), false) + ,-[test:4:13] + 3 | bit[5] reg; + 4 | a = reg; + : ^^^ + 5 | + `---- + ]"#]]); } #[test] -#[ignore = "not yet implemented"] fn to_int_with_lower_width_decl_implicitly_fails() { let input = " bit[5] reg; int[4] a = reg; "; - check_classical_decls(input, &expect![[r#""#]]); + check_classical_decls(input, &expect![[r#" + Program: + version: + statements: + Stmt [9-20]: + annotations: + kind: ClassicalDeclarationStmt [9-20]: + symbol_id: 8 + ty_span: [9-15] + init_expr: Expr [0-0]: + ty: BitArray(One(5), true) + kind: Lit: Bitstring("00000") + Stmt [29-44]: + annotations: + kind: ClassicalDeclarationStmt [29-44]: + symbol_id: 9 + ty_span: [29-35] + init_expr: Expr [40-43]: + ty: BitArray(One(5), false) + kind: SymbolId(8) + + [Qsc.Qasm3.Compile.CannotCast + + x Cannot cast expression of type BitArray(One(5), false) to type + | Int(Some(4), false) + ,-[test:3:20] + 2 | bit[5] reg; + 3 | int[4] a = reg; + : ^^^ + 4 | + `---- + ]"#]]); } diff --git a/compiler/qsc_qasm3/src/semantic/tests/expression/implicit_cast_from_float.rs b/compiler/qsc_qasm3/src/semantic/tests/expression/implicit_cast_from_float.rs index 706646a53b..bce8631b3f 100644 --- a/compiler/qsc_qasm3/src/semantic/tests/expression/implicit_cast_from_float.rs +++ b/compiler/qsc_qasm3/src/semantic/tests/expression/implicit_cast_from_float.rs @@ -531,7 +531,6 @@ fn to_explicit_complex_implicitly() { } #[test] -#[ignore = "not yet implemented"] fn to_angle_implicitly() { let input = " float x = 42.; @@ -541,44 +540,37 @@ fn to_angle_implicitly() { check_classical_decls( input, &expect![[r#" - Program: - version: - statements: - Stmt [9-23]: - annotations: - kind: ClassicalDeclarationStmt [9-23]: - symbol_id: 6 - ty_span: [9-14] - init_expr: Expr [19-22]: - ty: Float(None, true) - kind: Lit: Float(42.0) - - [Qsc.Qasm3.Compile.Unimplemented - - x this statement is not yet handled during OpenQASM 3 import: Cast float - | to angle - ,-[test:3:9] - 2 | float x = 42.; - 3 | angle y = x; - : ^^^^^^^^^^^^ - 4 | - `---- - , Qsc.Qasm3.Compile.CannotCast - - x Cannot cast expression of type Float(None, false) to type Angle(None, - | false) - ,-[test:3:9] - 2 | float x = 42.; - 3 | angle y = x; - : ^^^^^^^^^^^^ - 4 | - `---- - ]"#]], + ClassicalDeclarationStmt [9-23]: + symbol_id: 8 + ty_span: [9-14] + init_expr: Expr [19-22]: + ty: Float(None, false) + kind: Lit: Float(42.0) + [8] Symbol [15-16]: + name: x + type: Float(None, false) + qsharp_type: Double + io_kind: Default + ClassicalDeclarationStmt [32-44]: + symbol_id: 9 + ty_span: [32-37] + init_expr: Expr [42-43]: + ty: Angle(None, false) + kind: Cast [0-0]: + ty: Angle(None, false) + expr: Expr [42-43]: + ty: Float(None, false) + kind: SymbolId(8) + [9] Symbol [38-39]: + name: y + type: Angle(None, false) + qsharp_type: Angle + io_kind: Default + "#]], ); } #[test] -#[ignore = "not yet implemented"] fn to_explicit_angle_implicitly() { let input = " float x = 42.; @@ -588,38 +580,32 @@ fn to_explicit_angle_implicitly() { check_classical_decls( input, &expect![[r#" - Program: - version: - statements: - Stmt [9-23]: - annotations: - kind: ClassicalDeclarationStmt [9-23]: - symbol_id: 6 - ty_span: [9-14] - init_expr: Expr [19-22]: - ty: Float(None, true) - kind: Lit: Float(42.0) - - [Qsc.Qasm3.Compile.Unimplemented - - x this statement is not yet handled during OpenQASM 3 import: Cast float - | to angle - ,-[test:3:9] - 2 | float x = 42.; - 3 | angle[4] y = x; - : ^^^^^^^^^^^^^^^ - 4 | - `---- - , Qsc.Qasm3.Compile.CannotCast - - x Cannot cast expression of type Float(None, false) to type Angle(Some(4), - | false) - ,-[test:3:9] - 2 | float x = 42.; - 3 | angle[4] y = x; - : ^^^^^^^^^^^^^^^ - 4 | - `---- - ]"#]], + ClassicalDeclarationStmt [9-23]: + symbol_id: 8 + ty_span: [9-14] + init_expr: Expr [19-22]: + ty: Float(None, false) + kind: Lit: Float(42.0) + [8] Symbol [15-16]: + name: x + type: Float(None, false) + qsharp_type: Double + io_kind: Default + ClassicalDeclarationStmt [32-47]: + symbol_id: 9 + ty_span: [32-40] + init_expr: Expr [45-46]: + ty: Angle(Some(4), false) + kind: Cast [0-0]: + ty: Angle(Some(4), false) + expr: Expr [45-46]: + ty: Float(None, false) + kind: SymbolId(8) + [9] Symbol [41-42]: + name: y + type: Angle(Some(4), false) + qsharp_type: Angle + io_kind: Default + "#]], ); } diff --git a/compiler/qsc_qasm3/src/tests/declaration/float.rs b/compiler/qsc_qasm3/src/tests/declaration/float.rs index e8eb351e99..2f7ca8a970 100644 --- a/compiler/qsc_qasm3/src/tests/declaration/float.rs +++ b/compiler/qsc_qasm3/src/tests/declaration/float.rs @@ -87,7 +87,6 @@ fn const_explicit_width_lit_decl() -> miette::Result<(), Vec> { } #[test] -#[ignore = "oq3 parser bug, can't read float with leading dot"] fn lit_decl_leading_dot() -> miette::Result<(), Vec> { let source = " float x = .421; @@ -104,7 +103,6 @@ fn lit_decl_leading_dot() -> miette::Result<(), Vec> { } #[test] -#[ignore = "oq3 parser bug, can't read float with leading dot"] fn const_lit_decl_leading_dot() -> miette::Result<(), Vec> { let source = " const float x = .421; @@ -121,7 +119,6 @@ fn const_lit_decl_leading_dot() -> miette::Result<(), Vec> { } #[test] -#[ignore = "oq3 parser bug, can't read float with leading dot"] fn const_lit_decl_leading_dot_scientific() -> miette::Result<(), Vec> { let source = " const float x = .421e2; diff --git a/compiler/qsc_qasm3/src/tests/declaration/integer.rs b/compiler/qsc_qasm3/src/tests/declaration/integer.rs index 187431b62d..09ba4498b6 100644 --- a/compiler/qsc_qasm3/src/tests/declaration/integer.rs +++ b/compiler/qsc_qasm3/src/tests/declaration/integer.rs @@ -71,7 +71,6 @@ fn const_implicit_bitness_int_lit_decl() -> miette::Result<(), Vec> { } #[test] -#[ignore = "oq3 parser bug, capital X is not recognized as hex"] fn implicit_bitness_int_hex_cap_decl() -> miette::Result<(), Vec> { let source = " int x = 0XFa_1F; @@ -104,10 +103,9 @@ fn const_implicit_bitness_int_hex_low_decl() -> miette::Result<(), Vec> } #[test] -#[ignore = "oq3 parser bug, capital X is not recognized as hex"] fn const_implicit_bitness_int_hex_cap_decl() -> miette::Result<(), Vec> { let source = " - const int y = 0XFa_1F; + const int x = 0XFa_1F; "; let qsharp = compile_qasm_stmt_to_qsharp(source)?; @@ -169,7 +167,6 @@ fn implicit_bitness_int_binary_low_decl() -> miette::Result<(), Vec> { } #[test] -#[ignore = "oq3 parser bug, capital B is not recognized as binary"] fn implicit_bitness_int_binary_cap_decl() -> miette::Result<(), Vec> { let source = " int x = 0B1010; @@ -202,7 +199,6 @@ fn const_implicit_bitness_int_binary_low_decl() -> miette::Result<(), Vec miette::Result<(), Vec> { let source = " const int x = 0B1010; diff --git a/compiler/qsc_qasm3/src/tests/declaration/unsigned_integer.rs b/compiler/qsc_qasm3/src/tests/declaration/unsigned_integer.rs index 58a93ab28e..87b40a8158 100644 --- a/compiler/qsc_qasm3/src/tests/declaration/unsigned_integer.rs +++ b/compiler/qsc_qasm3/src/tests/declaration/unsigned_integer.rs @@ -39,7 +39,6 @@ fn const_implicit_bitness_int_lit_decl() -> miette::Result<(), Vec> { } #[test] -#[ignore = "oq3 parser bug, capital X is not recognized as hex"] fn implicit_bitness_int_hex_cap_decl() -> miette::Result<(), Vec> { let source = " uint x = 0XFa_1F; @@ -72,10 +71,9 @@ fn const_implicit_bitness_int_hex_low_decl() -> miette::Result<(), Vec> } #[test] -#[ignore = "oq3 parser bug, capital X is not recognized as hex"] fn const_implicit_bitness_int_hex_cap_decl() -> miette::Result<(), Vec> { let source = " - const uint y = 0XFa_1F; + const uint x = 0XFa_1F; "; let qsharp = compile_qasm_stmt_to_qsharp(source)?; @@ -137,7 +135,6 @@ fn implicit_bitness_int_binary_low_decl() -> miette::Result<(), Vec> { } #[test] -#[ignore = "oq3 parser bug, capital B is not recognized as binary"] fn implicit_bitness_int_binary_cap_decl() -> miette::Result<(), Vec> { let source = " uint x = 0B1010; @@ -170,7 +167,6 @@ fn const_implicit_bitness_int_binary_low_decl() -> miette::Result<(), Vec miette::Result<(), Vec> { let source = " const uint x = 0B1010; diff --git a/compiler/qsc_qasm3/src/tests/expression/unary.rs b/compiler/qsc_qasm3/src/tests/expression/unary.rs index cb5f05270a..fd6b709470 100644 --- a/compiler/qsc_qasm3/src/tests/expression/unary.rs +++ b/compiler/qsc_qasm3/src/tests/expression/unary.rs @@ -7,20 +7,28 @@ use miette::Report; use crate::tests::compile_qasm_to_qsharp; #[test] -#[ignore = "OPENQASM 3.0 parser bug"] -fn bitwise_not_int() -> miette::Result<(), Vec> { +fn bitwise_not_int_fails() { let source = " int x = 5; int y = ~x; "; - let qsharp = compile_qasm_to_qsharp(source)?; + let Err(errors) = compile_qasm_to_qsharp(source) else { + panic!("Expected error"); + }; + expect![[r#" - mutable x = 5; - mutable y = ~~~x; - "#]] - .assert_eq(&qsharp); - Ok(()) + [Qsc.Qasm3.Compile.TypeDoesNotSupportedUnaryNegation + + x Unary negation is not allowed for instances of Int(None, false). + ,-[Test.qasm:3:18] + 2 | int x = 5; + 3 | int y = ~x; + : ^ + 4 | + `---- + ]"#]] + .assert_eq(&format!("{errors:?}")); } #[test] diff --git a/compiler/qsc_qasm3/src/tests/statement/const_eval.rs b/compiler/qsc_qasm3/src/tests/statement/const_eval.rs index 03de904b1c..14dd57add3 100644 --- a/compiler/qsc_qasm3/src/tests/statement/const_eval.rs +++ b/compiler/qsc_qasm3/src/tests/statement/const_eval.rs @@ -143,7 +143,7 @@ fn ident_const() -> miette::Result<(), Vec> { #[ignore = "indexed ident is not yet supported"] fn indexed_ident() -> miette::Result<(), Vec> { let source = r#" - const uint[2] a = {1, 2}; + const array[uint, 2] a = {1, 2}; bit[a[1]] r; "#; diff --git a/compiler/qsc_qasm3/src/tests/statement/if_stmt.rs b/compiler/qsc_qasm3/src/tests/statement/if_stmt.rs index 3f551c4b7b..a5714850fd 100644 --- a/compiler/qsc_qasm3/src/tests/statement/if_stmt.rs +++ b/compiler/qsc_qasm3/src/tests/statement/if_stmt.rs @@ -69,7 +69,6 @@ fn can_use_negated_cond_with_implicit_cast_to_bool() -> miette::Result<(), Vec miette::Result<(), Vec> { let source = r#" include "stdgates.inc"; @@ -79,17 +78,19 @@ fn then_branch_can_be_stmt() -> miette::Result<(), Vec> { let qsharp = compile_qasm_to_qsharp(source)?; expect![[r#" + import QasmStd.Angle.*; + import QasmStd.Convert.*; + import QasmStd.Intrinsic.*; let q = QIR.Runtime.__quantum__rt__qubit_allocate(); if 0 == 1 { - Z(q); + z(q); }; - "#]] + "#]] .assert_eq(&qsharp); Ok(()) } #[test] -#[ignore = "QASM3 Parser bug"] fn else_branch_can_be_stmt() -> miette::Result<(), Vec> { let source = r#" include "stdgates.inc"; @@ -100,19 +101,21 @@ fn else_branch_can_be_stmt() -> miette::Result<(), Vec> { let qsharp = compile_qasm_to_qsharp(source)?; expect![[r#" + import QasmStd.Angle.*; + import QasmStd.Convert.*; + import QasmStd.Intrinsic.*; let q = QIR.Runtime.__quantum__rt__qubit_allocate(); if 0 == 1 { - Z(q); + z(q); } else { - Y(q); + y(q); }; - "#]] + "#]] .assert_eq(&qsharp); Ok(()) } #[test] -#[ignore = "QASM3 Parser bug"] fn then_and_else_branch_can_be_stmt() -> miette::Result<(), Vec> { let source = r#" include "stdgates.inc"; @@ -123,13 +126,16 @@ fn then_and_else_branch_can_be_stmt() -> miette::Result<(), Vec> { let qsharp = compile_qasm_to_qsharp(source)?; expect![[r#" + import QasmStd.Angle.*; + import QasmStd.Convert.*; + import QasmStd.Intrinsic.*; let q = QIR.Runtime.__quantum__rt__qubit_allocate(); if 0 == 1 { - Z(q); + z(q); } else { - Y(q); + y(q); }; - "#]] + "#]] .assert_eq(&qsharp); Ok(()) } From 6cda9866cd150dfb400777268125f44d2c34b568 Mon Sep 17 00:00:00 2001 From: Oscar Puente Date: Tue, 1 Apr 2025 13:32:18 -0700 Subject: [PATCH 077/108] add simulatable intrinsic QIR unit tests --- .../src/tests/expression/function_call.rs | 54 ++++++++++++++++++- .../src/tests/statement/gate_call.rs | 21 ++++++++ 2 files changed, 74 insertions(+), 1 deletion(-) diff --git a/compiler/qsc_qasm3/src/tests/expression/function_call.rs b/compiler/qsc_qasm3/src/tests/expression/function_call.rs index a679d76f82..183110a613 100644 --- a/compiler/qsc_qasm3/src/tests/expression/function_call.rs +++ b/compiler/qsc_qasm3/src/tests/expression/function_call.rs @@ -1,9 +1,10 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. -use crate::tests::compile_qasm_to_qsharp; +use crate::tests::{compile_qasm_to_qir, compile_qasm_to_qsharp}; use expect_test::expect; use miette::Report; +use qsc::target::Profile; #[test] fn funcall_with_no_arguments_generates_correct_qsharp() -> miette::Result<(), Vec> { @@ -311,3 +312,54 @@ fn funcall_implicit_arg_cast_uint_to_qubit_errors() { ]"#]] .assert_eq(&format!("{errors:?}")); } + +#[test] +fn simulatable_intrinsic_on_gates_generates_correct_qir() -> miette::Result<(), Vec> { + let source = r#" + include "stdgates.inc"; + + @SimulatableIntrinsic + def my_gate(qubit q) { + x q; + } + + qubit q; + my_gate(q); + bit result = measure q; + "#; + + let qsharp = compile_qasm_to_qir(source, Profile::AdaptiveRI)?; + expect![[r#" + %Result = type opaque + %Qubit = type opaque + + define void @ENTRYPOINT__main() #0 { + block_0: + call void @my_gate(%Qubit* inttoptr (i64 0 to %Qubit*)) + call void @__quantum__qis__m__body(%Qubit* inttoptr (i64 0 to %Qubit*), %Result* inttoptr (i64 0 to %Result*)) + call void @__quantum__rt__tuple_record_output(i64 0, i8* null) + ret void + } + + declare void @my_gate(%Qubit*) + + declare void @__quantum__qis__m__body(%Qubit*, %Result*) #1 + + declare void @__quantum__rt__tuple_record_output(i64, i8*) + + attributes #0 = { "entry_point" "output_labeling_schema" "qir_profiles"="adaptive_profile" "required_num_qubits"="1" "required_num_results"="1" } + attributes #1 = { "irreversible" } + + ; module flags + + !llvm.module.flags = !{!0, !1, !2, !3, !4} + + !0 = !{i32 1, !"qir_major_version", i32 1} + !1 = !{i32 7, !"qir_minor_version", i32 0} + !2 = !{i32 1, !"dynamic_qubit_management", i1 false} + !3 = !{i32 1, !"dynamic_result_management", i1 false} + !4 = !{i32 1, !"int_computations", !"i64"} + "#]] + .assert_eq(&qsharp); + Ok(()) +} \ No newline at end of file diff --git a/compiler/qsc_qasm3/src/tests/statement/gate_call.rs b/compiler/qsc_qasm3/src/tests/statement/gate_call.rs index 1da5555965..9b0d8929d2 100644 --- a/compiler/qsc_qasm3/src/tests/statement/gate_call.rs +++ b/compiler/qsc_qasm3/src/tests/statement/gate_call.rs @@ -473,3 +473,24 @@ fn custom_gate_can_be_called_with_pow_modifier() -> miette::Result<(), Vec miette::Result<(), Vec> { + let source = r#" + include "stdgates.inc"; + + @SimulatableIntrinsic + gate my_gate(a) q { + rx(a) q; + } + + qubit q; + my_gate(2.0) q; + bit result = measure q; + "#; + + let qsharp = compile_qasm_to_qir(source, Profile::AdaptiveRI)?; + expect![[r#""#]] + .assert_eq(&qsharp); + Ok(()) +} \ No newline at end of file From 8b77982e354683f911213a2ad23db2f6407bcd4b Mon Sep 17 00:00:00 2001 From: Oscar Puente Date: Wed, 2 Apr 2025 06:13:47 -0700 Subject: [PATCH 078/108] clippy lints --- .../tests/expression/binary/comparison.rs | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/compiler/qsc_qasm3/src/semantic/tests/expression/binary/comparison.rs b/compiler/qsc_qasm3/src/semantic/tests/expression/binary/comparison.rs index 36c2847de0..2ec6e5c4e8 100644 --- a/compiler/qsc_qasm3/src/semantic/tests/expression/binary/comparison.rs +++ b/compiler/qsc_qasm3/src/semantic/tests/expression/binary/comparison.rs @@ -12,6 +12,7 @@ use expect_test::expect; use crate::semantic::tests::check_stmt_kinds; +#[allow(clippy::too_many_lines)] #[test] fn bitarray_var_comparisons_can_be_translated() { let input = r#" @@ -25,7 +26,9 @@ fn bitarray_var_comparisons_can_be_translated() { bool d = x != y; "#; - check_stmt_kinds(input, &expect![[r#" + check_stmt_kinds( + input, + &expect![[r#" ClassicalDeclarationStmt [9-24]: symbol_id: 8 ty_span: [9-15] @@ -164,9 +167,11 @@ fn bitarray_var_comparisons_can_be_translated() { expr: Expr [194-195]: ty: BitArray(One(1), false) kind: SymbolId(9) - "#]]); + "#]], + ); } +#[allow(clippy::too_many_lines)] #[test] fn bitarray_var_comparison_to_int_can_be_translated() { let input = r#" @@ -186,7 +191,9 @@ fn bitarray_var_comparison_to_int_can_be_translated() { bool l = y != x; "#; - check_stmt_kinds(input, &expect![[r#" + check_stmt_kinds( + input, + &expect![[r#" ClassicalDeclarationStmt [9-24]: symbol_id: 8 ty_span: [9-15] @@ -399,5 +406,6 @@ fn bitarray_var_comparison_to_int_can_be_translated() { expr: Expr [339-340]: ty: BitArray(One(1), false) kind: SymbolId(8) - "#]]); + "#]], + ); } From 91607e70b4a9e6052cf9608983820f93422d91e3 Mon Sep 17 00:00:00 2001 From: Oscar Puente Date: Wed, 2 Apr 2025 06:16:45 -0700 Subject: [PATCH 079/108] apply cargo fmt --- .../tests/expression/binary/complex.rs | 35 +++++++++---- .../expression/implicit_cast_from_bitarray.rs | 49 +++++++++++++------ .../src/tests/expression/function_call.rs | 2 +- .../src/tests/statement/gate_call.rs | 5 +- 4 files changed, 63 insertions(+), 28 deletions(-) diff --git a/compiler/qsc_qasm3/src/semantic/tests/expression/binary/complex.rs b/compiler/qsc_qasm3/src/semantic/tests/expression/binary/complex.rs index 66fc8ce939..49c7d68b3c 100644 --- a/compiler/qsc_qasm3/src/semantic/tests/expression/binary/complex.rs +++ b/compiler/qsc_qasm3/src/semantic/tests/expression/binary/complex.rs @@ -13,7 +13,9 @@ fn subtraction() { complex x = (a - b); "; - check_stmt_kinds(input, &expect![[r#" + check_stmt_kinds( + input, + &expect![[r#" InputDeclaration [9-32]: symbol_id: 8 InputDeclaration [41-64]: @@ -33,7 +35,8 @@ fn subtraction() { rhs: Expr [90-91]: ty: Complex(None, false) kind: SymbolId(9) - "#]]); + "#]], + ); } #[test] @@ -44,7 +47,9 @@ fn addition() { complex x = (a + b); "; - check_stmt_kinds(input, &expect![[r#" + check_stmt_kinds( + input, + &expect![[r#" InputDeclaration [9-32]: symbol_id: 8 InputDeclaration [41-64]: @@ -64,7 +69,8 @@ fn addition() { rhs: Expr [90-91]: ty: Complex(None, false) kind: SymbolId(9) - "#]]); + "#]], + ); } #[test] @@ -75,7 +81,9 @@ fn multiplication() { complex x = (a * b); "; - check_stmt_kinds(input, &expect![[r#" + check_stmt_kinds( + input, + &expect![[r#" InputDeclaration [9-32]: symbol_id: 8 InputDeclaration [41-64]: @@ -95,7 +103,8 @@ fn multiplication() { rhs: Expr [90-91]: ty: Complex(None, false) kind: SymbolId(9) - "#]]); + "#]], + ); } #[test] @@ -106,7 +115,9 @@ fn division() { complex x = (a / b); "; - check_stmt_kinds(input, &expect![[r#" + check_stmt_kinds( + input, + &expect![[r#" InputDeclaration [9-32]: symbol_id: 8 InputDeclaration [41-64]: @@ -126,7 +137,8 @@ fn division() { rhs: Expr [90-91]: ty: Complex(None, false) kind: SymbolId(9) - "#]]); + "#]], + ); } #[test] @@ -137,7 +149,9 @@ fn power() { complex x = (a ** b); "; - check_stmt_kinds(input, &expect![[r#" + check_stmt_kinds( + input, + &expect![[r#" InputDeclaration [9-32]: symbol_id: 8 InputDeclaration [41-64]: @@ -157,5 +171,6 @@ fn power() { rhs: Expr [91-92]: ty: Complex(None, false) kind: SymbolId(9) - "#]]); + "#]], + ); } diff --git a/compiler/qsc_qasm3/src/semantic/tests/expression/implicit_cast_from_bitarray.rs b/compiler/qsc_qasm3/src/semantic/tests/expression/implicit_cast_from_bitarray.rs index b20511e089..d48f090faa 100644 --- a/compiler/qsc_qasm3/src/semantic/tests/expression/implicit_cast_from_bitarray.rs +++ b/compiler/qsc_qasm3/src/semantic/tests/expression/implicit_cast_from_bitarray.rs @@ -53,7 +53,9 @@ fn to_int_assignment_implicitly() { a = reg; "#; - check_classical_decls(input, &expect![[r#" + check_classical_decls( + input, + &expect![[r#" ClassicalDeclarationStmt [9-20]: symbol_id: 8 ty_span: [9-15] @@ -91,7 +93,8 @@ fn to_int_assignment_implicitly() { type: Int(None, false) qsharp_type: Int io_kind: Default - "#]]); + "#]], + ); } #[test] @@ -102,7 +105,9 @@ fn to_int_with_equal_width_in_assignment_implicitly() { a = reg; "#; - check_classical_decls(input, &expect![[r#" + check_classical_decls( + input, + &expect![[r#" ClassicalDeclarationStmt [9-20]: symbol_id: 8 ty_span: [9-15] @@ -140,7 +145,8 @@ fn to_int_with_equal_width_in_assignment_implicitly() { type: Int(Some(5), false) qsharp_type: Int io_kind: Default - "#]]); + "#]], + ); } #[test] @@ -150,7 +156,9 @@ fn to_int_with_equal_width_in_decl_implicitly() { int[5] a = reg; "#; - check_classical_decls(input, &expect![[r#" + check_classical_decls( + input, + &expect![[r#" ClassicalDeclarationStmt [9-20]: symbol_id: 8 ty_span: [9-15] @@ -177,7 +185,8 @@ fn to_int_with_equal_width_in_decl_implicitly() { type: Int(Some(5), false) qsharp_type: Int io_kind: Default - "#]]); + "#]], + ); } #[test] @@ -188,7 +197,9 @@ fn to_int_with_higher_width_implicitly_fails() { a = reg; "; - check_classical_decls(input, &expect![[r#" + check_classical_decls( + input, + &expect![[r#" Program: version: statements: @@ -227,7 +238,8 @@ fn to_int_with_higher_width_implicitly_fails() { : ^^^ 5 | `---- - ]"#]]); + ]"#]], + ); } #[test] @@ -236,7 +248,9 @@ fn to_int_with_higher_width_decl_implicitly_fails() { bit[5] reg; int[6] a = reg; "; - check_classical_decls(input, &expect![[r#" + check_classical_decls( + input, + &expect![[r#" Program: version: statements: @@ -267,7 +281,8 @@ fn to_int_with_higher_width_decl_implicitly_fails() { : ^^^ 4 | `---- - ]"#]]); + ]"#]], + ); } #[test] @@ -278,7 +293,9 @@ fn to_int_with_lower_width_implicitly_fails() { a = reg; "; - check_classical_decls(input, &expect![[r#" + check_classical_decls( + input, + &expect![[r#" Program: version: statements: @@ -313,7 +330,8 @@ fn to_int_with_lower_width_implicitly_fails() { : ^^^ 5 | `---- - ]"#]]); + ]"#]], + ); } #[test] @@ -323,7 +341,9 @@ fn to_int_with_lower_width_decl_implicitly_fails() { int[4] a = reg; "; - check_classical_decls(input, &expect![[r#" + check_classical_decls( + input, + &expect![[r#" Program: version: statements: @@ -354,5 +374,6 @@ fn to_int_with_lower_width_decl_implicitly_fails() { : ^^^ 4 | `---- - ]"#]]); + ]"#]], + ); } diff --git a/compiler/qsc_qasm3/src/tests/expression/function_call.rs b/compiler/qsc_qasm3/src/tests/expression/function_call.rs index 183110a613..fb8088c735 100644 --- a/compiler/qsc_qasm3/src/tests/expression/function_call.rs +++ b/compiler/qsc_qasm3/src/tests/expression/function_call.rs @@ -362,4 +362,4 @@ fn simulatable_intrinsic_on_gates_generates_correct_qir() -> miette::Result<(), "#]] .assert_eq(&qsharp); Ok(()) -} \ No newline at end of file +} diff --git a/compiler/qsc_qasm3/src/tests/statement/gate_call.rs b/compiler/qsc_qasm3/src/tests/statement/gate_call.rs index 9b0d8929d2..407c610753 100644 --- a/compiler/qsc_qasm3/src/tests/statement/gate_call.rs +++ b/compiler/qsc_qasm3/src/tests/statement/gate_call.rs @@ -490,7 +490,6 @@ fn simulatable_intrinsic_on_gates_generates_correct_qir() -> miette::Result<(), "#; let qsharp = compile_qasm_to_qir(source, Profile::AdaptiveRI)?; - expect![[r#""#]] - .assert_eq(&qsharp); + expect![[r#""#]].assert_eq(&qsharp); Ok(()) -} \ No newline at end of file +} From 0b84fc9140360bee3348d57f5eead17ee433c4e1 Mon Sep 17 00:00:00 2001 From: Oscar Puente Date: Wed, 2 Apr 2025 06:40:33 -0700 Subject: [PATCH 080/108] make const measurement a parser error --- compiler/qsc_qasm3/src/parser/expr.rs | 14 ++++++++++++ compiler/qsc_qasm3/src/parser/stmt.rs | 2 +- .../src/parser/stmt/tests/classical_decl.rs | 22 +++++++++++++++++++ 3 files changed, 37 insertions(+), 1 deletion(-) diff --git a/compiler/qsc_qasm3/src/parser/expr.rs b/compiler/qsc_qasm3/src/parser/expr.rs index f657496b3a..ae69817719 100644 --- a/compiler/qsc_qasm3/src/parser/expr.rs +++ b/compiler/qsc_qasm3/src/parser/expr.rs @@ -724,6 +724,20 @@ pub(super) fn declaration_expr(s: &mut ParserContext) -> Result { Ok(ValueExpr::Expr(expr)) } +/// These are expressions allowed in constant classical declarations. +/// Note, that the spec doesn't specify that measurements are not allowed +/// here, but this is a spec bug, since measuremnts can't be performed at +/// compile time. +pub(super) fn const_declaration_expr(s: &mut ParserContext) -> Result { + let expr = if let Some(expr) = opt(s, expr)? { + expr + } else { + lit_array(s)? + }; + + Ok(ValueExpr::Expr(expr)) +} + /// These are expressions allowed in `Assign`, `AssignOp`, and return stmts. /// Grammar: `expression | measureExpression`. pub(super) fn expr_or_measurement(s: &mut ParserContext) -> Result { diff --git a/compiler/qsc_qasm3/src/parser/stmt.rs b/compiler/qsc_qasm3/src/parser/stmt.rs index b909ef427a..17eeac9c9c 100644 --- a/compiler/qsc_qasm3/src/parser/stmt.rs +++ b/compiler/qsc_qasm3/src/parser/stmt.rs @@ -743,7 +743,7 @@ fn parse_constant_classical_decl(s: &mut ParserContext) -> Result { let ty = scalar_or_array_type(s)?; let identifier = Box::new(prim::ident(s)?); token(s, TokenKind::Eq)?; - let init_expr = expr::declaration_expr(s)?; + let init_expr = expr::const_declaration_expr(s)?; recovering_semi(s); let decl = ConstantDeclStmt { span: s.span(lo), diff --git a/compiler/qsc_qasm3/src/parser/stmt/tests/classical_decl.rs b/compiler/qsc_qasm3/src/parser/stmt/tests/classical_decl.rs index 2ae53268ce..f6c15e9267 100644 --- a/compiler/qsc_qasm3/src/parser/stmt/tests/classical_decl.rs +++ b/compiler/qsc_qasm3/src/parser/stmt/tests/classical_decl.rs @@ -1103,3 +1103,25 @@ fn measure_register_decl() { Expr [28-29]: Lit: Int(3)"#]], ); } + +#[test] +fn const_decl_with_measurement_init_fails() { + check( + parse, + "const bit res = measure q;", + &expect![[r#" + Error( + Token( + Open( + Brace, + ), + Measure, + Span { + lo: 16, + hi: 23, + }, + ), + ) + "#]], + ); +} \ No newline at end of file From f241ed0176d4cab1d5ad60a6d72ade9c663138b0 Mon Sep 17 00:00:00 2001 From: Oscar Puente Date: Wed, 2 Apr 2025 11:29:54 -0700 Subject: [PATCH 081/108] do not generate functors when `@SimulatableIntrinsic` is present --- compiler/qsc_qasm3/src/compiler.rs | 12 +++++- .../src/tests/expression/function_call.rs | 2 +- .../src/tests/statement/annotation.rs | 4 +- .../src/tests/statement/gate_call.rs | 40 ++++++++++++++++--- .../qsc_qasm3/src/tests/statement/include.rs | 2 +- 5 files changed, 50 insertions(+), 10 deletions(-) diff --git a/compiler/qsc_qasm3/src/compiler.rs b/compiler/qsc_qasm3/src/compiler.rs index eb709e4361..a2ae9b609d 100644 --- a/compiler/qsc_qasm3/src/compiler.rs +++ b/compiler/qsc_qasm3/src/compiler.rs @@ -930,6 +930,16 @@ impl QasmCompiler { .iter() .filter_map(|annotation| self.compile_annotation(annotation)); + // Do not compile functors if we have the @SimulatableIntrinsic annotation. + let functors = if annotations + .iter() + .any(|annotation| annotation.identifier.as_ref() == "SimulatableIntrinsic") + { + None + } else { + Some(build_adj_plus_ctl_functor()) + }; + Some(build_function_or_operation( name, cargs, @@ -940,7 +950,7 @@ impl QasmCompiler { stmt.span, None, qsast::CallableKind::Operation, - Some(build_adj_plus_ctl_functor()), + functors, list_from_iter(attrs), )) } diff --git a/compiler/qsc_qasm3/src/tests/expression/function_call.rs b/compiler/qsc_qasm3/src/tests/expression/function_call.rs index fb8088c735..f31582c480 100644 --- a/compiler/qsc_qasm3/src/tests/expression/function_call.rs +++ b/compiler/qsc_qasm3/src/tests/expression/function_call.rs @@ -314,7 +314,7 @@ fn funcall_implicit_arg_cast_uint_to_qubit_errors() { } #[test] -fn simulatable_intrinsic_on_gates_generates_correct_qir() -> miette::Result<(), Vec> { +fn simulatable_intrinsic_on_def_stmt_generates_correct_qir() -> miette::Result<(), Vec> { let source = r#" include "stdgates.inc"; diff --git a/compiler/qsc_qasm3/src/tests/statement/annotation.rs b/compiler/qsc_qasm3/src/tests/statement/annotation.rs index bbedda3b53..672058209d 100644 --- a/compiler/qsc_qasm3/src/tests/statement/annotation.rs +++ b/compiler/qsc_qasm3/src/tests/statement/annotation.rs @@ -21,10 +21,10 @@ fn simulatable_intrinsic_can_be_applied_to_gate() -> miette::Result<(), Vec miette::Result<(), Vec miette::Result<(), Vec> { +fn simulatable_intrinsic_on_gate_stmt_generates_correct_qir() -> miette::Result<(), Vec> { let source = r#" include "stdgates.inc"; @SimulatableIntrinsic - gate my_gate(a) q { - rx(a) q; + gate my_gate q { + x q; } qubit q; - my_gate(2.0) q; + my_gate q; bit result = measure q; "#; let qsharp = compile_qasm_to_qir(source, Profile::AdaptiveRI)?; - expect![[r#""#]].assert_eq(&qsharp); + expect![[r#" + %Result = type opaque + %Qubit = type opaque + + define void @ENTRYPOINT__main() #0 { + block_0: + call void @my_gate(%Qubit* inttoptr (i64 0 to %Qubit*)) + call void @__quantum__qis__m__body(%Qubit* inttoptr (i64 0 to %Qubit*), %Result* inttoptr (i64 0 to %Result*)) + call void @__quantum__rt__tuple_record_output(i64 0, i8* null) + ret void + } + + declare void @my_gate(%Qubit*) + + declare void @__quantum__qis__m__body(%Qubit*, %Result*) #1 + + declare void @__quantum__rt__tuple_record_output(i64, i8*) + + attributes #0 = { "entry_point" "output_labeling_schema" "qir_profiles"="adaptive_profile" "required_num_qubits"="1" "required_num_results"="1" } + attributes #1 = { "irreversible" } + + ; module flags + + !llvm.module.flags = !{!0, !1, !2, !3, !4} + + !0 = !{i32 1, !"qir_major_version", i32 1} + !1 = !{i32 7, !"qir_minor_version", i32 0} + !2 = !{i32 1, !"dynamic_qubit_management", i1 false} + !3 = !{i32 1, !"dynamic_result_management", i1 false} + !4 = !{i32 1, !"int_computations", !"i64"} + "#]].assert_eq(&qsharp); Ok(()) } diff --git a/compiler/qsc_qasm3/src/tests/statement/include.rs b/compiler/qsc_qasm3/src/tests/statement/include.rs index 5a7da1a6e8..19e4f96472 100644 --- a/compiler/qsc_qasm3/src/tests/statement/include.rs +++ b/compiler/qsc_qasm3/src/tests/statement/include.rs @@ -46,7 +46,7 @@ fn programs_with_includes_can_be_parsed() -> miette::Result<(), Vec> { @EntryPoint() operation Test() : Result[] { @SimulatableIntrinsic() - operation my_gate(q : Qubit) : Unit is Adj + Ctl { + operation my_gate(q : Qubit) : Unit { x(q); } mutable c = [Zero]; From ed328822ea0994e1ac91cd41511a2f25720a1454 Mon Sep 17 00:00:00 2001 From: Oscar Puente Date: Wed, 2 Apr 2025 11:31:18 -0700 Subject: [PATCH 082/108] cargo fmt --- compiler/qsc_qasm3/src/parser/stmt/tests/classical_decl.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/qsc_qasm3/src/parser/stmt/tests/classical_decl.rs b/compiler/qsc_qasm3/src/parser/stmt/tests/classical_decl.rs index f6c15e9267..0b7f4ddb01 100644 --- a/compiler/qsc_qasm3/src/parser/stmt/tests/classical_decl.rs +++ b/compiler/qsc_qasm3/src/parser/stmt/tests/classical_decl.rs @@ -1124,4 +1124,4 @@ fn const_decl_with_measurement_init_fails() { ) "#]], ); -} \ No newline at end of file +} From 7ceed9136c72803451a6893ab25ed630b2f77e8c Mon Sep 17 00:00:00 2001 From: Oscar Puente Date: Wed, 2 Apr 2025 13:16:19 -0700 Subject: [PATCH 083/108] break and continue stmts --- compiler/qsc_qasm3/src/compiler.rs | 12 ++ compiler/qsc_qasm3/src/semantic/ast.rs | 26 ++++ compiler/qsc_qasm3/src/semantic/error.rs | 4 + compiler/qsc_qasm3/src/semantic/lowerer.rs | 33 +++- compiler/qsc_qasm3/src/semantic/symbols.rs | 30 +++- .../src/semantic/tests/statements.rs | 2 + .../semantic/tests/statements/break_stmt.rs | 146 ++++++++++++++++++ .../tests/statements/continue_stmt.rs | 146 ++++++++++++++++++ .../semantic/tests/statements/while_stmt.rs | 57 +++---- 9 files changed, 413 insertions(+), 43 deletions(-) create mode 100644 compiler/qsc_qasm3/src/semantic/tests/statements/break_stmt.rs create mode 100644 compiler/qsc_qasm3/src/semantic/tests/statements/continue_stmt.rs diff --git a/compiler/qsc_qasm3/src/compiler.rs b/compiler/qsc_qasm3/src/compiler.rs index a2ae9b609d..f73e9eb8b0 100644 --- a/compiler/qsc_qasm3/src/compiler.rs +++ b/compiler/qsc_qasm3/src/compiler.rs @@ -400,10 +400,12 @@ impl QasmCompiler { semast::StmtKind::Barrier(stmt) => Self::compile_barrier_stmt(stmt), semast::StmtKind::Box(stmt) => self.compile_box_stmt(stmt), semast::StmtKind::Block(stmt) => self.compile_block_stmt(stmt), + semast::StmtKind::Break(stmt) => self.compile_break_stmt(stmt), semast::StmtKind::CalibrationGrammar(stmt) => { self.compile_calibration_grammar_stmt(stmt) } semast::StmtKind::ClassicalDecl(stmt) => self.compile_classical_decl(stmt), + semast::StmtKind::Continue(stmt) => self.compile_continue_stmt(stmt), semast::StmtKind::Def(def_stmt) => self.compile_def_stmt(def_stmt, &stmt.annotations), semast::StmtKind::DefCal(stmt) => self.compile_def_cal_stmt(stmt), semast::StmtKind::Delay(stmt) => self.compile_delay_stmt(stmt), @@ -586,6 +588,11 @@ impl QasmCompiler { Some(build_stmt_semi_from_expr(build_wrapped_block_expr(block))) } + fn compile_break_stmt(&mut self, stmt: &semast::BreakStmt) -> Option { + self.push_unsupported_error_message("break stmt", stmt.span); + None + } + fn compile_calibration_grammar_stmt( &mut self, stmt: &semast::CalibrationGrammarStmt, @@ -615,6 +622,11 @@ impl QasmCompiler { Some(stmt) } + fn compile_continue_stmt(&mut self, stmt: &semast::ContinueStmt) -> Option { + self.push_unsupported_error_message("continue stmt", stmt.span); + None + } + fn compile_def_stmt( &mut self, stmt: &semast::DefStmt, diff --git a/compiler/qsc_qasm3/src/semantic/ast.rs b/compiler/qsc_qasm3/src/semantic/ast.rs index 625a7e91e5..95218b80d3 100644 --- a/compiler/qsc_qasm3/src/semantic/ast.rs +++ b/compiler/qsc_qasm3/src/semantic/ast.rs @@ -347,8 +347,10 @@ pub enum StmtKind { Barrier(BarrierStmt), Box(BoxStmt), Block(Box), + Break(BreakStmt), CalibrationGrammar(CalibrationGrammarStmt), ClassicalDecl(ClassicalDeclarationStmt), + Continue(ContinueStmt), Def(DefStmt), DefCal(DefCalStmt), Delay(DelayStmt), @@ -384,8 +386,10 @@ impl Display for StmtKind { StmtKind::Barrier(barrier) => write!(f, "{barrier}"), StmtKind::Box(box_stmt) => write!(f, "{box_stmt}"), StmtKind::Block(block) => write!(f, "{block}"), + StmtKind::Break(stmt) => write!(f, "{stmt}"), StmtKind::CalibrationGrammar(grammar) => write!(f, "{grammar}"), StmtKind::ClassicalDecl(decl) => write!(f, "{decl}"), + StmtKind::Continue(stmt) => write!(f, "{stmt}"), StmtKind::Def(def) => write!(f, "{def}"), StmtKind::DefCal(defcal) => write!(f, "{defcal}"), StmtKind::Delay(delay) => write!(f, "{delay}"), @@ -498,6 +502,28 @@ impl Display for Block { } } +#[derive(Clone, Debug, Default)] +pub struct BreakStmt { + pub span: Span, +} + +impl Display for BreakStmt { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + write_header(f, "BreakStmt", self.span) + } +} + +#[derive(Clone, Debug, Default)] +pub struct ContinueStmt { + pub span: Span, +} + +impl Display for ContinueStmt { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + write_header(f, "ContinueStmt", self.span) + } +} + #[derive(Clone, Debug)] pub enum Identifier { Ident(Box), diff --git a/compiler/qsc_qasm3/src/semantic/error.rs b/compiler/qsc_qasm3/src/semantic/error.rs index b9ed712721..e5198ea09f 100644 --- a/compiler/qsc_qasm3/src/semantic/error.rs +++ b/compiler/qsc_qasm3/src/semantic/error.rs @@ -141,6 +141,9 @@ pub enum SemanticErrorKind { #[error("Gate expects {0} qubit arguments, but {1} were provided.")] #[diagnostic(code("Qsc.Qasm3.Compile.InvalidNumberOfQubitArgs"))] InvalidNumberOfQubitArgs(usize, usize, #[label] Span), + #[error("{0} can only appear in {1} scopes.")] + #[diagnostic(code("Qsc.Qasm3.Compile.InvalidScope"))] + InvalidScope(String, String, #[label] Span), #[error("Measure statements must have a name.")] #[diagnostic(code("Qsc.Qasm3.Compile.MeasureExpressionsMustHaveName"))] MeasureExpressionsMustHaveName(#[label] Span), @@ -299,6 +302,7 @@ impl SemanticErrorKind { Self::InconsistentTypesInAlias(name, span) => { Self::InconsistentTypesInAlias(name, span + offset) } + Self::InvalidScope(name, scope, span) => Self::InvalidScope(name, scope, span + offset), Self::IfStmtMissingExpression(name, span) => { Self::IfStmtMissingExpression(name, span + offset) } diff --git a/compiler/qsc_qasm3/src/semantic/lowerer.rs b/compiler/qsc_qasm3/src/semantic/lowerer.rs index d476177763..d8df3ccdc1 100644 --- a/compiler/qsc_qasm3/src/semantic/lowerer.rs +++ b/compiler/qsc_qasm3/src/semantic/lowerer.rs @@ -850,8 +850,16 @@ impl Lowerer { } fn lower_break(&mut self, stmt: &syntax::BreakStmt) -> semantic::StmtKind { - self.push_unimplemented_error_message("break stmt", stmt.span); - semantic::StmtKind::Err + if self.symbols.is_scope_rooted_in_loop_scope() { + semantic::StmtKind::Break(semantic::BreakStmt { span: stmt.span }) + } else { + self.push_semantic_error(SemanticErrorKind::InvalidScope( + "break".into(), + "loop".into(), + stmt.span, + )); + semantic::StmtKind::Err + } } fn lower_block(&mut self, stmt: &syntax::Block) -> semantic::Block { @@ -968,8 +976,16 @@ impl Lowerer { } fn lower_continue_stmt(&mut self, stmt: &syntax::ContinueStmt) -> semantic::StmtKind { - self.push_unimplemented_error_message("continue stmt", stmt.span); - semantic::StmtKind::Err + if self.symbols.is_scope_rooted_in_loop_scope() { + semantic::StmtKind::Continue(semantic::ContinueStmt { span: stmt.span }) + } else { + self.push_semantic_error(SemanticErrorKind::InvalidScope( + "continue".into(), + "loop".into(), + stmt.span, + )); + semantic::StmtKind::Err + } } fn lower_def(&mut self, stmt: &syntax::DefStmt) -> semantic::StmtKind { @@ -1152,7 +1168,7 @@ impl Lowerer { let set_declaration = self.lower_enumerable_set(&stmt.set_declaration); // Push scope where the loop variable lives. - self.symbols.push_scope(ScopeKind::Block); + self.symbols.push_scope(ScopeKind::Loop); let ty = self.get_semantic_type_from_scalar_ty(&stmt.ty, false); let qsharp_ty = self.convert_semantic_type_to_qsharp_type(&ty.clone(), stmt.ty.span); @@ -1748,6 +1764,10 @@ impl Lowerer { } fn lower_while_stmt(&mut self, stmt: &syntax::WhileLoop) -> semantic::StmtKind { + // Push scope where the while loop lives. The while loop needs its own scope + // so that break and continue know if they are inside a valid scope. + self.symbols.push_scope(ScopeKind::Loop); + let condition = self.lower_expr(&stmt.while_condition); let body = self.lower_stmt(&stmt.body); @@ -1756,6 +1776,9 @@ impl Lowerer { let cond_ty = Type::Bool(condition.ty.is_const()); let while_condition = self.cast_expr_to_type(&cond_ty, &condition); + // Pop scope where the while loop lives. + self.symbols.pop_scope(); + semantic::StmtKind::WhileLoop(semantic::WhileLoop { span: stmt.span, condition: while_condition, diff --git a/compiler/qsc_qasm3/src/semantic/symbols.rs b/compiler/qsc_qasm3/src/semantic/symbols.rs index 0da5f644e7..667e96d5af 100644 --- a/compiler/qsc_qasm3/src/semantic/symbols.rs +++ b/compiler/qsc_qasm3/src/semantic/symbols.rs @@ -277,6 +277,7 @@ pub enum ScopeKind { Function, Gate, Block, + Loop, } const BUILTIN_SYMBOLS: [(&str, f64); 6] = [ @@ -424,7 +425,10 @@ impl SymbolTable { { let scopes = self.scopes.iter().rev(); let predicate = |x: &Scope| { - x.kind == ScopeKind::Block || x.kind == ScopeKind::Function || x.kind == ScopeKind::Gate + matches!( + x.kind, + ScopeKind::Block | ScopeKind::Loop | ScopeKind::Function | ScopeKind::Gate + ) }; // Use scan to track the last item that returned false @@ -510,6 +514,30 @@ impl SymbolTable { .any(|scope| matches!(scope.kind, ScopeKind::Gate | ScopeKind::Function)) } + #[must_use] + pub fn is_scope_rooted_in_loop_scope(&self) -> bool { + for scope in self.scopes.iter().rev() { + if matches!(scope.kind, ScopeKind::Loop) { + return true; + } + + // Even though semantically correct qasm3 doesn't allow function + // or gate scopes outside the global scope, the user could write + // incorrect qasm3 while editing. This if statement warns the user + // if they write something like: + // while true { + // def f() { break; } + // } + // + // Note that the `break` in the example will be rooted in a loop + // scope unless we include the following condition. + if matches!(scope.kind, ScopeKind::Function | ScopeKind::Gate) { + return false; + } + } + false + } + #[must_use] pub fn is_scope_rooted_in_global(&self) -> bool { for scope in self.scopes.iter().rev() { diff --git a/compiler/qsc_qasm3/src/semantic/tests/statements.rs b/compiler/qsc_qasm3/src/semantic/tests/statements.rs index eda5cb3db6..c31fcb3141 100644 --- a/compiler/qsc_qasm3/src/semantic/tests/statements.rs +++ b/compiler/qsc_qasm3/src/semantic/tests/statements.rs @@ -2,6 +2,8 @@ // Licensed under the MIT License. mod box_stmt; +mod break_stmt; +mod continue_stmt; mod for_stmt; mod if_stmt; mod switch_stmt; diff --git a/compiler/qsc_qasm3/src/semantic/tests/statements/break_stmt.rs b/compiler/qsc_qasm3/src/semantic/tests/statements/break_stmt.rs new file mode 100644 index 0000000000..d1fbac0a82 --- /dev/null +++ b/compiler/qsc_qasm3/src/semantic/tests/statements/break_stmt.rs @@ -0,0 +1,146 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +use crate::semantic::tests::check_stmt_kinds; +use expect_test::expect; + +#[test] +fn for_loop() { + check_stmt_kinds( + "for int i in {1, 2, 3} break;", + &expect![[r#" + ForStmt [0-29]: + loop_variable: 8 + iterable: DiscreteSet [13-22]: + values: + Expr [14-15]: + ty: Int(None, true) + kind: Lit: Int(1) + Expr [17-18]: + ty: Int(None, true) + kind: Lit: Int(2) + Expr [20-21]: + ty: Int(None, true) + kind: Lit: Int(3) + body: Stmt [23-29]: + annotations: + kind: BreakStmt [23-29]: + "#]], + ); +} + +#[test] +fn while_loop() { + check_stmt_kinds( + "while (true) break;", + &expect![[r#" + WhileLoop [0-19]: + condition: Expr [7-11]: + ty: Bool(true) + kind: Lit: Bool(true) + body: Stmt [13-19]: + annotations: + kind: BreakStmt [13-19]: + "#]], + ); +} + +#[test] +fn nested_scopes() { + check_stmt_kinds( + "while (true) { { break; } }", + &expect![[r#" + WhileLoop [0-27]: + condition: Expr [7-11]: + ty: Bool(true) + kind: Lit: Bool(true) + body: Stmt [13-27]: + annotations: + kind: Block [13-27]: + Stmt [15-25]: + annotations: + kind: Block [15-25]: + Stmt [17-23]: + annotations: + kind: BreakStmt [17-23]: + "#]], + ); +} + +#[test] +fn break_in_non_loop_scope_fails() { + check_stmt_kinds( + "break;", + &expect![[r#" + Program: + version: + statements: + Stmt [0-6]: + annotations: + kind: Err + + [Qsc.Qasm3.Compile.InvalidScope + + x break can only appear in loop scopes. + ,-[test:1:1] + 1 | break; + : ^^^^^^ + `---- + ]"#]], + ); +} + +#[test] +fn intermediate_def_scope_fails() { + check_stmt_kinds( + " + while (true) { + def f() { break; } + } + ", + &expect![[r#" + Program: + version: + statements: + Stmt [9-64]: + annotations: + kind: WhileLoop [9-64]: + condition: Expr [16-20]: + ty: Bool(true) + kind: Lit: Bool(true) + body: Stmt [22-64]: + annotations: + kind: Block [22-64]: + Stmt [36-54]: + annotations: + kind: DefStmt [36-54]: + symbol_id: 8 + has_qubit_params: false + parameters: + return_type: + body: Block [36-54]: + Stmt [46-52]: + annotations: + kind: Err + + [Qsc.Qasm3.Compile.DefDeclarationInNonGlobalScope + + x Def declarations must be done in global scope. + ,-[test:3:13] + 2 | while (true) { + 3 | def f() { break; } + : ^^^^^^^^^^^^^^^^^^ + 4 | } + `---- + , Qsc.Qasm3.Compile.InvalidScope + + x break can only appear in loop scopes. + ,-[test:3:23] + 2 | while (true) { + 3 | def f() { break; } + : ^^^^^^ + 4 | } + `---- + ]"#]], + ); +} diff --git a/compiler/qsc_qasm3/src/semantic/tests/statements/continue_stmt.rs b/compiler/qsc_qasm3/src/semantic/tests/statements/continue_stmt.rs new file mode 100644 index 0000000000..eaeefd1518 --- /dev/null +++ b/compiler/qsc_qasm3/src/semantic/tests/statements/continue_stmt.rs @@ -0,0 +1,146 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +use crate::semantic::tests::check_stmt_kinds; +use expect_test::expect; + +#[test] +fn for_loop() { + check_stmt_kinds( + "for int i in {1, 2, 3} continue;", + &expect![[r#" + ForStmt [0-32]: + loop_variable: 8 + iterable: DiscreteSet [13-22]: + values: + Expr [14-15]: + ty: Int(None, true) + kind: Lit: Int(1) + Expr [17-18]: + ty: Int(None, true) + kind: Lit: Int(2) + Expr [20-21]: + ty: Int(None, true) + kind: Lit: Int(3) + body: Stmt [23-32]: + annotations: + kind: ContinueStmt [23-32]: + "#]], + ); +} + +#[test] +fn while_loop() { + check_stmt_kinds( + "while (true) continue;", + &expect![[r#" + WhileLoop [0-22]: + condition: Expr [7-11]: + ty: Bool(true) + kind: Lit: Bool(true) + body: Stmt [13-22]: + annotations: + kind: ContinueStmt [13-22]: + "#]], + ); +} + +#[test] +fn nested_scopes() { + check_stmt_kinds( + "while (true) { { continue; } }", + &expect![[r#" + WhileLoop [0-30]: + condition: Expr [7-11]: + ty: Bool(true) + kind: Lit: Bool(true) + body: Stmt [13-30]: + annotations: + kind: Block [13-30]: + Stmt [15-28]: + annotations: + kind: Block [15-28]: + Stmt [17-26]: + annotations: + kind: ContinueStmt [17-26]: + "#]], + ); +} + +#[test] +fn continue_in_non_loop_scope_fails() { + check_stmt_kinds( + "continue;", + &expect![[r#" + Program: + version: + statements: + Stmt [0-9]: + annotations: + kind: Err + + [Qsc.Qasm3.Compile.InvalidScope + + x continue can only appear in loop scopes. + ,-[test:1:1] + 1 | continue; + : ^^^^^^^^^ + `---- + ]"#]], + ); +} + +#[test] +fn intermediate_def_scope_fails() { + check_stmt_kinds( + " + while (true) { + def f() { continue; } + } + ", + &expect![[r#" + Program: + version: + statements: + Stmt [9-67]: + annotations: + kind: WhileLoop [9-67]: + condition: Expr [16-20]: + ty: Bool(true) + kind: Lit: Bool(true) + body: Stmt [22-67]: + annotations: + kind: Block [22-67]: + Stmt [36-57]: + annotations: + kind: DefStmt [36-57]: + symbol_id: 8 + has_qubit_params: false + parameters: + return_type: + body: Block [36-57]: + Stmt [46-55]: + annotations: + kind: Err + + [Qsc.Qasm3.Compile.DefDeclarationInNonGlobalScope + + x Def declarations must be done in global scope. + ,-[test:3:13] + 2 | while (true) { + 3 | def f() { continue; } + : ^^^^^^^^^^^^^^^^^^^^^ + 4 | } + `---- + , Qsc.Qasm3.Compile.InvalidScope + + x continue can only appear in loop scopes. + ,-[test:3:23] + 2 | while (true) { + 3 | def f() { continue; } + : ^^^^^^^^^ + 4 | } + `---- + ]"#]], + ); +} diff --git a/compiler/qsc_qasm3/src/semantic/tests/statements/while_stmt.rs b/compiler/qsc_qasm3/src/semantic/tests/statements/while_stmt.rs index 8d926b8974..bf8d92833f 100644 --- a/compiler/qsc_qasm3/src/semantic/tests/statements/while_stmt.rs +++ b/compiler/qsc_qasm3/src/semantic/tests/statements/while_stmt.rs @@ -5,49 +5,32 @@ use crate::semantic::tests::check_stmt_kinds; use expect_test::expect; #[test] -fn single_stmt_body_doesnt_creates_its_own_scope() { +fn single_stmt_body_creates_its_own_scope() { check_stmt_kinds( " int a = 3; while(true) int a = 1; ", &expect![[r#" - Program: - version: - statements: - Stmt [5-15]: - annotations: - kind: ClassicalDeclarationStmt [5-15]: - symbol_id: 8 - ty_span: [5-8] - init_expr: Expr [13-14]: - ty: Int(None, false) - kind: Lit: Int(3) - Stmt [20-42]: - annotations: - kind: WhileLoop [20-42]: - condition: Expr [26-30]: - ty: Bool(true) - kind: Lit: Bool(true) - body: Stmt [32-42]: - annotations: - kind: ClassicalDeclarationStmt [32-42]: - symbol_id: 8 - ty_span: [32-35] - init_expr: Expr [40-41]: - ty: Int(None, false) - kind: Lit: Int(1) - - [Qsc.Qasm3.Compile.RedefinedSymbol - - x Redefined symbol: a. - ,-[test:3:21] - 2 | int a = 3; - 3 | while(true) int a = 1; - : ^ - 4 | - `---- - ]"#]], + ClassicalDeclarationStmt [5-15]: + symbol_id: 8 + ty_span: [5-8] + init_expr: Expr [13-14]: + ty: Int(None, false) + kind: Lit: Int(3) + WhileLoop [20-42]: + condition: Expr [26-30]: + ty: Bool(true) + kind: Lit: Bool(true) + body: Stmt [32-42]: + annotations: + kind: ClassicalDeclarationStmt [32-42]: + symbol_id: 9 + ty_span: [32-35] + init_expr: Expr [40-41]: + ty: Int(None, false) + kind: Lit: Int(1) + "#]], ); } From 681f3f7804d98726f4ccdab2ef6bd6726b6e22a9 Mon Sep 17 00:00:00 2001 From: Oscar Puente Date: Wed, 2 Apr 2025 21:28:45 -0700 Subject: [PATCH 084/108] lower duration literals --- compiler/qsc_qasm3/src/compiler.rs | 2 +- compiler/qsc_qasm3/src/parser/ast.rs | 2 +- compiler/qsc_qasm3/src/semantic/ast.rs | 12 ++++++++++ compiler/qsc_qasm3/src/semantic/lowerer.rs | 24 ++++++++++--------- .../src/semantic/tests/decls/duration.rs | 9 +------ compiler/qsc_qasm3/src/tests/declaration.rs | 24 +++++++++---------- 6 files changed, 40 insertions(+), 33 deletions(-) diff --git a/compiler/qsc_qasm3/src/compiler.rs b/compiler/qsc_qasm3/src/compiler.rs index f73e9eb8b0..5dc517bdcf 100644 --- a/compiler/qsc_qasm3/src/compiler.rs +++ b/compiler/qsc_qasm3/src/compiler.rs @@ -1512,7 +1512,7 @@ impl QasmCompiler { _unit: TimeUnit, span: Span, ) -> qsast::Expr { - self.push_unsupported_error_message("timing literals", span); + self.push_unsupported_error_message("Timing literals", span); err_expr(span) } diff --git a/compiler/qsc_qasm3/src/parser/ast.rs b/compiler/qsc_qasm3/src/parser/ast.rs index 442a13e7aa..0dc85a26c1 100644 --- a/compiler/qsc_qasm3/src/parser/ast.rs +++ b/compiler/qsc_qasm3/src/parser/ast.rs @@ -1744,7 +1744,7 @@ impl From for crate::semantic::symbols::IOKind { } } -#[derive(Clone, Debug)] +#[derive(Clone, Debug, Copy)] pub enum TimeUnit { Dt, /// Nanoseconds. diff --git a/compiler/qsc_qasm3/src/semantic/ast.rs b/compiler/qsc_qasm3/src/semantic/ast.rs index 95218b80d3..23782f7d92 100644 --- a/compiler/qsc_qasm3/src/semantic/ast.rs +++ b/compiler/qsc_qasm3/src/semantic/ast.rs @@ -1755,6 +1755,18 @@ impl Display for TimeUnit { } } +impl From for TimeUnit { + fn from(value: crate::parser::ast::TimeUnit) -> Self { + match value { + syntax::TimeUnit::Dt => Self::Dt, + syntax::TimeUnit::Ns => Self::Ns, + syntax::TimeUnit::Us => Self::Us, + syntax::TimeUnit::Ms => Self::Ms, + syntax::TimeUnit::S => Self::S, + } + } +} + #[derive(Clone, Debug)] pub struct EndStmt { pub span: Span, diff --git a/compiler/qsc_qasm3/src/semantic/lowerer.rs b/compiler/qsc_qasm3/src/semantic/lowerer.rs index d8df3ccdc1..2e4e2d343f 100644 --- a/compiler/qsc_qasm3/src/semantic/lowerer.rs +++ b/compiler/qsc_qasm3/src/semantic/lowerer.rs @@ -602,10 +602,13 @@ impl Lowerer { self.push_unsupported_error_message("String literals", expr.span); (semantic::ExprKind::Err, Type::Err) } - syntax::LiteralKind::Duration(_, _) => { - self.push_unsupported_error_message("Duration literals", expr.span); - (semantic::ExprKind::Err, Type::Err) - } + syntax::LiteralKind::Duration(value, time_unit) => ( + semantic::ExprKind::Lit(semantic::LiteralKind::Duration( + *value, + (*time_unit).into(), + )), + Type::Duration(true), + ), syntax::LiteralKind::Array(exprs) => { // array literals are only valid in classical decals (const and mut) // and we have to know the expected type of the array in order to lower it @@ -2105,6 +2108,10 @@ impl Lowerer { None } }, + Type::Duration(_) => Some(from_lit_kind(LiteralKind::Duration( + 0.0, + semantic::TimeUnit::Ns, + ))), Type::BoolArray(_) => { self.push_unimplemented_error_message("bool array default value", span); None @@ -2133,13 +2140,8 @@ impl Lowerer { self.push_unimplemented_error_message("uint array default value", span); None } - Type::Duration(_) - | Type::Gate(_, _) - | Type::Function(..) - | Type::Range - | Type::Set - | Type::Void => { - let message = format!("Default values for {ty:?} are unsupported."); + Type::Gate(_, _) | Type::Function(..) | Type::Range | Type::Set | Type::Void => { + let message = format!("Default values for {ty:?}"); self.push_unsupported_error_message(message, span); None } diff --git a/compiler/qsc_qasm3/src/semantic/tests/decls/duration.rs b/compiler/qsc_qasm3/src/semantic/tests/decls/duration.rs index 9f8a20da98..56d173ae52 100644 --- a/compiler/qsc_qasm3/src/semantic/tests/decls/duration.rs +++ b/compiler/qsc_qasm3/src/semantic/tests/decls/duration.rs @@ -20,7 +20,7 @@ fn with_no_init_expr_has_generated_lit_expr() { ty_span: [0-8] init_expr: Expr [0-0]: ty: Duration(true) - kind: Err + kind: Lit: Duration(0.0, Ns) [Qsc.Qasm3.Compile.NotSupported @@ -29,13 +29,6 @@ fn with_no_init_expr_has_generated_lit_expr() { 1 | duration a; : ^^^^^^^^ `---- - , Qsc.Qasm3.Compile.NotSupported - - x Default values for Duration(false) are unsupported. are not supported. - ,-[test:1:1] - 1 | duration a; - : ^^^^^^^^^^^ - `---- ]"#]], ); } diff --git a/compiler/qsc_qasm3/src/tests/declaration.rs b/compiler/qsc_qasm3/src/tests/declaration.rs index c23f0af07d..debae1f218 100644 --- a/compiler/qsc_qasm3/src/tests/declaration.rs +++ b/compiler/qsc_qasm3/src/tests/declaration.rs @@ -17,7 +17,6 @@ use crate::{ tests::{compile_fragments, compile_with_config, fail_on_compilation_errors}, CompilerConfig, OutputSemantics, ProgramType, QubitSemantics, }; - use miette::Report; #[test] @@ -65,17 +64,16 @@ fn duration_literal() -> miette::Result<(), Vec> { None, ); let unit = compile_with_config(source, config).expect("parse failed"); - println!("{:?}", unit.errors); - assert_eq!(unit.errors.len(), 5); for error in &unit.errors { - assert!( - error - .to_string() - .contains("Duration type values are not supported.") - || error - .to_string() - .contains("Timing literal expressions are not supported.") - ); + println!("{error}"); + } + assert_eq!(unit.errors.len(), 10); + for error in &unit.errors { + assert!([ + "Duration type values are not supported.", + "Timing literals are not supported.", + ] + .contains(&error.to_string().as_str())); } Ok(()) @@ -96,7 +94,9 @@ fn stretch() { ); let unit = compile_with_config(source, config).expect("parse failed"); assert!(unit.has_errors()); - println!("{:?}", unit.errors); + for error in &unit.errors { + println!("{error}"); + } assert!(unit.errors.len() == 2); assert!(unit.errors[0] .to_string() From 5434022270d80bad7db7b1cc192f185b38b84f19 Mon Sep 17 00:00:00 2001 From: Oscar Puente Date: Wed, 2 Apr 2025 21:55:39 -0700 Subject: [PATCH 085/108] use `Type::Void` instead of `Option::None` to represent no return --- compiler/qsc_qasm3/src/ast_builder.rs | 8 +------- compiler/qsc_qasm3/src/compiler.rs | 6 +++--- compiler/qsc_qasm3/src/semantic/ast.rs | 4 ++-- compiler/qsc_qasm3/src/semantic/lowerer.rs | 15 +++++++-------- .../src/semantic/tests/statements/break_stmt.rs | 2 +- .../semantic/tests/statements/continue_stmt.rs | 2 +- compiler/qsc_qasm3/src/semantic/types.rs | 2 +- 7 files changed, 16 insertions(+), 23 deletions(-) diff --git a/compiler/qsc_qasm3/src/ast_builder.rs b/compiler/qsc_qasm3/src/ast_builder.rs index a4b23ac8a6..e74154743a 100644 --- a/compiler/qsc_qasm3/src/ast_builder.rs +++ b/compiler/qsc_qasm3/src/ast_builder.rs @@ -1689,7 +1689,7 @@ pub(crate) fn build_function_or_operation( name_span: Span, body_span: Span, gate_span: Span, - return_type: Option, + return_type: Ty, kind: CallableKind, functors: Option, attrs: List, @@ -1724,12 +1724,6 @@ pub(crate) fn build_function_or_operation( ..Default::default() }; - let return_type = if let Some(ty) = return_type { - ty - } else { - build_path_ident_ty("Unit") - }; - let body = CallableBody::Block(Box::new(body.unwrap_or_else(|| Block { id: NodeId::default(), span: body_span, diff --git a/compiler/qsc_qasm3/src/compiler.rs b/compiler/qsc_qasm3/src/compiler.rs index 5dc517bdcf..0ef44dba90 100644 --- a/compiler/qsc_qasm3/src/compiler.rs +++ b/compiler/qsc_qasm3/src/compiler.rs @@ -23,7 +23,7 @@ use crate::{ build_lit_bigint_expr, build_lit_bool_expr, build_lit_complex_expr, build_lit_double_expr, build_lit_int_expr, build_lit_result_array_expr_from_bitstring, build_lit_result_expr, build_managed_qubit_alloc, build_math_call_from_exprs, build_math_call_no_params, - build_measure_call, build_operation_with_stmts, build_path_ident_expr, + build_measure_call, build_operation_with_stmts, build_path_ident_expr, build_path_ident_ty, build_qasm_import_decl, build_qasm_import_items, build_range_expr, build_reset_call, build_return_expr, build_return_unit, build_stmt_semi_from_expr, build_stmt_semi_from_expr_with_span, build_top_level_ns_with_items, build_tuple_expr, @@ -651,7 +651,7 @@ impl QasmCompiler { .collect(); let body = Some(self.compile_block(&stmt.body)); - let return_type = stmt.return_type.as_ref().map(map_qsharp_type_to_ast_ty); + let return_type = map_qsharp_type_to_ast_ty(&stmt.return_type); let kind = if stmt.has_qubit_params { qsast::CallableKind::Operation } else { @@ -960,7 +960,7 @@ impl QasmCompiler { symbol.span, stmt.body.span, stmt.span, - None, + build_path_ident_ty("Unit"), qsast::CallableKind::Operation, functors, list_from_iter(attrs), diff --git a/compiler/qsc_qasm3/src/semantic/ast.rs b/compiler/qsc_qasm3/src/semantic/ast.rs index 23782f7d92..c77fff55df 100644 --- a/compiler/qsc_qasm3/src/semantic/ast.rs +++ b/compiler/qsc_qasm3/src/semantic/ast.rs @@ -1291,7 +1291,7 @@ pub struct DefStmt { pub has_qubit_params: bool, pub params: Box<[SymbolId]>, pub body: Block, - pub return_type: Option, + pub return_type: crate::types::Type, } impl Display for DefStmt { @@ -1300,7 +1300,7 @@ impl Display for DefStmt { writeln_field(f, "symbol_id", &self.symbol_id)?; writeln_field(f, "has_qubit_params", &self.has_qubit_params)?; writeln_list_field(f, "parameters", &self.params)?; - writeln_opt_field(f, "return_type", self.return_type.as_ref())?; + writeln_field(f, "return_type", &self.return_type)?; write_field(f, "body", &self.body) } } diff --git a/compiler/qsc_qasm3/src/semantic/lowerer.rs b/compiler/qsc_qasm3/src/semantic/lowerer.rs index 2e4e2d343f..e6ddcd18e8 100644 --- a/compiler/qsc_qasm3/src/semantic/lowerer.rs +++ b/compiler/qsc_qasm3/src/semantic/lowerer.rs @@ -1015,9 +1015,12 @@ impl Lowerer { let tydef = syntax::TypeDef::Scalar(*ty.clone()); let ty = self.get_semantic_type_from_tydef(&tydef, false); let qsharp_ty = self.convert_semantic_type_to_qsharp_type(&ty, ty_span); - (Some(ty), Some(qsharp_ty)) + (ty, qsharp_ty) } else { - (None, None) + ( + crate::semantic::types::Type::Void, + crate::types::Type::Tuple(Default::default()), + ) }; // 2. Push the function symbol to the symbol table. @@ -1025,7 +1028,7 @@ impl Lowerer { let arity = stmt.params.len() as u32; let name = stmt.name.name.clone(); let name_span = stmt.name.span; - let ty = crate::semantic::types::Type::Function(param_types.into(), return_ty.map(Rc::new)); + let ty = crate::semantic::types::Type::Function(param_types.into(), Rc::new(return_ty)); let has_qubit_params = stmt .params @@ -1244,11 +1247,7 @@ impl Lowerer { )); } - if let Some(return_ty) = return_ty { - (params_ty.clone(), (**return_ty).clone()) - } else { - (Rc::default(), crate::semantic::types::Type::Err) - } + (params_ty.clone(), (**return_ty).clone()) } else { self.push_semantic_error(SemanticErrorKind::CannotCallNonFunction(symbol.span)); (Rc::default(), crate::semantic::types::Type::Err) diff --git a/compiler/qsc_qasm3/src/semantic/tests/statements/break_stmt.rs b/compiler/qsc_qasm3/src/semantic/tests/statements/break_stmt.rs index d1fbac0a82..5e3c9fcc3b 100644 --- a/compiler/qsc_qasm3/src/semantic/tests/statements/break_stmt.rs +++ b/compiler/qsc_qasm3/src/semantic/tests/statements/break_stmt.rs @@ -117,7 +117,7 @@ fn intermediate_def_scope_fails() { symbol_id: 8 has_qubit_params: false parameters: - return_type: + return_type: () body: Block [36-54]: Stmt [46-52]: annotations: diff --git a/compiler/qsc_qasm3/src/semantic/tests/statements/continue_stmt.rs b/compiler/qsc_qasm3/src/semantic/tests/statements/continue_stmt.rs index eaeefd1518..7a73aaae27 100644 --- a/compiler/qsc_qasm3/src/semantic/tests/statements/continue_stmt.rs +++ b/compiler/qsc_qasm3/src/semantic/tests/statements/continue_stmt.rs @@ -117,7 +117,7 @@ fn intermediate_def_scope_fails() { symbol_id: 8 has_qubit_params: false parameters: - return_type: + return_type: () body: Block [36-57]: Stmt [46-55]: annotations: diff --git a/compiler/qsc_qasm3/src/semantic/types.rs b/compiler/qsc_qasm3/src/semantic/types.rs index e05dadc203..e4f24d60a7 100644 --- a/compiler/qsc_qasm3/src/semantic/types.rs +++ b/compiler/qsc_qasm3/src/semantic/types.rs @@ -43,7 +43,7 @@ pub enum Type { // realistically the sizes could be u3 Gate(u32, u32), - Function(Rc<[Type]>, Option>), + Function(Rc<[Type]>, Rc), Range, Set, Void, From 0f970854567ed59cafbcd02cf9057391306055e8 Mon Sep 17 00:00:00 2001 From: Oscar Puente Date: Wed, 2 Apr 2025 23:03:22 -0700 Subject: [PATCH 086/108] return stmt casts to the return type of the function --- compiler/qsc_qasm3/src/semantic/error.rs | 12 +++ compiler/qsc_qasm3/src/semantic/lowerer.rs | 41 +++++++- compiler/qsc_qasm3/src/semantic/symbols.rs | 31 +++++-- .../qsc_qasm3/src/tests/declaration/def.rs | 93 +++++++++++++++++++ .../src/tests/expression/function_call.rs | 6 +- 5 files changed, 167 insertions(+), 16 deletions(-) diff --git a/compiler/qsc_qasm3/src/semantic/error.rs b/compiler/qsc_qasm3/src/semantic/error.rs index e5198ea09f..330ca9cb41 100644 --- a/compiler/qsc_qasm3/src/semantic/error.rs +++ b/compiler/qsc_qasm3/src/semantic/error.rs @@ -150,6 +150,9 @@ pub enum SemanticErrorKind { #[error("Measure statements must have a gate operand name.")] #[diagnostic(code("Qsc.Qasm3.Compile.MeasureExpressionsMustHaveGateOperand"))] MeasureExpressionsMustHaveGateOperand(#[label] Span), + #[error("Return statements on a non-void subroutine should have a target expression.")] + #[diagnostic(code("Qsc.Qasm3.Compile.MissingTargetExpressionInReturnStmt"))] + MissingTargetExpressionInReturnStmt(#[label] Span), #[error("Control counts must be postitive integers.")] #[diagnostic(code("Qsc.Qasm3.Compile.NegativeControlCount"))] NegativeControlCount(#[label] Span), @@ -189,6 +192,9 @@ pub enum SemanticErrorKind { #[error("Reset expression must have a name.")] #[diagnostic(code("Qsc.Qasm3.Compile.ResetExpressionMustHaveName"))] ResetExpressionMustHaveName(#[label] Span), + #[error("Cannot return an expression from a void subroutine.")] + #[diagnostic(code("Qsc.Qasm3.Compile.ReturningExpressionFromVoidSubroutine"))] + ReturningExpressionFromVoidSubroutine(#[label] Span), #[error("Return statements are only allowed within subroutines.")] #[diagnostic(code("Qsc.Qasm3.Compile.ReturnNotInSubroutine"))] ReturnNotInSubroutine(#[label] Span), @@ -333,6 +339,9 @@ impl SemanticErrorKind { Self::MeasureExpressionsMustHaveName(span) => { Self::MeasureExpressionsMustHaveName(span + offset) } + Self::MissingTargetExpressionInReturnStmt(span) => { + Self::MissingTargetExpressionInReturnStmt(span + offset) + } Self::NegativeControlCount(span) => Self::NegativeControlCount(span + offset), Self::NotSupported(name, span) => Self::NotSupported(name, span + offset), Self::NotSupportedInThisVersion(name, version, span) => { @@ -366,6 +375,9 @@ impl SemanticErrorKind { Self::ResetExpressionMustHaveName(span) => { Self::ResetExpressionMustHaveName(span + offset) } + Self::ReturningExpressionFromVoidSubroutine(span) => { + Self::ReturningExpressionFromVoidSubroutine(span + offset) + } Self::ReturnNotInSubroutine(span) => Self::ReturnNotInSubroutine(span + offset), Self::SwitchStatementMustHaveAtLeastOneCase(span) => { Self::SwitchStatementMustHaveAtLeastOneCase(span + offset) diff --git a/compiler/qsc_qasm3/src/semantic/lowerer.rs b/compiler/qsc_qasm3/src/semantic/lowerer.rs index e6ddcd18e8..7c56eedf31 100644 --- a/compiler/qsc_qasm3/src/semantic/lowerer.rs +++ b/compiler/qsc_qasm3/src/semantic/lowerer.rs @@ -1015,10 +1015,10 @@ impl Lowerer { let tydef = syntax::TypeDef::Scalar(*ty.clone()); let ty = self.get_semantic_type_from_tydef(&tydef, false); let qsharp_ty = self.convert_semantic_type_to_qsharp_type(&ty, ty_span); - (ty, qsharp_ty) + (Rc::new(ty), qsharp_ty) } else { ( - crate::semantic::types::Type::Void, + Rc::new(crate::semantic::types::Type::Void), crate::types::Type::Tuple(Default::default()), ) }; @@ -1028,7 +1028,7 @@ impl Lowerer { let arity = stmt.params.len() as u32; let name = stmt.name.name.clone(); let name_span = stmt.name.span; - let ty = crate::semantic::types::Type::Function(param_types.into(), Rc::new(return_ty)); + let ty = crate::semantic::types::Type::Function(param_types.into(), return_ty.clone()); let has_qubit_params = stmt .params @@ -1047,7 +1047,7 @@ impl Lowerer { let symbol_id = self.try_insert_or_get_existing_symbol_id(name, symbol); // Push the scope where the def lives. - self.symbols.push_scope(ScopeKind::Function); + self.symbols.push_scope(ScopeKind::Function(return_ty)); let params = param_symbols .into_iter() @@ -1678,7 +1678,7 @@ impl Lowerer { } fn lower_return(&mut self, stmt: &syntax::ReturnStmt) -> semantic::StmtKind { - let expr = stmt + let mut expr = stmt .expr .as_ref() .map(|expr| match &**expr { @@ -1687,6 +1687,37 @@ impl Lowerer { }) .map(Box::new); + let return_ty = self.symbols.get_subroutine_return_ty(); + + match (&mut expr, return_ty) { + // If we don't have a return type then we are not rooted in a subroutine scope. + (_, None) => { + self.push_semantic_error(SemanticErrorKind::InvalidScope( + "Return statements".into(), + "subroutine".into(), + stmt.span, + )); + return semantic::StmtKind::Err; + } + (None, Some(ty)) => { + if !matches!(ty.as_ref(), Type::Void) { + self.push_semantic_error( + SemanticErrorKind::MissingTargetExpressionInReturnStmt(stmt.span), + ); + return semantic::StmtKind::Err; + } + } + (Some(expr), Some(ty)) => { + if matches!(ty.as_ref(), Type::Void) { + self.push_semantic_error( + SemanticErrorKind::ReturningExpressionFromVoidSubroutine(expr.span), + ); + return semantic::StmtKind::Err; + } + *expr = Box::new(self.cast_expr_to_type(&ty, expr)) + } + } + semantic::StmtKind::Return(semantic::ReturnStmt { span: stmt.span, expr, diff --git a/compiler/qsc_qasm3/src/semantic/symbols.rs b/compiler/qsc_qasm3/src/semantic/symbols.rs index 667e96d5af..0fbb4081e3 100644 --- a/compiler/qsc_qasm3/src/semantic/symbols.rs +++ b/compiler/qsc_qasm3/src/semantic/symbols.rs @@ -274,7 +274,9 @@ pub enum ScopeKind { /// Global scope, which is the current scope only when no other scopes are active. /// This is the only scope where gates, qubits, and arrays can be declared. Global, - Function, + /// Function scopes need to remember their return type, so that `return` stmts + /// can do an implicit cast to the correct type, if any; + Function(Rc), Gate, Block, Loop, @@ -427,7 +429,7 @@ impl SymbolTable { let predicate = |x: &Scope| { matches!( x.kind, - ScopeKind::Block | ScopeKind::Loop | ScopeKind::Function | ScopeKind::Gate + ScopeKind::Block | ScopeKind::Loop | ScopeKind::Function(..) | ScopeKind::Gate ) }; @@ -485,7 +487,7 @@ impl SymbolTable { } if matches!( scope.kind, - ScopeKind::Gate | ScopeKind::Function | ScopeKind::Global + ScopeKind::Gate | ScopeKind::Function(..) | ScopeKind::Global ) { return true; } @@ -503,7 +505,19 @@ impl SymbolTable { self.scopes .iter() .rev() - .any(|scope| scope.kind == ScopeKind::Function) + .any(|scope| matches!(scope.kind, ScopeKind::Function(..))) + } + + /// Returns `None` if the current scope is not rooted in a subroutine. + /// Otherwise, returns the return type of the subroutine. + #[must_use] + pub fn get_subroutine_return_ty(&self) -> Option> { + for scope in self.scopes.iter().rev() { + if let ScopeKind::Function(return_ty) = &scope.kind { + return Some(return_ty.clone()); + } + } + None } #[must_use] @@ -511,7 +525,7 @@ impl SymbolTable { self.scopes .iter() .rev() - .any(|scope| matches!(scope.kind, ScopeKind::Gate | ScopeKind::Function)) + .any(|scope| matches!(scope.kind, ScopeKind::Gate | ScopeKind::Function(..))) } #[must_use] @@ -531,7 +545,7 @@ impl SymbolTable { // // Note that the `break` in the example will be rooted in a loop // scope unless we include the following condition. - if matches!(scope.kind, ScopeKind::Function | ScopeKind::Gate) { + if matches!(scope.kind, ScopeKind::Function(..) | ScopeKind::Gate) { return false; } } @@ -541,10 +555,7 @@ impl SymbolTable { #[must_use] pub fn is_scope_rooted_in_global(&self) -> bool { for scope in self.scopes.iter().rev() { - if scope.kind == ScopeKind::Function { - return false; - } - if scope.kind == ScopeKind::Gate { + if matches!(scope.kind, ScopeKind::Function(..) | ScopeKind::Gate) { return false; } } diff --git a/compiler/qsc_qasm3/src/tests/declaration/def.rs b/compiler/qsc_qasm3/src/tests/declaration/def.rs index 6f30a22b97..d4947bb9cd 100644 --- a/compiler/qsc_qasm3/src/tests/declaration/def.rs +++ b/compiler/qsc_qasm3/src/tests/declaration/def.rs @@ -70,3 +70,96 @@ fn qubit_array_parameter() -> miette::Result<(), Vec> { .assert_eq(&qsharp); Ok(()) } + +#[test] +fn implicit_cast_to_function_return_type() -> miette::Result<(), Vec> { + let source = r#" + def square(int a) -> bit { + return a; + } + "#; + + let qsharp = compile_qasm_stmt_to_qsharp(source)?; + expect![[r#" + function square(a : Int) : Result { + return if a == 0 { + One + } else { + Zero + }; + } + "#]] + .assert_eq(&qsharp); + Ok(()) +} + +#[test] +fn return_from_void_function() -> miette::Result<(), Vec> { + let source = r#" + def square(int a) { + return; + } + "#; + + let qsharp = compile_qasm_stmt_to_qsharp(source)?; + expect![[r#" + function square(a : Int) : Unit { + return (); + } + "#]] + .assert_eq(&qsharp); + Ok(()) +} + +#[test] +fn return_expr_on_void_function_fails() { + let source = r#" + def square(int val) { + return val; + } + "#; + + let Err(errors) = compile_qasm_stmt_to_qsharp(source) else { + panic!("Expected error"); + }; + + expect![[r#" + [Qsc.Qasm3.Compile.ReturningExpressionFromVoidSubroutine + + x Cannot return an expression from a void subroutine. + ,-[Test.qasm:3:20] + 2 | def square(int val) { + 3 | return val; + : ^^^ + 4 | } + `---- + ]"#]] + .assert_eq(&format!("{errors:?}")); +} + +#[test] +fn missing_return_expr_on_non_void_function_fails() { + let source = r#" + def square(int a) -> bit { + return; + } + "#; + + let Err(errors) = compile_qasm_stmt_to_qsharp(source) else { + panic!("Expected error"); + }; + + expect![[r#" + [Qsc.Qasm3.Compile.MissingTargetExpressionInReturnStmt + + x Return statements on a non-void subroutine should have a target + | expression. + ,-[Test.qasm:3:13] + 2 | def square(int a) -> bit { + 3 | return; + : ^^^^^^^ + 4 | } + `---- + ]"#]] + .assert_eq(&format!("{errors:?}")); +} diff --git a/compiler/qsc_qasm3/src/tests/expression/function_call.rs b/compiler/qsc_qasm3/src/tests/expression/function_call.rs index f31582c480..1599bf4d13 100644 --- a/compiler/qsc_qasm3/src/tests/expression/function_call.rs +++ b/compiler/qsc_qasm3/src/tests/expression/function_call.rs @@ -277,7 +277,11 @@ fn funcall_implicit_arg_cast_uint_to_bitarray() -> miette::Result<(), Vec Date: Wed, 2 Apr 2025 23:23:31 -0700 Subject: [PATCH 087/108] fix clippy warning --- compiler/qsc_qasm3/src/semantic/lowerer.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/qsc_qasm3/src/semantic/lowerer.rs b/compiler/qsc_qasm3/src/semantic/lowerer.rs index 7c56eedf31..3cc4e1a6e8 100644 --- a/compiler/qsc_qasm3/src/semantic/lowerer.rs +++ b/compiler/qsc_qasm3/src/semantic/lowerer.rs @@ -1714,7 +1714,7 @@ impl Lowerer { ); return semantic::StmtKind::Err; } - *expr = Box::new(self.cast_expr_to_type(&ty, expr)) + *expr = Box::new(self.cast_expr_to_type(&ty, expr)); } } From 21917ea89c41399d5b1cda323baa4a718042cf77 Mon Sep 17 00:00:00 2001 From: Oscar Puente Date: Thu, 3 Apr 2025 08:04:10 -0700 Subject: [PATCH 088/108] add assign_op complex tests --- .../tests/expression/binary/complex.rs | 280 ++++++++++++++---- .../src/tests/expression/binary/complex.rs | 20 +- 2 files changed, 227 insertions(+), 73 deletions(-) diff --git a/compiler/qsc_qasm3/src/semantic/tests/expression/binary/complex.rs b/compiler/qsc_qasm3/src/semantic/tests/expression/binary/complex.rs index 49c7d68b3c..d070cbb067 100644 --- a/compiler/qsc_qasm3/src/semantic/tests/expression/binary/complex.rs +++ b/compiler/qsc_qasm3/src/semantic/tests/expression/binary/complex.rs @@ -1,16 +1,46 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. -use expect_test::expect; - use crate::semantic::tests::check_stmt_kinds; +use expect_test::expect; #[test] -fn subtraction() { +fn addition() { let input = " input complex[float] a; input complex[float] b; - complex x = (a - b); + complex x = a + b; + "; + + check_stmt_kinds( + input, + &expect![[r#" + InputDeclaration [9-32]: + symbol_id: 8 + InputDeclaration [41-64]: + symbol_id: 9 + ClassicalDeclarationStmt [73-91]: + symbol_id: 10 + ty_span: [73-80] + init_expr: Expr [85-90]: + ty: Complex(None, false) + kind: BinaryOpExpr: + op: Add + lhs: Expr [85-86]: + ty: Complex(None, false) + kind: SymbolId(8) + rhs: Expr [89-90]: + ty: Complex(None, false) + kind: SymbolId(9) + "#]], + ); +} + +fn addition_assign_op() { + let input = " + input complex[float] a; + complex x = 0.0; + x += a; "; check_stmt_kinds( @@ -28,7 +58,7 @@ fn subtraction() { kind: Paren Expr [86-91]: ty: Complex(None, false) kind: BinaryOpExpr: - op: Sub + op: Add lhs: Expr [86-87]: ty: Complex(None, false) kind: SymbolId(8) @@ -40,36 +70,67 @@ fn subtraction() { } #[test] -fn addition() { +fn subtraction() { let input = " input complex[float] a; input complex[float] b; - complex x = (a + b); + complex x = a - b; "; check_stmt_kinds( input, &expect![[r#" - InputDeclaration [9-32]: - symbol_id: 8 - InputDeclaration [41-64]: - symbol_id: 9 - ClassicalDeclarationStmt [73-93]: - symbol_id: 10 - ty_span: [73-80] - init_expr: Expr [85-92]: - ty: Complex(None, false) - kind: Paren Expr [86-91]: + InputDeclaration [9-32]: + symbol_id: 8 + InputDeclaration [41-64]: + symbol_id: 9 + ClassicalDeclarationStmt [73-91]: + symbol_id: 10 + ty_span: [73-80] + init_expr: Expr [85-90]: ty: Complex(None, false) kind: BinaryOpExpr: - op: Add - lhs: Expr [86-87]: + op: Sub + lhs: Expr [85-86]: ty: Complex(None, false) kind: SymbolId(8) - rhs: Expr [90-91]: + rhs: Expr [89-90]: ty: Complex(None, false) kind: SymbolId(9) - "#]], + "#]], + ); +} + +#[test] +fn subtraction_assign_op() { + let input = " + input complex[float] a; + complex x = 0.0; + x -= a; + "; + + check_stmt_kinds( + input, + &expect![[r#" + InputDeclaration [9-32]: + symbol_id: 8 + ClassicalDeclarationStmt [41-57]: + symbol_id: 9 + ty_span: [41-48] + init_expr: Expr [53-56]: + ty: Complex(None, true) + kind: Lit: Complex(0.0, 0.0) + AssignOpStmt [66-73]: + symbol_id: 9 + indices: + op: Sub + lhs: Expr [71-72]: + ty: Complex(None, false) + kind: SymbolId(8) + rhs: Expr [71-72]: + ty: Complex(None, false) + kind: SymbolId(8) + "#]], ); } @@ -78,32 +139,63 @@ fn multiplication() { let input = " input complex[float] a; input complex[float] b; - complex x = (a * b); + complex x = a * b; "; check_stmt_kinds( input, &expect![[r#" - InputDeclaration [9-32]: - symbol_id: 8 - InputDeclaration [41-64]: - symbol_id: 9 - ClassicalDeclarationStmt [73-93]: - symbol_id: 10 - ty_span: [73-80] - init_expr: Expr [85-92]: - ty: Complex(None, false) - kind: Paren Expr [86-91]: + InputDeclaration [9-32]: + symbol_id: 8 + InputDeclaration [41-64]: + symbol_id: 9 + ClassicalDeclarationStmt [73-91]: + symbol_id: 10 + ty_span: [73-80] + init_expr: Expr [85-90]: ty: Complex(None, false) kind: BinaryOpExpr: op: Mul - lhs: Expr [86-87]: + lhs: Expr [85-86]: ty: Complex(None, false) kind: SymbolId(8) - rhs: Expr [90-91]: + rhs: Expr [89-90]: ty: Complex(None, false) kind: SymbolId(9) - "#]], + "#]], + ); +} + +#[test] +fn multiplication_assign_op() { + let input = " + input complex[float] a; + complex x = 0.0; + x *= a; + "; + + check_stmt_kinds( + input, + &expect![[r#" + InputDeclaration [9-32]: + symbol_id: 8 + ClassicalDeclarationStmt [41-57]: + symbol_id: 9 + ty_span: [41-48] + init_expr: Expr [53-56]: + ty: Complex(None, true) + kind: Lit: Complex(0.0, 0.0) + AssignOpStmt [66-73]: + symbol_id: 9 + indices: + op: Mul + lhs: Expr [71-72]: + ty: Complex(None, false) + kind: SymbolId(8) + rhs: Expr [71-72]: + ty: Complex(None, false) + kind: SymbolId(8) + "#]], ); } @@ -112,32 +204,63 @@ fn division() { let input = " input complex[float] a; input complex[float] b; - complex x = (a / b); + complex x = a / b; "; check_stmt_kinds( input, &expect![[r#" - InputDeclaration [9-32]: - symbol_id: 8 - InputDeclaration [41-64]: - symbol_id: 9 - ClassicalDeclarationStmt [73-93]: - symbol_id: 10 - ty_span: [73-80] - init_expr: Expr [85-92]: - ty: Complex(None, false) - kind: Paren Expr [86-91]: + InputDeclaration [9-32]: + symbol_id: 8 + InputDeclaration [41-64]: + symbol_id: 9 + ClassicalDeclarationStmt [73-91]: + symbol_id: 10 + ty_span: [73-80] + init_expr: Expr [85-90]: ty: Complex(None, false) kind: BinaryOpExpr: op: Div - lhs: Expr [86-87]: + lhs: Expr [85-86]: ty: Complex(None, false) kind: SymbolId(8) - rhs: Expr [90-91]: + rhs: Expr [89-90]: ty: Complex(None, false) kind: SymbolId(9) - "#]], + "#]], + ); +} + +#[test] +fn division_assign_op() { + let input = " + input complex[float] a; + complex x = 0.0; + x /= a; + "; + + check_stmt_kinds( + input, + &expect![[r#" + InputDeclaration [9-32]: + symbol_id: 8 + ClassicalDeclarationStmt [41-57]: + symbol_id: 9 + ty_span: [41-48] + init_expr: Expr [53-56]: + ty: Complex(None, true) + kind: Lit: Complex(0.0, 0.0) + AssignOpStmt [66-73]: + symbol_id: 9 + indices: + op: Div + lhs: Expr [71-72]: + ty: Complex(None, false) + kind: SymbolId(8) + rhs: Expr [71-72]: + ty: Complex(None, false) + kind: SymbolId(8) + "#]], ); } @@ -146,31 +269,62 @@ fn power() { let input = " input complex[float] a; input complex[float] b; - complex x = (a ** b); + complex x = a ** b; "; check_stmt_kinds( input, &expect![[r#" - InputDeclaration [9-32]: - symbol_id: 8 - InputDeclaration [41-64]: - symbol_id: 9 - ClassicalDeclarationStmt [73-94]: - symbol_id: 10 - ty_span: [73-80] - init_expr: Expr [85-93]: - ty: Complex(None, false) - kind: Paren Expr [86-92]: + InputDeclaration [9-32]: + symbol_id: 8 + InputDeclaration [41-64]: + symbol_id: 9 + ClassicalDeclarationStmt [73-92]: + symbol_id: 10 + ty_span: [73-80] + init_expr: Expr [85-91]: ty: Complex(None, false) kind: BinaryOpExpr: op: Exp - lhs: Expr [86-87]: + lhs: Expr [85-86]: ty: Complex(None, false) kind: SymbolId(8) - rhs: Expr [91-92]: + rhs: Expr [90-91]: ty: Complex(None, false) kind: SymbolId(9) - "#]], + "#]], + ); +} + +#[test] +fn power_assign_op() { + let input = " + input complex[float] a; + complex x = 0.0; + x **= a; + "; + + check_stmt_kinds( + input, + &expect![[r#" + InputDeclaration [9-32]: + symbol_id: 8 + ClassicalDeclarationStmt [41-57]: + symbol_id: 9 + ty_span: [41-48] + init_expr: Expr [53-56]: + ty: Complex(None, true) + kind: Lit: Complex(0.0, 0.0) + AssignOpStmt [66-74]: + symbol_id: 9 + indices: + op: Exp + lhs: Expr [72-73]: + ty: Complex(None, false) + kind: SymbolId(8) + rhs: Expr [72-73]: + ty: Complex(None, false) + kind: SymbolId(8) + "#]], ); } diff --git a/compiler/qsc_qasm3/src/tests/expression/binary/complex.rs b/compiler/qsc_qasm3/src/tests/expression/binary/complex.rs index fb70121884..00366c153a 100644 --- a/compiler/qsc_qasm3/src/tests/expression/binary/complex.rs +++ b/compiler/qsc_qasm3/src/tests/expression/binary/complex.rs @@ -10,12 +10,12 @@ fn addition() -> miette::Result<(), Vec> { let source = " input complex[float] a; input complex[float] b; - complex x = (a + b); + complex x = a + b; "; let qsharp = compile_qasm_stmt_to_qsharp(source)?; expect![[r#" - mutable x = (Microsoft.Quantum.Math.PlusC(a, b)); + mutable x = Microsoft.Quantum.Math.PlusC(a, b); "#]] .assert_eq(&qsharp); Ok(()) @@ -42,12 +42,12 @@ fn subtraction() -> miette::Result<(), Vec> { let source = " input complex[float] a; input complex[float] b; - complex x = (a - b); + complex x = a - b; "; let qsharp = compile_qasm_stmt_to_qsharp(source)?; expect![[r#" - mutable x = (Microsoft.Quantum.Math.MinusC(a, b)); + mutable x = Microsoft.Quantum.Math.MinusC(a, b); "#]] .assert_eq(&qsharp); Ok(()) @@ -74,12 +74,12 @@ fn multiplication() -> miette::Result<(), Vec> { let source = " input complex[float] a; input complex[float] b; - complex x = (a * b); + complex x = a * b; "; let qsharp = compile_qasm_stmt_to_qsharp(source)?; expect![[r#" - mutable x = (Microsoft.Quantum.Math.TimesC(a, b)); + mutable x = Microsoft.Quantum.Math.TimesC(a, b); "#]] .assert_eq(&qsharp); Ok(()) @@ -106,12 +106,12 @@ fn division() -> miette::Result<(), Vec> { let source = " input complex[float] a; input complex[float] b; - complex x = (a / b); + complex x = a / b; "; let qsharp = compile_qasm_stmt_to_qsharp(source)?; expect![[r#" - mutable x = (Microsoft.Quantum.Math.DividedByC(a, b)); + mutable x = Microsoft.Quantum.Math.DividedByC(a, b); "#]] .assert_eq(&qsharp); Ok(()) @@ -138,12 +138,12 @@ fn power() -> miette::Result<(), Vec> { let source = " input complex[float] a; input complex[float] b; - complex x = (a ** b); + complex x = a ** b; "; let qsharp = compile_qasm_stmt_to_qsharp(source)?; expect![[r#" - mutable x = (Microsoft.Quantum.Math.PowC(a, b)); + mutable x = Microsoft.Quantum.Math.PowC(a, b); "#]] .assert_eq(&qsharp); Ok(()) From c68748b172f1b96bb8c2bcf1c37139e15958a4c0 Mon Sep 17 00:00:00 2001 From: Oscar Puente Date: Thu, 3 Apr 2025 09:04:46 -0700 Subject: [PATCH 089/108] mark assigning_uint_to_negative_lit_results_in_semantic_error as ignore --- compiler/qsc_qasm3/src/tests/declaration/unsigned_integer.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/compiler/qsc_qasm3/src/tests/declaration/unsigned_integer.rs b/compiler/qsc_qasm3/src/tests/declaration/unsigned_integer.rs index 87b40a8158..e35fe8a539 100644 --- a/compiler/qsc_qasm3/src/tests/declaration/unsigned_integer.rs +++ b/compiler/qsc_qasm3/src/tests/declaration/unsigned_integer.rs @@ -231,6 +231,7 @@ fn explicit_bitness_int_decl() -> miette::Result<(), Vec> { } #[test] +#[ignore = "not implemented"] fn assigning_uint_to_negative_lit_results_in_semantic_error() { let source = " const uint[10] x = -42; From d0b1424bf78eb57632c25e32f02e0e973dad8d9c Mon Sep 17 00:00:00 2001 From: Oscar Puente Date: Thu, 3 Apr 2025 14:42:45 -0700 Subject: [PATCH 090/108] lower extern --- compiler/qsc_qasm3/src/parser/ast.rs | 14 +++-- compiler/qsc_qasm3/src/parser/mut_visit.rs | 4 -- compiler/qsc_qasm3/src/semantic/ast.rs | 10 ++-- compiler/qsc_qasm3/src/semantic/lowerer.rs | 63 +++++++++++++++++++++- 4 files changed, 75 insertions(+), 16 deletions(-) diff --git a/compiler/qsc_qasm3/src/parser/ast.rs b/compiler/qsc_qasm3/src/parser/ast.rs index 0dc85a26c1..6f974e5180 100644 --- a/compiler/qsc_qasm3/src/parser/ast.rs +++ b/compiler/qsc_qasm3/src/parser/ast.rs @@ -711,19 +711,24 @@ impl Display for ClassicalArgument { #[derive(Clone, Debug)] pub enum ExternParameter { ArrayReference(ArrayReferenceType, Span), - Quantum(Option, Span), Scalar(ScalarType, Span), } +impl ExternParameter { + #[must_use] + pub fn span(&self) -> Span { + match self { + ExternParameter::ArrayReference(_, span) | ExternParameter::Scalar(_, span) => *span, + } + } +} + impl Display for ExternParameter { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { match self { ExternParameter::Scalar(ty, span) => { write!(f, "{span}: {ty}") } - ExternParameter::Quantum(expr, span) => { - write!(f, "{span}: {expr:?}") - } ExternParameter::ArrayReference(ty, span) => { write!(f, "{span}: {ty}") } @@ -741,7 +746,6 @@ impl WithSpan for ExternParameter { fn with_span(self, span: Span) -> Self { match self { ExternParameter::Scalar(ty, _) => ExternParameter::Scalar(ty, span), - ExternParameter::Quantum(expr, _) => ExternParameter::Quantum(expr, span), ExternParameter::ArrayReference(ty, _) => ExternParameter::ArrayReference(ty, span), } } diff --git a/compiler/qsc_qasm3/src/parser/mut_visit.rs b/compiler/qsc_qasm3/src/parser/mut_visit.rs index f14543d1ad..e848076e1a 100644 --- a/compiler/qsc_qasm3/src/parser/mut_visit.rs +++ b/compiler/qsc_qasm3/src/parser/mut_visit.rs @@ -801,10 +801,6 @@ pub fn walk_extern_parameter(vis: &mut impl MutVisitor, param: &mut ExternParame vis.visit_span(span); vis.visit_array_ref_type(ty); } - ExternParameter::Quantum(expr, span) => { - vis.visit_span(span); - expr.iter_mut().for_each(|expr| vis.visit_expr(expr)); - } ExternParameter::Scalar(ty, span) => { vis.visit_span(span); vis.visit_scalar_type(ty); diff --git a/compiler/qsc_qasm3/src/semantic/ast.rs b/compiler/qsc_qasm3/src/semantic/ast.rs index c77fff55df..e044269351 100644 --- a/compiler/qsc_qasm3/src/semantic/ast.rs +++ b/compiler/qsc_qasm3/src/semantic/ast.rs @@ -1087,17 +1087,17 @@ impl Display for QuantumGateDefinition { #[derive(Clone, Debug)] pub struct ExternDecl { pub span: Span, - pub ident: Box, - pub params: List, - pub return_type: Option, + pub symbol_id: SymbolId, + pub params: Box<[crate::types::Type]>, + pub return_type: crate::types::Type, } impl Display for ExternDecl { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { writeln_header(f, "ExternDecl", self.span)?; - writeln_field(f, "ident", &self.ident)?; + writeln_field(f, "symbol_id", &self.symbol_id)?; writeln_list_field(f, "parameters", &self.params)?; - write_opt_field(f, "return_type", self.return_type.as_ref()) + write_field(f, "return_type", &self.return_type) } } diff --git a/compiler/qsc_qasm3/src/semantic/lowerer.rs b/compiler/qsc_qasm3/src/semantic/lowerer.rs index 3cc4e1a6e8..3ea32e5076 100644 --- a/compiler/qsc_qasm3/src/semantic/lowerer.rs +++ b/compiler/qsc_qasm3/src/semantic/lowerer.rs @@ -1166,8 +1166,67 @@ impl Lowerer { } fn lower_extern(&mut self, stmt: &syntax::ExternDecl) -> semantic::StmtKind { - self.push_unimplemented_error_message("extern stmt", stmt.span); - semantic::StmtKind::Err + // 1. Check that we are in the global scope. QASM3 semantics + // only allow extern declarations in the global scope. + if !self.symbols.is_current_scope_global() { + let kind = SemanticErrorKind::DefDeclarationInNonGlobalScope(stmt.span); + self.push_semantic_error(kind); + } + + // 2. Build the parameter's type. + let mut params = Vec::with_capacity(stmt.params.len()); + let mut qsharp_params = Vec::with_capacity(stmt.params.len()); + + for param in &stmt.params { + let ty = self.lower_extern_param(param); + let qsharp_ty = self.convert_semantic_type_to_qsharp_type(&ty, param.span()); + params.push(ty); + qsharp_params.push(qsharp_ty); + } + + // 2. Build the return type. + let (return_ty, qsharp_return_ty) = if let Some(ty) = &stmt.return_type { + let ty_span = ty.span; + let tydef = syntax::TypeDef::Scalar(ty.clone()); + let ty = self.get_semantic_type_from_tydef(&tydef, false); + let qsharp_ty = self.convert_semantic_type_to_qsharp_type(&ty, ty_span); + (Rc::new(ty), qsharp_ty) + } else { + ( + Rc::new(crate::semantic::types::Type::Void), + crate::types::Type::Tuple(Default::default()), + ) + }; + + // 3. Push the extern symbol to the symbol table. + #[allow(clippy::cast_possible_truncation)] + let arity = stmt.params.len() as u32; + let name = stmt.ident.name.clone(); + let name_span = stmt.ident.span; + let ty = crate::semantic::types::Type::Function(params.into(), return_ty.clone()); + let kind = crate::types::CallableKind::Function; + let qsharp_ty = crate::types::Type::Callable(kind, arity, 0); + let symbol = Symbol::new(&name, name_span, ty, qsharp_ty, IOKind::Default); + let symbol_id = self.try_insert_or_get_existing_symbol_id(name, symbol); + + semantic::StmtKind::ExternDecl(semantic::ExternDecl { + span: stmt.span, + symbol_id, + params: qsharp_params.into(), + return_type: qsharp_return_ty, + }) + } + + fn lower_extern_param(&mut self, param: &syntax::ExternParameter) -> Type { + let tydef = match param { + syntax::ExternParameter::ArrayReference(array_reference_type, _) => { + syntax::TypeDef::ArrayReference(array_reference_type.clone()) + } + syntax::ExternParameter::Scalar(scalar_type, _) => { + syntax::TypeDef::Scalar(scalar_type.clone()) + } + }; + self.get_semantic_type_from_tydef(&tydef, false) } fn lower_for_stmt(&mut self, stmt: &syntax::ForStmt) -> semantic::StmtKind { From 59f1650e5fd1d27b82c9e6832ab286cdbce3ed07 Mon Sep 17 00:00:00 2001 From: Oscar Puente Date: Thu, 3 Apr 2025 15:08:38 -0700 Subject: [PATCH 091/108] extern unit tests --- compiler/qsc_qasm3/src/semantic/error.rs | 6 ++ compiler/qsc_qasm3/src/semantic/lowerer.rs | 2 +- .../qsc_qasm3/src/semantic/tests/decls.rs | 1 + .../src/semantic/tests/decls/extern_decl.rs | 88 +++++++++++++++++++ 4 files changed, 96 insertions(+), 1 deletion(-) create mode 100644 compiler/qsc_qasm3/src/semantic/tests/decls/extern_decl.rs diff --git a/compiler/qsc_qasm3/src/semantic/error.rs b/compiler/qsc_qasm3/src/semantic/error.rs index 330ca9cb41..ccbe28a2a6 100644 --- a/compiler/qsc_qasm3/src/semantic/error.rs +++ b/compiler/qsc_qasm3/src/semantic/error.rs @@ -93,6 +93,9 @@ pub enum SemanticErrorKind { #[error("Designator is too large.")] #[diagnostic(code("Qsc.Qasm3.Compile.DesignatorTooLarge"))] DesignatorTooLarge(#[label] Span), + #[error("Extern declarations must be done in global scope.")] + #[diagnostic(code("Qsc.Qasm3.Compile.DefDeclarationInNonGlobalScope"))] + ExternDeclarationInNonGlobalScope(#[label] Span), #[error("Failed to compile all expressions in expression list.")] #[diagnostic(code("Qsc.Qasm3.Compile.FailedToCompileExpressionList"))] FailedToCompileExpressionList(#[label] Span), @@ -296,6 +299,9 @@ impl SemanticErrorKind { Self::DefDeclarationInNonGlobalScope(span + offset) } Self::DesignatorTooLarge(span) => Self::DesignatorTooLarge(span + offset), + Self::ExternDeclarationInNonGlobalScope(span) => { + Self::ExternDeclarationInNonGlobalScope(span + offset) + } Self::FailedToCompileExpressionList(span) => { Self::FailedToCompileExpressionList(span + offset) } diff --git a/compiler/qsc_qasm3/src/semantic/lowerer.rs b/compiler/qsc_qasm3/src/semantic/lowerer.rs index 3ea32e5076..c73076fbaa 100644 --- a/compiler/qsc_qasm3/src/semantic/lowerer.rs +++ b/compiler/qsc_qasm3/src/semantic/lowerer.rs @@ -1169,7 +1169,7 @@ impl Lowerer { // 1. Check that we are in the global scope. QASM3 semantics // only allow extern declarations in the global scope. if !self.symbols.is_current_scope_global() { - let kind = SemanticErrorKind::DefDeclarationInNonGlobalScope(stmt.span); + let kind = SemanticErrorKind::ExternDeclarationInNonGlobalScope(stmt.span); self.push_semantic_error(kind); } diff --git a/compiler/qsc_qasm3/src/semantic/tests/decls.rs b/compiler/qsc_qasm3/src/semantic/tests/decls.rs index 94629a0e1a..1240c0c4d8 100644 --- a/compiler/qsc_qasm3/src/semantic/tests/decls.rs +++ b/compiler/qsc_qasm3/src/semantic/tests/decls.rs @@ -7,6 +7,7 @@ mod bool; mod complex; mod creg; mod duration; +mod extern_decl; mod float; mod int; mod qreg; diff --git a/compiler/qsc_qasm3/src/semantic/tests/decls/extern_decl.rs b/compiler/qsc_qasm3/src/semantic/tests/decls/extern_decl.rs new file mode 100644 index 0000000000..ddf10d85b8 --- /dev/null +++ b/compiler/qsc_qasm3/src/semantic/tests/decls/extern_decl.rs @@ -0,0 +1,88 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +use crate::semantic::tests::check_stmt_kind; +use expect_test::expect; + +#[test] +fn void_no_args() { + check_stmt_kind( + "extern f();", + &expect![[r#" + ExternDecl [0-11]: + symbol_id: 8 + parameters: + return_type: ()"#]], + ); +} + +#[test] +fn void_one_arg() { + check_stmt_kind( + "extern f(int);", + &expect![[r#" + ExternDecl [0-14]: + symbol_id: 8 + parameters: + Int + return_type: ()"#]], + ); +} + +#[test] +fn void_multiple_args() { + check_stmt_kind( + "extern f(uint, int, float, bit, bool);", + &expect![[r#" + ExternDecl [0-38]: + symbol_id: 8 + parameters: + Int + Int + Double + Result + bool + return_type: ()"#]], + ); +} + +#[test] +fn return_type() { + check_stmt_kind( + "extern f() -> int;", + &expect![[r#" + ExternDecl [0-18]: + symbol_id: 8 + parameters: + return_type: Int"#]], + ); +} + +#[test] +fn no_allowed_in_non_global_scope() { + check_stmt_kind( + "{ extern f(); }", + &expect![[r#" + Program: + version: + statements: + Stmt [0-15]: + annotations: + kind: Block [0-15]: + Stmt [2-13]: + annotations: + kind: ExternDecl [2-13]: + symbol_id: 8 + parameters: + return_type: () + + [Qsc.Qasm3.Compile.DefDeclarationInNonGlobalScope + + x Extern declarations must be done in global scope. + ,-[test:1:3] + 1 | { extern f(); } + : ^^^^^^^^^^^ + `---- + ]"#]], + ); +} From c558bdadd09b2c2345215323cac6c1ebb223a8f7 Mon Sep 17 00:00:00 2001 From: orpuente-MS <156957451+orpuente-MS@users.noreply.github.com> Date: Fri, 4 Apr 2025 15:44:10 -0700 Subject: [PATCH 092/108] Better error messages for const evaluator (#2277) --- compiler/qsc_qasm3/src/lib.rs | 4 + .../qsc_qasm3/src/semantic/ast/const_eval.rs | 278 ++++++++++-------- compiler/qsc_qasm3/src/semantic/lowerer.rs | 32 +- .../binary/arithmetic_conversions.rs | 44 +++ compiler/qsc_qasm3/src/semantic/types.rs | 2 +- .../qsc_qasm3/src/tests/declaration/def.rs | 94 ++++++ .../qsc_qasm3/src/tests/declaration/gate.rs | 94 ++++++ .../src/tests/statement/const_eval.rs | 40 +++ 8 files changed, 461 insertions(+), 127 deletions(-) diff --git a/compiler/qsc_qasm3/src/lib.rs b/compiler/qsc_qasm3/src/lib.rs index 1ad5ee67d0..a6e3699f95 100644 --- a/compiler/qsc_qasm3/src/lib.rs +++ b/compiler/qsc_qasm3/src/lib.rs @@ -77,6 +77,9 @@ pub enum ErrorKind { #[error(transparent)] #[diagnostic(transparent)] Semantic(#[from] crate::semantic::Error), + #[error(transparent)] + #[diagnostic(transparent)] + ConstEval(#[from] crate::semantic::ast::const_eval::ConstEvalError), #[error("QASM3 Parse Error: Not Found {0}")] NotFound(String), #[error("IO Error: {0}")] @@ -90,6 +93,7 @@ impl ErrorKind { ErrorKind::Parse(error, span) => Self::Parse(error, span + offset), ErrorKind::Parser(error) => Self::Parser(error.with_offset(offset)), ErrorKind::Semantic(error) => Self::Semantic(error.with_offset(offset)), + ErrorKind::ConstEval(error) => Self::ConstEval(error), ErrorKind::NotFound(error) => Self::NotFound(error), ErrorKind::OldIO(error) => Self::OldIO(error), } diff --git a/compiler/qsc_qasm3/src/semantic/ast/const_eval.rs b/compiler/qsc_qasm3/src/semantic/ast/const_eval.rs index a899186e55..cc7fc68f39 100644 --- a/compiler/qsc_qasm3/src/semantic/ast/const_eval.rs +++ b/compiler/qsc_qasm3/src/semantic/ast/const_eval.rs @@ -10,33 +10,53 @@ use super::{ BinOp, BinaryOpExpr, Cast, Expr, ExprKind, FunctionCall, IndexExpr, IndexedIdent, LiteralKind, SymbolId, UnaryOp, UnaryOpExpr, }; +use crate::semantic::Lowerer; use crate::stdlib::angle; use crate::{ oqasm_helpers::safe_i64_to_f64, - semantic::{ - symbols::SymbolTable, - types::{ArrayDimensions, Type}, - }, + semantic::types::{ArrayDimensions, Type}, }; +use miette::Diagnostic; use num_bigint::BigInt; +use qsc_data_structures::span::Span; +use thiserror::Error; + +#[derive(Clone, Debug, Diagnostic, Eq, Error, PartialEq)] +pub enum ConstEvalError { + #[error("expression must be const")] + #[diagnostic(code("Qsc.Qasm3.Compile.ExprMustBeConst"))] + ExprMustBeConst(#[label] Span), + #[error("uint expression must evaluate to a non-negative value, but it evaluated to {0}")] + #[diagnostic(code("Qsc.Qasm3.Compile.NegativeUIntValue"))] + NegativeUIntValue(i64, #[label] Span), + #[error("{0} doesn't fit in {1}")] + #[diagnostic(code("Qsc.Qasm3.Compile.ValueOverflow"))] + ValueOverflow(String, String, #[label] Span), +} + +impl ConstEvalError {} impl Expr { - pub fn const_eval(&self, symbols: &SymbolTable) -> Option { + /// Tries to evaluate the expression. It takes the current `Lowerer` as + /// the evaluation context to resolve symbols and push errors in case + /// of failure. + pub(crate) fn const_eval(&self, ctx: &mut Lowerer) -> Option { let ty = &self.ty; if !ty.is_const() { + ctx.push_const_eval_error(ConstEvalError::ExprMustBeConst(self.span)); return None; } match &*self.kind { - ExprKind::Ident(symbol_id) => symbol_id.const_eval(symbols), - ExprKind::IndexedIdentifier(indexed_ident) => indexed_ident.const_eval(symbols), - ExprKind::UnaryOp(unary_op_expr) => unary_op_expr.const_eval(symbols), - ExprKind::BinaryOp(binary_op_expr) => binary_op_expr.const_eval(symbols), + ExprKind::Ident(symbol_id) => symbol_id.const_eval(ctx), + ExprKind::IndexedIdentifier(indexed_ident) => indexed_ident.const_eval(ctx), + ExprKind::UnaryOp(unary_op_expr) => unary_op_expr.const_eval(ctx), + ExprKind::BinaryOp(binary_op_expr) => binary_op_expr.const_eval(ctx), ExprKind::Lit(literal_kind) => Some(literal_kind.clone()), - ExprKind::FunctionCall(function_call) => function_call.const_eval(symbols, ty), - ExprKind::Cast(cast) => cast.const_eval(symbols, ty), - ExprKind::IndexExpr(index_expr) => index_expr.const_eval(symbols, ty), - ExprKind::Paren(expr) => expr.const_eval(symbols), + ExprKind::FunctionCall(function_call) => function_call.const_eval(ctx, ty), + ExprKind::Cast(cast) => cast.const_eval(ctx), + ExprKind::IndexExpr(index_expr) => index_expr.const_eval(ctx, ty), + ExprKind::Paren(expr) => expr.const_eval(ctx), // Measurements are non-const, so we don't need to implement them. ExprKind::Measure(_) | ExprKind::Err => None, } @@ -44,17 +64,17 @@ impl Expr { } impl SymbolId { - fn const_eval(self, symbols: &SymbolTable) -> Option { - let symbol = symbols[self].clone(); + fn const_eval(self, ctx: &mut Lowerer) -> Option { + let symbol = ctx.symbols[self].clone(); symbol .get_const_expr() // get the value of the symbol (an Expr) - .const_eval(symbols) // const eval that Expr + .const_eval(ctx) // const eval that Expr } } impl IndexedIdent { #[allow(clippy::unused_self)] - fn const_eval(&self, _symbols: &SymbolTable) -> Option { + fn const_eval(&self, _ctx: &mut Lowerer) -> Option { None } } @@ -69,16 +89,16 @@ macro_rules! rewrap_lit { if let $pat = $lit { Some($out) } else { - None + unreachable!("if we hit this there is a bug in the type system") } }; } impl UnaryOpExpr { - fn const_eval(&self, symbols: &SymbolTable) -> Option { + fn const_eval(&self, ctx: &mut Lowerer) -> Option { use LiteralKind::{Angle, Bit, Bitstring, Bool, Float, Int}; let operand_ty = &self.expr.ty; - let lit = self.expr.const_eval(symbols)?; + let lit = self.expr.const_eval(ctx)?; match &self.op { UnaryOp::Neg => match operand_ty { @@ -138,86 +158,88 @@ fn assert_binary_op_ty_invariant(op: BinOp, lhs_ty: &Type, rhs_ty: &Type) { impl BinaryOpExpr { #[allow(clippy::too_many_lines)] - fn const_eval(&self, symbols: &SymbolTable) -> Option { + fn const_eval(&self, ctx: &mut Lowerer) -> Option { use LiteralKind::{Angle, Bit, Bitstring, Bool, Float, Int}; assert_binary_op_ty_invariant(self.op, &self.lhs.ty, &self.rhs.ty); - let lhs = self.lhs.const_eval(symbols)?; - let rhs = self.rhs.const_eval(symbols)?; + let lhs = self.lhs.const_eval(ctx); + let rhs = self.rhs.const_eval(ctx); + let (lhs, rhs) = (lhs?, rhs?); let lhs_ty = &self.lhs.ty; match &self.op { // Bit Shifts - BinOp::Shl => match lhs_ty { - Type::UInt(Some(size), _) => rewrap_lit!((lhs, rhs), (Int(lhs), Int(rhs)), { - if rhs < 0 { - return None; + BinOp::Shl => { + assert!( + matches!(self.rhs.ty, Type::UInt(..)), + "shift left rhs should have been casted to uint during lowering" + ); + let LiteralKind::Int(rhs) = rhs else { + unreachable!("if we hit this there is a bug in the type system"); + }; + if rhs < 0 { + ctx.push_const_eval_error(ConstEvalError::NegativeUIntValue( + rhs, + self.rhs.span, + )); + return None; + } + + match lhs_ty { + Type::UInt(Some(size), _) => rewrap_lit!(lhs, Int(lhs), { + let mask = (1 << size) - 1; + Int((lhs << rhs) & mask) + }), + Type::UInt(..) => rewrap_lit!(lhs, Int(lhs), Int(lhs << rhs)), + Type::Angle(..) => { + rewrap_lit!(lhs, Angle(lhs), Angle(lhs << rhs)) } - let mask = (1 << size) - 1; - Int((lhs << rhs) & mask) - }), - Type::UInt(..) => rewrap_lit!((lhs, rhs), (Int(lhs), Int(rhs)), { - if rhs < 0 { - return None; + Type::Bit(..) => rewrap_lit!(lhs, Bit(lhs), { + // The Spec says "The shift operators shift bits off the end." + // Therefore if the rhs is > 0 the value becomes zero. + Bit(rhs == 0 && lhs) + }), + Type::BitArray(..) => { + rewrap_lit!(lhs, Bitstring(lhs, size), { + let mask = BigInt::from((1 << size) - 1); + Bitstring((lhs << rhs) & mask, size) + }) } - Int(lhs << rhs) - }), - Type::Angle(..) => { - rewrap_lit!((lhs, rhs), (Angle(lhs), Int(rhs)), { - if rhs < 0 { - return None; - } - Angle(lhs << rhs) - }) + _ => None, } - Type::Bit(..) => rewrap_lit!((lhs, rhs), (Bit(lhs), Int(rhs)), { - if rhs < 0 { - return None; - } - // The Spec says "The shift operators shift bits off the end." - // Therefore if the rhs is > 0 the value becomes zero. - Bit(rhs == 0 && lhs) - }), - Type::BitArray(..) => rewrap_lit!((lhs, rhs), (Bitstring(lhs, size), Int(rhs)), { - if rhs < 0 { - return None; + } + BinOp::Shr => { + assert!( + matches!(self.rhs.ty, Type::UInt(..)), + "shift right rhs should have been casted to uint during lowering" + ); + let LiteralKind::Int(rhs) = rhs else { + unreachable!("if we hit this there is a bug in the type system"); + }; + if rhs < 0 { + ctx.push_const_eval_error(ConstEvalError::NegativeUIntValue( + rhs, + self.rhs.span, + )); + return None; + } + + match lhs_ty { + Type::UInt(..) => rewrap_lit!(lhs, Int(lhs), Int(lhs >> rhs)), + Type::Angle(..) => { + rewrap_lit!(lhs, Angle(lhs), Angle(lhs >> rhs)) } - let mask = BigInt::from((1 << size) - 1); - Bitstring((lhs << rhs) & mask, size) - }), - _ => None, - }, - BinOp::Shr => match lhs_ty { - Type::UInt(..) => rewrap_lit!((lhs, rhs), (Int(lhs), Int(rhs)), { - if rhs < 0 { - return None; + Type::Bit(..) => rewrap_lit!(lhs, Bit(lhs), { + // The Spec says "The shift operators shift bits off the end." + // Therefore if the rhs is > 0 the value becomes zero. + Bit(rhs == 0 && lhs) + }), + Type::BitArray(..) => { + rewrap_lit!(lhs, Bitstring(lhs, size), Bitstring(lhs >> rhs, size)) } - Int(lhs >> rhs) - }), - Type::Angle(..) => { - rewrap_lit!((lhs, rhs), (Angle(lhs), Int(rhs)), { - if rhs < 0 { - return None; - } - Angle(lhs >> rhs) - }) + _ => None, } - Type::Bit(..) => rewrap_lit!((lhs, rhs), (Bit(lhs), Int(rhs)), { - if rhs < 0 { - return None; - } - // The Spec says "The shift operators shift bits off the end." - // Therefore if the rhs is > 0 the value becomes zero. - Bit(rhs == 0 && lhs) - }), - Type::BitArray(..) => rewrap_lit!((lhs, rhs), (Bitstring(lhs, size), Int(rhs)), { - if rhs < 0 { - return None; - } - Bitstring(lhs >> rhs, size) - }), - _ => None, - }, + } // Bitwise BinOp::AndB => match lhs_ty { @@ -423,6 +445,10 @@ impl BinaryOpExpr { } Type::Angle(..) => rewrap_lit!((lhs, rhs), (Int(lhs), Angle(rhs)), { if lhs < 0 { + ctx.push_const_eval_error(ConstEvalError::NegativeUIntValue( + lhs, + self.lhs.span, + )); return None; } #[allow(clippy::cast_sign_loss)] @@ -494,31 +520,28 @@ impl BinaryOpExpr { impl FunctionCall { #[allow(clippy::unused_self)] - fn const_eval(&self, _symbols: &SymbolTable, _ty: &Type) -> Option { + fn const_eval(&self, _ctx: &mut Lowerer, _ty: &Type) -> Option { None } } impl IndexExpr { #[allow(clippy::unused_self)] - fn const_eval(&self, _symbols: &SymbolTable, _ty: &Type) -> Option { + fn const_eval(&self, _ctx: &mut Lowerer, _ty: &Type) -> Option { None } } impl Cast { - fn const_eval(&self, symbols: &SymbolTable, ty: &Type) -> Option { - let lit = self.expr.const_eval(symbols)?; - let lit_ty = &self.expr.ty; - - match ty { - Type::Bool(_) => cast_to_bool(lit_ty, lit), - Type::Int(_, _) => cast_to_int(lit_ty, lit), - Type::UInt(_, _) => cast_to_uint(lit_ty, lit), - Type::Float(_, _) => cast_to_float(lit_ty, lit), - Type::Angle(_, _) => cast_to_angle(lit, lit_ty, ty), - Type::Bit(_) => cast_to_bit(lit_ty, lit), - Type::BitArray(dims, _) => cast_to_bitarray(lit_ty, lit, dims), + fn const_eval(&self, ctx: &mut Lowerer) -> Option { + match &self.ty { + Type::Bool(..) => cast_to_bool(self, ctx), + Type::Int(..) => cast_to_int(self, ctx), + Type::UInt(..) => cast_to_uint(self, ctx), + Type::Float(..) => cast_to_float(self, ctx), + Type::Angle(..) => cast_to_angle(self, ctx), + Type::Bit(..) => cast_to_bit(self, ctx), + Type::BitArray(..) => cast_to_bitarray(self, ctx), _ => None, } } @@ -531,10 +554,11 @@ impl Cast { /// +---------------+------+-----+------+-------+-------+-----+ /// | bool | - | Yes | Yes | Yes | Yes | Yes | /// +---------------+------+-----+------+-------+-------+-----+ -fn cast_to_bool(ty: &Type, lit: LiteralKind) -> Option { +fn cast_to_bool(cast: &Cast, ctx: &mut Lowerer) -> Option { use LiteralKind::{Angle, Bit, Bitstring, Bool, Float, Int}; + let lit = cast.expr.const_eval(ctx)?; - match ty { + match &cast.expr.ty { Type::Bool(..) => Some(lit), Type::Bit(..) => rewrap_lit!(lit, Bit(val), Bool(val)), Type::BitArray(..) => rewrap_lit!(lit, Bitstring(val, _), Bool(val != BigInt::ZERO)), @@ -552,10 +576,11 @@ fn cast_to_bool(ty: &Type, lit: LiteralKind) -> Option { /// +---------------+------+-----+------+-------+-------+-----+ /// | int | Yes | - | Yes | Yes | No | Yes | /// +---------------+------+-----+------+-------+-------+-----+ -fn cast_to_int(ty: &Type, lit: LiteralKind) -> Option { +fn cast_to_int(cast: &Cast, ctx: &mut Lowerer) -> Option { use LiteralKind::{Bit, Bitstring, Bool, Float, Int}; + let lit = cast.expr.const_eval(ctx)?; - match ty { + match &cast.expr.ty { Type::Bool(..) => rewrap_lit!(lit, Bool(val), Int(i64::from(val))), Type::Bit(..) => rewrap_lit!(lit, Bit(val), Int(i64::from(val))), Type::BitArray(..) => { @@ -581,10 +606,11 @@ fn cast_to_int(ty: &Type, lit: LiteralKind) -> Option { /// +---------------+------+-----+------+-------+-------+-----+ /// | uint | Yes | Yes | - | Yes | No | Yes | /// +---------------+------+-----+------+-------+-------+-----+ -fn cast_to_uint(ty: &Type, lit: LiteralKind) -> Option { +fn cast_to_uint(cast: &Cast, ctx: &mut Lowerer) -> Option { use LiteralKind::{Bit, Bitstring, Bool, Float, Int}; + let lit = cast.expr.const_eval(ctx)?; - match ty { + match &cast.expr.ty { Type::Bool(..) => rewrap_lit!(lit, Bool(val), Int(i64::from(val))), Type::Bit(..) => rewrap_lit!(lit, Bit(val), Int(i64::from(val))), Type::BitArray(..) => { @@ -611,10 +637,11 @@ fn cast_to_uint(ty: &Type, lit: LiteralKind) -> Option { /// +---------------+------+-----+------+-------+-------+-----+ /// | float | Yes | Yes | Yes | - | No | No | /// +---------------+------+-----+------+-------+-------+-----+ -fn cast_to_float(ty: &Type, lit: LiteralKind) -> Option { +fn cast_to_float(cast: &Cast, ctx: &mut Lowerer) -> Option { use LiteralKind::{Bool, Float, Int}; + let lit = cast.expr.const_eval(ctx)?; - match ty { + match &cast.expr.ty { Type::Bool(..) => rewrap_lit!(lit, Bool(val), Float(if val { 1.0 } else { 0.0 })), Type::Int(..) | Type::UInt(..) => rewrap_lit!(lit, Int(val), { // TODO: we need to issue the same lint in Q#. @@ -633,9 +660,11 @@ fn cast_to_float(ty: &Type, lit: LiteralKind) -> Option { /// +---------------+------+-----+------+-------+-------+-----+ /// | angle | No | No | No | Yes | - | Yes | /// +---------------+------+-----+------+-------+-------+-----+ -fn cast_to_angle(lit: LiteralKind, lit_ty: &Type, target_ty: &Type) -> Option { +fn cast_to_angle(cast: &Cast, ctx: &mut Lowerer) -> Option { use LiteralKind::{Angle, Bit, Bitstring, Float}; - match lit_ty { + let lit = cast.expr.const_eval(ctx)?; + + match &cast.expr.ty { Type::Float(size, _) => rewrap_lit!( lit, Float(val), @@ -644,7 +673,7 @@ fn cast_to_angle(lit: LiteralKind, lit_ty: &Type, target_ty: &Type) -> Option

  • rewrap_lit!( lit, Angle(val), - Angle(val.cast_to_maybe_sized(target_ty.width())) + Angle(val.cast_to_maybe_sized(cast.ty.width())) ), Type::Bit(..) => rewrap_lit!( lit, @@ -673,10 +702,11 @@ fn cast_to_angle(lit: LiteralKind, lit_ty: &Type, target_ty: &Type) -> Option
  • Option { +fn cast_to_bit(cast: &Cast, ctx: &mut Lowerer) -> Option { use LiteralKind::{Angle, Bit, Bool, Int}; + let lit = cast.expr.const_eval(ctx)?; - match ty { + match &cast.expr.ty { Type::Bool(..) => rewrap_lit!(lit, Bool(val), Bit(val)), Type::Bit(..) => Some(lit), Type::Int(..) | Type::UInt(..) => rewrap_lit!(lit, Int(val), Bit(val != 0)), @@ -692,15 +722,21 @@ fn cast_to_bit(ty: &Type, lit: LiteralKind) -> Option { /// +---------------+------+-----+------+-------+-------+-----+ /// | bitarray | Yes | Yes | Yes | No | Yes | - | /// +---------------+------+-----+------+-------+-------+-----+ -fn cast_to_bitarray(ty: &Type, lit: LiteralKind, dims: &ArrayDimensions) -> Option { +fn cast_to_bitarray(cast: &Cast, ctx: &mut Lowerer) -> Option { use LiteralKind::{Angle, Bit, Bitstring, Bool, Int}; + let lit = cast.expr.const_eval(ctx)?; + + let Type::BitArray(dims, _) = &cast.ty else { + unreachable!("we got here after matching Type::BitArray in Cast::const_eval"); + }; let ArrayDimensions::One(size) = dims else { + ctx.push_unsupported_error_message("multidimensional arrays", cast.span); return None; }; let size = *size; - match ty { + match &cast.expr.ty { Type::Bool(..) => rewrap_lit!(lit, Bool(val), Bitstring(BigInt::from(val), size)), Type::Angle(..) => rewrap_lit!(lit, Angle(val), { let new_val = val.cast_to_maybe_sized(Some(size)); @@ -709,6 +745,11 @@ fn cast_to_bitarray(ty: &Type, lit: LiteralKind, dims: &ArrayDimensions) -> Opti Type::Bit(..) => rewrap_lit!(lit, Bit(val), Bitstring(BigInt::from(val), size)), Type::BitArray(..) => rewrap_lit!(lit, Bitstring(val, rhs_size), { if rhs_size > size { + ctx.push_const_eval_error(ConstEvalError::ValueOverflow( + cast.expr.ty.to_string(), + cast.ty.to_string(), + cast.span, + )); return None; } Bitstring(val, size) @@ -716,6 +757,11 @@ fn cast_to_bitarray(ty: &Type, lit: LiteralKind, dims: &ArrayDimensions) -> Opti Type::Int(..) | Type::UInt(..) => rewrap_lit!(lit, Int(val), { let actual_bits = number_of_bits(val); if actual_bits > size { + ctx.push_const_eval_error(ConstEvalError::ValueOverflow( + cast.expr.ty.to_string(), + cast.ty.to_string(), + cast.span, + )); return None; } Bitstring(BigInt::from(val), size) diff --git a/compiler/qsc_qasm3/src/semantic/lowerer.rs b/compiler/qsc_qasm3/src/semantic/lowerer.rs index c73076fbaa..a9eb613d21 100644 --- a/compiler/qsc_qasm3/src/semantic/lowerer.rs +++ b/compiler/qsc_qasm3/src/semantic/lowerer.rs @@ -4,10 +4,11 @@ use std::ops::ShlAssign; use std::rc::Rc; +use super::ast::const_eval::ConstEvalError; use super::symbols::ScopeKind; use super::types::binop_requires_asymmetric_angle_op; use super::types::binop_requires_int_conversion_for_type; -use super::types::binop_requires_symmetric_int_conversion; +use super::types::binop_requires_symmetric_uint_conversion; use super::types::is_complex_binop_supported; use super::types::promote_to_uint_ty; use super::types::promote_width; @@ -61,7 +62,7 @@ macro_rules! err_expr { }; } -pub(super) struct Lowerer { +pub(crate) struct Lowerer { /// The root QASM source to compile. pub source: QasmSource, /// The source map of QASM sources for error reporting. @@ -546,11 +547,11 @@ impl Lowerer { && is_symbol_outside_most_inner_gate_or_function_scope; let kind = if is_const_evaluation_necessary { - if let Some(val) = symbol.get_const_expr().const_eval(&self.symbols) { + if let Some(val) = symbol.get_const_expr().const_eval(self) { semantic::ExprKind::Lit(val) } else { self.push_semantic_error(SemanticErrorKind::ExprMustBeConst( - ident.name.to_string(), + "A captured variable".into(), ident.span, )); semantic::ExprKind::Err @@ -1506,7 +1507,7 @@ impl Lowerer { self.push_invalid_cast_error(target_ty, &expr.ty, expr.span); return None; }; - let Some(lit) = expr.const_eval(&self.symbols) else { + let Some(lit) = expr.const_eval(self) else { self.push_semantic_error(SemanticErrorKind::ExprMustBeConst( "ctrl modifier argument".into(), expr.span, @@ -1678,7 +1679,7 @@ impl Lowerer { let size_expr = Self::try_cast_expr_to_type(&Type::UInt(None, true), &size_expr); if let Some(Some(semantic::LiteralKind::Int(val))) = - size_expr.map(|expr| expr.const_eval(&self.symbols)) + size_expr.map(|expr| expr.const_eval(self)) { if let Ok(size) = u32::try_from(val) { ( @@ -1906,7 +1907,7 @@ impl Lowerer { expr_span, ); - if let Some(lit) = expr.const_eval(&self.symbols) { + if let Some(lit) = expr.const_eval(self) { Some(lit) } else { self.push_semantic_error(SemanticErrorKind::ExprMustBeConst( @@ -2854,8 +2855,8 @@ impl Lowerer { } }; (new_left, new_right, promoted_type) - } else if binop_requires_symmetric_int_conversion(op) { - let ty = Type::Int(None, ty_constness); + } else if binop_requires_symmetric_uint_conversion(op) { + let ty = Type::UInt(None, ty_constness); let new_rhs = self.cast_expr_to_type(&ty, &rhs); (lhs, new_rhs, left_type) } else { @@ -2943,7 +2944,11 @@ impl Lowerer { fn binop_requires_bitwise_symmetric_conversion(op: syntax::BinOp) -> bool { matches!( op, - syntax::BinOp::AndB | syntax::BinOp::OrB | syntax::BinOp::XorB + syntax::BinOp::AndB + | syntax::BinOp::OrB + | syntax::BinOp::XorB + | syntax::BinOp::Shl + | syntax::BinOp::Shr ) } @@ -3207,6 +3212,13 @@ impl Lowerer { self.errors.push(error); } + /// Pushes a const eval error with the given kind. + pub fn push_const_eval_error(&mut self, kind: ConstEvalError) { + let kind = crate::ErrorKind::ConstEval(kind); + let error = self.create_err(kind); + self.errors.push(error); + } + /// Creates an error from the given kind with the current source mapping. fn create_err(&self, kind: crate::ErrorKind) -> WithSource { let error = crate::Error(kind); diff --git a/compiler/qsc_qasm3/src/semantic/tests/expression/binary/arithmetic_conversions.rs b/compiler/qsc_qasm3/src/semantic/tests/expression/binary/arithmetic_conversions.rs index 6b8223047b..847c4abe36 100644 --- a/compiler/qsc_qasm3/src/semantic/tests/expression/binary/arithmetic_conversions.rs +++ b/compiler/qsc_qasm3/src/semantic/tests/expression/binary/arithmetic_conversions.rs @@ -262,3 +262,47 @@ fn multiplying_int_idents_with_width_greater_than_64_result_in_bigint_result() { "#]], ); } + +#[test] +fn left_shift_casts_rhs_to_uint() { + let input = " + int x = 5; + int y = 3; + int z = x << y; + "; + + check_stmt_kinds( + input, + &expect![[r#" + ClassicalDeclarationStmt [9-19]: + symbol_id: 8 + ty_span: [9-12] + init_expr: Expr [17-18]: + ty: Int(None, false) + kind: Lit: Int(5) + ClassicalDeclarationStmt [28-38]: + symbol_id: 9 + ty_span: [28-31] + init_expr: Expr [36-37]: + ty: Int(None, false) + kind: Lit: Int(3) + ClassicalDeclarationStmt [47-62]: + symbol_id: 10 + ty_span: [47-50] + init_expr: Expr [55-61]: + ty: Int(None, false) + kind: BinaryOpExpr: + op: Shl + lhs: Expr [55-56]: + ty: Int(None, false) + kind: SymbolId(8) + rhs: Expr [60-61]: + ty: UInt(None, false) + kind: Cast [0-0]: + ty: UInt(None, false) + expr: Expr [60-61]: + ty: Int(None, false) + kind: SymbolId(9) + "#]], + ); +} diff --git a/compiler/qsc_qasm3/src/semantic/types.rs b/compiler/qsc_qasm3/src/semantic/types.rs index e4f24d60a7..7373338207 100644 --- a/compiler/qsc_qasm3/src/semantic/types.rs +++ b/compiler/qsc_qasm3/src/semantic/types.rs @@ -775,7 +775,7 @@ fn try_promote_bitarray_to_int(left_type: &Type, right_type: &Type) -> Option> -pub(crate) fn binop_requires_symmetric_int_conversion(op: syntax::BinOp) -> bool { +pub(crate) fn binop_requires_symmetric_uint_conversion(op: syntax::BinOp) -> bool { matches!(op, syntax::BinOp::Shl | syntax::BinOp::Shr) } diff --git a/compiler/qsc_qasm3/src/tests/declaration/def.rs b/compiler/qsc_qasm3/src/tests/declaration/def.rs index d4947bb9cd..3e1f200e5c 100644 --- a/compiler/qsc_qasm3/src/tests/declaration/def.rs +++ b/compiler/qsc_qasm3/src/tests/declaration/def.rs @@ -163,3 +163,97 @@ fn missing_return_expr_on_non_void_function_fails() { ]"#]] .assert_eq(&format!("{errors:?}")); } + +#[test] +fn capturing_external_variables_const_evaluate_them() -> miette::Result<(), Vec> { + let source = r#" + const int a = 2; + const int b = 3; + const int c = a * b; + def f() -> int { + return c; + } + "#; + + let qsharp = compile_qasm_stmt_to_qsharp(source)?; + expect![[r#" + function f() : Int { + return 6; + } + "#]] + .assert_eq(&qsharp); + Ok(()) +} + +#[test] +fn capturing_non_const_external_variable_fails() { + let source = r#" + int a = 2 << (-3); + def f() -> int { + return a; + } + "#; + + let Err(errors) = compile_qasm_stmt_to_qsharp(source) else { + panic!("Expected error"); + }; + + expect![[r#" + [Qsc.Qasm3.Compile.UndefinedSymbol + + x Undefined symbol: a. + ,-[Test.qasm:4:20] + 3 | def f() -> int { + 4 | return a; + : ^ + 5 | } + `---- + , Qsc.Qasm3.Compile.CannotCast + + x Cannot cast expression of type Err to type Int(None, false) + ,-[Test.qasm:4:20] + 3 | def f() -> int { + 4 | return a; + : ^ + 5 | } + `---- + ]"#]] + .assert_eq(&format!("{errors:?}")); +} + +#[test] +fn capturing_non_const_evaluatable_external_variable_fails() { + let source = r#" + const int a = 2 << (-3); + def f() -> int { + return a; + } + "#; + + let Err(errors) = compile_qasm_stmt_to_qsharp(source) else { + panic!("Expected error"); + }; + + expect![[r#" + [Qsc.Qasm3.Compile.NegativeUIntValue + + x uint expression must evaluate to a non-negative value, but it evaluated + | to -3 + ,-[Test.qasm:2:28] + 1 | + 2 | const int a = 2 << (-3); + : ^^^^ + 3 | def f() -> int { + `---- + , Qsc.Qasm3.Compile.ExprMustBeConst + + x A captured variable must be a const expression + ,-[Test.qasm:4:20] + 3 | def f() -> int { + 4 | return a; + : ^ + 5 | } + `---- + ]"#]] + .assert_eq(&format!("{errors:?}")); +} diff --git a/compiler/qsc_qasm3/src/tests/declaration/gate.rs b/compiler/qsc_qasm3/src/tests/declaration/gate.rs index 5f3e98d5bd..879b0cb354 100644 --- a/compiler/qsc_qasm3/src/tests/declaration/gate.rs +++ b/compiler/qsc_qasm3/src/tests/declaration/gate.rs @@ -84,3 +84,97 @@ fn two_angles_two_qubits() -> miette::Result<(), Vec> { .assert_eq(&qsharp); Ok(()) } + +#[test] +fn capturing_external_variables_const_evaluate_them() -> miette::Result<(), Vec> { + let source = r#" + const int a = 2; + const int b = 3; + const int c = a * b; + gate my_gate q { + int x = c; + } + "#; + + let qsharp = compile_qasm_stmt_to_qsharp(source)?; + expect![[r#" + operation my_gate(q : Qubit) : Unit is Adj + Ctl { + mutable x = 6; + } + "#]] + .assert_eq(&qsharp); + Ok(()) +} + +#[test] +fn capturing_non_const_external_variable_fails() { + let source = r#" + int a = 2 << (-3); + gate my_gate q { + int x = a; + } + "#; + + let Err(errors) = compile_qasm_stmt_to_qsharp(source) else { + panic!("Expected error"); + }; + + expect![[r#" + [Qsc.Qasm3.Compile.UndefinedSymbol + + x Undefined symbol: a. + ,-[Test.qasm:4:21] + 3 | gate my_gate q { + 4 | int x = a; + : ^ + 5 | } + `---- + , Qsc.Qasm3.Compile.CannotCast + + x Cannot cast expression of type Err to type Int(None, false) + ,-[Test.qasm:4:21] + 3 | gate my_gate q { + 4 | int x = a; + : ^ + 5 | } + `---- + ]"#]] + .assert_eq(&format!("{errors:?}")); +} + +#[test] +fn capturing_non_const_evaluatable_external_variable_fails() { + let source = r#" + const int a = 2 << (-3); + gate my_gate q { + int x = a; + } + "#; + + let Err(errors) = compile_qasm_stmt_to_qsharp(source) else { + panic!("Expected error"); + }; + + expect![[r#" + [Qsc.Qasm3.Compile.NegativeUIntValue + + x uint expression must evaluate to a non-negative value, but it evaluated + | to -3 + ,-[Test.qasm:2:28] + 1 | + 2 | const int a = 2 << (-3); + : ^^^^ + 3 | gate my_gate q { + `---- + , Qsc.Qasm3.Compile.ExprMustBeConst + + x A captured variable must be a const expression + ,-[Test.qasm:4:21] + 3 | gate my_gate q { + 4 | int x = a; + : ^ + 5 | } + `---- + ]"#]] + .assert_eq(&format!("{errors:?}")); +} diff --git a/compiler/qsc_qasm3/src/tests/statement/const_eval.rs b/compiler/qsc_qasm3/src/tests/statement/const_eval.rs index 14dd57add3..e4060157e8 100644 --- a/compiler/qsc_qasm3/src/tests/statement/const_eval.rs +++ b/compiler/qsc_qasm3/src/tests/statement/const_eval.rs @@ -76,6 +76,16 @@ fn non_const_exprs_fail_in_bitarray_size_position() { let errs: Vec<_> = errs.iter().map(|e| format!("{e:?}")).collect(); let errs_string = errs.join("\n"); expect![[r#" + Qsc.Qasm3.Compile.ExprMustBeConst + + x expression must be const + ,-[Test.qasm:5:13] + 4 | int c = a + 3; + 5 | bit[b] r1; + : ^ + 6 | bit[c] r2; + `---- + Qsc.Qasm3.Compile.ExprMustBeConst x designator must be a const expression @@ -86,6 +96,16 @@ fn non_const_exprs_fail_in_bitarray_size_position() { 6 | bit[c] r2; `---- + Qsc.Qasm3.Compile.ExprMustBeConst + + x expression must be const + ,-[Test.qasm:6:13] + 5 | bit[b] r1; + 6 | bit[c] r2; + : ^ + 7 | + `---- + Qsc.Qasm3.Compile.ExprMustBeConst x designator must be a const expression @@ -487,6 +507,16 @@ fn binary_op_shl_creg_fails() { 5 | `---- + Qsc.Qasm3.Compile.ExprMustBeConst + + x expression must be const + ,-[Test.qasm:4:13] + 3 | const creg b[3] = a << 2; + 4 | bit[b] r; + : ^ + 5 | + `---- + Qsc.Qasm3.Compile.ExprMustBeConst x designator must be a const expression @@ -650,6 +680,16 @@ fn binary_op_shr_creg_fails() { 5 | `---- + Qsc.Qasm3.Compile.ExprMustBeConst + + x expression must be const + ,-[Test.qasm:4:13] + 3 | const creg b[4] = a >> 2; + 4 | bit[b] r; + : ^ + 5 | + `---- + Qsc.Qasm3.Compile.ExprMustBeConst x designator must be a const expression From 16ceb641def3353ef446beafcfcccf49a5af5da4 Mon Sep 17 00:00:00 2001 From: orpuente-MS <156957451+orpuente-MS@users.noreply.github.com> Date: Mon, 7 Apr 2025 15:35:02 -0700 Subject: [PATCH 093/108] Cyclic and multiple include errors (#2278) This PR adds cyclic and multiple include errors to the qasm parser. --- compiler/qsc_qasm3/src/io.rs | 156 ++++++++++++++- compiler/qsc_qasm3/src/io/error.rs | 6 + compiler/qsc_qasm3/src/parse.rs | 14 +- compiler/qsc_qasm3/src/parser.rs | 21 +- compiler/qsc_qasm3/src/parser/tests.rs | 8 +- compiler/qsc_qasm3/src/semantic.rs | 6 +- compiler/qsc_qasm3/src/semantic/tests.rs | 16 +- compiler/qsc_qasm3/src/tests.rs | 8 +- .../qsc_qasm3/src/tests/statement/include.rs | 180 ++++++++++++++++++ fuzz/fuzz_targets/qasm3.rs | 4 +- pip/src/interop.rs | 31 +-- pip/src/interpreter.rs | 4 +- 12 files changed, 404 insertions(+), 50 deletions(-) diff --git a/compiler/qsc_qasm3/src/io.rs b/compiler/qsc_qasm3/src/io.rs index bc705f037f..fa88bfe55b 100644 --- a/compiler/qsc_qasm3/src/io.rs +++ b/compiler/qsc_qasm3/src/io.rs @@ -17,8 +17,10 @@ use rustc_hash::FxHashMap; /// Implementations of this trait can be provided to the parser /// to customize how include files are resolved. pub trait SourceResolver { + fn ctx(&mut self) -> &mut SourceResolverContext; + #[cfg(feature = "fs")] - fn resolve

    (&self, path: P) -> miette::Result<(PathBuf, String), Error> + fn resolve

    (&mut self, path: P) -> miette::Result<(PathBuf, String), Error> where P: AsRef, { @@ -27,8 +29,14 @@ pub trait SourceResolver { "Could not resolve include file path: {e}" ))) })?; + + self.ctx().check_include_errors(&path)?; + match std::fs::read_to_string(&path) { - Ok(source) => Ok((path, source)), + Ok(source) => { + self.ctx().add_path_to_include_graph(path.clone()); + Ok((path, source)) + } Err(_) => Err(Error(ErrorKind::NotFound(format!( "Could not resolve include file: {}", path.display() @@ -41,6 +49,137 @@ pub trait SourceResolver { P: AsRef; } +pub struct IncludeGraphNode { + parent: Option, + children: Vec, +} + +#[derive(Default)] +pub struct SourceResolverContext { + /// A graph representation of the include chain. + include_graph: FxHashMap, + /// Path being resolved. + current_file: Option, +} + +impl SourceResolverContext { + pub fn check_include_errors(&mut self, path: &PathBuf) -> miette::Result<(), Error> { + // If the new path makes a cycle in the include graph, we return + // an error showing the cycle to the user. + if let Some(cycle) = self.cycle_made_by_including_path(path) { + return Err(Error(ErrorKind::CyclicInclude(cycle))); + } + + // If the new path doesn't make a cycle but it was already + // included before, we return a `MultipleInclude` + // error saying " was already included in ". + if let Some(parent_file) = self.path_was_already_included(path) { + return Err(Error(ErrorKind::MultipleInclude( + path.display().to_string(), + parent_file.display().to_string(), + ))); + } + + self.add_path_to_include_graph(path.clone()); + + Ok(()) + } + + /// Changes `current_path` to its parent in the `include_graph`. + pub fn pop_current_file(&mut self) { + let parent = self + .current_file + .as_ref() + .and_then(|file| self.include_graph.get(file).map(|node| node.parent.clone())) + .flatten(); + self.current_file = parent; + } + + /// If including the path makes a cycle, returns a vector of the paths + /// that make the cycle. Else, returns None. + /// + /// To check if adding `path` to the include graph creates a cycle we just + /// need to verify if path is an ancestor of the current file. + fn cycle_made_by_including_path(&self, path: &PathBuf) -> Option { + let mut current_file = self.current_file.as_ref(); + let mut paths = Vec::new(); + + while let Some(file) = current_file { + paths.push(file.clone()); + current_file = self.get_parent(file); + if file == path { + paths.reverse(); + paths.push(path.clone()); + return Some(Cycle { paths }); + } + } + + None + } + + /// Returns the file that included `path`. + /// Returns `None` if `path` is the "main" file. + fn get_parent(&self, path: &PathBuf) -> Option<&PathBuf> { + self.include_graph + .get(path) + .and_then(|node| node.parent.as_ref()) + } + + /// If the path was already included, returns the path of the file that + /// included it. Else, returns None. + fn path_was_already_included(&self, path: &PathBuf) -> Option { + // SAFETY: The call to expect should be unreachable, since the parent + // will only be None for the "main" file. But including the + // main file will trigger a cyclic include error before this + // function is called. + self.include_graph + .get(path) + .map(|node| node.parent.clone().expect("unreachable")) + } + + /// Adds `path` as a child of `current_path`, and then changes + /// the `current_path` to `path`. + fn add_path_to_include_graph(&mut self, path: PathBuf) { + // 1. Add path to the current file children. + self.current_file.as_ref().and_then(|file| { + self.include_graph + .get_mut(file) + .map(|node| node.children.push(path.clone())) + }); + + // 2. Add path to the include graph. + self.include_graph.insert( + path.clone(), + IncludeGraphNode { + parent: self.current_file.clone(), + children: Vec::new(), + }, + ); + + // 3. Update the current file. + self.current_file = Some(path); + } +} + +/// We use this struct to print a nice error message when we find a cycle. +#[derive(Debug, Clone, Eq, PartialEq)] +pub struct Cycle { + paths: Vec, +} + +impl std::fmt::Display for Cycle { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + let parents = self.paths[0..(self.paths.len() - 1)].iter(); + let children = self.paths[1..].iter(); + + for (parent, child) in parents.zip(children) { + write!(f, "\n {} includes {}", parent.display(), child.display())?; + } + + Ok(()) + } +} + /// A source resolver that resolves include files from an in-memory map. /// This is useful for testing or environments in which file system access /// is not available. @@ -49,6 +188,7 @@ pub trait SourceResolver { /// contents prior to parsing. pub struct InMemorySourceResolver { sources: FxHashMap, + ctx: SourceResolverContext, } impl FromIterator<(Arc, Arc)> for InMemorySourceResolver { @@ -58,16 +198,24 @@ impl FromIterator<(Arc, Arc)> for InMemorySourceResolver { map.insert(PathBuf::from(path.to_string()), source.to_string()); } - InMemorySourceResolver { sources: map } + InMemorySourceResolver { + sources: map, + ctx: Default::default(), + } } } impl SourceResolver for InMemorySourceResolver { - fn resolve

    (&self, path: P) -> miette::Result<(PathBuf, String), Error> + fn ctx(&mut self) -> &mut SourceResolverContext { + &mut self.ctx + } + + fn resolve

    (&mut self, path: P) -> miette::Result<(PathBuf, String), Error> where P: AsRef, { let path = path.as_ref(); + self.ctx().check_include_errors(&path.to_path_buf())?; match self.sources.get(path) { Some(source) => Ok((path.to_owned(), source.clone())), None => Err(Error(ErrorKind::NotFound(format!( diff --git a/compiler/qsc_qasm3/src/io/error.rs b/compiler/qsc_qasm3/src/io/error.rs index 1a3b40ecf3..64f58f036c 100644 --- a/compiler/qsc_qasm3/src/io/error.rs +++ b/compiler/qsc_qasm3/src/io/error.rs @@ -4,6 +4,8 @@ use miette::Diagnostic; use thiserror::Error; +use super::Cycle; + #[derive(Clone, Debug, Diagnostic, Eq, Error, PartialEq)] #[error(transparent)] #[diagnostic(transparent)] @@ -15,6 +17,10 @@ pub enum ErrorKind { NotFound(String), #[error("IO Error: {0}")] IO(String), + #[error("{0} was already included in: {1}")] + MultipleInclude(String, String), + #[error("Cyclic include:{0}")] + CyclicInclude(Cycle), } impl From for crate::Error { diff --git a/compiler/qsc_qasm3/src/parse.rs b/compiler/qsc_qasm3/src/parse.rs index 99c77efccc..c8987c1872 100644 --- a/compiler/qsc_qasm3/src/parse.rs +++ b/compiler/qsc_qasm3/src/parse.rs @@ -67,7 +67,11 @@ impl QasmParseResult { /// This function will resolve includes using the provided resolver. /// If an include file cannot be resolved, an error will be returned. /// If a file is included recursively, a stack overflow occurs. -pub fn parse_source(source: S, path: P, resolver: &R) -> miette::Result +pub fn parse_source( + source: S, + path: P, + resolver: &mut R, +) -> miette::Result where S: AsRef, P: AsRef, @@ -203,7 +207,7 @@ impl QasmSource { /// This function is the start of a recursive process that will resolve all /// includes in the QASM file. Any includes are parsed as if their contents /// were defined where the include statement is. -fn parse_qasm_file(path: P, resolver: &R) -> miette::Result +fn parse_qasm_file(path: P, resolver: &mut R) -> miette::Result where P: AsRef, R: SourceResolver, @@ -212,7 +216,7 @@ where parse_qasm_source(source, path, resolver) } -fn parse_qasm_source(source: S, path: P, resolver: &R) -> miette::Result +fn parse_qasm_source(source: S, path: P, resolver: &mut R) -> miette::Result where S: AsRef, P: AsRef, @@ -224,7 +228,7 @@ where fn parse_source_and_includes, R>( source: P, - resolver: &R, + resolver: &mut R, ) -> miette::Result<(ParseOrErrors, Vec)> where R: SourceResolver, @@ -240,7 +244,7 @@ where fn parse_includes( syntax_ast: &ParseOrErrors, - resolver: &R, + resolver: &mut R, ) -> miette::Result> where R: SourceResolver, diff --git a/compiler/qsc_qasm3/src/parser.rs b/compiler/qsc_qasm3/src/parser.rs index 69b9e9204b..3c9bd80a6f 100644 --- a/compiler/qsc_qasm3/src/parser.rs +++ b/compiler/qsc_qasm3/src/parser.rs @@ -110,7 +110,7 @@ fn update_offsets(source_map: &SourceMap, source: &mut QasmSource) { /// This function will resolve includes using the provided resolver. /// If an include file cannot be resolved, an error will be returned. /// If a file is included recursively, a stack overflow occurs. -pub fn parse_source(source: S, path: P, resolver: &R) -> QasmParseResult +pub fn parse_source(source: S, path: P, resolver: &mut R) -> QasmParseResult where S: AsRef, P: AsRef, @@ -230,13 +230,22 @@ impl QasmSource { /// This function is the start of a recursive process that will resolve all /// includes in the QASM file. Any includes are parsed as if their contents /// were defined where the include statement is. -fn parse_qasm_file(path: P, resolver: &R) -> QasmSource +fn parse_qasm_file(path: P, resolver: &mut R) -> QasmSource where P: AsRef, R: SourceResolver, { match resolver.resolve(&path) { - Ok((path, source)) => parse_qasm_source(source, path, resolver), + Ok((path, source)) => { + let parse_result = parse_qasm_source(source, path, resolver); + + // Once we finish parsing the source, we pop the file from the + // resolver. This is needed to keep track of multiple includes + // and cyclic includes. + resolver.ctx().pop_current_file(); + + parse_result + } Err(e) => { let error = crate::parser::error::ErrorKind::IO(e); let error = crate::parser::Error(error, None); @@ -255,7 +264,7 @@ where } } -fn parse_qasm_source(source: S, path: P, resolver: &R) -> QasmSource +fn parse_qasm_source(source: S, path: P, resolver: &mut R) -> QasmSource where S: AsRef, P: AsRef, @@ -267,7 +276,7 @@ where fn parse_source_and_includes, R>( source: P, - resolver: &R, + resolver: &mut R, ) -> (Program, Vec, Vec) where R: SourceResolver, @@ -277,7 +286,7 @@ where (program, errors, included) } -fn parse_includes(program: &Program, resolver: &R) -> Vec +fn parse_includes(program: &Program, resolver: &mut R) -> Vec where R: SourceResolver, { diff --git a/compiler/qsc_qasm3/src/parser/tests.rs b/compiler/qsc_qasm3/src/parser/tests.rs index 30bd4e45d7..99ce264ffd 100644 --- a/compiler/qsc_qasm3/src/parser/tests.rs +++ b/compiler/qsc_qasm3/src/parser/tests.rs @@ -23,11 +23,11 @@ pub(crate) fn parse_all

    ( where P: AsRef, { - let resolver = InMemorySourceResolver::from_iter(sources); + let mut resolver = InMemorySourceResolver::from_iter(sources); let (path, source) = resolver .resolve(path.as_ref()) .map_err(|e| vec![Report::new(e)])?; - let res = crate::parser::parse_source(source, path, &resolver); + let res = crate::parser::parse_source(source, path, &mut resolver); if res.source.has_errors() { let errors = res .errors() @@ -44,8 +44,8 @@ pub(crate) fn parse(source: S) -> miette::Result where S: AsRef, { - let resolver = InMemorySourceResolver::from_iter([("test".into(), source.as_ref().into())]); - let res = parse_source(source, "test", &resolver); + let mut resolver = InMemorySourceResolver::from_iter([("test".into(), source.as_ref().into())]); + let res = parse_source(source, "test", &mut resolver); if res.source.has_errors() { let errors = res .errors() diff --git a/compiler/qsc_qasm3/src/semantic.rs b/compiler/qsc_qasm3/src/semantic.rs index e99dac9906..7780a6a159 100644 --- a/compiler/qsc_qasm3/src/semantic.rs +++ b/compiler/qsc_qasm3/src/semantic.rs @@ -96,18 +96,18 @@ where S: AsRef, P: AsRef, { - let resolver = InMemorySourceResolver::from_iter([( + let mut resolver = InMemorySourceResolver::from_iter([( path.as_ref().display().to_string().into(), source.as_ref().into(), )]); - parse_source(source, path, &resolver) + parse_source(source, path, &mut resolver) } /// Parse a QASM file and return the parse result. /// This function will resolve includes using the provided resolver. /// If an include file cannot be resolved, an error will be returned. /// If a file is included recursively, a stack overflow occurs. -pub fn parse_source(source: S, path: P, resolver: &R) -> QasmSemanticParseResult +pub fn parse_source(source: S, path: P, resolver: &mut R) -> QasmSemanticParseResult where S: AsRef, P: AsRef, diff --git a/compiler/qsc_qasm3/src/semantic/tests.rs b/compiler/qsc_qasm3/src/semantic/tests.rs index d2eb3f29a0..1b2aa04d14 100644 --- a/compiler/qsc_qasm3/src/semantic/tests.rs +++ b/compiler/qsc_qasm3/src/semantic/tests.rs @@ -29,11 +29,11 @@ pub(crate) fn parse_all

    ( where P: AsRef, { - let resolver = InMemorySourceResolver::from_iter(sources); + let mut resolver = InMemorySourceResolver::from_iter(sources); let (path, source) = resolver .resolve(path.as_ref()) .map_err(|e| vec![Report::new(e)])?; - let res = parse_source(source, path, &resolver); + let res = parse_source(source, path, &mut resolver); if res.source.has_errors() { let errors = res .errors() @@ -50,8 +50,8 @@ pub(crate) fn parse(source: S) -> miette::Result, { - let resolver = InMemorySourceResolver::from_iter([("test".into(), source.as_ref().into())]); - let res = parse_source(source, "test", &resolver); + let mut resolver = InMemorySourceResolver::from_iter([("test".into(), source.as_ref().into())]); + let res = parse_source(source, "test", &mut resolver); if res.source.has_errors() { let errors = res .errors() @@ -139,8 +139,8 @@ fn check_map( ) where S: AsRef, { - let resolver = InMemorySourceResolver::from_iter([("test".into(), input.as_ref().into())]); - let res = parse_source(input, "test", &resolver); + let mut resolver = InMemorySourceResolver::from_iter([("test".into(), input.as_ref().into())]); + let res = parse_source(input, "test", &mut resolver); let errors = res.all_errors(); @@ -182,13 +182,13 @@ fn check_map_all

    ( ) where P: AsRef, { - let resolver = InMemorySourceResolver::from_iter(sources); + let mut resolver = InMemorySourceResolver::from_iter(sources); let source = resolver .resolve(path.as_ref()) .map_err(|e| vec![e]) .expect("could not load source") .1; - let res = parse_source(source, path, &resolver); + let res = parse_source(source, path, &mut resolver); let errors = res.all_errors(); diff --git a/compiler/qsc_qasm3/src/tests.rs b/compiler/qsc_qasm3/src/tests.rs index e47f0d4a30..901ce15265 100644 --- a/compiler/qsc_qasm3/src/tests.rs +++ b/compiler/qsc_qasm3/src/tests.rs @@ -213,9 +213,9 @@ pub(crate) fn parse(source: S) -> miette::Result, { - let resolver = + let mut resolver = InMemorySourceResolver::from_iter([("Test.qasm".into(), source.as_ref().into())]); - let res = parse_source(source, "Test.qasm", &resolver); + let res = parse_source(source, "Test.qasm", &mut resolver); if res.source.has_errors() { let errors = res .errors() @@ -234,12 +234,12 @@ pub(crate) fn parse_all

    ( where P: AsRef, { - let resolver = InMemorySourceResolver::from_iter(sources); + let mut resolver = InMemorySourceResolver::from_iter(sources); let source = resolver .resolve(path.as_ref()) .map_err(|e| vec![Report::new(e)])? .1; - let res = parse_source(source, path, &resolver); + let res = parse_source(source, path, &mut resolver); if res.source.has_errors() { let errors = res .errors() diff --git a/compiler/qsc_qasm3/src/tests/statement/include.rs b/compiler/qsc_qasm3/src/tests/statement/include.rs index 19e4f96472..03e9e27717 100644 --- a/compiler/qsc_qasm3/src/tests/statement/include.rs +++ b/compiler/qsc_qasm3/src/tests/statement/include.rs @@ -59,3 +59,183 @@ fn programs_with_includes_can_be_parsed() -> miette::Result<(), Vec> { .assert_eq(&qsharp); Ok(()) } + +#[test] +fn multiple_include_in_same_file_errors() { + let main = r#" + include "source1.inc"; + include "source1.inc"; + "#; + let source1 = r#" + bit[1] c; + "#; + let all_sources = [ + ("main.qasm".into(), main.into()), + ("source1.inc".into(), source1.into()), + ]; + let config = CompilerConfig::new( + QubitSemantics::Qiskit, + OutputSemantics::Qiskit, + ProgramType::File, + Some("Test".into()), + None, + ); + + let Err(errors) = compile_all_with_config("main.qasm", all_sources, config) else { + panic!("expected errors") + }; + + let errors: Vec<_> = errors.iter().map(|e| format!("{e}")).collect(); + let errors_string = errors.join("\n"); + expect!["source1.inc was already included in: main.qasm"].assert_eq(&errors_string); +} + +#[test] +fn multiple_include_in_different_files_errors() { + let main = r#" + include "source1.inc"; + include "source2.inc"; + "#; + let source1 = r#" + include "source3.inc"; + "#; + let source2 = r#" + include "source3.inc"; + "#; + let source3 = r#" + bit[1] c; + "#; + let all_sources = [ + ("main.qasm".into(), main.into()), + ("source1.inc".into(), source1.into()), + ("source2.inc".into(), source2.into()), + ("source3.inc".into(), source3.into()), + ]; + let config = CompilerConfig::new( + QubitSemantics::Qiskit, + OutputSemantics::Qiskit, + ProgramType::File, + Some("Test".into()), + None, + ); + + let Err(errors) = compile_all_with_config("main.qasm", all_sources, config) else { + panic!("expected errors") + }; + + let errors: Vec<_> = errors.iter().map(|e| format!("{e}")).collect(); + let errors_string = errors.join("\n"); + expect!["source3.inc was already included in: source1.inc"].assert_eq(&errors_string); +} + +#[test] +fn self_include_errors() { + let main = r#" + include "source1.inc"; + "#; + let source1 = r#" + include "source1.inc"; + "#; + let all_sources = [ + ("main.qasm".into(), main.into()), + ("source1.inc".into(), source1.into()), + ]; + let config = CompilerConfig::new( + QubitSemantics::Qiskit, + OutputSemantics::Qiskit, + ProgramType::File, + Some("Test".into()), + None, + ); + + let Err(errors) = compile_all_with_config("main.qasm", all_sources, config) else { + panic!("expected errors") + }; + + let errors: Vec<_> = errors.iter().map(|e| format!("{e}")).collect(); + let errors_string = errors.join("\n"); + expect![[r#" + Cyclic include: + source1.inc includes source1.inc"#]] + .assert_eq(&errors_string); +} + +#[test] +fn mutual_include_errors() { + let main = r#" + include "source1.inc"; + "#; + let source1 = r#" + include "source2.inc"; + "#; + let source2 = r#" + include "source1.inc"; + "#; + let all_sources = [ + ("main.qasm".into(), main.into()), + ("source1.inc".into(), source1.into()), + ("source2.inc".into(), source2.into()), + ]; + let config = CompilerConfig::new( + QubitSemantics::Qiskit, + OutputSemantics::Qiskit, + ProgramType::File, + Some("Test".into()), + None, + ); + + let Err(errors) = compile_all_with_config("main.qasm", all_sources, config) else { + panic!("expected errors") + }; + + let errors: Vec<_> = errors.iter().map(|e| format!("{e}")).collect(); + let errors_string = errors.join("\n"); + expect![[r#" + Cyclic include: + source1.inc includes source2.inc + source2.inc includes source1.inc"#]] + .assert_eq(&errors_string); +} + +#[test] +fn cyclic_include_errors() { + let main = r#" + include "source1.inc"; + "#; + let source1 = r#" + include "source2.inc"; + "#; + let source2 = r#" + include "source3.inc"; + "#; + let source3 = r#" + include "source1.inc"; + "#; + let all_sources = [ + ("main.qasm".into(), main.into()), + ("source1.inc".into(), source1.into()), + ("source2.inc".into(), source2.into()), + ("source3.inc".into(), source3.into()), + ]; + let config = CompilerConfig::new( + QubitSemantics::Qiskit, + OutputSemantics::Qiskit, + ProgramType::File, + Some("Test".into()), + None, + ); + + let Err(errors) = compile_all_with_config("main.qasm", all_sources, config) else { + panic!("expected errors") + }; + + let errors: Vec<_> = errors.iter().map(|e| format!("{e}")).collect(); + let errors_string = errors.join("\n"); + + expect![[r#" + Cyclic include: + source1.inc includes source2.inc + source2.inc includes source3.inc + source3.inc includes source1.inc"#]] + .assert_eq(&errors_string); +} diff --git a/fuzz/fuzz_targets/qasm3.rs b/fuzz/fuzz_targets/qasm3.rs index ce7b6f2ee1..3e785f27f9 100644 --- a/fuzz/fuzz_targets/qasm3.rs +++ b/fuzz/fuzz_targets/qasm3.rs @@ -10,8 +10,8 @@ use libfuzzer_sys::fuzz_target; fn compile(data: &[u8]) { if let Ok(fuzzed_code) = std::str::from_utf8(data) { - let resolver = qsc::qasm3::io::InMemorySourceResolver::from_iter([]); - let _ = qsc::qasm3::parser::parse_source(fuzzed_code, "fuzz.qasm", &resolver); + let mut resolver = qsc::qasm3::io::InMemorySourceResolver::from_iter([]); + let _ = qsc::qasm3::parser::parse_source(fuzzed_code, "fuzz.qasm", &mut resolver); } } diff --git a/pip/src/interop.rs b/pip/src/interop.rs index 40f6c65a31..e6e7b4d9f0 100644 --- a/pip/src/interop.rs +++ b/pip/src/interop.rs @@ -11,8 +11,8 @@ use pyo3::types::{PyDict, PyList}; use qsc::hir::PackageId; use qsc::interpret::output::Receiver; use qsc::interpret::{into_errors, Interpreter}; -use qsc::qasm3::io::SourceResolver; use qsc::qasm3::io::{Error, ErrorKind}; +use qsc::qasm3::io::{SourceResolver, SourceResolverContext}; use qsc::qasm3::{ qasm_to_program, CompilerConfig, OperationSignature, QasmCompileUnit, QubitSemantics, }; @@ -38,6 +38,7 @@ where { fs: T, path: PathBuf, + ctx: SourceResolverContext, } impl ImportResolver @@ -48,6 +49,7 @@ where Self { fs, path: PathBuf::from(path.as_ref()), + ctx: Default::default(), } } } @@ -56,11 +58,16 @@ impl SourceResolver for ImportResolver where T: FileSystem, { - fn resolve

    (&self, path: P) -> miette::Result<(PathBuf, String), Error> + fn ctx(&mut self) -> &mut SourceResolverContext { + &mut self.ctx + } + + fn resolve

    (&mut self, path: P) -> miette::Result<(PathBuf, String), Error> where P: AsRef, { let path = self.path.join(path); + self.ctx().check_include_errors(&path)?; let (path, source) = self .fs .read_file(path.as_ref()) @@ -101,12 +108,12 @@ pub fn run_qasm3( let search_path = get_search_path(&kwargs)?; let fs = create_filesystem_from_py(py, read_file, list_directory, resolve_path, fetch_github); - let resolver = ImportResolver::new(fs, PathBuf::from(search_path)); + let mut resolver = ImportResolver::new(fs, PathBuf::from(search_path)); let (package, source_map, signature) = compile_qasm_enriching_errors( source, &operation_name, - &resolver, + &mut resolver, ProgramType::File, OutputSemantics::Qiskit, false, @@ -172,14 +179,14 @@ pub(crate) fn resource_estimate_qasm3( let search_path = get_search_path(&kwargs)?; let fs = create_filesystem_from_py(py, read_file, list_directory, resolve_path, fetch_github); - let resolver = ImportResolver::new(fs, PathBuf::from(search_path)); + let mut resolver = ImportResolver::new(fs, PathBuf::from(search_path)); let program_type = ProgramType::File; let output_semantics = OutputSemantics::ResourceEstimation; let (package, source_map, _) = compile_qasm_enriching_errors( source, &operation_name, - &resolver, + &mut resolver, program_type, output_semantics, false, @@ -235,14 +242,14 @@ pub(crate) fn compile_qasm3_to_qir( let search_path = get_search_path(&kwargs)?; let fs = create_filesystem_from_py(py, read_file, list_directory, resolve_path, fetch_github); - let resolver = ImportResolver::new(fs, PathBuf::from(search_path)); + let mut resolver = ImportResolver::new(fs, PathBuf::from(search_path)); let program_ty = get_program_type(&kwargs)?; let output_semantics = get_output_semantics(&kwargs)?; let (package, source_map, signature) = compile_qasm_enriching_errors( source, &operation_name, - &resolver, + &mut resolver, program_ty, output_semantics, false, @@ -261,7 +268,7 @@ pub(crate) fn compile_qasm3_to_qir( pub(crate) fn compile_qasm, R: SourceResolver>( source: S, operation_name: S, - resolver: &R, + resolver: &mut R, program_ty: ProgramType, output_semantics: OutputSemantics, ) -> PyResult { @@ -302,7 +309,7 @@ pub(crate) fn compile_qasm, R: SourceResolver>( pub(crate) fn compile_qasm_enriching_errors, R: SourceResolver>( source: S, operation_name: S, - resolver: &R, + resolver: &mut R, program_ty: ProgramType, output_semantics: OutputSemantics, allow_input_params: bool, @@ -374,14 +381,14 @@ pub(crate) fn compile_qasm3_to_qsharp( let search_path = get_search_path(&kwargs)?; let fs = create_filesystem_from_py(py, read_file, list_directory, resolve_path, fetch_github); - let resolver = ImportResolver::new(fs, PathBuf::from(search_path)); + let mut resolver = ImportResolver::new(fs, PathBuf::from(search_path)); let program_ty = get_program_type(&kwargs)?; let output_semantics = get_output_semantics(&kwargs)?; let (package, _, _) = compile_qasm_enriching_errors( source, &operation_name, - &resolver, + &mut resolver, program_ty, output_semantics, true, diff --git a/pip/src/interpreter.rs b/pip/src/interpreter.rs index 38d5b6505f..69f62157f4 100644 --- a/pip/src/interpreter.rs +++ b/pip/src/interpreter.rs @@ -677,12 +677,12 @@ impl Interpreter { resolve_path, fetch_github, ); - let resolver = ImportResolver::new(fs, PathBuf::from(search_path)); + let mut resolver = ImportResolver::new(fs, PathBuf::from(search_path)); let (package, _source_map, signature) = compile_qasm_enriching_errors( source, &operation_name, - &resolver, + &mut resolver, program_type, output_semantics, false, From 83cc9a56554d7bce5ad0cba8369f55cb5e8ed013 Mon Sep 17 00:00:00 2001 From: Ian Davis Date: Wed, 9 Apr 2025 14:43:45 -0700 Subject: [PATCH 094/108] Remove external parser. Fix Qiskit's failure to emit gates. (#2284) --- Cargo.lock | 395 +- Cargo.toml | 5 - compiler/qsc/src/compile.rs | 4 + compiler/qsc/src/lib.rs | 5 +- compiler/qsc/src/qasm.rs | 70 + compiler/qsc_qasm3/Cargo.toml | 4 - compiler/qsc_qasm3/src/compile.rs | 4295 ----------------- compiler/qsc_qasm3/src/compiler.rs | 24 +- compiler/qsc_qasm3/src/convert.rs | 24 + compiler/qsc_qasm3/src/lib.rs | 9 +- compiler/qsc_qasm3/src/oqasm_helpers.rs | 183 - compiler/qsc_qasm3/src/oqasm_types.rs | 184 - compiler/qsc_qasm3/src/parse.rs | 270 -- compiler/qsc_qasm3/src/parser.rs | 6 +- compiler/qsc_qasm3/src/parser/completion.rs | 9 +- compiler/qsc_qasm3/src/parser/expr.rs | 7 +- compiler/qsc_qasm3/src/parser/prgm.rs | 2 +- compiler/qsc_qasm3/src/parser/prim.rs | 2 +- compiler/qsc_qasm3/src/parser/prim/tests.rs | 2 +- compiler/qsc_qasm3/src/parser/scan.rs | 2 +- compiler/qsc_qasm3/src/parser/stmt.rs | 6 +- .../qsc_qasm3/src/semantic/ast/const_eval.rs | 2 +- compiler/qsc_qasm3/src/semantic/lowerer.rs | 73 +- compiler/qsc_qasm3/src/semantic/tests.rs | 24 +- .../stdlib/QasmStd/src/QasmStd/Intrinsic.qs | 432 +- compiler/qsc_qasm3/src/stdlib/angle.rs | 2 +- compiler/qsc_qasm3/src/symbols.rs | 435 -- .../src/tests/statement/gate_call.rs | 208 + compiler/qsc_qasm3/src/types.rs | 137 - fuzz/fuzz_targets/qasm3.rs | 4 +- .../interop/qiskit/backends/backend_base.py | 11 +- pip/src/interop.rs | 78 +- pip/src/interpreter.rs | 16 +- .../interop_qiskit/test_circuits/__init__.py | 2 +- .../interop_qiskit/test_gateset_qasm.py | 5 +- .../interop_qiskit/test_qir.py | 4 +- .../interop_qiskit/test_re.py | 8 +- .../interop_qiskit/test_run_sim.py | 21 +- 38 files changed, 934 insertions(+), 6036 deletions(-) create mode 100644 compiler/qsc/src/qasm.rs delete mode 100644 compiler/qsc_qasm3/src/compile.rs create mode 100644 compiler/qsc_qasm3/src/convert.rs delete mode 100644 compiler/qsc_qasm3/src/oqasm_helpers.rs delete mode 100644 compiler/qsc_qasm3/src/oqasm_types.rs delete mode 100644 compiler/qsc_qasm3/src/parse.rs delete mode 100644 compiler/qsc_qasm3/src/symbols.rs diff --git a/Cargo.lock b/Cargo.lock index f3bc3bb375..1971c0f52d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -17,17 +17,6 @@ version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" -[[package]] -name = "ahash" -version = "0.7.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "891477e0c6a8957309ee5c45a6368af3ae14bb510732d2684ffa19af310920f9" -dependencies = [ - "getrandom", - "once_cell", - "version_check", -] - [[package]] name = "aho-corasick" version = "1.1.3" @@ -114,17 +103,6 @@ version = "1.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7d5a26814d8dcb93b0e5a0ff3c6d80a8843bafb21b39e8e18a6f05471870e110" -[[package]] -name = "ariadne" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72fe02fc62033df9ba41cba57ee19acf5e742511a140c7dbc3a873e19a19a1bd" -dependencies = [ - "concolor", - "unicode-width", - "yansi", -] - [[package]] name = "async-trait" version = "0.1.81" @@ -133,7 +111,7 @@ checksum = "6e0c28dcc82d7c8ead5cb13beb15405b57b8546e93215673ff8ca0349a028107" dependencies = [ "proc-macro2", "quote", - "syn 2.0.87", + "syn", ] [[package]] @@ -166,29 +144,12 @@ dependencies = [ "backtrace", ] -[[package]] -name = "bitflags" -version = "1.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" - [[package]] name = "bitflags" version = "2.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de" -[[package]] -name = "boolenum" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a6c8abd585d7026df20a9ae12982127ba5e81cc7a09397b957e71659da8c5de8" -dependencies = [ - "proc-macro-error", - "quote", - "syn 1.0.109", -] - [[package]] name = "bumpalo" version = "3.16.0" @@ -288,7 +249,7 @@ dependencies = [ "heck", "proc-macro2", "quote", - "syn 2.0.87", + "syn", ] [[package]] @@ -303,38 +264,6 @@ version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d3fd119d74b830634cea2a0f58bbd0d54540518a14397557951e79340abc28c0" -[[package]] -name = "concolor" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b946244a988c390a94667ae0e3958411fa40cc46ea496a929b263d883f5f9c3" -dependencies = [ - "bitflags 1.3.2", - "concolor-query", - "is-terminal", -] - -[[package]] -name = "concolor-query" -version = "0.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "88d11d52c3d7ca2e6d0040212be9e4dbbcd78b6447f535b6b561f449427944cf" -dependencies = [ - "windows-sys 0.45.0", -] - -[[package]] -name = "countme" -version = "3.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7704b5fdd17b18ae31c4c1da5a2e0305a2bf17b5249300a9ee9ed7b72114c636" - -[[package]] -name = "cov-mark" -version = "2.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0570650661aa447e7335f1d5e4f499d8e58796e617bedc9267d971e51c8b49d4" - [[package]] name = "criterion" version = "0.5.1" @@ -387,12 +316,6 @@ version = "1.0.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "59f8e79d1fbf76bdfbde321e902714bf6c49df88a7dda6fc682fc2979226962d" -[[package]] -name = "drop_bomb" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9bda8e21c04aca2ae33ffc2fd8c23134f3cac46db123ba97bd9d3f3b8a4a85e1" - [[package]] name = "either" version = "1.13.0" @@ -416,7 +339,7 @@ checksum = "a1ab991c1362ac86c61ab6f556cff143daa22e5a15e4e189df818b2fd19fe65b" dependencies = [ "proc-macro2", "quote", - "syn 2.0.87", + "syn", ] [[package]] @@ -442,12 +365,6 @@ dependencies = [ "log", ] -[[package]] -name = "equivalent" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" - [[package]] name = "errno" version = "0.3.9" @@ -530,7 +447,7 @@ checksum = "87750cf4b7a4c0625b1529e4c543c2182106e4dedc60a2a6455e00d212c489ac" dependencies = [ "proc-macro2", "quote", - "syn 2.0.87", + "syn", ] [[package]] @@ -601,21 +518,6 @@ dependencies = [ "crunchy", ] -[[package]] -name = "hashbrown" -version = "0.12.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" -dependencies = [ - "ahash", -] - -[[package]] -name = "hashbrown" -version = "0.14.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" - [[package]] name = "heck" version = "0.5.0" @@ -640,16 +542,6 @@ version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ce23b50ad8242c51a442f3ff322d56b02f08852c77e4c0b4d3fd684abc89c683" -[[package]] -name = "indexmap" -version = "2.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "93ead53efc7ea8ed3cfb0c79fc8023fbb782a5432b52830b6518941cebe6505c" -dependencies = [ - "equivalent", - "hashbrown 0.14.5", -] - [[package]] name = "indoc" version = "2.0.5" @@ -817,7 +709,7 @@ checksum = "dcf09caffaac8068c346b6df2a7fc27a177fd20b39421a39ce0a211bde679a6c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.87", + "syn", ] [[package]] @@ -860,7 +752,7 @@ checksum = "254a5372af8fc138e36684761d3c0cdb758a4410e938babcff1c860ce14ddbfc" dependencies = [ "proc-macro2", "quote", - "syn 2.0.87", + "syn", ] [[package]] @@ -956,71 +848,6 @@ version = "11.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b410bbe7e14ab526a0e86877eb47c6996a2bd7746f027ba551028c925390e4e9" -[[package]] -name = "oq3_lexer" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a27bbc91e3e9d6193a44aac8f5d62c1507c41669af71a4e7e0ef66fd6470e960" -dependencies = [ - "unicode-properties", - "unicode-xid", -] - -[[package]] -name = "oq3_parser" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a72022fcb414e8a0912920a1cf46417b6aa95f19d4b38778df7450f8a3c17fa" -dependencies = [ - "drop_bomb", - "oq3_lexer", - "ra_ap_limit", -] - -[[package]] -name = "oq3_semantics" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b72dffd869f3548190c705828d030fbb7fca94e519dcfa6a489227e5c3ffd777" -dependencies = [ - "boolenum", - "hashbrown 0.12.3", - "oq3_source_file", - "oq3_syntax", - "rowan", -] - -[[package]] -name = "oq3_source_file" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ff8c03f1f92c7a8f0b5249664b526169ceb8f925cb314ff93d3b27d8a4afb78c" -dependencies = [ - "ariadne", - "oq3_syntax", -] - -[[package]] -name = "oq3_syntax" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "42c754ce1d9da28d6c0334c212d64b521288fe8c7cf16e9727d45dcf661ff084" -dependencies = [ - "cov-mark", - "either", - "indexmap", - "itertools", - "once_cell", - "oq3_lexer", - "oq3_parser", - "rowan", - "rustc-hash", - "rustversion", - "smol_str", - "triomphe", - "xshell", -] - [[package]] name = "owo-colors" version = "4.0.0" @@ -1070,30 +897,6 @@ dependencies = [ "special", ] -[[package]] -name = "proc-macro-error" -version = "1.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c" -dependencies = [ - "proc-macro-error-attr", - "proc-macro2", - "quote", - "syn 1.0.109", - "version_check", -] - -[[package]] -name = "proc-macro-error-attr" -version = "1.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869" -dependencies = [ - "proc-macro2", - "quote", - "version_check", -] - [[package]] name = "proc-macro2" version = "1.0.86" @@ -1153,7 +956,7 @@ dependencies = [ "proc-macro2", "pyo3-macros-backend", "quote", - "syn 2.0.87", + "syn", ] [[package]] @@ -1166,7 +969,7 @@ dependencies = [ "proc-macro2", "pyo3-build-config", "quote", - "syn 2.0.87", + "syn", ] [[package]] @@ -1276,7 +1079,7 @@ dependencies = [ name = "qsc_data_structures" version = "0.0.0" dependencies = [ - "bitflags 2.6.0", + "bitflags", "expect-test", "miette", "rustc-hash", @@ -1400,7 +1203,7 @@ dependencies = [ name = "qsc_parse" version = "0.0.0" dependencies = [ - "bitflags 2.6.0", + "bitflags", "enum-iterator", "expect-test", "indoc", @@ -1476,7 +1279,7 @@ dependencies = [ name = "qsc_qasm3" version = "0.0.0" dependencies = [ - "bitflags 2.6.0", + "bitflags", "difference", "enum-iterator", "expect-test", @@ -1485,10 +1288,6 @@ dependencies = [ "miette", "num-bigint", "num-traits", - "oq3_parser", - "oq3_semantics", - "oq3_source_file", - "oq3_syntax", "qsc", "qsc_ast", "qsc_data_structures", @@ -1505,7 +1304,7 @@ dependencies = [ name = "qsc_rca" version = "0.0.0" dependencies = [ - "bitflags 2.6.0", + "bitflags", "expect-test", "indenter", "miette", @@ -1617,12 +1416,6 @@ dependencies = [ "proc-macro2", ] -[[package]] -name = "ra_ap_limit" -version = "0.0.188" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "92d33758724f997689f84146e5401e28d875a061804f861f113696f44f5232aa" - [[package]] name = "rand" version = "0.8.5" @@ -1720,19 +1513,6 @@ dependencies = [ "thiserror", ] -[[package]] -name = "rowan" -version = "0.15.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32a58fa8a7ccff2aec4f39cc45bf5f985cec7125ab271cf681c279fd00192b49" -dependencies = [ - "countme", - "hashbrown 0.14.5", - "memoffset", - "rustc-hash", - "text-size", -] - [[package]] name = "rustc-demangle" version = "0.1.24" @@ -1751,7 +1531,7 @@ version = "0.38.35" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a85d50532239da68e9addb745ba38ff4612a242c1c7ceea689c4bc7c2f43c36f" dependencies = [ - "bitflags 2.6.0", + "bitflags", "errno", "libc", "linux-raw-sys", @@ -1825,7 +1605,7 @@ checksum = "5a9bf7cf98d04a2b28aead066b7496853d4779c9cc183c440dbac457641e19a0" dependencies = [ "proc-macro2", "quote", - "syn 2.0.87", + "syn", ] [[package]] @@ -1874,15 +1654,6 @@ version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b7c388c1b5e93756d0c740965c41e8822f866621d41acbdf6336a6a168f8840c" -[[package]] -name = "smol_str" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dd538fb6910ac1099850255cf94a94df6551fbdd602454387d0adb2d1ca6dead" -dependencies = [ - "serde", -] - [[package]] name = "special" version = "0.10.3" @@ -1919,17 +1690,6 @@ version = "3.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b7401a30af6cb5818bb64852270bb722533397edcfc7344954a38f420819ece2" -[[package]] -name = "syn" -version = "1.0.109" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" -dependencies = [ - "proc-macro2", - "quote", - "unicode-ident", -] - [[package]] name = "syn" version = "2.0.87" @@ -1957,12 +1717,6 @@ dependencies = [ "windows-sys 0.48.0", ] -[[package]] -name = "text-size" -version = "1.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f18aa187839b2bdb1ad2fa35ead8c4c2976b64e4363c386d45ac0f7ee85c9233" - [[package]] name = "textwrap" version = "0.16.1" @@ -1991,7 +1745,7 @@ checksum = "a4558b58466b9ad7ca0f102865eccc95938dca1a74a856f2b57b6629050da261" dependencies = [ "proc-macro2", "quote", - "syn 2.0.87", + "syn", ] [[package]] @@ -2023,15 +1777,9 @@ checksum = "6e06d43f1345a3bcd39f6a56dbb7dcab2ba47e68e8ac134855e7e2bdbaf8cab8" dependencies = [ "proc-macro2", "quote", - "syn 2.0.87", + "syn", ] -[[package]] -name = "triomphe" -version = "0.1.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "859eb650cfee7434994602c3a68b25d77ad9e68c8a6cd491616ef86661382eb3" - [[package]] name = "typenum" version = "1.17.0" @@ -2050,24 +1798,12 @@ version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3b09c83c3c29d37506a3e260c08c03743a6bb66a9cd432c6934ab501a190571f" -[[package]] -name = "unicode-properties" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "52ea75f83c0137a9b98608359a5f1af8144876eb67bcb1ce837368e906a9f524" - [[package]] name = "unicode-width" version = "0.1.13" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0336d538f7abc86d282a4189614dfaa90810dfc2c6f6427eaf88e16311dd225d" -[[package]] -name = "unicode-xid" -version = "0.2.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "229730647fbc343e3a80e463c1db7f78f3855d3f3739bee0dda773c9a037c90a" - [[package]] name = "unindent" version = "0.2.3" @@ -2080,12 +1816,6 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" -[[package]] -name = "version_check" -version = "0.9.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" - [[package]] name = "walkdir" version = "2.5.0" @@ -2124,7 +1854,7 @@ dependencies = [ "log", "proc-macro2", "quote", - "syn 2.0.87", + "syn", "wasm-bindgen-shared", ] @@ -2158,7 +1888,7 @@ checksum = "8ae87ea40c9f689fc23f209965b6fb8a99ad69aeeb0231408be24920604395de" dependencies = [ "proc-macro2", "quote", - "syn 2.0.87", + "syn", "wasm-bindgen-backend", "wasm-bindgen-shared", ] @@ -2201,15 +1931,6 @@ dependencies = [ "windows-sys 0.59.0", ] -[[package]] -name = "windows-sys" -version = "0.45.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "75283be5efb2831d37ea142365f009c02ec203cd29a3ebecbc093d52315b66d0" -dependencies = [ - "windows-targets 0.42.2", -] - [[package]] name = "windows-sys" version = "0.48.0" @@ -2237,21 +1958,6 @@ dependencies = [ "windows-targets 0.52.6", ] -[[package]] -name = "windows-targets" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e5180c00cd44c9b1c88adb3693291f1cd93605ded80c250a75d472756b4d071" -dependencies = [ - "windows_aarch64_gnullvm 0.42.2", - "windows_aarch64_msvc 0.42.2", - "windows_i686_gnu 0.42.2", - "windows_i686_msvc 0.42.2", - "windows_x86_64_gnu 0.42.2", - "windows_x86_64_gnullvm 0.42.2", - "windows_x86_64_msvc 0.42.2", -] - [[package]] name = "windows-targets" version = "0.48.5" @@ -2283,12 +1989,6 @@ dependencies = [ "windows_x86_64_msvc 0.52.6", ] -[[package]] -name = "windows_aarch64_gnullvm" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "597a5118570b68bc08d8d59125332c54f1ba9d9adeedeef5b99b02ba2b0698f8" - [[package]] name = "windows_aarch64_gnullvm" version = "0.48.5" @@ -2301,12 +2001,6 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" -[[package]] -name = "windows_aarch64_msvc" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e08e8864a60f06ef0d0ff4ba04124db8b0fb3be5776a5cd47641e942e58c4d43" - [[package]] name = "windows_aarch64_msvc" version = "0.48.5" @@ -2319,12 +2013,6 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" -[[package]] -name = "windows_i686_gnu" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c61d927d8da41da96a81f029489353e68739737d3beca43145c8afec9a31a84f" - [[package]] name = "windows_i686_gnu" version = "0.48.5" @@ -2343,12 +2031,6 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" -[[package]] -name = "windows_i686_msvc" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "44d840b6ec649f480a41c8d80f9c65108b92d89345dd94027bfe06ac444d1060" - [[package]] name = "windows_i686_msvc" version = "0.48.5" @@ -2361,12 +2043,6 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" -[[package]] -name = "windows_x86_64_gnu" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8de912b8b8feb55c064867cf047dda097f92d51efad5b491dfb98f6bbb70cb36" - [[package]] name = "windows_x86_64_gnu" version = "0.48.5" @@ -2379,12 +2055,6 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" -[[package]] -name = "windows_x86_64_gnullvm" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26d41b46a36d453748aedef1486d5c7a85db22e56aff34643984ea85514e94a3" - [[package]] name = "windows_x86_64_gnullvm" version = "0.48.5" @@ -2397,12 +2067,6 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" -[[package]] -name = "windows_x86_64_msvc" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0" - [[package]] name = "windows_x86_64_msvc" version = "0.48.5" @@ -2415,27 +2079,6 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" -[[package]] -name = "xshell" -version = "0.2.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6db0ab86eae739efd1b054a8d3d16041914030ac4e01cd1dca0cf252fd8b6437" -dependencies = [ - "xshell-macros", -] - -[[package]] -name = "xshell-macros" -version = "0.2.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9d422e8e38ec76e2f06ee439ccc765e9c6a9638b9e7c9f2e8255e4d41e8bd852" - -[[package]] -name = "yansi" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09041cd90cf85f7f8b2df60c646f853b7f535ce68f85244eb6731cf89fa498ec" - [[package]] name = "zerocopy" version = "0.7.35" @@ -2454,5 +2097,5 @@ checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e" dependencies = [ "proc-macro2", "quote", - "syn 2.0.87", + "syn", ] diff --git a/Cargo.toml b/Cargo.toml index 0932575714..a41f5caaf5 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -62,11 +62,6 @@ ndarray = "0.15.4" num-bigint = "0.4" num-complex = "0.4" num-traits = "0.2" -oq3_source_file = "0.7.0" -oq3_syntax = "0.7.0" -oq3_parser = "0.7.0" -oq3_lexer = "0.7.0" -oq3_semantics = "0.7.0" probability = "0.20" indenter = "0.3" regex-lite = "0.1" diff --git a/compiler/qsc/src/compile.rs b/compiler/qsc/src/compile.rs index d0e97abe4c..a26252e820 100644 --- a/compiler/qsc/src/compile.rs +++ b/compiler/qsc/src/compile.rs @@ -41,6 +41,10 @@ pub enum ErrorKind { #[error("{0}")] /// `CircuitParse` variant represents errors that occur while parsing circuit files. CircuitParse(String), + + /// `OpenQASM` compilation errors. + #[diagnostic(transparent)] + OpenQasm(#[from] crate::qasm::error::Error), } /// Compiles a package from its AST representation. diff --git a/compiler/qsc/src/lib.rs b/compiler/qsc/src/lib.rs index 539f54516d..abfce34b34 100644 --- a/compiler/qsc/src/lib.rs +++ b/compiler/qsc/src/lib.rs @@ -78,7 +78,4 @@ pub mod partial_eval { pub use qsc_partial_eval::Error; } -pub mod qasm3 { - pub use qsc_qasm3::parse::*; - pub use qsc_qasm3::*; -} +pub mod qasm; diff --git a/compiler/qsc/src/qasm.rs b/compiler/qsc/src/qasm.rs new file mode 100644 index 0000000000..67c1d6db58 --- /dev/null +++ b/compiler/qsc/src/qasm.rs @@ -0,0 +1,70 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +use std::path::Path; + +use qsc_qasm3::io::SourceResolver; +pub use qsc_qasm3::CompilerConfig; +pub use qsc_qasm3::OperationSignature; +pub use qsc_qasm3::OutputSemantics; +pub use qsc_qasm3::ProgramType; +pub use qsc_qasm3::QasmCompileUnit; +pub use qsc_qasm3::QubitSemantics; +pub mod io { + pub use qsc_qasm3::io::*; +} +pub mod parser { + pub use qsc_qasm3::parser::*; +} +pub mod error { + pub use qsc_qasm3::Error; + pub use qsc_qasm3::ErrorKind; +} +pub mod completion { + pub use qsc_qasm3::parser::completion::*; +} +pub use qsc_qasm3::compile_to_qsharp_ast_with_config; +pub use qsc_qasm3::package_store_with_qasm; + +#[must_use] +pub fn parse_raw_qasm_as_fragments( + source: S, + path: P, + resolver: Option<&mut R>, +) -> QasmCompileUnit +where + S: AsRef, + P: AsRef, + R: SourceResolver, +{ + let config = CompilerConfig::new( + QubitSemantics::Qiskit, + OutputSemantics::OpenQasm, + ProgramType::Fragments, + None, + None, + ); + compile_to_qsharp_ast_with_config(source, path, resolver, config) +} + +#[must_use] +pub fn parse_raw_qasm_as_operation( + source: S, + name: S, + path: P, + resolver: Option<&mut R>, +) -> QasmCompileUnit +where + S: AsRef, + P: AsRef, + R: SourceResolver, +{ + let config = CompilerConfig::new( + QubitSemantics::Qiskit, + OutputSemantics::OpenQasm, + ProgramType::Operation, + Some(name.as_ref().into()), + None, + ); + compile_to_qsharp_ast_with_config(source, path, resolver, config) +} diff --git a/compiler/qsc_qasm3/Cargo.toml b/compiler/qsc_qasm3/Cargo.toml index 8f95afc821..0ba3b89e06 100644 --- a/compiler/qsc_qasm3/Cargo.toml +++ b/compiler/qsc_qasm3/Cargo.toml @@ -22,10 +22,6 @@ qsc_parse = { path = "../qsc_parse" } qsc_passes = { path = "../qsc_passes" } rustc-hash = { workspace = true } thiserror = { workspace = true } -oq3_source_file = { workspace = true } -oq3_syntax = { workspace = true } -oq3_parser = { workspace = true } -oq3_semantics = { workspace = true } [dev-dependencies] difference = { workspace = true } diff --git a/compiler/qsc_qasm3/src/compile.rs b/compiler/qsc_qasm3/src/compile.rs deleted file mode 100644 index bcfecbaefb..0000000000 --- a/compiler/qsc_qasm3/src/compile.rs +++ /dev/null @@ -1,4295 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. - -use core::panic; -use std::path::PathBuf; - -use crate::ast_builder::{ - self, build_arg_pat, build_array_reverse_expr, build_assignment_statement, build_attr, - build_barrier_call, build_binary_expr, build_cast_call, build_classical_decl, - build_complex_binary_expr, build_complex_from_expr, build_convert_call_expr, - build_default_result_array_expr, build_expr_array_expr, build_gate_call_param_expr, - build_global_call_with_two_params, build_if_expr_then_block, - build_if_expr_then_block_else_block, build_if_expr_then_block_else_expr, - build_if_expr_then_expr_else_expr, build_implicit_return_stmt, - build_indexed_assignment_statement, build_lambda, build_lit_bigint_expr, build_lit_bool_expr, - build_lit_complex_expr, build_lit_double_expr, build_lit_int_expr, - build_lit_result_array_expr_from_bitstring, build_lit_result_expr, build_managed_qubit_alloc, - build_math_call_no_params, build_measure_call, build_operation_with_stmts, - build_path_ident_expr, build_range_expr, build_reset_call, build_stmt_semi_from_expr, - build_stmt_wrapped_block_expr, build_top_level_ns_with_item, build_tuple_expr, - build_unary_op_expr, build_unmanaged_qubit_alloc, build_unmanaged_qubit_alloc_array, - build_wrapped_block_expr, is_complex_binop_supported, managed_qubit_alloc_array, - map_qsharp_type_to_ast_ty, -}; - -use crate::oqasm_helpers::{ - binop_requires_symmetric_int_conversion, can_cast_literal_with_value_knowledge, - extract_dims_from_designator, get_designator_from_scalar_type, requires_symmetric_conversion, - requires_types_already_match_conversion, safe_u128_to_f64, span_for_named_item, - span_for_syntax_node, span_for_syntax_token, -}; -use crate::oqasm_types::{promote_to_uint_ty, promote_types, types_equal_except_const}; -use crate::runtime::{get_runtime_function_decls, RuntimeFunctions}; -use crate::symbols::IOKind; -use crate::symbols::Symbol; -use crate::symbols::SymbolTable; -use crate::types::{get_indexed_type, get_qsharp_gate_name, GateModifier, QasmTypedExpr}; -use crate::{ - semantic::SemanticErrorKind, CompilerConfig, OperationSignature, OutputSemantics, ProgramType, - QubitSemantics, -}; - -use ast::NodeId; -use ast::Package; -use ast::TopLevelNode; -use num_bigint::BigInt; -use oq3_semantics::types::{ArrayDims, IsConst, Type}; -use oq3_syntax::ast::{ - AnnotationStatement, ArithOp, BinaryOp, BitString, CastExpression, DelayStmt, Expr, - GateOperand, HasArgList, HasName, Literal, LiteralKind, Modifier, ParamList, ParenExpr, - PragmaStatement, Stmt, TimeUnit, TimingLiteral, UnaryOp, -}; -use oq3_syntax::SyntaxNode; -use oq3_syntax::{AstNode, HasTextName}; -use qsc_ast::ast; -use qsc_data_structures::span::Span; -use qsc_frontend::{compile::SourceMap, error::WithSource}; - -use crate::{parse::QasmSource, QasmCompileUnit}; - -#[cfg(test)] -pub(crate) mod tests; - -/// Compiles a QASM3 source to a Q# AST package. -#[must_use] -pub fn qasm_to_program( - source: QasmSource, - source_map: SourceMap, - config: CompilerConfig, -) -> QasmCompileUnit { - let compiler = QasmCompiler { - source, - source_map, - config, - stmts: Vec::new(), - runtime: RuntimeFunctions::default(), - errors: Vec::new(), - file_stack: Vec::new(), - symbols: SymbolTable::new(), - version: None, - next_gate_as_item: false, - }; - compiler.compile_program() -} - -struct QasmCompiler { - /// The root QASM source to compile. - source: QasmSource, - /// The source map of QASM sources for error reporting. - source_map: SourceMap, - /// The configuration for the compiler. - /// This includes the qubit semantics to follow when compiling to Q# AST. - /// The output semantics to follow when compiling to Q# AST. - /// The program type to compile to. - config: CompilerConfig, - /// The compiled statments accumulated during compilation. - stmts: Vec, - /// The runtime functions that need to be included at the end of - /// compilation - runtime: RuntimeFunctions, - errors: Vec>, - /// The file stack is used to track the current file for error reporting. - /// When we include a file, we push the file path to the stack and pop it - /// when we are done with the file. - /// This allows us to report errors with the correct file path. - file_stack: Vec, - symbols: SymbolTable, - /// The QASM version parsed from the source file. This is a placeholder - /// for future use. We may want to use this to generate/parse different code - /// based on the QASM version. - version: Option, - /// If the next gate should be compiled as a top level item instead of a lambda. - /// In order to close over captured variables, we compile gates as a lambda - /// operations; however, if the gate is annotated, we need to compile it as a - /// top level item as attributes are not supported on lambdas. This isn't an - /// issue as any gates that need attributes can't be used in a lambda anyway. - /// This value is set once we encounter an annotation statement and is reset - /// after the next gate is compiled, we run out of statements, or we encounter - /// an error. - next_gate_as_item: bool, -} - -impl QasmCompiler { - /// The main entry into compilation. This function will compile the - /// source file and build the appropriate package based on the - /// configuration. - fn compile_program(mut self) -> QasmCompileUnit { - self.compile_source(&self.source.clone()); - self.prepend_runtime_decls(); - let program_ty = self.config.program_ty.clone(); - let (package, signature) = match program_ty { - ProgramType::File => self.build_file(), - ProgramType::Operation => self.build_operation(), - ProgramType::Fragments => (self.build_fragments(), None), - }; - - QasmCompileUnit::new(self.source_map, self.errors, Some(package), signature) - } - - /// Prepends the runtime declarations to the beginning of the statements. - /// Any runtime functions that are required by the compiled code are set - /// in the `self.runtime` field during compilation. - /// - /// We could declare these as top level functions when compiling to - /// `ProgramType::File`, but prepending them to the statements is the - /// most flexible approach. - fn prepend_runtime_decls(&mut self) { - let mut runtime = get_runtime_function_decls(self.runtime); - self.stmts.splice(0..0, runtime.drain(..)); - } - - /// Build a package with namespace and an operation - /// containing the compiled statements. - fn build_file(&mut self) -> (Package, Option) { - let tree = self.source.tree(); - let whole_span = span_for_syntax_node(tree.syntax()); - let operation_name = self.config.operation_name(); - let (operation, mut signature) = self.create_entry_operation(operation_name, whole_span); - let ns = self.config.namespace(); - signature.ns = Some(ns.to_string()); - let top = build_top_level_ns_with_item(whole_span, ns, operation); - ( - Package { - nodes: Box::new([top]), - ..Default::default() - }, - Some(signature), - ) - } - - /// Creates an operation with the given name. - fn build_operation(&mut self) -> (Package, Option) { - let tree = self.source.tree(); - let whole_span = span_for_syntax_node(tree.syntax()); - let operation_name = self.config.operation_name(); - let (operation, signature) = self.create_entry_operation(operation_name, whole_span); - ( - Package { - nodes: Box::new([TopLevelNode::Stmt(Box::new(ast::Stmt { - kind: Box::new(ast::StmtKind::Item(Box::new(operation))), - span: whole_span, - id: NodeId::default(), - }))]), - ..Default::default() - }, - Some(signature), - ) - } - - /// Turns the compiled statements into package of top level nodes - fn build_fragments(&mut self) -> Package { - let nodes = self - .stmts - .drain(..) - .map(Box::new) - .map(TopLevelNode::Stmt) - .collect::>() - .into_boxed_slice(); - Package { - nodes, - ..Default::default() - } - } - - /// Root recursive function for compiling the source. - fn compile_source(&mut self, source: &QasmSource) { - // we push the file path to the stack so we can track the current file - // for reporting errors. This saves us from having to pass around - // the current QasmSource value. - self.file_stack.push(source.path()); - - let mut annotations = Vec::new(); - - // we keep an iterator of the includes so we can match them with the - // source includes. The include statements only have the path, but - // we have already loaded all of source files in the - // `source.includes()` - let mut includes = source.includes().iter(); - for stmt in source.tree().statements() { - match stmt { - Stmt::AnnotationStatement(annotation) => { - if let Some(annotation) = self.compile_annotation_stmt(&annotation) { - annotations.push(annotation); - self.next_gate_as_item = true; - } - } - Stmt::Include(include) => { - let Some(Some(path)) = include.file().map(|f| f.to_string()) else { - let span = span_for_syntax_node(include.syntax()); - let kind = SemanticErrorKind::IncludeStatementMissingPath(span); - self.push_semantic_error(kind); - continue; - }; - - // if we are not in the root we should not be able to include - // as this is a limitation of the QASM3 language - if !self.symbols.is_current_scope_global() { - let kind = SemanticErrorKind::IncludeNotInGlobalScope( - path.to_string(), - span_for_syntax_node(include.syntax()), - ); - self.push_semantic_error(kind); - continue; - } - - // special case for stdgates.inc - // it won't be in the includes list - if path.to_lowercase() == "stdgates.inc" { - self.define_stdgates(&include); - continue; - } - - let include = includes.next().expect("missing include"); - self.compile_source(include); - } - _ => { - if let Some(stmt) = self.compile_stmt(&stmt) { - if annotations.is_empty() { - self.stmts.push(stmt); - continue; - } - - // we drain the annotations regardless of whether the statement - // can have them attached or not. This is to prevent the attrs - // from being attached to the wrong statement. - - // If there is an error, we record the error, push the stmt - // without the annotations, and continue. - // The error is fatal for overall compilation, but this way we - // can continue to compile the rest of the statements - let mut stmt = stmt; - self.apply_annotations_to_stmt(&mut annotations, &mut stmt); - self.stmts.push(stmt); - self.next_gate_as_item = false; - } - } - } - } - - if !annotations.is_empty() { - let span = annotations.last().map(|x| x.span).unwrap_or_default(); - let kind = SemanticErrorKind::AnnotationWithoutStatement(span); - self.push_semantic_error(kind); - self.next_gate_as_item = false; - } - - // Finally we pop the file path from the stack so that we - // can return to the previous file for error handling. - self.file_stack.pop(); - } - - fn compile_stmts(&mut self, stmt: &[oq3_syntax::ast::Stmt]) -> Vec { - let mut annotations = Vec::new(); - let mut stmts = Vec::new(); - for stmt in stmt { - if let Stmt::AnnotationStatement(annotation) = stmt { - // we compile the annotation and push it to the annotations. - // If compiling fails, we record the error and continue. - if let Some(annotation) = self.compile_annotation_stmt(annotation) { - annotations.push(annotation); - self.next_gate_as_item = true; - } - continue; - } - let stmt = self.compile_stmt(stmt); - if stmt.is_none() { - continue; - } - let stmt = stmt.expect("stmt is not None"); - if annotations.is_empty() { - stmts.push(stmt); - continue; - } - let mut stmt = stmt; - - // we drain the annotations regardless of whether the statement - // can have them attached or not. This is to prevent the attrs - // from being attached to the wrong statement. - - // If there is an error, we record the error, push the stmt - // without the annotations, and continue. - // The error is fatal for overall compilation, but this way we - // can continue to compile the rest of the statements - - self.apply_annotations_to_stmt(&mut annotations, &mut stmt); - self.next_gate_as_item = false; - } - if !annotations.is_empty() { - let span = annotations.last().map(|x| x.span).unwrap_or_default(); - let kind = SemanticErrorKind::AnnotationWithoutStatement(span); - self.push_semantic_error(kind); - self.next_gate_as_item = false; - } - stmts - } - - fn apply_annotations_to_stmt( - &mut self, - annotations: &mut Vec, - stmt: &mut ast::Stmt, - ) { - let current_annotations: Vec<_> = annotations.drain(..).map(Box::new).collect(); - if let ast::StmtKind::Item(item) = stmt.kind.as_mut() { - if let ast::ItemKind::Callable(_) = item.kind.as_ref() { - let mut existing_attrs = item.attrs.to_vec(); - existing_attrs.extend(current_annotations); - item.attrs = existing_attrs.into_boxed_slice(); - } else { - let kind = SemanticErrorKind::InvalidAnnotationTarget(stmt.span); - self.push_semantic_error(kind); - } - } else { - let kind = SemanticErrorKind::InvalidAnnotationTarget(stmt.span); - self.push_semantic_error(kind); - } - } - - /// Match against the different types of statements and compile them - /// to the appropriate AST statement. There should be no logic here. - fn compile_stmt(&mut self, stmt: &oq3_syntax::ast::Stmt) -> Option { - match stmt { - Stmt::AliasDeclarationStatement(alias) => self.compile_alias_decl(alias), - Stmt::AssignmentStmt(assignment) => self.compile_assignment_stmt(assignment), - Stmt::Barrier(barrier) => self.compile_barrier_stmt(barrier), - Stmt::BreakStmt(break_stmt) => self.compile_break_stmt(break_stmt), - Stmt::ClassicalDeclarationStatement(decl) => self.compile_classical_decl(decl), - Stmt::ContinueStmt(continue_stmt) => self.compile_continue_stmt(continue_stmt), - Stmt::Def(def) => self.compile_def_decl(def), - Stmt::EndStmt(end) => Some(compile_end_stmt(end)), - Stmt::ExprStmt(expr) => self.compile_expr_stmt(expr), - Stmt::ForStmt(for_stmt) => self.compile_for_stmt(for_stmt), - Stmt::Gate(gate) => self.compile_gate_decl(gate), - Stmt::IfStmt(if_stmt) => self.compile_if_stmt(if_stmt), - Stmt::IODeclarationStatement(io_decl) => self.compile_io_decl_stmt(io_decl), - Stmt::LetStmt(let_stmt) => self.compile_let_stmt(let_stmt), - Stmt::Measure(measure) => self.compile_measure_stmt(measure), - Stmt::QuantumDeclarationStatement(decl) => self.compile_quantum_decl(decl), - Stmt::Reset(reset) => self.compile_reset_call(reset), - Stmt::SwitchCaseStmt(switch_case) => self.compile_switch_stmt(switch_case), - Stmt::VersionString(version) => self.compile_version_stmt(version), - Stmt::WhileStmt(while_stmt) => self.compile_while_stmt(while_stmt), - Stmt::Include(include) => self.compile_include_stmt(include), - Stmt::Cal(..) | Stmt::DefCal(..) | Stmt::DefCalGrammar(..) => { - self.compile_calibration_stmt(stmt) - } - Stmt::AnnotationStatement(..) => { - panic!("Annotation statements should have been handled in compile_stmts") - } - Stmt::DelayStmt(delay) => self.compile_delay_stmt(delay), - Stmt::PragmaStatement(pragma) => self.compile_pragma_stmt(pragma), - } - } - - fn compile_pragma_stmt(&mut self, stmt: &PragmaStatement) -> Option { - self.push_unsupported_error_message("Pragma statements", stmt.syntax()); - None - } - - fn compile_delay_stmt(&mut self, stmt: &DelayStmt) -> Option { - self.push_unsupported_error_message("Delay statements", stmt.syntax()); - None - } - - /// Annotations are defined by the compiler and their behavior are not part of the QASM3 - /// specification. They start with an `@` symbol and are used to define attributes for - /// the next statement. Their values are not validated by the compiler and are passed - /// directly to the AST as a string containing the whole line. It is up to the compiler - /// to interpret the annotation. - /// - /// We use annotations to define intrinsic gates that are simulatable in Q# mapping the - /// annotation to a Q# attribute. We also only allow the annotation to be attached to - /// gates. Any other usage will result in a semantic error. - fn compile_annotation_stmt(&mut self, stmt: &AnnotationStatement) -> Option { - let text = stmt.annotation_text(); - let span = span_for_syntax_node(stmt.syntax()); - if let "@SimulatableIntrinsic" = text.as_str() { - let (_at, name) = text.split_at(1); - Some(build_attr(name.to_string(), None, span)) - } else { - let span = span_for_syntax_node(stmt.syntax()); - let kind = SemanticErrorKind::UnknownAnnotation(text.to_string(), span); - self.push_semantic_error(kind); - None - } - } - - /// We don't support callibration statements in Q# so we push an error - fn compile_calibration_stmt(&mut self, stmt: &Stmt) -> Option { - self.push_calibration_error(stmt.syntax()); - None - } - - /// This function is always a indication of a error. Either the - /// program is declaring include in a non-global scope or the - /// include is not handled in `self.compile_source` properly. - fn compile_include_stmt(&mut self, include: &oq3_syntax::ast::Include) -> Option { - // if we are not in the root we should not be able to include - if !self.symbols.is_current_scope_global() { - let name = include.to_string(); - let span = span_for_syntax_node(include.syntax()); - let kind = SemanticErrorKind::IncludeNotInGlobalScope(name, span); - self.push_semantic_error(kind); - return None; - } - // if we are at the root and we have an include, we should have - // already handled it and we are in an invalid state - panic!("Include should have been handled in compile_source") - } - - /// Aliasing allows declared qubit bits and registers to be referred to by another name. - /// Aliasing can slice a qubit array or register. Aliasing uses the `let` keyword. - /// Example: - /// ```qasm - /// qubit[4] = q; - /// let qubit = q[1:3]; // qubit now points to the middle two qubits of q - /// // qubit[0] is now q[1], and qubit[1] is now q[2] - /// ``` - /// We can eventually support this for qubits, but we don't support it for registers. - /// Creating an array slice of results in Q# would be difficult as it would normally - /// copy the conents of the array. We could potentially create a view of the array, but - /// further investigation is needed. - fn compile_alias_decl( - &mut self, - alias: &oq3_syntax::ast::AliasDeclarationStatement, - ) -> Option { - let name = alias.name().expect("Alias declaration must have a name"); - let name_span = span_for_named_item(alias); - let decl_span = span_for_syntax_node(alias.syntax()); - let rhs = alias.expr().and_then(|f| self.compile_expr(&f))?; - - if !matches!(rhs.ty, Type::Qubit | Type::QubitArray(_)) { - let kind = SemanticErrorKind::CannotAliasType(format!("{:?}", rhs.ty), decl_span); - self.push_semantic_error(kind); - return None; - } - - let qsharp_ty = self.convert_semantic_type_to_qsharp_type(&rhs.ty, name.syntax())?; - let symbol = Symbol { - name: name.to_string(), - span: span_for_syntax_node(name.syntax()), - ty: rhs.ty.clone(), - qsharp_ty, - io_kind: IOKind::Default, - }; - - if self.symbols.insert_symbol(symbol).is_err() { - self.push_redefined_symbol_error(name.to_string(), name_span); - return None; - } - - let name = name.to_string(); - let is_const = true; - let ty_span = Span::default(); - let qsharp_ty = self.convert_semantic_type_to_qsharp_type(&rhs.ty, alias.syntax())?; - let stmt = build_classical_decl( - name, is_const, ty_span, decl_span, name_span, &qsharp_ty, rhs.expr, - ); - Some(stmt) - } - - /// Assignment statements have two forms: simple and indexed. - /// Simple assignments are of the form `ident = expr;` and indexed - /// assignments are of the form `ident[index] = expr;`. - /// This function will dispatch to the appropriate function based - /// on the type of assignment. - /// - /// While they are similar, the indexed assignment is far more complex. - fn compile_assignment_stmt( - &mut self, - assignment: &oq3_syntax::ast::AssignmentStmt, - ) -> Option { - if let Some(name) = assignment.identifier() { - self.compile_simple_assignment_stmt(assignment, &name) - } else { - self.compile_indexed_assignment_stmt(assignment) - } - } - - /// `ident = expr;` - fn compile_simple_assignment_stmt( - &mut self, - assignment: &oq3_syntax::ast::AssignmentStmt, - name: &oq3_syntax::ast::Identifier, - ) -> Option { - let name_span = span_for_named_item(assignment); - let assignment_span = span_for_syntax_node(assignment.syntax()); - let lhs_symbol = self - .symbols - .get_symbol_by_name(name.to_string().as_str())? - .clone(); - if lhs_symbol.ty.is_const() { - let kind = SemanticErrorKind::CannotUpdateConstVariable(name.to_string(), name_span); - self.push_semantic_error(kind); - // usually we'd return None here, but we'll continue to compile the rhs - // looking for more errors. There is nothing in this type of error that - // would prevent us from compiling the rhs. - } - // resolve the rhs expression to match the lhs type - let rhs = self.compile_expr_to_ty_with_casts( - assignment.rhs(), - &lhs_symbol.ty, - assignment.syntax(), - )?; - let stmt = build_assignment_statement(name_span, name.to_string(), rhs, assignment_span); - Some(stmt) - } - - /// `ident[index] = expr;` - fn compile_indexed_assignment_stmt( - &mut self, - assignment: &oq3_syntax::ast::AssignmentStmt, - ) -> Option { - let indexed_ident = assignment - .indexed_identifier() - .expect("assignment without name must have an indexed identifier"); - let name = indexed_ident - .identifier() - .expect("indexed identifier must have a name"); - let string_name = name.to_string(); - let name_span = span_for_named_item(&indexed_ident); - let stmt_span = span_for_syntax_node(assignment.syntax()); - let rhs_span = span_for_syntax_node(assignment.rhs()?.syntax()); - - // resolve the index expression - // we only support single index expressions for now - // but in the future we may support slice/range/array indexing - let indices: Vec<_> = indexed_ident - .index_operators() - .filter_map(|op| self.compile_index_operator(&op)) - .flatten() - .collect(); - - if indices.len() != 1 { - // This is a temporary limitation. We can only handle - // single index expressions for now. - let kind = SemanticErrorKind::IndexMustBeSingleExpr(span_for_syntax_node( - indexed_ident.syntax(), - )); - self.push_semantic_error(kind); - return None; - } - let index = indices[0].clone(); - - let lhs_symbol = self - .symbols - .get_symbol_by_name(name.to_string().as_str())? - .clone(); - if index.ty.num_dims() > lhs_symbol.ty.num_dims() { - let kind = SemanticErrorKind::TypeRankError(rhs_span); - self.push_semantic_error(kind); - } - let index_expr = index.expr.clone(); - - let Some(indexed_ty) = get_indexed_type(&lhs_symbol.ty) else { - let kind = SemanticErrorKind::CannotIndexType(format!("{:?}", lhs_symbol.ty), rhs_span); - self.push_semantic_error(kind); - return None; - }; - let rhs = - self.compile_expr_to_ty_with_casts(assignment.rhs(), &indexed_ty, assignment.syntax())?; - let stmt = - build_indexed_assignment_statement(name_span, string_name, index_expr, rhs, stmt_span); - Some(stmt) - } - - /// Barrier isn't supported in Q# so we insert a runtime function that is - /// a no-op for simulation but is `@SimulatableIntrinsic()` so that it can - /// be emited in code gen as `__quantum__qis__barrier__body()`. This - /// matches the existing `qiskit-qir` behavior. Qiskit barriers are - /// variadic, but QIR doesn't support variadic gates. - /// We should look at how to handle this better in the future. - fn compile_barrier_stmt(&mut self, barrier: &oq3_syntax::ast::Barrier) -> Option { - let qubit_args: Vec<_> = if let Some(qubits) = barrier.qubit_list() { - qubits - .gate_operands() - .map(|op| self.compile_gate_operand(&op).map(|x| x.expr)) - .collect() - } else { - vec![] - }; - - if qubit_args.iter().any(Option::is_none) { - // if any of the qubit arguments failed to compile, we can't proceed - // This can happen if the qubit is not defined or if the qubit was - // a hardware qubit - return None; - } - let call_span = span_for_syntax_node(barrier.syntax()); - // we don't support barrier, but we can insert a runtime function - // which will generate a barrier call in QIR - self.runtime.insert(RuntimeFunctions::Barrier); - Some(build_barrier_call(call_span)) - } - - /// Need to add break support in Q# to support break statements - fn compile_break_stmt(&mut self, break_stmt: &oq3_syntax::ast::BreakStmt) -> Option { - self.push_unsupported_error_message("break statements", break_stmt.syntax()); - None - } - - /// Classical decls are used to declare classical variables. They have two - /// main forms: - /// - `type ident;` - /// - `type ident = expr;` - /// - /// Q# requires classical variables to be initialized, so we will use the - /// default value for the type to initialize the variable. In theory this - /// isn't a problem as any classical variable that is used should be - /// initialized before use and would be a bug anyway. This leads to awkward - /// code in Q# where we have to initialize classical variables that are - /// always overwritten before use. - fn compile_classical_decl( - &mut self, - decl: &oq3_syntax::ast::ClassicalDeclarationStatement, - ) -> Option { - let name = decl.name().expect("classical declaration must have a name"); - let name_span = span_for_syntax_node(name.syntax()); - let scalar_ty = decl - .scalar_type() - .expect("Classical declaration must have a scalar type"); - let is_const = decl.const_token().is_some(); - // if we can't convert the scalar type, we can't proceed, an error has been pushed - let ty = self.get_semantic_type_from_scalar_type(&scalar_ty, is_const)?; - let qsharp_ty = self.convert_semantic_type_to_qsharp_type(&ty, name.syntax())?; - - let symbol = Symbol { - name: name.to_string(), - span: name_span, - ty: ty.clone(), - qsharp_ty: qsharp_ty.clone(), - io_kind: IOKind::Default, - }; - - if self.symbols.insert_symbol(symbol).is_err() { - self.push_redefined_symbol_error(name.to_string(), name_span); - return None; - } - - // if there is an expression, compile it to match the decl type - let rhs = self.compile_expr_to_ty_with_casts(decl.expr(), &ty, decl.syntax())?; - - // create the let binding and assign the rhs to the lhs - let ty_span = span_for_syntax_node(scalar_ty.syntax()); - let stmt_span = span_for_syntax_node(decl.syntax()); - let stmt = build_classical_decl( - name.to_string(), - is_const, - ty_span, - stmt_span, - name_span, - &qsharp_ty, - rhs, - ); - - Some(stmt) - } - - /// The expr type is fixed so we try to resolve the expression to match the type - /// via implicit casts or literal conversion if possible. - fn compile_expr_to_ty_with_casts( - &mut self, - expr: Option, - ty: &Type, - node: &SyntaxNode, - ) -> Option { - let Some(expr) = expr else { - // In OpenQASM, classical variables may be uninitialized, but in Q#, - // they must be initialized. We will use the default value for the type - // to initialize the variable. - return self.get_default_value(ty, node); - }; - - // since we have an expr, we can refine the node for errors - let span = span_for_syntax_node(expr.syntax()); - - let rhs = self.compile_expr(&expr)?; - let rhs_ty = rhs.ty.clone(); - - // if we have an exact type match, we can use the rhs as is - if types_equal_except_const(ty, &rhs_ty) { - return Some(rhs.expr); - } - - if let Expr::Literal(literal) = &expr { - // if the rhs is a literal, we can try to cast it to the lhs type - // we can do better than just types given we have a literal value - if can_cast_literal(ty, &rhs_ty) || can_cast_literal_with_value_knowledge(ty, literal) { - return Some(self.cast_literal_expr_to_type(ty, &rhs, literal)?.expr); - } - // if we can't cast the literal, we can't proceed - // create a semantic error and return - let kind = SemanticErrorKind::CannotAssignToType( - format!("{:?}", rhs.ty), - format!("{ty:?}"), - span, - ); - self.push_semantic_error(kind); - return None; - } - let is_negated_lit = if let Expr::PrefixExpr(prefix_expr) = &expr { - if let Some(UnaryOp::Neg) = prefix_expr.op_kind() { - matches!(&prefix_expr.expr(), Some(Expr::Literal(..))) - } else { - false - } - } else { - false - }; - if matches!(ty, Type::UInt(..)) && is_negated_lit { - let kind = SemanticErrorKind::CannotAssignToType( - "Negative Int".to_string(), - format!("{ty:?}"), - span, - ); - self.push_semantic_error(kind); - return None; - } - if let Expr::PrefixExpr(prefix_expr) = &expr { - if let Some(UnaryOp::Neg) = prefix_expr.op_kind() { - if let Some(Expr::Literal(literal)) = &prefix_expr.expr() { - // if the rhs is a literal, we can try to cast it to the lhs type - // we can do better than just types given we have a literal value - - if can_cast_literal(ty, &rhs_ty) - || can_cast_literal_with_value_knowledge(ty, literal) - { - // if the literal is negated, we need to compile it as a negated literal - // This will only work for int/float as we can't express any other - // kind of negated literal - return Some(self.compile_negated_literal_as_ty(literal, Some(ty))?.expr); - } - // if we can't cast the literal, we can't proceed - // create a semantic error and return - let kind = SemanticErrorKind::CannotAssignToType( - format!("{:?}", rhs.ty), - format!("{ty:?}"), - span_for_syntax_node(node), - ); - self.push_semantic_error(kind); - return None; - } - } - } - // the lhs has a type, but the rhs may be of a different type with - // implicit and explicit conversions. We need to cast the rhs to the - // lhs type, but if that cast fails, we will have already pushed an error - // and we can't proceed - - Some(self.cast_expr_to_type(ty, &rhs, node)?.expr) - } - - /// Need to add continue support in Q# to support continue statements - fn compile_continue_stmt( - &mut self, - continue_stmt: &oq3_syntax::ast::ContinueStmt, - ) -> Option { - self.push_unsupported_error_message("continue statements", continue_stmt.syntax()); - None - } - - /// - /// Qiskit can't handle def statements, so we push an error - /// and return None. We also push a semantic error if the def - /// statement is not in the global scope. - fn compile_def_decl(&mut self, def: &oq3_syntax::ast::Def) -> Option { - let def_span = span_for_syntax_node(def.syntax()); - if !self.symbols.is_current_scope_global() { - let kind = SemanticErrorKind::QuantumDeclarationInNonGlobalScope(def_span); - self.push_semantic_error(kind); - return None; - } - self.push_unsupported_error_message("def declarations", def.syntax()); - None - } - - /// Some statements don't fall into the normal categories, so they - /// are handled here. This is a catch-all for non-declaration - /// assignments and calculations. - /// Example: - /// ```qasm - /// input int a; - /// input int b; - /// a * b; // this is an expr statement - /// ``` - fn compile_expr_stmt(&mut self, expr: &oq3_syntax::ast::ExprStmt) -> Option { - let expr = expr.expr()?; - let texpr = self.compile_expr(&expr)?; - Some(build_stmt_semi_from_expr(texpr.expr)) - } - - /// This is the heart of compilation. All statements eventually call this - /// function and it is where we start to build the AST. From here we start - /// to return `QasmTypedExpr` so that we can track the QASM type of for the - /// Q# expresssion that we are building. This is needed for type checking - /// and casting. We must make sure all types match while building the AST - /// as Q# doesn't have implicit casting and would otherwise fail to compile. - fn compile_expr(&mut self, expr: &oq3_syntax::ast::Expr) -> Option { - match expr { - Expr::ArrayExpr(array_expr) => self.compile_array_expr(array_expr, expr), - Expr::ArrayLiteral(array_literal) => { - self.compile_array_literal_expr(array_literal, expr) - } - Expr::BinExpr(bin_expr) => self.compile_bin_expr(bin_expr, expr), - Expr::BlockExpr(_) => { - // block expressions are handled by their containing statements - panic!("Block expressions should not be compiled directly") - } - Expr::BoxExpr(box_expr) => self.compile_box_expr(box_expr), - Expr::CallExpr(call_expr) => self.compile_call_expr(call_expr), - Expr::CastExpression(cast_expr) => self.compile_cast_expr(cast_expr), - Expr::GateCallExpr(gate_call_expr) => self.compile_gate_call_expr(gate_call_expr, expr), - Expr::GPhaseCallExpr(_) => { - panic!("GPhase expressions should not be compiled directly") - } - Expr::HardwareQubit(hardware_qubit) => { - self.compile_hardware_qubit_expr(hardware_qubit, expr) - } - Expr::Identifier(identifier) => self.compile_identifier_expr(identifier, expr), - Expr::IndexExpr(index_expr) => self.compile_index_expr(index_expr), - Expr::IndexedIdentifier(indexed_identifier) => { - self.compile_indexed_identifier_expr(indexed_identifier) - } - Expr::Literal(lit) => self.compile_literal_expr(lit, expr), - Expr::TimingLiteral(lit) => self.compile_timing_literal_expr(lit, expr), - Expr::MeasureExpression(measure_expr) => self.compile_measure_expr(measure_expr, expr), - Expr::ModifiedGateCallExpr(modified_gate_call_expr) => { - self.compile_modified_gate_call_expr(modified_gate_call_expr) - } - Expr::ParenExpr(paren_expr) => self.compile_paren_expr(paren_expr), - Expr::PrefixExpr(prefix_expr) => self.compile_prefix_expr(prefix_expr), - Expr::RangeExpr(range_expr) => self.compile_range_expr(range_expr, expr.syntax()), - Expr::ReturnExpr(return_expr) => self.compile_return_expr(return_expr), - } - } - - /// Qubit and bit registers are handled in the their own statements. - /// Arrays for classical variables would be handled here. Qiskit - /// can't handle array expressions yet, so we push an error and return None. - fn compile_array_expr( - &mut self, - _array_expr: &oq3_syntax::ast::ArrayExpr, - expr: &Expr, - ) -> Option { - self.push_unimplemented_error_message("array expressions", expr.syntax()); - None - } - - /// Qubit and bit registers are handled in the their own statements. - /// Arrays for classical variables would be handled here. Qiskit - /// can't handle array expressions yet, so we push an error and return None. - fn compile_array_literal_expr( - &mut self, - _array_literal: &oq3_syntax::ast::ArrayLiteral, - expr: &Expr, - ) -> Option { - self.push_unimplemented_error_message("array literal expressions", expr.syntax()); - None - } - - /// Create a binary expression from the given binary expression node - /// The binary expression is created by recursively compiling the left and right - /// expressions and then creating a binary expression from the compiled expressions - /// - /// This is more complex than it seems because we need to handle type promotion - /// and casting. The `OpenQASM3` language has a specific set of rules for casting - /// between types. The rules can be found at: - /// - /// - /// This harder than decl statements as we need to deal with promotion and casting - /// for both the lhs and rhs expressions instead of having a fixed LHS type. - /// - /// complex > float > int/uint - /// within type widths are promoted to the larger type - #[allow(clippy::too_many_lines)] - fn compile_bin_expr( - &mut self, - bin_expr: &oq3_syntax::ast::BinExpr, - expr: &Expr, - ) -> Option { - // We don't need to worry about semantic errors as binary expression - // must have a lhs and rhs expression and an operator. This is - // verified in the binary expression tests. - let op = bin_expr.op_kind()?; - let lhs_expr = bin_expr.lhs()?; - let rhs_expr = bin_expr.rhs()?; - - let lhs = self.compile_expr(&lhs_expr)?; - let rhs = self.compile_expr(&rhs_expr)?; - - if lhs.ty.is_quantum() { - let kind = SemanticErrorKind::QuantumTypesInBinaryExpression(lhs.expr.span); - self.push_semantic_error(kind); - } - if rhs.ty.is_quantum() { - let kind = SemanticErrorKind::QuantumTypesInBinaryExpression(rhs.expr.span); - self.push_semantic_error(kind); - } - - let qsop = self.map_bin_op(op, expr.syntax())?; - let is_assignment = matches!(op, oq3_syntax::ast::BinaryOp::Assignment { op: _ }); - - let left_type = lhs.ty.clone(); - let right_type = rhs.ty.clone(); - - if binop_requires_bitwise_conversion(op, &left_type) { - // if the operator requires bitwise conversion, we need to determine - // what size of UInt to promote to. If we can't promote to a UInt, we - // push an error and return None. - let (ty, lhs_uint_promotion, rhs_uint_promotion) = - promote_to_uint_ty(&left_type, &right_type); - let Some(ty) = ty else { - let target_ty = Type::UInt(None, IsConst::False); - if lhs_uint_promotion.is_none() { - let target_str: String = format!("{target_ty:?}"); - let kind = SemanticErrorKind::CannotCast( - format!("{left_type:?}"), - target_str, - lhs.expr.span, - ); - self.push_semantic_error(kind); - } - if rhs_uint_promotion.is_none() { - let target_str: String = format!("{target_ty:?}"); - let kind = SemanticErrorKind::CannotCast( - format!("{right_type:?}"), - target_str, - rhs.expr.span, - ); - self.push_semantic_error(kind); - } - return None; - }; - // Now that we know the effective Uint type, we can cast the lhs and rhs - // so that operations can be performed on them. - let new_lhs = self.cast_expr_to_type(&ty, &lhs, lhs_expr.syntax())?; - // only cast the rhs if the operator requires symmetric conversion - let new_rhs = if binop_requires_bitwise_symmetric_conversion(op) { - self.cast_expr_to_type(&ty, &rhs, rhs_expr.syntax())? - } else { - rhs - }; - let expr = build_binary_expr( - is_assignment, - qsop, - new_lhs.expr, - new_rhs.expr, - span_for_syntax_node(expr.syntax()), - ); - let texpr = QasmTypedExpr { ty, expr }; - let final_expr = self.cast_expr_to_type(&left_type, &texpr, bin_expr.syntax())?; - return Some(final_expr); - } - - let span = span_for_syntax_node(expr.syntax()); - - if requires_types_already_match_conversion(op) { - if !types_equal_except_const(&left_type, &right_type) { - let kind = SemanticErrorKind::CannotApplyOperatorToTypes( - format!("{op:?}"), - format!("{left_type:?}"), - format!("{right_type:?}"), - span, - ); - self.push_semantic_error(kind); - return None; - } - let expr = build_binary_expr(is_assignment, qsop, lhs.expr, rhs.expr, span); - return Some(QasmTypedExpr { - ty: left_type, - expr, - }); - } - - // for int, uint, float, and complex, the lesser of the two types is cast to - // the greater of the two. Within each level of complex and float, types with - // greater width are greater than types with lower width. - // complex > float > int/uint - // Q# has built-in functions: IntAsDouble, IntAsBigInt to handle two cases. - // If the width of a float is greater than 64, we can't represent it as a double. - - let (lhs, rhs, ty) = if binop_requires_bool_conversion_for_type(op) { - let ty = Type::Bool(IsConst::False); - let new_lhs = self.cast_expr_to_type(&ty, &lhs, lhs_expr.syntax())?; - let new_rhs = self.cast_expr_to_type(&ty, &rhs, rhs_expr.syntax())?; - (new_lhs.expr, new_rhs.expr, ty) - } else if binop_requires_int_conversion_for_type(op, &left_type, &rhs.ty) { - let ty = Type::Int(None, IsConst::False); - let new_lhs = self.cast_expr_to_type(&ty, &lhs, lhs_expr.syntax())?; - let new_rhs = self.cast_expr_to_type(&ty, &rhs, rhs_expr.syntax())?; - (new_lhs.expr, new_rhs.expr, ty) - } else if requires_symmetric_conversion(op) { - let promoted_type = try_promote_with_casting(&left_type, &right_type); - let new_left = if promoted_type == left_type { - lhs - } else { - let node = lhs_expr.syntax(); - match &lhs_expr { - Expr::Literal(literal) => { - if can_cast_literal(&promoted_type, &left_type) - || can_cast_literal_with_value_knowledge(&promoted_type, literal) - { - self.cast_literal_expr_to_type(&promoted_type, &lhs, literal)? - } else { - self.cast_expr_to_type(&promoted_type, &lhs, node)? - } - } - _ => self.cast_expr_to_type(&promoted_type, &lhs, node)?, - } - }; - let new_right = if promoted_type == right_type { - rhs - } else { - let node = rhs_expr.syntax(); - match &rhs_expr { - Expr::Literal(literal) => { - if can_cast_literal(&promoted_type, &right_type) - || can_cast_literal_with_value_knowledge(&promoted_type, literal) - { - self.cast_literal_expr_to_type(&promoted_type, &rhs, literal)? - } else { - self.cast_expr_to_type(&promoted_type, &rhs, node)? - } - } - _ => self.cast_expr_to_type(&promoted_type, &rhs, node)?, - } - }; - (new_left.expr, new_right.expr, promoted_type) - } else { - // we don't have symmetric promotion, so we need to promote the rhs only - if is_assignment { - let oq3_syntax::ast::BinaryOp::Assignment { op: arith_op } = op else { - unreachable!() - }; - let (lhs, rhs) = - if arith_op.is_some() && binop_requires_symmetric_int_conversion(op) { - let ty = Type::Int(None, IsConst::False); - let lhs = self.cast_expr_to_type(&ty, &lhs, lhs_expr.syntax())?.expr; - let rhs = self.cast_expr_to_type(&ty, &rhs, rhs_expr.syntax())?.expr; - (lhs, rhs) - } else { - let rhs = self.compile_expr_to_ty_with_casts( - Some(rhs_expr.clone()), - &left_type, - rhs_expr.syntax(), - )?; - (lhs.expr, rhs) - }; - - (lhs, rhs, left_type) - } else if binop_requires_symmetric_int_conversion(op) { - let ty = Type::Int(None, IsConst::False); - let new_rhs = self.cast_expr_to_type(&ty, &rhs, rhs_expr.syntax())?; - (lhs.expr, new_rhs.expr, left_type) - } else { - (lhs.expr, rhs.expr, left_type) - } - }; - - // now that we have the lhs and rhs expressions, we can create the binary expression - // but we need to check if the chosen operator is supported by the types after - // promotion and conversion. - - let expr = if matches!(ty, Type::Complex(..)) { - if is_assignment { - let kind = SemanticErrorKind::ComplexBinaryAssignment(span); - self.push_semantic_error(kind); - None - } else if is_complex_binop_supported(qsop) { - Some(build_complex_binary_expr( - is_assignment, - qsop, - lhs, - rhs, - span, - )) - } else { - let kind = SemanticErrorKind::OperatorNotSupportedForTypes( - format!("{qsop:?}"), - format!("{ty:?}"), - format!("{ty:?}"), - span, - ); - self.push_semantic_error(kind); - None - } - } else { - Some(build_binary_expr(is_assignment, qsop, lhs, rhs, span)) - }; - let expr = expr?; - let ty = match &op { - BinaryOp::CmpOp(..) | BinaryOp::LogicOp(..) => Type::Bool(IsConst::False), - _ => ty, - }; - Some(QasmTypedExpr { ty, expr }) - } - - fn map_bin_op( - &mut self, - op: oq3_syntax::ast::BinaryOp, - node: &SyntaxNode, - ) -> Option { - match op { - oq3_syntax::ast::BinaryOp::LogicOp(logic_op) => Some(match logic_op { - oq3_syntax::ast::LogicOp::And => ast::BinOp::AndL, - oq3_syntax::ast::LogicOp::Or => ast::BinOp::OrL, - }), - oq3_syntax::ast::BinaryOp::ArithOp(arith) => Some(match arith { - oq3_syntax::ast::ArithOp::Add => ast::BinOp::Add, - oq3_syntax::ast::ArithOp::Mul => ast::BinOp::Mul, - oq3_syntax::ast::ArithOp::Sub => ast::BinOp::Sub, - oq3_syntax::ast::ArithOp::Div => ast::BinOp::Div, - oq3_syntax::ast::ArithOp::Rem => ast::BinOp::Mod, - oq3_syntax::ast::ArithOp::Shl => ast::BinOp::Shl, - oq3_syntax::ast::ArithOp::Shr => ast::BinOp::Shr, - oq3_syntax::ast::ArithOp::BitXor => ast::BinOp::XorB, - oq3_syntax::ast::ArithOp::BitOr => ast::BinOp::OrB, - oq3_syntax::ast::ArithOp::BitAnd => ast::BinOp::AndB, - }), - oq3_syntax::ast::BinaryOp::CmpOp(cmp_op) => Some(match cmp_op { - oq3_syntax::ast::CmpOp::Eq { negated } => { - if negated { - ast::BinOp::Neq - } else { - ast::BinOp::Eq - } - } - oq3_syntax::ast::CmpOp::Ord { ordering, strict } => match ordering { - oq3_syntax::ast::Ordering::Less => { - if strict { - ast::BinOp::Lt - } else { - ast::BinOp::Lte - } - } - oq3_syntax::ast::Ordering::Greater => { - if strict { - ast::BinOp::Gt - } else { - ast::BinOp::Gte - } - } - }, - }), - oq3_syntax::ast::BinaryOp::ConcatenationOp => { - // This is only used for types which we don't currently support. - self.push_unimplemented_error_message("Concatenation operators", node); - None - } - oq3_syntax::ast::BinaryOp::Assignment { op } => op.map(|op| match op { - oq3_syntax::ast::ArithOp::Add => ast::BinOp::Add, - oq3_syntax::ast::ArithOp::Mul => ast::BinOp::Mul, - oq3_syntax::ast::ArithOp::Sub => ast::BinOp::Sub, - oq3_syntax::ast::ArithOp::Div => ast::BinOp::Div, - oq3_syntax::ast::ArithOp::Rem => ast::BinOp::Mod, - oq3_syntax::ast::ArithOp::Shl => ast::BinOp::Shl, - oq3_syntax::ast::ArithOp::Shr => ast::BinOp::Shr, - oq3_syntax::ast::ArithOp::BitXor => ast::BinOp::XorB, - oq3_syntax::ast::ArithOp::BitOr => ast::BinOp::OrB, - oq3_syntax::ast::ArithOp::BitAnd => ast::BinOp::AndB, - }), - } - } - - fn compile_block_expr(&mut self, block_expr: &oq3_syntax::ast::BlockExpr) -> ast::Block { - let stmts = self.compile_stmts(&block_expr.statements().collect::>()); - let stmts = stmts - .into_iter() - .map(Box::new) - .collect::>() - .into_boxed_slice(); - - let span = span_for_syntax_node(block_expr.syntax()); - ast::Block { - id: NodeId::default(), - span, - stmts, - } - } - - /// - /// Qiskit can't emit the box/delay, so we push an error and return None. - /// The QASM grammar specifies this is a statement, but the parser has it as an expression. - /// - /// We don't really have anything in Q# the box statement is for scoping the - /// timing of a particular part of the circuit. We could generates call around - /// a new block, but that would be a bit of a hack. - fn compile_box_expr(&mut self, box_expr: &oq3_syntax::ast::BoxExpr) -> Option { - self.push_unimplemented_error_message("box expressions", box_expr.syntax()); - None - } - - /// Qiskit can't handle call expressions yet, so we push an error and return None. - fn compile_call_expr( - &mut self, - call_expr: &oq3_syntax::ast::CallExpr, - ) -> Option { - self.push_unimplemented_error_message("call expressions", call_expr.syntax()); - None - } - - /// explicit casts have no defined behavior AFAICT from the spec. I'm - /// guessing that they are a best effort for the compiler implementor. - fn compile_cast_expr(&mut self, cast_expr: &CastExpression) -> Option { - let scalar_ty = cast_expr - .scalar_type() - .expect("cast expression must have a scalar type"); - let is_const = false; - let ty = self.get_semantic_type_from_scalar_type(&scalar_ty, is_const)?; - let expr = self.compile_expr_to_ty_with_casts(cast_expr.expr(), &ty, cast_expr.syntax())?; - Some(QasmTypedExpr { ty, expr }) - } - - /// Gate call expression. We delegate compilation to - /// `compile_gate_call_expr_impl` with empty modifiers so that we can reuse - /// gate call compilation logic. - fn compile_gate_call_expr( - &mut self, - gate_call_expr: &oq3_syntax::ast::GateCallExpr, - expr: &Expr, - ) -> Option { - let expr_span = span_for_syntax_node(expr.syntax()); - self.compile_gate_call_expr_impl(gate_call_expr, expr_span, &[]) - } - - /// Compile gate call expression with modifiers. - /// We have to resolve the gate name and modifiers as some of the stdgates - /// have implicit modifiers and different gate names that we have to map - /// into Q# names with the appropriate modifiers/fuctors. - /// - The `inv` modifier is the `Adjoint` fuctor. - /// - The `pow` modifier is the `__Pow__` function which we define as a - /// runtime function at the end of code generation if it is used. - /// - the `ctrl` modifier is the `Controlled` functor. - /// the `negctrl` modifier is a special case equivalent to - /// `ApplyControlledOnInt(0, _, _, _)`. - /// - /// Apply the modifiers are applied in reverse order to the gate call. - /// A simplified binding of the modifiers to the gate call with all - /// operations being on a single qubit gate would look like: - /// `a @ b @ c g(r) q0, q1, q2` `=>` `(a @ (b @ (c g(r) q0), q1), q2)` - /// - /// This get more complex when we have multiple qubit gates and controls. - #[allow(clippy::too_many_lines)] - fn compile_gate_call_expr_impl( - &mut self, - gate_call_expr: &oq3_syntax::ast::GateCallExpr, - expr_span: Span, - modifiers: &[crate::types::GateModifier], - ) -> Option { - let name = gate_call_expr - .identifier() - .expect("gate call must have a name"); - let name_span = span_for_syntax_node(name.syntax()); - let name_text = name.to_string(); - let call_span = span_for_syntax_node(gate_call_expr.syntax()); - // if we fail to map the name, we don't have a valid Q# gate - // but the user may have defined their own. We check the symbol - // table looking for such a definition. - let gate_name = get_qsharp_gate_name(&name_text).unwrap_or(&name_text); - let (gate_name, additional_modifiers) = get_implicit_modifiers(gate_name, name_span); - let Some(sym) = self.symbols.get_symbol_by_name(&gate_name) else { - self.push_missing_symbol_error(name_text, name.syntax()); - return None; - }; - let Type::Gate(cargs_len, qargs_len) = sym.ty else { - let kind = SemanticErrorKind::CannotCallNonGate(call_span); - self.push_semantic_error(kind); - return None; - }; - - let classical_args = self.compile_gate_call_classical_args(gate_call_expr, cargs_len)?; - let mut qubit_args = self.compile_gate_call_quantum_args(gate_call_expr)?; - - // at this point we all of the information we need, but we have to deal with modifiers - // We have the modifiers which we have been given, plus the implicit modifiers - // from the gate definition. We need to merge these two sets of modifiers - // See: ch, crx, cry, crz, sdg, and tdg - let modifiers = modifiers - .iter() - .chain(additional_modifiers.iter()) - .rev() - .collect::>(); - let num_ctrls = calculate_num_ctrls(&modifiers); - self.verify_qubit_args_match_gate_and_ctrls( - &qubit_args, - qargs_len, - num_ctrls, - gate_call_expr, - )?; - // take the nuber of qubit args that the gates expects from the source qubits - let gate_qubits = qubit_args.split_off(qubit_args.len() - qargs_len); - // and then merge the classical args with the qubit args - // this will give us the args for the call prior to wrapping in tuples - // for controls - let args: Vec<_> = classical_args.into_iter().chain(gate_qubits).collect(); - let mut args = build_gate_call_param_expr(args, qubit_args.len()); - let mut callee = build_path_ident_expr(&gate_name, name_span, expr_span); - - for modifier in modifiers { - match modifier { - GateModifier::Inv(mod_span) => { - callee = build_unary_op_expr( - ast::UnOp::Functor(ast::Functor::Adj), - callee, - *mod_span, - ); - } - GateModifier::Pow(exponent, mod_span) => { - // The exponent is only an option when initially parsing the gate - // call. The stmt would not have been created. If we don't have an - // an eponent at this point it is a bug - let exponent = exponent.expect("Exponent must be present"); - let exponent_expr = build_lit_int_expr(exponent, *mod_span); - self.runtime |= RuntimeFunctions::Pow; - args = build_tuple_expr(vec![exponent_expr, callee, args]); - callee = build_path_ident_expr("__Pow__", *mod_span, expr_span); - } - GateModifier::Ctrl(controls, mod_span) => { - // remove the last n qubits from the qubit list - let num_ctrls = controls.unwrap_or(1); - if qubit_args.len() < num_ctrls { - let kind = - SemanticErrorKind::InvalidNumberOfQubitArgs(qargs_len, 0, call_span); - self.push_semantic_error(kind); - return None; - } - let ctrl = qubit_args.split_off(qubit_args.len() - num_ctrls); - let ctrls = build_expr_array_expr(ctrl, *mod_span); - args = build_tuple_expr(vec![ctrls, args]); - callee = build_unary_op_expr( - ast::UnOp::Functor(ast::Functor::Ctl), - callee, - *mod_span, - ); - } - GateModifier::NegCtrl(controls, mod_span) => { - // remove the last n qubits from the qubit list - let num_ctrls = controls.unwrap_or(1); - if qubit_args.len() < num_ctrls { - let kind = - SemanticErrorKind::InvalidNumberOfQubitArgs(qargs_len, 0, call_span); - self.push_semantic_error(kind); - return None; - } - let ctrl = qubit_args.split_off(qubit_args.len() - num_ctrls); - let ctrls = build_expr_array_expr(ctrl, *mod_span); - let lit_0 = build_lit_int_expr(0, Span::default()); - args = build_tuple_expr(vec![lit_0, callee, ctrls, args]); - callee = build_path_ident_expr("ApplyControlledOnInt", *mod_span, expr_span); - } - } - } - - self.validate_all_quantum_args_have_been_consumed(&qubit_args, qargs_len, call_span)?; - - let expr = ast_builder::build_gate_call_with_params_and_callee(args, callee, expr_span); - Some(QasmTypedExpr { - ty: Type::Void, - expr, - }) - } - - /// Push if all qubit args have not been consumed. - /// Resurns None for an error, Some(()) for success. - /// This allows short-circuiting of the function. - fn validate_all_quantum_args_have_been_consumed( - &mut self, - qubit_args: &[ast::Expr], - qargs_len: usize, - call_span: Span, - ) -> Option<()> { - // This is a safety check. We should have peeled off all the controls - // but if we haven't, we need to push an error - if qubit_args.is_empty() { - return Some(()); - } - let kind = - SemanticErrorKind::InvalidNumberOfQubitArgs(qargs_len, qubit_args.len(), call_span); - self.push_semantic_error(kind); - None - } - - /// Raises an error if the number of qubit arguments does not match the number - /// of qubit arguments expected by the gate and the number of controls. - fn verify_qubit_args_match_gate_and_ctrls( - &mut self, - qubit_args: &[ast::Expr], - qargs_len: usize, - num_ctrls: u64, - gate_call_expr: &oq3_syntax::ast::GateCallExpr, - ) -> Option<()> { - let gate_call_span = span_for_syntax_node(gate_call_expr.syntax()); - let Some(num_ctrls) = usize::try_from(num_ctrls).ok() else { - let kind = SemanticErrorKind::TooManyControls(gate_call_span); - self.push_semantic_error(kind); - return None; - }; - - if qubit_args.len() != qargs_len + num_ctrls { - let span = if qubit_args.is_empty() { - gate_call_span - } else { - span_for_syntax_node( - gate_call_expr - .qubit_list() - .expect("Qubit list must exist") - .syntax(), - ) - }; - let kind = - SemanticErrorKind::InvalidNumberOfQubitArgs(qargs_len, qubit_args.len(), span); - self.push_semantic_error(kind); - return None; - } - Some(()) - } - - /// Compiles the gate call qubit arguments. This is a helper function - fn compile_gate_call_quantum_args( - &mut self, - gate_call_expr: &oq3_syntax::ast::GateCallExpr, - ) -> Option> { - let qubit_args: Vec<_> = gate_call_expr - .qubit_list() - .expect("Cannot call a gate without qubit arguments") - .gate_operands() - .map(|op| self.compile_gate_operand(&op).map(|x| x.expr)) - .collect(); - if qubit_args.iter().any(Option::is_none) { - // if any of the qubit arguments failed to compile, we can't proceed - // This can happen if the qubit is not defined or if the qubit was - // a hardware qubit - return None; - } - let qubit_args = qubit_args - .into_iter() - .map(|x| x.expect("All items should have value")) - .collect::>(); - Some(qubit_args) - } - - /// Compiles the gate call classical argument expressions. This is a helper function - fn compile_gate_call_classical_args( - &mut self, - gate_call_expr: &oq3_syntax::ast::GateCallExpr, - cargs_len: usize, - ) -> Option> { - let classical_args = match gate_call_expr.arg_list() { - Some(params) => { - let list = params - .expression_list() - .expect("Arg list must have an expression list"); - - // the classical args list is a list of expressions - // but the type of the args is fixed by the gate definition - // which should always move to float. - let angle_ty = Type::Float(None, IsConst::False); - let exprs = list - .exprs() - .map(|expr| { - self.compile_expr_to_ty_with_casts( - Some(expr), - &angle_ty, - gate_call_expr.syntax(), - ) - }) - .collect::>(); - - if !exprs.iter().all(Option::is_some) { - // There was an issue with one of the expressions - // and an error was pushed - return None; - } - exprs - .into_iter() - .map(|expr| expr.expect("All items should have value")) - .collect::>() - } - None => Vec::new(), - }; - - if classical_args.len() != cargs_len { - let gate_call_span = span_for_syntax_node(gate_call_expr.syntax()); - let span = if classical_args.is_empty() { - gate_call_span - } else { - span_for_syntax_node( - gate_call_expr - .arg_list() - .expect("Qubit list must exist") - .syntax(), - ) - }; - let kind = SemanticErrorKind::InvalidNumberOfClassicalArgs( - cargs_len, - classical_args.len(), - span, - ); - self.push_semantic_error(kind); - return None; - } - Some(classical_args) - } - - /// Compiles the expression list. Returns None if any of the expressions - /// fail to compile. If all expressions compile, returns a vector of - /// the compiled expressions. An error is pushed if any of the expressions - /// fail to compile. - fn compile_expression_list( - &mut self, - expr_list: &oq3_syntax::ast::ExpressionList, - ) -> Option> { - let exprs: Vec<_> = expr_list.exprs().collect(); - let exprs_len = exprs.len(); - let mapped_exprs: Vec<_> = exprs - .into_iter() - .filter_map(|expr| self.compile_expr(&expr)) - .collect(); - if exprs_len == mapped_exprs.len() { - return Some(mapped_exprs); - } - let kind = SemanticErrorKind::FailedToCompileExpressionList(span_for_syntax_node( - expr_list.syntax(), - )); - self.push_semantic_error(kind); - None - } - - /// Compiles the expression list attempting to coerce the expressions to a - /// specific type. - /// Returns None if any of the expressions fail to compile and an error is - /// pushed. If all expressions compile, returns a vector of the compiled - /// expressions. - fn compile_typed_expression_list( - &mut self, - expr_list: &oq3_syntax::ast::ExpressionList, - ty: &Type, - ) -> Option> { - let exprs: Vec<_> = expr_list.exprs().collect(); - let exprs_len = exprs.len(); - let mapped_exprs: Vec<_> = exprs - .into_iter() - .filter_map(|expr| { - self.compile_expr_to_ty_with_casts(Some(expr.clone()), ty, expr.syntax()) - .map(|expr| QasmTypedExpr { - expr, - ty: ty.clone(), - }) - }) - .collect(); - if exprs_len == mapped_exprs.len() { - return Some(mapped_exprs); - } - let kind = SemanticErrorKind::FailedToCompileExpressionList(span_for_syntax_node( - expr_list.syntax(), - )); - self.push_semantic_error(kind); - None - } - - /// Compiles qubit arguments for an instruction call. - fn compile_gate_operand(&mut self, op: &GateOperand) -> Option { - let op_span = span_for_syntax_node(op.syntax()); - match op { - GateOperand::HardwareQubit(hw) => { - // We don't support hardware qubits, so we need to push an error - // but we can still create an identifier for the hardware qubit - // and let the rest of the containing expression compile to - // catch any other errors - let message = "Hardware qubit operands"; - self.push_unsupported_error_message(message, hw.syntax()); - - let name = hw.to_string(); - let name_span = span_for_syntax_node(hw.syntax()); - let ident = build_path_ident_expr(name, name_span, op_span); - Some(QasmTypedExpr { - ty: Type::HardwareQubit, - expr: ident, - }) - } - GateOperand::Identifier(ident) => { - let name = ident.to_string(); - let name_span = span_for_syntax_node(ident.syntax()); - let Some(sym) = self.symbols.get_symbol_by_name(name.as_str()) else { - self.push_missing_symbol_error(name.as_str(), op.syntax()); - return None; - }; - let ty = sym.ty.clone(); - if !matches!(ty, Type::Qubit | Type::QubitArray(_)) { - let kind = SemanticErrorKind::InvalidGateOperand(op_span); - self.push_semantic_error(kind); - } - let ident = build_path_ident_expr(name, name_span, op_span); - Some(QasmTypedExpr { ty, expr: ident }) - } - GateOperand::IndexedIdentifier(indexed_ident) => { - let expr: QasmTypedExpr = self.compile_indexed_identifier_expr(indexed_ident)?; - // the type of the ident may be been Type::QubitArray, but the type of - // the returned expression should be Type::Qubit - if !matches!(expr.ty, Type::Qubit) { - let kind = SemanticErrorKind::InvalidIndexedGateOperand(op_span); - self.push_semantic_error(kind); - } - Some(expr) - } - } - } - fn compile_index_operator( - &mut self, - op: &oq3_syntax::ast::IndexOperator, - ) -> Option> { - match op.index_kind() { - Some(oq3_syntax::ast::IndexKind::SetExpression(expr)) => { - let expr = expr.expression_list()?; - self.compile_expression_list(&expr) - } - Some(oq3_syntax::ast::IndexKind::ExpressionList(expr)) => { - self.compile_expression_list(&expr) - } - None => { - let span = span_for_syntax_node(op.syntax()); - let kind = SemanticErrorKind::UnknownIndexedOperatorKind(span); - self.push_semantic_error(kind); - None - } - } - } - - #[allow(dead_code)] - fn compile_gphase_call_expr( - &mut self, - gphase_call_expr: &oq3_syntax::ast::GPhaseCallExpr, - expr: &Expr, - ) -> Option { - let expr_span = span_for_syntax_node(expr.syntax()); - self.compile_gphase_call_expr_impl(gphase_call_expr, expr_span, &[]) - } - - fn compile_gphase_call_expr_impl( - &mut self, - gphase_call_expr: &oq3_syntax::ast::GPhaseCallExpr, - _expr_span: Span, - _modifiers: &[crate::types::GateModifier], - ) -> Option { - self.push_unimplemented_error_message("gphase expressions", gphase_call_expr.syntax()); - None - } - - fn compile_hardware_qubit_expr( - &mut self, - _hardware_qubit: &oq3_syntax::ast::HardwareQubit, - expr: &Expr, - ) -> Option { - self.push_unsupported_error_message("hardware qubit expressions", expr.syntax()); - None - } - - fn compile_identifier_expr( - &mut self, - identifier: &oq3_syntax::ast::Identifier, - expr: &Expr, - ) -> Option { - let name = identifier.to_string(); - let Some(sym) = self.symbols.get_symbol_by_name(name.as_str()) else { - self.push_missing_symbol_error(&name, expr.syntax()); - return None; - }; - let span = span_for_syntax_node(identifier.syntax()); - let expr_span = span_for_syntax_node(expr.syntax()); - match sym.name.as_str() { - "euler" | "ℇ" => { - let expr = build_math_call_no_params("E", span); - let ty = Type::Float(None, IsConst::True); - Some(QasmTypedExpr { ty, expr }) - } - "pi" | "π" => { - let expr = build_math_call_no_params("PI", span); - let ty = Type::Float(None, IsConst::True); - Some(QasmTypedExpr { ty, expr }) - } - "tau" | "τ" => { - let expr = build_math_call_no_params("PI", span); - let ty = Type::Float(None, IsConst::True); - let expr = ast::Expr { - kind: Box::new(ast::ExprKind::BinOp( - ast::BinOp::Mul, - Box::new(build_lit_double_expr(2.0, span)), - Box::new(expr), - )), - span, - id: NodeId::default(), - }; - Some(QasmTypedExpr { ty, expr }) - } - _ => { - let expr = build_path_ident_expr(&sym.name, span, expr_span); - let ty = sym.ty.clone(); - Some(QasmTypedExpr { ty, expr }) - } - } - } - - fn compile_index_expr( - &mut self, - index_expr: &oq3_syntax::ast::IndexExpr, - ) -> Option { - let expr = index_expr.expr()?; - let expr_span = span_for_syntax_node(index_expr.syntax()); - let texpr = self.compile_expr(&expr)?; - let index = index_expr.index_operator()?; - let indices = self.compile_index_operator(&index)?; - let index_span = span_for_syntax_node(index.syntax()); - - if indices.len() != 1 { - // This is a temporary limitation. We can only handle - // single index expressions for now. - let kind = SemanticErrorKind::IndexMustBeSingleExpr(index_span); - self.push_semantic_error(kind); - return None; - } - let index = indices[0].clone(); - if index.ty.num_dims() > texpr.ty.num_dims() { - let kind = SemanticErrorKind::TypeRankError(index_span); - self.push_semantic_error(kind); - } - let index_expr = index.expr.clone(); - let Some(indexed_ty) = get_indexed_type(&texpr.ty) else { - let kind = - SemanticErrorKind::CannotIndexType(format!("{:?}", texpr.ty), texpr.expr.span); - self.push_semantic_error(kind); - return None; - }; - - let expr = ast_builder::build_index_expr(texpr.expr, index_expr, expr_span); - Some(QasmTypedExpr { - ty: indexed_ty, - expr, - }) - } - - /// Compiles a indexed expr `a[i]` where `a` is an identifier and `i` is an expression. - /// The type of the expression is determined by the indexed type of the identifier - /// resolved by `self.get_indexed_type`. - fn compile_indexed_identifier_expr( - &mut self, - indexed_ident: &oq3_syntax::ast::IndexedIdentifier, - ) -> Option { - let name = indexed_ident.identifier()?.to_string(); - let name_span = span_for_syntax_node(indexed_ident.syntax()); - let Some(sym) = self.symbols.get_symbol_by_name(name.as_str()) else { - self.push_missing_symbol_error(name.as_str(), indexed_ident.syntax()); - return None; - }; - let sym = sym.clone(); - let op_span = span_for_syntax_node(indexed_ident.syntax()); - - let index: Vec<_> = indexed_ident - .index_operators() - .filter_map(|op| self.compile_index_operator(&op)) - .flatten() - .collect(); - - assert!(index.len() == 1, "index must be a single expression"); - let ident = build_path_ident_expr(name, name_span, op_span); - let expr = ast::Expr { - id: NodeId::default(), - span: span_for_syntax_node(indexed_ident.syntax()), - kind: Box::new(ast::ExprKind::Index( - Box::new(ident), - Box::new(index[0].expr.clone()), - )), - }; - let Some(indexed_ty) = get_indexed_type(&sym.ty) else { - let kind = SemanticErrorKind::CannotIndexType(format!("{:?}", sym.ty), op_span); - self.push_semantic_error(kind); - return None; - }; - Some(QasmTypedExpr { - ty: indexed_ty, - expr, - }) - } - - fn compile_literal_expr( - &mut self, - lit: &oq3_syntax::ast::Literal, - expr: &Expr, - ) -> Option { - let span = span_for_syntax_node(lit.syntax()); - match lit.kind() { - LiteralKind::BitString(bitstring) => compile_bitstring(&bitstring, span), - LiteralKind::Bool(value) => { - let expr = build_lit_bool_expr(value, span); - let ty = Type::Bool(IsConst::True); - Some(QasmTypedExpr { ty, expr }) - } - LiteralKind::Byte(_) => { - self.push_unimplemented_error_message("byte literal expressions", expr.syntax()); - None - } - LiteralKind::Char(_) => { - self.push_unimplemented_error_message("char literal expressions", expr.syntax()); - None - } - LiteralKind::FloatNumber(value) => { - let expr = Self::compile_float_literal(&value, span); - let ty = Type::Float(None, IsConst::True); - Some(QasmTypedExpr { ty, expr }) - } - LiteralKind::IntNumber(value) => { - let expr = Self::compile_int_literal(&value, span); - let ty = Type::UInt(None, IsConst::True); - Some(QasmTypedExpr { ty, expr }) - } - LiteralKind::String(string) => self.compile_string_literal(&string, expr), - } - } - - /// Compiles a complex literal expression from a literal int. - fn compile_int_to_double_literal_to_complex( - &mut self, - value: &oq3_syntax::ast::IntNumber, - span: Span, - ) -> Option { - let value = value.value().expect("FloatNumber must have a value"); - if let Some(value) = safe_u128_to_f64(value) { - Some(build_complex_from_expr(build_lit_double_expr(value, span))) - } else { - let kind = SemanticErrorKind::InvalidCastValueRange( - "Integer".to_string(), - "Double".to_string(), - span, - ); - self.push_semantic_error(kind); - None - } - } - - /// Compiles a double expression from a literal int. - fn compile_int_to_double_literal( - &mut self, - value: &oq3_syntax::ast::IntNumber, - negate: bool, - span: Span, - ) -> Option { - let value = value.value().expect("FloatNumber must have a value"); - if let Some(value) = safe_u128_to_f64(value) { - let value = if negate { -value } else { value }; - Some(build_lit_double_expr(value, span)) - } else { - let kind = SemanticErrorKind::InvalidCastValueRange( - "Integer".to_string(), - "Double".to_string(), - span, - ); - self.push_semantic_error(kind); - None - } - } - - fn compile_float_literal(value: &oq3_syntax::ast::FloatNumber, span: Span) -> ast::Expr { - build_lit_double_expr(value.value().expect("FloatNumber must have a value"), span) - } - - fn compile_int_literal(value: &oq3_syntax::ast::IntNumber, span: Span) -> ast::Expr { - if let Some(value) = value.value() { - match value.try_into() { - Ok(value) => build_lit_int_expr(value, span), - Err(_) => build_lit_bigint_expr(value.into(), span), - } - } else { - panic!("IntNumber must have a value"); - } - } - - fn compile_string_literal( - &mut self, - _string: &oq3_syntax::ast::String, - expr: &Expr, - ) -> Option { - self.push_unimplemented_error_message("string literal expressions", expr.syntax()); - None - } - - fn compile_timing_literal_expr( - &mut self, - lit: &oq3_syntax::ast::TimingLiteral, - expr: &Expr, - ) -> Option { - self.compile_timing_literal_as_complex(lit, expr, false) - } - - // OpenQASM parser bundles complex numbers with timing literals - // so we have to disambiguate them during timing literal compilation - fn compile_timing_literal_as_complex( - &mut self, - lit: &TimingLiteral, - expr: &Expr, - negate: bool, - ) -> Option { - if let Some(TimeUnit::Imaginary) = lit.time_unit() { - let literal = lit.literal()?; - match literal.kind() { - LiteralKind::FloatNumber(value) => { - let value = value.value().expect("FloatNumber must have a value"); - let value = if negate { -value } else { value }; - let expr = build_lit_complex_expr( - crate::types::Complex::new(0.0, value), - span_for_syntax_node(lit.syntax()), - ); - let ty = Type::Complex(None, IsConst::True); - Some(QasmTypedExpr { ty, expr }) - } - LiteralKind::IntNumber(value) => { - let value = value.value().expect("IntNumber must have a value"); - - if let Some(value) = safe_u128_to_f64(value) { - let value = if negate { -value } else { value }; - let expr = build_lit_complex_expr( - crate::types::Complex::new(0.0, value), - span_for_syntax_node(lit.syntax()), - ); - let ty = Type::Complex(None, IsConst::True); - Some(QasmTypedExpr { ty, expr }) - } else { - let kind = SemanticErrorKind::InvalidCastValueRange( - "Complex imaginary".to_string(), - "Float".to_string(), - span_for_syntax_node(literal.syntax()), - ); - self.push_semantic_error(kind); - None - } - } - _ => { - // parser bug - unreachable!( - "Expected float or int literal, there is a bug in the OpenQASM parser." - ) - } - } - } else { - self.push_unsupported_error_message("Timing literal expressions", expr.syntax()); - None - } - } - - fn compile_measure_expr( - &mut self, - measure_expr: &oq3_syntax::ast::MeasureExpression, - expr: &Expr, - ) -> Option { - let Some(measure_token) = measure_expr.measure_token() else { - let span = span_for_syntax_node(expr.syntax()); - let kind = SemanticErrorKind::MeasureExpressionsMustHaveName(span); - self.push_semantic_error(kind); - return None; - }; - let name_span = span_for_syntax_token(&measure_token); - let stmt_span = span_for_syntax_node(measure_expr.syntax()); - - let Some(operand) = measure_expr.gate_operand() else { - let span = span_for_syntax_node(expr.syntax()); - let kind = SemanticErrorKind::MeasureExpressionsMustHaveGateOperand(span); - self.push_semantic_error(kind); - return None; - }; - - let args = self.compile_gate_operand(&operand)?; - let operand_span = span_for_syntax_node(operand.syntax()); - let expr = build_measure_call(args.expr, name_span, operand_span, stmt_span); - - Some(QasmTypedExpr { - ty: Type::Bit(IsConst::False), - expr, - }) - } - - fn compile_modified_gate_call_expr( - &mut self, - modified_gate_call_expr: &oq3_syntax::ast::ModifiedGateCallExpr, - ) -> Option { - let expr_span = span_for_syntax_node(modified_gate_call_expr.syntax()); - let modifiers = modified_gate_call_expr - .modifiers() - .map(|modifier| { - let span = span_for_syntax_node(modifier.syntax()); - match modifier { - Modifier::InvModifier(_) => GateModifier::Inv(span), - Modifier::PowModifier(pow_mod) => { - let Some(expr) = pow_mod.paren_expr() else { - let kind = SemanticErrorKind::PowModifierMustHaveExponent(span); - self.push_semantic_error(kind); - return GateModifier::Pow(None, span); - }; - extract_pow_exponent(&expr, span) - } - Modifier::CtrlModifier(ctrl_mod) => { - let ctrls = self.extract_controls_from_modifier(ctrl_mod.paren_expr()); - GateModifier::Ctrl(ctrls, span) - } - Modifier::NegCtrlModifier(neg_ctrl_mod) => { - let ctrls = self.extract_controls_from_modifier(neg_ctrl_mod.paren_expr()); - GateModifier::NegCtrl(ctrls, span) - } - } - }) - .collect::>(); - - if let Some(gate_call_expr) = modified_gate_call_expr.gate_call_expr() { - self.compile_gate_call_expr_impl(&gate_call_expr, expr_span, &modifiers) - } else { - let Some(gphase_call_expr) = modified_gate_call_expr.g_phase_call_expr() else { - // error - return None; - }; - self.compile_gphase_call_expr_impl(&gphase_call_expr, expr_span, &modifiers) - } - } - - /// Extracts the literal int from `ctrl(value)` and `negctrl(value)` modifiers. - /// Returns None if the modifier is invalid and pushes an error. - /// Returns Some(1) if the modifier is empty. - /// Returns Some(value) if the modifier is valid. - fn extract_controls_from_modifier(&mut self, paren_expr: Option) -> Option { - if let Some(paren_expr) = paren_expr { - if let Some((ctrl, sign)) = compile_paren_lit_int_expr(&paren_expr) { - if sign { - let kind = SemanticErrorKind::NegativeControlCount(span_for_syntax_node( - paren_expr.syntax(), - )); - self.push_semantic_error(kind); - } - Some(ctrl) - } else { - let kind = SemanticErrorKind::InvalidControlCount(span_for_syntax_node( - paren_expr.syntax(), - )); - self.push_semantic_error(kind); - None - } - } else { - Some(1) - } - } - - fn compile_paren_expr( - &mut self, - paren_expr: &oq3_syntax::ast::ParenExpr, - ) -> Option { - let span = span_for_syntax_node(paren_expr.syntax()); - let expr = paren_expr.expr()?; - let texpr = self.compile_expr(&expr)?; - let pexpr = ast_builder::wrap_expr_in_parens(texpr.expr, span); - Some(QasmTypedExpr { - ty: texpr.ty.clone(), - expr: pexpr, - }) - } - - fn compile_negated_literal_as_ty( - &mut self, - literal: &Literal, - ty: Option<&Type>, - ) -> Option { - let span = span_for_syntax_node(literal.syntax()); - match literal.kind() { - LiteralKind::IntNumber(value) => match ty { - Some(Type::Float(..)) => { - let expr = self.compile_int_to_double_literal(&value, true, span)?; - Some(QasmTypedExpr { - ty: ty.expect("Expected type").clone(), - expr, - }) - } - _ => Some(compile_intnumber_as_negated_int(&value, span)), - }, - LiteralKind::FloatNumber(value) => match ty { - Some(Type::Int(..) | Type::UInt(..)) => { - let value = value.value().expect("FloatNumber must have a value"); - #[allow(clippy::cast_possible_truncation)] - let converted_value = value.trunc() as i64; - #[allow(clippy::cast_precision_loss)] - if (converted_value as f64 - value).abs() > f64::EPSILON { - let span = span_for_syntax_node(literal.syntax()); - let kind = SemanticErrorKind::CastWouldCauseTruncation( - "Float".to_string(), - format!("{:?}", ty.expect("Expected type")), - span, - ); - self.push_semantic_error(kind); - None - } else { - let expr = build_lit_int_expr(-converted_value, span); - let ty = ty.expect("Expected type").clone(); - Some(QasmTypedExpr { ty, expr }) - } - } - _ => Some(compile_floatnumber_as_negated_double(&value, span)), - }, - _ => { - self.push_unimplemented_error_message( - "negated literal expressions", - literal.syntax(), - ); - None - } - } - } - - fn compile_prefix_expr( - &mut self, - prefix_expr: &oq3_syntax::ast::PrefixExpr, - ) -> Option { - let prefix_span = span_for_syntax_node(prefix_expr.syntax()); - match prefix_expr.op_kind() { - Some(UnaryOp::Neg) => match prefix_expr.expr() { - Some(Expr::Literal(lit)) => self.compile_negated_literal_as_ty(&lit, None), - Some(Expr::TimingLiteral(lit)) => { - let expr = prefix_expr - .expr() - .expect("TimingLiteral must have an expression"); - self.compile_timing_literal_as_complex(&lit, &expr, true) - } - Some(expr) => { - let texpr = self.compile_expr(&expr)?; - let expr = build_unary_op_expr(ast::UnOp::Neg, texpr.expr, prefix_span); - let ty = texpr.ty; - Some(QasmTypedExpr { ty, expr }) - } - None => { - self.push_unimplemented_error_message( - "negated empty expressions", - prefix_expr.syntax(), - ); - None - } - }, - Some(UnaryOp::LogicNot) => { - // bug in QASM parser, logical not and bitwise not are backwards - if let Some(prefix) = prefix_expr.expr() { - let texpr = self.compile_expr(&prefix)?; - let expr = build_unary_op_expr(ast::UnOp::NotB, texpr.expr, prefix_span); - let ty = texpr.ty; - Some(QasmTypedExpr { ty, expr }) - } else { - self.push_unimplemented_error_message( - "bitwise not empty expressions", - prefix_expr.syntax(), - ); - None - } - } - Some(UnaryOp::Not) => { - // bug in QASM parser, logical not and bitwise not are backwards - // THIS CODE IS FOR LOGICAL NOT - let ty = Type::Bool(IsConst::False); - let expr = self.compile_expr_to_ty_with_casts( - prefix_expr.expr(), - &ty, - prefix_expr.syntax(), - )?; - let expr = build_unary_op_expr(ast::UnOp::NotL, expr, prefix_span); - Some(QasmTypedExpr { ty, expr }) - } - None => None, - } - } - - #[allow(clippy::similar_names)] - fn compile_range_expr( - &mut self, - range_expr: &oq3_syntax::ast::RangeExpr, - node: &SyntaxNode, - ) -> Option { - let (start, step, stop) = range_expr.start_step_stop(); - let Some(start) = start else { - let span = span_for_syntax_node(range_expr.syntax()); - let kind = SemanticErrorKind::RangeExpressionsMustHaveStart(span); - self.push_semantic_error(kind); - return None; - }; - let Some(stop) = stop else { - let span = span_for_syntax_node(range_expr.syntax()); - let kind = SemanticErrorKind::RangeExpressionsMustHaveStop(span); - self.push_semantic_error(kind); - return None; - }; - let start_texpr = self.compile_expr(&start)?; - let stop_texpr = self.compile_expr(&stop)?; - let step_texpr = if let Some(step) = step { - Some(self.compile_expr(&step)?.expr) - } else { - None - }; - Some(QasmTypedExpr { - ty: Type::Range, - expr: build_range_expr( - start_texpr.expr, - stop_texpr.expr, - step_texpr, - span_for_syntax_node(node), - ), - }) - } - - fn compile_return_expr( - &mut self, - return_expr: &oq3_syntax::ast::ReturnExpr, - ) -> Option { - let stmt_span = span_for_syntax_node(return_expr.syntax()); - if !self.symbols.is_scope_rooted_in_subroutine() { - let kind = SemanticErrorKind::ReturnNotInSubroutine(stmt_span); - self.push_semantic_error(kind); - } - // the containing function will have an explicit return type - // or default of Void. We don't need to check the return type - // as that will be handled by Q# type checker. If there is no - // expression, we return Unit which Void maps to in Q#. - if let Some(expr) = return_expr.expr() { - let texpr = self.compile_expr(&expr)?; - let expr = ast_builder::build_return_expr(texpr.expr, stmt_span); - Some(QasmTypedExpr { ty: texpr.ty, expr }) - } else { - let expr = ast_builder::build_return_unit(stmt_span); - Some(QasmTypedExpr { - ty: Type::Void, - expr, - }) - } - } - - fn compile_for_stmt(&mut self, for_stmt: &oq3_syntax::ast::ForStmt) -> Option { - let loop_var = for_stmt - .loop_var() - .expect("For statement must have a loop variable"); - let loop_var_span = span_for_syntax_node(loop_var.syntax()); - let loop_var_scalar_ty = for_stmt - .scalar_type() - .expect("For statement must have a scalar type"); - let for_iterable = for_stmt - .for_iterable() - .expect("For statement must have an iterable"); - let stmt_span = span_for_syntax_node(for_stmt.syntax()); - let iterable = self.compile_for_iterable(&for_iterable)?; - let loop_var_sem_ty = - self.get_semantic_type_from_scalar_type(&loop_var_scalar_ty, false)?; - let qsharp_ty = - self.convert_semantic_type_to_qsharp_type(&loop_var_sem_ty, loop_var.syntax())?; - - let loop_var_symbol = Symbol { - name: loop_var.to_string(), - span: loop_var_span, - ty: loop_var_sem_ty.clone(), - qsharp_ty: qsharp_ty.clone(), - io_kind: IOKind::Default, - }; - - self.symbols.push_scope(crate::symbols::ScopeKind::Block); - if self.symbols.insert_symbol(loop_var_symbol.clone()).is_err() { - self.push_redefined_symbol_error(loop_var.to_string(), loop_var_span); - return None; - } - - let body = if let Some(stmt) = for_stmt.stmt() { - let stmt = self.compile_stmt(&stmt); - self.symbols.pop_scope(); - build_stmt_wrapped_block_expr(stmt?) - } else if let Some(block) = for_stmt.body() { - let block = self.compile_block_expr(&block); - self.symbols.pop_scope(); - block - } else { - let span = span_for_syntax_node(for_stmt.syntax()); - let kind = SemanticErrorKind::ForStatementsMustHaveABodyOrStatement(span); - self.push_semantic_error(kind); - None? - }; - - Some(ast_builder::build_for_stmt( - &loop_var_symbol.name, - loop_var_symbol.span, - &loop_var_symbol.qsharp_ty, - iterable.expr, - body, - stmt_span, - )) - } - - fn compile_for_iterable( - &mut self, - for_iterable: &oq3_syntax::ast::ForIterable, - ) -> Option { - if let Some(expr) = for_iterable.set_expression() { - let expr_list = expr.expression_list()?; - - let expression_list = self - .compile_expression_list(&expr_list)? - .into_iter() - .map(|e| e.expr) - .collect(); - - let expr = build_expr_array_expr(expression_list, span_for_syntax_node(expr.syntax())); - Some(QasmTypedExpr { - ty: Type::Set, - expr, - }) - } else if let Some(expr) = for_iterable.range_expr() { - self.compile_range_expr(&expr, for_iterable.syntax()) - } else if let Some(expr) = for_iterable.for_iterable_expr() { - // For iterating over something like bit[n] - self.compile_expr(&expr) - } else { - let span = span_for_syntax_node(for_iterable.syntax()); - let kind = SemanticErrorKind::ForIterableInvalidExpression(span); - self.push_semantic_error(kind); - None - } - } - - #[allow(clippy::too_many_lines)] - fn compile_gate_decl(&mut self, gate: &oq3_syntax::ast::Gate) -> Option { - let name = gate.name()?; - - // Once we support angle types, we will use this as the type for - // the angle parameters' coersion: - //let angle_ty = Type::Angle(None, IsConst::True); - let angle_ty = Type::Float(None, IsConst::True); - let qs_angle_ty = self.convert_semantic_type_to_qsharp_type(&angle_ty, gate.syntax())?; - let ast_angle_ty = map_qsharp_type_to_ast_ty(&qs_angle_ty); - let qubit_ty = Type::Qubit; - let qs_qubit_ty = self.convert_semantic_type_to_qsharp_type(&qubit_ty, gate.syntax())?; - let ast_qubit_ty = map_qsharp_type_to_ast_ty(&qs_qubit_ty); - - let gate_span = span_for_syntax_node(gate.syntax()); - if !self.symbols.is_current_scope_global() { - let kind = SemanticErrorKind::QuantumDeclarationInNonGlobalScope(gate_span); - self.push_semantic_error(kind); - return None; - } - // get the name of the args and their spans - let cargs = gate - .angle_params() - .iter() - .flat_map(ParamList::params) - .map(|e| { - ( - e.text().to_string(), - ast_angle_ty.clone(), - build_arg_pat( - e.text().to_string(), - span_for_named_item(&e), - ast_angle_ty.clone(), - ), - ) - }) - .collect::>(); - - let qargs = gate - .qubit_params() - .iter() - .flat_map(ParamList::params) - .map(|e| { - ( - e.text().to_string(), - ast_qubit_ty.clone(), - build_arg_pat( - e.text().to_string(), - span_for_named_item(&e), - ast_qubit_ty.clone(), - ), - ) - }) - .collect::>(); - - self.symbols.push_scope(crate::symbols::ScopeKind::Gate); - // bind the cargs, qargs and body - - for (name, _, pat) in &cargs { - let symbol = Symbol { - name: name.clone(), - span: pat.span, - ty: angle_ty.clone(), - qsharp_ty: qs_angle_ty.clone(), - io_kind: IOKind::Default, - }; - if self.symbols.insert_symbol(symbol).is_err() { - self.push_redefined_symbol_error(name, pat.span); - } - } - for (name, _, pat) in &qargs { - let symbol = Symbol { - name: name.clone(), - span: pat.span, - ty: qubit_ty.clone(), - qsharp_ty: qs_qubit_ty.clone(), - io_kind: IOKind::Default, - }; - if self.symbols.insert_symbol(symbol).is_err() { - self.push_redefined_symbol_error(name, pat.span); - } - } - let body = gate.body().map(|body| self.compile_block_expr(&body)); - let body_span = gate - .body() - .map(|body| span_for_syntax_node(body.syntax())) - .unwrap_or_default(); - self.symbols.pop_scope(); - // create a gate symbol with type information with num cargs and qargs - - let gate_ty = Type::Gate(cargs.len(), qargs.len()); - let qs_gate_ty = self.convert_semantic_type_to_qsharp_type(&gate_ty, gate.syntax())?; - let name_span = span_for_syntax_node(name.syntax()); - let symbol = Symbol { - name: name.to_string(), - span: name_span, - ty: gate_ty, - qsharp_ty: qs_gate_ty, - io_kind: IOKind::Default, - }; - if self.symbols.insert_symbol(symbol).is_err() { - self.push_redefined_symbol_error(name.to_string(), span_for_syntax_node(name.syntax())); - return None; - } - - if self.next_gate_as_item { - Some(ast_builder::build_gate_decl( - name.to_string(), - cargs, - qargs, - body, - name_span, - body_span, - gate_span, - )) - } else { - Some(build_lambda( - name.to_string(), - cargs, - qargs, - body, - name_span, - body_span, - gate_span, - None, - ast::CallableKind::Operation, - )) - } - } - - fn compile_if_stmt(&mut self, if_stmt: &oq3_syntax::ast::IfStmt) -> Option { - let stmt_span = span_for_syntax_node(if_stmt.syntax()); - - let Some(condition) = if_stmt.condition() else { - let kind = - SemanticErrorKind::IfStmtMissingExpression("condition".to_string(), stmt_span); - self.push_semantic_error(kind); - return None; - }; - let node = condition.syntax(); - let cond_ty = Type::Bool(IsConst::False); - let cond = self - .compile_expr_to_ty_with_casts(Some(condition.clone()), &cond_ty, node) - .map(|expr| QasmTypedExpr { ty: cond_ty, expr }); - - let Some(block) = if_stmt.then_branch() else { - let kind = - SemanticErrorKind::IfStmtMissingExpression("then block".to_string(), stmt_span); - self.push_semantic_error(kind); - return None; - }; - - self.symbols.push_scope(crate::symbols::ScopeKind::Block); - let then_block = self.compile_block_expr(&block); - self.symbols.pop_scope(); - let else_block = if_stmt.else_branch().map(|block_expr| { - self.symbols.push_scope(crate::symbols::ScopeKind::Block); - let else_expr = self.compile_block_expr(&block_expr); - self.symbols.pop_scope(); - else_expr - }); - - // The cond may have failed to compile, in which case we return None - // we let it go this far so that we could accumulate any errors in - // the block. - let cond = cond?; - let if_expr = if let Some(else_block) = else_block { - build_if_expr_then_block_else_block(cond.expr, then_block, else_block, stmt_span) - } else { - build_if_expr_then_block(cond.expr, then_block, stmt_span) - }; - - Some(build_stmt_semi_from_expr(if_expr)) - } - - fn compile_io_decl_stmt( - &mut self, - decl: &oq3_syntax::ast::IODeclarationStatement, - ) -> Option { - if decl.array_type().is_some() { - self.push_unimplemented_error_message("array io declarations", decl.syntax()); - return None; - } - let name = decl.name().expect("io declaration must have a name"); - let scalar_ty = decl - .scalar_type() - .expect("io declaration must have a scalar type"); - let io_kind = match decl.input_token() { - Some(_) => IOKind::Input, - None => IOKind::Output, - }; - // if we can't convert the scalar type, we can't proceed, an error has been pushed - let ty = self.get_semantic_type_from_scalar_type(&scalar_ty, false)?; - let qsharp_ty = self.convert_semantic_type_to_qsharp_type(&ty, name.syntax())?; - let symbol = Symbol { - name: name.to_string(), - span: span_for_syntax_node(name.syntax()), - ty: ty.clone(), - qsharp_ty: qsharp_ty.clone(), - io_kind: io_kind.clone(), - }; - - if self.symbols.insert_symbol(symbol).is_err() { - self.push_redefined_symbol_error(name.to_string(), span_for_syntax_node(name.syntax())); - return None; - } - - // if we have output, we need to assign a default value to declare the variable - // if we have input, we can keep return none as we would promote the variable - // to a parameter in the function signature once we generate the function - if io_kind == IOKind::Output { - let rhs = self.get_default_value(&ty, name.syntax())?; - let stmt = build_classical_decl( - name.to_string(), - false, - span_for_syntax_node(scalar_ty.syntax()), - span_for_syntax_node(decl.syntax()), - span_for_syntax_node(name.syntax()), - &qsharp_ty, - rhs, - ); - Some(stmt) - } else { - None - } - } - - /// Let statements shouldn't make it into parsing - /// Looking at the parser, this statement seems - /// anachronistic and should be removed from the parser - fn compile_let_stmt(&mut self, let_stmt: &oq3_syntax::ast::LetStmt) -> Option { - self.push_unsupported_error_message("let statements", let_stmt.syntax()); - None - } - - /// Measure statements shouldn't make it into parsing - /// Looking at the parser, this statement seems - /// anachronistic and should be removed from the parser - fn compile_measure_stmt(&mut self, measure: &oq3_syntax::ast::Measure) -> Option { - self.push_unsupported_error_message("measure statements", measure.syntax()); - None - } - - fn compile_quantum_decl( - &mut self, - decl: &oq3_syntax::ast::QuantumDeclarationStatement, - ) -> Option { - let decl_span = span_for_syntax_node(decl.syntax()); - if !self.symbols.is_current_scope_global() { - let kind = SemanticErrorKind::QuantumDeclarationInNonGlobalScope(decl_span); - self.push_semantic_error(kind); - return None; - } - let qubit_ty = decl - .qubit_type() - .expect("Quantum declaration must have a qubit type"); - let name = decl.name().expect("Quantum declaration must have a name"); - - let designator = match qubit_ty.designator() { - Some(designator) => { - let width_span = span_for_syntax_node(designator.syntax()); - let width = extract_dims_from_designator(Some(designator)) - .expect("Designator must be a literal integer"); - - Some((width, width_span)) - } - None => None, - }; - let ty = if let Some((width, _)) = designator { - Type::QubitArray(ArrayDims::D1(width as usize)) - } else { - Type::Qubit - }; - let qsharp_ty = self.convert_semantic_type_to_qsharp_type(&ty, name.syntax())?; - let symbol = Symbol { - name: name.to_string(), - span: span_for_syntax_node(name.syntax()), - ty, - qsharp_ty, - io_kind: IOKind::Default, - }; - - if self.symbols.insert_symbol(symbol).is_err() { - self.push_redefined_symbol_error(name.to_string(), span_for_syntax_node(name.syntax())); - return None; - } - let name = name.to_string(); - let name_span = span_for_named_item(decl); - let stmt = match self.config.qubit_semantics { - QubitSemantics::QSharp => { - if let Some((width, designator_span)) = designator { - managed_qubit_alloc_array(name, width, decl_span, name_span, designator_span) - } else { - build_managed_qubit_alloc(name, decl_span, name_span) - } - } - QubitSemantics::Qiskit => { - if let Some((width, span)) = designator { - build_unmanaged_qubit_alloc_array(name, width, decl_span, name_span, span) - } else { - build_unmanaged_qubit_alloc(name, decl_span, name_span) - } - } - }; - Some(stmt) - } - - fn compile_reset_call(&mut self, expr: &oq3_syntax::ast::Reset) -> Option { - let Some(token) = expr.reset_token() else { - let span = span_for_syntax_node(expr.syntax()); - let kind = SemanticErrorKind::ResetExpressionMustHaveName(span); - self.push_semantic_error(kind); - return None; - }; - let name_span = span_for_syntax_token(&token); - - let Some(operand) = expr.gate_operand() else { - let span = span_for_syntax_node(expr.syntax()); - let kind = SemanticErrorKind::ResetExpressionMustHaveGateOperand(span); - self.push_semantic_error(kind); - return None; - }; - let args = self.compile_gate_operand(&operand)?; - let operand_span = span_for_syntax_node(operand.syntax()); - let expr = build_reset_call(args.expr, name_span, operand_span); - - Some(build_stmt_semi_from_expr(expr)) - } - - fn compile_switch_stmt( - &mut self, - switch_case: &oq3_syntax::ast::SwitchCaseStmt, - ) -> Option { - // The condition for the switch statement must be an integer type - // so instead of using `compile_expr` we use `resolve_rhs_expr_with_casts` - // forcing the type to be an integer type with implicit casts if necessary - let cond_ty = Type::Int(None, IsConst::False); - // We try to compile all expressions first to accumulate errors - let control = switch_case.control().and_then(|control| { - self.compile_expr_to_ty_with_casts(Some(control), &cond_ty, switch_case.syntax()) - }); - let cases: Vec<_> = switch_case - .case_exprs() - .map(|case| { - let lhs = case - .expression_list() - .and_then(|expr| self.compile_typed_expression_list(&expr, &cond_ty)); - self.symbols.push_scope(crate::symbols::ScopeKind::Block); - let rhs = case - .block_expr() - .map(|block| self.compile_block_expr(&block)); - self.symbols.pop_scope(); - (lhs, rhs) - }) - .collect(); - self.symbols.push_scope(crate::symbols::ScopeKind::Block); - let default_block = switch_case - .default_block() - .map(|block| self.compile_block_expr(&block)); - self.symbols.pop_scope(); - - // at this point we tried to compile everything, bail if we have any errors - if control.is_none() - || cases - .iter() - .any(|(lhs, rhs)| lhs.is_none() || rhs.is_none()) - || cases.is_empty() - { - // See tests, but it is a parse error to have a switch statement with - // no cases, even if the default block is present. Getting here means - // the parser is broken or they changed the grammar. - panic!("Switch statement must have a control expression and at least one case"); - } - - // update bindings based on what we checked above - let control = control?; - let cases: Vec<_> = cases - .into_iter() - .map(|(lhs, rhs)| { - let lhs = lhs.expect("Case must have a lhs"); - let rhs = rhs.expect("Case must have a rhs"); - (lhs, rhs) - }) - .collect(); - - // Semantics of switch case is that the outer block doesn't introduce - // a new scope but each case rhs does. - - // Can we add a new scope anyway to hold a temporary variable? - // if we do that, we can refer to a new variable instead of the control - // expr this would allow us to avoid the need to resolve the control - // expr multiple times in the case where we have to coerce the control - // expr to the correct type. Introducing a new variable without a new - // scope would effect output semantics. - - // For each case, convert the lhs into a sequence of equality checks - // and then fold them into a single expression of logical ors for - // the if expr - let cases: Vec<_> = cases - .into_iter() - .map(|(lhs, rhs)| { - let case = lhs - .iter() - .map(|texpr| { - ast_builder::build_binary_expr( - false, - ast::BinOp::Eq, - control.clone(), - texpr.expr.clone(), - texpr.expr.span, - ) - }) - .fold(None, |acc, expr| match acc { - None => Some(expr), - Some(acc) => { - let qsop = ast::BinOp::OrL; - let span = Span { - lo: acc.span.lo, - hi: expr.span.hi, - }; - Some(build_binary_expr(false, qsop, acc, expr, span)) - } - }); - // The type checker doesn't know that we have at least one case - // so we have to unwrap here since the accumulation is guaranteed - // to have Some(value) - let case = case.expect("Case must have at least one expression"); - (case, rhs) - }) - .collect(); - - // cond is resolved, cases are resolved, default is resolved - // we can now build the if expression backwards. The default block - // is the last else block, the last case is the then block, and the rest - // are built as if-else blocks with the last case as the else block - let default_expr = default_block.map(build_wrapped_block_expr); - let if_expr = cases - .into_iter() - .rev() - .fold(default_expr, |else_expr, (cond, block)| { - let span = Span { - lo: cond.span.lo, - hi: block.span.hi, - }; - Some(build_if_expr_then_block_else_expr( - cond, block, else_expr, span, - )) - }); - if_expr.map(build_stmt_semi_from_expr) - } - - // This is a no-op in Q# but we will save it for future use - fn compile_version_stmt( - &mut self, - version: &oq3_syntax::ast::VersionString, - ) -> Option { - if let Some(version) = version.version() { - let version_str = format!("{version}"); - if !version_str.starts_with("3.") { - self.push_unsupported_error_message( - "OpenQASM versions other than 3", - version.syntax(), - ); - } - self.version = Some(version_str); - } - None - } - - /// Note: From the ``OpenQASM`` 3.0 specification: - /// This clearly allows users to write code that does not terminate. - /// We do not discuss implementation details here, but one possibility - /// is to compile into target code that imposes iteration limits. - fn compile_while_stmt(&mut self, while_stmt: &oq3_syntax::ast::WhileStmt) -> Option { - let stmt_span = span_for_syntax_node(while_stmt.syntax()); - let Some(condition) = while_stmt.condition() else { - let kind = - SemanticErrorKind::WhileStmtMissingExpression("condition".to_string(), stmt_span); - self.push_semantic_error(kind); - return None; - }; - - let node = condition.syntax(); - let cond_ty = Type::Bool(IsConst::False); - let cond = self - .compile_expr_to_ty_with_casts(Some(condition.clone()), &cond_ty, node) - .map(|expr| QasmTypedExpr { ty: cond_ty, expr }); - - // if cond is none, an error was pushed - // or the expression couldn't be resolved - // We keep going to catch more errors but only if the condition - // expression can be compiled - let cond = match cond { - Some(cond) => cond, - None => self.compile_expr(&condition)?, - }; - - let Some(block) = while_stmt.body() else { - let kind = SemanticErrorKind::WhileStmtMissingExpression("body".to_string(), stmt_span); - self.push_semantic_error(kind); - return None; - }; - - self.symbols.push_scope(crate::symbols::ScopeKind::Block); - let block_body = self.compile_block_expr(&block); - self.symbols.pop_scope(); - Some(ast_builder::build_while_stmt( - cond.expr, block_body, stmt_span, - )) - } - - fn convert_semantic_type_to_qsharp_type( - &mut self, - ty: &Type, - node: &SyntaxNode, - ) -> Option { - let is_const = ty.is_const(); - match ty { - Type::Bit(_) => Some(crate::types::Type::Result(is_const)), - Type::Qubit => Some(crate::types::Type::Qubit), - Type::HardwareQubit => { - let message = "HardwareQubit to Q# type"; - self.push_unsupported_error_message(message, node); - None - } - Type::Int(width, _) | Type::UInt(width, _) => { - if let Some(width) = width { - if *width > 64 { - Some(crate::types::Type::BigInt(is_const)) - } else { - Some(crate::types::Type::Int(is_const)) - } - } else { - Some(crate::types::Type::Int(is_const)) - } - } - Type::Float(_, _) | Type::Angle(_, _) => Some(crate::types::Type::Double(is_const)), - Type::Complex(_, _) => Some(crate::types::Type::Complex(is_const)), - Type::Bool(_) => Some(crate::types::Type::Bool(is_const)), - Type::Duration(_) => { - self.push_unsupported_error_message("Duration type values", node); - None - } - Type::Stretch(_) => { - self.push_unsupported_error_message("Stretch type values", node); - None - } - Type::BitArray(dims, _) => Some(crate::types::Type::ResultArray(dims.into(), is_const)), - Type::QubitArray(dims) => Some(crate::types::Type::QubitArray(dims.into())), - Type::IntArray(dims) | Type::UIntArray(dims) => { - Some(crate::types::Type::IntArray(dims.into(), is_const)) - } - Type::FloatArray(dims) => Some(crate::types::Type::DoubleArray(dims.into())), - Type::AngleArray(_) => todo!("AngleArray to Q# type"), - Type::ComplexArray(_) => todo!("ComplexArray to Q# type"), - Type::BoolArray(dims) => Some(crate::types::Type::BoolArray(dims.into(), is_const)), - Type::Gate(cargs, qargs) => { - if let (Ok(cargs), Ok(qargs)) = (u32::try_from(*cargs), u32::try_from(*qargs)) { - Some(crate::types::Type::Callable( - crate::types::CallableKind::Operation, - cargs, - qargs, - )) - } else { - let message = format!( - "Gate with {cargs} control and {qargs} qubits has too many arguments" - ); - self.push_unsupported_error_message(message, node); - None - } - } - Type::Range => Some(crate::types::Type::Range), - Type::Set => todo!("Set to Q# type"), - Type::Void => Some(crate::types::Type::Tuple(vec![])), - _ => { - let msg = format!("Converting {ty:?} to Q# type"); - self.push_unimplemented_error_message(msg, node); - None - } - } - } - - fn get_default_value(&mut self, ty: &Type, node: &SyntaxNode) -> Option { - let span = span_for_syntax_node(node); - match ty { - Type::Bit(_) => Some(build_lit_result_expr(ast::Result::Zero, span)), - Type::Qubit => { - let message = "Qubit default values"; - self.push_unsupported_error_message(message, node); - None - } - Type::HardwareQubit => { - let message = "HardwareQubit default values"; - self.push_unsupported_error_message(message, node); - None - } - Type::Int(width, _) | Type::UInt(width, _) => { - if let Some(width) = width { - if *width > 64 { - Some(build_lit_bigint_expr(BigInt::from(0), span)) - } else { - Some(build_lit_int_expr(0, span)) - } - } else { - Some(build_lit_int_expr(0, span)) - } - } - Type::Float(_, _) => Some(build_lit_double_expr(0.0, span)), - Type::Angle(_, _) => todo!("Angle default values"), - Type::Complex(_, _) => Some(build_lit_complex_expr( - crate::types::Complex::new(0.0, 0.0), - span, - )), - Type::Bool(_) => Some(build_lit_bool_expr(false, span)), - Type::Duration(_) => { - self.push_unsupported_error_message( - "Duration type values are not supported.", - node, - ); - None - } - Type::Stretch(_) => { - self.push_unsupported_error_message("Stretch type values are not supported.", node); - None - } - Type::BitArray(dims, _) => match dims { - ArrayDims::D1(len) => Some(build_default_result_array_expr(*len, span)), - ArrayDims::D2(_, _) => { - self.push_unsupported_error_message( - "2-dim Bit Arrays without default values", - node, - ); - None - } - ArrayDims::D3(_, _, _) => { - self.push_unsupported_error_message( - "3-dim Bit Arrays without default values", - node, - ); - None - } - }, - Type::QubitArray(_) => { - let message = "QubitArray default values"; - self.push_unsupported_error_message(message, node); - None - } - Type::IntArray(_) - | Type::UIntArray(_) - | Type::FloatArray(_) - | Type::AngleArray(_) - | Type::ComplexArray(_) - | Type::BoolArray(_) => { - self.push_unsupported_error_message("Arrays without default values", node); - None - } - Type::DurationArray(_) => { - self.push_unsupported_error_message( - "DurationArray type values are not supported.", - node, - ); - None - } - Type::Gate(_, _) - | Type::Range - | Type::Set - | Type::Void - | Type::ToDo - | Type::Undefined => { - let mut message = format!("Default values for {ty:?} are unsupported."); - message.push_str(" This is likely a bug in the compiler."); - self.push_unsupported_error_message(message, node); - None - } - } - } - - /// Define the standard gates in the symbol table. - /// The sdg, tdg, crx, cry, crz, and ch are defined - /// as their bare gates, and modifiers are applied - /// when calling them. - fn define_stdgates(&mut self, include: &oq3_syntax::ast::Include) { - fn gate_symbol(name: &str, cargs: usize, qargs: usize) -> Symbol { - Symbol { - name: name.to_string(), - ty: Type::Gate(cargs, qargs), - ..Default::default() - } - } - let gates = vec![ - gate_symbol("X", 0, 1), - gate_symbol("Y", 0, 1), - gate_symbol("Z", 0, 1), - gate_symbol("H", 0, 1), - gate_symbol("S", 0, 1), - gate_symbol("T", 0, 1), - gate_symbol("Rx", 1, 1), - gate_symbol("Rxx", 1, 2), - gate_symbol("Ry", 1, 1), - gate_symbol("Ryy", 1, 2), - gate_symbol("Rz", 1, 1), - gate_symbol("Rzz", 1, 2), - gate_symbol("CNOT", 0, 2), - gate_symbol("CY", 0, 2), - gate_symbol("CZ", 0, 2), - gate_symbol("I", 0, 1), - gate_symbol("SWAP", 0, 2), - gate_symbol("CCNOT", 0, 3), - ]; - for gate in gates { - let name = gate.name.clone(); - if self.symbols.insert_symbol(gate).is_err() { - self.push_redefined_symbol_error( - name.as_str(), - span_for_syntax_node(include.syntax()), - ); - } - } - } - - fn get_semantic_type_from_scalar_type( - &mut self, - scalar_ty: &oq3_syntax::ast::ScalarType, - is_const: bool, - ) -> Option { - let designator = get_designator_from_scalar_type(scalar_ty); - let is_const = is_const.into(); - let width = if let Some(designator) = designator { - match designator.expr() { - Some(oq3_syntax::ast::Expr::Literal(ref literal)) => match literal.kind() { - oq3_syntax::ast::LiteralKind::IntNumber(int_num) => { - let size: u32 = u32::try_from(int_num.value()?).ok()?; - Some(size) - } - _ => None, - }, - Some(expr) => { - let span = span_for_syntax_node(expr.syntax()); - let kind = SemanticErrorKind::DesignatorMustBePositiveIntLiteral(span); - self.push_semantic_error(kind); - return None; - } - None => None, - } - } else { - None - }; - - let ty = match scalar_ty.kind() { - oq3_syntax::ast::ScalarTypeKind::Angle => { - oq3_semantics::types::Type::Angle(width, is_const) - } - oq3_syntax::ast::ScalarTypeKind::Bit => match width { - Some(width) => { - oq3_semantics::types::Type::BitArray(ArrayDims::D1(width as usize), is_const) - } - None => oq3_semantics::types::Type::Bit(is_const), - }, - oq3_syntax::ast::ScalarTypeKind::Bool => oq3_semantics::types::Type::Bool(is_const), - oq3_syntax::ast::ScalarTypeKind::Complex => { - oq3_semantics::types::Type::Complex(width, is_const) - } - oq3_syntax::ast::ScalarTypeKind::Duration => { - oq3_semantics::types::Type::Duration(is_const) - } - oq3_syntax::ast::ScalarTypeKind::Float => { - oq3_semantics::types::Type::Float(width, is_const) - } - oq3_syntax::ast::ScalarTypeKind::Int => { - oq3_semantics::types::Type::Int(width, is_const) - } - oq3_syntax::ast::ScalarTypeKind::Qubit => match width { - Some(width) => { - oq3_semantics::types::Type::QubitArray(ArrayDims::D1(width as usize)) - } - None => oq3_semantics::types::Type::Qubit, - }, - oq3_syntax::ast::ScalarTypeKind::Stretch => { - oq3_semantics::types::Type::Stretch(is_const) - } - oq3_syntax::ast::ScalarTypeKind::UInt => { - oq3_semantics::types::Type::UInt(width, is_const) - } - oq3_syntax::ast::ScalarTypeKind::None => { - let msg = "ScalarTypeKind::None should have been handled by the parser".to_string(); - let span = span_for_syntax_node(scalar_ty.syntax()); - let kind = SemanticErrorKind::UnexpectedParserError(msg, span); - self.push_semantic_error(kind); - return None; - } - }; - Some(ty) - } - - fn try_cast_expr_to_type( - &mut self, - ty: &Type, - rhs: &QasmTypedExpr, - node: &SyntaxNode, - ) -> Option { - if *ty == rhs.ty { - // Base case, we shouldn't have gotten here - // but if we did, we can just return the rhs - return Some(rhs.clone()); - } - if types_equal_except_const(ty, &rhs.ty) { - if rhs.ty.is_const() { - // lhs isn't const, but rhs is, we can just return the rhs - return Some(rhs.clone()); - } - // the lsh is supposed to be const but is being initialized - // to a non-const value, we can't allow this - return None; - } - // if the target type is wider, we can try to relax the rhs type - // We only do this for float and complex. Int types - // require extra handling for BigInts - match (ty, &rhs.ty) { - (Type::Float(w1, _), Type::Float(w2, _)) - | (Type::Complex(w1, _), Type::Complex(w2, _)) => { - if w1.is_none() && w2.is_some() { - return Some(QasmTypedExpr { - ty: ty.clone(), - expr: rhs.expr.clone(), - }); - } - - if *w1 >= *w2 { - return Some(QasmTypedExpr { - ty: ty.clone(), - expr: rhs.expr.clone(), - }); - } - } - _ => {} - } - // Casting of literals is handled elsewhere. This is for casting expressions - // which cannot be bypassed and must be handled by built-in Q# casts from - // the standard library. - match &rhs.ty { - Type::Angle(_, _) => self.cast_angle_expr_to_type(ty, rhs, node), - Type::Bit(_) => self.cast_bit_expr_to_type(ty, rhs, node), - Type::Bool(_) => self.cast_bool_expr_to_type(ty, rhs), - Type::Complex(_, _) => cast_complex_expr_to_type(ty, rhs), - Type::Float(_, _) => self.cast_float_expr_to_type(ty, rhs, node), - Type::Int(_, _) | Type::UInt(_, _) => self.cast_int_expr_to_type(ty, rhs), - Type::BitArray(dims, _) => self.cast_bitarray_expr_to_type(dims, ty, rhs), - _ => None, - } - } - - fn cast_expr_to_type( - &mut self, - ty: &Type, - rhs: &QasmTypedExpr, - node: &SyntaxNode, - ) -> Option { - let cast_expr = self.try_cast_expr_to_type(ty, rhs, node); - if cast_expr.is_none() { - let rhs_ty_name = format!("{:?}", rhs.ty); - let lhs_ty_name = format!("{ty:?}"); - let span = span_for_syntax_node(node); - let kind = SemanticErrorKind::CannotCast(rhs_ty_name, lhs_ty_name, span); - self.push_semantic_error(kind); - } - cast_expr - } - - #[allow(clippy::too_many_lines)] - fn cast_literal_expr_to_type( - &mut self, - ty: &Type, - rhs: &QasmTypedExpr, - literal: &Literal, - ) -> Option { - if *ty == rhs.ty { - // Base case, we shouldn't have gotten here - // but if we did, we can just return the rhs - return Some(rhs.clone()); - } - if types_equal_except_const(ty, &rhs.ty) { - if rhs.ty.is_const() { - // lhs isn't const, but rhs is, we can just return the rhs - return Some(rhs.clone()); - } - // the lsh is supposed to be const but is being initialized - // to a non-const value, we can't allow this - return None; - } - assert!( - can_cast_literal(ty, &rhs.ty) || can_cast_literal_with_value_knowledge(ty, literal) - ); - let lhs_ty = ty.clone(); - let rhs_ty = rhs.ty.clone(); - let span = rhs.expr.span; - - if matches!(lhs_ty, Type::Bit(..)) { - if let LiteralKind::IntNumber(value) = literal.kind() { - return compile_intnumber_as_bit(&value, span, ty); - } else if let LiteralKind::Bool(value) = literal.kind() { - let expr = build_lit_result_expr(value.into(), span); - return Some(QasmTypedExpr { - ty: ty.clone(), - expr, - }); - } - } - // if lhs_ty is 1 dim bitarray and rhs is int/uint, we can cast - let (is_int_to_bit_array, size) = match &lhs_ty { - Type::BitArray(dims, _) => { - if matches!(rhs.ty, Type::Int(..) | Type::UInt(..)) { - match dims { - ArrayDims::D1(size) => (true, *size), - _ => (false, 0), - } - } else { - (false, 0) - } - } - _ => (false, 0), - }; - if is_int_to_bit_array { - if let LiteralKind::IntNumber(value) = literal.kind() { - // Value can't be negative as IntNumber is unsigned - // any sign would come from a prefix expression - if let Some(value) = value.value() { - if let Ok(value) = value.try_into() { - let value: i64 = value; - if value >= 0 && value < (1 << size) { - let bitstring = format!("{value:0size$b}"); - let expr = build_lit_result_array_expr_from_bitstring(bitstring, span); - return Some(QasmTypedExpr { - ty: ty.clone(), - expr, - }); - } - return None; - } - } - } - } - if matches!(lhs_ty, Type::UInt(..)) { - if let LiteralKind::IntNumber(value) = literal.kind() { - // Value can't be negative as IntNumber is unsigned - // any sign would come from a prefix expression - if let Some(value) = value.value() { - if let Ok(value) = value.try_into() { - let value: i64 = value; - let expr = build_lit_int_expr(value, span); - let ty = Type::UInt(None, IsConst::True); - return Some(QasmTypedExpr { ty, expr }); - } - } - } - } - let result = match (&lhs_ty, &rhs_ty) { - (Type::Float(..), Type::Int(..) | Type::UInt(..)) => { - // the qasm type is int/uint, but the expr will be q# int - if let LiteralKind::IntNumber(value) = literal.kind() { - let expr = self.compile_int_to_double_literal(&value, false, span)?; - Some(QasmTypedExpr { - ty: ty.clone(), - expr, - }) - } else { - panic!("Literal must be an IntNumber") - } - } - (Type::Float(..), Type::Float(..)) => { - if let LiteralKind::FloatNumber(value) = literal.kind() { - let value = value.value().expect("FloatNumber must have a value"); - let expr = build_lit_double_expr(value, span); - Some(QasmTypedExpr { - ty: ty.clone(), - expr, - }) - } else { - panic!("Literal must be a FloatNumber") - } - } - (Type::Complex(..), Type::Float(..)) => { - let expr = build_complex_from_expr(rhs.expr.clone()); - Some(QasmTypedExpr { - ty: ty.clone(), - expr, - }) - } - (Type::Complex(..), Type::Int(..) | Type::UInt(..)) => { - // complex requires a double as input, so we need to - // convert the int to a double, then create the complex - if let LiteralKind::IntNumber(value) = literal.kind() { - if let Some(expr) = self.compile_int_to_double_literal_to_complex(&value, span) - { - return Some(QasmTypedExpr { - ty: ty.clone(), - expr, - }); - } - } - panic!("Literal must be an IntNumber") - } - (Type::Bit(..), Type::Int(..) | Type::UInt(..)) => { - // we've already checked that the value is 0 or 1 - if let LiteralKind::IntNumber(value) = literal.kind() { - let value = value.value().expect("IntNumber must have a value"); - if value == 0 || value == 1 { - let expr = build_lit_result_expr((value == 1).into(), rhs.expr.span); - Some(QasmTypedExpr { - ty: ty.clone(), - expr, - }) - } else { - panic!("Value must be 0 or 1"); - } - } else { - panic!("Literal must be an IntNumber"); - } - } - (Type::Int(..), Type::Int(..) | Type::UInt(..)) => { - // we've already checked that this conversion can happen - if let LiteralKind::IntNumber(value) = literal.kind() { - let value = value.value().expect("IntNumber must have a value"); - let expr = if let Ok(value) = value.try_into() { - let value: i64 = value; - build_lit_int_expr(value, span) - } else { - build_lit_bigint_expr(BigInt::from(value), span) - }; - Some(QasmTypedExpr { - ty: ty.clone(), - expr, - }) - } else { - panic!("Literal must be an IntNumber"); - } - } - _ => None, - }; - if result.is_none() { - // we assert that the type can be casted - // but we didn't cast it, so this is a bug - panic!("Literal type cast failed lhs: {:?}, rhs: {:?}", ty, rhs.ty); - } else { - result - } - } - - fn create_entry_operation>( - &mut self, - name: S, - whole_span: Span, - ) -> (ast::Item, OperationSignature) { - let stmts = self.stmts.drain(..).collect::>(); - let input = self.symbols.get_input(); - let output = self.symbols.get_output(); - self.create_entry_item( - name, - stmts, - input, - output, - whole_span, - self.config.output_semantics, - ) - } - - /// +----------------+-------------------------------------------------------------+ - /// | Allowed casts | Casting To | - /// +----------------+-------+-----+------+-------+-------+-----+----------+-------+ - /// | Casting From | bool | int | uint | float | angle | bit | duration | qubit | - /// +----------------+-------+-----+------+-------+-------+-----+----------+-------+ - /// | angle | Yes | No | No | No | - | Yes | No | No | - /// +----------------+-------+-----+------+-------+-------+-----+----------+-------+ - fn cast_angle_expr_to_type( - &mut self, - ty: &Type, - rhs: &QasmTypedExpr, - node: &SyntaxNode, - ) -> Option { - assert!(matches!(rhs.ty, Type::Bit(..))); - match ty { - Type::Bit(..) => { - let msg = "Cast angle to bit"; - self.push_unimplemented_error_message(msg, node); - None - } - Type::Bool(..) => { - let msg = "Cast angle to bool"; - self.push_unimplemented_error_message(msg, node); - None - } - - _ => None, - } - } - - /// +----------------+-------------------------------------------------------------+ - /// | Allowed casts | Casting To | - /// +----------------+-------+-----+------+-------+-------+-----+----------+-------+ - /// | Casting From | bool | int | uint | float | angle | bit | duration | qubit | - /// +----------------+-------+-----+------+-------+-------+-----+----------+-------+ - /// | bit | Yes | Yes | Yes | No | Yes | - | No | No | - /// +----------------+-------+-----+------+-------+-------+-----+----------+-------+ - fn cast_bit_expr_to_type( - &mut self, - ty: &Type, - rhs: &QasmTypedExpr, - node: &SyntaxNode, - ) -> Option { - assert!(matches!(rhs.ty, Type::Bit(..))); - // There is no operand, choosing the span of the node - // but we could use the expr span as well. - let operand_span = span_for_syntax_node(node); - let name_span = rhs.expr.span; - match ty { - &Type::Angle(..) => { - let msg = "Cast bit to angle"; - self.push_unimplemented_error_message(msg, node); - None - } - &Type::Bool(..) => { - self.runtime |= RuntimeFunctions::ResultAsBool; - Some(QasmTypedExpr { - ty: ty.clone(), - expr: build_cast_call( - RuntimeFunctions::ResultAsBool, - rhs.expr.clone(), - name_span, - operand_span, - ), - }) - } - &Type::Float(..) => { - // The spec says that this cast isn't supported, but it - // casts to other types that case to float. For now, we'll - // say it is invalid like the spec. - None - } - &Type::Int(w, _) | &Type::UInt(w, _) => { - let function = if let Some(width) = w { - if width > 64 { - RuntimeFunctions::ResultAsBigInt - } else { - RuntimeFunctions::ResultAsInt - } - } else { - RuntimeFunctions::ResultAsInt - }; - self.runtime |= function; - let expr = build_cast_call(function, rhs.expr.clone(), name_span, operand_span); - Some(QasmTypedExpr { - ty: ty.clone(), - expr, - }) - } - - _ => None, - } - } - - /// +----------------+-------------------------------------------------------------+ - /// | Allowed casts | Casting To | - /// +----------------+-------+-----+------+-------+-------+-----+----------+-------+ - /// | Casting From | bool | int | uint | float | angle | bit | duration | qubit | - /// +----------------+-------+-----+------+-------+-------+-----+----------+-------+ - /// | float | Yes | Yes | Yes | - | Yes | No | No | No | - /// +----------------+-------+-----+------+-------+-------+-----+----------+-------+ - /// - /// Additional cast to complex - fn cast_float_expr_to_type( - &mut self, - ty: &Type, - rhs: &QasmTypedExpr, - node: &SyntaxNode, - ) -> Option { - assert!(matches!(rhs.ty, Type::Float(..))); - match ty { - &Type::Complex(..) => { - let expr = build_complex_from_expr(rhs.expr.clone()); - Some(QasmTypedExpr { - ty: ty.clone(), - expr, - }) - } - &Type::Angle(..) => { - let msg = "Cast float to angle"; - self.push_unimplemented_error_message(msg, node); - None - } - &Type::Int(w, _) | &Type::UInt(w, _) => { - let span = span_for_syntax_node(node); - let expr = ast_builder::build_math_call_from_exprs( - "Truncate", - vec![rhs.expr.clone()], - span, - ); - let expr = if let Some(w) = w { - if w > 64 { - build_convert_call_expr(expr, "IntAsBigInt") - } else { - expr - } - } else { - expr - }; - - Some(QasmTypedExpr { - ty: ty.clone(), - expr, - }) - } - &Type::Bool(..) => { - let span = span_for_syntax_node(node); - let expr = ast_builder::build_math_call_from_exprs( - "Truncate", - vec![rhs.expr.clone()], - span, - ); - let const_int_zero_expr = build_lit_int_expr(0, rhs.expr.span); - let qsop = ast::BinOp::Eq; - let cond = ast_builder::build_binary_expr( - false, - qsop, - expr, - const_int_zero_expr, - rhs.expr.span, - ); - let coerce_expr = build_if_expr_then_expr_else_expr( - cond, - build_lit_bool_expr(false, rhs.expr.span), - build_lit_bool_expr(true, rhs.expr.span), - rhs.expr.span, - ); - Some(QasmTypedExpr { - ty: ty.clone(), - expr: coerce_expr, - }) - } - _ => None, - } - } - - fn create_entry_item>( - &mut self, - name: S, - stmts: Vec, - input: Option>, - output: Option>, - whole_span: Span, - output_semantics: OutputSemantics, - ) -> (ast::Item, OperationSignature) { - let mut stmts = stmts; - let is_qiskit = matches!(output_semantics, OutputSemantics::Qiskit); - let mut signature = OperationSignature { - input: vec![], - output: String::new(), - name: name.as_ref().to_string(), - ns: None, - }; - let output_ty = if matches!(output_semantics, OutputSemantics::ResourceEstimation) { - // we have no output, but need to set the entry point return type - crate::types::Type::Tuple(vec![]) - } else if let Some(output) = output { - let output_exprs = if is_qiskit { - output - .iter() - .rev() - .filter(|symbol| matches!(symbol.ty, Type::BitArray(..))) - .map(|symbol| { - let ident = - build_path_ident_expr(symbol.name.as_str(), symbol.span, symbol.span); - - build_array_reverse_expr(ident) - }) - .collect::>() - } else { - output - .iter() - .map(|symbol| { - build_path_ident_expr(symbol.name.as_str(), symbol.span, symbol.span) - }) - .collect::>() - }; - // this is the output whether it is inferred or explicitly defined - // map the output symbols into a return statement, add it to the nodes list, - // and get the entry point return type - let output_types = if is_qiskit { - output - .iter() - .rev() - .filter(|symbol| matches!(symbol.ty, Type::BitArray(..))) - .map(|symbol| symbol.qsharp_ty.clone()) - .collect::>() - } else { - output - .iter() - .map(|symbol| symbol.qsharp_ty.clone()) - .collect::>() - }; - - let (output_ty, output_expr) = if output_types.len() == 1 { - (output_types[0].clone(), output_exprs[0].clone()) - } else { - let output_ty = crate::types::Type::Tuple(output_types); - let output_expr = build_tuple_expr(output_exprs); - (output_ty, output_expr) - }; - - let return_stmt = build_implicit_return_stmt(output_expr); - stmts.push(return_stmt); - output_ty - } else { - if is_qiskit { - let kind = SemanticErrorKind::QiskitEntryPointMissingOutput(whole_span); - self.push_semantic_error(kind); - } - crate::types::Type::Tuple(vec![]) - }; - - let ast_ty = map_qsharp_type_to_ast_ty(&output_ty); - signature.output = format!("{output_ty}"); - // TODO: This can create a collision on multiple compiles when interactive - // We also have issues with the new entry point inference logic - let input_desc = input - .iter() - .flat_map(|s| { - s.iter() - .map(|s| (s.name.to_string(), format!("{}", s.qsharp_ty))) - }) - .collect::>(); - signature.input = input_desc; - let input_pats = input - .into_iter() - .flat_map(|s| { - s.into_iter() - .map(|s| build_arg_pat(s.name, s.span, map_qsharp_type_to_ast_ty(&s.qsharp_ty))) - }) - .collect::>(); - - ( - build_operation_with_stmts(name, input_pats, ast_ty, stmts, whole_span, true), - signature, - ) - } - - /// +----------------+-------------------------------------------------------------+ - /// | Allowed casts | Casting To | - /// +----------------+-------+-----+------+-------+-------+-----+----------+-------+ - /// | Casting From | bool | int | uint | float | angle | bit | duration | qubit | - /// +----------------+-------+-----+------+-------+-------+-----+----------+-------+ - /// | bool | - | Yes | Yes | Yes | No | Yes | No | No | - /// +----------------+-------+-----+------+-------+-------+-----+----------+-------+ - fn cast_bool_expr_to_type(&mut self, ty: &Type, rhs: &QasmTypedExpr) -> Option { - assert!(matches!(rhs.ty, Type::Bool(..))); - let name_span = rhs.expr.span; - let operand_span = rhs.expr.span; - match ty { - &Type::Bit(..) => { - self.runtime |= RuntimeFunctions::BoolAsResult; - let expr = build_cast_call( - RuntimeFunctions::BoolAsResult, - rhs.expr.clone(), - name_span, - operand_span, - ); - Some(QasmTypedExpr { - ty: ty.clone(), - expr, - }) - } - &Type::Float(..) => { - self.runtime |= RuntimeFunctions::BoolAsDouble; - let expr = build_cast_call( - RuntimeFunctions::BoolAsDouble, - rhs.expr.clone(), - name_span, - operand_span, - ); - Some(QasmTypedExpr { - ty: ty.clone(), - expr, - }) - } - &Type::Int(w, _) | &Type::UInt(w, _) => { - let function = if let Some(width) = w { - if width > 64 { - RuntimeFunctions::BoolAsBigInt - } else { - RuntimeFunctions::BoolAsInt - } - } else { - RuntimeFunctions::BoolAsInt - }; - self.runtime |= function; - let expr = build_cast_call(function, rhs.expr.clone(), name_span, operand_span); - Some(QasmTypedExpr { - ty: ty.clone(), - expr, - }) - } - _ => None, - } - } - - /// +----------------+-------------------------------------------------------------+ - /// | Allowed casts | Casting To | - /// +----------------+-------+-----+------+-------+-------+-----+----------+-------+ - /// | Casting From | bool | int | uint | float | angle | bit | duration | qubit | - /// +----------------+-------+-----+------+-------+-------+-----+----------+-------+ - /// | int | Yes | - | Yes | Yes | No | Yes | No | No | - /// +----------------+-------+-----+------+-------+-------+-----+----------+-------+ - /// | uint | Yes | Yes | - | Yes | No | Yes | No | No | - /// +----------------+-------+-----+------+-------+-------+-----+----------+-------+ - /// - /// Additional cast to ``BigInt`` - #[allow(clippy::too_many_lines)] - fn cast_int_expr_to_type(&mut self, ty: &Type, rhs: &QasmTypedExpr) -> Option { - assert!(matches!(rhs.ty, Type::Int(..) | Type::UInt(..))); - let name_span = rhs.expr.span; - let operand_span = rhs.expr.span; - match ty { - Type::BitArray(dims, _) => { - self.runtime |= RuntimeFunctions::IntAsResultArrayBE; - let size = match dims { - ArrayDims::D1(size) => i64::try_from(*size).ok()?, - _ => 0, - }; - - let size_expr = build_lit_int_expr(size, Span::default()); - let expr = build_global_call_with_two_params( - "__IntAsResultArrayBE__", - rhs.expr.clone(), - size_expr, - name_span, - operand_span, - ); - Some(QasmTypedExpr { - ty: ty.clone(), - expr, - }) - } - Type::Float(..) => { - let expr = build_convert_call_expr(rhs.expr.clone(), "IntAsDouble"); - Some(QasmTypedExpr { - ty: ty.clone(), - expr, - }) - } - Type::Int(tw, _) | Type::UInt(tw, _) => { - // uint to int, or int/uint to BigInt - if let Some(tw) = tw { - if *tw > 64 { - let expr = build_convert_call_expr(rhs.expr.clone(), "IntAsBigInt"); - Some(QasmTypedExpr { - ty: ty.clone(), - expr, - }) - } else { - Some(QasmTypedExpr { - ty: ty.clone(), - expr: rhs.expr.clone(), - }) - } - } else { - Some(QasmTypedExpr { - ty: ty.clone(), - expr: rhs.expr.clone(), - }) - } - } - Type::Bool(..) => { - let const_int_zero_expr = build_lit_int_expr(0, rhs.expr.span); - let qsop = ast::BinOp::Eq; - let cond = ast_builder::build_binary_expr( - false, - qsop, - rhs.expr.clone(), - const_int_zero_expr, - rhs.expr.span, - ); - let coerce_expr = build_if_expr_then_expr_else_expr( - cond, - build_lit_bool_expr(false, rhs.expr.span), - build_lit_bool_expr(true, rhs.expr.span), - rhs.expr.span, - ); - Some(QasmTypedExpr { - ty: ty.clone(), - expr: coerce_expr, - }) - } - Type::Bit(..) => { - let const_int_zero_expr = build_lit_int_expr(0, rhs.expr.span); - let qsop = ast::BinOp::Eq; - let cond = ast_builder::build_binary_expr( - false, - qsop, - rhs.expr.clone(), - const_int_zero_expr, - rhs.expr.span, - ); - let coerce_expr = build_if_expr_then_expr_else_expr( - cond, - build_lit_result_expr(ast::Result::One, rhs.expr.span), - build_lit_result_expr(ast::Result::Zero, rhs.expr.span), - rhs.expr.span, - ); - Some(QasmTypedExpr { - ty: ty.clone(), - expr: coerce_expr, - }) - } - Type::Complex(..) => { - let expr = build_convert_call_expr(rhs.expr.clone(), "IntAsDouble"); - let expr = build_complex_from_expr(expr); - Some(QasmTypedExpr { - ty: ty.clone(), - expr, - }) - } - _ => None, - } - } - - fn cast_bitarray_expr_to_type( - &mut self, - dims: &ArrayDims, - ty: &Type, - rhs: &QasmTypedExpr, - ) -> Option { - let ArrayDims::D1(array_width) = dims else { - return None; - }; - if !matches!(ty, Type::Int(..) | Type::UInt(..)) { - return None; - } - // we know we have a bit array being cast to an int/uint - // verfiy widths - let int_width = ty.width(); - - if int_width.is_none() || (int_width == Some(u32::try_from(*array_width).ok()?)) { - let name_span = rhs.expr.span; - let operand_span = rhs.expr.span; - self.runtime |= RuntimeFunctions::ResultArrayAsIntBE; - let expr = build_cast_call( - RuntimeFunctions::ResultArrayAsIntBE, - rhs.expr.clone(), - name_span, - operand_span, - ); - Some(QasmTypedExpr { - ty: ty.clone(), - expr, - }) - } else { - None - } - } - - /// Pushes an unimplemented error with the supplied message. - pub fn push_unimplemented_error_message>( - &mut self, - message: S, - node: &SyntaxNode, - ) { - let span = span_for_syntax_node(node); - let kind = crate::ErrorKind::Semantic(crate::semantic::Error( - SemanticErrorKind::Unimplemented(message.as_ref().to_string(), span), - )); - let error = self.create_err(kind); - self.errors.push(error); - } - - /// Pushes a missing symbol error with the given name - /// This is a convenience method for pushing a `SemanticErrorKind::UndefinedSymbol` error. - pub fn push_missing_symbol_error>(&mut self, name: S, node: &SyntaxNode) { - let span = span_for_syntax_node(node); - let kind = SemanticErrorKind::UndefinedSymbol(name.as_ref().to_string(), span); - let kind = crate::ErrorKind::Semantic(crate::semantic::Error(kind)); - let error = self.create_err(kind); - self.errors.push(error); - } - - /// Pushes a redefined symbol error with the given name and span. - /// This is a convenience method for pushing a `SemanticErrorKind::RedefinedSymbol` error. - pub fn push_redefined_symbol_error>(&mut self, name: S, span: Span) { - let kind = SemanticErrorKind::RedefinedSymbol(name.as_ref().to_string(), span); - self.push_semantic_error(kind); - } - - /// Pushes a semantic error with the given kind. - pub fn push_semantic_error(&mut self, kind: SemanticErrorKind) { - let kind = crate::ErrorKind::Semantic(crate::semantic::Error(kind)); - let error = self.create_err(kind); - self.errors.push(error); - } - - /// Pushes an unsupported error with the supplied message. - pub fn push_unsupported_error_message>(&mut self, message: S, node: &SyntaxNode) { - let span = span_for_syntax_node(node); - let kind = crate::ErrorKind::Semantic(crate::semantic::Error( - SemanticErrorKind::NotSupported(message.as_ref().to_string(), span), - )); - let error = self.create_err(kind); - self.errors.push(error); - } - - /// Pushes an error for a gate not being supported. - pub fn push_calibration_error(&mut self, node: &SyntaxNode) { - let span = span_for_syntax_node(node); - let text = node.text().to_string(); - let kind = crate::ErrorKind::Semantic(crate::semantic::Error( - SemanticErrorKind::CalibrationsNotSupported(text, span), - )); - let error = self.create_err(kind); - self.errors.push(error); - } - - /// Creates an error from the given kind with the current source mapping. - fn create_err(&self, kind: crate::ErrorKind) -> WithSource { - let error = crate::Error(kind); - let path = self.file_stack.last().map_or("", |p| { - p.to_str().expect("expected source mapping to exist.") - }); - let source = self.source_map.find_by_name(path); - let offset = source.map_or(0, |x| x.offset); - let offset_error = error.with_offset(offset); - WithSource::from_map(&self.source_map, offset_error) - } -} - -fn compile_end_stmt(end: &oq3_syntax::ast::EndStmt) -> ast::Stmt { - ast_builder::build_end_stmt(span_for_syntax_node(end.syntax())) -} - -/// This is missing bitwise negation, but it is impossible to test -/// as the parser doesn't support it. -fn binop_requires_bitwise_conversion(op: BinaryOp, left_type: &Type) -> bool { - match op { - BinaryOp::ArithOp(arith) => match arith { - ArithOp::BitAnd | ArithOp::BitOr | ArithOp::BitXor => matches!( - left_type, - Type::Bit(..) - | Type::UInt(..) - | Type::Angle(..) - | Type::BitArray(ArrayDims::D1(_), _) - ), - ArithOp::Shl | ArithOp::Shr => matches!( - left_type, - Type::Bit(..) - | Type::UInt(..) - | Type::Angle(..) - | Type::BitArray(ArrayDims::D1(_), _) - ), - _ => false, - }, - _ => false, - } -} - -fn binop_requires_bitwise_symmetric_conversion(op: BinaryOp) -> bool { - match op { - BinaryOp::ArithOp(arith) => { - matches!(arith, ArithOp::BitAnd | ArithOp::BitOr | ArithOp::BitXor) - } - _ => false, - } -} - -fn calculate_num_ctrls(modifiers: &[&GateModifier]) -> u64 { - let num_ctrls: u64 = modifiers - .iter() - .map(|m| match m { - GateModifier::Inv(_) | GateModifier::Pow(_, _) => 0, - GateModifier::Ctrl(ctls, _) | GateModifier::NegCtrl(ctls, _) => { - TryInto::::try_into(ctls.unwrap_or(1)) - .ok() - .unwrap_or(0) - } - }) - .sum(); - num_ctrls -} - -fn get_implicit_modifiers>( - gate_name: S, - name_span: Span, -) -> (String, Vec) { - // ch, crx, cry, crz, sdg, and tdg - match gate_name.as_ref() { - "ch" => ("H".to_string(), vec![GateModifier::Ctrl(None, name_span)]), - "crx" => ("Rx".to_string(), vec![GateModifier::Ctrl(None, name_span)]), - "cry" => ("Ry".to_string(), vec![GateModifier::Ctrl(None, name_span)]), - "crz" => ("Rz".to_string(), vec![GateModifier::Ctrl(None, name_span)]), - "sdg" => ("S".to_string(), vec![GateModifier::Inv(name_span)]), - "tdg" => ("T".to_string(), vec![GateModifier::Inv(name_span)]), - _ => (gate_name.as_ref().to_string(), vec![]), - } -} - -/// Bit arrays can be compared, but need to be converted to int first -fn binop_requires_int_conversion_for_type(op: BinaryOp, ty_1: &Type, ty_2: &Type) -> bool { - match op { - BinaryOp::CmpOp(_) => match (ty_1, ty_2) { - (Type::BitArray(ArrayDims::D1(d1), _), Type::BitArray(ArrayDims::D1(d2), _)) => { - d1 == d2 - } - _ => false, - }, - _ => false, - } -} - -fn binop_requires_bool_conversion_for_type(op: BinaryOp) -> bool { - matches!(op, BinaryOp::LogicOp(..)) -} - -fn compile_intnumber_as_bit( - value: &oq3_syntax::ast::IntNumber, - span: Span, - ty: &Type, -) -> Option { - let value = value.value().expect("IntNumber must have a value"); - if value == 0 || value == 1 { - let expr = build_lit_result_expr((value == 1).into(), span); - Some(QasmTypedExpr { - ty: ty.clone(), - expr, - }) - } else { - None - } -} - -fn compile_floatnumber_as_negated_double( - value: &oq3_syntax::ast::FloatNumber, - span: Span, -) -> QasmTypedExpr { - let expr = build_lit_double_expr(-value.value().expect("FloatNumber must have a value"), span); - let ty = Type::Float(None, IsConst::True); - QasmTypedExpr { ty, expr } -} - -fn compile_intnumber_as_negated_int( - value: &oq3_syntax::ast::IntNumber, - span: Span, -) -> QasmTypedExpr { - let value = value.value().expect("IntNumber must have a value"); - if let Ok(value) = value.try_into() { - let value: i64 = value; - let expr = build_lit_int_expr(-value, span); - let ty = Type::Int(None, IsConst::True); - QasmTypedExpr { ty, expr } - } else { - let expr = build_lit_bigint_expr(BigInt::from(-1) * BigInt::from(value), span); - let ty = Type::Int(Some(128), IsConst::True); - QasmTypedExpr { ty, expr } - } -} - -/// +----------------+-------------------------------------------------------------+ -/// | Allowed casts | Casting To | -/// +----------------+-------+-----+------+-------+-------+-----+----------+-------+ -/// | Casting From | bool | int | uint | float | angle | bit | duration | qubit | -/// +----------------+-------+-----+------+-------+-------+-----+----------+-------+ -/// | complex | ?? | ?? | ?? | ?? | No | ?? | No | No | -/// +----------------+-------+-----+------+-------+-------+-----+----------+-------+ -fn cast_complex_expr_to_type(ty: &Type, rhs: &QasmTypedExpr) -> Option { - assert!(matches!(rhs.ty, Type::Complex(..))); - - if matches!((ty, &rhs.ty), (Type::Complex(..), Type::Complex(..))) { - // we are both complex, but our widths are different. If both - // had implicit widths, we would have already matched for the - // (None, None). If the rhs width is bigger, we will return - // None to let the cast fail - - // Here, we can safely cast the rhs to the lhs type if the - // lhs width can hold the rhs's width - if ty.width().is_none() && rhs.ty.width().is_some() { - return Some(QasmTypedExpr { - ty: ty.clone(), - expr: rhs.expr.clone(), - }); - } - if ty.width() >= rhs.ty.width() { - return Some(QasmTypedExpr { - ty: ty.clone(), - expr: rhs.expr.clone(), - }); - } - } - None -} - -fn try_promote_with_casting(left_type: &Type, right_type: &Type) -> Type { - let promoted_type = promote_types(left_type, right_type); - - if promoted_type != Type::Void { - return promoted_type; - } - if let Some(value) = try_promote_bitarray_to_int(left_type, right_type) { - return value; - } - // simple promotion failed, try a lossless cast - // each side to double - let promoted_rhs = promote_types(&Type::Float(None, IsConst::False), right_type); - let promoted_lhs = promote_types(left_type, &Type::Float(None, IsConst::False)); - - match (promoted_lhs, promoted_rhs) { - (Type::Void, Type::Void) => Type::Float(None, IsConst::False), - (Type::Void, promoted_rhs) => promoted_rhs, - (promoted_lhs, Type::Void) => promoted_lhs, - (promoted_lhs, promoted_rhs) => { - // return the greater of the two promoted types - if matches!(promoted_lhs, Type::Complex(..)) { - promoted_lhs - } else if matches!(promoted_rhs, Type::Complex(..)) { - promoted_rhs - } else if matches!(promoted_lhs, Type::Float(..)) { - promoted_lhs - } else if matches!(promoted_rhs, Type::Float(..)) { - promoted_rhs - } else { - Type::Float(None, IsConst::False) - } - } - } -} - -fn try_promote_bitarray_to_int(left_type: &Type, right_type: &Type) -> Option { - if matches!( - (left_type, right_type), - (Type::Int(..) | Type::UInt(..), Type::BitArray(..)) - ) { - let ty = left_type; - let r = right_type.dims().expect("")[0]; - - if ty.dims().is_none() || (ty.num_dims() == 1 && ty.dims().expect("")[0] == r) { - return Some(left_type.clone()); - } - } - if matches!( - (left_type, right_type), - (Type::BitArray(..), Type::Int(..) | Type::UInt(..)) - ) { - let ty = right_type; - let r = left_type.dims().expect("")[0]; - - if ty.dims().is_none() || (ty.num_dims() == 1 && ty.dims().expect("")[0] == r) { - return Some(right_type.clone()); - } - } - None -} - -fn compile_bitstring(bitstring: &BitString, span: Span) -> Option { - let width = bitstring - .to_string() - .chars() - .filter(|c| *c == '0' || *c == '1') - .count(); - let expr = bitstring - .value() - .map(|value| build_lit_result_array_expr_from_bitstring(value, span))?; - let ty = Type::BitArray(ArrayDims::D1(width), IsConst::True); - Some(QasmTypedExpr { ty, expr }) -} - -pub fn can_cast_literal(lhs_ty: &Type, ty_lit: &Type) -> bool { - if matches!(lhs_ty, Type::Int(..)) && matches!(ty_lit, Type::UInt(..)) { - return true; - } - if matches!(lhs_ty, Type::UInt(..)) { - return matches!(ty_lit, Type::Complex(..)); - } - oq3_semantics::types::can_cast_literal(lhs_ty, ty_lit) - || { - matches!(lhs_ty, Type::Bit(..) | Type::Bool(..)) - && matches!(ty_lit, Type::Bit(..) | Type::Bool(..)) - } - || { - match lhs_ty { - Type::BitArray(dims, _) => { - matches!(dims, ArrayDims::D1(_)) - && matches!(ty_lit, Type::Int(..) | Type::UInt(..)) - } - _ => false, - } - } -} - -fn extract_pow_exponent(expr: &oq3_syntax::ast::ParenExpr, span: Span) -> GateModifier { - let lit = compile_paren_lit_int_expr(expr); - if let Some((exponent, sign)) = lit { - let exponent = i64::try_from(exponent).ok(); - let Some(exponent) = exponent else { - return GateModifier::Pow(None, span); - }; - if sign { - GateModifier::Pow(Some(-exponent), span) - } else { - GateModifier::Pow(Some(exponent), span) - } - } else { - GateModifier::Pow(None, span) - } -} - -fn compile_paren_lit_int_expr(paren_expr: &oq3_syntax::ast::ParenExpr) -> Option<(usize, bool)> { - let expr = paren_expr.expr()?; - match expr { - Expr::Literal(lit) => match lit.kind() { - LiteralKind::IntNumber(value) => { - let size: usize = usize::try_from(value.value()?).ok()?; - Some((size, false)) - } - _ => None, - }, - Expr::PrefixExpr(prefix) => match prefix.op_kind() { - Some(UnaryOp::Neg) => { - let expr = prefix.expr()?; - match expr { - Expr::Literal(lit) => match lit.kind() { - LiteralKind::IntNumber(value) => { - let size: usize = usize::try_from(value.value()?).ok()?; - Some((size, true)) - } - _ => None, - }, - _ => None, - } - } - _ => None, - }, - _ => None, - } -} diff --git a/compiler/qsc_qasm3/src/compiler.rs b/compiler/qsc_qasm3/src/compiler.rs index 0ef44dba90..b4bd31e16a 100644 --- a/compiler/qsc_qasm3/src/compiler.rs +++ b/compiler/qsc_qasm3/src/compiler.rs @@ -31,6 +31,7 @@ use crate::{ build_while_stmt, build_wrapped_block_expr, managed_qubit_alloc_array, map_qsharp_type_to_ast_ty, wrap_expr_in_parens, }, + io::SourceResolver, parser::ast::{list_from_iter, List}, semantic::{ ast::{ @@ -58,12 +59,22 @@ fn err_expr(span: Span) -> qsast::Expr { } } -pub fn compile_with_config(source: S, path: P, config: CompilerConfig) -> QasmCompileUnit +pub fn compile_to_qsharp_ast_with_config( + source: S, + path: P, + resolver: Option<&mut R>, + config: CompilerConfig, +) -> QasmCompileUnit where S: AsRef, P: AsRef, + R: SourceResolver, { - let res = crate::semantic::parse(source, path); + let res = if let Some(resolver) = resolver { + crate::semantic::parse_source(source, path, resolver) + } else { + crate::semantic::parse(source, path) + }; let program = res.program; let compiler = crate::compiler::QasmCompiler { @@ -749,7 +760,9 @@ impl QasmCompiler { let symbol = self.symbols[expr.symbol_id].clone(); let name = &symbol.name; let name_span = symbol.span; - if expr.args.len() > 0 { + if expr.args.is_empty() { + build_call_no_params(name, &[], expr.span) + } else { let args: Vec<_> = expr .args .iter() @@ -763,8 +776,6 @@ impl QasmCompiler { } else { build_call_with_params(name, &[], args, name_span, expr.span) } - } else { - build_call_no_params(name, &[], expr.span) } } @@ -905,6 +916,9 @@ impl QasmCompiler { ) -> Option { let symbol = self.symbols[stmt.symbol_id].clone(); let name = symbol.name.clone(); + // if the gate has the name of a qasm or qiskit built-in gate + // it means that the stdgates libraries are not being used. + // we let the user compile their own gates with the same name. let cargs: Vec<_> = stmt .params diff --git a/compiler/qsc_qasm3/src/convert.rs b/compiler/qsc_qasm3/src/convert.rs new file mode 100644 index 0000000000..b7ed2466d8 --- /dev/null +++ b/compiler/qsc_qasm3/src/convert.rs @@ -0,0 +1,24 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +/// `i64` is 64 bits wide, but `f64`'s mantissa is only 53 bits wide +pub(crate) fn safe_i64_to_f64(value: i64) -> Option { + const MAX_EXACT_INT: i64 = 2i64.pow(f64::MANTISSA_DIGITS); + const MAX_EXACT_NEG_INT: i64 = -(2i64.pow(f64::MANTISSA_DIGITS)); + if (MAX_EXACT_NEG_INT..=MAX_EXACT_INT).contains(&value) { + #[allow(clippy::cast_precision_loss)] + Some(value as f64) + } else { + None + } +} + +pub(crate) fn safe_u64_to_f64(value: u64) -> Option { + const MAX_EXACT_UINT: u64 = 2u64.pow(f64::MANTISSA_DIGITS); + if value <= MAX_EXACT_UINT { + #[allow(clippy::cast_precision_loss)] + Some(value as f64) + } else { + None + } +} diff --git a/compiler/qsc_qasm3/src/lib.rs b/compiler/qsc_qasm3/src/lib.rs index a6e3699f95..a4aa09c757 100644 --- a/compiler/qsc_qasm3/src/lib.rs +++ b/compiler/qsc_qasm3/src/lib.rs @@ -5,22 +5,17 @@ #![allow(dead_code)] mod ast_builder; -mod compile; mod compiler; mod stdlib; -pub use compile::qasm_to_program; -pub use compiler::compile_with_config; +pub use compiler::compile_to_qsharp_ast_with_config; pub use stdlib::package_store_with_qasm; +mod convert; pub mod io; mod keyword; mod lex; -mod oqasm_helpers; -mod oqasm_types; -pub mod parse; pub mod parser; mod runtime; pub mod semantic; -mod symbols; mod types; #[cfg(test)] diff --git a/compiler/qsc_qasm3/src/oqasm_helpers.rs b/compiler/qsc_qasm3/src/oqasm_helpers.rs deleted file mode 100644 index 344139d49a..0000000000 --- a/compiler/qsc_qasm3/src/oqasm_helpers.rs +++ /dev/null @@ -1,183 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. - -use oq3_semantics::types::Type; -use oq3_syntax::ast::{ArithOp, BinaryOp, Designator, Expr, Literal, LiteralKind}; -use qsc_data_structures::span::Span; - -/// Extracts a Q# ```Span``` from the QASM3 syntax named element -pub(crate) fn span_for_named_item(value: &T) -> Span { - let Some(name) = value.name() else { - return Span::default(); - }; - let Some(ident) = name.ident_token() else { - return Span::default(); - }; - text_range_to_span(ident.text_range()) -} - -/// Converts the QASM3 syntax ```TextRange``` to a Q# ```Span``` -pub(crate) fn text_range_to_span(range: oq3_syntax::TextRange) -> Span { - Span { - lo: range.start().into(), - hi: range.end().into(), - } -} - -/// Extracts a Q# ```Span``` from the QASM3 syntax node -pub(crate) fn span_for_syntax_node(node: &oq3_syntax::SyntaxNode) -> Span { - text_range_to_span(node.text_range()) -} - -/// Extracts a Q# ```Span``` from the QASM3 syntax token -pub(crate) fn span_for_syntax_token(token: &oq3_syntax::SyntaxToken) -> Span { - text_range_to_span(token.text_range()) -} - -/// The QASM3 parser stores integers as u128, any conversion we do -/// must not crash if the value is too large to fit in the target type -/// and instead return None. -/// Safe in the following functions means that the conversion will not -/// panic if the value is too large to fit in the target type. -/// -/// Values may be truncated or rounded as necessary. -pub(crate) fn safe_u128_to_f64(value: u128) -> Option { - if value <= u128::from(i64::MAX as u64) { - let value = i64::try_from(value).ok()?; - safe_i64_to_f64(value) - } else { - None - } -} - -/// `i64` is 64 bits wide, but `f64`'s mantissa is only 53 bits wide -pub(crate) fn safe_i64_to_f64(value: i64) -> Option { - const MAX_EXACT_INT: i64 = 2i64.pow(f64::MANTISSA_DIGITS); - const MAX_EXACT_NEG_INT: i64 = -(2i64.pow(f64::MANTISSA_DIGITS)); - if (MAX_EXACT_NEG_INT..=MAX_EXACT_INT).contains(&value) { - #[allow(clippy::cast_precision_loss)] - Some(value as f64) - } else { - None - } -} - -pub(crate) fn safe_u64_to_f64(value: u64) -> Option { - const MAX_EXACT_UINT: u64 = 2u64.pow(f64::MANTISSA_DIGITS); - if value <= MAX_EXACT_UINT { - #[allow(clippy::cast_precision_loss)] - Some(value as f64) - } else { - None - } -} - -/// The spec defines a designator as ```designator: LBRACKET expression RBRACKET;``` -/// However, in every use case, the expression is a literal integer. -/// This isn't enforced by the parser/grammar, but we can assume it here. -/// For example, when describing qubit arrays, the spec says: -/// - The label ```name[j]``` refers to a qubit of this register, where -/// ```j element_of {0, 1, ... size(name)-1}``` is an integer. -pub(crate) fn extract_dims_from_designator(designator: Option) -> Option { - let designator = designator?; - match designator.expr() { - Some(Expr::Literal(lit)) => match lit.kind() { - LiteralKind::IntNumber(int) => { - // qasm parser stores ints as u128 - let value = int.value().expect("Designator must be a literal integer"); - let value: u32 = u32::try_from(value).expect("Designator must fit in u32"); - Some(value) - } - _ => { - unreachable!("designator must be a literal integer") - } - }, - None => None, - _ => { - unreachable!("designator must be a literal integer") - } - } -} - -/// The designator must be accessed differently depending on the type. -/// For complex types, the designator is stored in the scalar type. -/// For other types, the designator is stored in the type itself. -pub(crate) fn get_designator_from_scalar_type( - ty: &oq3_syntax::ast::ScalarType, -) -> Option { - if let Some(scalar_ty) = ty.scalar_type() { - // we have a complex type, need to grab the inner - // designator for the width - scalar_ty.designator() - } else { - ty.designator() - } -} - -/// Symmetric arithmetic conversions are applied to: -/// binary arithmetic *, /, %, +, - -/// relational operators <, >, <=, >=, ==, != -/// binary bitwise arithmetic &, ^, |, -pub(crate) fn requires_symmetric_conversion(op: BinaryOp) -> bool { - match op { - BinaryOp::LogicOp(_) | BinaryOp::CmpOp(_) => true, - BinaryOp::ArithOp(arith_op) => match arith_op { - ArithOp::Mul - | ArithOp::Div - | ArithOp::Rem - | ArithOp::Add - | ArithOp::Sub - | ArithOp::BitAnd - | ArithOp::BitXor - | ArithOp::BitOr => true, - ArithOp::Shl | ArithOp::Shr => false, - }, - #[allow(clippy::match_same_arms)] - BinaryOp::ConcatenationOp => { - // concatenation is a special case where we can't have a symmetric conversion - // as the lhs and rhs must be of the same type - false - } - BinaryOp::Assignment { op: _ } => false, - } -} - -pub(crate) fn requires_types_already_match_conversion(op: BinaryOp) -> bool { - match op { - BinaryOp::ConcatenationOp => { - // concatenation is a special case where we can't have a symmetric conversion - // as the lhs and rhs must be of the same type - true - } - _ => false, - } -} - -// integer promotions are applied only to both operands of -// the shift operators << and >> -pub(crate) fn binop_requires_symmetric_int_conversion(op: BinaryOp) -> bool { - match op { - BinaryOp::ArithOp(arith_op) => matches!(arith_op, ArithOp::Shl | ArithOp::Shr), - BinaryOp::Assignment { op } => matches!(op, Some(ArithOp::Shl | ArithOp::Shr)), - _ => false, - } -} - -/// some literals can be cast to a specific type if the value is known -/// This is useful to avoid generating a cast expression in the AST -pub(crate) fn can_cast_literal_with_value_knowledge(lhs_ty: &Type, literal: &Literal) -> bool { - if matches!(lhs_ty, &Type::Bit(..)) { - if let LiteralKind::IntNumber(value) = literal.kind() { - let value = value.value().expect("IntNumber must have a value"); - return value == 0 || value == 1; - } - } - if matches!(lhs_ty, &Type::UInt(..)) { - if let LiteralKind::IntNumber(_) = literal.kind() { - // Value can't be negative as IntNumber is unsigned - // any sign would come from a prefix expression - return true; - } - } - false -} diff --git a/compiler/qsc_qasm3/src/oqasm_types.rs b/compiler/qsc_qasm3/src/oqasm_types.rs deleted file mode 100644 index a2475b246d..0000000000 --- a/compiler/qsc_qasm3/src/oqasm_types.rs +++ /dev/null @@ -1,184 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. - -use std::cmp::max; - -use oq3_semantics::types::{ArrayDims, IsConst, Type}; - -/// When two types are combined, the result is a type that can represent both. -/// For constness, the result is const iff both types are const. -fn relax_constness(lhs_ty: &Type, rhs_ty: &Type) -> IsConst { - IsConst::from(lhs_ty.is_const() && rhs_ty.is_const()) -} - -/// Having no width means that the type is not a fixed-width type -/// and can hold any explicit width. If both types have a width, -/// the result is the maximum of the two. Otherwise, the result -/// is a type without a width. -fn promote_width(lhs_ty: &Type, rhs_ty: &Type) -> Option { - match (lhs_ty.width(), rhs_ty.width()) { - (Some(w1), Some(w2)) => Some(max(w1, w2)), - (Some(_) | None, None) | (None, Some(_)) => None, - } -} - -fn get_effective_width(lhs_ty: &Type, rhs_ty: &Type) -> Option { - match (lhs_ty.width(), rhs_ty.width()) { - (Some(w1), Some(w2)) => Some(max(w1, w2)), - (Some(w), None) | (None, Some(w)) => Some(w), - (None, None) => None, - } -} - -/// If both can be promoted to a common type, the result is that type. -/// If the types are not compatible, the result is `Type::Void`. -pub fn promote_types(lhs_ty: &Type, rhs_ty: &Type) -> Type { - if types_equal_except_const(lhs_ty, rhs_ty) { - return lhs_ty.clone(); - } - let ty = promote_types_symmetric(lhs_ty, rhs_ty); - if ty != Type::Void { - return ty; - } - let ty = promote_types_asymmetric(lhs_ty, rhs_ty); - if ty == Type::Void { - return promote_types_asymmetric(rhs_ty, lhs_ty); - } - ty -} - -pub(crate) fn promote_to_uint_ty( - lhs_ty: &Type, - rhs_ty: &Type, -) -> (Option, Option, Option) { - let is_const = relax_constness(lhs_ty, rhs_ty); - let lhs_ty = get_uint_ty(lhs_ty); - let rhs_ty = get_uint_ty(rhs_ty); - match (lhs_ty, rhs_ty) { - (Some(lhs_ty), Some(rhs_ty)) => { - let width = get_effective_width(&lhs_ty, &rhs_ty); - ( - Some(Type::UInt(width, is_const)), - Some(lhs_ty), - Some(rhs_ty), - ) - } - (Some(lhs_ty), None) => (None, Some(lhs_ty), None), - (None, Some(rhs_ty)) => (None, None, Some(rhs_ty)), - (None, None) => (None, None, None), - } -} - -fn get_uint_ty(ty: &Type) -> Option { - if matches!(ty, Type::UInt(..) | Type::Angle(..)) { - Some(Type::UInt(ty.width(), ty.is_const().into())) - } else if let Type::BitArray(dims, _) = ty { - match dims { - ArrayDims::D1(d) => Some(Type::UInt( - Some(u32::try_from(*d).ok()?), - ty.is_const().into(), - )), - _ => None, - } - } else { - None - } -} - -/// Promotes two types if they share a common base type with -/// their constness relaxed, and their width promoted. -/// If the types are not compatible, the result is `Type::Void`. -fn promote_types_symmetric(lhs_ty: &Type, rhs_ty: &Type) -> Type { - let is_const = relax_constness(lhs_ty, rhs_ty); - match (lhs_ty, rhs_ty) { - (Type::Bit(..), Type::Bit(..)) => Type::Bit(is_const), - (Type::Bool(..), Type::Bool(..)) => Type::Bool(is_const), - (Type::Int(..), Type::Int(..)) => Type::Int(promote_width(lhs_ty, rhs_ty), is_const), - (Type::UInt(..), Type::UInt(..)) => Type::UInt(promote_width(lhs_ty, rhs_ty), is_const), - (Type::Angle(..), Type::Angle(..)) => Type::Angle(promote_width(lhs_ty, rhs_ty), is_const), - (Type::Float(..), Type::Float(..)) => Type::Float(promote_width(lhs_ty, rhs_ty), is_const), - (Type::Complex(..), Type::Complex(..)) => { - Type::Complex(promote_width(lhs_ty, rhs_ty), is_const) - } - _ => Type::Void, - } -} - -/// Promotion follows casting rules. We only match one way, as the -/// both combinations are covered by calling this function twice -/// with the arguments swapped. -/// -/// If the types are not compatible, the result is `Type::Void`. -/// -/// The left-hand side is the type to promote from, and the right-hand -/// side is the type to promote to. So any promotion goes from lesser -/// type to greater type. -/// -/// This is more complicated as we have C99 promotion for simple types, -/// but complex types like `Complex`, and `Angle` don't follow those rules. -fn promote_types_asymmetric(lhs_ty: &Type, rhs_ty: &Type) -> Type { - let is_const = relax_constness(lhs_ty, rhs_ty); - #[allow(clippy::match_same_arms)] - match (lhs_ty, rhs_ty) { - (Type::Bit(..), Type::Bool(..)) => Type::Bool(is_const), - (Type::Bit(..), Type::Int(w, _)) => Type::Int(*w, is_const), - (Type::Bit(..), Type::UInt(w, _)) => Type::UInt(*w, is_const), - - (Type::Bit(..), Type::Angle(w, _)) => Type::Angle(*w, is_const), - - (Type::Bool(..), Type::Int(w, _)) => Type::Int(*w, is_const), - (Type::Bool(..), Type::UInt(w, _)) => Type::UInt(*w, is_const), - (Type::Bool(..), Type::Float(w, _)) => Type::Float(*w, is_const), - (Type::Bool(..), Type::Complex(w, _)) => Type::Complex(*w, is_const), - - (Type::UInt(..), Type::Int(..)) => Type::Int(promote_width(lhs_ty, rhs_ty), is_const), - (Type::UInt(..), Type::Float(..)) => Type::Float(promote_width(lhs_ty, rhs_ty), is_const), - (Type::UInt(..), Type::Complex(..)) => { - Type::Complex(promote_width(lhs_ty, rhs_ty), is_const) - } - - (Type::Int(..), Type::Float(..)) => Type::Float(promote_width(lhs_ty, rhs_ty), is_const), - (Type::Int(..), Type::Complex(..)) => { - Type::Complex(promote_width(lhs_ty, rhs_ty), is_const) - } - (Type::Angle(..), Type::Float(..)) => Type::Float(promote_width(lhs_ty, rhs_ty), is_const), - (Type::Float(..), Type::Complex(..)) => { - Type::Complex(promote_width(lhs_ty, rhs_ty), is_const) - } - _ => Type::Void, - } -} - -/// Compares two types for equality, ignoring constness. -pub(crate) fn types_equal_except_const(lhs: &Type, rhs: &Type) -> bool { - match (lhs, rhs) { - (Type::Bit(_), Type::Bit(_)) - | (Type::Qubit, Type::Qubit) - | (Type::HardwareQubit, Type::HardwareQubit) - | (Type::Bool(_), Type::Bool(_)) - | (Type::Duration(_), Type::Duration(_)) - | (Type::Stretch(_), Type::Stretch(_)) - | (Type::Range, Type::Range) - | (Type::Set, Type::Set) - | (Type::Void, Type::Void) - | (Type::ToDo, Type::ToDo) - | (Type::Undefined, Type::Undefined) => true, - (Type::Int(lhs_width, _), Type::Int(rhs_width, _)) - | (Type::UInt(lhs_width, _), Type::UInt(rhs_width, _)) - | (Type::Float(lhs_width, _), Type::Float(rhs_width, _)) - | (Type::Angle(lhs_width, _), Type::Angle(rhs_width, _)) - | (Type::Complex(lhs_width, _), Type::Complex(rhs_width, _)) => lhs_width == rhs_width, - (Type::BitArray(lhs_dims, _), Type::BitArray(rhs_dims, _)) - | (Type::QubitArray(lhs_dims), Type::QubitArray(rhs_dims)) - | (Type::IntArray(lhs_dims), Type::IntArray(rhs_dims)) - | (Type::UIntArray(lhs_dims), Type::UIntArray(rhs_dims)) - | (Type::FloatArray(lhs_dims), Type::FloatArray(rhs_dims)) - | (Type::AngleArray(lhs_dims), Type::AngleArray(rhs_dims)) - | (Type::ComplexArray(lhs_dims), Type::ComplexArray(rhs_dims)) - | (Type::BoolArray(lhs_dims), Type::BoolArray(rhs_dims)) => lhs_dims == rhs_dims, - (Type::Gate(lhs_cargs, lhs_qargs), Type::Gate(rhs_cargs, rhs_qargs)) => { - lhs_cargs == rhs_cargs && lhs_qargs == rhs_qargs - } - _ => false, - } -} diff --git a/compiler/qsc_qasm3/src/parse.rs b/compiler/qsc_qasm3/src/parse.rs deleted file mode 100644 index c8987c1872..0000000000 --- a/compiler/qsc_qasm3/src/parse.rs +++ /dev/null @@ -1,270 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. - -use crate::io::SourceResolver; -use crate::oqasm_helpers::text_range_to_span; -use oq3_syntax::SyntaxNode; -use oq3_syntax::{ast::Stmt, ParseOrErrors, SourceFile}; -use qsc_frontend::compile::SourceMap; -use qsc_frontend::error::WithSource; -use std::path::{Path, PathBuf}; -use std::sync::Arc; - -#[cfg(test)] -pub(crate) mod tests; - -pub struct QasmParseResult { - pub source: QasmSource, - pub source_map: SourceMap, -} - -impl QasmParseResult { - #[must_use] - pub fn new(source: QasmSource) -> QasmParseResult { - let source_map = create_source_map(&source); - QasmParseResult { source, source_map } - } - - #[must_use] - pub fn has_errors(&self) -> bool { - self.source.has_errors() - } - - pub fn all_errors(&self) -> Vec> { - let mut self_errors = self.errors(); - let include_errors = self - .source - .includes() - .iter() - .flat_map(QasmSource::all_errors) - .map(|e| self.map_error(e)); - - self_errors.extend(include_errors); - self_errors - } - - #[must_use] - pub fn errors(&self) -> Vec> { - self.source - .errors() - .iter() - .map(|e| self.map_error(e.clone())) - .collect() - } - - fn map_error(&self, error: crate::Error) -> WithSource { - let path = self.source.path().display().to_string(); - let source = self.source_map.find_by_name(&path); - let offset = source.map_or(0, |source| source.offset); - - let offset_error = error.with_offset(offset); - - WithSource::from_map(&self.source_map, offset_error) - } -} - -/// Parse a QASM file and return the parse result. -/// This function will resolve includes using the provided resolver. -/// If an include file cannot be resolved, an error will be returned. -/// If a file is included recursively, a stack overflow occurs. -pub fn parse_source( - source: S, - path: P, - resolver: &mut R, -) -> miette::Result -where - S: AsRef, - P: AsRef, - R: SourceResolver, -{ - let res = parse_qasm_source(source, path, resolver)?; - Ok(QasmParseResult::new(res)) -} - -/// Creates a Q# source map from a QASM parse output. The `QasmSource` -/// has all of the recursive includes resolved with their own source -/// and parse results. -fn create_source_map(source: &QasmSource) -> SourceMap { - let mut files: Vec<(Arc, Arc)> = Vec::new(); - files.push(( - Arc::from(source.path().to_string_lossy().to_string()), - Arc::from(source.source()), - )); - // Collect all source files from the includes, this - // begins the recursive process of collecting all source files. - for include in source.includes() { - collect_source_files(include, &mut files); - } - SourceMap::new(files, None) -} - -/// Recursively collect all source files from the includes -fn collect_source_files(source: &QasmSource, files: &mut Vec<(Arc, Arc)>) { - files.push(( - Arc::from(source.path().to_string_lossy().to_string()), - Arc::from(source.source()), - )); - for include in source.includes() { - collect_source_files(include, files); - } -} - -/// Represents a QASM source file that has been parsed. -#[derive(Clone, Debug)] -pub struct QasmSource { - /// The path to the source file. This is used for error reporting. - /// This path is just a name, it does not have to exist on disk. - path: PathBuf, - /// The source code of the file. - source: Arc, - /// The parsed AST of the source file or any parse errors. - ast: ParseOrErrors, - /// Any included files that were resolved. - /// Note that this is a recursive structure. - included: Vec, -} - -impl QasmSource { - pub fn new, P: AsRef>( - source: T, - file_path: P, - ast: ParseOrErrors, - included: Vec, - ) -> QasmSource { - QasmSource { - source: source.as_ref().into(), - path: file_path.as_ref().to_owned(), - ast, - included, - } - } - - #[must_use] - pub fn has_errors(&self) -> bool { - if !self.parse_result().errors().is_empty() { - return true; - } - self.includes().iter().any(QasmSource::has_errors) - } - - #[must_use] - pub fn all_errors(&self) -> Vec { - let mut self_errors = self.errors(); - let include_errors = self.includes().iter().flat_map(QasmSource::all_errors); - self_errors.extend(include_errors); - self_errors - } - - #[must_use] - pub fn tree(&self) -> oq3_syntax::SourceFile { - self.parse_result().tree() - } - - #[must_use] - pub fn syntax_node(&self) -> SyntaxNode { - self.parse_result().syntax_node() - } - - #[must_use] - pub fn includes(&self) -> &Vec { - self.included.as_ref() - } - - #[must_use] - pub fn parse_result(&self) -> &ParseOrErrors { - &self.ast - } - - #[must_use] - pub fn path(&self) -> PathBuf { - self.path.clone() - } - - #[must_use] - pub fn errors(&self) -> Vec { - self.parse_result() - .errors() - .iter() - .map(|e| { - crate::Error(crate::ErrorKind::Parse( - e.message().to_string(), - text_range_to_span(e.range()), - )) - }) - .collect() - } - - #[must_use] - pub fn source(&self) -> &str { - self.source.as_ref() - } -} - -/// Parse a QASM file and return the parse result using the provided resolver. -/// Returns `Err` if the resolver cannot resolve the file. -/// Returns `Ok` otherwise. Any parse errors will be included in the result. -/// -/// This function is the start of a recursive process that will resolve all -/// includes in the QASM file. Any includes are parsed as if their contents -/// were defined where the include statement is. -fn parse_qasm_file(path: P, resolver: &mut R) -> miette::Result -where - P: AsRef, - R: SourceResolver, -{ - let (path, source) = resolver.resolve(&path)?; - parse_qasm_source(source, path, resolver) -} - -fn parse_qasm_source(source: S, path: P, resolver: &mut R) -> miette::Result -where - S: AsRef, - P: AsRef, - R: SourceResolver, -{ - let (parse_result, includes) = parse_source_and_includes(source.as_ref(), resolver)?; - Ok(QasmSource::new(source, path, parse_result, includes)) -} - -fn parse_source_and_includes, R>( - source: P, - resolver: &mut R, -) -> miette::Result<(ParseOrErrors, Vec)> -where - R: SourceResolver, -{ - let parse_result = oq3_syntax::SourceFile::parse_check_lex(source.as_ref()); - if parse_result.have_parse() { - let included = parse_includes(&parse_result, resolver)?; - Ok((parse_result, included)) - } else { - Ok((parse_result, vec![])) - } -} - -fn parse_includes( - syntax_ast: &ParseOrErrors, - resolver: &mut R, -) -> miette::Result> -where - R: SourceResolver, -{ - let mut includes = vec![]; - for stmt in syntax_ast.tree().statements() { - if let Stmt::Include(include) = stmt { - if let Some(file) = include.file() { - if let Some(file_path) = file.to_string() { - // Skip the standard gates include file. - // Handling of this file is done by the compiler. - if file_path.to_lowercase() == "stdgates.inc" { - continue; - } - let source = parse_qasm_file(file_path, resolver)?; - includes.push(source); - } - } - } - } - - Ok(includes) -} diff --git a/compiler/qsc_qasm3/src/parser.rs b/compiler/qsc_qasm3/src/parser.rs index 3c9bd80a6f..002bb03543 100644 --- a/compiler/qsc_qasm3/src/parser.rs +++ b/compiler/qsc_qasm3/src/parser.rs @@ -15,7 +15,7 @@ use std::sync::Arc; #[cfg(test)] pub(crate) mod tests; -mod completion; +pub mod completion; mod error; pub use error::Error; mod expr; @@ -296,7 +296,9 @@ where let file_path = &include.filename; // Skip the standard gates include file. // Handling of this file is done by the compiler. - if file_path.to_lowercase() == "stdgates.inc" { + if file_path.to_lowercase() == "stdgates.inc" + || file_path.to_lowercase() == "qiskit_stdgates.inc" + { continue; } let source = parse_qasm_file(file_path, resolver); diff --git a/compiler/qsc_qasm3/src/parser/completion.rs b/compiler/qsc_qasm3/src/parser/completion.rs index ae8f8e57e9..118b24436b 100644 --- a/compiler/qsc_qasm3/src/parser/completion.rs +++ b/compiler/qsc_qasm3/src/parser/completion.rs @@ -1,13 +1,14 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. -pub(crate) mod collector; #[cfg(test)] mod tests; -pub(crate) mod word_kinds; -pub(crate) use collector::ValidWordCollector; -pub(crate) use word_kinds::WordKinds; +pub mod collector; +pub mod word_kinds; + +use collector::ValidWordCollector; +use word_kinds::WordKinds; use super::{prgm, ParserContext}; diff --git a/compiler/qsc_qasm3/src/parser/expr.rs b/compiler/qsc_qasm3/src/parser/expr.rs index ae69817719..4d29649c71 100644 --- a/compiler/qsc_qasm3/src/parser/expr.rs +++ b/compiler/qsc_qasm3/src/parser/expr.rs @@ -28,7 +28,7 @@ use super::{ IndexSetItem, IndexedIdent, List, Lit, LiteralKind, MeasureExpr, RangeDefinition, TimeUnit, TypeDef, UnaryOp, UnaryOpExpr, ValueExpr, Version, }, - completion::WordKinds, + completion::word_kinds::WordKinds, error::{Error, ErrorKind}, prim::{ident, many, opt, recovering_token, seq, shorten, token, FinalSep}, scan::ParserContext, @@ -390,10 +390,7 @@ fn lit_int(lexeme: &str, radix: u32) -> Option { fn lit_bigint(lexeme: &str, radix: u32) -> Option { // from_str_radix does removes underscores as long as the lexeme // doesn't start with an underscore. - match BigInt::from_str_radix(lexeme, radix) { - Ok(value) => Some(value), - Err(_) => None, - } + BigInt::from_str_radix(lexeme, radix).ok() } fn timing_literal(lexeme: &str, token: Token, kind: TimingLiteralKind) -> Result> { diff --git a/compiler/qsc_qasm3/src/parser/prgm.rs b/compiler/qsc_qasm3/src/parser/prgm.rs index b9ca8586cd..3cd6a5f905 100644 --- a/compiler/qsc_qasm3/src/parser/prgm.rs +++ b/compiler/qsc_qasm3/src/parser/prgm.rs @@ -7,7 +7,7 @@ use super::{ }; use crate::{ lex::{Delim, TokenKind}, - parser::{completion::WordKinds, expr}, + parser::{completion::word_kinds::WordKinds, expr}, }; use super::ast::{Program, Stmt, StmtKind, Version}; diff --git a/compiler/qsc_qasm3/src/parser/prim.rs b/compiler/qsc_qasm3/src/parser/prim.rs index 7f8d189a10..c00f8c5413 100644 --- a/compiler/qsc_qasm3/src/parser/prim.rs +++ b/compiler/qsc_qasm3/src/parser/prim.rs @@ -9,7 +9,7 @@ use super::{ scan::ParserContext, Parser, Result, }; -use crate::{lex::TokenKind, parser::completion::WordKinds}; +use crate::{lex::TokenKind, parser::completion::word_kinds::WordKinds}; use super::ast::{Ident, IncompletePath, Path, PathKind}; diff --git a/compiler/qsc_qasm3/src/parser/prim/tests.rs b/compiler/qsc_qasm3/src/parser/prim/tests.rs index 7b2814b717..055250057d 100644 --- a/compiler/qsc_qasm3/src/parser/prim/tests.rs +++ b/compiler/qsc_qasm3/src/parser/prim/tests.rs @@ -7,7 +7,7 @@ use crate::{ lex::TokenKind, parser::ast::PathKind, parser::{ - completion::WordKinds, + completion::word_kinds::WordKinds, error::{Error, ErrorKind}, scan::ParserContext, tests::{check, check_opt, check_seq}, diff --git a/compiler/qsc_qasm3/src/parser/scan.rs b/compiler/qsc_qasm3/src/parser/scan.rs index 50fbd3b45f..de008383c7 100644 --- a/compiler/qsc_qasm3/src/parser/scan.rs +++ b/compiler/qsc_qasm3/src/parser/scan.rs @@ -3,7 +3,7 @@ use crate::{ lex::{Lexer, Token, TokenKind}, - parser::completion::{collector::ValidWordCollector, WordKinds}, + parser::completion::{collector::ValidWordCollector, word_kinds::WordKinds}, }; use qsc_data_structures::span::Span; diff --git a/compiler/qsc_qasm3/src/parser/stmt.rs b/compiler/qsc_qasm3/src/parser/stmt.rs index 17eeac9c9c..21cab143c7 100644 --- a/compiler/qsc_qasm3/src/parser/stmt.rs +++ b/compiler/qsc_qasm3/src/parser/stmt.rs @@ -8,7 +8,7 @@ use qsc_data_structures::span::Span; use std::rc::Rc; use super::{ - completion::WordKinds, + completion::word_kinds::WordKinds, error::{Error, ErrorKind}, expr::{self, designator, gate_operand, indexed_identifier}, prim::{self, barrier, many, opt, recovering, recovering_semi, recovering_token, seq, shorten}, @@ -381,7 +381,7 @@ fn parse_include(s: &mut ParserContext) -> Result { filename: v.to_string(), })); } - }; + } Err(Error::new(ErrorKind::Rule( "string literal", next.kind, @@ -1642,7 +1642,7 @@ fn parse_calibration_grammar_stmt(s: &mut ParserContext) -> Result, pub stmts: Vec, } + impl Lowerer { pub fn new(source: QasmSource, source_map: SourceMap) -> Self { let symbols = SymbolTable::default(); @@ -170,6 +171,13 @@ impl Lowerer { continue; } + // special case for stdgates.inc + // it won't be in the includes list + if include.filename.to_lowercase() == "qiskit_stdgates.inc" { + self.define_qiskit_stadandard_gates(include); + continue; + } + let include = includes.next().expect("missing include"); self.lower_source(include); } else { @@ -250,11 +258,8 @@ impl Lowerer { gate_symbol("t", 0, 1), gate_symbol("sx", 0, 1), gate_symbol("rx", 1, 1), - gate_symbol("rxx", 1, 2), gate_symbol("ry", 1, 1), - gate_symbol("ryy", 1, 2), gate_symbol("rz", 1, 1), - gate_symbol("rzz", 1, 2), gate_symbol("cx", 0, 2), gate_symbol("cy", 0, 2), gate_symbol("cz", 0, 2), @@ -277,6 +282,60 @@ impl Lowerer { } } + /// Define the Qiskit standard gates in the symbol table. + /// Qiskit emits QASM3 that can't compile because it omits + /// definitions for many gates that aren't included in the + /// standard gates include file. We define them here so that + /// the symbol table is complete and we can lower the QASM3. + /// We must also define the gates in the `QasmStd` module so + /// that we can compile the QASM3 to Q#. + fn define_qiskit_stadandard_gates(&mut self, include: &syntax::IncludeStmt) { + fn gate_symbol(name: &str, cargs: u32, qargs: u32) -> Symbol { + Symbol::new( + name, + Span::default(), + Type::Gate(cargs, qargs), + Default::default(), + Default::default(), + ) + } + // QIR intrinsics missing from qasm std library, that Qiskit won't emit qasm defs for + // rxx, ryy, rzz; + + // Remaining gates that are not in the qasm std library, but are standard gates in Qiskit + // that Qiskit wont emit correctly. + // dcx, ecr, r, rzx, cs, csdg, sxdg, csx, cu1, cu3, rccx, c3sqrtx, c3x, rc3x, xx_minus_yy, xx_plus_yy, ccz; + + let gates = vec![ + gate_symbol("rxx", 1, 2), + gate_symbol("ryy", 1, 2), + gate_symbol("rzz", 1, 2), + gate_symbol("dcx", 0, 2), + gate_symbol("ecr", 0, 2), + gate_symbol("r", 2, 1), + gate_symbol("rzx", 1, 2), + gate_symbol("cs", 0, 2), + gate_symbol("csdg", 0, 2), + gate_symbol("sxdg", 0, 1), + gate_symbol("csx", 0, 2), + gate_symbol("cu1", 1, 2), + gate_symbol("cu3", 3, 2), + gate_symbol("rccx", 0, 3), + gate_symbol("c3sqrtx", 0, 4), + gate_symbol("c3x", 0, 4), + gate_symbol("rc3x", 0, 4), + gate_symbol("xx_minus_yy", 2, 2), + gate_symbol("xx_plus_yy", 2, 2), + gate_symbol("ccz", 0, 3), + ]; + for gate in gates { + let name = gate.name.clone(); + if self.symbols.insert_symbol(gate).is_err() { + self.push_redefined_symbol_error(name.as_str(), include.span); + } + } + } + fn try_insert_or_get_existing_symbol_id( &mut self, name: S, @@ -454,7 +513,7 @@ impl Lowerer { let (symbol_id, symbol) = self.try_get_existing_or_insert_err_symbol(&ident.name, ident.span); - let ty = if lhs.indices.len() == 0 { + let ty = if lhs.indices.is_empty() { &symbol.ty } else { &self.get_indexed_type(&symbol.ty, lhs.name.span, lhs.indices.len()) @@ -1931,7 +1990,7 @@ impl Lowerer { expr.span, )); return None; - }; + } let Ok(val) = u32::try_from(val) else { self.push_semantic_error(SemanticErrorKind::DesignatorTooLarge(expr.span)); @@ -1954,7 +2013,7 @@ impl Lowerer { expr.span, )); return None; - }; + } let Ok(val) = u32::try_from(val) else { self.push_semantic_error(SemanticErrorKind::DesignatorTooLarge(expr.span)); diff --git a/compiler/qsc_qasm3/src/semantic/tests.rs b/compiler/qsc_qasm3/src/semantic/tests.rs index 1b2aa04d14..58ddba1212 100644 --- a/compiler/qsc_qasm3/src/semantic/tests.rs +++ b/compiler/qsc_qasm3/src/semantic/tests.rs @@ -1,6 +1,8 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. +use std::fmt::Write; + pub mod assignment; pub mod decls; @@ -81,7 +83,7 @@ pub(super) fn check_classical_decl(input: &str, expect: &Expect) { let mut value = decl.to_string(); value.push('\n'); let symbol = &symbol_table[decl.symbol_id]; - value.push_str(&format!("[{}] {symbol}", decl.symbol_id)); + write!(value, "[{}] {symbol}", decl.symbol_id).expect("writing symbol id"); value }); } @@ -106,7 +108,7 @@ pub(super) fn check_classical_decls(input: &str, expect: &Expect) { value.push_str(&str); value.push('\n'); let symbol = &symbol_table[symbol_id]; - value.push_str(&format!("[{symbol_id}] {symbol}")); + write!(value, "[{symbol_id}] {symbol}").expect("writing symbol id"); value.push('\n'); } @@ -241,7 +243,7 @@ fn semantic_errors_map_to_their_corresponding_file_specific_spans() { Stmt [196-206]: annotations: kind: ClassicalDeclarationStmt [196-206]: - symbol_id: 35 + symbol_id: 32 ty_span: [196-199] init_expr: Expr [204-205]: ty: Bit(true) @@ -249,7 +251,7 @@ fn semantic_errors_map_to_their_corresponding_file_specific_spans() { Stmt [211-227]: annotations: kind: ClassicalDeclarationStmt [211-227]: - symbol_id: 35 + symbol_id: 32 ty_span: [211-215] init_expr: Expr [220-226]: ty: Bool(false) @@ -257,18 +259,18 @@ fn semantic_errors_map_to_their_corresponding_file_specific_spans() { op: AndL lhs: Expr [220-221]: ty: Err - kind: SymbolId(36) + kind: SymbolId(33) rhs: Expr [225-226]: ty: Bool(false) kind: Cast [0-0]: ty: Bool(false) expr: Expr [225-226]: ty: Bit(false) - kind: SymbolId(35) + kind: SymbolId(32) Stmt [140-154]: annotations: kind: ClassicalDeclarationStmt [140-154]: - symbol_id: 37 + symbol_id: 34 ty_span: [140-145] init_expr: Expr [150-153]: ty: Angle(None, true) @@ -276,7 +278,7 @@ fn semantic_errors_map_to_their_corresponding_file_specific_spans() { Stmt [159-179]: annotations: kind: ClassicalDeclarationStmt [159-179]: - symbol_id: 38 + symbol_id: 35 ty_span: [159-164] init_expr: Expr [169-178]: ty: Float(None, false) @@ -284,7 +286,7 @@ fn semantic_errors_map_to_their_corresponding_file_specific_spans() { op: Add lhs: Expr [169-170]: ty: Angle(None, false) - kind: SymbolId(37) + kind: SymbolId(34) rhs: Expr [173-178]: ty: Float(None, false) kind: Cast [0-0]: @@ -295,11 +297,11 @@ fn semantic_errors_map_to_their_corresponding_file_specific_spans() { Stmt [74-84]: annotations: kind: ClassicalDeclarationStmt [74-84]: - symbol_id: 40 + symbol_id: 37 ty_span: [74-77] init_expr: Expr [82-83]: ty: Err - kind: SymbolId(39) + kind: SymbolId(36) [Qsc.Qasm3.Compile.UndefinedSymbol diff --git a/compiler/qsc_qasm3/src/stdlib/QasmStd/src/QasmStd/Intrinsic.qs b/compiler/qsc_qasm3/src/stdlib/QasmStd/src/QasmStd/Intrinsic.qs index 8c99eb8374..54a3977419 100644 --- a/compiler/qsc_qasm3/src/stdlib/QasmStd/src/QasmStd/Intrinsic.qs +++ b/compiler/qsc_qasm3/src/stdlib/QasmStd/src/QasmStd/Intrinsic.qs @@ -1,12 +1,55 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. +// stdgates.inc, gates with implied modifiers are omitted as they are mapped +// to the base gate with modifiers in the lowerer. export gphase, U, p, x, y, z, h, s, sdg, t, tdg, sx, rx, ry, rz, cx, cp, swap, ccx, cu, phase, id, u1, u2, u3; +// gates that qiskit won't emit qasm defs for that are NOT part of stgates.inc +// but are have the _standard_gate property in Qiskit: + +// QIR intrinsics missing from qasm std library, that Qiskit won't emit qasm defs for +export rxx, ryy, rzz; + +// Remaining gates that are not in the qasm std library, but are standard gates in Qiskit +// that Qiskit wont emit correctly. +export dcx, ecr, r, rzx, cs, csdg, sxdg, csx, cu1, cu3, rccx, c3sqrtx, c3x, rc3x, xx_minus_yy, xx_plus_yy, ccz; + import Angle.*; import Std.Intrinsic.*; +function ZERO_ANGLE() : __Angle__ { + return __DoubleAsAngle__(0., 1); +} + +function PI_OVER_2() : __Angle__ { + return __DoubleAsAngle__(Std.Math.PI() / 2., 53); +} + +function PI_OVER_4() : __Angle__ { + return __DoubleAsAngle__(Std.Math.PI() / 4., 53); +} + +function PI_OVER_8() : __Angle__ { + return __DoubleAsAngle__(Std.Math.PI() / 8., 53); +} + +function PI_ANGLE() : __Angle__ { + return __DoubleAsAngle__(Std.Math.PI(), 53); +} + +function NEG_PI_OVER_2() : __Angle__ { + return __DoubleAsAngle__(-Std.Math.PI() / 2., 53); +} + +function NEG_PI_OVER_4() : __Angle__ { + return __DoubleAsAngle__(-Std.Math.PI() / 4., 53); +} + +function NEG_PI_OVER_8() : __Angle__ { + return __DoubleAsAngle__(-Std.Math.PI() / 8., 53); +} operation gphase(theta : __Angle__) : Unit is Adj + Ctl { body ... { @@ -71,6 +114,7 @@ operation tdg(qubit : Qubit) : Unit is Adj + Ctl { operation sx(qubit : Qubit) : Unit is Adj + Ctl { Rx(Std.Math.PI() / 2., qubit); + Adjoint R(PauliI, Std.Math.PI() / 2., qubit); } operation rx(theta : __Angle__, qubit : Qubit) : Unit is Adj + Ctl { @@ -111,7 +155,7 @@ operation cu(theta : __Angle__, phi : __Angle__, lambda : __Angle__, gamma : __A // Gates for OpenQASM 2 backwards compatibility operation phase(lambda : __Angle__, qubit : Qubit) : Unit is Adj + Ctl { - U(__DoubleAsAngle__(0., 1), __DoubleAsAngle__(0., 1), lambda, qubit); + U(ZERO_ANGLE(), ZERO_ANGLE(), lambda, qubit); } operation id(qubit : Qubit) : Unit is Adj + Ctl { @@ -119,21 +163,19 @@ operation id(qubit : Qubit) : Unit is Adj + Ctl { } operation u1(lambda : __Angle__, qubit : Qubit) : Unit is Adj + Ctl { - U(__DoubleAsAngle__(0., 1), __DoubleAsAngle__(0., 1), lambda, qubit); + U(ZERO_ANGLE(), ZERO_ANGLE(), lambda, qubit); } operation u2(phi : __Angle__, lambda : __Angle__, qubit : Qubit) : Unit is Adj + Ctl { - let half_pi = __DivideAngleByInt__(__DoubleAsAngle__(Std.Math.PI(), 53), 2); - gphase(__NegAngle__(__DivideAngleByInt__(__AddAngles__( phi, __AddAngles__( lambda, - half_pi + PI_OVER_2() ) ), 2))); - U(half_pi, phi, lambda, qubit); + U(PI_OVER_2(), phi, lambda, qubit); } operation u3(theta : __Angle__, phi : __Angle__, lambda : __Angle__, qubit : Qubit) : Unit is Adj + Ctl { @@ -148,3 +190,381 @@ operation u3(theta : __Angle__, phi : __Angle__, lambda : __Angle__, qubit : Qub U(theta, phi, lambda, qubit); } + +/// rxx: gate rxx(theta) a, b { h a; h b; cx a, b; rz(theta) b; cx a, b; h b; h a; } +operation rxx(theta : __Angle__, qubit0 : Qubit, qubit1 : Qubit) : Unit is Adj + Ctl { + Rxx(__AngleAsDouble__(theta), qubit0, qubit1); +} + +/// ryy: gate ryy(theta) a, b { rx(pi/2) a; rx(pi/2) b; cx a, b; rz(theta) b; cx a, b; rx(-pi/2) a; rx(-pi/2) b; } +operation ryy(theta : __Angle__, qubit0 : Qubit, qubit1 : Qubit) : Unit is Adj + Ctl { + Ryy(__AngleAsDouble__(theta), qubit0, qubit1); +} + +/// rzz: gate rzz(theta) a, b { cx a, b; u1(theta) b; cx a, b; } +operation rzz(theta : __Angle__, qubit0 : Qubit, qubit1 : Qubit) : Unit is Adj + Ctl { + Rzz(__AngleAsDouble__(theta), qubit0, qubit1); +} + +/// Double-CNOT gate. +/// ``` +/// gate dcx a, b { +/// cx a, b; +/// cx b, a; +/// } +/// ``` +operation dcx(qubit0 : Qubit, qubit1 : Qubit) : Unit is Adj + Ctl { + cx(qubit0, qubit1); + cx(qubit1, qubit0); +} + +/// An echoed cross-resonance gate. +/// `gate ecr a, b { rzx(pi/4) a, b; x a; rzx(-pi/4) a, b; }` +operation ecr(qubit0 : Qubit, qubit1 : Qubit) : Unit is Adj + Ctl { + rzx(PI_OVER_4(), qubit0, qubit1); + x(qubit0); + rzx(NEG_PI_OVER_4(), qubit0, qubit1); +} + +/// Rotation θ around the cos(φ)x + sin(φ)y axis. +/// `gate r(θ, φ) a {u3(θ, φ - π/2, -φ + π/2) a;}` +operation r(theta : __Angle__, phi : __Angle__, qubit : Qubit) : Unit is Adj + Ctl { + u3(theta, PI_OVER_4(), __SubtractAngles__( + phi, + NEG_PI_OVER_2() + ), qubit); +} + +/// A parametric 2-qubit `Z ⊗ X` interaction (rotation about ZX). +/// `gate rzx(theta) a, b { h b; cx a, b; u1(theta) b; cx a, b; h b; }` +operation rzx(theta : __Angle__, qubit0 : Qubit, qubit1 : Qubit) : Unit is Adj + Ctl { + h(qubit1); + cx(qubit0, qubit1); + u1(theta, qubit1); + cx(qubit0, qubit1); + h(qubit1); +} + + +/// Controlled-S gate. +/// `gate cs a,b { h b; cp(pi/2) a,b; h b; }` +operation cs(qubit0 : Qubit, qubit1 : Qubit) : Unit is Adj + Ctl { + Controlled s([qubit1], qubit0); +} + +/// Controlled-S† gate. +/// csdg: gate csdg a,b { h b; cp(-pi/2) a,b; h b; } +operation csdg(qubit0 : Qubit, qubit1 : Qubit) : Unit is Adj + Ctl { + Controlled Adjoint S([qubit1], qubit0); +} + +/// The inverse single-qubit Sqrt(X) gate. +/// `gate sxdg a { rz(pi/2) a; h a; rz(pi/2); }` +operation sxdg(qubit : Qubit) : Unit is Adj + Ctl { + Adjoint sx(qubit); +} + +// Controlled-√X gate. +/// `gate csx a,b { h b; cu1(pi/2) a,b; h b; }` +operation csx(qubit0 : Qubit, qubit1 : Qubit) : Unit is Adj + Ctl { + Controlled sx([qubit1], qubit0); +} + +/// Controlled-U1 gate. +/// `ctrl @ u1(lambda) a, b` or: +/// ``` +/// gate cu1(lambda) a,b { +/// u1(lambda/2) a; +/// cx a,b; +/// u1(-lambda/2) b; +/// cx a,b; +/// u1(lambda/2) b; +/// } +/// ``` +operation cu1(lambda : __Angle__, ctrl : Qubit, target : Qubit) : Unit is Adj + Ctl { + Controlled u1([ctrl], (lambda, target)); +} + +/// Controlled-U3 gate (3-parameter two-qubit gate). +/// `ctrl @ u3(theta, phi, lambda) a, b` or: +/// ``` +/// gate cu3(theta,phi,lambda) c, t { +/// u1((lambda+phi)/2) c; +/// u1((lambda-phi)/2) t; +/// cx c,t; +/// u3(-theta/2,0,-(phi+lambda)/2) t; +/// cx c,t; +/// u3(theta/2,phi,0) t; +/// } +/// ``` +operation cu3(theta : __Angle__, phi : __Angle__, lambda : __Angle__, ctrl : Qubit, target : Qubit) : Unit is Adj + Ctl { + Controlled u3([ctrl], (theta, phi, lambda, target)); +} + +/// The simplified Toffoli gate, also referred to as Margolus gate. +/// `gate rccx a,b,c { u2(0,pi) c; u1(pi/4) c; cx b, c; u1(-pi/4) c; cx a, c; u1(pi/4) c; cx b, c; u1(-pi/4) c; u2(0,pi) c; }` +operation rccx(ctrl1 : Qubit, ctrl2 : Qubit, target : Qubit) : Unit is Adj + Ctl { + u2(ZERO_ANGLE(), PI_ANGLE(), target); + u1(PI_OVER_4(), target); + cx(ctrl2, target); + u1(NEG_PI_OVER_4(), target); + cx(ctrl1, target); + u1(PI_OVER_4(), target); + cx(ctrl2, target); + u1(NEG_PI_OVER_4(), target); + u2(ZERO_ANGLE(), PI_ANGLE(), target); +} + +/// c3sx/c3sqrtx: The 3-qubit controlled sqrt-X gate. +/// ``` +/// gate c3sqrtx a,b,c,d { +/// h d; +/// cu1(pi/8) a,d; +/// h d; cx a,b; +/// h d; +/// cu1(-pi/8) b,d; +/// h d; cx a,b; h d; +/// cu1(pi/8) b,d; +/// h d; +/// cx b,c; +/// h d; +/// cu1(-pi/8) c,d; +/// h d; +/// cx a,c; +/// h d; +/// cu1(pi/8) c,d; +/// h d; +/// cx b,c; +/// h d; +/// cu1(-pi/8) c,d; +/// h d; +/// cx a,c; +/// h d; +/// cu1(pi/8) c,d; +/// h d; +/// } +/// ``` +operation c3sqrtx(a : Qubit, b : Qubit, c : Qubit, target : Qubit) : Unit is Adj + Ctl { + h(target); + Controlled u1([a], (PI_OVER_8(), target)); + h(target); + cx(a, b); + h(target); + Controlled u1([b], (NEG_PI_OVER_8(), target)); + h(target); + cx(a, b); + h(target); + Controlled u1([b], (PI_OVER_8(), target)); + h(target); + cx(b, c); + h(target); + Controlled u1([c], (NEG_PI_OVER_8(), target)); + h(target); + cx(a, c); + h(target); + Controlled u1([c], (PI_OVER_8(), target)); + h(target); + cx(b, c); + h(target); + Controlled u1([c], (NEG_PI_OVER_8(), target)); + h(target); + cx(a, c); + h(target); + Controlled u1([c], (PI_OVER_8(), target)); + h(target); +} + +/// The X gate controlled on 3 qubits. +/// ``` +/// gate c3x a,b,c,d +/// { +/// h d; +/// p(pi/8) a; +/// p(pi/8) b; +/// p(pi/8) c; +/// p(pi/8) d; +/// cx a, b; +/// p(-pi/8) b; +/// cx a, b; +/// cx b, c; +/// p(-pi/8) c; +/// cx a, c; +/// p(pi/8) c; +/// cx b, c; +/// p(-pi/8) c; +/// cx a, c; +/// cx c, d; +/// p(-pi/8) d; +/// cx b, d; +/// p(pi/8) d; +/// cx c, d; +/// p(-pi/8) d; +/// cx a, d; +/// p(pi/8) d; +/// cx c, d; +/// p(-pi/8) d; +/// cx b, d; +/// p(pi/8) d; +/// cx c, d; +/// p(-pi/8) d; +/// cx a, d; +/// h d; +/// } +/// ``` +operation c3x(a : Qubit, b : Qubit, c : Qubit, d : Qubit) : Unit is Adj + Ctl { + h(d); + p(PI_OVER_8(), a); + p(PI_OVER_8(), b); + p(PI_OVER_8(), c); + p(PI_OVER_8(), d); + cx(a, b); + p(NEG_PI_OVER_8(), b); + cx(a, b); + cx(b, c); + p(NEG_PI_OVER_8(), c); + cx(a, c); + p(PI_OVER_8(), c); + cx(b, c); + p(NEG_PI_OVER_8(), c); + cx(a, c); + cx(c, d); + p(NEG_PI_OVER_8(), d); + cx(b, d); + p(PI_OVER_8(), d); + cx(c, d); + p(NEG_PI_OVER_8(), d); + cx(a, d); + p(PI_OVER_8(), d); + cx(c, d); + p(NEG_PI_OVER_8(), d); + cx(b, d); + p(PI_OVER_8(), d); + cx(c, d); + p(NEG_PI_OVER_8(), d); + cx(a, d); + h(d); +} + +/// Simplified 3-controlled Toffoli gate. +/// ``` +/// gate rc3x a,b,c,d{ +/// u2(0,pi) d; +/// u1(pi/4) d; +/// cx c,d; +/// u1(-pi/4) d; +/// u2(0,pi) d; +/// cx a,d; +/// u1(pi/4) d; +/// cx b,d; +/// u1(-pi/4) d; +/// cx a,d; +/// u1(pi/4) d; +/// cx b,d; +/// u1(-pi/4) d; +/// u2(0,pi) d; +/// u1(pi/4) d; +/// cx c,d; +/// u1(-pi/4) d; +/// u2(0,pi) d; +/// } +/// ``` +operation rc3x(a : Qubit, b : Qubit, c : Qubit, d : Qubit) : Unit is Adj + Ctl { + u2(ZERO_ANGLE(), PI_ANGLE(), d); + u1(PI_OVER_4(), d); + cx(c, d); + u1(NEG_PI_OVER_4(), d); + u2(ZERO_ANGLE(), PI_ANGLE(), d); + cx(a, d); + u1(PI_OVER_4(), d); + cx(b, d); + u1(NEG_PI_OVER_4(), d); + cx(a, d); + u1(PI_OVER_4(), d); + cx(b, d); + u1(NEG_PI_OVER_4(), d); + u2(ZERO_ANGLE(), PI_ANGLE(), d); + u1(PI_OVER_4(), d); + cx(c, d); + u1(NEG_PI_OVER_4(), d); + u2(ZERO_ANGLE(), PI_ANGLE(), d); +} + +/// XX-YY gate. +/// ``` +/// gate xx_minus_yy(theta, beta) a, b { +/// rz(-beta) b; +/// rz(-pi/2) a; +/// sx a; +/// rz(pi/2) a; +/// s b; +/// cx a, b; +/// ry(theta/2) a; +/// ry(-theta/2) b; +/// cx a, b; +/// sdg b; +/// rz(-pi/2) a; +/// sxdg a; +/// rz(pi/2) a; +/// rz(beta) b; +/// } +/// ``` +operation xx_minus_yy(theta : __Angle__, beta : __Angle__, qubit0 : Qubit, qubit1 : Qubit) : Unit is Adj + Ctl { + rz(__NegAngle__(beta), qubit1); + rz(NEG_PI_OVER_2(), qubit0); + sx(qubit0); + rz(PI_OVER_2(), qubit0); + s(qubit1); + cx(qubit0, qubit1); + ry(__DivideAngleByInt__(theta, 2), qubit0); + ry(__NegAngle__(__DivideAngleByInt__(theta, 2)), qubit1); + cx(qubit0, qubit1); + sdg(qubit1); + rz(NEG_PI_OVER_2(), qubit0); + sxdg(qubit0); + rz(PI_OVER_2(), qubit0); + rz(beta, qubit1); +} + +/// XX+YY gate. +/// ``` +/// gate xx_plus_yy(theta, beta) a, b { +/// rz(beta) b; +/// rz(-pi/2) a; +/// sx a; +/// rz(pi/2) a; +/// s b; +/// cx a, b; +/// ry(theta/2) a; +/// ry(theta/2) b; +/// cx a, b; +/// sdg b; +/// rz(-pi/2) a; +/// sxdg a; +/// rz(pi/2) a; +/// rz(-beta) b; +/// } +/// ``` +operation xx_plus_yy(theta : __Angle__, beta : __Angle__, qubit0 : Qubit, qubit1 : Qubit) : Unit is Adj + Ctl { + rz(beta, qubit1); + rz(NEG_PI_OVER_2(), qubit0); + sx(qubit0); + rz(PI_OVER_2(), qubit0); + s(qubit1); + cx(qubit0, qubit1); + ry(__DivideAngleByInt__(theta, 2), qubit0); + ry(__DivideAngleByInt__(theta, 2), qubit1); + cx(qubit0, qubit1); + sdg(qubit1); + rz(NEG_PI_OVER_2(), qubit0); + sxdg(qubit0); + rz(PI_OVER_2(), qubit0); + rz(__NegAngle__(beta), qubit1); +} + +/// CCZ gate. +/// `gate ccz a,b,c { h c; ccx a,b,c; h c; }` +operation ccz(ctrl1 : Qubit, ctrl2 : Qubit, target : Qubit) : Unit is Adj + Ctl { + h(target); + ccx(ctrl1, ctrl2, target); + h(target); +} diff --git a/compiler/qsc_qasm3/src/stdlib/angle.rs b/compiler/qsc_qasm3/src/stdlib/angle.rs index 63bd90463b..61b007e9b7 100644 --- a/compiler/qsc_qasm3/src/stdlib/angle.rs +++ b/compiler/qsc_qasm3/src/stdlib/angle.rs @@ -6,7 +6,7 @@ pub(crate) mod tests; use num_bigint::BigInt; -use crate::oqasm_helpers::safe_u64_to_f64; +use crate::convert::safe_u64_to_f64; use core::f64; use std::convert::TryInto; use std::f64::consts::TAU; diff --git a/compiler/qsc_qasm3/src/symbols.rs b/compiler/qsc_qasm3/src/symbols.rs deleted file mode 100644 index 679fe0b1a6..0000000000 --- a/compiler/qsc_qasm3/src/symbols.rs +++ /dev/null @@ -1,435 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. - -use oq3_semantics::types::{IsConst, Type}; -use qsc_data_structures::span::Span; -use rustc_hash::FxHashMap; - -/// We need a symbol table to keep track of the symbols in the program. -/// The scoping rules for QASM3 are slightly different from Q#. This also -/// means that we need to keep track of the input and output symbols in the -/// program. Additionally, we need to keep track of the types of the symbols -/// in the program for type checking. -/// Q# and QASM have different type systems, so we track the QASM semantic. -/// -/// A symbol ID is a unique identifier for a symbol in the symbol table. -/// This is used to look up symbols in the symbol table. -/// Every symbol in the symbol table has a unique ID. -#[derive(Debug, Default, Clone, Copy)] -pub struct SymbolId(pub u32); - -impl SymbolId { - /// The successor of this ID. - #[must_use] - pub fn successor(self) -> Self { - Self(self.0 + 1) - } -} - -impl From for SymbolId { - fn from(val: u32) -> Self { - SymbolId(val) - } -} - -impl From for u32 { - fn from(id: SymbolId) -> Self { - id.0 - } -} - -impl From for usize { - fn from(value: SymbolId) -> Self { - value.0 as usize - } -} - -impl From for SymbolId { - fn from(value: usize) -> Self { - SymbolId( - value - .try_into() - .unwrap_or_else(|_| panic!("Value, {value}, does not fit into SymbolId")), - ) - } -} - -impl PartialEq for SymbolId { - fn eq(&self, other: &Self) -> bool { - self.0 == other.0 - } -} - -impl Eq for SymbolId {} - -impl PartialOrd for SymbolId { - fn partial_cmp(&self, other: &Self) -> Option { - Some(self.cmp(other)) - } -} - -impl Ord for SymbolId { - fn cmp(&self, other: &Self) -> std::cmp::Ordering { - self.0.cmp(&other.0) - } -} - -impl std::hash::Hash for SymbolId { - fn hash(&self, state: &mut H) { - self.0.hash(state); - } -} - -impl std::fmt::Display for SymbolId { - fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { - std::fmt::Display::fmt(&self.0, f) - } -} - -#[derive(Debug, Clone, PartialEq, Eq)] -pub(crate) struct Symbol { - pub name: String, - pub span: Span, - pub ty: Type, - pub qsharp_ty: crate::types::Type, - pub io_kind: IOKind, -} - -/// A symbol in the symbol table. -/// Default Q# type is Unit -impl Default for Symbol { - fn default() -> Self { - Self { - name: String::default(), - span: Span::default(), - ty: Type::Undefined, - qsharp_ty: crate::types::Type::Tuple(vec![]), - io_kind: IOKind::default(), - } - } -} - -#[derive(Debug, Clone, PartialEq, Eq)] -pub enum SymbolError { - /// The symbol already exists in the symbol table, at the current scope. - AlreadyExists, -} - -/// Symbols have a an I/O kind that determines if they are input or output, or unspecified. -/// The default I/O kind means no explicit kind was part of the decl. -/// There is a specific statement for io decls which sets the I/O kind appropriately. -/// This is used to determine the input and output symbols in the program. -#[derive(Debug, Clone, PartialEq, Eq)] -pub enum IOKind { - Default, - Input, - Output, -} - -impl Default for IOKind { - fn default() -> Self { - Self::Default - } -} - -/// A scope is a collection of symbols and a kind. The kind determines semantic -/// rules for compliation as shadowing and decl rules vary by scope kind. -pub struct Scope { - /// A map from symbol name to symbol ID. - name_to_id: FxHashMap, - /// A map from symbol ID to symbol. - id_to_symbol: FxHashMap, - /// The order in which symbols were inserted into the scope. - /// This is used to determine the order of symbols in the output. - order: Vec, - /// The kind of the scope. - kind: ScopeKind, -} - -impl Scope { - pub fn new(kind: ScopeKind) -> Self { - Self { - name_to_id: FxHashMap::default(), - id_to_symbol: FxHashMap::default(), - order: vec![], - kind, - } - } - - /// Inserts the symbol into the current scope. - /// Returns the ID of the symbol. - /// - /// # Errors - /// - /// This function will return an error if a symbol of the same name has already - /// been declared in this scope. - pub fn insert_symbol(&mut self, id: SymbolId, symbol: Symbol) -> Result<(), SymbolError> { - if self.name_to_id.contains_key(&symbol.name) { - return Err(SymbolError::AlreadyExists); - } - self.name_to_id.insert(symbol.name.clone(), id); - self.id_to_symbol.insert(id, symbol); - self.order.push(id); - Ok(()) - } - - pub fn get_symbol_by_name(&self, name: &str) -> Option<&Symbol> { - self.name_to_id - .get(name) - .and_then(|id| self.id_to_symbol.get(id)) - } - - fn get_ordered_symbols(&self) -> Vec { - self.order - .iter() - .map(|id| self.id_to_symbol.get(id).expect("ID should exist").clone()) - .collect() - } -} - -/// A symbol table is a collection of scopes and manages the symbol ids. -pub struct SymbolTable { - scopes: Vec, - current_id: SymbolId, -} - -#[derive(Debug, Clone, PartialEq, Eq)] -pub enum ScopeKind { - /// Global scope, which is the current scope only when no other scopes are active. - /// This is the only scope where gates, qubits, and arrays can be declared. - Global, - Function, - Gate, - Block, -} - -const BUILTIN_SYMBOLS: [&str; 6] = ["pi", "π", "tau", "τ", "euler", "ℇ"]; - -impl SymbolTable { - pub fn new() -> Self { - let global = Scope::new(ScopeKind::Global); - - let mut slf = Self { - scopes: vec![global], - current_id: SymbolId::default(), - }; - - // Define global constants - for symbol in BUILTIN_SYMBOLS { - slf.insert_symbol(Symbol { - name: symbol.to_string(), - span: Span::default(), - ty: Type::Float(None, IsConst::True), - qsharp_ty: crate::types::Type::Double(true), - io_kind: IOKind::Default, - }) - .unwrap_or_else(|_| panic!("Failed to insert symbol: {symbol}")); - } - - slf - } - - pub fn push_scope(&mut self, kind: ScopeKind) { - assert!(kind != ScopeKind::Global, "Cannot push a global scope"); - self.scopes.push(Scope::new(kind)); - } - - pub fn pop_scope(&mut self) { - assert!(self.scopes.len() != 1, "Cannot pop the global scope"); - self.scopes.pop(); - } - - pub fn insert_symbol(&mut self, symbol: Symbol) -> Result { - let id = self.current_id; - self.current_id = self.current_id.successor(); - self.scopes - .last_mut() - .expect("At least one scope should be available") - .insert_symbol(id, symbol)?; - - Ok(id) - } - - pub fn get_symbol_by_name(&self, name: &str) -> Option<&Symbol> { - let scopes = self.scopes.iter().rev(); - let predicate = |x: &Scope| { - x.kind == ScopeKind::Block || x.kind == ScopeKind::Function || x.kind == ScopeKind::Gate - }; - - // Use scan to track the last item that returned false - let mut last_false = None; - let _ = scopes - .scan(&mut last_false, |state, item| { - if !predicate(item) { - **state = Some(item); - } - Some(predicate(item)) - }) - .take_while(|&x| x) - .last(); - let mut scopes = self.scopes.iter().rev(); - while let Some(scope) = scopes - .by_ref() - .take_while(|arg0: &&Scope| predicate(arg0)) - .next() - { - if let Some(symbol) = scope.get_symbol_by_name(name) { - return Some(symbol); - } - } - - if let Some(scope) = last_false { - if let Some(symbol) = scope.get_symbol_by_name(name) { - if symbol.ty.is_const() - || matches!(symbol.ty, Type::Gate(..) | Type::Void) - || self.is_scope_rooted_in_global() - { - return Some(symbol); - } - } - } - // we should be at the global, function, or gate scope now - for scope in scopes { - if let Some(symbol) = scope.get_symbol_by_name(name) { - if symbol.ty.is_const() || matches!(symbol.ty, Type::Gate(..) | Type::Void) { - return Some(symbol); - } - } - } - - None - } - - pub fn is_current_scope_global(&self) -> bool { - matches!(self.scopes.last(), Some(scope) if scope.kind == ScopeKind::Global) - } - - pub fn is_scope_rooted_in_subroutine(&self) -> bool { - self.scopes - .iter() - .rev() - .any(|scope| scope.kind == ScopeKind::Function) - } - - pub fn is_scope_rooted_in_global(&self) -> bool { - for scope in self.scopes.iter().rev() { - if scope.kind == ScopeKind::Function { - return false; - } - if scope.kind == ScopeKind::Gate { - return false; - } - } - true - } - - /// Get the input symbols in the program. - pub(crate) fn get_input(&self) -> Option> { - let io_input = self.get_io_input(); - if io_input.is_empty() { - None - } else { - Some(io_input) - } - } - - /// Get the output symbols in the program. - /// Output symbols are either inferred or explicitly declared. - /// If there are no explicitly declared output symbols, then the inferred - /// output symbols are returned. - pub(crate) fn get_output(&self) -> Option> { - let io_ouput = self.get_io_output(); - if io_ouput.is_some() { - io_ouput - } else { - self.get_inferred_output() - } - } - - /// Get all symbols in the global scope that are inferred output symbols. - /// Any global symbol that is not a built-in symbol and has a type that is - /// inferred to be an output type is considered an inferred output symbol. - fn get_inferred_output(&self) -> Option> { - let mut symbols = vec![]; - self.scopes - .iter() - .filter(|scope| scope.kind == ScopeKind::Global) - .for_each(|scope| { - for symbol in scope - .get_ordered_symbols() - .into_iter() - .filter(|symbol| !BUILTIN_SYMBOLS.contains(&symbol.name.as_str())) - .filter(|symbol| symbol.io_kind == IOKind::Default) - { - if is_inferred_output_type(&symbol.ty) { - symbols.push(symbol); - } - } - }); - if symbols.is_empty() { - None - } else { - Some(symbols) - } - } - - /// Get all symbols in the global scope that are output symbols. - fn get_io_output(&self) -> Option> { - let mut symbols = vec![]; - for scope in self - .scopes - .iter() - .filter(|scope| scope.kind == ScopeKind::Global) - { - for symbol in scope.get_ordered_symbols() { - if symbol.io_kind == IOKind::Output { - symbols.push(symbol); - } - } - } - if symbols.is_empty() { - None - } else { - Some(symbols) - } - } - - /// Get all symbols in the global scope that are input symbols. - fn get_io_input(&self) -> Vec { - let mut symbols = vec![]; - for scope in self - .scopes - .iter() - .filter(|scope| scope.kind == ScopeKind::Global) - { - for symbol in scope.get_ordered_symbols() { - if symbol.io_kind == IOKind::Input { - symbols.push(symbol); - } - } - } - symbols - } -} - -fn is_inferred_output_type(ty: &Type) -> bool { - matches!( - ty, - Type::Bit(_) - | Type::Int(_, _) - | Type::UInt(_, _) - | Type::Float(_, _) - | Type::Angle(_, _) - | Type::Complex(_, _) - | Type::Bool(_) - | Type::BitArray(_, _) - | Type::IntArray(_) - | Type::UIntArray(_) - | Type::FloatArray(_) - | Type::AngleArray(_) - | Type::ComplexArray(_) - | Type::BoolArray(_) - | Type::Range - | Type::Set - ) -} diff --git a/compiler/qsc_qasm3/src/tests/statement/gate_call.rs b/compiler/qsc_qasm3/src/tests/statement/gate_call.rs index d1f7ec175d..1586494757 100644 --- a/compiler/qsc_qasm3/src/tests/statement/gate_call.rs +++ b/compiler/qsc_qasm3/src/tests/statement/gate_call.rs @@ -42,6 +42,59 @@ fn gphase_gate_can_be_called() -> miette::Result<(), Vec> { Ok(()) } +#[test] +fn custom_gates_can_be_called_bypassing_stdgates() -> miette::Result<(), Vec> { + let source = r#" + gate h a { U(π/2, 0., π) a; gphase(-π/4);} + gate x a { U(π, 0., π) a; gphase(-π/2);} + gate cx a, b { ctrl @ x a, b; } + gate rz(λ) a { gphase(-λ/2); U(0., 0., λ) a; } + gate rxx(theta) a, b { h a; h b; cx a, b; rz(theta) b; cx a, b; h b; h a; } + + qubit a; + qubit b; + x a; + rxx(π/2) a, b; + "#; + + let qsharp = compile_qasm_to_qsharp(source)?; + expect![[r#" + import QasmStd.Angle.*; + import QasmStd.Convert.*; + import QasmStd.Intrinsic.*; + operation h(a : Qubit) : Unit is Adj + Ctl { + U(__DoubleAsAngle__(3.141592653589793 / 2., 53), __DoubleAsAngle__(0., 53), __DoubleAsAngle__(3.141592653589793, 53), a); + gphase(__DoubleAsAngle__(-3.141592653589793 / 4., 53)); + } + operation x(a : Qubit) : Unit is Adj + Ctl { + U(__DoubleAsAngle__(3.141592653589793, 53), __DoubleAsAngle__(0., 53), __DoubleAsAngle__(3.141592653589793, 53), a); + gphase(__DoubleAsAngle__(-3.141592653589793 / 2., 53)); + } + operation cx(a : Qubit, b : Qubit) : Unit is Adj + Ctl { + Controlled x([a], b); + } + operation rz(λ : __Angle__, a : Qubit) : Unit is Adj + Ctl { + gphase(__DivideAngleByInt__(__NegAngle__(λ), 2)); + U(__DoubleAsAngle__(0., 53), __DoubleAsAngle__(0., 53), λ, a); + } + operation rxx(theta : __Angle__, a : Qubit, b : Qubit) : Unit is Adj + Ctl { + h(a); + h(b); + cx(a, b); + rz(theta, b); + cx(a, b); + h(b); + h(a); + } + let a = QIR.Runtime.__quantum__rt__qubit_allocate(); + let b = QIR.Runtime.__quantum__rt__qubit_allocate(); + x(a); + rxx(__DoubleAsAngle__(Microsoft.Quantum.Math.PI() / 2., 53), a, b); + "#]] + .assert_eq(&qsharp); + Ok(()) +} + #[test] fn x_gate_can_be_called() -> miette::Result<(), Vec> { let source = r#" @@ -523,3 +576,158 @@ fn simulatable_intrinsic_on_gate_stmt_generates_correct_qir() -> miette::Result< "#]].assert_eq(&qsharp); Ok(()) } + +#[test] +fn rxx_gate_with_one_angle_can_be_called_with_qiskit_stdgates() -> miette::Result<(), Vec> { + let source = r#" + include "qiskit_stdgates.inc"; + qubit[2] q; + rxx(2.0) q[1], q[0]; + "#; + + let qsharp = compile_qasm_to_qsharp(source)?; + expect![[r#" + import QasmStd.Angle.*; + import QasmStd.Convert.*; + import QasmStd.Intrinsic.*; + let q = QIR.Runtime.AllocateQubitArray(2); + rxx(__DoubleAsAngle__(2., 53), q[1], q[0]); + "#]] + .assert_eq(&qsharp); + Ok(()) +} + +#[test] +fn ryy_gate_with_one_angle_can_be_called_with_qiskit_stdgates() -> miette::Result<(), Vec> { + let source = r#" + include "qiskit_stdgates.inc"; + qubit[2] q; + ryy(2.0) q[1], q[0]; + "#; + + let qsharp = compile_qasm_to_qsharp(source)?; + expect![[r#" + import QasmStd.Angle.*; + import QasmStd.Convert.*; + import QasmStd.Intrinsic.*; + let q = QIR.Runtime.AllocateQubitArray(2); + ryy(__DoubleAsAngle__(2., 53), q[1], q[0]); + "#]] + .assert_eq(&qsharp); + Ok(()) +} + +#[test] +fn rzz_gate_with_one_angle_can_be_called_with_qiskit_stdgates() -> miette::Result<(), Vec> { + let source = r#" + include "qiskit_stdgates.inc"; + qubit[2] q; + rzz(2.0) q[1], q[0]; + "#; + + let qsharp = compile_qasm_to_qsharp(source)?; + expect![[r#" + import QasmStd.Angle.*; + import QasmStd.Convert.*; + import QasmStd.Intrinsic.*; + let q = QIR.Runtime.AllocateQubitArray(2); + rzz(__DoubleAsAngle__(2., 53), q[1], q[0]); + "#]] + .assert_eq(&qsharp); + Ok(()) +} + +#[test] +fn rxx_gate_cannot_be_called_without_qiskit_stdgates() -> miette::Result<(), Vec> { + let source = r#" + include "stdgates.inc"; + qubit[2] q; + rxx(2.0) q[1], q[0]; + "#; + + compile_qasm_to_qsharp(source).expect_err("Error expected"); + Ok(()) +} + +#[test] +fn ryy_gate_cannot_be_called_without_qiskit_stdgates() -> miette::Result<(), Vec> { + let source = r#" + include "stdgates.inc"; + qubit[2] q; + ryy(2.0) q[1], q[0]; + "#; + + compile_qasm_to_qsharp(source).expect_err("Error expected"); + Ok(()) +} + +#[test] +fn rzz_gate_cannot_be_called_without_qiskit_stdgates() -> miette::Result<(), Vec> { + let source = r#" + include "stdgates.inc"; + qubit[2] q; + rzz(2.0) q[1], q[0]; + "#; + + compile_qasm_to_qsharp(source).expect_err("Error expected"); + Ok(()) +} + +#[test] +fn all_qiskit_stdgates_can_be_called_with_qiskit_stdgates_included( +) -> miette::Result<(), Vec> { + let source = r#" + include "qiskit_stdgates.inc"; + qubit[4] q; + rxx(pi / 2.0) q[1], q[0]; + ryy(pi / 2.0) q[1], q[0]; + rzz(pi / 2.0) q[1], q[0]; + dcx q[0], q[1]; + ecr q[0], q[1]; + r(pi / 2.0, pi / 4.0) q[1]; + rzx(pi / 2.0) q[1], q[0]; + cs q[0], q[1]; + csdg q[0], q[1]; + sxdg q[0]; + csx q[0], q[1]; + cu1(pi / 2.0) q[1], q[0]; + cu3(pi / 2.0, pi / 4.0, pi / 8.0) q[1], q[0]; + rccx q[0], q[1], q[2]; + c3sqrtx q[0], q[1], q[2], q[3]; + c3x q[0], q[1], q[2], q[3]; + rc3x q[0], q[1], q[2], q[3]; + xx_minus_yy(pi / 2.0, pi / 4.0) q[1], q[0]; + xx_plus_yy(pi / 2.0, pi / 4.0) q[1], q[0]; + ccz q[0], q[1], q[2]; + "#; + + let qsharp = compile_qasm_to_qsharp(source)?; + expect![[r#" + import QasmStd.Angle.*; + import QasmStd.Convert.*; + import QasmStd.Intrinsic.*; + let q = QIR.Runtime.AllocateQubitArray(4); + rxx(__DoubleAsAngle__(Microsoft.Quantum.Math.PI() / 2., 53), q[1], q[0]); + ryy(__DoubleAsAngle__(Microsoft.Quantum.Math.PI() / 2., 53), q[1], q[0]); + rzz(__DoubleAsAngle__(Microsoft.Quantum.Math.PI() / 2., 53), q[1], q[0]); + dcx(q[0], q[1]); + ecr(q[0], q[1]); + r(__DoubleAsAngle__(Microsoft.Quantum.Math.PI() / 2., 53), __DoubleAsAngle__(Microsoft.Quantum.Math.PI() / 4., 53), q[1]); + rzx(__DoubleAsAngle__(Microsoft.Quantum.Math.PI() / 2., 53), q[1], q[0]); + cs(q[0], q[1]); + csdg(q[0], q[1]); + sxdg(q[0]); + csx(q[0], q[1]); + cu1(__DoubleAsAngle__(Microsoft.Quantum.Math.PI() / 2., 53), q[1], q[0]); + cu3(__DoubleAsAngle__(Microsoft.Quantum.Math.PI() / 2., 53), __DoubleAsAngle__(Microsoft.Quantum.Math.PI() / 4., 53), __DoubleAsAngle__(Microsoft.Quantum.Math.PI() / 8., 53), q[1], q[0]); + rccx(q[0], q[1], q[2]); + c3sqrtx(q[0], q[1], q[2], q[3]); + c3x(q[0], q[1], q[2], q[3]); + rc3x(q[0], q[1], q[2], q[3]); + xx_minus_yy(__DoubleAsAngle__(Microsoft.Quantum.Math.PI() / 2., 53), __DoubleAsAngle__(Microsoft.Quantum.Math.PI() / 4., 53), q[1], q[0]); + xx_plus_yy(__DoubleAsAngle__(Microsoft.Quantum.Math.PI() / 2., 53), __DoubleAsAngle__(Microsoft.Quantum.Math.PI() / 4., 53), q[1], q[0]); + ccz(q[0], q[1], q[2]); + "#]] + .assert_eq(&qsharp); + Ok(()) +} diff --git a/compiler/qsc_qasm3/src/types.rs b/compiler/qsc_qasm3/src/types.rs index d8f607001b..4228c51d12 100644 --- a/compiler/qsc_qasm3/src/types.rs +++ b/compiler/qsc_qasm3/src/types.rs @@ -3,78 +3,7 @@ use std::fmt::{self, Display, Formatter}; -use oq3_semantics::types::ArrayDims; use qsc_data_structures::span::Span; -use rustc_hash::FxHashMap; - -thread_local! { - /// - pub static GATE_MAP: FxHashMap<&'static str, &'static str> = { - let mut m = FxHashMap::default(); - // p is rz, should have been replaced by rz by transpile - - m.insert("x", "X"); - m.insert("y", "Y"); - m.insert("z", "Z"); - - m.insert("h", "H"); - - m.insert("s", "S"); - m.insert("sdg", "sdg"); - - m.insert("t", "T"); - m.insert("tdg", "tdg"); - - // sx q is Rx(pi/2, q), should have been replaced by Rx by transpile - - m.insert("crx", "crx"); - m.insert("cry", "cry"); - m.insert("crz", "crz"); - - m.insert("rx", "Rx"); - m.insert("ry", "Ry"); - m.insert("rz", "Rz"); - - m.insert("rxx", "Rxx"); - m.insert("ryy", "Ryy"); - m.insert("rzz", "Rzz"); - - m.insert("cx", "CNOT"); - m.insert("cy", "CY"); - m.insert("cz", "CZ"); - - // cp (controlled-phase), should have been replaced by transpile - - m.insert("ch", "ch"); - - m.insert("id", "I"); - - m.insert("swap", "SWAP"); - - m.insert("ccx", "CCNOT"); - - // cswap (controlled-swap), should have been replaced by transpile - - // cu (controlled-U), should have been replaced by transpile - - // openqasm 2.0 gates should have been replaced by transpile - // CX, phase, cphase, id, u1, u2, u3 - m - }; -} - -pub(crate) fn get_qsharp_gate_name>(gate_name: S) -> Option<&'static str> { - GATE_MAP.with(|map| map.get(gate_name.as_ref()).copied()) -} - -/// When compiling QASM3 expressions, we need to keep track of the sematic QASM -/// type of the expression. This allows us to perform type checking and casting -/// when necessary. -#[derive(Debug, Clone, PartialEq)] -pub struct QasmTypedExpr { - pub ty: oq3_semantics::types::Type, - pub expr: qsc_ast::ast::Expr, -} #[derive(Clone, Debug, PartialEq)] pub enum GateModifier { @@ -148,16 +77,6 @@ pub enum ArrayDimensions { Three(usize, usize, usize), } -impl From<&ArrayDims> for ArrayDimensions { - fn from(value: &ArrayDims) -> Self { - match value { - ArrayDims::D1(dim) => ArrayDimensions::One(*dim), - ArrayDims::D2(dim1, dim2) => ArrayDimensions::Two(*dim1, *dim2), - ArrayDims::D3(dim1, dim2, dim3) => ArrayDimensions::Three(*dim1, *dim2, *dim3), - } - } -} - impl From<&crate::semantic::types::ArrayDimensions> for ArrayDimensions { fn from(value: &crate::semantic::types::ArrayDimensions) -> Self { match value { @@ -230,59 +149,3 @@ impl Display for ArrayDimensions { } } } - -/// Get the indexed type of a given type. -/// For example, if the type is `Int[2][3]`, the indexed type is `Int[2]`. -/// If the type is `Int[2]`, the indexed type is `Int`. -/// If the type is `Int`, the indexed type is `None`. -/// -/// This is useful for determining the type of an array element. -pub(crate) fn get_indexed_type( - ty: &oq3_semantics::types::Type, -) -> Option { - use oq3_semantics::types::{IsConst, Type}; - let ty = match &ty { - Type::AngleArray(dims) => match dims { - ArrayDims::D1(_) => Type::Angle(None, IsConst::False), - ArrayDims::D2(l, _) => Type::AngleArray(ArrayDims::D1(*l)), - ArrayDims::D3(l, w, _) => Type::AngleArray(ArrayDims::D2(*l, *w)), - }, - Type::BitArray(dims, is_const) => match dims { - ArrayDims::D1(_) => Type::Bit(is_const.clone()), - ArrayDims::D2(l, _) => Type::BitArray(ArrayDims::D1(*l), is_const.clone()), - ArrayDims::D3(l, w, _) => Type::BitArray(ArrayDims::D2(*l, *w), is_const.clone()), - }, - Type::BoolArray(dims) => match dims { - ArrayDims::D1(_) => Type::Bool(IsConst::False), - ArrayDims::D2(l, _) => Type::BoolArray(ArrayDims::D1(*l)), - ArrayDims::D3(l, w, _) => Type::BoolArray(ArrayDims::D2(*l, *w)), - }, - Type::ComplexArray(dims) => match dims { - ArrayDims::D1(_) => Type::Complex(None, IsConst::False), - ArrayDims::D2(l, _) => Type::ComplexArray(ArrayDims::D1(*l)), - ArrayDims::D3(l, w, _) => Type::ComplexArray(ArrayDims::D2(*l, *w)), - }, - Type::FloatArray(dims) => match dims { - ArrayDims::D1(_) => Type::Float(None, IsConst::False), - ArrayDims::D2(l, _) => Type::FloatArray(ArrayDims::D1(*l)), - ArrayDims::D3(l, w, _) => Type::FloatArray(ArrayDims::D2(*l, *w)), - }, - Type::IntArray(dims) => match dims { - ArrayDims::D1(_) => Type::Int(None, IsConst::False), - ArrayDims::D2(l, _) => Type::IntArray(ArrayDims::D1(*l)), - ArrayDims::D3(l, w, _) => Type::IntArray(ArrayDims::D2(*l, *w)), - }, - Type::QubitArray(dims) => match dims { - ArrayDims::D1(_) => Type::Qubit, - ArrayDims::D2(l, _) => Type::QubitArray(ArrayDims::D1(*l)), - ArrayDims::D3(l, w, _) => Type::QubitArray(ArrayDims::D2(*l, *w)), - }, - Type::UIntArray(dims) => match dims { - ArrayDims::D1(_) => Type::UInt(None, IsConst::False), - ArrayDims::D2(l, _) => Type::UIntArray(ArrayDims::D1(*l)), - ArrayDims::D3(l, w, _) => Type::UIntArray(ArrayDims::D2(*l, *w)), - }, - _ => return None, - }; - Some(ty) -} diff --git a/fuzz/fuzz_targets/qasm3.rs b/fuzz/fuzz_targets/qasm3.rs index 3e785f27f9..b4c38dfbcd 100644 --- a/fuzz/fuzz_targets/qasm3.rs +++ b/fuzz/fuzz_targets/qasm3.rs @@ -10,8 +10,8 @@ use libfuzzer_sys::fuzz_target; fn compile(data: &[u8]) { if let Ok(fuzzed_code) = std::str::from_utf8(data) { - let mut resolver = qsc::qasm3::io::InMemorySourceResolver::from_iter([]); - let _ = qsc::qasm3::parser::parse_source(fuzzed_code, "fuzz.qasm", &mut resolver); + let mut resolver = qsc::qasm::io::InMemorySourceResolver::from_iter([]); + let _ = qsc::qasm::parser::parse_source(fuzzed_code, "fuzz.qasm", &mut resolver); } } diff --git a/pip/qsharp/interop/qiskit/backends/backend_base.py b/pip/qsharp/interop/qiskit/backends/backend_base.py index a6b18d8ea2..df79b43df0 100644 --- a/pip/qsharp/interop/qiskit/backends/backend_base.py +++ b/pip/qsharp/interop/qiskit/backends/backend_base.py @@ -181,9 +181,9 @@ def __init__( target_gates -= set(_QISKIT_STDGATES) basis_gates = list(target_gates) - # selt the default options for the exporter + # set the default options for the exporter self._qasm_export_options = { - "includes": ("stdgates.inc",), + "includes": ("stdgates.inc", "qiskit_stdgates.inc",), "alias_classical_registers": False, "allow_aliasing": False, "disable_constants": True, @@ -424,12 +424,15 @@ def _qasm3(self, circuit: QuantumCircuit, **options) -> str: :raises QasmError: If there is an error generating or parsing QASM. """ - + transpiled_circuit = self.transpile(circuit, **options) try: export_options = self._build_qasm_export_options(**options) exporter = Exporter(**export_options) - transpiled_circuit = self.transpile(circuit, **options) qasm3_source = exporter.dumps(transpiled_circuit) + # Qiskit QASM exporter doesn't handle experimental features correctly and always emits + # OPENQASM 3.0; even though switch case is not supported in QASM 3.0, so we bump + # the version to 3.1 for now. + qasm3_source = qasm3_source.replace("OPENQASM 3.0", "OPENQASM 3.1") return qasm3_source except Exception as ex: from .. import QasmError diff --git a/pip/src/interop.rs b/pip/src/interop.rs index e6e7b4d9f0..7fb560eac2 100644 --- a/pip/src/interop.rs +++ b/pip/src/interop.rs @@ -11,11 +11,8 @@ use pyo3::types::{PyDict, PyList}; use qsc::hir::PackageId; use qsc::interpret::output::Receiver; use qsc::interpret::{into_errors, Interpreter}; -use qsc::qasm3::io::{Error, ErrorKind}; -use qsc::qasm3::io::{SourceResolver, SourceResolverContext}; -use qsc::qasm3::{ - qasm_to_program, CompilerConfig, OperationSignature, QasmCompileUnit, QubitSemantics, -}; +use qsc::qasm::io::{SourceResolver, SourceResolverContext}; +use qsc::qasm::{OperationSignature, QubitSemantics}; use qsc::target::Profile; use qsc::{ ast::Package, error::WithSource, interpret, project::FileSystem, LanguageFeatures, SourceMap, @@ -62,7 +59,7 @@ where &mut self.ctx } - fn resolve

    (&mut self, path: P) -> miette::Result<(PathBuf, String), Error> + fn resolve

    (&mut self, path: P) -> miette::Result<(PathBuf, String), qsc::qasm::io::Error> where P: AsRef, { @@ -71,7 +68,7 @@ where let (path, source) = self .fs .read_file(path.as_ref()) - .map_err(|e| Error(ErrorKind::IO(format!("{e}"))))?; + .map_err(|e| qsc::qasm::io::Error(qsc::qasm::io::ErrorKind::IO(e.to_string())))?; Ok(( PathBuf::from(path.as_ref().to_owned()), source.as_ref().to_owned(), @@ -265,47 +262,6 @@ pub(crate) fn compile_qasm3_to_qir( generate_qir_from_ast(entry_expr, &mut interpreter) } -pub(crate) fn compile_qasm, R: SourceResolver>( - source: S, - operation_name: S, - resolver: &mut R, - program_ty: ProgramType, - output_semantics: OutputSemantics, -) -> PyResult { - let parse_result = qsc::qasm3::parse::parse_source( - source, - format!("{}.qasm", operation_name.as_ref()), - resolver, - ) - .map_err(|report| { - // this will only fail if a file cannot be read - // most likely due to a missing file or search path - QasmError::new_err(format!("{report:?}")) - })?; - - if parse_result.has_errors() { - return Err(QasmError::new_err(format_qasm_errors( - parse_result.errors(), - ))); - } - let unit = qasm_to_program( - parse_result.source, - parse_result.source_map, - CompilerConfig::new( - QubitSemantics::Qiskit, - output_semantics.into(), - program_ty.into(), - Some(operation_name.as_ref().into()), - None, - ), - ); - - if unit.has_errors() { - return Err(QasmError::new_err(format_qasm_errors(unit.errors()))); - } - Ok(unit) -} - pub(crate) fn compile_qasm_enriching_errors, R: SourceResolver>( source: S, operation_name: S, @@ -314,18 +270,20 @@ pub(crate) fn compile_qasm_enriching_errors, R: SourceResolver>( output_semantics: OutputSemantics, allow_input_params: bool, ) -> PyResult<(Package, SourceMap, OperationSignature)> { - let unit = compile_qasm( - source, - operation_name, - resolver, - program_ty, - output_semantics, - )?; + let path = format!("{}.qasm", operation_name.as_ref()); + let config = qsc::qasm::CompilerConfig::new( + QubitSemantics::Qiskit, + output_semantics.into(), + program_ty.into(), + Some(operation_name.as_ref().into()), + None, + ); + let unit = qsc::qasm::compile_to_qsharp_ast_with_config(source, path, Some(resolver), config); - if unit.has_errors() { - return Err(QasmError::new_err(format_qasm_errors(unit.errors()))); + let (source_map, errors, package, sig) = unit.into_tuple(); + if !errors.is_empty() { + return Err(QasmError::new_err(format_qasm_errors(errors))); } - let (source_map, _, package, sig) = unit.into_tuple(); let Some(package) = package else { return Err(QasmError::new_err("package should have had value")); }; @@ -507,7 +465,7 @@ fn into_estimation_errors(errors: Vec) -> Vec>) -> String { +pub(crate) fn format_qasm_errors(errors: Vec>) -> String { errors .into_iter() .map(|e| { @@ -549,7 +507,7 @@ fn create_interpreter_from_ast( package_type: PackageType, ) -> Result> { let capabilities = profile.into(); - let (stdid, qasmid, mut store) = qsc::qasm3::package_store_with_qasm(capabilities); + let (stdid, qasmid, mut store) = qsc::qasm::package_store_with_qasm(capabilities); let dependencies = vec![ (PackageId::CORE, None), (stdid, None), diff --git a/pip/src/interpreter.rs b/pip/src/interpreter.rs index 69f62157f4..7cab5dc7fb 100644 --- a/pip/src/interpreter.rs +++ b/pip/src/interpreter.rs @@ -240,12 +240,12 @@ impl OutputSemantics { } } -impl From for qsc::qasm3::OutputSemantics { +impl From for qsc::qasm::OutputSemantics { fn from(output_semantics: OutputSemantics) -> Self { match output_semantics { - OutputSemantics::Qiskit => qsc::qasm3::OutputSemantics::Qiskit, - OutputSemantics::OpenQasm => qsc::qasm3::OutputSemantics::OpenQasm, - OutputSemantics::ResourceEstimation => qsc::qasm3::OutputSemantics::ResourceEstimation, + OutputSemantics::Qiskit => qsc::qasm::OutputSemantics::Qiskit, + OutputSemantics::OpenQasm => qsc::qasm::OutputSemantics::OpenQasm, + OutputSemantics::ResourceEstimation => qsc::qasm::OutputSemantics::ResourceEstimation, } } } @@ -300,12 +300,12 @@ impl ProgramType { } } -impl From for qsc::qasm3::ProgramType { +impl From for qsc::qasm::ProgramType { fn from(output_semantics: ProgramType) -> Self { match output_semantics { - ProgramType::File => qsc::qasm3::ProgramType::File, - ProgramType::Operation => qsc::qasm3::ProgramType::Operation, - ProgramType::Fragments => qsc::qasm3::ProgramType::Fragments, + ProgramType::File => qsc::qasm::ProgramType::File, + ProgramType::Operation => qsc::qasm::ProgramType::Operation, + ProgramType::Fragments => qsc::qasm::ProgramType::Fragments, } } } diff --git a/pip/tests-integration/interop_qiskit/test_circuits/__init__.py b/pip/tests-integration/interop_qiskit/test_circuits/__init__.py index b8acfdb654..0cb3408937 100644 --- a/pip/tests-integration/interop_qiskit/test_circuits/__init__.py +++ b/pip/tests-integration/interop_qiskit/test_circuits/__init__.py @@ -32,7 +32,7 @@ def generate_repro_information( return message try: - qsharp_source = backend.qsharp(circuit, **options) + qsharp_source = backend._qsharp(circuit, **options) message += "Q# source:" message += "\n" message += str(qsharp_source) diff --git a/pip/tests-integration/interop_qiskit/test_gateset_qasm.py b/pip/tests-integration/interop_qiskit/test_gateset_qasm.py index 68245e33ce..4037d91f16 100644 --- a/pip/tests-integration/interop_qiskit/test_gateset_qasm.py +++ b/pip/tests-integration/interop_qiskit/test_gateset_qasm.py @@ -21,12 +21,13 @@ def run_transpile_test( options["optimization_level"] = 0 info = QSharpBackend()._qasm3(circuit, **options) lines = info.splitlines() - # remove the first four lines, which are the header + # remove the first five lines, which are the header # OPENQASM 3.0; # include "stdgates.inc"; + # include "qiskit_stdgates.inc"; # bit[3] c; # qubit[3] q; - remaining_lines = lines[4:] + remaining_lines = lines[5:] result = "\n".join(remaining_lines) assert result == expected_output diff --git a/pip/tests-integration/interop_qiskit/test_qir.py b/pip/tests-integration/interop_qiskit/test_qir.py index bc48475f83..f937dee73f 100644 --- a/pip/tests-integration/interop_qiskit/test_qir.py +++ b/pip/tests-integration/interop_qiskit/test_qir.py @@ -168,7 +168,7 @@ def __init__(self): options = { "search_path": get_resource_path(), - "includes": ("stdgates.inc", "custom_intrinsics.inc"), + "includes": ("stdgates.inc", "qiskit_stdgates.inc", "custom_intrinsics.inc"), } backend = QSharpBackend(target_profile=target_profile, target=target) @@ -199,7 +199,7 @@ def __init__(self): options = { "search_path": get_resource_path(), - "includes": ("stdgates.inc", "custom_intrinsics.inc"), + "includes": ("stdgates.inc", "qiskit_stdgates.inc", "custom_intrinsics.inc"), } backend = QSharpBackend(target_profile=target_profile, target=target) diff --git a/pip/tests-integration/interop_qiskit/test_re.py b/pip/tests-integration/interop_qiskit/test_re.py index d4dee46571..b7b01d716e 100644 --- a/pip/tests-integration/interop_qiskit/test_re.py +++ b/pip/tests-integration/interop_qiskit/test_re.py @@ -69,8 +69,8 @@ def test_estimate_qiskit_rgqft_multiplier() -> None: { "numQubits": 16, "tCount": 90, - "rotationCount": 1002, - "rotationDepth": 680, + "rotationCount": 972, + "rotationDepth": 666, "cczCount": 0, "ccixCount": 0, "measurementCount": 0, @@ -94,8 +94,8 @@ def test_estimate_qiskit_rgqft_multiplier_in_threadpool() -> None: { "numQubits": 16, "tCount": 90, - "rotationCount": 1002, - "rotationDepth": 680, + "rotationCount": 972, + "rotationDepth": 666, "cczCount": 0, "ccixCount": 0, "measurementCount": 0, diff --git a/pip/tests-integration/interop_qiskit/test_run_sim.py b/pip/tests-integration/interop_qiskit/test_run_sim.py index ad3c8c475c..435e952883 100644 --- a/pip/tests-integration/interop_qiskit/test_run_sim.py +++ b/pip/tests-integration/interop_qiskit/test_run_sim.py @@ -22,6 +22,21 @@ ) +# Convert the circuit to QASM3 and back to a backend. +# Then load the QASM3 and convert it back to a circuit. +# This is to ensure that the QASM3 conversion is semantically correct. +# We must remove the include "qiskit_stdgates.inc" line from the QASM3 +# since the qiskit qasm loader can't support any non stdgates.inc include files. +def round_trip_circuit(circuit, backend): + qasm3 = backend._qasm3(circuit) + # remove any lines that contain: include "qiskit_stdgates.inc"; + qasm3 = "\n".join( + line for line in qasm3.splitlines() if "qiskit_stdgates.inc" not in line + ) + circuit = from_qasm3(qasm3) + return circuit + + @pytest.mark.skipif(not QISKIT_AVAILABLE, reason=SKIP_REASON) def test_run_smoke() -> None: circuit = QuantumCircuit(2, 2) @@ -50,8 +65,7 @@ def test_get_counts_matches_qiskit_simulator(): backend = QSharpBackend(target_profile=target_profile) try: - qasm3 = backend._qasm3(circuit) - circuit = from_qasm3(qasm3) + circuit = round_trip_circuit(circuit, backend) aersim = AerSimulator() job = aersim.run(circuit, shots=5) @@ -125,8 +139,7 @@ def test_get_counts_matches_qiskit_simulator_multiple_circuits(): backend = QSharpBackend(target_profile=target_profile) try: - qasm3 = backend._qasm3(circuit) - circuit = from_qasm3(qasm3) + circuit = round_trip_circuit(circuit, backend) aersim = AerSimulator() job = aersim.run([circuit, circuit2], shots=5) From b82e5a95a87af69a279e408fa02b609b7d5c8b34 Mon Sep 17 00:00:00 2001 From: orpuente-MS <156957451+orpuente-MS@users.noreply.github.com> Date: Wed, 9 Apr 2025 16:10:44 -0700 Subject: [PATCH 095/108] Cleanup TODO items (#2285) Remove TODO comments from code and add them as Postponed items to the tracking PR. --- compiler/qsc_qasm3/src/compiler.rs | 9 +++---- compiler/qsc_qasm3/src/parser/ast.rs | 5 ---- compiler/qsc_qasm3/src/parser/expr.rs | 27 +++++-------------- .../qsc_qasm3/src/semantic/ast/const_eval.rs | 18 +++++-------- compiler/qsc_qasm3/src/semantic/lowerer.rs | 1 - 5 files changed, 16 insertions(+), 44 deletions(-) diff --git a/compiler/qsc_qasm3/src/compiler.rs b/compiler/qsc_qasm3/src/compiler.rs index b4bd31e16a..d60c962fb0 100644 --- a/compiler/qsc_qasm3/src/compiler.rs +++ b/compiler/qsc_qasm3/src/compiler.rs @@ -243,8 +243,8 @@ impl QasmCompiler { let ast_ty = map_qsharp_type_to_ast_ty(&output_ty); signature.output = format!("{output_ty}"); - // TODO: This can create a collision on multiple compiles when interactive - // We also have issues with the new entry point inference logic + // This can create a collision on multiple compiles when interactive + // We also have issues with the new entry point inference logic. let input_desc = input .iter() .flat_map(|s| { @@ -873,11 +873,8 @@ impl QasmCompiler { let symbol = &self.symbols[stmt.symbol_id]; // input decls should have been pushed to symbol table, - // but should not be the stmts list. - // TODO: This may be an issue for tooling as there isn't a way to have a forward - // declared varible in Q#. + // but should not be in the stmts list. if symbol.io_kind != IOKind::Output { - //self.push_semantic_error(SemanticErrorKind::InvalidIODeclaration(stmt.span)); return None; } diff --git a/compiler/qsc_qasm3/src/parser/ast.rs b/compiler/qsc_qasm3/src/parser/ast.rs index 6f974e5180..f96bcad05e 100644 --- a/compiler/qsc_qasm3/src/parser/ast.rs +++ b/compiler/qsc_qasm3/src/parser/ast.rs @@ -16,11 +16,6 @@ use std::{ rc::Rc, }; -// TODO: Profile this with iai-callgrind in a large OpenQASM3 -// sample to verify that is actually faster than using Vec. -// Even though Box uses less stack space, it reduces cache -// locality, because now you need to be jumping around in -// memory to read contiguous elements of a list. /// An alternative to `Vec` that uses less stack space. pub(crate) type List = Box<[Box]>; diff --git a/compiler/qsc_qasm3/src/parser/expr.rs b/compiler/qsc_qasm3/src/parser/expr.rs index 4d29649c71..6c06d521ac 100644 --- a/compiler/qsc_qasm3/src/parser/expr.rs +++ b/compiler/qsc_qasm3/src/parser/expr.rs @@ -51,23 +51,12 @@ enum OpKind { Index, } -// TODO: This seems to be an unnecessary wrapper. -// OpName::Keyword is never used. -// Consider removing. #[derive(Clone, Copy)] enum OpName { Token(TokenKind), Keyword(Keyword), } -// TODO: This seems to be an unnecessary wrapper. -// We ended up removing the OpContext::Stmt variant. -// Consider removing. -#[derive(Clone, Copy)] -enum OpContext { - Precedence(u8), -} - #[derive(Clone, Copy)] enum Assoc { Left, @@ -77,18 +66,18 @@ enum Assoc { const RANGE_PRECEDENCE: u8 = 1; pub(super) fn expr(s: &mut ParserContext) -> Result { - expr_op(s, OpContext::Precedence(0)) + expr_op(s, 0) } pub(super) fn expr_with_lhs(s: &mut ParserContext, lhs: Expr) -> Result { - expr_op_with_lhs(s, OpContext::Precedence(0), lhs) + expr_op_with_lhs(s, 0, lhs) } -fn expr_op(s: &mut ParserContext, context: OpContext) -> Result { +fn expr_op(s: &mut ParserContext, min_precedence: u8) -> Result { let lo = s.peek().span.lo; let lhs = if let Some(op) = prefix_op(op_name(s)) { s.advance(); - let rhs = expr_op(s, OpContext::Precedence(op.precedence))?; + let rhs = expr_op(s, op.precedence)?; Expr { span: s.span(lo), kind: Box::new(ExprKind::UnaryOp(UnaryOpExpr { @@ -100,14 +89,12 @@ fn expr_op(s: &mut ParserContext, context: OpContext) -> Result { expr_base(s)? }; - expr_op_with_lhs(s, context, lhs) + expr_op_with_lhs(s, min_precedence, lhs) } -fn expr_op_with_lhs(s: &mut ParserContext, context: OpContext, mut lhs: Expr) -> Result { +fn expr_op_with_lhs(s: &mut ParserContext, min_precedence: u8, mut lhs: Expr) -> Result { let lo = lhs.span.lo; - let OpContext::Precedence(min_precedence) = context; - while let Some(op) = infix_op(op_name(s)) { if op.precedence < min_precedence { break; @@ -117,7 +104,7 @@ fn expr_op_with_lhs(s: &mut ParserContext, context: OpContext, mut lhs: Expr) -> let kind = match op.kind { OpKind::Binary(kind, assoc) => { let precedence = next_precedence(op.precedence, assoc); - let rhs = expr_op(s, OpContext::Precedence(precedence))?; + let rhs = expr_op(s, precedence)?; Box::new(ExprKind::BinaryOp(BinaryOpExpr { op: kind, lhs, rhs })) } OpKind::Funcall => { diff --git a/compiler/qsc_qasm3/src/semantic/ast/const_eval.rs b/compiler/qsc_qasm3/src/semantic/ast/const_eval.rs index 03962d0076..3254c16899 100644 --- a/compiler/qsc_qasm3/src/semantic/ast/const_eval.rs +++ b/compiler/qsc_qasm3/src/semantic/ast/const_eval.rs @@ -299,7 +299,6 @@ impl BinaryOpExpr { } Type::Float(..) => { rewrap_lit!((lhs, rhs), (Float(lhs), Float(rhs)), { - // TODO: we need to issue the same lint in Q#. #[allow(clippy::float_cmp)] Bool(lhs == rhs) }) @@ -321,7 +320,6 @@ impl BinaryOpExpr { } Type::Float(..) => { rewrap_lit!((lhs, rhs), (Float(lhs), Float(rhs)), { - // TODO: we need to issue the same lint in Q#. #[allow(clippy::float_cmp)] Bool(lhs != rhs) }) @@ -586,12 +584,11 @@ fn cast_to_int(cast: &Cast, ctx: &mut Lowerer) -> Option { Type::BitArray(..) => { rewrap_lit!(lit, Bitstring(val, _), Int(i64::try_from(val).ok()?)) } - // TODO: UInt Overflowing behavior. - // This is tricky because the inner repersentation - // already is a i64. Therefore, there is nothing to do? + // UInt Overflowing behavior. + // This is tricky because the inner representation of UInt + // is already an i64. Therefore, there is nothing to do. Type::Int(..) | Type::UInt(..) => Some(lit), Type::Float(..) => rewrap_lit!(lit, Float(val), { - // TODO: we need to issue the same lint in Q#. #[allow(clippy::cast_possible_truncation)] Int(val as i64) }), @@ -616,13 +613,11 @@ fn cast_to_uint(cast: &Cast, ctx: &mut Lowerer) -> Option { Type::BitArray(..) => { rewrap_lit!(lit, Bitstring(val, _), Int(i64::try_from(val).ok()?)) } - // TODO: Int Overflowing behavior. - // This is tricky because the inner representation - // is a i64. Therefore, even we might end with the - // same result anyways. Need to think through this. + // UInt Overflowing behavior. + // This is tricky because the inner representation of UInt + // is already an i64. Therefore, there is nothing to do. Type::Int(..) | Type::UInt(..) => Some(lit), Type::Float(..) => rewrap_lit!(lit, Float(val), { - // TODO: we need to issue the same lint in Q#. #[allow(clippy::cast_possible_truncation)] Int(val as i64) }), @@ -644,7 +639,6 @@ fn cast_to_float(cast: &Cast, ctx: &mut Lowerer) -> Option { match &cast.expr.ty { Type::Bool(..) => rewrap_lit!(lit, Bool(val), Float(if val { 1.0 } else { 0.0 })), Type::Int(..) | Type::UInt(..) => rewrap_lit!(lit, Int(val), { - // TODO: we need to issue the same lint in Q#. #[allow(clippy::cast_precision_loss)] Float(safe_i64_to_f64(val)?) }), diff --git a/compiler/qsc_qasm3/src/semantic/lowerer.rs b/compiler/qsc_qasm3/src/semantic/lowerer.rs index 9f071fab28..615f706858 100644 --- a/compiler/qsc_qasm3/src/semantic/lowerer.rs +++ b/compiler/qsc_qasm3/src/semantic/lowerer.rs @@ -3011,7 +3011,6 @@ impl Lowerer { ) } - // TODO: which these are parsed as different types, they are effectively the same fn lower_index_element(&mut self, index: &syntax::IndexElement) -> semantic::IndexElement { match index { syntax::IndexElement::DiscreteSet(set) => { From f9ccdf5b84d594a7fcda95fa7d60e9325ccbb993 Mon Sep 17 00:00:00 2001 From: orpuente-MS <156957451+orpuente-MS@users.noreply.github.com> Date: Wed, 9 Apr 2025 18:15:54 -0700 Subject: [PATCH 096/108] Review errors in lexer, parser, lowerer, and compiler (#2286) --- compiler/qsc_qasm3/src/compile/tests.rs | 48 -- compiler/qsc_qasm3/src/compiler.rs | 40 +- compiler/qsc_qasm3/src/compiler/error.rs | 41 ++ compiler/qsc_qasm3/src/lib.rs | 22 +- compiler/qsc_qasm3/src/semantic/error.rs | 474 ++++++------------ compiler/qsc_qasm3/src/semantic/lowerer.rs | 48 +- compiler/qsc_qasm3/src/semantic/tests.rs | 24 +- .../qsc_qasm3/src/semantic/tests/decls.rs | 12 +- .../src/semantic/tests/decls/angle.rs | 22 +- .../src/semantic/tests/decls/duration.rs | 4 +- .../src/semantic/tests/decls/extern_decl.rs | 4 +- .../src/semantic/tests/decls/float.rs | 10 +- .../src/semantic/tests/decls/stretch.rs | 8 +- .../expression/implicit_cast_from_angle.rs | 40 +- .../expression/implicit_cast_from_bit.rs | 4 +- .../expression/implicit_cast_from_bitarray.rs | 256 +++++----- .../expression/implicit_cast_from_float.rs | 8 +- .../src/semantic/tests/statements/box_stmt.rs | 10 +- .../semantic/tests/statements/break_stmt.rs | 12 +- .../tests/statements/continue_stmt.rs | 12 +- .../src/semantic/tests/statements/for_stmt.rs | 4 +- .../src/semantic/tests/statements/if_stmt.rs | 8 +- .../semantic/tests/statements/switch_stmt.rs | 2 +- compiler/qsc_qasm3/src/tests/declaration.rs | 8 +- .../qsc_qasm3/src/tests/declaration/def.rs | 21 +- .../qsc_qasm3/src/tests/declaration/gate.rs | 12 +- .../tests/declaration/io/explicit_input.rs | 2 +- .../src/tests/expression/function_call.rs | 16 +- .../qsc_qasm3/src/tests/expression/ident.rs | 10 +- .../expression/implicit_cast_from_bit.rs | 2 +- .../expression/implicit_cast_from_bitarray.rs | 8 +- .../expression/implicit_cast_from_float.rs | 4 +- .../qsc_qasm3/src/tests/expression/indexed.rs | 2 +- .../qsc_qasm3/src/tests/expression/unary.rs | 4 +- compiler/qsc_qasm3/src/tests/scopes.rs | 2 +- .../src/tests/statement/annotation.rs | 2 +- .../src/tests/statement/const_eval.rs | 24 +- .../qsc_qasm3/src/tests/statement/for_loop.rs | 2 +- .../src/tests/statement/gate_call.rs | 16 +- .../qsc_qasm3/src/tests/statement/if_stmt.rs | 2 +- .../qsc_qasm3/src/tests/statement/include.rs | 69 +++ .../qsc_qasm3/src/tests/statement/measure.rs | 4 +- .../src/tests/statement/while_loop.rs | 2 +- .../interop_qiskit/test_qir.py | 2 +- 44 files changed, 591 insertions(+), 736 deletions(-) delete mode 100644 compiler/qsc_qasm3/src/compile/tests.rs create mode 100644 compiler/qsc_qasm3/src/compiler/error.rs diff --git a/compiler/qsc_qasm3/src/compile/tests.rs b/compiler/qsc_qasm3/src/compile/tests.rs deleted file mode 100644 index 72b0210d14..0000000000 --- a/compiler/qsc_qasm3/src/compile/tests.rs +++ /dev/null @@ -1,48 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. - -use crate::tests::{compile_all_fragments, print_compilation_errors}; -use miette::Report; - -#[test] -fn programs_with_includes_with_includes_can_be_compiled() -> miette::Result<(), Vec> { - let source0 = r#"include "stdgates.inc"; - include "source1.qasm"; - "#; - let source1 = r#"include "source2.qasm"; - "#; - let source2 = ""; - let all_sources = [ - ("source0.qasm".into(), source0.into()), - ("source1.qasm".into(), source1.into()), - ("source2.qasm".into(), source2.into()), - ]; - - let unit = compile_all_fragments("source0.qasm", all_sources)?; - print_compilation_errors(&unit); - assert!(!unit.has_errors()); - Ok(()) -} - -#[test] -fn including_stdgates_multiple_times_causes_symbol_redifintion_errors( -) -> miette::Result<(), Vec> { - let source0 = r#"include "stdgates.inc"; - include "source1.qasm"; - "#; - let source1 = r#"include "source2.qasm"; - "#; - let source2 = r#"include "stdgates.inc";"#; - let all_sources = [ - ("source0.qasm".into(), source0.into()), - ("source1.qasm".into(), source1.into()), - ("source2.qasm".into(), source2.into()), - ]; - - let unit = compile_all_fragments("source0.qasm", all_sources)?; - assert!(unit.has_errors()); - for error in unit.errors() { - assert!(error.to_string().contains("Redefined symbol: ")); - } - Ok(()) -} diff --git a/compiler/qsc_qasm3/src/compiler.rs b/compiler/qsc_qasm3/src/compiler.rs index d60c962fb0..fc10679dd1 100644 --- a/compiler/qsc_qasm3/src/compiler.rs +++ b/compiler/qsc_qasm3/src/compiler.rs @@ -1,9 +1,12 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. +pub mod error; + use core::f64; use std::{path::Path, rc::Rc}; +use error::CompilerErrorKind; use num_bigint::BigInt; use qsc_data_structures::span::Span; use qsc_frontend::{compile::SourceMap, error::WithSource}; @@ -40,7 +43,6 @@ use crate::{ }, symbols::{IOKind, Symbol, SymbolId, SymbolTable}, types::{promote_types, ArrayDimensions, Type}, - SemanticErrorKind, }, CompilerConfig, OperationSignature, OutputSemantics, ProgramType, QasmCompileUnit, QubitSemantics, @@ -198,8 +200,8 @@ impl QasmCompiler { if matches!(input.qsharp_ty, crate::types::Type::Angle(..)) { let message = "use `float` types for passing input, using `angle` types".to_string(); - let kind = SemanticErrorKind::NotSupported(message, input.span); - self.push_semantic_error(kind); + let kind = CompilerErrorKind::NotSupported(message, input.span); + self.push_compiler_error(kind); } } } @@ -365,8 +367,8 @@ impl QasmCompiler { output_ty } else { if is_qiskit { - let kind = SemanticErrorKind::QiskitEntryPointMissingOutput(whole_span); - self.push_semantic_error(kind); + let kind = CompilerErrorKind::QiskitEntryPointMissingOutput(whole_span); + self.push_compiler_error(kind); } crate::types::Type::Tuple(vec![]) }; @@ -397,7 +399,7 @@ impl QasmCompiler { ) { for annotation in &stmt.annotations { - self.push_semantic_error(SemanticErrorKind::InvalidAnnotationTarget( + self.push_compiler_error(CompilerErrorKind::InvalidAnnotationTarget( annotation.span, )); } @@ -814,12 +816,12 @@ impl QasmCompiler { semast::GateModifierKind::Ctrl(num_ctrls) => { // remove the last n qubits from the qubit list if qubits.len() < *num_ctrls as usize { - let kind = SemanticErrorKind::InvalidNumberOfQubitArgs( + let kind = CompilerErrorKind::InvalidNumberOfQubitArgs( *num_ctrls as usize, qubits.len(), modifier.span, ); - self.push_semantic_error(kind); + self.push_compiler_error(kind); return None; } let ctrl = qubits.split_off(qubits.len().saturating_sub(*num_ctrls as usize)); @@ -834,12 +836,12 @@ impl QasmCompiler { semast::GateModifierKind::NegCtrl(num_ctrls) => { // remove the last n qubits from the qubit list if qubits.len() < *num_ctrls as usize { - let kind = SemanticErrorKind::InvalidNumberOfQubitArgs( + let kind = CompilerErrorKind::InvalidNumberOfQubitArgs( *num_ctrls as usize, qubits.len(), modifier.span, ); - self.push_semantic_error(kind); + self.push_compiler_error(kind); return None; } let ctrl = qubits.split_off(qubits.len().saturating_sub(*num_ctrls as usize)); @@ -986,7 +988,7 @@ impl QasmCompiler { annotation.span, )), _ => { - self.push_semantic_error(SemanticErrorKind::UnknownAnnotation( + self.push_compiler_error(CompilerErrorKind::UnknownAnnotation( format!("@{}", annotation.identifier), annotation.span, )); @@ -1441,7 +1443,7 @@ impl QasmCompiler { // but we can still create an identifier for the hardware qubit // and let the rest of the containing expression compile to // catch any other errors - let message = "Hardware qubit operands"; + let message = "hardware qubit operands"; self.push_unsupported_error_message(message, op.span); build_path_ident_expr(hw.name.clone(), hw.span, op.span) } @@ -1523,7 +1525,7 @@ impl QasmCompiler { _unit: TimeUnit, span: Span, ) -> qsast::Expr { - self.push_unsupported_error_message("Timing literals", span); + self.push_unsupported_error_message("timing literals", span); err_expr(span) } @@ -1560,19 +1562,19 @@ impl QasmCompiler { /// Pushes an unsupported error with the supplied message. pub fn push_unsupported_error_message>(&mut self, message: S, span: Span) { - let kind = SemanticErrorKind::NotSupported(message.as_ref().to_string(), span); - self.push_semantic_error(kind); + let kind = CompilerErrorKind::NotSupported(message.as_ref().to_string(), span); + self.push_compiler_error(kind); } /// Pushes an unimplemented error with the supplied message. pub fn push_unimplemented_error_message>(&mut self, message: S, span: Span) { - let kind = SemanticErrorKind::Unimplemented(message.as_ref().to_string(), span); - self.push_semantic_error(kind); + let kind = CompilerErrorKind::Unimplemented(message.as_ref().to_string(), span); + self.push_compiler_error(kind); } /// Pushes a semantic error with the given kind. - pub fn push_semantic_error(&mut self, kind: SemanticErrorKind) { - let kind = crate::ErrorKind::Semantic(crate::semantic::Error(kind)); + pub fn push_compiler_error(&mut self, kind: CompilerErrorKind) { + let kind = crate::ErrorKind::Compiler(error::Error(kind)); let error = crate::Error(kind); let error = WithSource::from_map(&self.source_map, error); self.errors.push(error); diff --git a/compiler/qsc_qasm3/src/compiler/error.rs b/compiler/qsc_qasm3/src/compiler/error.rs new file mode 100644 index 0000000000..ef665717a5 --- /dev/null +++ b/compiler/qsc_qasm3/src/compiler/error.rs @@ -0,0 +1,41 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. +use miette::Diagnostic; +use qsc_data_structures::span::Span; +use thiserror::Error; + +#[derive(Clone, Debug, Diagnostic, Eq, Error, PartialEq)] +#[error(transparent)] +#[diagnostic(transparent)] +pub struct Error(pub CompilerErrorKind); + +/// Represents the kind of semantic error that occurred during compilation of a QASM file(s). +/// For the most part, these errors are fatal and prevent compilation and are +/// safety checks to ensure that the QASM code is valid. +#[derive(Clone, Debug, Diagnostic, Eq, Error, PartialEq)] +pub enum CompilerErrorKind { + #[error("annotations only valid on def and gate statements")] + #[diagnostic(code("Qsc.Qasm3.Compiler.InvalidAnnotationTarget"))] + InvalidAnnotationTarget(#[label] Span), + #[error("gate expects {0} qubit arguments, but {1} were provided")] + #[diagnostic(code("Qsc.Qasm3.Compiler.InvalidNumberOfQubitArgs"))] + InvalidNumberOfQubitArgs(usize, usize, #[label] Span), + #[error("{0} are not supported")] + #[diagnostic(code("Qsc.Qasm3.Compiler.NotSupported"))] + NotSupported(String, #[label] Span), + #[error("Qiskit circuits must have output registers")] + #[diagnostic(code("Qsc.Qasm3.Compiler.QiskitEntryPointMissingOutput"))] + QiskitEntryPointMissingOutput(#[label] Span), + #[error("unexpected annotation: {0}")] + #[diagnostic(code("Qsc.Qasm3.Compiler.UnknownAnnotation"))] + UnknownAnnotation(String, #[label] Span), + #[error("this statement is not yet handled during OpenQASM 3 import: {0}")] + #[diagnostic(code("Qsc.Qasm3.Compiler.Unimplemented"))] + Unimplemented(String, #[label] Span), +} + +impl From for crate::Error { + fn from(val: Error) -> Self { + crate::Error(crate::ErrorKind::Compiler(val)) + } +} diff --git a/compiler/qsc_qasm3/src/lib.rs b/compiler/qsc_qasm3/src/lib.rs index a4aa09c757..1536cfb12a 100644 --- a/compiler/qsc_qasm3/src/lib.rs +++ b/compiler/qsc_qasm3/src/lib.rs @@ -35,11 +35,6 @@ use thiserror::Error; pub struct Error(pub ErrorKind); impl Error { - #[must_use] - pub fn with_offset(self, offset: u32) -> Self { - Self(self.0.with_offset(offset)) - } - #[must_use] pub fn is_syntax_error(&self) -> bool { matches!(self.0, ErrorKind::Parse(_, _)) @@ -61,6 +56,9 @@ impl Error { #[derive(Clone, Debug, Diagnostic, Eq, Error, PartialEq)] #[error(transparent)] pub enum ErrorKind { + #[error(transparent)] + #[diagnostic(transparent)] + Compiler(#[from] crate::compiler::error::Error), #[error(transparent)] #[diagnostic(transparent)] IO(#[from] crate::io::Error), @@ -81,20 +79,6 @@ pub enum ErrorKind { OldIO(String), } -impl ErrorKind { - fn with_offset(self, offset: u32) -> Self { - match self { - ErrorKind::IO(error) => Self::IO(error), - ErrorKind::Parse(error, span) => Self::Parse(error, span + offset), - ErrorKind::Parser(error) => Self::Parser(error.with_offset(offset)), - ErrorKind::Semantic(error) => Self::Semantic(error.with_offset(offset)), - ErrorKind::ConstEval(error) => Self::ConstEval(error), - ErrorKind::NotFound(error) => Self::NotFound(error), - ErrorKind::OldIO(error) => Self::OldIO(error), - } - } -} - /// Qubit semantics differ between Q# and Qiskit. This enum is used to /// specify which semantics to use when compiling QASM to Q#. /// diff --git a/compiler/qsc_qasm3/src/semantic/error.rs b/compiler/qsc_qasm3/src/semantic/error.rs index ccbe28a2a6..09dd56a24d 100644 --- a/compiler/qsc_qasm3/src/semantic/error.rs +++ b/compiler/qsc_qasm3/src/semantic/error.rs @@ -10,14 +10,7 @@ use thiserror::Error; #[diagnostic(transparent)] pub struct Error(pub SemanticErrorKind); -impl Error { - #[must_use] - pub fn with_offset(self, offset: u32) -> Self { - Self(self.0.with_offset(offset)) - } -} - -/// Represents the kind of semantic error that occurred during compilation of a QASM file(s). +/// Represents the kind of semantic error that occurred during lowering of a QASM file(s). /// For the most part, these errors are fatal and prevent compilation and are /// safety checks to ensure that the QASM code is valid. /// @@ -26,399 +19,216 @@ impl Error { /// - it is missing many language features #[derive(Clone, Debug, Diagnostic, Eq, Error, PartialEq)] pub enum SemanticErrorKind { - #[error("Array literals are only allowed in classical declarations.")] - #[diagnostic(code("Qsc.Qasm3.Compile.ArrayLiteralInNonClassicalDecl"))] - ArrayLiteralInNonClassicalDecl(#[label] Span), - #[error("{0} must fit in a u32")] - #[diagnostic(code("Qsc.Qasm3.Compile.ExprMustFitInU32"))] - ExprMustFitInU32(String, #[label] Span), - #[error("{0} must be a const expression")] - #[diagnostic(code("Qsc.Qasm3.Compile.ExprMustBeConst"))] - ExprMustBeConst(String, #[label] Span), - #[error("Annotation missing target statement.")] - #[diagnostic(code("Qsc.Qasm3.Compile.AnnotationWithoutStatement"))] + #[error("annotation missing target statement")] + #[diagnostic(code("Qsc.Qasm3.Lowerer.AnnotationWithoutStatement"))] AnnotationWithoutStatement(#[label] Span), + #[error("array literals are only allowed in classical declarations")] + #[diagnostic(code("Qsc.Qasm3.Lowerer.ArrayLiteralInNonClassicalDecl"))] + ArrayLiteralInNonClassicalDecl(#[label] Span), + #[error("array size must be a non-negative integer const expression")] + #[diagnostic(code("Qsc.Qasm3.Lowerer.ArraySizeMustBeNonNegativeConstExpr"))] + ArraySizeMustBeNonNegativeConstExpr(#[label] Span), #[error("calibration statements are not supported: {0}")] - #[diagnostic(code("Qsc.Qasm3.Compile.CalibrationsNotSupported"))] + #[diagnostic(code("Qsc.Qasm3.Lowerer.CalibrationsNotSupported"))] CalibrationsNotSupported(String, #[label] Span), - #[error("Cannot alias type {0}. Only qubit and qubit[] can be aliased.")] - #[diagnostic(code("Qsc.Qasm3.Compile.CannotAliasType"))] + #[error("cannot alias type {0}. Only qubit and qubit[] can be aliased")] + #[diagnostic(code("Qsc.Qasm3.Lowerer.CannotAliasType"))] CannotAliasType(String, #[label] Span), - #[error("Cannot apply operator {0} to types {1} and {2}.")] - #[diagnostic(code("Qsc.Qasm3.Compile.CannotApplyOperatorToTypes"))] + #[error("cannot apply operator {0} to types {1} and {2}")] + #[diagnostic(code("Qsc.Qasm3.Lowerer.CannotApplyOperatorToTypes"))] CannotApplyOperatorToTypes(String, String, String, #[label] Span), - #[error("Cannot assign a value of {0} type to a classical variable of {1} type.")] - #[diagnostic(code("Qsc.Qasm3.Compile.CannotAssignToType"))] + #[error("cannot assign a value of {0} type to a classical variable of {1} type")] + #[diagnostic(code("Qsc.Qasm3.Lowerer.CannotAssignToType"))] CannotAssignToType(String, String, #[label] Span), - #[error("Cannot call an expression that is not a function.")] - #[diagnostic(code("Qsc.Qasm3.Compile.CannotCallNonFunction"))] + #[error("cannot call an expression that is not a function")] + #[diagnostic(code("Qsc.Qasm3.Lowerer.CannotCallNonFunction"))] CannotCallNonFunction(#[label] Span), - #[error("Cannot call a gate that is not a gate.")] - #[diagnostic(code("Qsc.Qasm3.Compile.CannotCallNonGate"))] + #[error("cannot call a gate that is not a gate")] + #[diagnostic(code("Qsc.Qasm3.Lowerer.CannotCallNonGate"))] CannotCallNonGate(#[label] Span), - #[error("Cannot cast expression of type {0} to type {1}")] - #[diagnostic(code("Qsc.Qasm3.Compile.CannotCast"))] + #[error("cannot cast expression of type {0} to type {1}")] + #[diagnostic(code("Qsc.Qasm3.Lowerer.CannotCast"))] CannotCast(String, String, #[label] Span), - #[error("Cannot cast literal expression of type {0} to type {1}")] - #[diagnostic(code("Qsc.Qasm3.Compile.CannotCastLiteral"))] + #[error("cannot cast literal expression of type {0} to type {1}")] + #[diagnostic(code("Qsc.Qasm3.Lowerer.CannotCastLiteral"))] CannotCastLiteral(String, String, #[label] Span), - #[error("Cannot index variables of type {0}")] - #[diagnostic(code("Qsc.Qasm3.Compile.CannotIndexType"))] + #[error("cannot index variables of type {0}")] + #[diagnostic(code("Qsc.Qasm3.Lowerer.CannotIndexType"))] CannotIndexType(String, #[label] Span), - #[error("Cannot update const variable {0}")] - #[diagnostic(help("mutable variables must be declared without the keyword `const`."))] - #[diagnostic(code("Qsc.Qasm3.Compile.CannotUpdateConstVariable"))] + #[error("cannot update const variable {0}")] + #[diagnostic(help("mutable variables must be declared without the keyword `const`"))] + #[diagnostic(code("Qsc.Qasm3.Lowerer.CannotUpdateConstVariable"))] CannotUpdateConstVariable(String, #[label] Span), - #[error("Cannot cast expression of type {0} to type {1} as it would cause truncation.")] - #[diagnostic(code("Qsc.Qasm3.Compile.CastWouldCauseTruncation"))] + #[error("cannot cast expression of type {0} to type {1} as it would cause truncation")] + #[diagnostic(code("Qsc.Qasm3.Lowerer.CastWouldCauseTruncation"))] CastWouldCauseTruncation(String, String, #[label] Span), #[error("invalid classical statement in box")] - #[diagnostic(code("Qsc.Qasm3.Compile.ClassicalStmtInBox"))] + #[diagnostic(code("Qsc.Qasm3.Lowerer.ClassicalStmtInBox"))] ClassicalStmtInBox(#[label] Span), - #[error("Complex numbers in assignment binary expressions are not yet supported.")] - #[diagnostic(code("Qsc.Qasm3.Compile.ComplexBinaryAssignment"))] + #[error("complex numbers in assignment binary expressions are not yet supported")] + #[diagnostic(code("Qsc.Qasm3.Lowerer.ComplexBinaryAssignment"))] ComplexBinaryAssignment(#[label] Span), - #[error("Designator must be a positive literal integer.")] - #[diagnostic(code("Qsc.Qasm3.Compile.DesignatorMustBePositiveIntLiteral"))] + #[error("designator must be a positive literal integer")] + #[diagnostic(code("Qsc.Qasm3.Lowerer.DesignatorMustBePositiveIntLiteral"))] DesignatorMustBePositiveIntLiteral(#[label] Span), - #[error("Type width must be a positive integer const expression.")] - #[diagnostic(code("Qsc.Qasm3.Compile.TypeWidthMustBePositiveIntConstExpr"))] - TypeWidthMustBePositiveIntConstExpr(#[label] Span), - #[error("Array size must be a non-negative integer const expression.")] - #[diagnostic(code("Qsc.Qasm3.Compile.ArraySizeMustBeNonNegativeConstExpr"))] - ArraySizeMustBeNonNegativeConstExpr(#[label] Span), - #[error("Def declarations must be done in global scope.")] - #[diagnostic(code("Qsc.Qasm3.Compile.DefDeclarationInNonGlobalScope"))] + #[error("def declarations must be done in global scope")] + #[diagnostic(code("Qsc.Qasm3.Lowerer.DefDeclarationInNonGlobalScope"))] DefDeclarationInNonGlobalScope(#[label] Span), - #[error("Designator is too large.")] - #[diagnostic(code("Qsc.Qasm3.Compile.DesignatorTooLarge"))] + #[error("designator is too large")] + #[diagnostic(code("Qsc.Qasm3.Lowerer.DesignatorTooLarge"))] DesignatorTooLarge(#[label] Span), - #[error("Extern declarations must be done in global scope.")] - #[diagnostic(code("Qsc.Qasm3.Compile.DefDeclarationInNonGlobalScope"))] + #[error("{0} must be a const expression")] + #[diagnostic(code("Qsc.Qasm3.Lowerer.ExprMustBeConst"))] + ExprMustBeConst(String, #[label] Span), + #[error("{0} must fit in a u32")] + #[diagnostic(code("Qsc.Qasm3.Lowerer.ExprMustFitInU32"))] + ExprMustFitInU32(String, #[label] Span), + #[error("extern declarations must be done in global scope")] + #[diagnostic(code("Qsc.Qasm3.Lowerer.DefDeclarationInNonGlobalScope"))] ExternDeclarationInNonGlobalScope(#[label] Span), - #[error("Failed to compile all expressions in expression list.")] - #[diagnostic(code("Qsc.Qasm3.Compile.FailedToCompileExpressionList"))] + #[error("failed to compile all expressions in expression list")] + #[diagnostic(code("Qsc.Qasm3.Lowerer.FailedToCompileExpressionList"))] FailedToCompileExpressionList(#[label] Span), - #[error("For iterable must have a set expression, range expression, or iterable expression.")] - #[diagnostic(code("Qsc.Qasm3.Compile.ForIterableInvalidExpression"))] + #[error("for iterable must have a set expression, range expression, or iterable expression")] + #[diagnostic(code("Qsc.Qasm3.Lowerer.ForIterableInvalidExpression"))] ForIterableInvalidExpression(#[label] Span), - #[error("For statements must have a body or statement.")] - #[diagnostic(code("Qsc.Qasm3.Compile.ForStatementsMustHaveABodyOrStatement"))] + #[error("for statements must have a body or statement")] + #[diagnostic(code("Qsc.Qasm3.Lowerer.ForStatementsMustHaveABodyOrStatement"))] ForStatementsMustHaveABodyOrStatement(#[label] Span), - #[error("Inconsisten types in alias expression: {0}.")] - #[diagnostic(code("Qsc.Qasm3.Compile.InconsistentTypesInAlias"))] - InconsistentTypesInAlias(String, #[label] Span), - #[error("If statement missing {0} expression.")] - #[diagnostic(code("Qsc.Qasm3.Compile.IfStmtMissingExpression"))] + #[error("if statement missing {0} expression")] + #[diagnostic(code("Qsc.Qasm3.Lowerer.IfStmtMissingExpression"))] IfStmtMissingExpression(String, #[label] Span), - #[error("include {0} could not be found.")] - #[diagnostic(code("Qsc.Qasm3.Compile.IncludeNotFound"))] + #[error("include {0} could not be found")] + #[diagnostic(code("Qsc.Qasm3.Lowerer.IncludeNotFound"))] IncludeNotFound(String, #[label] Span), - #[error("include {0} must be declared in global scope.")] - #[diagnostic(code("Qsc.Qasm3.Compile.IncludeNotInGlobalScope"))] + #[error("include {0} must be declared in global scope")] + #[diagnostic(code("Qsc.Qasm3.Lowerer.IncludeNotInGlobalScope"))] IncludeNotInGlobalScope(String, #[label] Span), - #[error("include {0} must be declared in global scope.")] - #[diagnostic(code("Qsc.Qasm3.Compile.IncludeStatementMissingPath"))] + #[error("include {0} must be declared in global scope")] + #[diagnostic(code("Qsc.Qasm3.Lowerer.IncludeStatementMissingPath"))] IncludeStatementMissingPath(#[label] Span), - #[error("Indexed must be a single expression.")] - #[diagnostic(code("Qsc.Qasm3.Compile.IndexMustBeSingleExpr"))] + #[error("inconsistent types in alias expression: {0}")] + #[diagnostic(code("Qsc.Qasm3.Lowerer.InconsistentTypesInAlias"))] + InconsistentTypesInAlias(String, #[label] Span), + #[error("indexed must be a single expression")] + #[diagnostic(code("Qsc.Qasm3.Lowerer.IndexMustBeSingleExpr"))] IndexMustBeSingleExpr(#[label] Span), - #[error("Annotations only valid on def and gate statements.")] - #[diagnostic(code("Qsc.Qasm3.Compile.InvalidAnnotationTarget"))] - InvalidAnnotationTarget(#[label] Span), - #[error("Assigning {0} values to {1} must be in a range that be converted to {1}.")] - #[diagnostic(code("Qsc.Qasm3.Compile.InvalidCastValueRange"))] + #[error("assigning {0} values to {1} must be in a range that be converted to {1}")] + #[diagnostic(code("Qsc.Qasm3.Lowerer.InvalidCastValueRange"))] InvalidCastValueRange(String, String, #[label] Span), - #[error("Gate operands other than qubits or qubit arrays are not supported.")] - #[diagnostic(code("Qsc.Qasm3.Compile.InvalidGateOperand"))] + #[error("gate operands other than qubits or qubit arrays are not supported")] + #[diagnostic(code("Qsc.Qasm3.Lowerer.InvalidGateOperand"))] InvalidGateOperand(#[label] Span), - #[error("Control counts must be integer literals.")] - #[diagnostic(code("Qsc.Qasm3.Compile.InvalidControlCount"))] + #[error("control counts must be integer literals")] + #[diagnostic(code("Qsc.Qasm3.Lowerer.InvalidControlCount"))] InvalidControlCount(#[label] Span), - #[error("Gate operands other than qubit arrays are not supported.")] - #[diagnostic(code("Qsc.Qasm3.Compile.InvalidIndexedGateOperand"))] + #[error("gate operands other than qubit arrays are not supported")] + #[diagnostic(code("Qsc.Qasm3.Lowerer.InvalidIndexedGateOperand"))] InvalidIndexedGateOperand(#[label] Span), - #[error("Gate expects {0} classical arguments, but {1} were provided.")] - #[diagnostic(code("Qsc.Qasm3.Compile.InvalidNumberOfClassicalArgs"))] + #[error("gate expects {0} classical arguments, but {1} were provided")] + #[diagnostic(code("Qsc.Qasm3.Lowerer.InvalidNumberOfClassicalArgs"))] InvalidNumberOfClassicalArgs(usize, usize, #[label] Span), - #[error("Gate expects {0} qubit arguments, but {1} were provided.")] - #[diagnostic(code("Qsc.Qasm3.Compile.InvalidNumberOfQubitArgs"))] + #[error("gate expects {0} qubit arguments, but {1} were provided")] + #[diagnostic(code("Qsc.Qasm3.Lowerer.InvalidNumberOfQubitArgs"))] InvalidNumberOfQubitArgs(usize, usize, #[label] Span), - #[error("{0} can only appear in {1} scopes.")] - #[diagnostic(code("Qsc.Qasm3.Compile.InvalidScope"))] + #[error("{0} can only appear in {1} scopes")] + #[diagnostic(code("Qsc.Qasm3.Lowerer.InvalidScope"))] InvalidScope(String, String, #[label] Span), - #[error("Measure statements must have a name.")] - #[diagnostic(code("Qsc.Qasm3.Compile.MeasureExpressionsMustHaveName"))] + #[error("measure statements must have a name")] + #[diagnostic(code("Qsc.Qasm3.Lowerer.MeasureExpressionsMustHaveName"))] MeasureExpressionsMustHaveName(#[label] Span), - #[error("Measure statements must have a gate operand name.")] - #[diagnostic(code("Qsc.Qasm3.Compile.MeasureExpressionsMustHaveGateOperand"))] + #[error("measure statements must have a gate operand name")] + #[diagnostic(code("Qsc.Qasm3.Lowerer.MeasureExpressionsMustHaveGateOperand"))] MeasureExpressionsMustHaveGateOperand(#[label] Span), - #[error("Return statements on a non-void subroutine should have a target expression.")] - #[diagnostic(code("Qsc.Qasm3.Compile.MissingTargetExpressionInReturnStmt"))] + #[error("return statements on a non-void subroutine should have a target expression")] + #[diagnostic(code("Qsc.Qasm3.Lowerer.MissingTargetExpressionInReturnStmt"))] MissingTargetExpressionInReturnStmt(#[label] Span), - #[error("Control counts must be postitive integers.")] - #[diagnostic(code("Qsc.Qasm3.Compile.NegativeControlCount"))] + #[error("control counts must be postitive integers")] + #[diagnostic(code("Qsc.Qasm3.Lowerer.NegativeControlCount"))] NegativeControlCount(#[label] Span), - #[error("{0} are not supported.")] - #[diagnostic(code("Qsc.Qasm3.Compile.NotSupported"))] + #[error("{0} are not supported")] + #[diagnostic(code("Qsc.Qasm3.Lowerer.NotSupported"))] NotSupported(String, #[label] Span), #[error("{0} were introduced in version {1}")] - #[diagnostic(code("Qsc.Qasm3.Compile.NotSupportedInThisVersion"))] + #[diagnostic(code("Qsc.Qasm3.Lowerer.NotSupportedInThisVersion"))] NotSupportedInThisVersion(String, String, #[label] Span), - #[error("The operator {0} is not valid with lhs {1} and rhs {2}.")] - #[diagnostic(code("Qsc.Qasm3.Compile.OperatorNotSupportedForTypes"))] + #[error("the operator {0} is not valid with lhs {1} and rhs {2}")] + #[diagnostic(code("Qsc.Qasm3.Lowerer.OperatorNotSupportedForTypes"))] OperatorNotSupportedForTypes(String, String, String, #[label] Span), - #[error("Pow gate modifiers must have an exponent.")] - #[diagnostic(code("Qsc.Qasm3.Compile.PowModifierMustHaveExponent"))] + #[error("pow gate modifiers must have an exponent")] + #[diagnostic(code("Qsc.Qasm3.Lowerer.PowModifierMustHaveExponent"))] PowModifierMustHaveExponent(#[label] Span), - #[error("Qiskit circuits must have output registers.")] - #[diagnostic(code("Qsc.Qasm3.Compile.QiskitEntryPointMissingOutput"))] - QiskitEntryPointMissingOutput(#[label] Span), - #[error("Quantum declarations must be done in global scope.")] - #[diagnostic(code("Qsc.Qasm3.Compile.QuantumDeclarationInNonGlobalScope"))] + #[error("quantum declarations must be done in global scope")] + #[diagnostic(code("Qsc.Qasm3.Lowerer.QuantumDeclarationInNonGlobalScope"))] QuantumDeclarationInNonGlobalScope(#[label] Span), - #[error("Quantum typed values cannot be used in binary expressions.")] - #[diagnostic(code("Qsc.Qasm3.Compile.QuantumTypesInBinaryExpression"))] + #[error("quantum typed values cannot be used in binary expressions")] + #[diagnostic(code("Qsc.Qasm3.Lowerer.QuantumTypesInBinaryExpression"))] QuantumTypesInBinaryExpression(#[label] Span), - #[error("Range expressions must have a start.")] - #[diagnostic(code("Qsc.Qasm3.Compile.RangeExpressionsMustHaveStart"))] + #[error("range expressions must have a start")] + #[diagnostic(code("Qsc.Qasm3.Lowerer.RangeExpressionsMustHaveStart"))] RangeExpressionsMustHaveStart(#[label] Span), - #[error("Range expressions must have a stop.")] - #[diagnostic(code("Qsc.Qasm3.Compile.RangeExpressionsMustHaveStop"))] + #[error("range expressions must have a stop")] + #[diagnostic(code("Qsc.Qasm3.Lowerer.RangeExpressionsMustHaveStop"))] RangeExpressionsMustHaveStop(#[label] Span), - #[error("Redefined symbol: {0}.")] - #[diagnostic(code("Qsc.Qasm3.Compile.RedefinedSymbol"))] + #[error("redefined symbol: {0}")] + #[diagnostic(code("Qsc.Qasm3.Lowerer.RedefinedSymbol"))] RedefinedSymbol(String, #[label] Span), - #[error("Reset expression must have a gate operand.")] - #[diagnostic(code("Qsc.Qasm3.Compile.ResetExpressionMustHaveGateOperand"))] + #[error("reset expression must have a gate operand")] + #[diagnostic(code("Qsc.Qasm3.Lowerer.ResetExpressionMustHaveGateOperand"))] ResetExpressionMustHaveGateOperand(#[label] Span), - #[error("Reset expression must have a name.")] - #[diagnostic(code("Qsc.Qasm3.Compile.ResetExpressionMustHaveName"))] + #[error("reset expression must have a name")] + #[diagnostic(code("Qsc.Qasm3.Lowerer.ResetExpressionMustHaveName"))] ResetExpressionMustHaveName(#[label] Span), - #[error("Cannot return an expression from a void subroutine.")] - #[diagnostic(code("Qsc.Qasm3.Compile.ReturningExpressionFromVoidSubroutine"))] + #[error("cannot return an expression from a void subroutine")] + #[diagnostic(code("Qsc.Qasm3.Lowerer.ReturningExpressionFromVoidSubroutine"))] ReturningExpressionFromVoidSubroutine(#[label] Span), - #[error("Return statements are only allowed within subroutines.")] - #[diagnostic(code("Qsc.Qasm3.Compile.ReturnNotInSubroutine"))] + #[error("return statements are only allowed within subroutines")] + #[diagnostic(code("Qsc.Qasm3.Lowerer.ReturnNotInSubroutine"))] ReturnNotInSubroutine(#[label] Span), - #[error("Switch statement must have at least one non-default case.")] - #[diagnostic(code("Qsc.Qasm3.Compile.SwitchStatementMustHaveAtLeastOneCase"))] + #[error("switch statement must have at least one non-default case")] + #[diagnostic(code("Qsc.Qasm3.Lowerer.SwitchStatementMustHaveAtLeastOneCase"))] SwitchStatementMustHaveAtLeastOneCase(#[label] Span), - #[error("Too many controls specified.")] - #[diagnostic(code("Qsc.Qasm3.Compile.TooManyControls"))] + #[error("too many controls specified")] + #[diagnostic(code("Qsc.Qasm3.Lowerer.TooManyControls"))] TooManyControls(#[label] Span), - #[error("Too many indicies specified.")] - #[diagnostic(code("Qsc.Qasm3.Compile.TooManyIndices"))] + #[error("too many indicies specified")] + #[diagnostic(code("Qsc.Qasm3.Lowerer.TooManyIndices"))] TooManyIndices(#[label] Span), - #[error("Bitwise not `~` is not allowed for instances of {0}.")] - #[diagnostic(code("Qsc.Qasm3.Compile.TypeDoesNotSupportBitwiseNot"))] + #[error("bitwise not `~` is not allowed for instances of {0}")] + #[diagnostic(code("Qsc.Qasm3.Lowerer.TypeDoesNotSupportBitwiseNot"))] TypeDoesNotSupportBitwiseNot(String, #[label] Span), - #[error("Unary negation is not allowed for instances of {0}.")] - #[diagnostic(code("Qsc.Qasm3.Compile.TypeDoesNotSupportedUnaryNegation"))] + #[error("unary negation is not allowed for instances of {0}")] + #[diagnostic(code("Qsc.Qasm3.Lowerer.TypeDoesNotSupportedUnaryNegation"))] TypeDoesNotSupportedUnaryNegation(String, #[label] Span), - #[error("{0} max width is {1} but {2} was provided.")] - #[diagnostic(code("Qsc.Qasm3.Compile.TypeMaxWidthExceeded"))] + #[error("{0} max width is {1} but {2} was provided")] + #[diagnostic(code("Qsc.Qasm3.Lowerer.TypeMaxWidthExceeded"))] TypeMaxWidthExceeded(String, usize, usize, #[label] Span), - #[error("Types differ by dimensions and are incompatible.")] - #[diagnostic(code("Qsc.Qasm3.Compile.TypeRankError"))] + #[error("types differ by dimensions and are incompatible")] + #[diagnostic(code("Qsc.Qasm3.Lowerer.TypeRankError"))] TypeRankError(#[label] Span), - #[error("Undefined symbol: {0}.")] - #[diagnostic(code("Qsc.Qasm3.Compile.UndefinedSymbol"))] + #[error("type width must be a positive integer const expression")] + #[diagnostic(code("Qsc.Qasm3.Lowerer.TypeWidthMustBePositiveIntConstExpr"))] + TypeWidthMustBePositiveIntConstExpr(#[label] Span), + #[error("undefined symbol: {0}")] + #[diagnostic(code("Qsc.Qasm3.Lowerer.UndefinedSymbol"))] UndefinedSymbol(String, #[label] Span), - #[error("Unexpected parser error: {0}.")] - #[diagnostic(code("Qsc.Qasm3.Compile.UnexpectedParserError"))] + #[error("unexpected parser error: {0}")] + #[diagnostic(code("Qsc.Qasm3.Lowerer.UnexpectedParserError"))] UnexpectedParserError(String, #[label] Span), #[error("this statement is not yet handled during OpenQASM 3 import: {0}")] - #[diagnostic(code("Qsc.Qasm3.Compile.Unimplemented"))] + #[diagnostic(code("Qsc.Qasm3.Lowerer.Unimplemented"))] Unimplemented(String, #[label] Span), - #[error("Unexpected annotation: {0}.")] - #[diagnostic(code("Qsc.Qasm3.Compile.UnknownAnnotation"))] - UnknownAnnotation(String, #[label] Span), - #[error("Unknown index operation kind.")] - #[diagnostic(code("Qsc.Qasm3.Compile.UnknownIndexedOperatorKind"))] + #[error("unknown index operation kind")] + #[diagnostic(code("Qsc.Qasm3.Lowerer.UnknownIndexedOperatorKind"))] UnknownIndexedOperatorKind(#[label] Span), - #[error("Unsupported version: '{0}'.")] - #[diagnostic(code("Qsc.Qasm3.Compile.UnsupportedVersion"))] + #[error("unsupported version: '{0}'")] + #[diagnostic(code("Qsc.Qasm3.Lowerer.UnsupportedVersion"))] UnsupportedVersion(String, #[label] Span), - #[error("While statement missing {0} expression.")] - #[diagnostic(code("Qsc.Qasm3.Compile.WhileStmtMissingExpression"))] + #[error("while statement missing {0} expression")] + #[diagnostic(code("Qsc.Qasm3.Lowerer.WhileStmtMissingExpression"))] WhileStmtMissingExpression(String, #[label] Span), } -impl SemanticErrorKind { - /// The semantic errors are reported with the span of the syntax that caused the error. - /// This offset is relative to the start of the file in which the error occurred. - /// This method is used to adjust the span of the error to be relative to where the - /// error was reported in the entire compilation unit as part of the source map. - #[allow(clippy::too_many_lines)] - fn with_offset(self, offset: u32) -> Self { - match self { - Self::ArrayLiteralInNonClassicalDecl(span) => { - Self::ArrayLiteralInNonClassicalDecl(span + offset) - } - Self::ExprMustBeConst(name, span) => Self::ExprMustBeConst(name, span + offset), - Self::ExprMustFitInU32(name, span) => Self::ExprMustFitInU32(name, span + offset), - Self::AnnotationWithoutStatement(span) => { - Self::AnnotationWithoutStatement(span + offset) - } - Self::CannotCast(lhs, rhs, span) => Self::CannotCast(lhs, rhs, span + offset), - Self::CannotCastLiteral(lhs, rhs, span) => { - Self::CannotCastLiteral(lhs, rhs, span + offset) - } - Self::CastWouldCauseTruncation(lhs, rhs, span) => { - Self::CastWouldCauseTruncation(lhs, rhs, span + offset) - } - Self::ClassicalStmtInBox(span) => Self::ClassicalStmtInBox(span + offset), - Self::CalibrationsNotSupported(name, span) => { - Self::CalibrationsNotSupported(name, span + offset) - } - Self::CannotAliasType(name, span) => Self::CannotAliasType(name, span + offset), - Self::CannotApplyOperatorToTypes(op, lhs, rhs, span) => { - Self::CannotApplyOperatorToTypes(op, lhs, rhs, span + offset) - } - Self::CannotAssignToType(lhs, rhs, span) => { - Self::CannotAssignToType(lhs, rhs, span + offset) - } - Self::CannotCallNonFunction(span) => Self::CannotCallNonFunction(span + offset), - Self::CannotCallNonGate(span) => Self::CannotCallNonGate(span + offset), - Self::CannotIndexType(name, span) => Self::CannotIndexType(name, span + offset), - Self::CannotUpdateConstVariable(name, span) => { - Self::CannotUpdateConstVariable(name, span + offset) - } - Self::ComplexBinaryAssignment(span) => Self::ComplexBinaryAssignment(span + offset), - Self::DesignatorMustBePositiveIntLiteral(span) => { - Self::DesignatorMustBePositiveIntLiteral(span + offset) - } - Self::TypeWidthMustBePositiveIntConstExpr(span) => { - Self::TypeWidthMustBePositiveIntConstExpr(span + offset) - } - Self::ArraySizeMustBeNonNegativeConstExpr(span) => { - Self::ArraySizeMustBeNonNegativeConstExpr(span + offset) - } - Self::DefDeclarationInNonGlobalScope(span) => { - Self::DefDeclarationInNonGlobalScope(span + offset) - } - Self::DesignatorTooLarge(span) => Self::DesignatorTooLarge(span + offset), - Self::ExternDeclarationInNonGlobalScope(span) => { - Self::ExternDeclarationInNonGlobalScope(span + offset) - } - Self::FailedToCompileExpressionList(span) => { - Self::FailedToCompileExpressionList(span + offset) - } - Self::ForIterableInvalidExpression(span) => { - Self::ForIterableInvalidExpression(span + offset) - } - Self::ForStatementsMustHaveABodyOrStatement(span) => { - Self::ForStatementsMustHaveABodyOrStatement(span + offset) - } - Self::InconsistentTypesInAlias(name, span) => { - Self::InconsistentTypesInAlias(name, span + offset) - } - Self::InvalidScope(name, scope, span) => Self::InvalidScope(name, scope, span + offset), - Self::IfStmtMissingExpression(name, span) => { - Self::IfStmtMissingExpression(name, span + offset) - } - Self::IncludeNotFound(name, span) => Self::IncludeNotFound(name, span + offset), - Self::IncludeNotInGlobalScope(name, span) => { - Self::IncludeNotInGlobalScope(name, span + offset) - } - Self::IncludeStatementMissingPath(span) => { - Self::IncludeStatementMissingPath(span + offset) - } - Self::IndexMustBeSingleExpr(span) => Self::IndexMustBeSingleExpr(span + offset), - Self::InvalidAnnotationTarget(span) => Self::InvalidAnnotationTarget(span + offset), - Self::InvalidControlCount(span) => Self::InvalidControlCount(span + offset), - Self::InvalidNumberOfClassicalArgs(expected, actual, span) => { - Self::InvalidNumberOfClassicalArgs(expected, actual, span + offset) - } - Self::InvalidNumberOfQubitArgs(expected, actual, span) => { - Self::InvalidNumberOfQubitArgs(expected, actual, span + offset) - } - Self::InvalidCastValueRange(lhs, rhs, span) => { - Self::InvalidCastValueRange(lhs, rhs, span + offset) - } - Self::InvalidGateOperand(span) => Self::InvalidGateOperand(span + offset), - Self::InvalidIndexedGateOperand(span) => Self::InvalidIndexedGateOperand(span + offset), - Self::MeasureExpressionsMustHaveGateOperand(span) => { - Self::MeasureExpressionsMustHaveGateOperand(span + offset) - } - Self::MeasureExpressionsMustHaveName(span) => { - Self::MeasureExpressionsMustHaveName(span + offset) - } - Self::MissingTargetExpressionInReturnStmt(span) => { - Self::MissingTargetExpressionInReturnStmt(span + offset) - } - Self::NegativeControlCount(span) => Self::NegativeControlCount(span + offset), - Self::NotSupported(name, span) => Self::NotSupported(name, span + offset), - Self::NotSupportedInThisVersion(name, version, span) => { - Self::NotSupportedInThisVersion(name, version, span + offset) - } - Self::OperatorNotSupportedForTypes(op, lhs, rhs, span) => { - Self::OperatorNotSupportedForTypes(op, lhs, rhs, span + offset) - } - Self::PowModifierMustHaveExponent(span) => { - Self::PowModifierMustHaveExponent(span + offset) - } - Self::QiskitEntryPointMissingOutput(span) => { - Self::QiskitEntryPointMissingOutput(span + offset) - } - Self::QuantumDeclarationInNonGlobalScope(span) => { - Self::QuantumDeclarationInNonGlobalScope(span + offset) - } - Self::QuantumTypesInBinaryExpression(span) => { - Self::QuantumTypesInBinaryExpression(span + offset) - } - Self::RangeExpressionsMustHaveStart(span) => { - Self::RangeExpressionsMustHaveStart(span + offset) - } - Self::RangeExpressionsMustHaveStop(span) => { - Self::RangeExpressionsMustHaveStop(span + offset) - } - Self::RedefinedSymbol(name, span) => Self::RedefinedSymbol(name, span + offset), - Self::ResetExpressionMustHaveGateOperand(span) => { - Self::ResetExpressionMustHaveGateOperand(span + offset) - } - Self::ResetExpressionMustHaveName(span) => { - Self::ResetExpressionMustHaveName(span + offset) - } - Self::ReturningExpressionFromVoidSubroutine(span) => { - Self::ReturningExpressionFromVoidSubroutine(span + offset) - } - Self::ReturnNotInSubroutine(span) => Self::ReturnNotInSubroutine(span + offset), - Self::SwitchStatementMustHaveAtLeastOneCase(span) => { - Self::SwitchStatementMustHaveAtLeastOneCase(span + offset) - } - Self::TooManyControls(span) => Self::TooManyControls(span + offset), - Self::TooManyIndices(span) => Self::TooManyIndices(span + offset), - Self::TypeDoesNotSupportBitwiseNot(name, span) => { - Self::TypeDoesNotSupportBitwiseNot(name, span + offset) - } - Self::TypeDoesNotSupportedUnaryNegation(name, span) => { - Self::TypeDoesNotSupportedUnaryNegation(name, span + offset) - } - Self::TypeMaxWidthExceeded(name, max_width, provided_width, span) => { - Self::TypeMaxWidthExceeded(name, max_width, provided_width, span + offset) - } - Self::TypeRankError(span) => Self::TypeRankError(span + offset), - Self::UndefinedSymbol(name, span) => Self::UndefinedSymbol(name, span + offset), - Self::UnexpectedParserError(error, span) => { - Self::UnexpectedParserError(error, span + offset) - } - Self::Unimplemented(name, span) => Self::Unimplemented(name, span + offset), - Self::UnknownAnnotation(name, span) => Self::UnknownAnnotation(name, span + offset), - Self::UnknownIndexedOperatorKind(span) => { - Self::UnknownIndexedOperatorKind(span + offset) - } - Self::UnsupportedVersion(version, span) => { - Self::UnsupportedVersion(version, span + offset) - } - Self::WhileStmtMissingExpression(name, span) => { - Self::WhileStmtMissingExpression(name, span + offset) - } - } - } -} - impl From for crate::Error { fn from(val: Error) -> Self { crate::Error(crate::ErrorKind::Semantic(val)) diff --git a/compiler/qsc_qasm3/src/semantic/lowerer.rs b/compiler/qsc_qasm3/src/semantic/lowerer.rs index 615f706858..23e532ba40 100644 --- a/compiler/qsc_qasm3/src/semantic/lowerer.rs +++ b/compiler/qsc_qasm3/src/semantic/lowerer.rs @@ -102,7 +102,7 @@ impl Lowerer { assert!( self.symbols.is_current_scope_global(), - "Scope stack was non popped correctly." + "scope stack was non popped correctly" ); let program = semantic::Program { @@ -610,7 +610,7 @@ impl Lowerer { semantic::ExprKind::Lit(val) } else { self.push_semantic_error(SemanticErrorKind::ExprMustBeConst( - "A captured variable".into(), + "a captured variable".into(), ident.span, )); semantic::ExprKind::Err @@ -659,7 +659,7 @@ impl Lowerer { Type::Complex(None, true), ), syntax::LiteralKind::String(_) => { - self.push_unsupported_error_message("String literals", expr.span); + self.push_unsupported_error_message("string literals", expr.span); (semantic::ExprKind::Err, Type::Err) } syntax::LiteralKind::Duration(value, time_unit) => ( @@ -808,7 +808,7 @@ impl Lowerer { Type::Bit(_) => crate::types::Type::Result(is_const), Type::Qubit => crate::types::Type::Qubit, Type::HardwareQubit => { - let message = "HardwareQubit to Q# type"; + let message = "hardware qubit to Q# type"; self.push_unsupported_error_message(message, span); crate::types::Type::Err } @@ -828,11 +828,11 @@ impl Lowerer { Type::Complex(_, _) => crate::types::Type::Complex(is_const), Type::Bool(_) => crate::types::Type::Bool(is_const), Type::Duration(_) => { - self.push_unsupported_error_message("Duration type values", span); + self.push_unsupported_error_message("duration type values", span); crate::types::Type::Err } Type::Stretch(_) => { - self.push_unsupported_error_message("Stretch type values", span); + self.push_unsupported_error_message("stretch type values", span); crate::types::Type::Err } Type::BitArray(dims, _) => crate::types::Type::ResultArray(dims.into(), is_const), @@ -857,7 +857,7 @@ impl Lowerer { Type::Void => crate::types::Type::Tuple(vec![]), Type::Err => crate::types::Type::Err, _ => { - let msg = format!("Converting {ty:?} to Q# type"); + let msg = format!("converting {ty:?} to Q# type"); self.push_unimplemented_error_message(msg, span); crate::types::Type::Err } @@ -1602,7 +1602,7 @@ impl Lowerer { } // if we are at the root and we have an include, we should have // already handled it and we are in an invalid state - panic!("Include should have been handled in lower_source") + panic!("include should have been handled in lower_source") } fn lower_io_decl(&mut self, stmt: &syntax::IODeclaration) -> semantic::StmtKind { @@ -1812,7 +1812,7 @@ impl Lowerer { // If we don't have a return type then we are not rooted in a subroutine scope. (_, None) => { self.push_semantic_error(SemanticErrorKind::InvalidScope( - "Return statements".into(), + "return statements".into(), "subroutine".into(), stmt.span, )); @@ -2178,7 +2178,7 @@ impl Lowerer { } // if we can't cast the literal, we can't proceed // create a semantic error and return - let kind = SemanticErrorKind::CannotAssignToType( + let kind = SemanticErrorKind::CannotCastLiteral( format!("{:?}", rhs.ty), format!("{ty:?}"), span, @@ -2225,22 +2225,22 @@ impl Lowerer { Type::Float(_, _) => Some(from_lit_kind(LiteralKind::Float(0.0))), Type::Complex(_, _) => Some(from_lit_kind(LiteralKind::Complex(0.0, 0.0))), Type::Stretch(_) => { - let message = "Stretch default values"; + let message = "stretch default values"; self.push_unsupported_error_message(message, span); None } Type::Qubit => { - let message = "Qubit default values"; + let message = "qubit default values"; self.push_unsupported_error_message(message, span); None } Type::HardwareQubit => { - let message = "HardwareQubit default values"; + let message = "hardware qubit default values"; self.push_unsupported_error_message(message, span); None } Type::QubitArray(_) => { - let message = "QubitArray default values"; + let message = "qubit array default values"; self.push_unsupported_error_message(message, span); None } @@ -2290,7 +2290,7 @@ impl Lowerer { None } Type::Gate(_, _) | Type::Function(..) | Type::Range | Type::Set | Type::Void => { - let message = format!("Default values for {ty:?}"); + let message = format!("default values for {ty:?}"); self.push_unsupported_error_message(message, span); None } @@ -2323,7 +2323,7 @@ impl Lowerer { kind: &semantic::LiteralKind, ) -> Option { assert!(matches!(*rhs.kind, semantic::ExprKind::Lit(..))); - assert!(rhs.ty.is_const(), "Literals must have const types"); + assert!(rhs.ty.is_const(), "literals must have const types"); if *ty == rhs.ty { // Base case, we shouldn't have gotten here @@ -2486,8 +2486,8 @@ impl Lowerer { }); } let kind = SemanticErrorKind::InvalidCastValueRange( - "Integer".to_string(), - "Double".to_string(), + "int".to_string(), + "float".to_string(), span, ); self.push_semantic_error(kind); @@ -2507,9 +2507,9 @@ impl Lowerer { ty: lhs_ty.as_const(), }); } - panic!("Value must be 0 or 1"); + panic!("value must be 0 or 1"); } else { - panic!("Literal must be an IntNumber"); + panic!("literal must be an Int"); } } (Type::Int(width, _), Type::Int(_, _) | Type::UInt(_, _)) => { @@ -2530,8 +2530,8 @@ impl Lowerer { BigInt::shl_assign(&mut cap, width); if *value >= cap { self.push_semantic_error(SemanticErrorKind::InvalidCastValueRange( - "BigInt".to_string(), - "Int".to_string(), + value.to_string(), + format!("int[{width}]"), span, )); return None; @@ -2545,7 +2545,7 @@ impl Lowerer { ty: lhs_ty.as_const(), }); } - _ => panic!("Literal must be an IntNumber or BigInt"), + _ => panic!("literal must be an Int or BigInt"), } } _ => None, @@ -2553,7 +2553,7 @@ impl Lowerer { if result.is_none() { // we assert that the type can be casted // but we didn't cast it, so this is a bug - panic!("Literal type cast failed lhs: {:?}, rhs: {:?}", ty, rhs.ty); + panic!("literal type cast failed lhs: {:?}, rhs: {:?}", ty, rhs.ty); } else { result } diff --git a/compiler/qsc_qasm3/src/semantic/tests.rs b/compiler/qsc_qasm3/src/semantic/tests.rs index 58ddba1212..8033310e2b 100644 --- a/compiler/qsc_qasm3/src/semantic/tests.rs +++ b/compiler/qsc_qasm3/src/semantic/tests.rs @@ -303,51 +303,51 @@ fn semantic_errors_map_to_their_corresponding_file_specific_spans() { ty: Err kind: SymbolId(36) - [Qsc.Qasm3.Compile.UndefinedSymbol + [Qsc.Qasm3.Lowerer.UndefinedSymbol - x Undefined symbol: v. + x undefined symbol: v ,-[source2.qasm:2:14] 1 | bit l = 1; 2 | bool l = v && l; // undefined y, redefine l : ^ `---- - , Qsc.Qasm3.Compile.CannotCast + , Qsc.Qasm3.Lowerer.CannotCast - x Cannot cast expression of type Err to type Bool(false) + x cannot cast expression of type Err to type Bool(false) ,-[source2.qasm:2:14] 1 | bit l = 1; 2 | bool l = v && l; // undefined y, redefine l : ^ `---- - , Qsc.Qasm3.Compile.RedefinedSymbol + , Qsc.Qasm3.Lowerer.RedefinedSymbol - x Redefined symbol: l. + x redefined symbol: l ,-[source2.qasm:2:10] 1 | bit l = 1; 2 | bool l = v && l; // undefined y, redefine l : ^ `---- - , Qsc.Qasm3.Compile.CannotCast + , Qsc.Qasm3.Lowerer.CannotCast - x Cannot cast expression of type Angle(None, false) to type Float(None, + x cannot cast expression of type Angle(None, false) to type Float(None, | false) ,-[source1.qasm:3:15] 2 | angle j = 7.0; 3 | float k = j + false; // invalid cast : ^ `---- - , Qsc.Qasm3.Compile.UndefinedSymbol + , Qsc.Qasm3.Lowerer.UndefinedSymbol - x Undefined symbol: r. + x undefined symbol: r ,-[source0.qasm:4:13] 3 | include "source1.qasm"; 4 | bit c = r; // undefined symbol r : ^ 5 | `---- - , Qsc.Qasm3.Compile.CannotCast + , Qsc.Qasm3.Lowerer.CannotCast - x Cannot cast expression of type Err to type Bit(false) + x cannot cast expression of type Err to type Bit(false) ,-[source0.qasm:4:13] 3 | include "source1.qasm"; 4 | bit c = r; // undefined symbol r diff --git a/compiler/qsc_qasm3/src/semantic/tests/decls.rs b/compiler/qsc_qasm3/src/semantic/tests/decls.rs index 1240c0c4d8..230b04acac 100644 --- a/compiler/qsc_qasm3/src/semantic/tests/decls.rs +++ b/compiler/qsc_qasm3/src/semantic/tests/decls.rs @@ -68,9 +68,9 @@ fn scalar_ty_designator_must_be_positive() { ty: Err kind: Err - [Qsc.Qasm3.Compile.TypeWidthMustBePositiveIntConstExpr + [Qsc.Qasm3.Lowerer.TypeWidthMustBePositiveIntConstExpr - x Type width must be a positive integer const expression. + x type width must be a positive integer const expression ,-[test:1:5] 1 | int[-5] i; : ^^ @@ -104,16 +104,16 @@ fn scalar_ty_designator_must_be_castable_to_const_int() { ty: Err kind: Err - [Qsc.Qasm3.Compile.CannotCast + [Qsc.Qasm3.Lowerer.CannotCast - x Cannot cast expression of type Angle(None, true) to type UInt(None, true) + x cannot cast expression of type Angle(None, true) to type UInt(None, true) ,-[test:1:29] 1 | const angle size = 2.0; int[size] i; : ^^^^ `---- - , Qsc.Qasm3.Compile.TypeWidthMustBePositiveIntConstExpr + , Qsc.Qasm3.Lowerer.TypeWidthMustBePositiveIntConstExpr - x Type width must be a positive integer const expression. + x type width must be a positive integer const expression ,-[test:1:29] 1 | const angle size = 2.0; int[size] i; : ^^^^ diff --git a/compiler/qsc_qasm3/src/semantic/tests/decls/angle.rs b/compiler/qsc_qasm3/src/semantic/tests/decls/angle.rs index e64a0ff65a..6d00b8b7b2 100644 --- a/compiler/qsc_qasm3/src/semantic/tests/decls/angle.rs +++ b/compiler/qsc_qasm3/src/semantic/tests/decls/angle.rs @@ -395,9 +395,9 @@ fn const_lit_decl_signed_int_lit_cast_neg_fails() { ty: Int(None, true) kind: Lit: Int(7) - [Qsc.Qasm3.Compile.CannotCast + [Qsc.Qasm3.Lowerer.CannotCast - x Cannot cast expression of type Int(None, true) to type Angle(None, true) + x cannot cast expression of type Int(None, true) to type Angle(None, true) ,-[test:1:18] 1 | const angle x = -7; : ^ @@ -423,17 +423,16 @@ fn explicit_zero_width_fails() { ty: Float(None, true) kind: Lit: Float(42.1) - [Qsc.Qasm3.Compile.TypeWidthMustBePositiveIntConstExpr + [Qsc.Qasm3.Lowerer.TypeWidthMustBePositiveIntConstExpr - x Type width must be a positive integer const expression. + x type width must be a positive integer const expression ,-[test:1:7] 1 | angle[0] x = 42.1; : ^ `---- - , Qsc.Qasm3.Compile.CannotAssignToType + , Qsc.Qasm3.Lowerer.CannotCastLiteral - x Cannot assign a value of Float(None, true) type to a classical variable of - | Err type. + x cannot cast literal expression of type Float(None, true) to type Err ,-[test:1:1] 1 | angle[0] x = 42.1; : ^^^^^^^^^^^^^^^^^^ @@ -459,17 +458,16 @@ fn explicit_width_over_64_fails() { ty: Float(None, true) kind: Lit: Float(42.1) - [Qsc.Qasm3.Compile.TypeMaxWidthExceeded + [Qsc.Qasm3.Lowerer.TypeMaxWidthExceeded - x angle max width is 64 but 65 was provided. + x angle max width is 64 but 65 was provided ,-[test:1:7] 1 | const angle[65] x = 42.1; : ^^^^^^^^^ `---- - , Qsc.Qasm3.Compile.CannotAssignToType + , Qsc.Qasm3.Lowerer.CannotCastLiteral - x Cannot assign a value of Float(None, true) type to a classical variable of - | Err type. + x cannot cast literal expression of type Float(None, true) to type Err ,-[test:1:1] 1 | const angle[65] x = 42.1; : ^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/compiler/qsc_qasm3/src/semantic/tests/decls/duration.rs b/compiler/qsc_qasm3/src/semantic/tests/decls/duration.rs index 56d173ae52..776d89e1d4 100644 --- a/compiler/qsc_qasm3/src/semantic/tests/decls/duration.rs +++ b/compiler/qsc_qasm3/src/semantic/tests/decls/duration.rs @@ -22,9 +22,9 @@ fn with_no_init_expr_has_generated_lit_expr() { ty: Duration(true) kind: Lit: Duration(0.0, Ns) - [Qsc.Qasm3.Compile.NotSupported + [Qsc.Qasm3.Lowerer.NotSupported - x Duration type values are not supported. + x duration type values are not supported ,-[test:1:1] 1 | duration a; : ^^^^^^^^ diff --git a/compiler/qsc_qasm3/src/semantic/tests/decls/extern_decl.rs b/compiler/qsc_qasm3/src/semantic/tests/decls/extern_decl.rs index ddf10d85b8..963790f57c 100644 --- a/compiler/qsc_qasm3/src/semantic/tests/decls/extern_decl.rs +++ b/compiler/qsc_qasm3/src/semantic/tests/decls/extern_decl.rs @@ -76,9 +76,9 @@ fn no_allowed_in_non_global_scope() { parameters: return_type: () - [Qsc.Qasm3.Compile.DefDeclarationInNonGlobalScope + [Qsc.Qasm3.Lowerer.DefDeclarationInNonGlobalScope - x Extern declarations must be done in global scope. + x extern declarations must be done in global scope ,-[test:1:3] 1 | { extern f(); } : ^^^^^^^^^^^ diff --git a/compiler/qsc_qasm3/src/semantic/tests/decls/float.rs b/compiler/qsc_qasm3/src/semantic/tests/decls/float.rs index 5c372ba5b6..51f72a9b29 100644 --- a/compiler/qsc_qasm3/src/semantic/tests/decls/float.rs +++ b/compiler/qsc_qasm3/src/semantic/tests/decls/float.rs @@ -436,17 +436,17 @@ fn init_float_with_int_value_greater_than_safely_representable_values() { ty: Int(None, true) kind: Lit: Int(9007199254740993) - [Qsc.Qasm3.Compile.InvalidCastValueRange + [Qsc.Qasm3.Lowerer.InvalidCastValueRange - x Assigning Int(None, true) values to Float(None, false) must be in a range - | that be converted to Float(None, false). + x assigning Int(None, true) values to Float(None, false) must be in a range + | that be converted to Float(None, false) ,-[test:1:11] 1 | float a = 9007199254740993; : ^^^^^^^^^^^^^^^^ `---- - , Qsc.Qasm3.Compile.CannotCastLiteral + , Qsc.Qasm3.Lowerer.CannotCastLiteral - x Cannot cast literal expression of type Int(None, true) to type Float(None, + x cannot cast literal expression of type Int(None, true) to type Float(None, | false) ,-[test:1:11] 1 | float a = 9007199254740993; diff --git a/compiler/qsc_qasm3/src/semantic/tests/decls/stretch.rs b/compiler/qsc_qasm3/src/semantic/tests/decls/stretch.rs index 56f4f0dd61..7210bcc943 100644 --- a/compiler/qsc_qasm3/src/semantic/tests/decls/stretch.rs +++ b/compiler/qsc_qasm3/src/semantic/tests/decls/stretch.rs @@ -22,16 +22,16 @@ fn with_no_init_expr_has_generated_lit_expr() { ty: Stretch(true) kind: Err - [Qsc.Qasm3.Compile.NotSupported + [Qsc.Qasm3.Lowerer.NotSupported - x Stretch type values are not supported. + x stretch type values are not supported ,-[test:1:1] 1 | stretch a; : ^^^^^^^ `---- - , Qsc.Qasm3.Compile.NotSupported + , Qsc.Qasm3.Lowerer.NotSupported - x Stretch default values are not supported. + x stretch default values are not supported ,-[test:1:1] 1 | stretch a; : ^^^^^^^^^^ diff --git a/compiler/qsc_qasm3/src/semantic/tests/expression/implicit_cast_from_angle.rs b/compiler/qsc_qasm3/src/semantic/tests/expression/implicit_cast_from_angle.rs index 3936536762..7d4ef8e844 100644 --- a/compiler/qsc_qasm3/src/semantic/tests/expression/implicit_cast_from_angle.rs +++ b/compiler/qsc_qasm3/src/semantic/tests/expression/implicit_cast_from_angle.rs @@ -154,9 +154,9 @@ fn to_implicit_int_implicitly_fails() { ty: Angle(None, false) kind: SymbolId(8) - [Qsc.Qasm3.Compile.CannotCast + [Qsc.Qasm3.Lowerer.CannotCast - x Cannot cast expression of type Angle(None, false) to type Int(None, false) + x cannot cast expression of type Angle(None, false) to type Int(None, false) ,-[test:3:17] 2 | angle x = 42.; 3 | int y = x; @@ -197,9 +197,9 @@ fn to_explicit_int_implicitly_fails() { ty: Angle(None, false) kind: SymbolId(8) - [Qsc.Qasm3.Compile.CannotCast + [Qsc.Qasm3.Lowerer.CannotCast - x Cannot cast expression of type Angle(None, false) to type Int(Some(32), + x cannot cast expression of type Angle(None, false) to type Int(Some(32), | false) ,-[test:3:21] 2 | angle x = 42.; @@ -241,9 +241,9 @@ fn to_implicit_uint_implicitly_fails() { ty: Angle(None, false) kind: SymbolId(8) - [Qsc.Qasm3.Compile.CannotCast + [Qsc.Qasm3.Lowerer.CannotCast - x Cannot cast expression of type Angle(None, false) to type UInt(None, + x cannot cast expression of type Angle(None, false) to type UInt(None, | false) ,-[test:3:18] 2 | angle x = 42.; @@ -293,9 +293,9 @@ fn negative_lit_to_implicit_uint_implicitly_fails() { ty: Angle(None, false) kind: SymbolId(8) - [Qsc.Qasm3.Compile.CannotCast + [Qsc.Qasm3.Lowerer.CannotCast - x Cannot cast expression of type Angle(None, false) to type UInt(None, + x cannot cast expression of type Angle(None, false) to type UInt(None, | false) ,-[test:3:18] 2 | angle x = -42.; @@ -337,9 +337,9 @@ fn to_explicit_uint_implicitly_fails() { ty: Angle(None, false) kind: SymbolId(8) - [Qsc.Qasm3.Compile.CannotCast + [Qsc.Qasm3.Lowerer.CannotCast - x Cannot cast expression of type Angle(None, false) to type UInt(Some(32), + x cannot cast expression of type Angle(None, false) to type UInt(Some(32), | false) ,-[test:3:22] 2 | angle x = 42.; @@ -381,9 +381,9 @@ fn to_explicit_bigint_implicitly_fails() { ty: Angle(None, false) kind: SymbolId(8) - [Qsc.Qasm3.Compile.CannotCast + [Qsc.Qasm3.Lowerer.CannotCast - x Cannot cast expression of type Angle(None, false) to type Int(Some(65), + x cannot cast expression of type Angle(None, false) to type Int(Some(65), | false) ,-[test:3:21] 2 | angle x = 42.; @@ -425,9 +425,9 @@ fn to_implicit_float_implicitly_fails() { ty: Angle(None, false) kind: SymbolId(8) - [Qsc.Qasm3.Compile.CannotCast + [Qsc.Qasm3.Lowerer.CannotCast - x Cannot cast expression of type Angle(None, false) to type Float(None, + x cannot cast expression of type Angle(None, false) to type Float(None, | false) ,-[test:3:19] 2 | angle x = 42.; @@ -469,9 +469,9 @@ fn to_explicit_float_implicitly_fails() { ty: Angle(None, false) kind: SymbolId(8) - [Qsc.Qasm3.Compile.CannotCast + [Qsc.Qasm3.Lowerer.CannotCast - x Cannot cast expression of type Angle(None, false) to type Float(Some(32), + x cannot cast expression of type Angle(None, false) to type Float(Some(32), | false) ,-[test:3:23] 2 | angle x = 42.; @@ -513,9 +513,9 @@ fn to_implicit_complex_implicitly_fails() { ty: Angle(None, false) kind: SymbolId(8) - [Qsc.Qasm3.Compile.CannotCast + [Qsc.Qasm3.Lowerer.CannotCast - x Cannot cast expression of type Angle(None, false) to type Complex(None, + x cannot cast expression of type Angle(None, false) to type Complex(None, | false) ,-[test:3:28] 2 | angle x = 42.; @@ -557,9 +557,9 @@ fn to_explicit_complex_implicitly_fails() { ty: Angle(None, false) kind: SymbolId(8) - [Qsc.Qasm3.Compile.CannotCast + [Qsc.Qasm3.Lowerer.CannotCast - x Cannot cast expression of type Angle(None, false) to type + x cannot cast expression of type Angle(None, false) to type | Complex(Some(32), false) ,-[test:3:32] 2 | angle x = 42.; diff --git a/compiler/qsc_qasm3/src/semantic/tests/expression/implicit_cast_from_bit.rs b/compiler/qsc_qasm3/src/semantic/tests/expression/implicit_cast_from_bit.rs index b0f0e96648..b8e1d735ed 100644 --- a/compiler/qsc_qasm3/src/semantic/tests/expression/implicit_cast_from_bit.rs +++ b/compiler/qsc_qasm3/src/semantic/tests/expression/implicit_cast_from_bit.rs @@ -355,9 +355,9 @@ fn to_implicit_float_implicitly_fails() { ty: Bit(false) kind: SymbolId(8) - [Qsc.Qasm3.Compile.CannotCast + [Qsc.Qasm3.Lowerer.CannotCast - x Cannot cast expression of type Bit(false) to type Float(None, false) + x cannot cast expression of type Bit(false) to type Float(None, false) ,-[test:3:19] 2 | bit x = 1; 3 | float y = x; diff --git a/compiler/qsc_qasm3/src/semantic/tests/expression/implicit_cast_from_bitarray.rs b/compiler/qsc_qasm3/src/semantic/tests/expression/implicit_cast_from_bitarray.rs index d48f090faa..99054a4d8b 100644 --- a/compiler/qsc_qasm3/src/semantic/tests/expression/implicit_cast_from_bitarray.rs +++ b/compiler/qsc_qasm3/src/semantic/tests/expression/implicit_cast_from_bitarray.rs @@ -200,45 +200,45 @@ fn to_int_with_higher_width_implicitly_fails() { check_classical_decls( input, &expect![[r#" - Program: - version: - statements: - Stmt [9-18]: - annotations: - kind: ClassicalDeclarationStmt [9-18]: - symbol_id: 8 - ty_span: [9-15] - init_expr: Expr [0-0]: - ty: Int(Some(6), true) - kind: Lit: Int(0) - Stmt [27-38]: - annotations: - kind: ClassicalDeclarationStmt [27-38]: - symbol_id: 9 - ty_span: [27-33] - init_expr: Expr [0-0]: - ty: BitArray(One(5), true) - kind: Lit: Bitstring("00000") - Stmt [47-55]: - annotations: - kind: AssignStmt [47-55]: - symbol_id: 8 - lhs_span: [47-48] - rhs: Expr [51-54]: - ty: BitArray(One(5), false) - kind: SymbolId(9) + Program: + version: + statements: + Stmt [9-18]: + annotations: + kind: ClassicalDeclarationStmt [9-18]: + symbol_id: 8 + ty_span: [9-15] + init_expr: Expr [0-0]: + ty: Int(Some(6), true) + kind: Lit: Int(0) + Stmt [27-38]: + annotations: + kind: ClassicalDeclarationStmt [27-38]: + symbol_id: 9 + ty_span: [27-33] + init_expr: Expr [0-0]: + ty: BitArray(One(5), true) + kind: Lit: Bitstring("00000") + Stmt [47-55]: + annotations: + kind: AssignStmt [47-55]: + symbol_id: 8 + lhs_span: [47-48] + rhs: Expr [51-54]: + ty: BitArray(One(5), false) + kind: SymbolId(9) - [Qsc.Qasm3.Compile.CannotCast + [Qsc.Qasm3.Lowerer.CannotCast - x Cannot cast expression of type BitArray(One(5), false) to type - | Int(Some(6), false) - ,-[test:4:13] - 3 | bit[5] reg; - 4 | a = reg; - : ^^^ - 5 | - `---- - ]"#]], + x cannot cast expression of type BitArray(One(5), false) to type + | Int(Some(6), false) + ,-[test:4:13] + 3 | bit[5] reg; + 4 | a = reg; + : ^^^ + 5 | + `---- + ]"#]], ); } @@ -251,37 +251,37 @@ fn to_int_with_higher_width_decl_implicitly_fails() { check_classical_decls( input, &expect![[r#" - Program: - version: - statements: - Stmt [9-20]: - annotations: - kind: ClassicalDeclarationStmt [9-20]: - symbol_id: 8 - ty_span: [9-15] - init_expr: Expr [0-0]: - ty: BitArray(One(5), true) - kind: Lit: Bitstring("00000") - Stmt [29-44]: - annotations: - kind: ClassicalDeclarationStmt [29-44]: - symbol_id: 9 - ty_span: [29-35] - init_expr: Expr [40-43]: - ty: BitArray(One(5), false) - kind: SymbolId(8) + Program: + version: + statements: + Stmt [9-20]: + annotations: + kind: ClassicalDeclarationStmt [9-20]: + symbol_id: 8 + ty_span: [9-15] + init_expr: Expr [0-0]: + ty: BitArray(One(5), true) + kind: Lit: Bitstring("00000") + Stmt [29-44]: + annotations: + kind: ClassicalDeclarationStmt [29-44]: + symbol_id: 9 + ty_span: [29-35] + init_expr: Expr [40-43]: + ty: BitArray(One(5), false) + kind: SymbolId(8) - [Qsc.Qasm3.Compile.CannotCast + [Qsc.Qasm3.Lowerer.CannotCast - x Cannot cast expression of type BitArray(One(5), false) to type - | Int(Some(6), false) - ,-[test:3:20] - 2 | bit[5] reg; - 3 | int[6] a = reg; - : ^^^ - 4 | - `---- - ]"#]], + x cannot cast expression of type BitArray(One(5), false) to type + | Int(Some(6), false) + ,-[test:3:20] + 2 | bit[5] reg; + 3 | int[6] a = reg; + : ^^^ + 4 | + `---- + ]"#]], ); } @@ -296,41 +296,41 @@ fn to_int_with_lower_width_implicitly_fails() { check_classical_decls( input, &expect![[r#" - Program: - version: - statements: - Stmt [9-24]: - annotations: - kind: InputDeclaration [9-24]: - symbol_id: 8 - Stmt [33-44]: - annotations: - kind: ClassicalDeclarationStmt [33-44]: - symbol_id: 9 - ty_span: [33-39] - init_expr: Expr [0-0]: - ty: BitArray(One(5), true) - kind: Lit: Bitstring("00000") - Stmt [53-61]: - annotations: - kind: AssignStmt [53-61]: - symbol_id: 8 - lhs_span: [53-54] - rhs: Expr [57-60]: - ty: BitArray(One(5), false) - kind: SymbolId(9) + Program: + version: + statements: + Stmt [9-24]: + annotations: + kind: InputDeclaration [9-24]: + symbol_id: 8 + Stmt [33-44]: + annotations: + kind: ClassicalDeclarationStmt [33-44]: + symbol_id: 9 + ty_span: [33-39] + init_expr: Expr [0-0]: + ty: BitArray(One(5), true) + kind: Lit: Bitstring("00000") + Stmt [53-61]: + annotations: + kind: AssignStmt [53-61]: + symbol_id: 8 + lhs_span: [53-54] + rhs: Expr [57-60]: + ty: BitArray(One(5), false) + kind: SymbolId(9) - [Qsc.Qasm3.Compile.CannotCast + [Qsc.Qasm3.Lowerer.CannotCast - x Cannot cast expression of type BitArray(One(5), false) to type - | Int(Some(4), false) - ,-[test:4:13] - 3 | bit[5] reg; - 4 | a = reg; - : ^^^ - 5 | - `---- - ]"#]], + x cannot cast expression of type BitArray(One(5), false) to type + | Int(Some(4), false) + ,-[test:4:13] + 3 | bit[5] reg; + 4 | a = reg; + : ^^^ + 5 | + `---- + ]"#]], ); } @@ -344,36 +344,36 @@ fn to_int_with_lower_width_decl_implicitly_fails() { check_classical_decls( input, &expect![[r#" - Program: - version: - statements: - Stmt [9-20]: - annotations: - kind: ClassicalDeclarationStmt [9-20]: - symbol_id: 8 - ty_span: [9-15] - init_expr: Expr [0-0]: - ty: BitArray(One(5), true) - kind: Lit: Bitstring("00000") - Stmt [29-44]: - annotations: - kind: ClassicalDeclarationStmt [29-44]: - symbol_id: 9 - ty_span: [29-35] - init_expr: Expr [40-43]: - ty: BitArray(One(5), false) - kind: SymbolId(8) + Program: + version: + statements: + Stmt [9-20]: + annotations: + kind: ClassicalDeclarationStmt [9-20]: + symbol_id: 8 + ty_span: [9-15] + init_expr: Expr [0-0]: + ty: BitArray(One(5), true) + kind: Lit: Bitstring("00000") + Stmt [29-44]: + annotations: + kind: ClassicalDeclarationStmt [29-44]: + symbol_id: 9 + ty_span: [29-35] + init_expr: Expr [40-43]: + ty: BitArray(One(5), false) + kind: SymbolId(8) - [Qsc.Qasm3.Compile.CannotCast + [Qsc.Qasm3.Lowerer.CannotCast - x Cannot cast expression of type BitArray(One(5), false) to type - | Int(Some(4), false) - ,-[test:3:20] - 2 | bit[5] reg; - 3 | int[4] a = reg; - : ^^^ - 4 | - `---- - ]"#]], + x cannot cast expression of type BitArray(One(5), false) to type + | Int(Some(4), false) + ,-[test:3:20] + 2 | bit[5] reg; + 3 | int[4] a = reg; + : ^^^ + 4 | + `---- + ]"#]], ); } diff --git a/compiler/qsc_qasm3/src/semantic/tests/expression/implicit_cast_from_float.rs b/compiler/qsc_qasm3/src/semantic/tests/expression/implicit_cast_from_float.rs index bce8631b3f..4bfe0d0925 100644 --- a/compiler/qsc_qasm3/src/semantic/tests/expression/implicit_cast_from_float.rs +++ b/compiler/qsc_qasm3/src/semantic/tests/expression/implicit_cast_from_float.rs @@ -35,9 +35,9 @@ fn to_bit_implicitly_fails() { ty: Float(None, false) kind: SymbolId(8) - [Qsc.Qasm3.Compile.CannotCast + [Qsc.Qasm3.Lowerer.CannotCast - x Cannot cast expression of type Float(None, false) to type Bit(false) + x cannot cast expression of type Float(None, false) to type Bit(false) ,-[test:3:17] 2 | float x = 42.; 3 | bit y = x; @@ -78,9 +78,9 @@ fn explicit_width_to_bit_implicitly_fails() { ty: Float(Some(64), false) kind: SymbolId(8) - [Qsc.Qasm3.Compile.CannotCast + [Qsc.Qasm3.Lowerer.CannotCast - x Cannot cast expression of type Float(Some(64), false) to type Bit(false) + x cannot cast expression of type Float(Some(64), false) to type Bit(false) ,-[test:3:17] 2 | float[64] x = 42.; 3 | bit y = x; diff --git a/compiler/qsc_qasm3/src/semantic/tests/statements/box_stmt.rs b/compiler/qsc_qasm3/src/semantic/tests/statements/box_stmt.rs index 5089ce953f..1a78ce96f8 100644 --- a/compiler/qsc_qasm3/src/semantic/tests/statements/box_stmt.rs +++ b/compiler/qsc_qasm3/src/semantic/tests/statements/box_stmt.rs @@ -18,7 +18,7 @@ fn with_invalid_instruction_fails() { annotations: kind: Err - [Qsc.Qasm3.Compile.ClassicalStmtInBox + [Qsc.Qasm3.Lowerer.ClassicalStmtInBox x invalid classical statement in box ,-[test:2:9] @@ -27,7 +27,7 @@ fn with_invalid_instruction_fails() { : ^^^^^^ 3 | } `---- - , Qsc.Qasm3.Compile.Unimplemented + , Qsc.Qasm3.Lowerer.Unimplemented x this statement is not yet handled during OpenQASM 3 import: box stmt ,-[test:1:1] @@ -51,14 +51,14 @@ fn with_duration_fails() { annotations: kind: Err - [Qsc.Qasm3.Compile.NotSupported + [Qsc.Qasm3.Lowerer.NotSupported - x Box with duration are not supported. + x Box with duration are not supported ,-[test:1:6] 1 | box [4us] { } : ^^^ `---- - , Qsc.Qasm3.Compile.Unimplemented + , Qsc.Qasm3.Lowerer.Unimplemented x this statement is not yet handled during OpenQASM 3 import: box stmt ,-[test:1:1] diff --git a/compiler/qsc_qasm3/src/semantic/tests/statements/break_stmt.rs b/compiler/qsc_qasm3/src/semantic/tests/statements/break_stmt.rs index 5e3c9fcc3b..f677e18f70 100644 --- a/compiler/qsc_qasm3/src/semantic/tests/statements/break_stmt.rs +++ b/compiler/qsc_qasm3/src/semantic/tests/statements/break_stmt.rs @@ -79,9 +79,9 @@ fn break_in_non_loop_scope_fails() { annotations: kind: Err - [Qsc.Qasm3.Compile.InvalidScope + [Qsc.Qasm3.Lowerer.InvalidScope - x break can only appear in loop scopes. + x break can only appear in loop scopes ,-[test:1:1] 1 | break; : ^^^^^^ @@ -123,18 +123,18 @@ fn intermediate_def_scope_fails() { annotations: kind: Err - [Qsc.Qasm3.Compile.DefDeclarationInNonGlobalScope + [Qsc.Qasm3.Lowerer.DefDeclarationInNonGlobalScope - x Def declarations must be done in global scope. + x def declarations must be done in global scope ,-[test:3:13] 2 | while (true) { 3 | def f() { break; } : ^^^^^^^^^^^^^^^^^^ 4 | } `---- - , Qsc.Qasm3.Compile.InvalidScope + , Qsc.Qasm3.Lowerer.InvalidScope - x break can only appear in loop scopes. + x break can only appear in loop scopes ,-[test:3:23] 2 | while (true) { 3 | def f() { break; } diff --git a/compiler/qsc_qasm3/src/semantic/tests/statements/continue_stmt.rs b/compiler/qsc_qasm3/src/semantic/tests/statements/continue_stmt.rs index 7a73aaae27..4709fec712 100644 --- a/compiler/qsc_qasm3/src/semantic/tests/statements/continue_stmt.rs +++ b/compiler/qsc_qasm3/src/semantic/tests/statements/continue_stmt.rs @@ -79,9 +79,9 @@ fn continue_in_non_loop_scope_fails() { annotations: kind: Err - [Qsc.Qasm3.Compile.InvalidScope + [Qsc.Qasm3.Lowerer.InvalidScope - x continue can only appear in loop scopes. + x continue can only appear in loop scopes ,-[test:1:1] 1 | continue; : ^^^^^^^^^ @@ -123,18 +123,18 @@ fn intermediate_def_scope_fails() { annotations: kind: Err - [Qsc.Qasm3.Compile.DefDeclarationInNonGlobalScope + [Qsc.Qasm3.Lowerer.DefDeclarationInNonGlobalScope - x Def declarations must be done in global scope. + x def declarations must be done in global scope ,-[test:3:13] 2 | while (true) { 3 | def f() { continue; } : ^^^^^^^^^^^^^^^^^^^^^ 4 | } `---- - , Qsc.Qasm3.Compile.InvalidScope + , Qsc.Qasm3.Lowerer.InvalidScope - x continue can only appear in loop scopes. + x continue can only appear in loop scopes ,-[test:3:23] 2 | while (true) { 3 | def f() { continue; } diff --git a/compiler/qsc_qasm3/src/semantic/tests/statements/for_stmt.rs b/compiler/qsc_qasm3/src/semantic/tests/statements/for_stmt.rs index 147c328b78..665f2853b0 100644 --- a/compiler/qsc_qasm3/src/semantic/tests/statements/for_stmt.rs +++ b/compiler/qsc_qasm3/src/semantic/tests/statements/for_stmt.rs @@ -30,9 +30,9 @@ fn shadowing_loop_variable_in_single_stmt_body_fails() { ty: Int(None, false) kind: Lit: Int(2) - [Qsc.Qasm3.Compile.RedefinedSymbol + [Qsc.Qasm3.Lowerer.RedefinedSymbol - x Redefined symbol: x. + x redefined symbol: x ,-[test:3:13] 2 | for int x in {} 3 | int x = 2; diff --git a/compiler/qsc_qasm3/src/semantic/tests/statements/if_stmt.rs b/compiler/qsc_qasm3/src/semantic/tests/statements/if_stmt.rs index 850228461a..a700f322c1 100644 --- a/compiler/qsc_qasm3/src/semantic/tests/statements/if_stmt.rs +++ b/compiler/qsc_qasm3/src/semantic/tests/statements/if_stmt.rs @@ -39,9 +39,9 @@ fn if_branch_doesnt_create_its_own_scope() { kind: Lit: Int(1) else_body: - [Qsc.Qasm3.Compile.RedefinedSymbol + [Qsc.Qasm3.Lowerer.RedefinedSymbol - x Redefined symbol: a. + x redefined symbol: a ,-[test:3:19] 2 | int a = 2; 3 | if (true) int a = 1; @@ -90,9 +90,9 @@ fn else_branch_doesnt_create_its_own_scope() { ty: Int(None, false) kind: Lit: Int(1) - [Qsc.Qasm3.Compile.RedefinedSymbol + [Qsc.Qasm3.Lowerer.RedefinedSymbol - x Redefined symbol: a. + x redefined symbol: a ,-[test:4:14] 3 | if (true) {} 4 | else int a = 1; diff --git a/compiler/qsc_qasm3/src/semantic/tests/statements/switch_stmt.rs b/compiler/qsc_qasm3/src/semantic/tests/statements/switch_stmt.rs index fd72b20b3b..63a1aee080 100644 --- a/compiler/qsc_qasm3/src/semantic/tests/statements/switch_stmt.rs +++ b/compiler/qsc_qasm3/src/semantic/tests/statements/switch_stmt.rs @@ -30,7 +30,7 @@ fn not_supported_before_version_3_1() { block: Block [43-45]: default_case: - [Qsc.Qasm3.Compile.NotSupportedInThisVersion + [Qsc.Qasm3.Lowerer.NotSupportedInThisVersion x switch statements were introduced in version 3.1 ,-[test:3:5] diff --git a/compiler/qsc_qasm3/src/tests/declaration.rs b/compiler/qsc_qasm3/src/tests/declaration.rs index debae1f218..209de9563b 100644 --- a/compiler/qsc_qasm3/src/tests/declaration.rs +++ b/compiler/qsc_qasm3/src/tests/declaration.rs @@ -70,8 +70,8 @@ fn duration_literal() -> miette::Result<(), Vec> { assert_eq!(unit.errors.len(), 10); for error in &unit.errors { assert!([ - "Duration type values are not supported.", - "Timing literals are not supported.", + "duration type values are not supported", + "timing literals are not supported", ] .contains(&error.to_string().as_str())); } @@ -100,8 +100,8 @@ fn stretch() { assert!(unit.errors.len() == 2); assert!(unit.errors[0] .to_string() - .contains("Stretch type values are not supported."),); + .contains("stretch type values are not supported"),); assert!(unit.errors[1] .to_string() - .contains("Stretch default values are not supported."),); + .contains("stretch default values are not supported"),); } diff --git a/compiler/qsc_qasm3/src/tests/declaration/def.rs b/compiler/qsc_qasm3/src/tests/declaration/def.rs index 3e1f200e5c..315eb94ab6 100644 --- a/compiler/qsc_qasm3/src/tests/declaration/def.rs +++ b/compiler/qsc_qasm3/src/tests/declaration/def.rs @@ -124,9 +124,9 @@ fn return_expr_on_void_function_fails() { }; expect![[r#" - [Qsc.Qasm3.Compile.ReturningExpressionFromVoidSubroutine + [Qsc.Qasm3.Lowerer.ReturningExpressionFromVoidSubroutine - x Cannot return an expression from a void subroutine. + x cannot return an expression from a void subroutine ,-[Test.qasm:3:20] 2 | def square(int val) { 3 | return val; @@ -150,10 +150,9 @@ fn missing_return_expr_on_non_void_function_fails() { }; expect![[r#" - [Qsc.Qasm3.Compile.MissingTargetExpressionInReturnStmt + [Qsc.Qasm3.Lowerer.MissingTargetExpressionInReturnStmt - x Return statements on a non-void subroutine should have a target - | expression. + x return statements on a non-void subroutine should have a target expression ,-[Test.qasm:3:13] 2 | def square(int a) -> bit { 3 | return; @@ -199,18 +198,18 @@ fn capturing_non_const_external_variable_fails() { }; expect![[r#" - [Qsc.Qasm3.Compile.UndefinedSymbol + [Qsc.Qasm3.Lowerer.UndefinedSymbol - x Undefined symbol: a. + x undefined symbol: a ,-[Test.qasm:4:20] 3 | def f() -> int { 4 | return a; : ^ 5 | } `---- - , Qsc.Qasm3.Compile.CannotCast + , Qsc.Qasm3.Lowerer.CannotCast - x Cannot cast expression of type Err to type Int(None, false) + x cannot cast expression of type Err to type Int(None, false) ,-[Test.qasm:4:20] 3 | def f() -> int { 4 | return a; @@ -245,9 +244,9 @@ fn capturing_non_const_evaluatable_external_variable_fails() { : ^^^^ 3 | def f() -> int { `---- - , Qsc.Qasm3.Compile.ExprMustBeConst + , Qsc.Qasm3.Lowerer.ExprMustBeConst - x A captured variable must be a const expression + x a captured variable must be a const expression ,-[Test.qasm:4:20] 3 | def f() -> int { 4 | return a; diff --git a/compiler/qsc_qasm3/src/tests/declaration/gate.rs b/compiler/qsc_qasm3/src/tests/declaration/gate.rs index 879b0cb354..219312632e 100644 --- a/compiler/qsc_qasm3/src/tests/declaration/gate.rs +++ b/compiler/qsc_qasm3/src/tests/declaration/gate.rs @@ -120,18 +120,18 @@ fn capturing_non_const_external_variable_fails() { }; expect![[r#" - [Qsc.Qasm3.Compile.UndefinedSymbol + [Qsc.Qasm3.Lowerer.UndefinedSymbol - x Undefined symbol: a. + x undefined symbol: a ,-[Test.qasm:4:21] 3 | gate my_gate q { 4 | int x = a; : ^ 5 | } `---- - , Qsc.Qasm3.Compile.CannotCast + , Qsc.Qasm3.Lowerer.CannotCast - x Cannot cast expression of type Err to type Int(None, false) + x cannot cast expression of type Err to type Int(None, false) ,-[Test.qasm:4:21] 3 | gate my_gate q { 4 | int x = a; @@ -166,9 +166,9 @@ fn capturing_non_const_evaluatable_external_variable_fails() { : ^^^^ 3 | gate my_gate q { `---- - , Qsc.Qasm3.Compile.ExprMustBeConst + , Qsc.Qasm3.Lowerer.ExprMustBeConst - x A captured variable must be a const expression + x a captured variable must be a const expression ,-[Test.qasm:4:21] 3 | gate my_gate q { 4 | int x = a; diff --git a/compiler/qsc_qasm3/src/tests/declaration/io/explicit_input.rs b/compiler/qsc_qasm3/src/tests/declaration/io/explicit_input.rs index 7224dc346f..afd9795fbe 100644 --- a/compiler/qsc_qasm3/src/tests/declaration/io/explicit_input.rs +++ b/compiler/qsc_qasm3/src/tests/declaration/io/explicit_input.rs @@ -232,7 +232,7 @@ input angle a; assert_eq!( error[0].to_string(), - "use `float` types for passing input, using `angle` types are not supported." + "use `float` types for passing input, using `angle` types are not supported" ); } diff --git a/compiler/qsc_qasm3/src/tests/expression/function_call.rs b/compiler/qsc_qasm3/src/tests/expression/function_call.rs index 1599bf4d13..41b29a9f6b 100644 --- a/compiler/qsc_qasm3/src/tests/expression/function_call.rs +++ b/compiler/qsc_qasm3/src/tests/expression/function_call.rs @@ -141,9 +141,9 @@ fn funcall_with_too_few_arguments_generates_error() { }; expect![[r#" - [Qsc.Qasm3.Compile.InvalidNumberOfClassicalArgs + [Qsc.Qasm3.Lowerer.InvalidNumberOfClassicalArgs - x Gate expects 1 classical arguments, but 0 were provided. + x gate expects 1 classical arguments, but 0 were provided ,-[Test.qasm:6:9] 5 | 6 | square(); @@ -169,9 +169,9 @@ fn funcall_with_too_many_arguments_generates_error() { }; expect![[r#" - [Qsc.Qasm3.Compile.InvalidNumberOfClassicalArgs + [Qsc.Qasm3.Lowerer.InvalidNumberOfClassicalArgs - x Gate expects 1 classical arguments, but 2 were provided. + x gate expects 1 classical arguments, but 2 were provided ,-[Test.qasm:6:9] 5 | 6 | square(2, 3); @@ -248,9 +248,9 @@ fn classical_decl_initialized_with_incompatible_funcall_errors() { }; expect![[r#" - [Qsc.Qasm3.Compile.CannotCast + [Qsc.Qasm3.Lowerer.CannotCast - x Cannot cast expression of type Float(None, false) to type Bit(false) + x cannot cast expression of type Float(None, false) to type Bit(false) ,-[Test.qasm:6:17] 5 | 6 | bit a = square(2.0); @@ -304,9 +304,9 @@ fn funcall_implicit_arg_cast_uint_to_qubit_errors() { }; expect![[r#" - [Qsc.Qasm3.Compile.CannotCast + [Qsc.Qasm3.Lowerer.CannotCast - x Cannot cast expression of type Int(None, true) to type QubitArray(One(2)) + x cannot cast expression of type Int(None, true) to type QubitArray(One(2)) ,-[Test.qasm:6:24] 5 | 6 | bit p = parity(2); diff --git a/compiler/qsc_qasm3/src/tests/expression/ident.rs b/compiler/qsc_qasm3/src/tests/expression/ident.rs index b7a5b1ce6b..53b7719c04 100644 --- a/compiler/qsc_qasm3/src/tests/expression/ident.rs +++ b/compiler/qsc_qasm3/src/tests/expression/ident.rs @@ -18,9 +18,9 @@ fn unresolved_idenfiers_raise_symbol_error() { let errs: Vec<_> = errors.iter().map(|e| format!("{e:?}")).collect(); let errs_string = errs.join("\n"); expect![[r#" - Qsc.Qasm3.Compile.UndefinedSymbol + Qsc.Qasm3.Lowerer.UndefinedSymbol - x Undefined symbol: t. + x undefined symbol: t ,-[Test.qasm:2:19] 1 | 2 | float x = t; @@ -28,9 +28,9 @@ fn unresolved_idenfiers_raise_symbol_error() { 3 | `---- - Qsc.Qasm3.Compile.CannotCast + Qsc.Qasm3.Lowerer.CannotCast - x Cannot cast expression of type Err to type Float(None, false) + x cannot cast expression of type Err to type Float(None, false) ,-[Test.qasm:2:19] 1 | 2 | float x = t; @@ -54,7 +54,7 @@ fn redefining_symbols_in_same_scope_raise_symbol_error() { panic!("Expected an error"); }; assert_eq!(1, errors.len(), "Expected one error"); - expect![r#"Redefined symbol: x."#].assert_eq(&errors[0].to_string()); + expect!["redefined symbol: x"].assert_eq(&errors[0].to_string()); } #[test] diff --git a/compiler/qsc_qasm3/src/tests/expression/implicit_cast_from_bit.rs b/compiler/qsc_qasm3/src/tests/expression/implicit_cast_from_bit.rs index 9ff05cade3..7079da8755 100644 --- a/compiler/qsc_qasm3/src/tests/expression/implicit_cast_from_bit.rs +++ b/compiler/qsc_qasm3/src/tests/expression/implicit_cast_from_bit.rs @@ -162,6 +162,6 @@ fn to_implicit_float_implicitly_fails() { panic!("Expected error") }; - expect![["Cannot cast expression of type Bit(false) to type Float(None, false)"]] + expect!["cannot cast expression of type Bit(false) to type Float(None, false)"] .assert_eq(&error[0].to_string()); } diff --git a/compiler/qsc_qasm3/src/tests/expression/implicit_cast_from_bitarray.rs b/compiler/qsc_qasm3/src/tests/expression/implicit_cast_from_bitarray.rs index c76bffefd4..b956faaebe 100644 --- a/compiler/qsc_qasm3/src/tests/expression/implicit_cast_from_bitarray.rs +++ b/compiler/qsc_qasm3/src/tests/expression/implicit_cast_from_bitarray.rs @@ -98,7 +98,7 @@ fn to_int_with_higher_width_implicitly_fails() { panic!("Expected error") }; - expect!["Cannot cast expression of type BitArray(One(5), false) to type Int(Some(6), false)"] + expect!["cannot cast expression of type BitArray(One(5), false) to type Int(Some(6), false)"] .assert_eq(&error[0].to_string()); } @@ -113,7 +113,7 @@ fn to_int_with_higher_width_decl_implicitly_fails() { panic!("Expected error") }; - expect!["Cannot cast expression of type BitArray(One(5), false) to type Int(Some(6), false)"] + expect!["cannot cast expression of type BitArray(One(5), false) to type Int(Some(6), false)"] .assert_eq(&error[0].to_string()); } @@ -129,7 +129,7 @@ fn to_int_with_lower_width_implicitly_fails() { panic!("Expected error") }; - expect!["Cannot cast expression of type BitArray(One(5), false) to type Int(Some(4), false)"] + expect!["cannot cast expression of type BitArray(One(5), false) to type Int(Some(4), false)"] .assert_eq(&error[0].to_string()); } @@ -144,6 +144,6 @@ fn to_int_with_lower_width_decl_implicitly_fails() { panic!("Expected error") }; - expect!["Cannot cast expression of type BitArray(One(5), false) to type Int(Some(4), false)"] + expect!["cannot cast expression of type BitArray(One(5), false) to type Int(Some(4), false)"] .assert_eq(&error[0].to_string()); } diff --git a/compiler/qsc_qasm3/src/tests/expression/implicit_cast_from_float.rs b/compiler/qsc_qasm3/src/tests/expression/implicit_cast_from_float.rs index a1de1bd0e0..3b67688e0f 100644 --- a/compiler/qsc_qasm3/src/tests/expression/implicit_cast_from_float.rs +++ b/compiler/qsc_qasm3/src/tests/expression/implicit_cast_from_float.rs @@ -17,7 +17,7 @@ fn to_bit_implicitly() { panic!("Expected error") }; - expect![["Cannot cast expression of type Float(None, false) to type Bit(false)"]] + expect!["cannot cast expression of type Float(None, false) to type Bit(false)"] .assert_eq(&error[0].to_string()); } @@ -32,7 +32,7 @@ fn explicit_width_to_bit_implicitly() { panic!("Expected error") }; - expect![[r#"Cannot cast expression of type Float(Some(64), false) to type Bit(false)"#]] + expect!["cannot cast expression of type Float(Some(64), false) to type Bit(false)"] .assert_eq(&error[0].to_string()); } diff --git a/compiler/qsc_qasm3/src/tests/expression/indexed.rs b/compiler/qsc_qasm3/src/tests/expression/indexed.rs index ab308947d3..e80a73bdaf 100644 --- a/compiler/qsc_qasm3/src/tests/expression/indexed.rs +++ b/compiler/qsc_qasm3/src/tests/expression/indexed.rs @@ -19,7 +19,7 @@ fn indexed_bit_cannot_be_implicitly_converted_to_float() { }; assert_eq!(1, errors.len(), "Expected one error"); - expect!["Cannot cast expression of type Bit(false) to type Float(None, true)"] + expect!["cannot cast expression of type Bit(false) to type Float(None, true)"] .assert_eq(&errors[0].to_string()); } diff --git a/compiler/qsc_qasm3/src/tests/expression/unary.rs b/compiler/qsc_qasm3/src/tests/expression/unary.rs index fd6b709470..da6127bb22 100644 --- a/compiler/qsc_qasm3/src/tests/expression/unary.rs +++ b/compiler/qsc_qasm3/src/tests/expression/unary.rs @@ -18,9 +18,9 @@ fn bitwise_not_int_fails() { }; expect![[r#" - [Qsc.Qasm3.Compile.TypeDoesNotSupportedUnaryNegation + [Qsc.Qasm3.Lowerer.TypeDoesNotSupportedUnaryNegation - x Unary negation is not allowed for instances of Int(None, false). + x unary negation is not allowed for instances of Int(None, false) ,-[Test.qasm:3:18] 2 | int x = 5; 3 | int y = ~x; diff --git a/compiler/qsc_qasm3/src/tests/scopes.rs b/compiler/qsc_qasm3/src/tests/scopes.rs index c200ad0ffd..15270a3562 100644 --- a/compiler/qsc_qasm3/src/tests/scopes.rs +++ b/compiler/qsc_qasm3/src/tests/scopes.rs @@ -52,7 +52,7 @@ fn cannot_access_mutable_decls_from_global_scope() { let Err(errors) = compile_qasm_to_qsharp(source) else { panic!("Expected an error"); }; - expect![r#"Undefined symbol: i."#].assert_eq(&errors[0].to_string()); + expect!["undefined symbol: i"].assert_eq(&errors[0].to_string()); } #[test] diff --git a/compiler/qsc_qasm3/src/tests/statement/annotation.rs b/compiler/qsc_qasm3/src/tests/statement/annotation.rs index 672058209d..b02929c326 100644 --- a/compiler/qsc_qasm3/src/tests/statement/annotation.rs +++ b/compiler/qsc_qasm3/src/tests/statement/annotation.rs @@ -114,7 +114,7 @@ fn unknown_annotation_raises_error() { let Err(errors) = compile_qasm_to_qsharp(source) else { panic!("Expected an error"); }; - expect![r#"Unexpected annotation: @SomeUnknownAnnotation."#].assert_eq(&errors[0].to_string()); + expect!["unexpected annotation: @SomeUnknownAnnotation"].assert_eq(&errors[0].to_string()); } #[test] diff --git a/compiler/qsc_qasm3/src/tests/statement/const_eval.rs b/compiler/qsc_qasm3/src/tests/statement/const_eval.rs index e4060157e8..b948bb94a2 100644 --- a/compiler/qsc_qasm3/src/tests/statement/const_eval.rs +++ b/compiler/qsc_qasm3/src/tests/statement/const_eval.rs @@ -86,7 +86,7 @@ fn non_const_exprs_fail_in_bitarray_size_position() { 6 | bit[c] r2; `---- - Qsc.Qasm3.Compile.ExprMustBeConst + Qsc.Qasm3.Lowerer.ExprMustBeConst x designator must be a const expression ,-[Test.qasm:5:13] @@ -106,7 +106,7 @@ fn non_const_exprs_fail_in_bitarray_size_position() { 7 | `---- - Qsc.Qasm3.Compile.ExprMustBeConst + Qsc.Qasm3.Lowerer.ExprMustBeConst x designator must be a const expression ,-[Test.qasm:6:13] @@ -487,9 +487,9 @@ fn binary_op_shl_creg_fails() { 4 | bit[b] r; `---- - Qsc.Qasm3.Compile.UndefinedSymbol + Qsc.Qasm3.Lowerer.UndefinedSymbol - x Undefined symbol: b. + x undefined symbol: b ,-[Test.qasm:4:13] 3 | const creg b[3] = a << 2; 4 | bit[b] r; @@ -497,9 +497,9 @@ fn binary_op_shl_creg_fails() { 5 | `---- - Qsc.Qasm3.Compile.CannotCast + Qsc.Qasm3.Lowerer.CannotCast - x Cannot cast expression of type Err to type UInt(None, true) + x cannot cast expression of type Err to type UInt(None, true) ,-[Test.qasm:4:13] 3 | const creg b[3] = a << 2; 4 | bit[b] r; @@ -517,7 +517,7 @@ fn binary_op_shl_creg_fails() { 5 | `---- - Qsc.Qasm3.Compile.ExprMustBeConst + Qsc.Qasm3.Lowerer.ExprMustBeConst x designator must be a const expression ,-[Test.qasm:4:13] @@ -660,9 +660,9 @@ fn binary_op_shr_creg_fails() { 4 | bit[b] r; `---- - Qsc.Qasm3.Compile.UndefinedSymbol + Qsc.Qasm3.Lowerer.UndefinedSymbol - x Undefined symbol: b. + x undefined symbol: b ,-[Test.qasm:4:13] 3 | const creg b[4] = a >> 2; 4 | bit[b] r; @@ -670,9 +670,9 @@ fn binary_op_shr_creg_fails() { 5 | `---- - Qsc.Qasm3.Compile.CannotCast + Qsc.Qasm3.Lowerer.CannotCast - x Cannot cast expression of type Err to type UInt(None, true) + x cannot cast expression of type Err to type UInt(None, true) ,-[Test.qasm:4:13] 3 | const creg b[4] = a >> 2; 4 | bit[b] r; @@ -690,7 +690,7 @@ fn binary_op_shr_creg_fails() { 5 | `---- - Qsc.Qasm3.Compile.ExprMustBeConst + Qsc.Qasm3.Lowerer.ExprMustBeConst x designator must be a const expression ,-[Test.qasm:4:13] diff --git a/compiler/qsc_qasm3/src/tests/statement/for_loop.rs b/compiler/qsc_qasm3/src/tests/statement/for_loop.rs index ea708fccef..091037302b 100644 --- a/compiler/qsc_qasm3/src/tests/statement/for_loop.rs +++ b/compiler/qsc_qasm3/src/tests/statement/for_loop.rs @@ -161,5 +161,5 @@ fn loop_variables_should_be_scoped_to_for_loop() { panic!("Expected error"); }; - expect![[r#"Undefined symbol: i."#]].assert_eq(&errors[0].to_string()); + expect!["undefined symbol: i"].assert_eq(&errors[0].to_string()); } diff --git a/compiler/qsc_qasm3/src/tests/statement/gate_call.rs b/compiler/qsc_qasm3/src/tests/statement/gate_call.rs index 1586494757..76361bbcc3 100644 --- a/compiler/qsc_qasm3/src/tests/statement/gate_call.rs +++ b/compiler/qsc_qasm3/src/tests/statement/gate_call.rs @@ -245,9 +245,9 @@ fn cx_called_with_one_qubit_generates_error() { }; expect![[r#" - [Qsc.Qasm3.Compile.InvalidNumberOfQubitArgs + [Qsc.Qasm3.Lowerer.InvalidNumberOfQubitArgs - x Gate expects 2 qubit arguments, but 1 were provided. + x gate expects 2 qubit arguments, but 1 were provided ,-[Test.qasm:4:9] 3 | qubit[2] q; 4 | cx q[0]; @@ -271,9 +271,9 @@ fn cx_called_with_too_many_qubits_generates_error() { }; expect![[r#" - [Qsc.Qasm3.Compile.InvalidNumberOfQubitArgs + [Qsc.Qasm3.Lowerer.InvalidNumberOfQubitArgs - x Gate expects 2 qubit arguments, but 3 were provided. + x gate expects 2 qubit arguments, but 3 were provided ,-[Test.qasm:4:9] 3 | qubit[3] q; 4 | cx q[0], q[1], q[2]; @@ -297,9 +297,9 @@ fn rx_gate_with_no_angles_generates_error() { }; expect![[r#" - [Qsc.Qasm3.Compile.InvalidNumberOfClassicalArgs + [Qsc.Qasm3.Lowerer.InvalidNumberOfClassicalArgs - x Gate expects 1 classical arguments, but 0 were provided. + x gate expects 1 classical arguments, but 0 were provided ,-[Test.qasm:4:9] 3 | qubit q; 4 | rx q; @@ -343,9 +343,9 @@ fn rx_gate_with_too_many_angles_generates_error() { }; expect![[r#" - [Qsc.Qasm3.Compile.InvalidNumberOfClassicalArgs + [Qsc.Qasm3.Lowerer.InvalidNumberOfClassicalArgs - x Gate expects 1 classical arguments, but 2 were provided. + x gate expects 1 classical arguments, but 2 were provided ,-[Test.qasm:4:9] 3 | qubit q; 4 | rx(2.0, 3.0) q; diff --git a/compiler/qsc_qasm3/src/tests/statement/if_stmt.rs b/compiler/qsc_qasm3/src/tests/statement/if_stmt.rs index a5714850fd..bfc7839d6e 100644 --- a/compiler/qsc_qasm3/src/tests/statement/if_stmt.rs +++ b/compiler/qsc_qasm3/src/tests/statement/if_stmt.rs @@ -153,6 +153,6 @@ fn using_cond_that_cannot_implicit_cast_to_bool_fail() { panic!("Expected error"); }; - expect!["Cannot cast expression of type Qubit to type Bool(false)"] + expect!["cannot cast expression of type Qubit to type Bool(false)"] .assert_eq(&errors[0].to_string()); } diff --git a/compiler/qsc_qasm3/src/tests/statement/include.rs b/compiler/qsc_qasm3/src/tests/statement/include.rs index 03e9e27717..76a0d2d74a 100644 --- a/compiler/qsc_qasm3/src/tests/statement/include.rs +++ b/compiler/qsc_qasm3/src/tests/statement/include.rs @@ -60,6 +60,75 @@ fn programs_with_includes_can_be_parsed() -> miette::Result<(), Vec> { Ok(()) } +#[test] +fn programs_with_includes_with_includes_can_be_compiled() -> miette::Result<(), Vec> { + let source0 = r#" + include "stdgates.inc"; + include "source1.qasm"; + "#; + let source1 = r#"include "source2.qasm";"#; + let source2 = "bit a;"; + let all_sources = [ + ("source0.qasm".into(), source0.into()), + ("source1.qasm".into(), source1.into()), + ("source2.qasm".into(), source2.into()), + ]; + + let config = CompilerConfig::new( + QubitSemantics::Qiskit, + OutputSemantics::Qiskit, + ProgramType::File, + Some("Test".into()), + None, + ); + let r = compile_all_with_config("source0.qasm", all_sources, config)?; + let qsharp = qsharp_from_qasm_compilation(r)?; + expect![[r#" + namespace qasm3_import { + import QasmStd.Angle.*; + import QasmStd.Convert.*; + import QasmStd.Intrinsic.*; + @EntryPoint() + operation Test() : Unit { + mutable a = Zero; + () + } + }"#]] + .assert_eq(&qsharp); + Ok(()) +} + +#[test] +fn including_stdgates_multiple_times_causes_symbol_redifintion_errors() { + let source0 = r#" + include "stdgates.inc"; + include "source1.qasm"; + "#; + let source1 = r#"include "source2.qasm";"#; + let source2 = r#"include "stdgates.inc";"#; + let all_sources = [ + ("source0.qasm".into(), source0.into()), + ("source1.qasm".into(), source1.into()), + ("source2.qasm".into(), source2.into()), + ]; + + let config = CompilerConfig::new( + QubitSemantics::Qiskit, + OutputSemantics::Qiskit, + ProgramType::File, + Some("Test".into()), + None, + ); + + let Err(errors) = compile_all_with_config("main.qasm", all_sources, config) else { + panic!("expected errors") + }; + + let errors: Vec<_> = errors.iter().map(|e| format!("{e}")).collect(); + let errors_string = errors.join("\n"); + expect!["Not Found Could not resolve include file: main.qasm"].assert_eq(&errors_string); +} + #[test] fn multiple_include_in_same_file_errors() { let main = r#" diff --git a/compiler/qsc_qasm3/src/tests/statement/measure.rs b/compiler/qsc_qasm3/src/tests/statement/measure.rs index f3695d51e6..e1984bce2a 100644 --- a/compiler/qsc_qasm3/src/tests/statement/measure.rs +++ b/compiler/qsc_qasm3/src/tests/statement/measure.rs @@ -110,9 +110,9 @@ fn measuring_hardware_qubits_generates_an_error() { } expect![[r#" - Qsc.Qasm3.Compile.NotSupported + Qsc.Qasm3.Compiler.NotSupported - x Hardware qubit operands are not supported. + x hardware qubit operands are not supported ,-[Test.qasm:3:21] 2 | bit c; 3 | c = measure $2; diff --git a/compiler/qsc_qasm3/src/tests/statement/while_loop.rs b/compiler/qsc_qasm3/src/tests/statement/while_loop.rs index e2910a871b..8d1ae79787 100644 --- a/compiler/qsc_qasm3/src/tests/statement/while_loop.rs +++ b/compiler/qsc_qasm3/src/tests/statement/while_loop.rs @@ -55,6 +55,6 @@ fn using_cond_that_cannot_implicit_cast_to_bool_fail() { panic!("Expected error"); }; - expect!["Cannot cast expression of type Qubit to type Bool(false)"] + expect!["cannot cast expression of type Qubit to type Bool(false)"] .assert_eq(&errors[0].to_string()); } diff --git a/pip/tests-integration/interop_qiskit/test_qir.py b/pip/tests-integration/interop_qiskit/test_qir.py index f937dee73f..2c63429fe0 100644 --- a/pip/tests-integration/interop_qiskit/test_qir.py +++ b/pip/tests-integration/interop_qiskit/test_qir.py @@ -135,7 +135,7 @@ def test_generating_qir_without_registers_raises(): try: with pytest.raises(QasmError) as ex: _ = backend.qir(circuit) - assert "Qiskit circuits must have output registers." in str(ex) + assert "Qiskit circuits must have output registers" in str(ex) except AssertionError: raise except Exception as ex: From 9a47fcee900103d4786cb6aaaa078aedf385d56c Mon Sep 17 00:00:00 2001 From: Ian Davis Date: Thu, 10 Apr 2025 16:08:52 -0700 Subject: [PATCH 097/108] Qiskit stdgates workaround (#2289) --- compiler/qsc_qasm3/src/parser.rs | 4 +- compiler/qsc_qasm3/src/semantic/lowerer.rs | 117 +++++++++++------- .../src/tests/statement/gate_call.rs | 49 +------- .../interop/qiskit/backends/backend_base.py | 2 +- .../interop_qiskit/test_gateset_qasm.py | 5 +- .../interop_qiskit/test_qir.py | 4 +- .../interop_qiskit/test_run_sim.py | 6 - 7 files changed, 85 insertions(+), 102 deletions(-) diff --git a/compiler/qsc_qasm3/src/parser.rs b/compiler/qsc_qasm3/src/parser.rs index 002bb03543..fb1225d34a 100644 --- a/compiler/qsc_qasm3/src/parser.rs +++ b/compiler/qsc_qasm3/src/parser.rs @@ -296,9 +296,7 @@ where let file_path = &include.filename; // Skip the standard gates include file. // Handling of this file is done by the compiler. - if file_path.to_lowercase() == "stdgates.inc" - || file_path.to_lowercase() == "qiskit_stdgates.inc" - { + if file_path.to_lowercase() == "stdgates.inc" { continue; } let source = parse_qasm_file(file_path, resolver); diff --git a/compiler/qsc_qasm3/src/semantic/lowerer.rs b/compiler/qsc_qasm3/src/semantic/lowerer.rs index 23e532ba40..169f6d03b3 100644 --- a/compiler/qsc_qasm3/src/semantic/lowerer.rs +++ b/compiler/qsc_qasm3/src/semantic/lowerer.rs @@ -22,6 +22,7 @@ use num_traits::FromPrimitive; use num_traits::Num; use qsc_data_structures::span::Span; use qsc_frontend::{compile::SourceMap, error::WithSource}; +use rustc_hash::FxHashMap; use super::symbols::{IOKind, Symbol, SymbolTable}; @@ -167,14 +168,7 @@ impl Lowerer { // special case for stdgates.inc // it won't be in the includes list if include.filename.to_lowercase() == "stdgates.inc" { - self.define_stdgates(include); - continue; - } - - // special case for stdgates.inc - // it won't be in the includes list - if include.filename.to_lowercase() == "qiskit_stdgates.inc" { - self.define_qiskit_stadandard_gates(include); + self.define_stdgates(include.span); continue; } @@ -238,7 +232,7 @@ impl Lowerer { /// The sdg, tdg, crx, cry, crz, and ch are defined /// as their bare gates, and modifiers are applied /// when calling them. - fn define_stdgates(&mut self, include: &syntax::IncludeStmt) { + fn define_stdgates(&mut self, span: Span) { fn gate_symbol(name: &str, cargs: u32, qargs: u32) -> Symbol { Symbol::new( name, @@ -277,7 +271,7 @@ impl Lowerer { for gate in gates { let name = gate.name.clone(); if self.symbols.insert_symbol(gate).is_err() { - self.push_redefined_symbol_error(name.as_str(), include.span); + self.push_redefined_symbol_error(name.as_str(), span); } } } @@ -289,7 +283,45 @@ impl Lowerer { /// the symbol table is complete and we can lower the QASM3. /// We must also define the gates in the `QasmStd` module so /// that we can compile the QASM3 to Q#. - fn define_qiskit_stadandard_gates(&mut self, include: &syntax::IncludeStmt) { + fn define_qiskit_standard_gate_if_needed(&mut self, name: S, span: Span) + where + S: AsRef, + { + const QISKIT_STDGATES: [&str; 20] = [ + "rxx", + "ryy", + "rzz", + "dcx", + "ecr", + "r", + "rzx", + "cs", + "csdg", + "sxdg", + "csx", + "cu1", + "cu3", + "rccx", + "c3sqrtx", + "c3x", + "rc3x", + "xx_minus_yy", + "xx_plus_yy", + "ccz", + ]; + // only define the gate if it is not already defined + // and it is in the list of Qiskit standard gates + if self.symbols.get_symbol_by_name(&name).is_none() + && QISKIT_STDGATES.contains(&name.as_ref()) + { + self.define_qiskit_standard_gate(name, span); + } + } + + fn define_qiskit_standard_gate(&mut self, name: S, span: Span) + where + S: AsRef, + { fn gate_symbol(name: &str, cargs: u32, qargs: u32) -> Symbol { Symbol::new( name, @@ -305,34 +337,32 @@ impl Lowerer { // Remaining gates that are not in the qasm std library, but are standard gates in Qiskit // that Qiskit wont emit correctly. // dcx, ecr, r, rzx, cs, csdg, sxdg, csx, cu1, cu3, rccx, c3sqrtx, c3x, rc3x, xx_minus_yy, xx_plus_yy, ccz; - - let gates = vec![ - gate_symbol("rxx", 1, 2), - gate_symbol("ryy", 1, 2), - gate_symbol("rzz", 1, 2), - gate_symbol("dcx", 0, 2), - gate_symbol("ecr", 0, 2), - gate_symbol("r", 2, 1), - gate_symbol("rzx", 1, 2), - gate_symbol("cs", 0, 2), - gate_symbol("csdg", 0, 2), - gate_symbol("sxdg", 0, 1), - gate_symbol("csx", 0, 2), - gate_symbol("cu1", 1, 2), - gate_symbol("cu3", 3, 2), - gate_symbol("rccx", 0, 3), - gate_symbol("c3sqrtx", 0, 4), - gate_symbol("c3x", 0, 4), - gate_symbol("rc3x", 0, 4), - gate_symbol("xx_minus_yy", 2, 2), - gate_symbol("xx_plus_yy", 2, 2), - gate_symbol("ccz", 0, 3), - ]; - for gate in gates { - let name = gate.name.clone(); - if self.symbols.insert_symbol(gate).is_err() { - self.push_redefined_symbol_error(name.as_str(), include.span); - } + //iter) + let gates = FxHashMap::from_iter([ + ("rxx", gate_symbol("rxx", 1, 2)), + ("ryy", gate_symbol("ryy", 1, 2)), + ("rzz", gate_symbol("rzz", 1, 2)), + ("dcx", gate_symbol("dcx", 0, 2)), + ("ecr", gate_symbol("ecr", 0, 2)), + ("r", gate_symbol("r", 2, 1)), + ("rzx", gate_symbol("rzx", 1, 2)), + ("cs", gate_symbol("cs", 0, 2)), + ("csdg", gate_symbol("csdg", 0, 2)), + ("sxdg", gate_symbol("sxdg", 0, 1)), + ("csx", gate_symbol("csx", 0, 2)), + ("cu1", gate_symbol("cu1", 1, 2)), + ("cu3", gate_symbol("cu3", 3, 2)), + ("rccx", gate_symbol("rccx", 0, 3)), + ("c3sqrtx", gate_symbol("c3sqrtx", 0, 4)), + ("c3x", gate_symbol("c3x", 0, 4)), + ("rc3x", gate_symbol("rc3x", 0, 4)), + ("xx_minus_yy", gate_symbol("xx_minus_yy", 2, 2)), + ("xx_plus_yy", gate_symbol("xx_plus_yy", 2, 2)), + ("ccz", gate_symbol("ccz", 0, 3)), + ]); + let gate = gates.get(name.as_ref()).expect("missing gate symbol"); + if self.symbols.insert_symbol(gate.clone()).is_err() { + self.push_redefined_symbol_error(name.as_ref(), span); } } @@ -1433,11 +1463,11 @@ impl Lowerer { } let mut name = stmt.name.name.to_string(); - if let Some((qsharp_name, implicit_modifier)) = + if let Some((gate_name, implicit_modifier)) = try_get_qsharp_name_and_implicit_modifiers(&name, stmt.name.span) { - // Override the gate name if Q# name is another. - name = qsharp_name; + // Override the gate name if we mapped with modifiers + name = gate_name; // 2. Get implicit modifiers and make them explicit. // Q: Do we need this during lowering? @@ -1445,6 +1475,9 @@ impl Lowerer { modifiers.push(implicit_modifier); } + // need a workaround for qiskit generating gate calls without having declared the gate + self.define_qiskit_standard_gate_if_needed(&name, stmt.name.span); + // 3. Check that the gate_name actually refers to a gate in the symbol table // and get its symbol_id & symbol. Make sure to use the name that could've // been overriden by the Q# name and the span of the original name. diff --git a/compiler/qsc_qasm3/src/tests/statement/gate_call.rs b/compiler/qsc_qasm3/src/tests/statement/gate_call.rs index 76361bbcc3..bfb14bec7e 100644 --- a/compiler/qsc_qasm3/src/tests/statement/gate_call.rs +++ b/compiler/qsc_qasm3/src/tests/statement/gate_call.rs @@ -578,9 +578,8 @@ fn simulatable_intrinsic_on_gate_stmt_generates_correct_qir() -> miette::Result< } #[test] -fn rxx_gate_with_one_angle_can_be_called_with_qiskit_stdgates() -> miette::Result<(), Vec> { +fn rxx_gate_with_one_angle_can_be_called() -> miette::Result<(), Vec> { let source = r#" - include "qiskit_stdgates.inc"; qubit[2] q; rxx(2.0) q[1], q[0]; "#; @@ -598,9 +597,8 @@ fn rxx_gate_with_one_angle_can_be_called_with_qiskit_stdgates() -> miette::Resul } #[test] -fn ryy_gate_with_one_angle_can_be_called_with_qiskit_stdgates() -> miette::Result<(), Vec> { +fn ryy_gate_with_one_angle_can_be_called() -> miette::Result<(), Vec> { let source = r#" - include "qiskit_stdgates.inc"; qubit[2] q; ryy(2.0) q[1], q[0]; "#; @@ -618,9 +616,8 @@ fn ryy_gate_with_one_angle_can_be_called_with_qiskit_stdgates() -> miette::Resul } #[test] -fn rzz_gate_with_one_angle_can_be_called_with_qiskit_stdgates() -> miette::Result<(), Vec> { +fn rzz_gate_with_one_angle_can_be_called() -> miette::Result<(), Vec> { let source = r#" - include "qiskit_stdgates.inc"; qubit[2] q; rzz(2.0) q[1], q[0]; "#; @@ -638,46 +635,8 @@ fn rzz_gate_with_one_angle_can_be_called_with_qiskit_stdgates() -> miette::Resul } #[test] -fn rxx_gate_cannot_be_called_without_qiskit_stdgates() -> miette::Result<(), Vec> { +fn all_qiskit_stdgates_can_be_called_included() -> miette::Result<(), Vec> { let source = r#" - include "stdgates.inc"; - qubit[2] q; - rxx(2.0) q[1], q[0]; - "#; - - compile_qasm_to_qsharp(source).expect_err("Error expected"); - Ok(()) -} - -#[test] -fn ryy_gate_cannot_be_called_without_qiskit_stdgates() -> miette::Result<(), Vec> { - let source = r#" - include "stdgates.inc"; - qubit[2] q; - ryy(2.0) q[1], q[0]; - "#; - - compile_qasm_to_qsharp(source).expect_err("Error expected"); - Ok(()) -} - -#[test] -fn rzz_gate_cannot_be_called_without_qiskit_stdgates() -> miette::Result<(), Vec> { - let source = r#" - include "stdgates.inc"; - qubit[2] q; - rzz(2.0) q[1], q[0]; - "#; - - compile_qasm_to_qsharp(source).expect_err("Error expected"); - Ok(()) -} - -#[test] -fn all_qiskit_stdgates_can_be_called_with_qiskit_stdgates_included( -) -> miette::Result<(), Vec> { - let source = r#" - include "qiskit_stdgates.inc"; qubit[4] q; rxx(pi / 2.0) q[1], q[0]; ryy(pi / 2.0) q[1], q[0]; diff --git a/pip/qsharp/interop/qiskit/backends/backend_base.py b/pip/qsharp/interop/qiskit/backends/backend_base.py index df79b43df0..d54beba15a 100644 --- a/pip/qsharp/interop/qiskit/backends/backend_base.py +++ b/pip/qsharp/interop/qiskit/backends/backend_base.py @@ -183,7 +183,7 @@ def __init__( # set the default options for the exporter self._qasm_export_options = { - "includes": ("stdgates.inc", "qiskit_stdgates.inc",), + "includes": ("stdgates.inc",), "alias_classical_registers": False, "allow_aliasing": False, "disable_constants": True, diff --git a/pip/tests-integration/interop_qiskit/test_gateset_qasm.py b/pip/tests-integration/interop_qiskit/test_gateset_qasm.py index 4037d91f16..68245e33ce 100644 --- a/pip/tests-integration/interop_qiskit/test_gateset_qasm.py +++ b/pip/tests-integration/interop_qiskit/test_gateset_qasm.py @@ -21,13 +21,12 @@ def run_transpile_test( options["optimization_level"] = 0 info = QSharpBackend()._qasm3(circuit, **options) lines = info.splitlines() - # remove the first five lines, which are the header + # remove the first four lines, which are the header # OPENQASM 3.0; # include "stdgates.inc"; - # include "qiskit_stdgates.inc"; # bit[3] c; # qubit[3] q; - remaining_lines = lines[5:] + remaining_lines = lines[4:] result = "\n".join(remaining_lines) assert result == expected_output diff --git a/pip/tests-integration/interop_qiskit/test_qir.py b/pip/tests-integration/interop_qiskit/test_qir.py index 2c63429fe0..40fbcebc3d 100644 --- a/pip/tests-integration/interop_qiskit/test_qir.py +++ b/pip/tests-integration/interop_qiskit/test_qir.py @@ -168,7 +168,7 @@ def __init__(self): options = { "search_path": get_resource_path(), - "includes": ("stdgates.inc", "qiskit_stdgates.inc", "custom_intrinsics.inc"), + "includes": ("stdgates.inc", "custom_intrinsics.inc"), } backend = QSharpBackend(target_profile=target_profile, target=target) @@ -199,7 +199,7 @@ def __init__(self): options = { "search_path": get_resource_path(), - "includes": ("stdgates.inc", "qiskit_stdgates.inc", "custom_intrinsics.inc"), + "includes": ("stdgates.inc", "custom_intrinsics.inc"), } backend = QSharpBackend(target_profile=target_profile, target=target) diff --git a/pip/tests-integration/interop_qiskit/test_run_sim.py b/pip/tests-integration/interop_qiskit/test_run_sim.py index 435e952883..21d540e844 100644 --- a/pip/tests-integration/interop_qiskit/test_run_sim.py +++ b/pip/tests-integration/interop_qiskit/test_run_sim.py @@ -25,14 +25,8 @@ # Convert the circuit to QASM3 and back to a backend. # Then load the QASM3 and convert it back to a circuit. # This is to ensure that the QASM3 conversion is semantically correct. -# We must remove the include "qiskit_stdgates.inc" line from the QASM3 -# since the qiskit qasm loader can't support any non stdgates.inc include files. def round_trip_circuit(circuit, backend): qasm3 = backend._qasm3(circuit) - # remove any lines that contain: include "qiskit_stdgates.inc"; - qasm3 = "\n".join( - line for line in qasm3.splitlines() if "qiskit_stdgates.inc" not in line - ) circuit = from_qasm3(qasm3) return circuit From a540d6b50be44749015ca7de8baae3a0594af9a7 Mon Sep 17 00:00:00 2001 From: orpuente-MS <156957451+orpuente-MS@users.noreply.github.com> Date: Thu, 10 Apr 2025 17:13:35 -0700 Subject: [PATCH 098/108] Add benchmark to new parser (#2288) --- Cargo.lock | 1 + compiler/qsc_qasm3/Cargo.toml | 8 + .../qsc_qasm3/benches/rgqft_multiplier.rs | 38 + .../benches/rgqft_multiplier_1q.qasm | 96 + .../benches/rgqft_multiplier_4q.qasm | 3040 +++++++++++++++++ 5 files changed, 3183 insertions(+) create mode 100644 compiler/qsc_qasm3/benches/rgqft_multiplier.rs create mode 100644 compiler/qsc_qasm3/benches/rgqft_multiplier_1q.qasm create mode 100644 compiler/qsc_qasm3/benches/rgqft_multiplier_4q.qasm diff --git a/Cargo.lock b/Cargo.lock index 1971c0f52d..ea7939fde2 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1280,6 +1280,7 @@ name = "qsc_qasm3" version = "0.0.0" dependencies = [ "bitflags", + "criterion", "difference", "enum-iterator", "expect-test", diff --git a/compiler/qsc_qasm3/Cargo.toml b/compiler/qsc_qasm3/Cargo.toml index 0ba3b89e06..3ea33cefdc 100644 --- a/compiler/qsc_qasm3/Cargo.toml +++ b/compiler/qsc_qasm3/Cargo.toml @@ -24,6 +24,7 @@ rustc-hash = { workspace = true } thiserror = { workspace = true } [dev-dependencies] +criterion = { workspace = true, features = ["cargo_bench_support"] } difference = { workspace = true } expect-test = { workspace = true } indoc = { workspace = true } @@ -38,3 +39,10 @@ fs = [] [lints] workspace = true + +[lib] +bench = false + +[[bench]] +name = "rgqft_multiplier" +harness = false diff --git a/compiler/qsc_qasm3/benches/rgqft_multiplier.rs b/compiler/qsc_qasm3/benches/rgqft_multiplier.rs new file mode 100644 index 0000000000..cbf883be5a --- /dev/null +++ b/compiler/qsc_qasm3/benches/rgqft_multiplier.rs @@ -0,0 +1,38 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +use criterion::{black_box, criterion_group, criterion_main, Criterion}; +use qsc_qasm3::{ + compile_to_qsharp_ast_with_config, io::InMemorySourceResolver, CompilerConfig, OutputSemantics, + ProgramType, QasmCompileUnit, QubitSemantics, +}; + +fn rgqft_multiplier(source: &str) -> QasmCompileUnit { + let config = CompilerConfig::new( + QubitSemantics::Qiskit, + OutputSemantics::OpenQasm, + ProgramType::File, + Some("Test".into()), + None, + ); + compile_to_qsharp_ast_with_config(source, "", None::<&mut InMemorySourceResolver>, config) +} + +pub fn rgqft_multiplier_1q(c: &mut Criterion) { + const SOURCE: &str = include_str!("./rgqft_multiplier_1q.qasm"); + + c.bench_function("rgqft_multiplier_1q sample compilation", |b| { + b.iter(move || black_box(rgqft_multiplier(SOURCE))); + }); +} + +pub fn rgqft_multiplier_4q(c: &mut Criterion) { + const SOURCE: &str = include_str!("./rgqft_multiplier_4q.qasm"); + + c.bench_function("rgqft_multiplier_4q sample compilation", |b| { + b.iter(move || black_box(rgqft_multiplier(SOURCE))); + }); +} + +criterion_group!(benches, rgqft_multiplier_1q, rgqft_multiplier_4q); +criterion_main!(benches); diff --git a/compiler/qsc_qasm3/benches/rgqft_multiplier_1q.qasm b/compiler/qsc_qasm3/benches/rgqft_multiplier_1q.qasm new file mode 100644 index 0000000000..c186a33a55 --- /dev/null +++ b/compiler/qsc_qasm3/benches/rgqft_multiplier_1q.qasm @@ -0,0 +1,96 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +OPENQASM 3.0; +include "stdgates.inc"; +qubit[1] a; +qubit[1] b; +qubit[2] out; +h out[1]; +rz(pi/4) out[1]; +cx out[1], out[0]; +rz(-pi/4) out[0]; +cx out[1], out[0]; +rz(pi/4) out[0]; +h out[0]; +cx a[0], out[0]; +rz(-pi/8) out[0]; +rx(pi/2) out[0]; +rz(pi) out[0]; +rx(pi/2) out[0]; +rz(9.032078879070655) out[0]; +cx b[0], out[0]; +rz(-7*pi/8) out[0]; +rx(pi/2) out[0]; +rz(pi) out[0]; +rx(pi/2) out[0]; +rz(6.675884388878311) out[0]; +cx a[0], out[0]; +rz(-pi/8) out[0]; +rx(pi/2) out[0]; +rz(pi) out[0]; +rx(pi/2) out[0]; +rz(9.032078879070655) out[0]; +cx b[0], out[0]; +rz(pi/4) b[0]; +rx(pi/2) b[0]; +rz(pi) b[0]; +rx(pi/2) b[0]; +rz(3*pi) b[0]; +cx a[0], b[0]; +rz(-pi/4) b[0]; +rx(pi/2) b[0]; +rz(pi) b[0]; +rx(pi/2) b[0]; +rz(3*pi) b[0]; +cx a[0], b[0]; +rz(pi/4) a[0]; +cx a[0], out[1]; +rz(-7*pi/8) out[0]; +rx(pi/2) out[0]; +rz(pi) out[0]; +rx(pi/2) out[0]; +rz(6.675884388878311) out[0]; +h out[0]; +rz(-pi/16) out[1]; +rx(pi/2) out[1]; +rz(pi) out[1]; +rx(pi/2) out[1]; +rz(9.228428419920018) out[1]; +cx b[0], out[1]; +rz(-15*pi/16) out[1]; +rx(pi/2) out[1]; +rz(pi) out[1]; +rx(pi/2) out[1]; +rz(6.4795348480289485) out[1]; +cx a[0], out[1]; +rz(-pi/16) out[1]; +rx(pi/2) out[1]; +rz(pi) out[1]; +rx(pi/2) out[1]; +rz(9.228428419920018) out[1]; +cx b[0], out[1]; +rz(pi/8) b[0]; +rx(pi/2) b[0]; +rz(pi) b[0]; +rx(pi/2) b[0]; +rz(3*pi) b[0]; +cx a[0], b[0]; +rz(-pi/8) b[0]; +rx(pi/2) b[0]; +rz(pi) b[0]; +rx(pi/2) b[0]; +rz(3*pi) b[0]; +cx a[0], b[0]; +rz(pi/8) a[0]; +rz(-15*pi/16) out[1]; +rx(pi/2) out[1]; +rz(pi) out[1]; +rx(pi/2) out[1]; +rz(6.4795348480289485) out[1]; +rz(-pi/4) out[1]; +cx out[1], out[0]; +rz(pi/4) out[0]; +cx out[1], out[0]; +rz(-pi/4) out[0]; +h out[1]; diff --git a/compiler/qsc_qasm3/benches/rgqft_multiplier_4q.qasm b/compiler/qsc_qasm3/benches/rgqft_multiplier_4q.qasm new file mode 100644 index 0000000000..c1fb7a0a93 --- /dev/null +++ b/compiler/qsc_qasm3/benches/rgqft_multiplier_4q.qasm @@ -0,0 +1,3040 @@ +OPENQASM 3.0; +include "stdgates.inc"; +qubit[4] a; +qubit[4] b; +qubit[8] out; +rz(1.4787574795217566) a[0]; +rx(-pi) a[0]; +rz(-0.18407769454627942) a[1]; +rx(-pi) a[1]; +rz(2.773437264497235) a[2]; +rx(-pi) a[2]; +rz(2.405281875404682) a[3]; +rx(-pi) a[3]; +rx(-pi) b[0]; +rx(-pi) b[1]; +rx(-pi) b[2]; +rx(-pi) b[3]; +rz(pi/2) out[0]; +rz(pi/2) out[1]; +rz(pi/2) out[2]; +rz(pi/2) out[3]; +rz(pi/2) out[4]; +rz(pi/2) out[5]; +rz(-pi/2) out[6]; +rx(-pi) out[6]; +rx(1.29567233598587) out[7]; +rzz(pi/2) out[6], out[7]; +ry(pi) out[6]; +rz(-pi) out[7]; +ry(-pi/4) out[7]; +rzz(pi/2) out[6], out[7]; +rz(-pi/4) out[6]; +rx(-pi/2) out[6]; +rx(-1.0605221542064758) out[7]; +rzz(pi/2) out[5], out[7]; +ry(pi) out[5]; +rz(-pi) out[7]; +ry(-pi/8) out[7]; +rzz(pi/2) out[5], out[7]; +rz(-pi/8) out[5]; +rzz(pi/2) out[5], out[6]; +ry(pi) out[5]; +rz(-pi) out[6]; +ry(-pi/4) out[6]; +rzz(pi/2) out[5], out[6]; +rz(-pi/4) out[5]; +rx(-pi/2) out[5]; +rx(3*pi/4) out[6]; +rz(-pi) out[7]; +rx(-pi/8) out[7]; +rzz(pi/2) out[4], out[7]; +ry(pi) out[4]; +rz(-pi) out[7]; +ry(-pi/16) out[7]; +rzz(pi/2) out[4], out[7]; +rz(-pi/16) out[4]; +ry(pi) out[4]; +rzz(pi/2) out[4], out[6]; +ry(pi) out[4]; +rz(-pi) out[6]; +ry(-pi/8) out[6]; +rzz(pi/2) out[4], out[6]; +rz(-pi/8) out[4]; +rzz(pi/2) out[4], out[5]; +ry(pi) out[4]; +rz(-pi) out[5]; +ry(-pi/4) out[5]; +rzz(pi/2) out[4], out[5]; +rz(-pi/4) out[4]; +rx(-pi/2) out[4]; +rx(3*pi/4) out[5]; +rz(-pi) out[6]; +rx(-pi/8) out[6]; +rz(-pi) out[7]; +rx(-pi/16) out[7]; +rzz(pi/2) out[3], out[7]; +ry(pi) out[3]; +rz(-pi) out[7]; +ry(-pi/32) out[7]; +rzz(pi/2) out[3], out[7]; +rz(-pi/32) out[3]; +ry(pi) out[3]; +rzz(pi/2) out[3], out[6]; +ry(pi) out[3]; +rz(-pi) out[6]; +ry(-pi/16) out[6]; +rzz(pi/2) out[3], out[6]; +rz(-pi/16) out[3]; +ry(pi) out[3]; +rzz(pi/2) out[3], out[5]; +ry(pi) out[3]; +rz(-pi) out[5]; +ry(-pi/8) out[5]; +rzz(pi/2) out[3], out[5]; +rz(-pi/8) out[3]; +rzz(pi/2) out[3], out[4]; +ry(pi) out[3]; +rz(-pi) out[4]; +ry(-pi/4) out[4]; +rzz(pi/2) out[3], out[4]; +rz(-pi/4) out[3]; +rx(-pi/2) out[3]; +rx(3*pi/4) out[4]; +rz(-pi) out[5]; +rx(-pi/8) out[5]; +rz(-pi) out[6]; +rx(-pi/16) out[6]; +rz(-pi) out[7]; +rx(-pi/32) out[7]; +rzz(pi/2) out[2], out[7]; +ry(pi) out[2]; +rz(-pi) out[7]; +ry(-pi/64) out[7]; +rzz(pi/2) out[2], out[7]; +rz(-pi/64) out[2]; +ry(pi) out[2]; +rzz(pi/2) out[2], out[6]; +ry(pi) out[2]; +rz(-pi) out[6]; +ry(-pi/32) out[6]; +rzz(pi/2) out[2], out[6]; +rz(-pi/32) out[2]; +ry(pi) out[2]; +rzz(pi/2) out[2], out[5]; +ry(pi) out[2]; +rz(-pi) out[5]; +ry(-pi/16) out[5]; +rzz(pi/2) out[2], out[5]; +rz(-pi/16) out[2]; +ry(pi) out[2]; +rzz(pi/2) out[2], out[4]; +ry(pi) out[2]; +rz(-pi) out[4]; +ry(-pi/8) out[4]; +rzz(pi/2) out[2], out[4]; +rz(-pi/8) out[2]; +rzz(pi/2) out[2], out[3]; +ry(pi) out[2]; +rz(-pi) out[3]; +ry(-pi/4) out[3]; +rzz(pi/2) out[2], out[3]; +rz(-pi/4) out[2]; +rx(-pi/2) out[2]; +rx(3*pi/4) out[3]; +rz(-pi) out[4]; +rx(-pi/8) out[4]; +rz(-pi) out[5]; +rx(-pi/16) out[5]; +rz(-pi) out[6]; +rx(-pi/32) out[6]; +rz(-pi) out[7]; +rx(-pi/64) out[7]; +rzz(pi/2) out[1], out[7]; +ry(pi) out[1]; +rz(-pi) out[7]; +ry(-pi/128) out[7]; +rzz(pi/2) out[1], out[7]; +rz(-pi/128) out[1]; +ry(pi) out[1]; +rzz(pi/2) out[1], out[6]; +ry(pi) out[1]; +rz(-pi) out[6]; +ry(-pi/64) out[6]; +rzz(pi/2) out[1], out[6]; +rz(-pi/64) out[1]; +ry(pi) out[1]; +rzz(pi/2) out[1], out[5]; +ry(pi) out[1]; +rz(-pi) out[5]; +ry(-pi/32) out[5]; +rzz(pi/2) out[1], out[5]; +rz(-pi/32) out[1]; +ry(pi) out[1]; +rzz(pi/2) out[1], out[4]; +ry(pi) out[1]; +rz(-pi) out[4]; +ry(-pi/16) out[4]; +rzz(pi/2) out[1], out[4]; +rz(-pi/16) out[1]; +ry(pi) out[1]; +rzz(pi/2) out[1], out[3]; +ry(pi) out[1]; +rz(-pi) out[3]; +ry(-pi/8) out[3]; +rzz(pi/2) out[1], out[3]; +rz(-pi/8) out[1]; +rzz(pi/2) out[1], out[2]; +ry(pi) out[1]; +rz(-pi) out[2]; +ry(-pi/4) out[2]; +rzz(pi/2) out[1], out[2]; +rz(-pi/4) out[1]; +rx(-pi/2) out[1]; +rx(3*pi/4) out[2]; +rz(-pi) out[3]; +rx(-pi/8) out[3]; +rz(-pi) out[4]; +rx(-pi/16) out[4]; +rz(-pi) out[5]; +rx(-pi/32) out[5]; +rz(-pi) out[6]; +rx(-pi/64) out[6]; +rz(-pi) out[7]; +rx(-pi/128) out[7]; +rzz(pi/2) out[0], out[7]; +ry(pi) out[0]; +rz(-pi) out[7]; +ry(-pi/256) out[7]; +rzz(pi/2) out[0], out[7]; +rz(-pi/256) out[0]; +ry(pi) out[0]; +rzz(pi/2) out[0], out[6]; +ry(pi) out[0]; +rz(-pi) out[6]; +ry(-pi/128) out[6]; +rzz(pi/2) out[0], out[6]; +rz(-pi/128) out[0]; +ry(pi) out[0]; +rzz(pi/2) out[0], out[5]; +ry(pi) out[0]; +rz(-pi) out[5]; +ry(-pi/64) out[5]; +rzz(pi/2) out[0], out[5]; +rz(-pi/64) out[0]; +ry(pi) out[0]; +rzz(pi/2) out[0], out[4]; +ry(pi) out[0]; +rz(-pi) out[4]; +ry(-pi/32) out[4]; +rzz(pi/2) out[0], out[4]; +rz(-pi/32) out[0]; +ry(pi) out[0]; +rzz(pi/2) out[0], out[3]; +ry(pi) out[0]; +rz(-pi) out[3]; +ry(-pi/16) out[3]; +rzz(pi/2) out[0], out[3]; +rz(-pi/16) out[0]; +ry(pi) out[0]; +rzz(pi/2) out[0], out[2]; +ry(pi) out[0]; +rz(-pi) out[2]; +ry(-pi/8) out[2]; +rzz(pi/2) out[0], out[2]; +rz(-pi/8) out[0]; +rzz(pi/2) out[0], out[1]; +ry(pi) out[0]; +rz(-pi) out[1]; +ry(-pi/4) out[1]; +rzz(pi/2) out[0], out[1]; +rz(3*pi/4) out[0]; +ry(pi) out[0]; +rz(-pi) out[1]; +rx(-pi/4) out[1]; +rx(5*pi/8) out[2]; +rx(9*pi/16) out[3]; +rx(1.6689710972195786) out[4]; +rx(1.6198837120072371) out[5]; +rzz(pi/2) a[3], out[5]; +rz(pi/2) a[3]; +ry(-pi/2) out[5]; +rz(3*pi/4) out[5]; +rzz(pi/2) b[3], out[5]; +rz(pi/2) b[3]; +rz(pi/4) out[5]; +rx(-pi/2) out[5]; +rzz(pi/2) a[3], out[5]; +rx(-pi) a[3]; +rz(-pi/2) a[3]; +ry(-pi/2) out[5]; +rz(3*pi/4) out[5]; +rzz(pi/2) b[3], out[5]; +rz(pi/2) b[3]; +rzz(pi/2) a[3], b[3]; +rz(-pi/2) b[3]; +rx(-pi) b[3]; +rz(3*pi/4) out[5]; +ry(pi/2) out[5]; +rz(-2.1072299372197243) out[5]; +rx(-1.5462526341887264) out[6]; +rz(-0.5364336104248277) out[6]; +rzz(pi/2) a[3], out[6]; +rz(-pi/2) a[3]; +rz(-2.6051590431649654) out[6]; +ry(pi/4) out[6]; +rz(-2.5551204732437065) out[6]; +rzz(pi/2) b[3], out[6]; +rz(-pi/2) b[3]; +rz(-2.157268507140982) out[6]; +ry(pi/4) out[6]; +rz(-2.1072299372197243) out[6]; +rzz(pi/2) a[3], out[6]; +rz(-2.6051590431649654) out[6]; +ry(pi/4) out[6]; +rz(-2.5551204732437065) out[6]; +rzz(pi/2) b[3], out[6]; +ry(-pi/2) b[3]; +rzz(pi/2) a[3], b[3]; +ry(pi) a[3]; +rz(-pi) b[3]; +ry(-pi/4) b[3]; +rzz(pi/2) a[3], b[3]; +rz(-pi/2) a[3]; +rx(-pi) a[3]; +ry(pi/2) b[3]; +rz(pi/4) b[3]; +rz(-2.1572685071409827) out[6]; +ry(pi/4) out[6]; +rz(0.3325895879972256) out[6]; +rx(-1.5585244804918112) out[7]; +rz(1.9033859147921213) out[7]; +rzz(pi/2) a[3], out[7]; +rz(-pi/2) a[3]; +rz(-1.903385914792119) out[7]; +ry(7*pi/8) out[7]; +rz(-1.6971534443990168) out[7]; +rzz(pi/2) b[3], out[7]; +rz(pi/2) b[3]; +rz(0.12635711760412294) out[7]; +ry(7*pi/8) out[7]; +rz(-2.8090030655925666) out[7]; +rzz(pi/2) a[3], out[7]; +rz(-1.903385914792119) out[7]; +ry(7*pi/8) out[7]; +rz(-1.6971534443990168) out[7]; +rzz(pi/2) b[3], out[7]; +ry(-pi/2) b[3]; +rz(-pi) b[3]; +rzz(pi/2) a[3], b[3]; +ry(pi) a[3]; +rz(-pi) b[3]; +ry(-pi/8) b[3]; +rzz(pi/2) a[3], b[3]; +rz(pi/2) a[3]; +rzz(pi/2) a[3], out[4]; +rz(pi/2) a[3]; +ry(pi/2) b[3]; +rz(3*pi/8) b[3]; +ry(-pi/2) out[4]; +rz(3*pi/4) out[4]; +rzz(pi/2) b[2], out[4]; +rz(pi/2) b[2]; +rz(pi/4) out[4]; +rx(-pi/2) out[4]; +rzz(pi/2) a[3], out[4]; +rx(-pi) a[3]; +rz(-pi/2) a[3]; +ry(-pi/2) out[4]; +rz(3*pi/4) out[4]; +rzz(pi/2) b[2], out[4]; +rz(pi/2) b[2]; +rzz(pi/2) a[3], b[2]; +rzz(pi/2) a[3], out[5]; +rz(-pi/2) a[3]; +rz(-pi/2) b[2]; +rx(-pi) b[2]; +rz(3*pi/4) out[4]; +ry(pi/2) out[4]; +rz(-2.1072299372197243) out[4]; +rz(-2.6051590431649654) out[5]; +ry(pi/4) out[5]; +rz(-2.5551204732437065) out[5]; +rzz(pi/2) b[2], out[5]; +rz(-pi/2) b[2]; +rz(-2.157268507140982) out[5]; +ry(pi/4) out[5]; +rz(-2.1072299372197243) out[5]; +rzz(pi/2) a[3], out[5]; +rz(-2.6051590431649654) out[5]; +ry(pi/4) out[5]; +rz(-2.5551204732437065) out[5]; +rzz(pi/2) b[2], out[5]; +ry(-pi/2) b[2]; +rzz(pi/2) a[3], b[2]; +ry(pi) a[3]; +rz(-pi) b[2]; +ry(-pi/4) b[2]; +rzz(pi/2) a[3], b[2]; +rz(-pi/2) a[3]; +rx(-pi) a[3]; +rzz(pi/2) a[3], out[6]; +rz(-pi/2) a[3]; +ry(pi/2) b[2]; +rz(pi/4) b[2]; +rz(-2.1572685071409827) out[5]; +ry(pi/4) out[5]; +rz(0.3325895879972256) out[5]; +rz(-1.903385914792119) out[6]; +ry(7*pi/8) out[6]; +rz(-1.6971534443990168) out[6]; +rzz(pi/2) b[2], out[6]; +rz(pi/2) b[2]; +rz(0.12635711760412294) out[6]; +ry(7*pi/8) out[6]; +rz(-2.8090030655925666) out[6]; +rzz(pi/2) a[3], out[6]; +rz(-1.903385914792119) out[6]; +ry(7*pi/8) out[6]; +rz(-1.6971534443990168) out[6]; +rzz(pi/2) b[2], out[6]; +ry(-pi/2) b[2]; +rz(-pi) b[2]; +rzz(pi/2) a[3], b[2]; +ry(pi) a[3]; +rz(-pi) b[2]; +ry(-pi/8) b[2]; +rzz(pi/2) a[3], b[2]; +rz(-pi/2) a[3]; +ry(-pi/2) b[2]; +rz(5*pi/8) b[2]; +rz(-3.0152355359856715) out[6]; +ry(pi/8) out[6]; +rz(1.294861248499081) out[6]; +rz(-3.0152355359856715) out[7]; +ry(pi/8) out[7]; +rz(1.294861248499081) out[7]; +rzz(pi/2) a[3], out[7]; +rz(pi/2) a[3]; +rz(0.2759350782958139) out[7]; +ry(15*pi/16) out[7]; +rz(-0.32190695107235934) out[7]; +rzz(pi/2) b[2], out[7]; +rz(-pi/2) b[2]; +rz(1.892703277867259) out[7]; +ry(15*pi/16) out[7]; +rz(-1.846731405090709) out[7]; +rzz(pi/2) a[3], out[7]; +rx(pi) a[3]; +rz(0.2759350782958139) out[7]; +ry(15*pi/16) out[7]; +rz(-0.32190695107235934) out[7]; +rzz(pi/2) b[2], out[7]; +ry(-pi/2) b[2]; +rzz(pi/2) a[3], b[2]; +ry(pi) a[3]; +rz(-pi) b[2]; +ry(-pi/16) b[2]; +rzz(pi/2) a[3], b[2]; +rz(pi/2) a[3]; +rzz(pi/2) a[3], out[3]; +rz(pi/2) a[3]; +ry(pi/2) b[2]; +rz(7*pi/16) b[2]; +ry(-pi/2) out[3]; +rz(3*pi/4) out[3]; +rzz(pi/2) b[1], out[3]; +rz(pi/2) b[1]; +rz(pi/4) out[3]; +rx(-pi/2) out[3]; +rzz(pi/2) a[3], out[3]; +rx(-pi) a[3]; +rz(-pi/2) a[3]; +ry(-pi/2) out[3]; +rz(3*pi/4) out[3]; +rzz(pi/2) b[1], out[3]; +rz(pi/2) b[1]; +rzz(pi/2) a[3], b[1]; +rzz(pi/2) a[3], out[4]; +rz(-pi/2) a[3]; +rz(-pi/2) b[1]; +rx(-pi) b[1]; +rz(3*pi/4) out[3]; +ry(pi/2) out[3]; +rz(-2.1072299372197243) out[3]; +rz(-2.6051590431649654) out[4]; +ry(pi/4) out[4]; +rz(-2.5551204732437065) out[4]; +rzz(pi/2) b[1], out[4]; +rz(-pi/2) b[1]; +rz(-2.157268507140982) out[4]; +ry(pi/4) out[4]; +rz(-2.1072299372197243) out[4]; +rzz(pi/2) a[3], out[4]; +rz(-2.6051590431649654) out[4]; +ry(pi/4) out[4]; +rz(-2.5551204732437065) out[4]; +rzz(pi/2) b[1], out[4]; +ry(-pi/2) b[1]; +rzz(pi/2) a[3], b[1]; +ry(pi) a[3]; +rz(-pi) b[1]; +ry(-pi/4) b[1]; +rzz(pi/2) a[3], b[1]; +rz(-pi/2) a[3]; +rx(-pi) a[3]; +rzz(pi/2) a[3], out[5]; +rz(-pi/2) a[3]; +ry(pi/2) b[1]; +rz(pi/4) b[1]; +rz(-2.1572685071409827) out[4]; +ry(pi/4) out[4]; +rz(0.3325895879972256) out[4]; +rz(-1.903385914792119) out[5]; +ry(7*pi/8) out[5]; +rz(-1.6971534443990168) out[5]; +rzz(pi/2) b[1], out[5]; +rz(pi/2) b[1]; +rz(0.12635711760412294) out[5]; +ry(7*pi/8) out[5]; +rz(-2.8090030655925666) out[5]; +rzz(pi/2) a[3], out[5]; +rz(-1.903385914792119) out[5]; +ry(7*pi/8) out[5]; +rz(-1.6971534443990168) out[5]; +rzz(pi/2) b[1], out[5]; +ry(-pi/2) b[1]; +rz(-pi) b[1]; +rzz(pi/2) a[3], b[1]; +ry(pi) a[3]; +rz(-pi) b[1]; +ry(-pi/8) b[1]; +rzz(pi/2) a[3], b[1]; +rz(-pi/2) a[3]; +rzz(pi/2) a[3], out[6]; +rz(pi/2) a[3]; +ry(-pi/2) b[1]; +rz(5*pi/8) b[1]; +rz(-3.0152355359856715) out[5]; +ry(pi/8) out[5]; +rz(1.294861248499081) out[5]; +rz(0.2759350782958139) out[6]; +ry(15*pi/16) out[6]; +rz(-0.32190695107235934) out[6]; +rzz(pi/2) b[1], out[6]; +rz(-pi/2) b[1]; +rz(1.892703277867259) out[6]; +ry(15*pi/16) out[6]; +rz(-1.846731405090709) out[6]; +rzz(pi/2) a[3], out[6]; +rx(pi) a[3]; +rz(0.2759350782958139) out[6]; +ry(15*pi/16) out[6]; +rz(-0.32190695107235934) out[6]; +rzz(pi/2) b[1], out[6]; +ry(-pi/2) b[1]; +rzz(pi/2) a[3], b[1]; +ry(pi) a[3]; +rz(-pi) b[1]; +ry(-pi/16) b[1]; +rzz(pi/2) a[3], b[1]; +rz(-pi/2) a[3]; +rx(-pi) a[3]; +ry(-pi/2) b[1]; +rz(9*pi/16) b[1]; +rz(-1.2488893757225379) out[6]; +ry(pi/16) out[6]; +rz(0.9832827023934838) out[6]; +rz(-1.2488893757225379) out[7]; +ry(pi/16) out[7]; +rz(0.9832827023934838) out[7]; +rzz(pi/2) a[3], out[7]; +rz(-pi/2) a[3]; +rz(0.5875136244014145) out[7]; +ry(pi/32) out[7]; +rz(1.230855619167917) out[7]; +rzz(pi/2) b[1], out[7]; +rz(-pi/2) b[1]; +rz(0.3399407076269809) out[7]; +ry(pi/32) out[7]; +rz(0.9832827023934785) out[7]; +rzz(pi/2) a[3], out[7]; +rz(0.5875136244014145) out[7]; +ry(pi/32) out[7]; +rz(1.230855619167917) out[7]; +rzz(pi/2) b[1], out[7]; +ry(-pi/2) b[1]; +rzz(pi/2) a[3], b[1]; +ry(pi) a[3]; +rz(-pi) b[1]; +ry(-pi/32) b[1]; +rzz(pi/2) a[3], b[1]; +rz(pi/2) a[3]; +rzz(pi/2) a[3], out[2]; +rz(pi/2) a[3]; +ry(pi/2) b[1]; +rz(1.472621556370215) b[1]; +ry(-pi/2) out[2]; +rz(3*pi/4) out[2]; +rzz(pi/2) b[0], out[2]; +rz(pi/2) b[0]; +rz(pi/4) out[2]; +rx(-pi/2) out[2]; +rzz(pi/2) a[3], out[2]; +rx(-pi) a[3]; +rz(-pi/2) a[3]; +ry(-pi/2) out[2]; +rz(3*pi/4) out[2]; +rzz(pi/2) b[0], out[2]; +rz(pi/2) b[0]; +rzz(pi/2) a[3], b[0]; +rzz(pi/2) a[3], out[3]; +rz(-pi/2) a[3]; +rz(-pi/2) b[0]; +rx(-pi) b[0]; +rz(pi/4) out[2]; +rx(-pi/2) out[2]; +rz(-2.6051590431649654) out[3]; +ry(pi/4) out[3]; +rz(-2.5551204732437065) out[3]; +rzz(pi/2) b[0], out[3]; +rz(-pi/2) b[0]; +rz(-2.157268507140982) out[3]; +ry(pi/4) out[3]; +rz(-2.1072299372197243) out[3]; +rzz(pi/2) a[3], out[3]; +rz(-2.6051590431649654) out[3]; +ry(pi/4) out[3]; +rz(-2.5551204732437065) out[3]; +rzz(pi/2) b[0], out[3]; +ry(-pi/2) b[0]; +rzz(pi/2) a[3], b[0]; +ry(pi) a[3]; +rz(-pi) b[0]; +ry(-pi/4) b[0]; +rzz(pi/2) a[3], b[0]; +rz(-pi/2) a[3]; +rx(-pi) a[3]; +rzz(pi/2) a[3], out[4]; +rz(-pi/2) a[3]; +ry(pi/2) b[0]; +rz(pi/4) b[0]; +rz(2.555120473243707) out[3]; +rx(-3*pi/4) out[3]; +rz(-1.903385914792119) out[4]; +ry(7*pi/8) out[4]; +rz(-1.6971534443990168) out[4]; +rzz(pi/2) b[0], out[4]; +rz(pi/2) b[0]; +rz(0.12635711760412294) out[4]; +ry(7*pi/8) out[4]; +rz(-2.8090030655925666) out[4]; +rzz(pi/2) a[3], out[4]; +rz(-1.903385914792119) out[4]; +ry(7*pi/8) out[4]; +rz(-1.6971534443990168) out[4]; +rzz(pi/2) b[0], out[4]; +ry(-pi/2) b[0]; +rz(-pi) b[0]; +rzz(pi/2) a[3], b[0]; +ry(pi) a[3]; +rz(-pi) b[0]; +ry(-pi/8) b[0]; +rzz(pi/2) a[3], b[0]; +rz(-pi/2) a[3]; +rzz(pi/2) a[3], out[5]; +rz(pi/2) a[3]; +ry(-pi/2) b[0]; +rz(5*pi/8) b[0]; +rz(1.6971534443990208) out[4]; +rx(pi/8) out[4]; +rzz(pi/2) a[2], out[4]; +rz(pi/2) a[2]; +ry(-pi/2) out[4]; +rz(3*pi/4) out[4]; +rzz(pi/2) b[3], out[4]; +rz(pi/2) b[3]; +rz(pi/4) out[4]; +rx(-pi/2) out[4]; +rzz(pi/2) a[2], out[4]; +rx(-pi) a[2]; +rz(-pi/2) a[2]; +ry(-pi/2) out[4]; +rz(3*pi/4) out[4]; +rzz(pi/2) b[3], out[4]; +rz(pi/2) b[3]; +rzz(pi/2) a[2], b[3]; +rz(-pi/2) b[3]; +rx(-pi) b[3]; +rz(3*pi/4) out[4]; +ry(pi/2) out[4]; +rz(-2.1072299372197243) out[4]; +rz(0.2759350782958139) out[5]; +ry(15*pi/16) out[5]; +rz(-0.32190695107235934) out[5]; +rzz(pi/2) b[0], out[5]; +rz(-pi/2) b[0]; +rz(1.892703277867259) out[5]; +ry(15*pi/16) out[5]; +rz(-1.846731405090709) out[5]; +rzz(pi/2) a[3], out[5]; +rx(pi) a[3]; +rz(0.2759350782958139) out[5]; +ry(15*pi/16) out[5]; +rz(-0.32190695107235934) out[5]; +rzz(pi/2) b[0], out[5]; +ry(-pi/2) b[0]; +rzz(pi/2) a[3], b[0]; +ry(pi) a[3]; +rz(-pi) b[0]; +ry(-pi/16) b[0]; +rzz(pi/2) a[3], b[0]; +rz(-pi/2) a[3]; +rx(-pi) a[3]; +rzz(pi/2) a[3], out[6]; +rz(-pi/2) a[3]; +ry(-pi/2) b[0]; +rz(9*pi/16) b[0]; +rz(-1.2488893757225354) out[5]; +ry(pi/16) out[5]; +rz(-2.107229937219726) out[5]; +rzz(pi/2) a[2], out[5]; +rz(-pi/2) a[2]; +rz(-2.6051590431649654) out[5]; +ry(pi/4) out[5]; +rz(-2.5551204732437065) out[5]; +rzz(pi/2) b[3], out[5]; +rz(-pi/2) b[3]; +rz(-2.157268507140982) out[5]; +ry(pi/4) out[5]; +rz(-2.1072299372197243) out[5]; +rzz(pi/2) a[2], out[5]; +rz(-2.6051590431649654) out[5]; +ry(pi/4) out[5]; +rz(-2.5551204732437065) out[5]; +rzz(pi/2) b[3], out[5]; +ry(-pi/2) b[3]; +rzz(pi/2) a[2], b[3]; +ry(pi) a[2]; +rz(-pi) b[3]; +ry(-pi/4) b[3]; +rzz(pi/2) a[2], b[3]; +rz(-pi/2) a[2]; +rx(-pi) a[2]; +ry(pi/2) b[3]; +rz(pi/4) b[3]; +rz(-2.1572685071409827) out[5]; +ry(pi/4) out[5]; +rz(0.3325895879972256) out[5]; +rz(0.5875136244014145) out[6]; +ry(pi/32) out[6]; +rz(1.230855619167917) out[6]; +rzz(pi/2) b[0], out[6]; +rz(-pi/2) b[0]; +rz(0.3399407076269809) out[6]; +ry(pi/32) out[6]; +rz(0.9832827023934785) out[6]; +rzz(pi/2) a[3], out[6]; +rz(0.5875136244014145) out[6]; +ry(pi/32) out[6]; +rz(1.230855619167917) out[6]; +rzz(pi/2) b[0], out[6]; +ry(-pi/2) b[0]; +rzz(pi/2) a[3], b[0]; +ry(pi) a[3]; +rz(-pi) b[0]; +ry(-pi/32) b[0]; +rzz(pi/2) a[3], b[0]; +rz(-pi/2) a[3]; +ry(pi/2) b[0]; +rz(1.472621556370215) b[0]; +rz(0.3399407076269818) out[6]; +ry(pi/32) out[6]; +rz(0.3325895879972194) out[6]; +rzz(pi/2) a[2], out[6]; +rz(-pi/2) a[2]; +rz(-1.903385914792119) out[6]; +ry(7*pi/8) out[6]; +rz(-1.6971534443990168) out[6]; +rzz(pi/2) b[3], out[6]; +rz(pi/2) b[3]; +rz(0.12635711760412294) out[6]; +ry(7*pi/8) out[6]; +rz(-2.8090030655925666) out[6]; +rzz(pi/2) a[2], out[6]; +rz(-1.903385914792119) out[6]; +ry(7*pi/8) out[6]; +rz(-1.6971534443990168) out[6]; +rzz(pi/2) b[3], out[6]; +ry(-pi/2) b[3]; +rz(-pi) b[3]; +rzz(pi/2) a[2], b[3]; +ry(pi) a[2]; +rz(-pi) b[3]; +ry(-pi/8) b[3]; +rzz(pi/2) a[2], b[3]; +rz(-pi/2) a[2]; +ry(-pi/2) b[3]; +rz(5*pi/8) b[3]; +rz(-3.0152355359856715) out[6]; +ry(pi/8) out[6]; +rz(1.294861248499081) out[6]; +rz(-2.801651945962806) out[7]; +ry(3.043417883165112) out[7]; +rz(-0.8226060790210696) out[7]; +rzz(pi/2) a[3], out[7]; +rz(pi/2) a[3]; +rz(2.3934024058159675) out[7]; +ry(pi/64) out[7]; +rz(2.3982406999461094) out[7]; +rzz(pi/2) b[0], out[7]; +rz(pi/2) b[0]; +rz(2.3141482804385793) out[7]; +ry(pi/64) out[7]; +rz(2.3189865745687213) out[7]; +rzz(pi/2) a[3], out[7]; +rx(pi) a[3]; +rz(2.3934024058159675) out[7]; +ry(pi/64) out[7]; +rz(2.3982406999461094) out[7]; +rzz(pi/2) b[0], out[7]; +ry(-pi/2) b[0]; +rz(-pi) b[0]; +rzz(pi/2) a[3], b[0]; +ry(pi) a[3]; +rz(-pi) b[0]; +ry(-pi/64) b[0]; +rzz(pi/2) a[3], b[0]; +rz(-pi/2) a[3]; +rx(-pi) a[3]; +ry(pi/2) b[0]; +rz(1.5217089415825562) b[0]; +rz(2.3141482804385793) out[7]; +ry(pi/64) out[7]; +rz(1.294861248499089) out[7]; +rzz(pi/2) a[2], out[7]; +rz(pi/2) a[2]; +rz(0.2759350782958139) out[7]; +ry(15*pi/16) out[7]; +rz(-0.32190695107235934) out[7]; +rzz(pi/2) b[3], out[7]; +rz(-pi/2) b[3]; +rz(1.892703277867259) out[7]; +ry(15*pi/16) out[7]; +rz(-1.846731405090709) out[7]; +rzz(pi/2) a[2], out[7]; +rx(pi) a[2]; +rz(0.2759350782958139) out[7]; +ry(15*pi/16) out[7]; +rz(-0.32190695107235934) out[7]; +rzz(pi/2) b[3], out[7]; +ry(-pi/2) b[3]; +rzz(pi/2) a[2], b[3]; +ry(pi) a[2]; +rz(-pi) b[3]; +ry(-pi/16) b[3]; +rzz(pi/2) a[2], b[3]; +rz(pi/2) a[2]; +rzz(pi/2) a[2], out[3]; +rz(pi/2) a[2]; +ry(pi/2) b[3]; +rz(7*pi/16) b[3]; +ry(-pi/2) out[3]; +rz(3*pi/4) out[3]; +rzz(pi/2) b[2], out[3]; +rz(pi/2) b[2]; +rz(pi/4) out[3]; +rx(-pi/2) out[3]; +rzz(pi/2) a[2], out[3]; +rx(-pi) a[2]; +rz(-pi/2) a[2]; +ry(-pi/2) out[3]; +rz(3*pi/4) out[3]; +rzz(pi/2) b[2], out[3]; +rz(pi/2) b[2]; +rzz(pi/2) a[2], b[2]; +rzz(pi/2) a[2], out[4]; +rz(-pi/2) a[2]; +rz(-pi/2) b[2]; +rx(-pi) b[2]; +rz(3*pi/4) out[3]; +ry(pi/2) out[3]; +rz(-2.1072299372197243) out[3]; +rz(-2.6051590431649654) out[4]; +ry(pi/4) out[4]; +rz(-2.5551204732437065) out[4]; +rzz(pi/2) b[2], out[4]; +rz(-pi/2) b[2]; +rz(-2.157268507140982) out[4]; +ry(pi/4) out[4]; +rz(-2.1072299372197243) out[4]; +rzz(pi/2) a[2], out[4]; +rz(-2.6051590431649654) out[4]; +ry(pi/4) out[4]; +rz(-2.5551204732437065) out[4]; +rzz(pi/2) b[2], out[4]; +ry(-pi/2) b[2]; +rzz(pi/2) a[2], b[2]; +ry(pi) a[2]; +rz(-pi) b[2]; +ry(-pi/4) b[2]; +rzz(pi/2) a[2], b[2]; +rz(-pi/2) a[2]; +rx(-pi) a[2]; +rzz(pi/2) a[2], out[5]; +rz(-pi/2) a[2]; +ry(pi/2) b[2]; +rz(pi/4) b[2]; +rz(-2.1572685071409827) out[4]; +ry(pi/4) out[4]; +rz(0.3325895879972256) out[4]; +rz(-1.903385914792119) out[5]; +ry(7*pi/8) out[5]; +rz(-1.6971534443990168) out[5]; +rzz(pi/2) b[2], out[5]; +rz(pi/2) b[2]; +rz(0.12635711760412294) out[5]; +ry(7*pi/8) out[5]; +rz(-2.8090030655925666) out[5]; +rzz(pi/2) a[2], out[5]; +rz(-1.903385914792119) out[5]; +ry(7*pi/8) out[5]; +rz(-1.6971534443990168) out[5]; +rzz(pi/2) b[2], out[5]; +ry(-pi/2) b[2]; +rz(-pi) b[2]; +rzz(pi/2) a[2], b[2]; +ry(pi) a[2]; +rz(-pi) b[2]; +ry(-pi/8) b[2]; +rzz(pi/2) a[2], b[2]; +rz(-pi/2) a[2]; +rzz(pi/2) a[2], out[6]; +rz(pi/2) a[2]; +ry(-pi/2) b[2]; +rz(5*pi/8) b[2]; +rz(-3.0152355359856715) out[5]; +ry(pi/8) out[5]; +rz(1.294861248499081) out[5]; +rz(0.2759350782958139) out[6]; +ry(15*pi/16) out[6]; +rz(-0.32190695107235934) out[6]; +rzz(pi/2) b[2], out[6]; +rz(-pi/2) b[2]; +rz(1.892703277867259) out[6]; +ry(15*pi/16) out[6]; +rz(-1.846731405090709) out[6]; +rzz(pi/2) a[2], out[6]; +rx(pi) a[2]; +rz(0.2759350782958139) out[6]; +ry(15*pi/16) out[6]; +rz(-0.32190695107235934) out[6]; +rzz(pi/2) b[2], out[6]; +ry(-pi/2) b[2]; +rzz(pi/2) a[2], b[2]; +ry(pi) a[2]; +rz(-pi) b[2]; +ry(-pi/16) b[2]; +rzz(pi/2) a[2], b[2]; +rz(-pi/2) a[2]; +rx(-pi) a[2]; +ry(-pi/2) b[2]; +rz(9*pi/16) b[2]; +rz(-1.2488893757225379) out[6]; +ry(pi/16) out[6]; +rz(0.9832827023934838) out[6]; +rz(-1.2488893757225379) out[7]; +ry(pi/16) out[7]; +rz(0.9832827023934838) out[7]; +rzz(pi/2) a[2], out[7]; +rz(-pi/2) a[2]; +rz(0.5875136244014145) out[7]; +ry(pi/32) out[7]; +rz(1.230855619167917) out[7]; +rzz(pi/2) b[2], out[7]; +rz(-pi/2) b[2]; +rz(0.3399407076269809) out[7]; +ry(pi/32) out[7]; +rz(0.9832827023934785) out[7]; +rzz(pi/2) a[2], out[7]; +rz(0.5875136244014145) out[7]; +ry(pi/32) out[7]; +rz(1.230855619167917) out[7]; +rzz(pi/2) b[2], out[7]; +ry(-pi/2) b[2]; +rzz(pi/2) a[2], b[2]; +ry(pi) a[2]; +rz(-pi) b[2]; +ry(-pi/32) b[2]; +rzz(pi/2) a[2], b[2]; +rz(pi/2) a[2]; +rzz(pi/2) a[2], out[2]; +rz(pi/2) a[2]; +ry(pi/2) b[2]; +rz(1.472621556370215) b[2]; +ry(-pi/2) out[2]; +rz(3*pi/4) out[2]; +rzz(pi/2) b[1], out[2]; +rz(pi/2) b[1]; +rz(pi/4) out[2]; +rx(-pi/2) out[2]; +rzz(pi/2) a[2], out[2]; +rx(-pi) a[2]; +rz(-pi/2) a[2]; +ry(-pi/2) out[2]; +rz(3*pi/4) out[2]; +rzz(pi/2) b[1], out[2]; +rz(pi/2) b[1]; +rzz(pi/2) a[2], b[1]; +rzz(pi/2) a[2], out[3]; +rz(-pi/2) a[2]; +rz(-pi/2) b[1]; +rx(-pi) b[1]; +rz(3*pi/4) out[2]; +ry(pi/2) out[2]; +rz(-2.1072299372197243) out[2]; +rz(-2.6051590431649654) out[3]; +ry(pi/4) out[3]; +rz(-2.5551204732437065) out[3]; +rzz(pi/2) b[1], out[3]; +rz(-pi/2) b[1]; +rz(-2.157268507140982) out[3]; +ry(pi/4) out[3]; +rz(-2.1072299372197243) out[3]; +rzz(pi/2) a[2], out[3]; +rz(-2.6051590431649654) out[3]; +ry(pi/4) out[3]; +rz(-2.5551204732437065) out[3]; +rzz(pi/2) b[1], out[3]; +ry(-pi/2) b[1]; +rzz(pi/2) a[2], b[1]; +ry(pi) a[2]; +rz(-pi) b[1]; +ry(-pi/4) b[1]; +rzz(pi/2) a[2], b[1]; +rz(-pi/2) a[2]; +rx(-pi) a[2]; +rzz(pi/2) a[2], out[4]; +rz(-pi/2) a[2]; +ry(pi/2) b[1]; +rz(pi/4) b[1]; +rz(-2.1572685071409827) out[3]; +ry(pi/4) out[3]; +rz(0.3325895879972256) out[3]; +rz(-1.903385914792119) out[4]; +ry(7*pi/8) out[4]; +rz(-1.6971534443990168) out[4]; +rzz(pi/2) b[1], out[4]; +rz(pi/2) b[1]; +rz(0.12635711760412294) out[4]; +ry(7*pi/8) out[4]; +rz(-2.8090030655925666) out[4]; +rzz(pi/2) a[2], out[4]; +rz(-1.903385914792119) out[4]; +ry(7*pi/8) out[4]; +rz(-1.6971534443990168) out[4]; +rzz(pi/2) b[1], out[4]; +ry(-pi/2) b[1]; +rz(-pi) b[1]; +rzz(pi/2) a[2], b[1]; +ry(pi) a[2]; +rz(-pi) b[1]; +ry(-pi/8) b[1]; +rzz(pi/2) a[2], b[1]; +rz(-pi/2) a[2]; +rzz(pi/2) a[2], out[5]; +rz(pi/2) a[2]; +ry(-pi/2) b[1]; +rz(5*pi/8) b[1]; +rz(-3.0152355359856715) out[4]; +ry(pi/8) out[4]; +rz(1.294861248499081) out[4]; +rz(0.2759350782958139) out[5]; +ry(15*pi/16) out[5]; +rz(-0.32190695107235934) out[5]; +rzz(pi/2) b[1], out[5]; +rz(-pi/2) b[1]; +rz(1.892703277867259) out[5]; +ry(15*pi/16) out[5]; +rz(-1.846731405090709) out[5]; +rzz(pi/2) a[2], out[5]; +rx(pi) a[2]; +rz(0.2759350782958139) out[5]; +ry(15*pi/16) out[5]; +rz(-0.32190695107235934) out[5]; +rzz(pi/2) b[1], out[5]; +ry(-pi/2) b[1]; +rzz(pi/2) a[2], b[1]; +ry(pi) a[2]; +rz(-pi) b[1]; +ry(-pi/16) b[1]; +rzz(pi/2) a[2], b[1]; +rz(-pi/2) a[2]; +rx(-pi) a[2]; +rzz(pi/2) a[2], out[6]; +rz(-pi/2) a[2]; +ry(-pi/2) b[1]; +rz(9*pi/16) b[1]; +rz(-1.2488893757225379) out[5]; +ry(pi/16) out[5]; +rz(0.9832827023934838) out[5]; +rz(0.5875136244014145) out[6]; +ry(pi/32) out[6]; +rz(1.230855619167917) out[6]; +rzz(pi/2) b[1], out[6]; +rz(-pi/2) b[1]; +rz(0.3399407076269809) out[6]; +ry(pi/32) out[6]; +rz(0.9832827023934785) out[6]; +rzz(pi/2) a[2], out[6]; +rz(0.5875136244014145) out[6]; +ry(pi/32) out[6]; +rz(1.230855619167917) out[6]; +rzz(pi/2) b[1], out[6]; +ry(-pi/2) b[1]; +rzz(pi/2) a[2], b[1]; +ry(pi) a[2]; +rz(-pi) b[1]; +ry(-pi/32) b[1]; +rzz(pi/2) a[2], b[1]; +rz(-pi/2) a[2]; +ry(pi/2) b[1]; +rz(1.472621556370215) b[1]; +rz(-2.801651945962806) out[6]; +ry(3.043417883165112) out[6]; +rz(-0.8226060790210696) out[6]; +rz(-2.801651945962806) out[7]; +ry(3.043417883165112) out[7]; +rz(-0.8226060790210696) out[7]; +rzz(pi/2) a[2], out[7]; +rz(pi/2) a[2]; +rz(2.3934024058159675) out[7]; +ry(pi/64) out[7]; +rz(2.3982406999461094) out[7]; +rzz(pi/2) b[1], out[7]; +rz(pi/2) b[1]; +rz(2.3141482804385793) out[7]; +ry(pi/64) out[7]; +rz(2.3189865745687213) out[7]; +rzz(pi/2) a[2], out[7]; +rx(pi) a[2]; +rz(2.3934024058159675) out[7]; +ry(pi/64) out[7]; +rz(2.3982406999461094) out[7]; +rzz(pi/2) b[1], out[7]; +ry(-pi/2) b[1]; +rz(-pi) b[1]; +rzz(pi/2) a[2], b[1]; +ry(pi) a[2]; +rz(-pi) b[1]; +ry(-pi/64) b[1]; +rzz(pi/2) a[2], b[1]; +rz(pi/2) a[2]; +rzz(pi/2) a[2], out[1]; +rz(pi/2) a[2]; +ry(pi/2) b[1]; +rz(1.5217089415825562) b[1]; +ry(-pi/2) out[1]; +rz(3*pi/4) out[1]; +rzz(pi/2) b[0], out[1]; +rz(pi/2) b[0]; +rz(pi/4) out[1]; +rx(-pi/2) out[1]; +rzz(pi/2) a[2], out[1]; +rx(-pi) a[2]; +rz(-pi/2) a[2]; +ry(-pi/2) out[1]; +rz(3*pi/4) out[1]; +rzz(pi/2) b[0], out[1]; +rz(pi/2) b[0]; +rzz(pi/2) a[2], b[0]; +rzz(pi/2) a[2], out[2]; +rz(-pi/2) a[2]; +rz(-pi/2) b[0]; +rx(-pi) b[0]; +rz(pi/4) out[1]; +rx(-pi/2) out[1]; +rz(-2.6051590431649654) out[2]; +ry(pi/4) out[2]; +rz(-2.5551204732437065) out[2]; +rzz(pi/2) b[0], out[2]; +rz(-pi/2) b[0]; +rz(-2.157268507140982) out[2]; +ry(pi/4) out[2]; +rz(-2.1072299372197243) out[2]; +rzz(pi/2) a[2], out[2]; +rz(-2.6051590431649654) out[2]; +ry(pi/4) out[2]; +rz(-2.5551204732437065) out[2]; +rzz(pi/2) b[0], out[2]; +ry(-pi/2) b[0]; +rzz(pi/2) a[2], b[0]; +ry(pi) a[2]; +rz(-pi) b[0]; +ry(-pi/4) b[0]; +rzz(pi/2) a[2], b[0]; +rz(-pi/2) a[2]; +rx(-pi) a[2]; +rzz(pi/2) a[2], out[3]; +rz(-pi/2) a[2]; +ry(pi/2) b[0]; +rz(pi/4) b[0]; +rz(2.555120473243707) out[2]; +rx(-3*pi/4) out[2]; +rz(-1.903385914792119) out[3]; +ry(7*pi/8) out[3]; +rz(-1.6971534443990168) out[3]; +rzz(pi/2) b[0], out[3]; +rz(pi/2) b[0]; +rz(0.12635711760412294) out[3]; +ry(7*pi/8) out[3]; +rz(-2.8090030655925666) out[3]; +rzz(pi/2) a[2], out[3]; +rz(-1.903385914792119) out[3]; +ry(7*pi/8) out[3]; +rz(-1.6971534443990168) out[3]; +rzz(pi/2) b[0], out[3]; +ry(-pi/2) b[0]; +rz(-pi) b[0]; +rzz(pi/2) a[2], b[0]; +ry(pi) a[2]; +rz(-pi) b[0]; +ry(-pi/8) b[0]; +rzz(pi/2) a[2], b[0]; +rz(-pi/2) a[2]; +rzz(pi/2) a[2], out[4]; +rz(pi/2) a[2]; +ry(-pi/2) b[0]; +rz(5*pi/8) b[0]; +rz(1.6971534443990208) out[3]; +rx(pi/8) out[3]; +rzz(pi/2) a[1], out[3]; +rz(pi/2) a[1]; +ry(-pi/2) out[3]; +rz(3*pi/4) out[3]; +rzz(pi/2) b[3], out[3]; +rz(pi/2) b[3]; +rz(pi/4) out[3]; +rx(-pi/2) out[3]; +rzz(pi/2) a[1], out[3]; +rx(-pi) a[1]; +rz(-pi/2) a[1]; +ry(-pi/2) out[3]; +rz(3*pi/4) out[3]; +rzz(pi/2) b[3], out[3]; +rz(pi/2) b[3]; +rzz(pi/2) a[1], b[3]; +rz(-pi/2) b[3]; +rx(-pi) b[3]; +rz(3*pi/4) out[3]; +ry(pi/2) out[3]; +rz(-2.1072299372197243) out[3]; +rz(0.2759350782958139) out[4]; +ry(15*pi/16) out[4]; +rz(-0.32190695107235934) out[4]; +rzz(pi/2) b[0], out[4]; +rz(-pi/2) b[0]; +rz(1.892703277867259) out[4]; +ry(15*pi/16) out[4]; +rz(-1.846731405090709) out[4]; +rzz(pi/2) a[2], out[4]; +rx(pi) a[2]; +rz(0.2759350782958139) out[4]; +ry(15*pi/16) out[4]; +rz(-0.32190695107235934) out[4]; +rzz(pi/2) b[0], out[4]; +ry(-pi/2) b[0]; +rzz(pi/2) a[2], b[0]; +ry(pi) a[2]; +rz(-pi) b[0]; +ry(-pi/16) b[0]; +rzz(pi/2) a[2], b[0]; +rz(-pi/2) a[2]; +rx(-pi) a[2]; +rzz(pi/2) a[2], out[5]; +rz(-pi/2) a[2]; +ry(-pi/2) b[0]; +rz(9*pi/16) b[0]; +rz(-1.2488893757225354) out[4]; +ry(pi/16) out[4]; +rz(-2.107229937219726) out[4]; +rzz(pi/2) a[1], out[4]; +rz(-pi/2) a[1]; +rz(-2.6051590431649654) out[4]; +ry(pi/4) out[4]; +rz(-2.5551204732437065) out[4]; +rzz(pi/2) b[3], out[4]; +rz(-pi/2) b[3]; +rz(-2.157268507140982) out[4]; +ry(pi/4) out[4]; +rz(-2.1072299372197243) out[4]; +rzz(pi/2) a[1], out[4]; +rz(-2.6051590431649654) out[4]; +ry(pi/4) out[4]; +rz(-2.5551204732437065) out[4]; +rzz(pi/2) b[3], out[4]; +ry(-pi/2) b[3]; +rzz(pi/2) a[1], b[3]; +ry(pi) a[1]; +rz(-pi) b[3]; +ry(-pi/4) b[3]; +rzz(pi/2) a[1], b[3]; +rz(-pi/2) a[1]; +rx(-pi) a[1]; +ry(pi/2) b[3]; +rz(pi/4) b[3]; +rz(-2.1572685071409827) out[4]; +ry(pi/4) out[4]; +rz(0.3325895879972256) out[4]; +rz(0.5875136244014145) out[5]; +ry(pi/32) out[5]; +rz(1.230855619167917) out[5]; +rzz(pi/2) b[0], out[5]; +rz(-pi/2) b[0]; +rz(0.3399407076269809) out[5]; +ry(pi/32) out[5]; +rz(0.9832827023934785) out[5]; +rzz(pi/2) a[2], out[5]; +rz(0.5875136244014145) out[5]; +ry(pi/32) out[5]; +rz(1.230855619167917) out[5]; +rzz(pi/2) b[0], out[5]; +ry(-pi/2) b[0]; +rzz(pi/2) a[2], b[0]; +ry(pi) a[2]; +rz(-pi) b[0]; +ry(-pi/32) b[0]; +rzz(pi/2) a[2], b[0]; +rz(-pi/2) a[2]; +rzz(pi/2) a[2], out[6]; +rz(pi/2) a[2]; +ry(pi/2) b[0]; +rz(1.472621556370215) b[0]; +rz(0.3399407076269818) out[5]; +ry(pi/32) out[5]; +rz(0.3325895879972194) out[5]; +rzz(pi/2) a[1], out[5]; +rz(-pi/2) a[1]; +rz(-1.903385914792119) out[5]; +ry(7*pi/8) out[5]; +rz(-1.6971534443990168) out[5]; +rzz(pi/2) b[3], out[5]; +rz(pi/2) b[3]; +rz(0.12635711760412294) out[5]; +ry(7*pi/8) out[5]; +rz(-2.8090030655925666) out[5]; +rzz(pi/2) a[1], out[5]; +rz(-1.903385914792119) out[5]; +ry(7*pi/8) out[5]; +rz(-1.6971534443990168) out[5]; +rzz(pi/2) b[3], out[5]; +ry(-pi/2) b[3]; +rz(-pi) b[3]; +rzz(pi/2) a[1], b[3]; +ry(pi) a[1]; +rz(-pi) b[3]; +ry(-pi/8) b[3]; +rzz(pi/2) a[1], b[3]; +rz(-pi/2) a[1]; +ry(-pi/2) b[3]; +rz(5*pi/8) b[3]; +rz(-3.0152355359856715) out[5]; +ry(pi/8) out[5]; +rz(1.294861248499081) out[5]; +rz(2.3934024058159675) out[6]; +ry(pi/64) out[6]; +rz(2.3982406999461094) out[6]; +rzz(pi/2) b[0], out[6]; +rz(pi/2) b[0]; +rz(2.3141482804385793) out[6]; +ry(pi/64) out[6]; +rz(2.3189865745687213) out[6]; +rzz(pi/2) a[2], out[6]; +rx(pi) a[2]; +rz(2.3934024058159675) out[6]; +ry(pi/64) out[6]; +rz(2.3982406999461094) out[6]; +rzz(pi/2) b[0], out[6]; +ry(-pi/2) b[0]; +rz(-pi) b[0]; +rzz(pi/2) a[2], b[0]; +ry(pi) a[2]; +rz(-pi) b[0]; +ry(-pi/64) b[0]; +rzz(pi/2) a[2], b[0]; +rz(-pi/2) a[2]; +rx(-pi) a[2]; +ry(-pi/2) b[0]; +rz(1.619883712007237) b[0]; +rz(2.3141482804385793) out[6]; +ry(pi/64) out[6]; +rz(1.294861248499089) out[6]; +rzz(pi/2) a[1], out[6]; +rz(pi/2) a[1]; +rz(0.2759350782958139) out[6]; +ry(15*pi/16) out[6]; +rz(-0.32190695107235934) out[6]; +rzz(pi/2) b[3], out[6]; +rz(-pi/2) b[3]; +rz(1.892703277867259) out[6]; +ry(15*pi/16) out[6]; +rz(-1.846731405090709) out[6]; +rzz(pi/2) a[1], out[6]; +rx(pi) a[1]; +rz(0.2759350782958139) out[6]; +ry(15*pi/16) out[6]; +rz(-0.32190695107235934) out[6]; +rzz(pi/2) b[3], out[6]; +ry(-pi/2) b[3]; +rzz(pi/2) a[1], b[3]; +ry(pi) a[1]; +rz(-pi) b[3]; +ry(-pi/16) b[3]; +rzz(pi/2) a[1], b[3]; +rz(-pi/2) a[1]; +rx(-pi) a[1]; +ry(-pi/2) b[3]; +rz(9*pi/16) b[3]; +rz(-1.2488893757225379) out[6]; +ry(pi/16) out[6]; +rz(0.9832827023934838) out[6]; +rz(-0.8274443731512049) out[7]; +ry(3.0925052683774528) out[7]; +rz(2.3696099671542044) out[7]; +rzz(pi/2) a[2], out[7]; +rz(-pi/2) a[2]; +rz(2.342779013230494) out[7]; +ry(pi/128) out[7]; +rz(2.4852630457765574) out[7]; +rzz(pi/2) b[0], out[7]; +rz(-pi/2) b[0]; +rz(-0.9144667189816644) out[7]; +ry(pi/128) out[7]; +rz(-0.7719826864355919) out[7]; +rzz(pi/2) a[2], out[7]; +rz(2.342779013230494) out[7]; +ry(pi/128) out[7]; +rz(2.4852630457765574) out[7]; +rzz(pi/2) b[0], out[7]; +ry(-pi/2) b[0]; +rzz(pi/2) a[2], b[0]; +ry(pi) a[2]; +rz(-pi) b[0]; +ry(-pi/128) b[0]; +rzz(pi/2) a[2], b[0]; +rz(-pi/2) a[2]; +rx(-pi) a[2]; +ry(pi/2) b[0]; +rz(1.5462526341887255) b[0]; +rz(-0.9144667189816644) out[7]; +ry(pi/128) out[7]; +rz(0.9832827023934811) out[7]; +rzz(pi/2) a[1], out[7]; +rz(-pi/2) a[1]; +rz(0.5875136244014145) out[7]; +ry(pi/32) out[7]; +rz(1.230855619167917) out[7]; +rzz(pi/2) b[3], out[7]; +rz(-pi/2) b[3]; +rz(0.3399407076269809) out[7]; +ry(pi/32) out[7]; +rz(0.9832827023934785) out[7]; +rzz(pi/2) a[1], out[7]; +rz(0.5875136244014145) out[7]; +ry(pi/32) out[7]; +rz(1.230855619167917) out[7]; +rzz(pi/2) b[3], out[7]; +ry(-pi/2) b[3]; +rzz(pi/2) a[1], b[3]; +ry(pi) a[1]; +rz(-pi) b[3]; +ry(-pi/32) b[3]; +rzz(pi/2) a[1], b[3]; +rz(pi/2) a[1]; +rzz(pi/2) a[1], out[2]; +rz(pi/2) a[1]; +ry(pi/2) b[3]; +rz(1.472621556370215) b[3]; +ry(-pi/2) out[2]; +rz(3*pi/4) out[2]; +rzz(pi/2) b[2], out[2]; +rz(pi/2) b[2]; +rz(pi/4) out[2]; +rx(-pi/2) out[2]; +rzz(pi/2) a[1], out[2]; +rx(-pi) a[1]; +rz(-pi/2) a[1]; +ry(-pi/2) out[2]; +rz(3*pi/4) out[2]; +rzz(pi/2) b[2], out[2]; +rz(pi/2) b[2]; +rzz(pi/2) a[1], b[2]; +rzz(pi/2) a[1], out[3]; +rz(-pi/2) a[1]; +rz(-pi/2) b[2]; +rx(-pi) b[2]; +rz(3*pi/4) out[2]; +ry(pi/2) out[2]; +rz(-2.1072299372197243) out[2]; +rz(-2.6051590431649654) out[3]; +ry(pi/4) out[3]; +rz(-2.5551204732437065) out[3]; +rzz(pi/2) b[2], out[3]; +rz(-pi/2) b[2]; +rz(-2.157268507140982) out[3]; +ry(pi/4) out[3]; +rz(-2.1072299372197243) out[3]; +rzz(pi/2) a[1], out[3]; +rz(-2.6051590431649654) out[3]; +ry(pi/4) out[3]; +rz(-2.5551204732437065) out[3]; +rzz(pi/2) b[2], out[3]; +ry(-pi/2) b[2]; +rzz(pi/2) a[1], b[2]; +ry(pi) a[1]; +rz(-pi) b[2]; +ry(-pi/4) b[2]; +rzz(pi/2) a[1], b[2]; +rz(-pi/2) a[1]; +rx(-pi) a[1]; +rzz(pi/2) a[1], out[4]; +rz(-pi/2) a[1]; +ry(pi/2) b[2]; +rz(pi/4) b[2]; +rz(-2.1572685071409827) out[3]; +ry(pi/4) out[3]; +rz(0.3325895879972256) out[3]; +rz(-1.903385914792119) out[4]; +ry(7*pi/8) out[4]; +rz(-1.6971534443990168) out[4]; +rzz(pi/2) b[2], out[4]; +rz(pi/2) b[2]; +rz(0.12635711760412294) out[4]; +ry(7*pi/8) out[4]; +rz(-2.8090030655925666) out[4]; +rzz(pi/2) a[1], out[4]; +rz(-1.903385914792119) out[4]; +ry(7*pi/8) out[4]; +rz(-1.6971534443990168) out[4]; +rzz(pi/2) b[2], out[4]; +ry(-pi/2) b[2]; +rz(-pi) b[2]; +rzz(pi/2) a[1], b[2]; +ry(pi) a[1]; +rz(-pi) b[2]; +ry(-pi/8) b[2]; +rzz(pi/2) a[1], b[2]; +rz(-pi/2) a[1]; +rzz(pi/2) a[1], out[5]; +rz(pi/2) a[1]; +ry(-pi/2) b[2]; +rz(5*pi/8) b[2]; +rz(-3.0152355359856715) out[4]; +ry(pi/8) out[4]; +rz(1.294861248499081) out[4]; +rz(0.2759350782958139) out[5]; +ry(15*pi/16) out[5]; +rz(-0.32190695107235934) out[5]; +rzz(pi/2) b[2], out[5]; +rz(-pi/2) b[2]; +rz(1.892703277867259) out[5]; +ry(15*pi/16) out[5]; +rz(-1.846731405090709) out[5]; +rzz(pi/2) a[1], out[5]; +rx(pi) a[1]; +rz(0.2759350782958139) out[5]; +ry(15*pi/16) out[5]; +rz(-0.32190695107235934) out[5]; +rzz(pi/2) b[2], out[5]; +ry(-pi/2) b[2]; +rzz(pi/2) a[1], b[2]; +ry(pi) a[1]; +rz(-pi) b[2]; +ry(-pi/16) b[2]; +rzz(pi/2) a[1], b[2]; +rz(-pi/2) a[1]; +rx(-pi) a[1]; +rzz(pi/2) a[1], out[6]; +rz(-pi/2) a[1]; +ry(-pi/2) b[2]; +rz(9*pi/16) b[2]; +rz(-1.2488893757225379) out[5]; +ry(pi/16) out[5]; +rz(0.9832827023934838) out[5]; +rz(0.5875136244014145) out[6]; +ry(pi/32) out[6]; +rz(1.230855619167917) out[6]; +rzz(pi/2) b[2], out[6]; +rz(-pi/2) b[2]; +rz(0.3399407076269809) out[6]; +ry(pi/32) out[6]; +rz(0.9832827023934785) out[6]; +rzz(pi/2) a[1], out[6]; +rz(0.5875136244014145) out[6]; +ry(pi/32) out[6]; +rz(1.230855619167917) out[6]; +rzz(pi/2) b[2], out[6]; +ry(-pi/2) b[2]; +rzz(pi/2) a[1], b[2]; +ry(pi) a[1]; +rz(-pi) b[2]; +ry(-pi/32) b[2]; +rzz(pi/2) a[1], b[2]; +rz(-pi/2) a[1]; +ry(pi/2) b[2]; +rz(1.472621556370215) b[2]; +rz(-2.801651945962806) out[6]; +ry(3.043417883165112) out[6]; +rz(-0.8226060790210696) out[6]; +rz(-2.801651945962806) out[7]; +ry(3.043417883165112) out[7]; +rz(-0.8226060790210696) out[7]; +rzz(pi/2) a[1], out[7]; +rz(pi/2) a[1]; +rz(2.3934024058159675) out[7]; +ry(pi/64) out[7]; +rz(2.3982406999461094) out[7]; +rzz(pi/2) b[2], out[7]; +rz(pi/2) b[2]; +rz(2.3141482804385793) out[7]; +ry(pi/64) out[7]; +rz(2.3189865745687213) out[7]; +rzz(pi/2) a[1], out[7]; +rx(pi) a[1]; +rz(2.3934024058159675) out[7]; +ry(pi/64) out[7]; +rz(2.3982406999461094) out[7]; +rzz(pi/2) b[2], out[7]; +ry(-pi/2) b[2]; +rz(-pi) b[2]; +rzz(pi/2) a[1], b[2]; +ry(pi) a[1]; +rz(-pi) b[2]; +ry(-pi/64) b[2]; +rzz(pi/2) a[1], b[2]; +rz(pi/2) a[1]; +rzz(pi/2) a[1], out[1]; +rz(pi/2) a[1]; +ry(pi/2) b[2]; +rz(1.5217089415825562) b[2]; +ry(-pi/2) out[1]; +rz(3*pi/4) out[1]; +rzz(pi/2) b[1], out[1]; +rz(pi/2) b[1]; +rz(pi/4) out[1]; +rx(-pi/2) out[1]; +rzz(pi/2) a[1], out[1]; +rx(-pi) a[1]; +rz(-pi/2) a[1]; +ry(-pi/2) out[1]; +rz(3*pi/4) out[1]; +rzz(pi/2) b[1], out[1]; +rz(pi/2) b[1]; +rzz(pi/2) a[1], b[1]; +rzz(pi/2) a[1], out[2]; +rz(-pi/2) a[1]; +rz(-pi/2) b[1]; +rx(-pi) b[1]; +rz(3*pi/4) out[1]; +ry(pi/2) out[1]; +rz(-2.1072299372197243) out[1]; +rz(-2.6051590431649654) out[2]; +ry(pi/4) out[2]; +rz(-2.5551204732437065) out[2]; +rzz(pi/2) b[1], out[2]; +rz(-pi/2) b[1]; +rz(-2.157268507140982) out[2]; +ry(pi/4) out[2]; +rz(-2.1072299372197243) out[2]; +rzz(pi/2) a[1], out[2]; +rz(-2.6051590431649654) out[2]; +ry(pi/4) out[2]; +rz(-2.5551204732437065) out[2]; +rzz(pi/2) b[1], out[2]; +ry(-pi/2) b[1]; +rzz(pi/2) a[1], b[1]; +ry(pi) a[1]; +rz(-pi) b[1]; +ry(-pi/4) b[1]; +rzz(pi/2) a[1], b[1]; +rz(-pi/2) a[1]; +rx(-pi) a[1]; +rzz(pi/2) a[1], out[3]; +rz(-pi/2) a[1]; +ry(pi/2) b[1]; +rz(pi/4) b[1]; +rz(-2.1572685071409827) out[2]; +ry(pi/4) out[2]; +rz(0.3325895879972256) out[2]; +rz(-1.903385914792119) out[3]; +ry(7*pi/8) out[3]; +rz(-1.6971534443990168) out[3]; +rzz(pi/2) b[1], out[3]; +rz(pi/2) b[1]; +rz(0.12635711760412294) out[3]; +ry(7*pi/8) out[3]; +rz(-2.8090030655925666) out[3]; +rzz(pi/2) a[1], out[3]; +rz(-1.903385914792119) out[3]; +ry(7*pi/8) out[3]; +rz(-1.6971534443990168) out[3]; +rzz(pi/2) b[1], out[3]; +ry(-pi/2) b[1]; +rz(-pi) b[1]; +rzz(pi/2) a[1], b[1]; +ry(pi) a[1]; +rz(-pi) b[1]; +ry(-pi/8) b[1]; +rzz(pi/2) a[1], b[1]; +rz(-pi/2) a[1]; +rzz(pi/2) a[1], out[4]; +rz(pi/2) a[1]; +ry(-pi/2) b[1]; +rz(5*pi/8) b[1]; +rz(-3.0152355359856715) out[3]; +ry(pi/8) out[3]; +rz(1.294861248499081) out[3]; +rz(0.2759350782958139) out[4]; +ry(15*pi/16) out[4]; +rz(-0.32190695107235934) out[4]; +rzz(pi/2) b[1], out[4]; +rz(-pi/2) b[1]; +rz(1.892703277867259) out[4]; +ry(15*pi/16) out[4]; +rz(-1.846731405090709) out[4]; +rzz(pi/2) a[1], out[4]; +rx(pi) a[1]; +rz(0.2759350782958139) out[4]; +ry(15*pi/16) out[4]; +rz(-0.32190695107235934) out[4]; +rzz(pi/2) b[1], out[4]; +ry(-pi/2) b[1]; +rzz(pi/2) a[1], b[1]; +ry(pi) a[1]; +rz(-pi) b[1]; +ry(-pi/16) b[1]; +rzz(pi/2) a[1], b[1]; +rz(-pi/2) a[1]; +rx(-pi) a[1]; +rzz(pi/2) a[1], out[5]; +rz(-pi/2) a[1]; +ry(-pi/2) b[1]; +rz(9*pi/16) b[1]; +rz(-1.2488893757225379) out[4]; +ry(pi/16) out[4]; +rz(0.9832827023934838) out[4]; +rz(0.5875136244014145) out[5]; +ry(pi/32) out[5]; +rz(1.230855619167917) out[5]; +rzz(pi/2) b[1], out[5]; +rz(-pi/2) b[1]; +rz(0.3399407076269809) out[5]; +ry(pi/32) out[5]; +rz(0.9832827023934785) out[5]; +rzz(pi/2) a[1], out[5]; +rz(0.5875136244014145) out[5]; +ry(pi/32) out[5]; +rz(1.230855619167917) out[5]; +rzz(pi/2) b[1], out[5]; +ry(-pi/2) b[1]; +rzz(pi/2) a[1], b[1]; +ry(pi) a[1]; +rz(-pi) b[1]; +ry(-pi/32) b[1]; +rzz(pi/2) a[1], b[1]; +rz(-pi/2) a[1]; +rzz(pi/2) a[1], out[6]; +rz(pi/2) a[1]; +ry(pi/2) b[1]; +rz(1.472621556370215) b[1]; +rz(-2.801651945962806) out[5]; +ry(3.043417883165112) out[5]; +rz(-0.8226060790210696) out[5]; +rz(2.3934024058159675) out[6]; +ry(pi/64) out[6]; +rz(2.3982406999461094) out[6]; +rzz(pi/2) b[1], out[6]; +rz(pi/2) b[1]; +rz(2.3141482804385793) out[6]; +ry(pi/64) out[6]; +rz(2.3189865745687213) out[6]; +rzz(pi/2) a[1], out[6]; +rx(pi) a[1]; +rz(2.3934024058159675) out[6]; +ry(pi/64) out[6]; +rz(2.3982406999461094) out[6]; +rzz(pi/2) b[1], out[6]; +ry(-pi/2) b[1]; +rz(-pi) b[1]; +rzz(pi/2) a[1], b[1]; +ry(pi) a[1]; +rz(-pi) b[1]; +ry(-pi/64) b[1]; +rzz(pi/2) a[1], b[1]; +rz(-pi/2) a[1]; +rx(-pi) a[1]; +ry(-pi/2) b[1]; +rz(1.619883712007237) b[1]; +rz(-0.8274443731512049) out[6]; +ry(3.0925052683774528) out[6]; +rz(2.3696099671542044) out[6]; +rz(-0.8274443731512049) out[7]; +ry(3.0925052683774528) out[7]; +rz(2.3696099671542044) out[7]; +rzz(pi/2) a[1], out[7]; +rz(-pi/2) a[1]; +rz(2.342779013230494) out[7]; +ry(pi/128) out[7]; +rz(2.4852630457765574) out[7]; +rzz(pi/2) b[1], out[7]; +rz(-pi/2) b[1]; +rz(-0.9144667189816644) out[7]; +ry(pi/128) out[7]; +rz(-0.7719826864355919) out[7]; +rzz(pi/2) a[1], out[7]; +rz(2.342779013230494) out[7]; +ry(pi/128) out[7]; +rz(2.4852630457765574) out[7]; +rzz(pi/2) b[1], out[7]; +ry(-pi/2) b[1]; +rzz(pi/2) a[1], b[1]; +ry(pi) a[1]; +rz(-pi) b[1]; +ry(-pi/128) b[1]; +rzz(pi/2) a[1], b[1]; +rz(-pi/2) a[1]; +rzz(pi/2) a[1], out[0]; +rz(pi/2) a[1]; +ry(pi/2) b[1]; +rz(1.5462526341887255) b[1]; +ry(-pi/2) out[0]; +rz(3*pi/4) out[0]; +rzz(pi/2) b[0], out[0]; +rz(pi/2) b[0]; +rz(pi/4) out[0]; +rx(-pi/2) out[0]; +rzz(pi/2) a[1], out[0]; +rx(-pi) a[1]; +rz(-pi/2) a[1]; +ry(-pi/2) out[0]; +rz(3*pi/4) out[0]; +rzz(pi/2) b[0], out[0]; +rz(pi/2) b[0]; +rzz(pi/2) a[1], b[0]; +rzz(pi/2) a[1], out[1]; +rz(-pi/2) a[1]; +rz(-pi/2) b[0]; +rx(-pi) b[0]; +rz(pi/4) out[0]; +rx(-pi/2) out[0]; +rz(-2.6051590431649654) out[1]; +ry(pi/4) out[1]; +rz(-2.5551204732437065) out[1]; +rzz(pi/2) b[0], out[1]; +rz(-pi/2) b[0]; +rz(-2.157268507140982) out[1]; +ry(pi/4) out[1]; +rz(-2.1072299372197243) out[1]; +rzz(pi/2) a[1], out[1]; +rz(-2.6051590431649654) out[1]; +ry(pi/4) out[1]; +rz(-2.5551204732437065) out[1]; +rzz(pi/2) b[0], out[1]; +ry(-pi/2) b[0]; +rzz(pi/2) a[1], b[0]; +ry(pi) a[1]; +rz(-pi) b[0]; +ry(-pi/4) b[0]; +rzz(pi/2) a[1], b[0]; +rz(-pi/2) a[1]; +rx(-pi) a[1]; +rzz(pi/2) a[1], out[2]; +rz(-pi/2) a[1]; +ry(pi/2) b[0]; +rz(pi/4) b[0]; +rz(2.555120473243707) out[1]; +rx(-3*pi/4) out[1]; +rz(-1.903385914792119) out[2]; +ry(7*pi/8) out[2]; +rz(-1.6971534443990168) out[2]; +rzz(pi/2) b[0], out[2]; +rz(pi/2) b[0]; +rz(0.12635711760412294) out[2]; +ry(7*pi/8) out[2]; +rz(-2.8090030655925666) out[2]; +rzz(pi/2) a[1], out[2]; +rz(-1.903385914792119) out[2]; +ry(7*pi/8) out[2]; +rz(-1.6971534443990168) out[2]; +rzz(pi/2) b[0], out[2]; +ry(-pi/2) b[0]; +rz(-pi) b[0]; +rzz(pi/2) a[1], b[0]; +ry(pi) a[1]; +rz(-pi) b[0]; +ry(-pi/8) b[0]; +rzz(pi/2) a[1], b[0]; +rz(-pi/2) a[1]; +rzz(pi/2) a[1], out[3]; +rz(pi/2) a[1]; +ry(-pi/2) b[0]; +rz(5*pi/8) b[0]; +rz(1.6971534443990208) out[2]; +rx(pi/8) out[2]; +rzz(pi/2) a[0], out[2]; +rz(pi/2) a[0]; +ry(-pi/2) out[2]; +rz(3*pi/4) out[2]; +rzz(pi/2) b[3], out[2]; +rz(pi/2) b[3]; +rz(pi/4) out[2]; +rx(-pi/2) out[2]; +rzz(pi/2) a[0], out[2]; +rx(-pi) a[0]; +rz(-pi/2) a[0]; +ry(-pi/2) out[2]; +rz(3*pi/4) out[2]; +rzz(pi/2) b[3], out[2]; +rz(pi/2) b[3]; +rzz(pi/2) a[0], b[3]; +rz(-pi/2) b[3]; +rx(-pi) b[3]; +rz(3*pi/4) out[2]; +ry(pi/2) out[2]; +rz(-2.1072299372197243) out[2]; +rz(0.2759350782958139) out[3]; +ry(15*pi/16) out[3]; +rz(-0.32190695107235934) out[3]; +rzz(pi/2) b[0], out[3]; +rz(-pi/2) b[0]; +rz(1.892703277867259) out[3]; +ry(15*pi/16) out[3]; +rz(-1.846731405090709) out[3]; +rzz(pi/2) a[1], out[3]; +rx(pi) a[1]; +rz(0.2759350782958139) out[3]; +ry(15*pi/16) out[3]; +rz(-0.32190695107235934) out[3]; +rzz(pi/2) b[0], out[3]; +ry(-pi/2) b[0]; +rzz(pi/2) a[1], b[0]; +ry(pi) a[1]; +rz(-pi) b[0]; +ry(-pi/16) b[0]; +rzz(pi/2) a[1], b[0]; +rz(-pi/2) a[1]; +rx(-pi) a[1]; +rzz(pi/2) a[1], out[4]; +rz(-pi/2) a[1]; +ry(-pi/2) b[0]; +rz(9*pi/16) b[0]; +rz(-1.2488893757225354) out[3]; +ry(pi/16) out[3]; +rz(-2.107229937219726) out[3]; +rzz(pi/2) a[0], out[3]; +rz(-pi/2) a[0]; +rz(-2.6051590431649654) out[3]; +ry(pi/4) out[3]; +rz(-2.5551204732437065) out[3]; +rzz(pi/2) b[3], out[3]; +rz(-pi/2) b[3]; +rz(-2.157268507140982) out[3]; +ry(pi/4) out[3]; +rz(-2.1072299372197243) out[3]; +rzz(pi/2) a[0], out[3]; +rz(-2.6051590431649654) out[3]; +ry(pi/4) out[3]; +rz(-2.5551204732437065) out[3]; +rzz(pi/2) b[3], out[3]; +ry(-pi/2) b[3]; +rzz(pi/2) a[0], b[3]; +ry(pi) a[0]; +rz(-pi) b[3]; +ry(-pi/4) b[3]; +rzz(pi/2) a[0], b[3]; +rz(-pi/2) a[0]; +rx(-pi) a[0]; +ry(pi/2) b[3]; +rz(pi/4) b[3]; +rz(-2.1572685071409827) out[3]; +ry(pi/4) out[3]; +rz(0.3325895879972256) out[3]; +rz(0.5875136244014145) out[4]; +ry(pi/32) out[4]; +rz(1.230855619167917) out[4]; +rzz(pi/2) b[0], out[4]; +rz(-pi/2) b[0]; +rz(0.3399407076269809) out[4]; +ry(pi/32) out[4]; +rz(0.9832827023934785) out[4]; +rzz(pi/2) a[1], out[4]; +rz(0.5875136244014145) out[4]; +ry(pi/32) out[4]; +rz(1.230855619167917) out[4]; +rzz(pi/2) b[0], out[4]; +ry(-pi/2) b[0]; +rzz(pi/2) a[1], b[0]; +ry(pi) a[1]; +rz(-pi) b[0]; +ry(-pi/32) b[0]; +rzz(pi/2) a[1], b[0]; +rz(-pi/2) a[1]; +rzz(pi/2) a[1], out[5]; +rz(pi/2) a[1]; +ry(pi/2) b[0]; +rz(1.472621556370215) b[0]; +rz(0.3399407076269818) out[4]; +ry(pi/32) out[4]; +rz(0.3325895879972194) out[4]; +rzz(pi/2) a[0], out[4]; +rz(-pi/2) a[0]; +rz(-1.903385914792119) out[4]; +ry(7*pi/8) out[4]; +rz(-1.6971534443990168) out[4]; +rzz(pi/2) b[3], out[4]; +rz(pi/2) b[3]; +rz(0.12635711760412294) out[4]; +ry(7*pi/8) out[4]; +rz(-2.8090030655925666) out[4]; +rzz(pi/2) a[0], out[4]; +rz(-1.903385914792119) out[4]; +ry(7*pi/8) out[4]; +rz(-1.6971534443990168) out[4]; +rzz(pi/2) b[3], out[4]; +ry(-pi/2) b[3]; +rz(-pi) b[3]; +rzz(pi/2) a[0], b[3]; +ry(pi) a[0]; +rz(-pi) b[3]; +ry(-pi/8) b[3]; +rzz(pi/2) a[0], b[3]; +rz(-pi/2) a[0]; +ry(-pi/2) b[3]; +rz(5*pi/8) b[3]; +rz(-3.0152355359856715) out[4]; +ry(pi/8) out[4]; +rz(1.294861248499081) out[4]; +rz(2.3934024058159675) out[5]; +ry(pi/64) out[5]; +rz(2.3982406999461094) out[5]; +rzz(pi/2) b[0], out[5]; +rz(pi/2) b[0]; +rz(2.3141482804385793) out[5]; +ry(pi/64) out[5]; +rz(2.3189865745687213) out[5]; +rzz(pi/2) a[1], out[5]; +rx(pi) a[1]; +rz(2.3934024058159675) out[5]; +ry(pi/64) out[5]; +rz(2.3982406999461094) out[5]; +rzz(pi/2) b[0], out[5]; +ry(-pi/2) b[0]; +rz(-pi) b[0]; +rzz(pi/2) a[1], b[0]; +ry(pi) a[1]; +rz(-pi) b[0]; +ry(-pi/64) b[0]; +rzz(pi/2) a[1], b[0]; +rz(-pi/2) a[1]; +rx(-pi) a[1]; +rzz(pi/2) a[1], out[6]; +rz(-pi/2) a[1]; +ry(-pi/2) b[0]; +rz(1.619883712007237) b[0]; +rz(2.3141482804385793) out[5]; +ry(pi/64) out[5]; +rz(1.294861248499089) out[5]; +rzz(pi/2) a[0], out[5]; +rz(pi/2) a[0]; +rz(0.2759350782958139) out[5]; +ry(15*pi/16) out[5]; +rz(-0.32190695107235934) out[5]; +rzz(pi/2) b[3], out[5]; +rz(-pi/2) b[3]; +rz(1.892703277867259) out[5]; +ry(15*pi/16) out[5]; +rz(-1.846731405090709) out[5]; +rzz(pi/2) a[0], out[5]; +rx(pi) a[0]; +rz(0.2759350782958139) out[5]; +ry(15*pi/16) out[5]; +rz(-0.32190695107235934) out[5]; +rzz(pi/2) b[3], out[5]; +ry(-pi/2) b[3]; +rzz(pi/2) a[0], b[3]; +ry(pi) a[0]; +rz(-pi) b[3]; +ry(-pi/16) b[3]; +rzz(pi/2) a[0], b[3]; +rz(-pi/2) a[0]; +rx(-pi) a[0]; +ry(-pi/2) b[3]; +rz(9*pi/16) b[3]; +rz(-1.2488893757225379) out[5]; +ry(pi/16) out[5]; +rz(0.9832827023934838) out[5]; +rz(2.342779013230494) out[6]; +ry(pi/128) out[6]; +rz(2.4852630457765574) out[6]; +rzz(pi/2) b[0], out[6]; +rz(-pi/2) b[0]; +rz(-0.9144667189816644) out[6]; +ry(pi/128) out[6]; +rz(-0.7719826864355919) out[6]; +rzz(pi/2) a[1], out[6]; +rz(2.342779013230494) out[6]; +ry(pi/128) out[6]; +rz(2.4852630457765574) out[6]; +rzz(pi/2) b[0], out[6]; +ry(-pi/2) b[0]; +rzz(pi/2) a[1], b[0]; +ry(pi) a[1]; +rz(-pi) b[0]; +ry(-pi/128) b[0]; +rzz(pi/2) a[1], b[0]; +rz(-pi/2) a[1]; +ry(-pi/2) b[0]; +rz(1.5953400194010676) b[0]; +rz(-0.9144667189816644) out[6]; +ry(pi/128) out[6]; +rz(0.9832827023934811) out[6]; +rzz(pi/2) a[0], out[6]; +rz(-pi/2) a[0]; +rz(0.5875136244014145) out[6]; +ry(pi/32) out[6]; +rz(1.230855619167917) out[6]; +rzz(pi/2) b[3], out[6]; +rz(-pi/2) b[3]; +rz(0.3399407076269809) out[6]; +ry(pi/32) out[6]; +rz(0.9832827023934785) out[6]; +rzz(pi/2) a[0], out[6]; +rz(0.5875136244014145) out[6]; +ry(pi/32) out[6]; +rz(1.230855619167917) out[6]; +rzz(pi/2) b[3], out[6]; +ry(-pi/2) b[3]; +rzz(pi/2) a[0], b[3]; +ry(pi) a[0]; +rz(-pi) b[3]; +ry(-pi/32) b[3]; +rzz(pi/2) a[0], b[3]; +rz(-pi/2) a[0]; +ry(pi/2) b[3]; +rz(1.472621556370215) b[3]; +rz(-2.801651945962806) out[6]; +ry(3.043417883165112) out[6]; +rz(-0.8226060790210696) out[6]; +rz(2.2271259346081145) out[7]; +ry(3.1170489609836234) out[7]; +rz(2.0614966236345236) out[7]; +rzz(pi/2) a[1], out[7]; +rz(pi/2) a[1]; +rz(2.6508923567501874) out[7]; +ry(3.1293208072867085) out[7]; +rz(3.107697059996571) out[7]; +rzz(pi/2) b[0], out[7]; +rz(-pi/2) b[0]; +rz(-1.536900733201678) out[7]; +ry(3.129320807286707) out[7]; +rz(2.0614966236345014) out[7]; +rzz(pi/2) a[1], out[7]; +rx(pi) a[1]; +rz(2.6508923567501874) out[7]; +ry(3.1293208072867085) out[7]; +rz(3.107697059996571) out[7]; +rzz(pi/2) b[0], out[7]; +ry(-pi/2) b[0]; +rzz(pi/2) a[1], b[0]; +ry(pi) a[1]; +rz(-pi) b[0]; +ry(-pi/256) b[0]; +rzz(pi/2) a[1], b[0]; +rz(-pi/2) a[1]; +rx(-pi) a[1]; +ry(-pi/2) b[0]; +rz(1.5830681730979812) b[0]; +rz(-1.5369007332016145) out[7]; +ry(3.129320807286707) out[7]; +rz(-0.8226060790210514) out[7]; +rzz(pi/2) a[0], out[7]; +rz(pi/2) a[0]; +rz(2.3934024058159675) out[7]; +ry(pi/64) out[7]; +rz(2.3982406999461094) out[7]; +rzz(pi/2) b[3], out[7]; +rz(pi/2) b[3]; +rz(2.3141482804385793) out[7]; +ry(pi/64) out[7]; +rz(2.3189865745687213) out[7]; +rzz(pi/2) a[0], out[7]; +rx(pi) a[0]; +rz(2.3934024058159675) out[7]; +ry(pi/64) out[7]; +rz(2.3982406999461094) out[7]; +rzz(pi/2) b[3], out[7]; +ry(-pi/2) b[3]; +rz(-pi) b[3]; +rzz(pi/2) a[0], b[3]; +ry(pi) a[0]; +rz(-pi) b[3]; +ry(-pi/64) b[3]; +rzz(pi/2) a[0], b[3]; +rz(pi/2) a[0]; +rzz(pi/2) a[0], out[1]; +rz(pi/2) a[0]; +ry(-pi/2) b[3]; +rz(1.619883712007237) b[3]; +ry(-pi/2) out[1]; +rz(3*pi/4) out[1]; +rzz(pi/2) b[2], out[1]; +rz(pi/2) b[2]; +rz(pi/4) out[1]; +rx(-pi/2) out[1]; +rzz(pi/2) a[0], out[1]; +rx(-pi) a[0]; +rz(-pi/2) a[0]; +ry(-pi/2) out[1]; +rz(3*pi/4) out[1]; +rzz(pi/2) b[2], out[1]; +rz(pi/2) b[2]; +rzz(pi/2) a[0], b[2]; +rzz(pi/2) a[0], out[2]; +rz(-pi/2) a[0]; +rz(-pi/2) b[2]; +rx(-pi) b[2]; +rz(3*pi/4) out[1]; +ry(pi/2) out[1]; +rz(-2.1072299372197243) out[1]; +rz(-2.6051590431649654) out[2]; +ry(pi/4) out[2]; +rz(-2.5551204732437065) out[2]; +rzz(pi/2) b[2], out[2]; +rz(-pi/2) b[2]; +rz(-2.157268507140982) out[2]; +ry(pi/4) out[2]; +rz(-2.1072299372197243) out[2]; +rzz(pi/2) a[0], out[2]; +rz(-2.6051590431649654) out[2]; +ry(pi/4) out[2]; +rz(-2.5551204732437065) out[2]; +rzz(pi/2) b[2], out[2]; +ry(-pi/2) b[2]; +rzz(pi/2) a[0], b[2]; +ry(pi) a[0]; +rz(-pi) b[2]; +ry(-pi/4) b[2]; +rzz(pi/2) a[0], b[2]; +rz(-pi/2) a[0]; +rx(-pi) a[0]; +rzz(pi/2) a[0], out[3]; +rz(-pi/2) a[0]; +ry(pi/2) b[2]; +rz(pi/4) b[2]; +rz(-2.1572685071409827) out[2]; +ry(pi/4) out[2]; +rz(0.3325895879972256) out[2]; +rz(-1.903385914792119) out[3]; +ry(7*pi/8) out[3]; +rz(-1.6971534443990168) out[3]; +rzz(pi/2) b[2], out[3]; +rz(pi/2) b[2]; +rz(0.12635711760412294) out[3]; +ry(7*pi/8) out[3]; +rz(-2.8090030655925666) out[3]; +rzz(pi/2) a[0], out[3]; +rz(-1.903385914792119) out[3]; +ry(7*pi/8) out[3]; +rz(-1.6971534443990168) out[3]; +rzz(pi/2) b[2], out[3]; +ry(-pi/2) b[2]; +rz(-pi) b[2]; +rzz(pi/2) a[0], b[2]; +ry(pi) a[0]; +rz(-pi) b[2]; +ry(-pi/8) b[2]; +rzz(pi/2) a[0], b[2]; +rz(-pi/2) a[0]; +rzz(pi/2) a[0], out[4]; +rz(pi/2) a[0]; +ry(-pi/2) b[2]; +rz(5*pi/8) b[2]; +rz(-3.0152355359856715) out[3]; +ry(pi/8) out[3]; +rz(1.294861248499081) out[3]; +rz(0.2759350782958139) out[4]; +ry(15*pi/16) out[4]; +rz(-0.32190695107235934) out[4]; +rzz(pi/2) b[2], out[4]; +rz(-pi/2) b[2]; +rz(1.892703277867259) out[4]; +ry(15*pi/16) out[4]; +rz(-1.846731405090709) out[4]; +rzz(pi/2) a[0], out[4]; +rx(pi) a[0]; +rz(0.2759350782958139) out[4]; +ry(15*pi/16) out[4]; +rz(-0.32190695107235934) out[4]; +rzz(pi/2) b[2], out[4]; +ry(-pi/2) b[2]; +rzz(pi/2) a[0], b[2]; +ry(pi) a[0]; +rz(-pi) b[2]; +ry(-pi/16) b[2]; +rzz(pi/2) a[0], b[2]; +rz(-pi/2) a[0]; +rx(-pi) a[0]; +rzz(pi/2) a[0], out[5]; +rz(-pi/2) a[0]; +ry(-pi/2) b[2]; +rz(9*pi/16) b[2]; +rz(-1.2488893757225379) out[4]; +ry(pi/16) out[4]; +rz(0.9832827023934838) out[4]; +rz(0.5875136244014145) out[5]; +ry(pi/32) out[5]; +rz(1.230855619167917) out[5]; +rzz(pi/2) b[2], out[5]; +rz(-pi/2) b[2]; +rz(0.3399407076269809) out[5]; +ry(pi/32) out[5]; +rz(0.9832827023934785) out[5]; +rzz(pi/2) a[0], out[5]; +rz(0.5875136244014145) out[5]; +ry(pi/32) out[5]; +rz(1.230855619167917) out[5]; +rzz(pi/2) b[2], out[5]; +ry(-pi/2) b[2]; +rzz(pi/2) a[0], b[2]; +ry(pi) a[0]; +rz(-pi) b[2]; +ry(-pi/32) b[2]; +rzz(pi/2) a[0], b[2]; +rz(-pi/2) a[0]; +rzz(pi/2) a[0], out[6]; +rz(pi/2) a[0]; +ry(pi/2) b[2]; +rz(1.472621556370215) b[2]; +rz(-2.801651945962806) out[5]; +ry(3.043417883165112) out[5]; +rz(-0.8226060790210696) out[5]; +rz(2.3934024058159675) out[6]; +ry(pi/64) out[6]; +rz(2.3982406999461094) out[6]; +rzz(pi/2) b[2], out[6]; +rz(pi/2) b[2]; +rz(2.3141482804385793) out[6]; +ry(pi/64) out[6]; +rz(2.3189865745687213) out[6]; +rzz(pi/2) a[0], out[6]; +rx(pi) a[0]; +rz(2.3934024058159675) out[6]; +ry(pi/64) out[6]; +rz(2.3982406999461094) out[6]; +rzz(pi/2) b[2], out[6]; +ry(-pi/2) b[2]; +rz(-pi) b[2]; +rzz(pi/2) a[0], b[2]; +ry(pi) a[0]; +rz(-pi) b[2]; +ry(-pi/64) b[2]; +rzz(pi/2) a[0], b[2]; +rz(-pi/2) a[0]; +rx(-pi) a[0]; +ry(-pi/2) b[2]; +rz(1.619883712007237) b[2]; +rz(-0.8274443731512049) out[6]; +ry(3.0925052683774528) out[6]; +rz(2.3696099671542044) out[6]; +rz(-0.8274443731512049) out[7]; +ry(3.0925052683774528) out[7]; +rz(2.3696099671542044) out[7]; +rzz(pi/2) a[0], out[7]; +rz(-pi/2) a[0]; +rz(2.342779013230494) out[7]; +ry(pi/128) out[7]; +rz(2.4852630457765574) out[7]; +rzz(pi/2) b[2], out[7]; +rz(-pi/2) b[2]; +rz(-0.9144667189816644) out[7]; +ry(pi/128) out[7]; +rz(-0.7719826864355919) out[7]; +rzz(pi/2) a[0], out[7]; +rz(2.342779013230494) out[7]; +ry(pi/128) out[7]; +rz(2.4852630457765574) out[7]; +rzz(pi/2) b[2], out[7]; +ry(-pi/2) b[2]; +rzz(pi/2) a[0], b[2]; +ry(pi) a[0]; +rz(-pi) b[2]; +ry(-pi/128) b[2]; +rzz(pi/2) a[0], b[2]; +rz(-pi/2) a[0]; +rzz(pi/2) a[0], out[0]; +rz(pi/2) a[0]; +ry(-pi/2) b[2]; +rz(1.5953400194010676) b[2]; +ry(-pi/2) out[0]; +rz(3*pi/4) out[0]; +rzz(pi/2) b[1], out[0]; +rz(pi/2) b[1]; +rz(pi/4) out[0]; +rx(-pi/2) out[0]; +rzz(pi/2) a[0], out[0]; +rx(-pi) a[0]; +rz(-pi/2) a[0]; +ry(-pi/2) out[0]; +rz(3*pi/4) out[0]; +rzz(pi/2) b[1], out[0]; +rz(pi/2) b[1]; +rzz(pi/2) a[0], b[1]; +rzz(pi/2) a[0], out[1]; +rz(-pi/2) a[0]; +rz(-pi/2) b[1]; +rx(-pi) b[1]; +rz(3*pi/4) out[0]; +ry(pi/2) out[0]; +rz(-2.1072299372197243) out[0]; +rz(-2.6051590431649654) out[1]; +ry(pi/4) out[1]; +rz(-2.5551204732437065) out[1]; +rzz(pi/2) b[1], out[1]; +rz(-pi/2) b[1]; +rz(-2.157268507140982) out[1]; +ry(pi/4) out[1]; +rz(-2.1072299372197243) out[1]; +rzz(pi/2) a[0], out[1]; +rz(-2.6051590431649654) out[1]; +ry(pi/4) out[1]; +rz(-2.5551204732437065) out[1]; +rzz(pi/2) b[1], out[1]; +ry(-pi/2) b[1]; +rzz(pi/2) a[0], b[1]; +ry(pi) a[0]; +rz(-pi) b[1]; +ry(-pi/4) b[1]; +rzz(pi/2) a[0], b[1]; +rz(-pi/2) a[0]; +rx(-pi) a[0]; +rzz(pi/2) a[0], out[2]; +rz(-pi/2) a[0]; +ry(pi/2) b[1]; +rz(pi/4) b[1]; +rz(-2.1572685071409827) out[1]; +ry(pi/4) out[1]; +rz(0.3325895879972256) out[1]; +rz(-1.903385914792119) out[2]; +ry(7*pi/8) out[2]; +rz(-1.6971534443990168) out[2]; +rzz(pi/2) b[1], out[2]; +rz(pi/2) b[1]; +rz(0.12635711760412294) out[2]; +ry(7*pi/8) out[2]; +rz(-2.8090030655925666) out[2]; +rzz(pi/2) a[0], out[2]; +rz(-1.903385914792119) out[2]; +ry(7*pi/8) out[2]; +rz(-1.6971534443990168) out[2]; +rzz(pi/2) b[1], out[2]; +ry(-pi/2) b[1]; +rz(-pi) b[1]; +rzz(pi/2) a[0], b[1]; +ry(pi) a[0]; +rz(-pi) b[1]; +ry(-pi/8) b[1]; +rzz(pi/2) a[0], b[1]; +rz(-pi/2) a[0]; +rzz(pi/2) a[0], out[3]; +rz(pi/2) a[0]; +ry(-pi/2) b[1]; +rz(5*pi/8) b[1]; +rz(-3.0152355359856715) out[2]; +ry(pi/8) out[2]; +rz(1.294861248499081) out[2]; +rz(0.2759350782958139) out[3]; +ry(15*pi/16) out[3]; +rz(-0.32190695107235934) out[3]; +rzz(pi/2) b[1], out[3]; +rz(-pi/2) b[1]; +rz(1.892703277867259) out[3]; +ry(15*pi/16) out[3]; +rz(-1.846731405090709) out[3]; +rzz(pi/2) a[0], out[3]; +rx(pi) a[0]; +rz(0.2759350782958139) out[3]; +ry(15*pi/16) out[3]; +rz(-0.32190695107235934) out[3]; +rzz(pi/2) b[1], out[3]; +ry(-pi/2) b[1]; +rzz(pi/2) a[0], b[1]; +ry(pi) a[0]; +rz(-pi) b[1]; +ry(-pi/16) b[1]; +rzz(pi/2) a[0], b[1]; +rz(-pi/2) a[0]; +rx(-pi) a[0]; +rzz(pi/2) a[0], out[4]; +rz(-pi/2) a[0]; +ry(-pi/2) b[1]; +rz(9*pi/16) b[1]; +rz(-1.2488893757225379) out[3]; +ry(pi/16) out[3]; +rz(0.9832827023934838) out[3]; +rz(0.5875136244014145) out[4]; +ry(pi/32) out[4]; +rz(1.230855619167917) out[4]; +rzz(pi/2) b[1], out[4]; +rz(-pi/2) b[1]; +rz(0.3399407076269809) out[4]; +ry(pi/32) out[4]; +rz(0.9832827023934785) out[4]; +rzz(pi/2) a[0], out[4]; +rz(0.5875136244014145) out[4]; +ry(pi/32) out[4]; +rz(1.230855619167917) out[4]; +rzz(pi/2) b[1], out[4]; +ry(-pi/2) b[1]; +rzz(pi/2) a[0], b[1]; +ry(pi) a[0]; +rz(-pi) b[1]; +ry(-pi/32) b[1]; +rzz(pi/2) a[0], b[1]; +rz(-pi/2) a[0]; +rzz(pi/2) a[0], out[5]; +rz(pi/2) a[0]; +ry(pi/2) b[1]; +rz(1.472621556370215) b[1]; +rz(-2.801651945962806) out[4]; +ry(3.043417883165112) out[4]; +rz(-0.8226060790210696) out[4]; +rz(2.3934024058159675) out[5]; +ry(pi/64) out[5]; +rz(2.3982406999461094) out[5]; +rzz(pi/2) b[1], out[5]; +rz(pi/2) b[1]; +rz(2.3141482804385793) out[5]; +ry(pi/64) out[5]; +rz(2.3189865745687213) out[5]; +rzz(pi/2) a[0], out[5]; +rx(pi) a[0]; +rz(2.3934024058159675) out[5]; +ry(pi/64) out[5]; +rz(2.3982406999461094) out[5]; +rzz(pi/2) b[1], out[5]; +ry(-pi/2) b[1]; +rz(-pi) b[1]; +rzz(pi/2) a[0], b[1]; +ry(pi) a[0]; +rz(-pi) b[1]; +ry(-pi/64) b[1]; +rzz(pi/2) a[0], b[1]; +rz(-pi/2) a[0]; +rx(-pi) a[0]; +rzz(pi/2) a[0], out[6]; +rz(-pi/2) a[0]; +ry(-pi/2) b[1]; +rz(1.619883712007237) b[1]; +rz(-0.8274443731512049) out[5]; +ry(3.0925052683774528) out[5]; +rz(2.3696099671542044) out[5]; +rz(2.342779013230494) out[6]; +ry(pi/128) out[6]; +rz(2.4852630457765574) out[6]; +rzz(pi/2) b[1], out[6]; +rz(-pi/2) b[1]; +rz(-0.9144667189816644) out[6]; +ry(pi/128) out[6]; +rz(-0.7719826864355919) out[6]; +rzz(pi/2) a[0], out[6]; +rz(2.342779013230494) out[6]; +ry(pi/128) out[6]; +rz(2.4852630457765574) out[6]; +rzz(pi/2) b[1], out[6]; +ry(-pi/2) b[1]; +rzz(pi/2) a[0], b[1]; +ry(pi) a[0]; +rz(-pi) b[1]; +ry(-pi/128) b[1]; +rzz(pi/2) a[0], b[1]; +rz(-pi/2) a[0]; +ry(-pi/2) b[1]; +rz(1.5953400194010676) b[1]; +rz(2.2271259346081145) out[6]; +ry(3.1170489609836234) out[6]; +rz(2.0614966236345236) out[6]; +rz(2.2271259346081145) out[7]; +ry(3.1170489609836234) out[7]; +rz(2.0614966236345236) out[7]; +rzz(pi/2) a[0], out[7]; +rz(pi/2) a[0]; +rz(2.6508923567501874) out[7]; +ry(3.1293208072867085) out[7]; +rz(3.107697059996571) out[7]; +rzz(pi/2) b[1], out[7]; +rz(-pi/2) b[1]; +rz(-1.536900733201678) out[7]; +ry(3.129320807286707) out[7]; +rz(2.0614966236345014) out[7]; +rzz(pi/2) a[0], out[7]; +rx(pi) a[0]; +rz(2.6508923567501874) out[7]; +ry(3.1293208072867085) out[7]; +rz(3.107697059996571) out[7]; +rzz(pi/2) b[1], out[7]; +ry(-pi/2) b[1]; +rzz(pi/2) a[0], b[1]; +ry(pi) a[0]; +rz(-pi) b[1]; +ry(-pi/256) b[1]; +rzz(pi/2) a[0], b[1]; +rz(-pi/2) a[0]; +rx(-pi) a[0]; +rzz(pi/2) a[0], out[0]; +rz(-pi/2) a[0]; +ry(-pi/2) b[1]; +rz(1.5830681730979812) b[1]; +rz(-2.6051590431649654) out[0]; +ry(pi/4) out[0]; +rz(-2.5551204732437065) out[0]; +rzz(pi/2) b[0], out[0]; +rz(-pi/2) b[0]; +rx(-pi) b[0]; +rz(-2.157268507140982) out[0]; +ry(pi/4) out[0]; +rz(-2.1072299372197243) out[0]; +rzz(pi/2) a[0], out[0]; +rz(-1.0343627163700688) out[0]; +rx(3*pi/4) out[0]; +rzz(pi/2) b[0], out[0]; +ry(-pi/2) b[0]; +rz(-pi) b[0]; +rzz(pi/2) a[0], b[0]; +ry(pi) a[0]; +rz(-pi) b[0]; +ry(-pi/4) b[0]; +rzz(pi/2) a[0], b[0]; +rz(-pi/2) a[0]; +rx(-pi) a[0]; +rzz(pi/2) a[0], out[1]; +rz(-pi/2) a[0]; +ry(pi/2) b[0]; +rz(pi/4) b[0]; +ry(-pi/4) out[0]; +rz(-pi) out[0]; +rz(-1.903385914792119) out[1]; +ry(7*pi/8) out[1]; +rz(-1.6971534443990168) out[1]; +rzz(pi/2) b[0], out[1]; +rz(pi/2) b[0]; +rz(0.12635711760412294) out[1]; +ry(7*pi/8) out[1]; +rz(-2.8090030655925666) out[1]; +rzz(pi/2) a[0], out[1]; +rz(-1.903385914792119) out[1]; +ry(7*pi/8) out[1]; +rz(-1.6971534443990168) out[1]; +rzz(pi/2) b[0], out[1]; +ry(-pi/2) b[0]; +rz(-pi) b[0]; +rzz(pi/2) a[0], b[0]; +ry(pi) a[0]; +rz(-pi) b[0]; +ry(-pi/8) b[0]; +rzz(pi/2) a[0], b[0]; +rz(-pi/2) a[0]; +rzz(pi/2) a[0], out[2]; +rz(pi/2) a[0]; +ry(-pi/2) b[0]; +rz(5*pi/8) b[0]; +rz(-1.4444392091907758) out[1]; +rx(3*pi/8) out[1]; +rzz(pi/2) out[0], out[1]; +ry(pi) out[0]; +rz(-pi) out[1]; +ry(-pi/4) out[1]; +rzz(pi/2) out[0], out[1]; +rz(-pi/4) out[0]; +rz(pi/2) out[1]; +ry(pi/4) out[1]; +rz(0.2759350782958139) out[2]; +ry(15*pi/16) out[2]; +rz(-0.32190695107235934) out[2]; +rzz(pi/2) b[0], out[2]; +rz(-pi/2) b[0]; +rz(1.892703277867259) out[2]; +ry(15*pi/16) out[2]; +rz(-1.846731405090709) out[2]; +rzz(pi/2) a[0], out[2]; +rx(pi) a[0]; +rz(0.2759350782958139) out[2]; +ry(15*pi/16) out[2]; +rz(-0.32190695107235934) out[2]; +rzz(pi/2) b[0], out[2]; +ry(-pi/2) b[0]; +rzz(pi/2) a[0], b[0]; +ry(pi) a[0]; +rz(-pi) b[0]; +ry(-pi/16) b[0]; +rzz(pi/2) a[0], b[0]; +rz(-pi/2) a[0]; +rx(-pi) a[0]; +rzz(pi/2) a[0], out[3]; +rz(-pi/2) a[0]; +ry(-pi/2) b[0]; +rz(9*pi/16) b[0]; +rz(-2.8196857025174333) out[2]; +rx(-7*pi/16) out[2]; +rzz(pi/2) out[0], out[2]; +ry(pi) out[0]; +rz(-pi) out[2]; +ry(-pi/8) out[2]; +rzz(pi/2) out[0], out[2]; +rz(pi/8) out[0]; +ry(pi) out[0]; +rx(-7*pi/8) out[2]; +rzz(pi/2) out[1], out[2]; +ry(pi) out[1]; +rz(-pi) out[2]; +ry(-pi/4) out[2]; +rzz(pi/2) out[1], out[2]; +rz(-pi/4) out[1]; +rz(pi/2) out[2]; +ry(pi/4) out[2]; +rz(0.5875136244014145) out[3]; +ry(pi/32) out[3]; +rz(1.230855619167917) out[3]; +rzz(pi/2) b[0], out[3]; +rz(-pi/2) b[0]; +rz(0.3399407076269809) out[3]; +ry(pi/32) out[3]; +rz(0.9832827023934785) out[3]; +rzz(pi/2) a[0], out[3]; +rz(0.5875136244014145) out[3]; +ry(pi/32) out[3]; +rz(1.230855619167917) out[3]; +rzz(pi/2) b[0], out[3]; +ry(-pi/2) b[0]; +rzz(pi/2) a[0], b[0]; +ry(pi) a[0]; +rz(-pi) b[0]; +ry(-pi/32) b[0]; +rzz(pi/2) a[0], b[0]; +rz(-pi/2) a[0]; +rzz(pi/2) a[0], out[4]; +rz(pi/2) a[0]; +ry(pi/2) b[0]; +rz(1.472621556370215) b[0]; +rz(-1.2308556191679187) out[3]; +rx(-1.4726215563702154) out[3]; +rzz(pi/2) out[0], out[3]; +ry(pi) out[0]; +rz(-pi) out[3]; +ry(-pi/16) out[3]; +rzz(pi/2) out[0], out[3]; +rz(pi/16) out[0]; +rz(-pi) out[3]; +rx(-pi/16) out[3]; +rzz(pi/2) out[1], out[3]; +ry(pi) out[1]; +rz(-pi) out[3]; +ry(-pi/8) out[3]; +rzz(pi/2) out[1], out[3]; +rz(pi/8) out[1]; +ry(pi) out[1]; +rx(-7*pi/8) out[3]; +rzz(pi/2) out[2], out[3]; +ry(pi) out[2]; +rz(-pi) out[3]; +ry(-pi/4) out[3]; +rzz(pi/2) out[2], out[3]; +rz(-pi/4) out[2]; +rz(pi/2) out[3]; +ry(pi/4) out[3]; +rz(2.3934024058159675) out[4]; +ry(pi/64) out[4]; +rz(2.3982406999461094) out[4]; +rzz(pi/2) b[0], out[4]; +rz(pi/2) b[0]; +rz(2.3141482804385793) out[4]; +ry(pi/64) out[4]; +rz(2.3189865745687213) out[4]; +rzz(pi/2) a[0], out[4]; +rx(pi) a[0]; +rz(2.3934024058159675) out[4]; +ry(pi/64) out[4]; +rz(2.3982406999461094) out[4]; +rzz(pi/2) b[0], out[4]; +ry(-pi/2) b[0]; +rz(-pi) b[0]; +rzz(pi/2) a[0], b[0]; +ry(pi) a[0]; +rz(-pi) b[0]; +ry(-pi/64) b[0]; +rzz(pi/2) a[0], b[0]; +rz(-pi/2) a[0]; +rx(-pi) a[0]; +rzz(pi/2) a[0], out[5]; +rz(-pi/2) a[0]; +ry(-pi/2) b[0]; +rz(1.619883712007237) b[0]; +rz(-2.3982406999461032) out[4]; +rx(1.5217089415825562) out[4]; +rzz(pi/2) out[0], out[4]; +ry(pi) out[0]; +rz(-pi) out[4]; +ry(-pi/32) out[4]; +rzz(pi/2) out[0], out[4]; +rz(-pi/32) out[0]; +ry(pi) out[0]; +rx(3.0434178831651124) out[4]; +rzz(pi/2) out[1], out[4]; +ry(pi) out[1]; +rz(-pi) out[4]; +ry(-pi/16) out[4]; +rzz(pi/2) out[1], out[4]; +rz(pi/16) out[1]; +rz(-pi) out[4]; +rx(-pi/16) out[4]; +rzz(pi/2) out[2], out[4]; +ry(pi) out[2]; +rz(-pi) out[4]; +ry(-pi/8) out[4]; +rzz(pi/2) out[2], out[4]; +rz(pi/8) out[2]; +ry(pi) out[2]; +rx(-7*pi/8) out[4]; +rzz(pi/2) out[3], out[4]; +ry(pi) out[3]; +rz(-pi) out[4]; +ry(-pi/4) out[4]; +rzz(pi/2) out[3], out[4]; +rz(-pi/4) out[3]; +rz(pi/2) out[4]; +ry(pi/4) out[4]; +rz(2.342779013230494) out[5]; +ry(pi/128) out[5]; +rz(2.4852630457765574) out[5]; +rzz(pi/2) b[0], out[5]; +rz(-pi/2) b[0]; +rz(-0.9144667189816644) out[5]; +ry(pi/128) out[5]; +rz(-0.7719826864355919) out[5]; +rzz(pi/2) a[0], out[5]; +rz(2.342779013230494) out[5]; +ry(pi/128) out[5]; +rz(2.4852630457765574) out[5]; +rzz(pi/2) b[0], out[5]; +ry(-pi/2) b[0]; +rzz(pi/2) a[0], b[0]; +ry(pi) a[0]; +rz(-pi) b[0]; +ry(-pi/128) b[0]; +rzz(pi/2) a[0], b[0]; +rz(-pi/2) a[0]; +rzz(pi/2) a[0], out[6]; +rz(pi/2) a[0]; +ry(-pi/2) b[0]; +rz(1.5953400194010676) b[0]; +rz(0.6563296078132304) out[5]; +rx(-1.5953400194010665) out[5]; +rzz(pi/2) out[0], out[5]; +ry(pi) out[0]; +rz(-pi) out[5]; +ry(-pi/64) out[5]; +rzz(pi/2) out[0], out[5]; +rz(-pi/64) out[0]; +rx(-pi/64) out[5]; +rz(-pi) out[5]; +rzz(pi/2) out[1], out[5]; +ry(pi) out[1]; +rz(-pi) out[5]; +ry(-pi/32) out[5]; +rzz(pi/2) out[1], out[5]; +rz(-pi/32) out[1]; +ry(pi) out[1]; +rx(3.0434178831651124) out[5]; +rzz(pi/2) out[2], out[5]; +ry(pi) out[2]; +rz(-pi) out[5]; +ry(-pi/16) out[5]; +rzz(pi/2) out[2], out[5]; +rz(pi/16) out[2]; +rz(-pi) out[5]; +rx(-pi/16) out[5]; +rzz(pi/2) out[3], out[5]; +ry(pi) out[3]; +rz(-pi) out[5]; +ry(-pi/8) out[5]; +rzz(pi/2) out[3], out[5]; +rz(pi/8) out[3]; +ry(pi) out[3]; +rx(-7*pi/8) out[5]; +rzz(pi/2) out[4], out[5]; +ry(pi) out[4]; +rz(-pi) out[5]; +ry(-pi/4) out[5]; +rzz(pi/2) out[4], out[5]; +rz(-pi/4) out[4]; +rz(pi/2) out[5]; +ry(pi/4) out[5]; +rz(2.6508923567501874) out[6]; +ry(3.1293208072867085) out[6]; +rz(3.107697059996571) out[6]; +rzz(pi/2) b[0], out[6]; +rz(-pi/2) b[0]; +rz(-1.536900733201678) out[6]; +ry(3.129320807286707) out[6]; +rz(2.0614966236345014) out[6]; +rzz(pi/2) a[0], out[6]; +rx(pi) a[0]; +rz(2.6508923567501874) out[6]; +ry(3.1293208072867085) out[6]; +rz(3.107697059996571) out[6]; +rzz(pi/2) b[0], out[6]; +ry(-pi/2) b[0]; +rzz(pi/2) a[0], b[0]; +ry(pi) a[0]; +rz(-pi) b[0]; +ry(-pi/256) b[0]; +rzz(pi/2) a[0], b[0]; +rz(-pi/2) a[0]; +rx(-pi) a[0]; +ry(-pi/2) b[0]; +rz(1.5830681730979812) b[0]; +rz(0.033895593593253004) out[6]; +rx(-1.5585244804918106) out[6]; +rzz(pi/2) out[0], out[6]; +ry(pi) out[0]; +rz(-pi) out[6]; +ry(-pi/128) out[6]; +rzz(pi/2) out[0], out[6]; +rz(pi/128) out[0]; +ry(pi) out[0]; +rx(-3.117048960983623) out[6]; +rzz(pi/2) out[1], out[6]; +ry(pi) out[1]; +rz(-pi) out[6]; +ry(-pi/64) out[6]; +rzz(pi/2) out[1], out[6]; +rz(-pi/64) out[1]; +rx(-pi/64) out[6]; +rz(-pi) out[6]; +rzz(pi/2) out[2], out[6]; +ry(pi) out[2]; +rz(-pi) out[6]; +ry(-pi/32) out[6]; +rzz(pi/2) out[2], out[6]; +rz(-pi/32) out[2]; +ry(pi) out[2]; +rx(3.0434178831651124) out[6]; +rzz(pi/2) out[3], out[6]; +ry(pi) out[3]; +rz(-pi) out[6]; +ry(-pi/16) out[6]; +rzz(pi/2) out[3], out[6]; +rz(pi/16) out[3]; +rz(-pi) out[6]; +rx(-pi/16) out[6]; +rzz(pi/2) out[4], out[6]; +ry(pi) out[4]; +rz(-pi) out[6]; +ry(-pi/8) out[6]; +rzz(pi/2) out[4], out[6]; +rz(pi/8) out[4]; +ry(pi) out[4]; +rx(-7*pi/8) out[6]; +rzz(pi/2) out[5], out[6]; +ry(pi) out[5]; +rz(-pi) out[6]; +ry(-pi/4) out[6]; +rzz(pi/2) out[5], out[6]; +rz(-pi/4) out[5]; +rz(pi/2) out[6]; +ry(pi/4) out[6]; +rz(1.60469192038813) out[7]; +ry(pi/256) out[7]; +rz(0.0246709403348655) out[7]; +rzz(pi/2) a[0], out[7]; +rz(-pi/2) a[0]; +rz(1.5461253864600328) out[7]; +ry(pi/512) out[7]; +rz(-3.032529328254043) out[7]; +rzz(pi/2) b[0], out[7]; +rz(-pi/2) b[0]; +rz(-1.6798596521305806) out[7]; +ry(pi/512) out[7]; +rz(0.02467094033479711) out[7]; +rzz(pi/2) a[0], out[7]; +rz(1.5461253864600328) out[7]; +ry(pi/512) out[7]; +rz(-3.032529328254043) out[7]; +rzz(pi/2) b[0], out[7]; +ry(-pi/2) b[0]; +rzz(pi/2) a[0], b[0]; +ry(pi) a[0]; +rz(-pi) b[0]; +ry(-pi/512) b[0]; +rzz(pi/2) a[0], b[0]; +rz(-pi/2) a[0]; +rx(-pi) a[0]; +ry(-pi/2) b[0]; +rz(1.5769322499464389) b[0]; +rz(3.0325293282540606) out[7]; +rx(-1.564660403643354) out[7]; +rzz(pi/2) out[0], out[7]; +ry(pi) out[0]; +rz(-pi) out[7]; +ry(-pi/256) out[7]; +rzz(pi/2) out[0], out[7]; +rz(-1.5585244804918115) out[0]; +rx(-pi) out[0]; +rz(-pi) out[7]; +rx(-pi/256) out[7]; +rzz(pi/2) out[1], out[7]; +ry(pi) out[1]; +rz(-pi) out[7]; +ry(-pi/128) out[7]; +rzz(pi/2) out[1], out[7]; +rz(-1.5462526341887266) out[1]; +rx(-pi) out[1]; +rx(-3.117048960983623) out[7]; +rzz(pi/2) out[2], out[7]; +ry(pi) out[2]; +rz(-pi) out[7]; +ry(-pi/64) out[7]; +rzz(pi/2) out[2], out[7]; +rz(-1.6198837120072376) out[2]; +rx(-pi/64) out[7]; +rz(-pi) out[7]; +rzz(pi/2) out[3], out[7]; +ry(pi) out[3]; +rz(-pi) out[7]; +ry(-pi/32) out[7]; +rzz(pi/2) out[3], out[7]; +rz(-1.668971097219578) out[3]; +rx(3.0434178831651124) out[7]; +rzz(pi/2) out[4], out[7]; +ry(pi) out[4]; +rz(-pi) out[7]; +ry(-pi/16) out[7]; +rzz(pi/2) out[4], out[7]; +rz(-7*pi/16) out[4]; +rx(-pi) out[4]; +rz(-pi) out[7]; +rx(-pi/16) out[7]; +rzz(pi/2) out[5], out[7]; +ry(pi) out[5]; +rz(-pi) out[7]; +ry(-pi/8) out[7]; +rzz(pi/2) out[5], out[7]; +rz(-3*pi/8) out[5]; +rx(-pi) out[5]; +rx(-7*pi/8) out[7]; +rzz(pi/2) out[6], out[7]; +ry(pi) out[6]; +rz(-pi) out[7]; +ry(-pi/4) out[7]; +rzz(pi/2) out[6], out[7]; +rz(-3*pi/4) out[6]; +rx(-3*pi/4) out[7]; From dc07b4baf00a2a7322d299caa6bbb00c8bccebf7 Mon Sep 17 00:00:00 2001 From: orpuente-MS <156957451+orpuente-MS@users.noreply.github.com> Date: Fri, 11 Apr 2025 12:34:23 -0700 Subject: [PATCH 099/108] Fix bug in const evaluator (#2296) Fixes #2294 --- compiler/qsc_qasm3/src/parser/error.rs | 2 +- .../qsc_qasm3/src/semantic/ast/const_eval.rs | 3 +- .../src/tests/statement/const_eval.rs | 148 ++++++++++++++++++ 3 files changed, 151 insertions(+), 2 deletions(-) diff --git a/compiler/qsc_qasm3/src/parser/error.rs b/compiler/qsc_qasm3/src/parser/error.rs index 99042af191..1897374012 100644 --- a/compiler/qsc_qasm3/src/parser/error.rs +++ b/compiler/qsc_qasm3/src/parser/error.rs @@ -122,7 +122,7 @@ pub enum ErrorKind { #[error("missing switch statement case labels")] #[diagnostic(code("Qasm3.Parse.MissingSwitchCaseLabels"))] MissingSwitchCaseLabels(#[label] Span), - #[error("missing switch statement case labels")] + #[error("missing gate call operands")] #[diagnostic(code("Qasm3.Parse.MissingGateCallOperands"))] MissingGateCallOperands(#[label] Span), #[error("expected an item or closing brace, found {0}")] diff --git a/compiler/qsc_qasm3/src/semantic/ast/const_eval.rs b/compiler/qsc_qasm3/src/semantic/ast/const_eval.rs index 3254c16899..f7659a01df 100644 --- a/compiler/qsc_qasm3/src/semantic/ast/const_eval.rs +++ b/compiler/qsc_qasm3/src/semantic/ast/const_eval.rs @@ -161,12 +161,13 @@ impl BinaryOpExpr { fn const_eval(&self, ctx: &mut Lowerer) -> Option { use LiteralKind::{Angle, Bit, Bitstring, Bool, Float, Int}; - assert_binary_op_ty_invariant(self.op, &self.lhs.ty, &self.rhs.ty); let lhs = self.lhs.const_eval(ctx); let rhs = self.rhs.const_eval(ctx); let (lhs, rhs) = (lhs?, rhs?); let lhs_ty = &self.lhs.ty; + assert_binary_op_ty_invariant(self.op, &self.lhs.ty, &self.rhs.ty); + match &self.op { // Bit Shifts BinOp::Shl => { diff --git a/compiler/qsc_qasm3/src/tests/statement/const_eval.rs b/compiler/qsc_qasm3/src/tests/statement/const_eval.rs index b948bb94a2..794a9daa31 100644 --- a/compiler/qsc_qasm3/src/tests/statement/const_eval.rs +++ b/compiler/qsc_qasm3/src/tests/statement/const_eval.rs @@ -1982,3 +1982,151 @@ fn cast_to_bit() -> miette::Result<(), Vec> { .assert_eq(&qsharp); Ok(()) } + +#[test] +fn binary_op_err_type_fails() { + let source = r#" + int[a + b] x = 2; + "#; + + let Err(errs) = compile_qasm_to_qsharp(source) else { + panic!("should have generated an error"); + }; + let errs: Vec<_> = errs.iter().map(|e| format!("{e:?}")).collect(); + let errs_string = errs.join("\n"); + expect![[r#" + Qsc.Qasm3.Lowerer.UndefinedSymbol + + x undefined symbol: a + ,-[Test.qasm:2:13] + 1 | + 2 | int[a + b] x = 2; + : ^ + 3 | + `---- + + Qsc.Qasm3.Lowerer.UndefinedSymbol + + x undefined symbol: b + ,-[Test.qasm:2:17] + 1 | + 2 | int[a + b] x = 2; + : ^ + 3 | + `---- + + Qsc.Qasm3.Lowerer.CannotCast + + x cannot cast expression of type Err to type UInt(None, true) + ,-[Test.qasm:2:13] + 1 | + 2 | int[a + b] x = 2; + : ^^^^^ + 3 | + `---- + + Qsc.Qasm3.Compile.ExprMustBeConst + + x expression must be const + ,-[Test.qasm:2:13] + 1 | + 2 | int[a + b] x = 2; + : ^^^^^ + 3 | + `---- + + Qsc.Qasm3.Lowerer.ExprMustBeConst + + x designator must be a const expression + ,-[Test.qasm:2:13] + 1 | + 2 | int[a + b] x = 2; + : ^^^^^ + 3 | + `---- + + Qsc.Qasm3.Lowerer.CannotCastLiteral + + x cannot cast literal expression of type Int(None, true) to type Err + ,-[Test.qasm:2:9] + 1 | + 2 | int[a + b] x = 2; + : ^^^^^^^^^^^^^^^^^ + 3 | + `---- + "#]] + .assert_eq(&errs_string); +} + +#[test] +fn fuzzer_issue_2294() { + let source = r#" + ctrl(5/_)@l + "#; + + let Err(errs) = compile_qasm_to_qsharp(source) else { + panic!("should have generated an error"); + }; + let errs: Vec<_> = errs.iter().map(|e| format!("{e:?}")).collect(); + let errs_string = errs.join("\n"); + expect![[r#" + Qasm3.Parse.Token + + x expected `;`, found EOF + ,-[Test.qasm:3:5] + 2 | ctrl(5/_)@l + 3 | + `---- + + Qasm3.Parse.MissingGateCallOperands + + x missing gate call operands + ,-[Test.qasm:2:9] + 1 | + 2 | ctrl(5/_)@l + : ^^^^^^^^^^^ + 3 | + `---- + + Qsc.Qasm3.Lowerer.UndefinedSymbol + + x undefined symbol: _ + ,-[Test.qasm:2:16] + 1 | + 2 | ctrl(5/_)@l + : ^ + 3 | + `---- + + Qsc.Qasm3.Lowerer.CannotCast + + x cannot cast expression of type Err to type Float(None, true) + ,-[Test.qasm:2:16] + 1 | + 2 | ctrl(5/_)@l + : ^ + 3 | + `---- + + Qsc.Qasm3.Compile.ExprMustBeConst + + x expression must be const + ,-[Test.qasm:2:16] + 1 | + 2 | ctrl(5/_)@l + : ^ + 3 | + `---- + + Qsc.Qasm3.Lowerer.ExprMustBeConst + + x ctrl modifier argument must be a const expression + ,-[Test.qasm:2:14] + 1 | + 2 | ctrl(5/_)@l + : ^^^ + 3 | + `---- + "#]] + .assert_eq(&errs_string); +} From 686d408c13b8faa0173cafe29e80b334656b5282 Mon Sep 17 00:00:00 2001 From: Ian Davis Date: Wed, 16 Apr 2025 10:35:43 -0700 Subject: [PATCH 100/108] Fix fuzz issues 2297 & 2298 (#2299) - fixes #2297 - fixes #2298 --------- Co-authored-by: Oscar Puente --- Cargo.lock | 1 + compiler/qsc_frontend/src/compile.rs | 3 +- compiler/qsc_qasm3/Cargo.toml | 1 + compiler/qsc_qasm3/src/ast_builder.rs | 29 +++-- compiler/qsc_qasm3/src/compiler.rs | 47 +++++--- compiler/qsc_qasm3/src/parser/ast.rs | 11 +- compiler/qsc_qasm3/src/parser/mut_visit.rs | 11 +- compiler/qsc_qasm3/src/parser/prim.rs | 69 ++++++++++++ compiler/qsc_qasm3/src/parser/stmt.rs | 17 ++- .../src/parser/stmt/tests/gate_call.rs | 16 ++- .../qsc_qasm3/src/parser/stmt/tests/gphase.rs | 12 ++- .../stmt/tests/invalid_stmts/gate_calls.rs | 20 +++- compiler/qsc_qasm3/src/semantic/ast.rs | 13 ++- compiler/qsc_qasm3/src/semantic/lowerer.rs | 44 ++++++-- .../src/semantic/tests/assignment.rs | 2 +- .../semantic/tests/statements/break_stmt.rs | 2 +- .../tests/statements/continue_stmt.rs | 2 +- compiler/qsc_qasm3/src/tests.rs | 100 +++++++++++++++++- compiler/qsc_qasm3/src/tests/declaration.rs | 9 ++ compiler/qsc_qasm3/src/tests/fuzz.rs | 72 +++++++++++++ 20 files changed, 415 insertions(+), 66 deletions(-) create mode 100644 compiler/qsc_qasm3/src/tests/fuzz.rs diff --git a/Cargo.lock b/Cargo.lock index ea7939fde2..4d9c5ae568 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1291,6 +1291,7 @@ dependencies = [ "num-traits", "qsc", "qsc_ast", + "qsc_codegen", "qsc_data_structures", "qsc_frontend", "qsc_hir", diff --git a/compiler/qsc_frontend/src/compile.rs b/compiler/qsc_frontend/src/compile.rs index 570a0a5488..9065ddd8b7 100644 --- a/compiler/qsc_frontend/src/compile.rs +++ b/compiler/qsc_frontend/src/compile.rs @@ -489,7 +489,8 @@ pub fn std(store: &PackageStore, capabilities: TargetCapabilityFlags) -> Compile unit } -fn parse_all( +#[must_use] +pub fn parse_all( sources: &SourceMap, features: LanguageFeatures, ) -> (ast::Package, Vec) { diff --git a/compiler/qsc_qasm3/Cargo.toml b/compiler/qsc_qasm3/Cargo.toml index 3ea33cefdc..c86dab58b2 100644 --- a/compiler/qsc_qasm3/Cargo.toml +++ b/compiler/qsc_qasm3/Cargo.toml @@ -33,6 +33,7 @@ miette = { workspace = true, features = ["fancy"] } # loading qasm from file. qsc = { path = "../qsc" } qsc_qasm3 = { path = ".", features = ["fs"] } +qsc_codegen = { path = "../qsc_codegen" } [features] fs = [] diff --git a/compiler/qsc_qasm3/src/ast_builder.rs b/compiler/qsc_qasm3/src/ast_builder.rs index e74154743a..d2c1e88b8e 100644 --- a/compiler/qsc_qasm3/src/ast_builder.rs +++ b/compiler/qsc_qasm3/src/ast_builder.rs @@ -441,7 +441,7 @@ pub(crate) fn build_path_ident_expr>( }; let path = ast::Path { id: NodeId::default(), - span: Span::default(), + span: name_span, segments: None, name: Box::new(ident), }; @@ -749,7 +749,7 @@ pub(crate) fn build_global_call_with_one_param>( span: name_span, kind: Box::new(ast::ExprKind::Path(PathKind::Ok(Box::new(ast::Path { id: NodeId::default(), - span: Span::default(), + span: name_span, segments: None, name: Box::new(ident), })))), @@ -787,7 +787,7 @@ pub(crate) fn build_global_call_with_two_params>( span: name_span, kind: Box::new(ast::ExprKind::Path(PathKind::Ok(Box::new(ast::Path { id: NodeId::default(), - span: Span::default(), + span: name_span, segments: None, name: Box::new(ident), })))), @@ -838,10 +838,15 @@ pub fn build_gate_call_param_expr(args: Vec, remaining: usize) -> Expr { } pub(crate) fn build_math_call_no_params(name: &str, span: Span) -> Expr { - build_call_no_params(name, &["Microsoft", "Quantum", "Math"], span) + build_call_no_params(name, &["Microsoft", "Quantum", "Math"], span, span) } -pub(crate) fn build_call_no_params(name: &str, idents: &[&str], span: Span) -> Expr { +pub(crate) fn build_call_no_params( + name: &str, + idents: &[&str], + fn_call_span: Span, + fn_name_span: Span, +) -> Expr { let segments = build_idents(idents); let fn_name = Ident { name: Rc::from(name), @@ -852,8 +857,9 @@ pub(crate) fn build_call_no_params(name: &str, idents: &[&str], span: Span) -> E segments, name: Box::new(fn_name), id: NodeId::default(), - span: Span::default(), + span: fn_name_span, })))), + span: fn_name_span, ..Default::default() }; let call = ExprKind::Call( @@ -863,7 +869,7 @@ pub(crate) fn build_call_no_params(name: &str, idents: &[&str], span: Span) -> E Expr { id: NodeId::default(), - span, + span: fn_call_span, kind: Box::new(call), } } @@ -887,8 +893,9 @@ pub(crate) fn build_call_with_param( segments, name: Box::new(fn_name), id: NodeId::default(), - span: Span::default(), + span: name_span, })))), + span: name_span, ..Default::default() }; let call = ExprKind::Call( @@ -925,8 +932,9 @@ pub(crate) fn build_call_with_params( segments, name: Box::new(fn_name), id: NodeId::default(), - span: Span::default(), + span: name_span, })))), + span: name_span, ..Default::default() }; let call = ExprKind::Call(Box::new(path_expr), Box::new(build_tuple_expr(operands))); @@ -1122,6 +1130,7 @@ where let pat = Pat { kind: Box::new(PatKind::Bind(Box::new(ident), tydef)), + span: name_span, ..Default::default() }; let mutability = if is_const { @@ -1482,7 +1491,7 @@ pub(crate) fn build_index_expr(expr: Expr, index_expr: Expr, span: Span) -> Expr } pub(crate) fn build_barrier_call(span: Span) -> Stmt { - let expr = build_call_no_params("__quantum__qis__barrier__body", &[], span); + let expr = build_call_no_params("__quantum__qis__barrier__body", &[], span, span); build_stmt_semi_from_expr(expr) } diff --git a/compiler/qsc_qasm3/src/compiler.rs b/compiler/qsc_qasm3/src/compiler.rs index fc10679dd1..fce699a955 100644 --- a/compiler/qsc_qasm3/src/compiler.rs +++ b/compiler/qsc_qasm3/src/compiler.rs @@ -133,7 +133,7 @@ impl QasmCompiler { /// Build a package with namespace and an operation /// containing the compiled statements. fn build_file(&mut self) -> (Package, Option) { - let whole_span = Span::default(); + let whole_span = self.whole_span(); let operation_name = self.config.operation_name(); let (operation, mut signature) = self.create_entry_operation(operation_name, whole_span); let ns = self.config.namespace(); @@ -152,7 +152,7 @@ impl QasmCompiler { /// Creates an operation with the given name. fn build_operation(&mut self) -> (qsast::Package, Option) { - let whole_span = Span::default(); + let whole_span = self.whole_span(); let operation_name = self.config.operation_name(); let (operation, signature) = self.create_entry_operation(operation_name, whole_span); ( @@ -183,6 +183,21 @@ impl QasmCompiler { } } + /// Returns a span containing all the statements in the program. + fn whole_span(&self) -> Span { + let main_src = self + .source_map + .iter() + .next() + .expect("there is at least one source"); + + #[allow(clippy::cast_possible_truncation)] + Span { + lo: main_src.offset, + hi: main_src.offset + main_src.contents.len() as u32, + } + } + fn create_entry_operation>( &mut self, name: S, @@ -493,7 +508,7 @@ impl QasmCompiler { let index_expr = indices[0].clone(); let stmt = build_indexed_assignment_statement( - symbol.span, + stmt.name_span, symbol.name.clone(), index_expr, rhs, @@ -559,6 +574,7 @@ impl QasmCompiler { let stmt = semast::IndexedAssignStmt { span: stmt.span, symbol_id: stmt.symbol_id, + name_span: stmt.lhs.span, indices: stmt.indices.clone(), rhs, }; @@ -761,9 +777,9 @@ impl QasmCompiler { fn compile_function_call_expr(&mut self, expr: &semast::FunctionCall) -> qsast::Expr { let symbol = self.symbols[expr.symbol_id].clone(); let name = &symbol.name; - let name_span = symbol.span; + let name_span = expr.fn_name_span; if expr.args.is_empty() { - build_call_no_params(name, &[], expr.span) + build_call_no_params(name, &[], expr.span, expr.fn_name_span) } else { let args: Vec<_> = expr .args @@ -793,11 +809,12 @@ impl QasmCompiler { // Take the number of qubit args that the gates expects from the source qubits. let gate_qubits = qubits.split_off(qubits.len().saturating_sub(stmt.quantum_arity as usize)); + // Then merge the classical args with the qubit args. This will give // us the args for the call prior to wrapping in tuples for controls. let args: Vec<_> = args.into_iter().chain(gate_qubits).collect(); let mut args = build_gate_call_param_expr(args, qubits.len()); - let mut callee = build_path_ident_expr(&symbol.name, symbol.span, stmt.span); + let mut callee = build_path_ident_expr(&symbol.name, stmt.gate_name_span, stmt.span); for modifier in &stmt.modifiers { match &modifier.kind { @@ -805,7 +822,7 @@ impl QasmCompiler { callee = build_unary_op_expr( qsast::UnOp::Functor(qsast::Functor::Adj), callee, - modifier.span, + modifier.modifier_keyword_span, ); } semast::GateModifierKind::Pow(expr) => { @@ -830,7 +847,7 @@ impl QasmCompiler { callee = build_unary_op_expr( qsast::UnOp::Functor(qsast::Functor::Ctl), callee, - modifier.span, + modifier.modifier_keyword_span, ); } semast::GateModifierKind::NegCtrl(num_ctrls) => { @@ -848,8 +865,11 @@ impl QasmCompiler { let ctrls = build_expr_array_expr(ctrl, modifier.span); let lit_0 = build_lit_int_expr(0, Span::default()); args = build_tuple_expr(vec![lit_0, callee, ctrls, args]); - callee = - build_path_ident_expr("ApplyControlledOnInt", modifier.span, stmt.span); + callee = build_path_ident_expr( + "ApplyControlledOnInt", + modifier.modifier_keyword_span, + stmt.span, + ); } } } @@ -970,7 +990,7 @@ impl QasmCompiler { cargs, qargs, body, - symbol.span, + stmt.name_span, stmt.body.span, stmt.span, build_path_ident_ty("Unit"), @@ -1137,7 +1157,7 @@ impl QasmCompiler { span: expr.span, ..Default::default() }, - semast::ExprKind::Ident(symbol_id) => self.compile_ident_expr(*symbol_id), + semast::ExprKind::Ident(symbol_id) => self.compile_ident_expr(*symbol_id, expr.span), semast::ExprKind::IndexedIdentifier(indexed_ident) => { self.compile_indexed_ident_expr(indexed_ident) } @@ -1158,9 +1178,8 @@ impl QasmCompiler { } } - fn compile_ident_expr(&mut self, symbol_id: SymbolId) -> qsast::Expr { + fn compile_ident_expr(&mut self, symbol_id: SymbolId, span: Span) -> qsast::Expr { let symbol = &self.symbols[symbol_id]; - let span = symbol.span; match symbol.name.as_str() { "euler" | "ℇ" => build_math_call_no_params("E", span), "pi" | "π" => build_math_call_no_params("PI", span), diff --git a/compiler/qsc_qasm3/src/parser/ast.rs b/compiler/qsc_qasm3/src/parser/ast.rs index f96bcad05e..34d975947d 100644 --- a/compiler/qsc_qasm3/src/parser/ast.rs +++ b/compiler/qsc_qasm3/src/parser/ast.rs @@ -16,6 +16,8 @@ use std::{ rc::Rc, }; +use super::prim::SeqItem; + /// An alternative to `Vec` that uses less stack space. pub(crate) type List = Box<[Box]>; @@ -649,12 +651,15 @@ impl Display for RangeDefinition { #[derive(Clone, Debug)] pub struct QuantumGateModifier { pub span: Span, + pub modifier_keyword_span: Span, pub kind: GateModifierKind, } impl Display for QuantumGateModifier { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { - write!(f, "QuantumGateModifier {}: {}", self.span, self.kind) + writeln_header(f, "QuantumGateModifier", self.span)?; + writeln_field(f, "modifier_keyword_span", &self.modifier_keyword_span)?; + write_field(f, "kind", &self.kind) } } @@ -1033,8 +1038,8 @@ impl Display for QubitDeclaration { pub struct QuantumGateDefinition { pub span: Span, pub ident: Box, - pub params: List, - pub qubits: List, + pub params: List>, + pub qubits: List>, pub body: Box, } diff --git a/compiler/qsc_qasm3/src/parser/mut_visit.rs b/compiler/qsc_qasm3/src/parser/mut_visit.rs index e848076e1a..968ff07f83 100644 --- a/compiler/qsc_qasm3/src/parser/mut_visit.rs +++ b/compiler/qsc_qasm3/src/parser/mut_visit.rs @@ -551,8 +551,14 @@ fn walk_pragma_stmt(vis: &mut impl MutVisitor, stmt: &mut Pragma) { fn walk_quantum_gate_definition_stmt(vis: &mut impl MutVisitor, stmt: &mut QuantumGateDefinition) { vis.visit_span(&mut stmt.span); vis.visit_ident(&mut stmt.ident); - stmt.params.iter_mut().for_each(|p| vis.visit_ident(p)); - stmt.qubits.iter_mut().for_each(|p| vis.visit_ident(p)); + stmt.params.iter_mut().for_each(|p| match &mut **p { + super::prim::SeqItem::Item(i) => vis.visit_ident(i), + super::prim::SeqItem::Missing(span) => vis.visit_span(span), + }); + stmt.qubits.iter_mut().for_each(|p| match &mut **p { + super::prim::SeqItem::Item(i) => vis.visit_ident(i), + super::prim::SeqItem::Missing(span) => vis.visit_span(span), + }); vis.visit_block(&mut stmt.body); } @@ -820,6 +826,7 @@ pub fn walk_enumerable_set(vis: &mut impl MutVisitor, set: &mut EnumerableSet) { pub fn walk_gate_modifier(vis: &mut impl MutVisitor, modifier: &mut QuantumGateModifier) { vis.visit_span(&mut modifier.span); + vis.visit_span(&mut modifier.modifier_keyword_span); match &mut modifier.kind { GateModifierKind::Inv => {} GateModifierKind::Pow(expr) => vis.visit_expr(expr), diff --git a/compiler/qsc_qasm3/src/parser/prim.rs b/compiler/qsc_qasm3/src/parser/prim.rs index c00f8c5413..b5ffce2b2c 100644 --- a/compiler/qsc_qasm3/src/parser/prim.rs +++ b/compiler/qsc_qasm3/src/parser/prim.rs @@ -198,6 +198,75 @@ where Ok((xs, final_sep)) } +#[derive(Clone, Copy, Debug)] +pub enum SeqItem { + Item(T), + Missing(Span), +} + +impl std::fmt::Display for SeqItem { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + SeqItem::Item(x) => write!(f, "{x}"), + SeqItem::Missing(span) => write!(f, "Missing {span}"), + } + } +} + +impl SeqItem { + pub fn item(self) -> Option { + match self { + SeqItem::Item(x) => Some(x), + SeqItem::Missing(_) => None, + } + } + + pub fn item_as_ref(&self) -> Option<&T> { + match self { + SeqItem::Item(x) => Some(x), + SeqItem::Missing(_) => None, + } + } + + pub fn is_missing(&self) -> bool { + matches!(self, SeqItem::Missing(_)) + } +} + +/// Parses a sequence of items separated by commas. +/// Supports recovering on missing items. +pub(super) fn seq_item( + s: &mut ParserContext, + mut p: impl Parser, +) -> Result<(Vec>, FinalSep)> { + let mut xs = Vec::new(); + let mut final_sep = FinalSep::Missing; + while s.peek().kind == TokenKind::Comma { + let mut span = s.peek().span; + span.hi = span.lo; + s.push_error(Error::new(ErrorKind::MissingSeqEntry(span))); + xs.push(SeqItem::Missing(span)); + s.advance(); + } + while let Some(x) = opt(s, &mut p)? { + xs.push(SeqItem::Item(x)); + if token(s, TokenKind::Comma).is_ok() { + while s.peek().kind == TokenKind::Comma { + let mut span = s.peek().span; + span.hi = span.lo; + s.push_error(Error::new(ErrorKind::MissingSeqEntry(span))); + xs.push(SeqItem::Missing(span)); + s.advance(); + } + final_sep = FinalSep::Present; + } else { + final_sep = FinalSep::Missing; + break; + } + } + Ok((xs, final_sep)) +} + /// Try to parse with the given parser. /// /// If the parser fails on the first token, returns the default value. diff --git a/compiler/qsc_qasm3/src/parser/stmt.rs b/compiler/qsc_qasm3/src/parser/stmt.rs index 21cab143c7..ba99072283 100644 --- a/compiler/qsc_qasm3/src/parser/stmt.rs +++ b/compiler/qsc_qasm3/src/parser/stmt.rs @@ -11,7 +11,10 @@ use super::{ completion::word_kinds::WordKinds, error::{Error, ErrorKind}, expr::{self, designator, gate_operand, indexed_identifier}, - prim::{self, barrier, many, opt, recovering, recovering_semi, recovering_token, seq, shorten}, + prim::{ + self, barrier, many, opt, recovering, recovering_semi, recovering_token, seq, seq_item, + shorten, SeqItem, + }, Result, }; use crate::{ @@ -614,7 +617,7 @@ fn parse_gatedef(s: &mut ParserContext) -> Result { token(s, TokenKind::Keyword(crate::keyword::Keyword::Gate))?; let ident = Box::new(prim::ident(s)?); let params = opt(s, gate_params)?.unwrap_or_else(Vec::new); - let (qubits, _) = seq(s, prim::ident)?; + let (qubits, _) = seq_item(s, prim::ident)?; let body = Box::new(parse_block(s)?); Ok(StmtKind::QuantumGateDefinition(QuantumGateDefinition { span: s.span(lo), @@ -625,9 +628,9 @@ fn parse_gatedef(s: &mut ParserContext) -> Result { })) } -fn gate_params(s: &mut ParserContext<'_>) -> Result> { +fn gate_params(s: &mut ParserContext<'_>) -> Result>> { token(s, TokenKind::Open(Delim::Paren))?; - let (params, _) = seq(s, prim::ident)?; + let (params, _) = seq_item(s, prim::ident)?; token(s, TokenKind::Close(Delim::Paren))?; Ok(params) } @@ -1586,15 +1589,19 @@ fn parse_gphase( /// ) AT`. fn gate_modifier(s: &mut ParserContext) -> Result { let lo = s.peek().span.lo; + let modifier_keyword_span; let kind = if opt(s, |s| token(s, TokenKind::Inv))?.is_some() { + modifier_keyword_span = s.span(lo); GateModifierKind::Inv } else if opt(s, |s| token(s, TokenKind::Pow))?.is_some() { + modifier_keyword_span = s.span(lo); token(s, TokenKind::Open(Delim::Paren))?; let expr = expr::expr(s)?; recovering_token(s, TokenKind::Close(Delim::Paren)); GateModifierKind::Pow(expr) } else if opt(s, |s| token(s, TokenKind::Ctrl))?.is_some() { + modifier_keyword_span = s.span(lo); let expr = opt(s, |s| { token(s, TokenKind::Open(Delim::Paren))?; let expr = expr::expr(s)?; @@ -1604,6 +1611,7 @@ fn gate_modifier(s: &mut ParserContext) -> Result { GateModifierKind::Ctrl(expr) } else { token(s, TokenKind::NegCtrl)?; + modifier_keyword_span = s.span(lo); let expr = opt(s, |s| { token(s, TokenKind::Open(Delim::Paren))?; let expr = expr::expr(s)?; @@ -1617,6 +1625,7 @@ fn gate_modifier(s: &mut ParserContext) -> Result { Ok(QuantumGateModifier { span: s.span(lo), + modifier_keyword_span, kind, }) } diff --git a/compiler/qsc_qasm3/src/parser/stmt/tests/gate_call.rs b/compiler/qsc_qasm3/src/parser/stmt/tests/gate_call.rs index 103b3f066f..d905a0095f 100644 --- a/compiler/qsc_qasm3/src/parser/stmt/tests/gate_call.rs +++ b/compiler/qsc_qasm3/src/parser/stmt/tests/gate_call.rs @@ -92,7 +92,9 @@ fn gate_with_no_qubits() { annotations: kind: GateCall [0-8]: modifiers: - QuantumGateModifier [0-5]: Inv + QuantumGateModifier [0-5]: + modifier_keyword_span: [0-3] + kind: Inv name: Ident [6-7] "H" args: duration: @@ -147,7 +149,9 @@ fn gate_call_inv_modifier() { annotations: kind: GateCall [0-11]: modifiers: - QuantumGateModifier [0-5]: Inv + QuantumGateModifier [0-5]: + modifier_keyword_span: [0-3] + kind: Inv name: Ident [6-7] "H" args: duration: @@ -170,8 +174,12 @@ fn gate_call_ctrl_inv_modifiers() { annotations: kind: GateCall [0-38]: modifiers: - QuantumGateModifier [0-9]: Ctrl Some(Expr { span: Span { lo: 5, hi: 6 }, kind: Lit(Lit { span: Span { lo: 5, hi: 6 }, kind: Int(2) }) }) - QuantumGateModifier [10-15]: Inv + QuantumGateModifier [0-9]: + modifier_keyword_span: [0-4] + kind: Ctrl Some(Expr { span: Span { lo: 5, hi: 6 }, kind: Lit(Lit { span: Span { lo: 5, hi: 6 }, kind: Int(2) }) }) + QuantumGateModifier [10-15]: + modifier_keyword_span: [10-13] + kind: Inv name: Ident [16-18] "Rx" args: Expr [19-25]: BinaryOpExpr: diff --git a/compiler/qsc_qasm3/src/parser/stmt/tests/gphase.rs b/compiler/qsc_qasm3/src/parser/stmt/tests/gphase.rs index ca70bdef4f..4c589d366f 100644 --- a/compiler/qsc_qasm3/src/parser/stmt/tests/gphase.rs +++ b/compiler/qsc_qasm3/src/parser/stmt/tests/gphase.rs @@ -166,7 +166,9 @@ fn gphase_inv_modifier() { kind: GPhase [0-16]: gphase_token_span: [6-12] modifiers: - QuantumGateModifier [0-5]: Inv + QuantumGateModifier [0-5]: + modifier_keyword_span: [0-3] + kind: Inv args: Expr [13-14]: Ident [13-14] "a" duration: @@ -185,8 +187,12 @@ fn gphase_ctrl_inv_modifiers() { kind: GPhase [0-31]: gphase_token_span: [13-19] modifiers: - QuantumGateModifier [0-6]: Ctrl None - QuantumGateModifier [7-12]: Inv + QuantumGateModifier [0-6]: + modifier_keyword_span: [0-4] + kind: Ctrl None + QuantumGateModifier [7-12]: + modifier_keyword_span: [7-10] + kind: Inv args: Expr [20-26]: BinaryOpExpr: op: Div diff --git a/compiler/qsc_qasm3/src/parser/stmt/tests/invalid_stmts/gate_calls.rs b/compiler/qsc_qasm3/src/parser/stmt/tests/invalid_stmts/gate_calls.rs index 0fdeb38c57..e788385359 100644 --- a/compiler/qsc_qasm3/src/parser/stmt/tests/invalid_stmts/gate_calls.rs +++ b/compiler/qsc_qasm3/src/parser/stmt/tests/invalid_stmts/gate_calls.rs @@ -82,7 +82,9 @@ fn pow_with_two_args() { annotations: kind: GateCall [0-17]: modifiers: - QuantumGateModifier [0-11]: Pow Expr [4-5]: Lit: Int(2) + QuantumGateModifier [0-11]: + modifier_keyword_span: [0-3] + kind: Pow Expr [4-5]: Lit: Int(2) name: Ident [12-13] "x" args: duration: @@ -117,7 +119,9 @@ fn ctrl_with_two_args() { annotations: kind: GateCall [0-22]: modifiers: - QuantumGateModifier [0-12]: Ctrl Some(Expr { span: Span { lo: 5, hi: 6 }, kind: Lit(Lit { span: Span { lo: 5, hi: 6 }, kind: Int(2) }) }) + QuantumGateModifier [0-12]: + modifier_keyword_span: [0-4] + kind: Ctrl Some(Expr { span: Span { lo: 5, hi: 6 }, kind: Lit(Lit { span: Span { lo: 5, hi: 6 }, kind: Int(2) }) }) name: Ident [13-14] "x" args: duration: @@ -154,7 +158,9 @@ fn negctrl_with_two_args() { annotations: kind: GateCall [0-25]: modifiers: - QuantumGateModifier [0-15]: NegCtrl Some(Expr { span: Span { lo: 8, hi: 9 }, kind: Lit(Lit { span: Span { lo: 8, hi: 9 }, kind: Int(2) }) }) + QuantumGateModifier [0-15]: + modifier_keyword_span: [0-7] + kind: NegCtrl Some(Expr { span: Span { lo: 8, hi: 9 }, kind: Lit(Lit { span: Span { lo: 8, hi: 9 }, kind: Int(2) }) }) name: Ident [16-17] "x" args: duration: @@ -191,8 +197,12 @@ fn inv_with_arg() { annotations: kind: GateCall [0-25]: modifiers: - QuantumGateModifier [0-8]: Inv - QuantumGateModifier [9-15]: Ctrl None + QuantumGateModifier [0-8]: + modifier_keyword_span: [0-3] + kind: Inv + QuantumGateModifier [9-15]: + modifier_keyword_span: [9-13] + kind: Ctrl None name: Ident [16-17] "x" args: duration: diff --git a/compiler/qsc_qasm3/src/semantic/ast.rs b/compiler/qsc_qasm3/src/semantic/ast.rs index e044269351..b0bd15d7c0 100644 --- a/compiler/qsc_qasm3/src/semantic/ast.rs +++ b/compiler/qsc_qasm3/src/semantic/ast.rs @@ -677,12 +677,15 @@ impl Display for RangeDefinition { #[derive(Clone, Debug)] pub struct QuantumGateModifier { pub span: Span, + pub modifier_keyword_span: Span, pub kind: GateModifierKind, } impl Display for QuantumGateModifier { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { - write!(f, "QuantumGateModifier {}: {}", self.span, self.kind) + writeln_header(f, "QuantumGateModifier", self.span)?; + writeln_field(f, "modifier_keyword_span", &self.modifier_keyword_span)?; + write_field(f, "kind", &self.kind) } } @@ -1068,6 +1071,7 @@ impl Display for QubitArrayDeclaration { #[derive(Clone, Debug)] pub struct QuantumGateDefinition { pub span: Span, + pub name_span: Span, pub symbol_id: SymbolId, pub params: Box<[SymbolId]>, pub qubits: Box<[SymbolId]>, @@ -1077,6 +1081,7 @@ pub struct QuantumGateDefinition { impl Display for QuantumGateDefinition { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { writeln_header(f, "Gate", self.span)?; + writeln_field(f, "name_span", &self.name_span)?; writeln_field(f, "symbol_id", &self.symbol_id)?; writeln_list_field(f, "parameters", &self.params)?; writeln_list_field(f, "qubits", &self.qubits)?; @@ -1106,6 +1111,7 @@ pub struct GateCall { pub span: Span, pub modifiers: List, pub symbol_id: SymbolId, + pub gate_name_span: Span, pub args: List, pub qubits: List, pub duration: Option, @@ -1118,6 +1124,7 @@ impl Display for GateCall { writeln_header(f, "GateCall", self.span)?; writeln_list_field(f, "modifiers", &self.modifiers)?; writeln_field(f, "symbol_id", &self.symbol_id)?; + writeln_field(f, "gate_name_span", &self.gate_name_span)?; writeln_list_field(f, "args", &self.args)?; writeln_list_field(f, "qubits", &self.qubits)?; writeln_opt_field(f, "duration", self.duration.as_ref())?; @@ -1458,6 +1465,7 @@ impl Display for AssignStmt { pub struct IndexedAssignStmt { pub span: Span, pub symbol_id: SymbolId, + pub name_span: Span, pub indices: List, pub rhs: Expr, } @@ -1466,6 +1474,7 @@ impl Display for IndexedAssignStmt { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { writeln_header(f, "AssignStmt", self.span)?; writeln_field(f, "symbol_id", &self.symbol_id)?; + writeln_field(f, "name_span", &self.name_span)?; writeln_list_field(f, "indices", &self.indices)?; write_field(f, "rhs", &self.rhs) } @@ -1535,6 +1544,7 @@ impl Display for BinaryOpExpr { #[derive(Clone, Debug)] pub struct FunctionCall { pub span: Span, + pub fn_name_span: Span, pub symbol_id: SymbolId, pub args: List, } @@ -1542,6 +1552,7 @@ pub struct FunctionCall { impl Display for FunctionCall { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { writeln_header(f, "FunctionCall", self.span)?; + writeln_field(f, "fn_name_span", &self.fn_name_span)?; writeln_field(f, "symbol_id", &self.symbol_id)?; write_list_field(f, "args", &self.args) } diff --git a/compiler/qsc_qasm3/src/semantic/lowerer.rs b/compiler/qsc_qasm3/src/semantic/lowerer.rs index 169f6d03b3..8bbcbba57d 100644 --- a/compiler/qsc_qasm3/src/semantic/lowerer.rs +++ b/compiler/qsc_qasm3/src/semantic/lowerer.rs @@ -478,7 +478,6 @@ impl Lowerer { let kind = SemanticErrorKind::CannotUpdateConstVariable(ident.name.to_string(), ident.span); self.push_semantic_error(kind); - return semantic::StmtKind::Err; } semantic::StmtKind::Assign(semantic::AssignStmt { @@ -529,6 +528,7 @@ impl Lowerer { semantic::StmtKind::IndexedAssign(semantic::IndexedAssignStmt { span, symbol_id, + name_span: index_expr.name.span, indices, rhs, }) @@ -1148,7 +1148,7 @@ impl Lowerer { .collect(); let body = semantic::Block { - span: stmt.span, + span: stmt.body.span, stmts: list_from_iter(stmt.body.stmts.iter().map(|stmt| self.lower_stmt(stmt))), }; @@ -1421,6 +1421,7 @@ impl Lowerer { let kind = Box::new(semantic::ExprKind::FunctionCall(semantic::FunctionCall { span: expr.span, + fn_name_span: expr.name.span, symbol_id, args, })); @@ -1466,7 +1467,7 @@ impl Lowerer { if let Some((gate_name, implicit_modifier)) = try_get_qsharp_name_and_implicit_modifiers(&name, stmt.name.span) { - // Override the gate name if we mapped with modifiers + // Override the gate name if we mapped with modifiers. name = gate_name; // 2. Get implicit modifiers and make them explicit. @@ -1531,6 +1532,7 @@ impl Lowerer { span: stmt.span, modifiers, symbol_id, + gate_name_span: stmt.name.span, args, qubits, duration, @@ -1542,7 +1544,7 @@ impl Lowerer { // But it won't need to check arities, know about implicit modifiers, or do // any casting of classical args. There is still some inherit complexity to // building a Q# gate call with this information, but it won't be cluttered - // by all the semantic analysis. + // by all the QASM semantic analysis. } /// This is just syntax sugar around a gate call. @@ -1583,6 +1585,7 @@ impl Lowerer { Some(semantic::QuantumGateModifier { span: modifier.span, + modifier_keyword_span: modifier.modifier_keyword_span, kind, }) } @@ -1707,9 +1710,17 @@ impl Lowerer { // 2. Push the gate symbol to the symbol table. #[allow(clippy::cast_possible_truncation)] - let classical_arity = stmt.params.len() as u32; + let classical_arity = stmt + .params + .iter() + .filter_map(|seq_item| seq_item.item_as_ref()) + .count() as u32; #[allow(clippy::cast_possible_truncation)] - let quantum_arity = stmt.qubits.len() as u32; + let quantum_arity = stmt + .qubits + .iter() + .filter_map(|seq_item| seq_item.item_as_ref()) + .count() as u32; let name = stmt.ident.name.clone(); let ty = crate::semantic::types::Type::Gate(classical_arity, quantum_arity); let qsharp_ty = crate::types::Type::Callable( @@ -1723,31 +1734,40 @@ impl Lowerer { // Push the scope where the gate definition lives. self.symbols.push_scope(ScopeKind::Gate); + // Design Note: If a formal parameter is missing (i.e. there are two consecutive commas and we + // have a missing item in the formal parameters list), we have two options: + // 1. Treat the missing item as if it wasn't there, and just push a parser + // error saying there is a missing item. This is what Rust does. + // 2. Treat the missing item as a Type::Err and make it part of the gate + // signature, this is what Q# does. + // We decided to go with (1) because it avoids propagating the SeqItem enum + // to the compiler, which is simpler. let params = stmt .params .iter() + .filter_map(|seq_item| seq_item.item_as_ref()) .map(|arg| { let ty = crate::semantic::types::Type::Angle(None, false); let qsharp_ty = self.convert_semantic_type_to_qsharp_type(&ty, Span::default()); let symbol = Symbol::new(&arg.name, arg.span, ty, qsharp_ty, IOKind::Default); self.try_insert_or_get_existing_symbol_id(&arg.name, symbol) }) - .collect(); + .collect::>(); let qubits = stmt .qubits .iter() + .filter_map(|seq_item| seq_item.item_as_ref()) .map(|arg| { let ty = crate::semantic::types::Type::Qubit; let qsharp_ty = self.convert_semantic_type_to_qsharp_type(&ty, Span::default()); - let symbol = - Symbol::new(&arg.name, stmt.ident.span, ty, qsharp_ty, IOKind::Default); + let symbol = Symbol::new(&arg.name, arg.span, ty, qsharp_ty, IOKind::Default); self.try_insert_or_get_existing_symbol_id(&arg.name, symbol) }) - .collect(); + .collect::>(); let body = semantic::Block { - span: stmt.span, + span: stmt.body.span, stmts: list_from_iter(stmt.body.stmts.iter().map(|stmt| self.lower_stmt(stmt))), }; @@ -1756,6 +1776,7 @@ impl Lowerer { semantic::StmtKind::QuantumGateDefinition(semantic::QuantumGateDefinition { span: stmt.span, + name_span: stmt.ident.span, symbol_id, params, qubits, @@ -3372,6 +3393,7 @@ fn try_get_qsharp_name_and_implicit_modifiers>( let make_modifier = |kind| semantic::QuantumGateModifier { span: name_span, + modifier_keyword_span: name_span, kind, }; diff --git a/compiler/qsc_qasm3/src/semantic/tests/assignment.rs b/compiler/qsc_qasm3/src/semantic/tests/assignment.rs index bdf65084d2..170dd08e2e 100644 --- a/compiler/qsc_qasm3/src/semantic/tests/assignment.rs +++ b/compiler/qsc_qasm3/src/semantic/tests/assignment.rs @@ -7,7 +7,7 @@ use super::check; #[test] #[ignore = "Not yet implemented"] -fn too_many_indicies_in_indexed_assignment() { +fn too_many_indices_in_indexed_assignment() { check( r#" array[float[32], 3, 2] multiDim = {{1.1, 1.2}, {2.1, 2.2}, {3.1, 3.2}}; diff --git a/compiler/qsc_qasm3/src/semantic/tests/statements/break_stmt.rs b/compiler/qsc_qasm3/src/semantic/tests/statements/break_stmt.rs index f677e18f70..aa487a2df4 100644 --- a/compiler/qsc_qasm3/src/semantic/tests/statements/break_stmt.rs +++ b/compiler/qsc_qasm3/src/semantic/tests/statements/break_stmt.rs @@ -118,7 +118,7 @@ fn intermediate_def_scope_fails() { has_qubit_params: false parameters: return_type: () - body: Block [36-54]: + body: Block [44-54]: Stmt [46-52]: annotations: kind: Err diff --git a/compiler/qsc_qasm3/src/semantic/tests/statements/continue_stmt.rs b/compiler/qsc_qasm3/src/semantic/tests/statements/continue_stmt.rs index 4709fec712..9a154037f0 100644 --- a/compiler/qsc_qasm3/src/semantic/tests/statements/continue_stmt.rs +++ b/compiler/qsc_qasm3/src/semantic/tests/statements/continue_stmt.rs @@ -118,7 +118,7 @@ fn intermediate_def_scope_fails() { has_qubit_params: false parameters: return_type: () - body: Block [36-57]: + body: Block [44-57]: Stmt [46-55]: annotations: kind: Err diff --git a/compiler/qsc_qasm3/src/tests.rs b/compiler/qsc_qasm3/src/tests.rs index 901ce15265..47171dbd1d 100644 --- a/compiler/qsc_qasm3/src/tests.rs +++ b/compiler/qsc_qasm3/src/tests.rs @@ -1,24 +1,29 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. +use crate::io::{InMemorySourceResolver, SourceResolver}; +use crate::semantic::{parse_source, QasmSemanticParseResult}; use crate::stdlib::compile::package_store_with_qasm; -use crate::{CompilerConfig, OutputSemantics, ProgramType, QasmCompileUnit, QubitSemantics}; +use crate::{ + compile_to_qsharp_ast_with_config, CompilerConfig, OutputSemantics, ProgramType, + QasmCompileUnit, QubitSemantics, +}; use miette::Report; +use qsc::compile::compile_ast; use qsc::interpret::Error; +use qsc::target::Profile; use qsc::{ ast::{mut_visit::MutVisitor, Package, Stmt, TopLevelNode}, - target::Profile, SourceMap, Span, }; use qsc_hir::hir::PackageId; +use qsc_passes::PackageType; use std::{path::Path, sync::Arc}; -use crate::io::{InMemorySourceResolver, SourceResolver}; -use crate::semantic::{parse_source, QasmSemanticParseResult}; - pub(crate) mod assignment; pub(crate) mod declaration; pub(crate) mod expression; +pub(crate) mod fuzz; pub(crate) mod output; pub(crate) mod sample_circuits; pub(crate) mod scopes; @@ -197,6 +202,40 @@ fn compile_qasm_to_qir(source: &str, profile: Profile) -> Result String { qsc::codegen::qsharp::write_stmt_string(stmt) } @@ -406,3 +445,54 @@ impl qsc::hir::mut_visit::MutVisitor for HirDespanner { span.lo = 0; } } + +mod qsharp { + use qsc_ast::ast::Package; + use qsc_data_structures::language_features::LanguageFeatures; + use qsc_frontend::compile::{parse_all, SourceMap}; + + pub(super) fn parse_package(sources: Option) -> Package { + let (ast_package, _) = parse_all(&sources.unwrap_or_default(), LanguageFeatures::empty()); + ast_package + } + + #[must_use] + pub(super) fn parse_program(program: &str) -> Package { + let sources = SourceMap::new([("test".into(), program.into())], None); + parse_package(Some(sources)) + } +} + +#[allow(dead_code)] +pub(crate) fn compare_qasm_and_qasharp_asts(source: &str) { + // 1. Generate a despaned QASM package. + let config = crate::CompilerConfig::new( + crate::QubitSemantics::Qiskit, + crate::OutputSemantics::Qiskit, + crate::ProgramType::File, + None, + None, + ); + let mut resolver = crate::io::InMemorySourceResolver::from_iter([]); + let unit = crate::compile_to_qsharp_ast_with_config( + source, + "source.qasm", + Some(&mut resolver), + config, + ); + let qasm_package = unit.package.as_ref().expect("package must exist"); + let despanned_qasm_ast = AstDespanner.despan(qasm_package); + + // 2. Generate Q# source from the QASM ast. + let qsharp_src = gen_qsharp(&despanned_qasm_ast); + + // 3. Generate a despaned Q# package using the Q# compiler. + let qsharp_package = qsharp::parse_program(&qsharp_src); + let despanned_qsharp_ast = AstDespanner.despan(&qsharp_package); + + // 4. Compare diffs between the ASTs generated by QASM and by Q#. + let despanned_qasm_ast = despanned_qasm_ast.to_string(); + let despanned_qsharp_ast = despanned_qsharp_ast.to_string(); + + difference::assert_diff!(&despanned_qasm_ast, &despanned_qsharp_ast, "\n", 0); +} diff --git a/compiler/qsc_qasm3/src/tests/declaration.rs b/compiler/qsc_qasm3/src/tests/declaration.rs index 209de9563b..7d6f7e6434 100644 --- a/compiler/qsc_qasm3/src/tests/declaration.rs +++ b/compiler/qsc_qasm3/src/tests/declaration.rs @@ -18,6 +18,9 @@ use crate::{ CompilerConfig, OutputSemantics, ProgramType, QubitSemantics, }; use miette::Report; +use qsc::target::Profile; + +use super::compile_qasm_best_effort; #[test] fn classical() -> miette::Result<(), Vec> { @@ -105,3 +108,9 @@ fn stretch() { .to_string() .contains("stretch default values are not supported"),); } + +#[test] +fn gate_decl_with_missing_seq_item_doesnt_panic() { + let source = r#"gate g1 x,,y {}"#; + compile_qasm_best_effort(source, Profile::Unrestricted); +} diff --git a/compiler/qsc_qasm3/src/tests/fuzz.rs b/compiler/qsc_qasm3/src/tests/fuzz.rs new file mode 100644 index 0000000000..4f6e43972b --- /dev/null +++ b/compiler/qsc_qasm3/src/tests/fuzz.rs @@ -0,0 +1,72 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +use super::compile_qasm_best_effort; +use qsc::target::Profile; + +/// We also had an issue where +/// 1. naming a gate the same as a qubit parameter of a parent gate, +/// 2. and then referencing the qubit parameter of the inner gate. +/// +/// was causing a panic in the Q# resolver. +#[test] +fn fuzz_2297_referencing_qubit_parameter() { + let source = r#" + gate g q0 { + gate q0 q1 {} + q1; + } + "#; + super::compare_qasm_and_qasharp_asts(source); + compile_qasm_best_effort(source, Profile::Unrestricted); +} + +/// The same panic happened when referencing an angle parameter. +#[test] +fn fuzz_2297_referencing_angle_parameter() { + let source = r#" + gate g q0 { + gate q0(r) q1 {} + r; + } + "#; + super::compare_qasm_and_qasharp_asts(source); + compile_qasm_best_effort(source, Profile::Unrestricted); +} + +/// Subroutines didn't have this problem, even though they are also +/// compiled to operations when they take qubit arguments. +#[test] +fn fuzz_2297_def() { + let source = r#" + def g(qubit q0) { + def q0(qubit q1) {} + q1; + } + "#; + super::compare_qasm_and_qasharp_asts(source); + compile_qasm_best_effort(source, Profile::Unrestricted); +} + +/// We also had an issue where, in the same conditions as `fuzz_2297`, +/// a missing identifier in a comma separated list of formal paremeters +/// would generate an empty string Identifier and forward it to Q#, +/// which yields an invalid Q# AST. +#[test] +fn fuzz_2297_with_trailing_comma() { + let source = r#" + gate g q0 { + gate q0 ,q1 {} + q1; + } + "#; + super::compare_qasm_and_qasharp_asts(source); + compile_qasm_best_effort(source, Profile::Unrestricted); +} + +#[test] +fn fuzz_2298() { + let source = r#"gate y()a{gate a,b{}b"#; + super::compare_qasm_and_qasharp_asts(source); + compile_qasm_best_effort(source, Profile::Unrestricted); +} From 312cc2a5df15bcc0fc37bf58273760e8344352ab Mon Sep 17 00:00:00 2001 From: Ian Davis Date: Wed, 16 Apr 2025 10:51:28 -0700 Subject: [PATCH 101/108] Remove dead parser code from old compilation (#2300) --- compiler/qsc_qasm3/src/ast_builder.rs | 36 ---- compiler/qsc_qasm3/src/lib.rs | 1 - compiler/qsc_qasm3/src/runtime.rs | 290 -------------------------- 3 files changed, 327 deletions(-) delete mode 100644 compiler/qsc_qasm3/src/runtime.rs diff --git a/compiler/qsc_qasm3/src/ast_builder.rs b/compiler/qsc_qasm3/src/ast_builder.rs index d2c1e88b8e..5a1c9de1ee 100644 --- a/compiler/qsc_qasm3/src/ast_builder.rs +++ b/compiler/qsc_qasm3/src/ast_builder.rs @@ -15,7 +15,6 @@ use qsc_data_structures::span::Span; use crate::{ parser::ast::{list_from_iter, List}, - runtime::RuntimeFunctions, stdlib::angle::Angle, types::{ArrayDimensions, Complex}, }; @@ -668,21 +667,6 @@ pub(crate) fn build_if_expr_then_block(cond: Expr, then_block: Block, span: Span } } -pub(crate) fn build_cast_call_two_params( - function: RuntimeFunctions, - fst: ast::Expr, - snd: ast::Expr, - name_span: Span, - operand_span: Span, -) -> ast::Expr { - let name = match function { - RuntimeFunctions::IntAsResultArrayBE => "__IntAsResultArrayBE__", - _ => panic!("Unsupported cast function"), - }; - - build_global_call_with_two_params(name, fst, snd, name_span, operand_span) -} - pub(crate) fn build_cast_call_by_name( name: &str, expr: ast::Expr, @@ -692,26 +676,6 @@ pub(crate) fn build_cast_call_by_name( build_global_call_with_one_param(name, expr, name_span, operand_span) } -pub(crate) fn build_cast_call( - function: RuntimeFunctions, - expr: ast::Expr, - name_span: Span, - operand_span: Span, -) -> ast::Expr { - let name = match function { - RuntimeFunctions::BoolAsResult => "__BoolAsResult__", - RuntimeFunctions::BoolAsInt => "__BoolAsInt__", - RuntimeFunctions::BoolAsBigInt => "__BoolAsBigInt__", - RuntimeFunctions::BoolAsDouble => "__BoolAsDouble__", - RuntimeFunctions::ResultAsBool => "__ResultAsBool__", - RuntimeFunctions::ResultAsInt => "__ResultAsInt__", - RuntimeFunctions::ResultAsBigInt => "__ResultAsBigInt__", - RuntimeFunctions::ResultArrayAsIntBE => "__ResultArrayAsIntBE__", - _ => panic!("Unsupported cast function"), - }; - build_global_call_with_one_param(name, expr, name_span, operand_span) -} - pub(crate) fn build_measure_call( expr: ast::Expr, name_span: Span, diff --git a/compiler/qsc_qasm3/src/lib.rs b/compiler/qsc_qasm3/src/lib.rs index 1536cfb12a..725b26b0e4 100644 --- a/compiler/qsc_qasm3/src/lib.rs +++ b/compiler/qsc_qasm3/src/lib.rs @@ -14,7 +14,6 @@ pub mod io; mod keyword; mod lex; pub mod parser; -mod runtime; pub mod semantic; mod types; diff --git a/compiler/qsc_qasm3/src/runtime.rs b/compiler/qsc_qasm3/src/runtime.rs deleted file mode 100644 index d1bf332285..0000000000 --- a/compiler/qsc_qasm3/src/runtime.rs +++ /dev/null @@ -1,290 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. - -//! Q# doesn't support gphase and U gates which are used in QASM3. -//! We provide the implementation of these gates here so that QASM3 -//! users can still define custom gates in terms of these operations. -//! -//! We also provide runtime functions that are used in the generated AST. -//! These functions are not part of the QASM3 standard, but are used to implement -//! utility fuctions that would be cumbersome to implement building the AST -//! directly. -//! -//! Finally, we provide QASM3 runtime functions mapped to their Q# counterparts. - -use bitflags::bitflags; - -use qsc_ast::ast::{Stmt, TopLevelNode}; -use qsc_data_structures::language_features::LanguageFeatures; - -/// The POW function is used to implement the `pow` modifier in QASM3 for integers. -const POW: &str = " -operation __Pow__<'T>(N: Int, op: ('T => Unit is Adj), target : 'T) : Unit is Adj { - let op = if N > 0 { () => op(target) } else { () => Adjoint op(target) }; - for _ in 1..Microsoft.Quantum.Math.AbsI(N) { - op() - } -} -"; - -/// The ``BARRIER`` function is used to implement the `barrier` statement in QASM3. -/// The `@SimulatableIntrinsic` attribute is used to mark the operation for QIR -/// generation. -/// Q# doesn't support barriers, so this is a no-op. We need to figure out what -/// barriers mean in the context of QIR in the future for better support. -const BARRIER: &str = " -@SimulatableIntrinsic() -operation __quantum__qis__barrier__body() : Unit {} -"; - -/// The ``BOOL_AS_RESULT`` function is used to implement the cast expr in QASM3 for bool to bit. -/// This already exists in the Q# library, but is defined as a marker for casts from QASM3. -const BOOL_AS_RESULT: &str = " -function __BoolAsResult__(input: Bool) : Result { - Microsoft.Quantum.Convert.BoolAsResult(input) -} -"; - -/// The ``BOOL_AS_INT`` function is used to implement the cast expr in QASM3 for bool to int. -const BOOL_AS_INT: &str = " -function __BoolAsInt__(value: Bool) : Int { - if value { - 1 - } else { - 0 - } -} -"; - -/// The ``BOOL_AS_BIGINT`` function is used to implement the cast expr in QASM3 for bool to big int. -const BOOL_AS_BIGINT: &str = " -function __BoolAsBigInt__(value: Bool) : BigInt { - if value { - 1L - } else { - 0L - } -} -"; - -/// The ``BOOL_AS_DOUBLE`` function is used to implement the cast expr in QASM3 for bool to int. -const BOOL_AS_DOUBLE: &str = " -function __BoolAsDouble__(value: Bool) : Double { - if value { - 1. - } else { - 0. - } -} -"; - -/// The ``RESULT_AS_BOOL`` function is used to implement the cast expr in QASM3 for bit to bool. -/// This already exists in the Q# library, but is defined as a marker for casts from QASM3. -const RESULT_AS_BOOL: &str = " -function __ResultAsBool__(input: Result) : Bool { - Microsoft.Quantum.Convert.ResultAsBool(input) -} -"; - -/// The ``RESULT_AS_INT`` function is used to implement the cast expr in QASM3 for bit to bool. -const RESULT_AS_INT: &str = " -function __ResultAsInt__(input: Result) : Int { - if Microsoft.Quantum.Convert.ResultAsBool(input) { - 1 - } else { - 0 - } -} -"; - -/// The ``RESULT_AS_BIGINT`` function is used to implement the cast expr in QASM3 for bit to bool. -const RESULT_AS_BIGINT: &str = " -function __ResultAsBigInt__(input: Result) : BigInt { - if Microsoft.Quantum.Convert.ResultAsBool(input) { - 1L - } else { - 0L - } -} -"; - -/// The ``INT_AS_RESULT_ARRAY_BE`` function is used to implement the cast expr in QASM3 for int to bit[]. -/// with big-endian order. This is needed for round-trip conversion for bin ops. -const INT_AS_RESULT_ARRAY_BE: &str = " -function __IntAsResultArrayBE__(number : Int, bits : Int) : Result[] { - mutable runningValue = number; - mutable result = []; - for _ in 1..bits { - set result += [__BoolAsResult__((runningValue &&& 1) != 0)]; - set runningValue >>>= 1; - } - Microsoft.Quantum.Arrays.Reversed(result) -} -"; - -/// The ``RESULT_ARRAY_AS_INT_BE`` function is used to implement the cast expr in QASM3 for bit[] to uint. -/// with big-endian order. This is needed for round-trip conversion for bin ops. -const RESULT_ARRAY_AS_INT_BE: &str = " -function __ResultArrayAsIntBE__(results : Result[]) : Int { - Microsoft.Quantum.Convert.ResultArrayAsInt(Microsoft.Quantum.Arrays.Reversed(results)) -} -"; - -/// Runtime functions that are used in the generated AST. -/// Once compilation is complete, we can use this to determine -/// which runtime functions need to be included in the AST. -#[derive(Clone, Debug, PartialOrd, Ord, PartialEq, Eq, Copy)] -pub struct RuntimeFunctions(u16); - -bitflags! { - impl RuntimeFunctions: u16 { - const Pow = 0b1; - const Barrier = 0b10; - const BoolAsResult = 0b100; - const BoolAsInt = 0b1000; - const BoolAsBigInt = 0b1_0000; - const BoolAsDouble = 0b10_0000; - const ResultAsBool = 0b100_0000; - const ResultAsInt = 0b1000_0000; - const ResultAsBigInt = 0b1_0000_0000; - /// IntAsResultArray requires BoolAsResult to be included. - const IntAsResultArrayBE = 0b10_0000_0000 | 0b100; - const ResultArrayAsIntBE = 0b100_0000_0000; - const GATES = 0b1000_0000_0000; - } -} - -impl Default for RuntimeFunctions { - fn default() -> Self { - RuntimeFunctions::empty() - } -} - -pub(crate) fn get_pow_decl() -> Stmt { - parse_stmt(POW) -} - -pub(crate) fn get_barrier_decl() -> Stmt { - parse_stmt(BARRIER) -} - -pub(crate) fn get_bool_as_result_decl() -> Stmt { - parse_stmt(BOOL_AS_RESULT) -} - -pub(crate) fn get_bool_as_int_decl() -> Stmt { - parse_stmt(BOOL_AS_INT) -} - -pub(crate) fn get_bool_as_bigint_decl() -> Stmt { - parse_stmt(BOOL_AS_BIGINT) -} - -pub(crate) fn get_bool_as_double_decl() -> Stmt { - parse_stmt(BOOL_AS_DOUBLE) -} - -pub(crate) fn get_int_as_result_array_be_decl() -> Stmt { - parse_stmt(INT_AS_RESULT_ARRAY_BE) -} - -pub(crate) fn get_result_as_bool_decl() -> Stmt { - parse_stmt(RESULT_AS_BOOL) -} - -pub(crate) fn get_result_as_bigint_decl() -> Stmt { - parse_stmt(RESULT_AS_BIGINT) -} - -pub(crate) fn get_result_as_int_decl() -> Stmt { - parse_stmt(RESULT_AS_INT) -} - -pub(crate) fn get_result_array_as_int_be_decl() -> Stmt { - parse_stmt(RESULT_ARRAY_AS_INT_BE) -} - -/// As we are trying to add statements to the AST, we parse the Q# implementations -/// of the runtime functions and return the AST nodes. This saves us a lot of time -/// in writing the AST nodes manually. -fn parse_stmt(name: &str) -> Stmt { - let (nodes, errors) = qsc_parse::top_level_nodes(name, LanguageFeatures::default()); - assert!(errors.is_empty(), "Failed to parse: {errors:?}"); - assert!( - nodes.len() == 1, - "Expected one top-level node, found {:?}", - nodes.len() - ); - match nodes.into_iter().next().expect("no top-level nodes found") { - TopLevelNode::Namespace(..) => { - panic!("Expected stmt, got Namespace") - } - TopLevelNode::Stmt(stmt) => *stmt, - } -} - -fn parse_stmts(name: &str) -> Vec { - let (nodes, errors) = qsc_parse::top_level_nodes(name, LanguageFeatures::default()); - assert!(errors.is_empty(), "Failed to parse: {errors:?}"); - let mut stmts = vec![]; - for stmt in nodes { - match stmt { - TopLevelNode::Namespace(..) => { - panic!("Expected stmt, got Namespace") - } - TopLevelNode::Stmt(stmt) => stmts.push(*stmt), - } - } - - stmts -} - -/// Get the runtime function declarations for the given runtime functions. -pub(crate) fn get_runtime_function_decls(runtime: RuntimeFunctions) -> Vec { - let mut stmts = vec![]; - if runtime.contains(RuntimeFunctions::Pow) { - let stmt = crate::runtime::get_pow_decl(); - stmts.push(stmt); - } - if runtime.contains(RuntimeFunctions::Barrier) { - let stmt = crate::runtime::get_barrier_decl(); - stmts.push(stmt); - } - if runtime.contains(RuntimeFunctions::BoolAsBigInt) { - let stmt = crate::runtime::get_bool_as_bigint_decl(); - stmts.push(stmt); - } - if runtime.contains(RuntimeFunctions::BoolAsDouble) { - let stmt = crate::runtime::get_bool_as_double_decl(); - stmts.push(stmt); - } - if runtime.contains(RuntimeFunctions::BoolAsInt) { - let stmt = crate::runtime::get_bool_as_int_decl(); - stmts.push(stmt); - } - if runtime.contains(RuntimeFunctions::BoolAsResult) { - let stmt = crate::runtime::get_bool_as_result_decl(); - stmts.push(stmt); - } - if runtime.contains(RuntimeFunctions::IntAsResultArrayBE) { - let stmt = crate::runtime::get_int_as_result_array_be_decl(); - stmts.push(stmt); - } - if runtime.contains(RuntimeFunctions::ResultAsBool) { - let stmt = crate::runtime::get_result_as_bool_decl(); - stmts.push(stmt); - } - if runtime.contains(RuntimeFunctions::ResultAsBigInt) { - let stmt = crate::runtime::get_result_as_bigint_decl(); - stmts.push(stmt); - } - if runtime.contains(RuntimeFunctions::ResultAsInt) { - let stmt = crate::runtime::get_result_as_int_decl(); - stmts.push(stmt); - } - if runtime.contains(RuntimeFunctions::ResultArrayAsIntBE) { - let stmt = crate::runtime::get_result_array_as_int_be_decl(); - stmts.push(stmt); - } - stmts -} From 7f70408ab66fdeba7ef93c5a165c0537ab7fc919 Mon Sep 17 00:00:00 2001 From: orpuente-MS <156957451+orpuente-MS@users.noreply.github.com> Date: Wed, 16 Apr 2025 15:08:55 -0700 Subject: [PATCH 102/108] Remove dead code (#2301) --- compiler/qsc/src/codegen.rs | 1 - compiler/qsc_codegen/src/qsharp.rs | 16 - compiler/qsc_qasm3/src/ast_builder.rs | 241 +---------- compiler/qsc_qasm3/src/compiler.rs | 32 +- .../src/{parser/ast => }/display_utils.rs | 30 +- compiler/qsc_qasm3/src/io.rs | 2 +- compiler/qsc_qasm3/src/lib.rs | 14 +- compiler/qsc_qasm3/src/parse/tests.rs | 58 --- compiler/qsc_qasm3/src/parser/ast.rs | 4 +- compiler/qsc_qasm3/src/parser/completion.rs | 12 - .../qsc_qasm3/src/parser/completion/tests.rs | 6 - compiler/qsc_qasm3/src/parser/expr.rs | 6 +- compiler/qsc_qasm3/src/parser/prim.rs | 156 +------ compiler/qsc_qasm3/src/parser/prim/tests.rs | 89 +++- compiler/qsc_qasm3/src/parser/scan.rs | 4 +- compiler/qsc_qasm3/src/parser/tests.rs | 11 - compiler/qsc_qasm3/src/semantic.rs | 1 + compiler/qsc_qasm3/src/semantic/ast.rs | 397 +----------------- .../src/semantic/{ast => }/const_eval.rs | 5 +- compiler/qsc_qasm3/src/semantic/lowerer.rs | 2 +- compiler/qsc_qasm3/src/semantic/symbols.rs | 2 +- compiler/qsc_qasm3/src/semantic/tests.rs | 55 +-- .../tests/expression/binary/complex.rs | 38 +- compiler/qsc_qasm3/src/stdlib/angle.rs | 2 +- compiler/qsc_qasm3/src/stdlib/compile.rs | 2 - compiler/qsc_qasm3/src/tests.rs | 13 +- compiler/qsc_qasm3/src/types.rs | 12 - 27 files changed, 151 insertions(+), 1060 deletions(-) rename compiler/qsc_qasm3/src/{parser/ast => }/display_utils.rs (85%) delete mode 100644 compiler/qsc_qasm3/src/parse/tests.rs rename compiler/qsc_qasm3/src/semantic/{ast => }/const_eval.rs (99%) diff --git a/compiler/qsc/src/codegen.rs b/compiler/qsc/src/codegen.rs index 3b284f9e86..54ad8388c7 100644 --- a/compiler/qsc/src/codegen.rs +++ b/compiler/qsc/src/codegen.rs @@ -5,7 +5,6 @@ mod tests; pub mod qsharp { - pub use qsc_codegen::qsharp::write_item_string; pub use qsc_codegen::qsharp::write_package_string; pub use qsc_codegen::qsharp::write_stmt_string; } diff --git a/compiler/qsc_codegen/src/qsharp.rs b/compiler/qsc_codegen/src/qsharp.rs index c845e53227..63636c1d61 100644 --- a/compiler/qsc_codegen/src/qsharp.rs +++ b/compiler/qsc_codegen/src/qsharp.rs @@ -61,22 +61,6 @@ pub fn write_package_string(package: &Package) -> String { format_str(&s) } -#[must_use] -pub fn write_item_string(item: &Item) -> String { - let mut output = Vec::new(); - let mut gen = QSharpGen::new(&mut output); - - gen.visit_item(item); - - let s = match std::str::from_utf8(&output) { - Ok(v) => v.to_owned(), - Err(e) => format!("Invalid UTF-8 sequence: {e}"), - }; - - output.clear(); - format_str(&s) -} - #[must_use] pub fn write_stmt_string(stmt: &ast::Stmt) -> String { let mut output = Vec::new(); diff --git a/compiler/qsc_qasm3/src/ast_builder.rs b/compiler/qsc_qasm3/src/ast_builder.rs index 5a1c9de1ee..f2885456a0 100644 --- a/compiler/qsc_qasm3/src/ast_builder.rs +++ b/compiler/qsc_qasm3/src/ast_builder.rs @@ -248,6 +248,8 @@ pub(crate) fn build_expr_array_expr(values: Vec, span: Span) } } +// This will be used to compile arrays in the near future. +#[allow(dead_code)] pub(crate) fn build_default_result_array_expr(len: usize, span: Span) -> Expr { let exprs: Vec<_> = (0..len) .map(|_| Box::new(build_lit_result_expr(ast::Result::Zero, Span::default()))) @@ -363,35 +365,6 @@ pub(crate) fn build_binary_expr( } } -pub(crate) fn is_complex_binop_supported(op: qsc_ast::ast::BinOp) -> bool { - matches!( - op, - ast::BinOp::Add | ast::BinOp::Sub | ast::BinOp::Mul | ast::BinOp::Div | ast::BinOp::Exp - ) -} - -pub(crate) fn build_complex_binary_expr( - is_assignment: bool, - qsop: ast::BinOp, - lhs: ast::Expr, - rhs: ast::Expr, - span: Span, -) -> ast::Expr { - let name = match qsop { - ast::BinOp::Add => "PlusC", - ast::BinOp::Sub => "MinusC", - ast::BinOp::Mul => "TimesC", - ast::BinOp::Div => "DividedByC", - ast::BinOp::Exp => "PowC", - _ => unreachable!("Unsupported complex binary operation"), - }; - - if is_assignment { - unreachable!("Unsupported complex binary operation"); - } - build_math_call_from_exprs(name, vec![lhs, rhs], span) -} - pub(crate) fn build_math_call_from_exprs(name: &str, exprs: Vec, span: Span) -> Expr { let alloc_ident = Ident { name: Rc::from(name), @@ -942,14 +915,6 @@ pub(crate) fn build_wrapped_block_expr(block: Block) -> Expr { } } -pub(crate) fn build_stmt_wrapped_block_expr(stmt: Stmt) -> Block { - Block { - id: NodeId::default(), - span: stmt.span, - stmts: Box::new([Box::new(stmt)]), - } -} - pub(crate) fn build_expr_wrapped_block_expr(expr: Expr) -> Block { Block { id: NodeId::default(), @@ -1185,14 +1150,6 @@ pub(crate) fn build_top_level_ns_with_items>( }) } -pub(crate) fn build_top_level_ns_with_item>( - whole_span: Span, - ns: S, - entry: ast::Item, -) -> TopLevelNode { - build_top_level_ns_with_items(whole_span, ns, vec![entry]) -} - pub(crate) fn build_operation_with_stmts>( name: S, input_pats: Vec, @@ -1459,200 +1416,6 @@ pub(crate) fn build_barrier_call(span: Span) -> Stmt { build_stmt_semi_from_expr(expr) } -pub(crate) fn build_gate_decl( - name: String, - cargs: Vec<(String, Ty, Pat)>, - qargs: Vec<(String, Ty, Pat)>, - body: Option, - name_span: Span, - body_span: Span, - gate_span: Span, -) -> Stmt { - let args = cargs - .into_iter() - .chain(qargs) - .map(|(_, _, pat)| Box::new(pat)) - .collect::>(); - - let lo = args - .iter() - .min_by_key(|x| x.span.lo) - .map(|x| x.span.lo) - .unwrap_or_default(); - - let hi = args - .iter() - .max_by_key(|x| x.span.hi) - .map(|x| x.span.hi) - .unwrap_or_default(); - - let input_pat_kind = if args.len() > 1 { - PatKind::Tuple(args.into_boxed_slice()) - } else { - PatKind::Paren(args[0].clone()) - }; - - let input_pat = Pat { - kind: Box::new(input_pat_kind), - span: Span { lo, hi }, - ..Default::default() - }; - let body = CallableBody::Block(Box::new(body.unwrap_or_else(|| Block { - id: NodeId::default(), - span: body_span, - stmts: Box::new([]), - }))); - let decl = CallableDecl { - id: NodeId::default(), - span: name_span, - kind: CallableKind::Operation, - name: Box::new(Ident { - name: name.into(), - ..Default::default() - }), - generics: Box::new([]), - input: Box::new(input_pat), - output: Box::new(build_path_ident_ty("Unit")), - functors: None, - body: Box::new(body), - }; - let item = Item { - span: gate_span, - kind: Box::new(ast::ItemKind::Callable(Box::new(decl))), - ..Default::default() - }; - - Stmt { - kind: Box::new(StmtKind::Item(Box::new(item))), - span: gate_span, - ..Default::default() - } -} - -#[allow(clippy::too_many_arguments, clippy::too_many_lines)] -pub(crate) fn build_lambda>( - name: S, - cargs: Vec<(String, Ty, Pat)>, - qargs: Vec<(String, Ty, Pat)>, - body: Option, - name_span: Span, - body_span: Span, - gate_span: Span, - return_type: Option, - kind: CallableKind, -) -> Stmt { - let args = cargs - .into_iter() - .chain(qargs) - .map(|(name, ty, pat)| (name, ty, pat.span)) - .collect::>(); - - let lo = args - .iter() - .min_by_key(|(_, _, span)| span.lo) - .map(|(_, _, span)| span.lo) - .unwrap_or_default(); - - let hi = args - .iter() - .max_by_key(|(_, _, span)| span.hi) - .map(|(_, _, span)| span.hi) - .unwrap_or_default(); - - let name_args = args - .iter() - .map(|(name, _, span)| Pat { - kind: Box::new(PatKind::Bind( - Box::new(Ident { - span: *span, - name: Rc::from(name.as_ref()), - ..Default::default() - }), - None, - )), - ..Default::default() - }) - .map(Box::new) - .collect::>(); - let input_pat = if args.len() == 1 { - ast::Pat { - kind: Box::new(ast::PatKind::Paren(name_args[0].clone())), - span: Span { lo, hi }, - ..Default::default() - } - } else { - ast::Pat { - kind: Box::new(PatKind::Tuple(name_args.into_boxed_slice())), - span: Span { lo, hi }, - ..Default::default() - } - }; - - let block_expr = build_wrapped_block_expr(body.map_or_else( - || Block { - id: NodeId::default(), - span: body_span, - stmts: Box::new([]), - }, - |block| block, - )); - let lambda_expr = Expr { - id: NodeId::default(), - kind: Box::new(ExprKind::Lambda( - kind, - Box::new(input_pat), - Box::new(block_expr), - )), - span: gate_span, - }; - let ty_args = args.iter().map(|(_, ty, _)| ty.clone()).collect::>(); - let input_ty = if args.len() == 1 { - ast::Ty { - kind: Box::new(ast::TyKind::Paren(Box::new(ty_args[0].clone()))), - ..Default::default() - } - } else { - ast::Ty { - kind: Box::new(ast::TyKind::Tuple(ty_args.into_boxed_slice())), - ..Default::default() - } - }; - let return_type = if let Some(ty) = return_type { - ty - } else { - build_path_ident_ty("Unit") - }; - - let lambda_ty = ast::Ty { - kind: Box::new(ast::TyKind::Arrow( - kind, - Box::new(input_ty), - Box::new(return_type), - None, - )), - ..Default::default() - }; - Stmt { - span: gate_span, - kind: Box::new(StmtKind::Local( - Mutability::Immutable, - Box::new(Pat { - kind: Box::new(PatKind::Bind( - Box::new(Ident { - span: name_span, - name: Rc::from(name.as_ref()), - ..Default::default() - }), - Some(Box::new(lambda_ty)), - )), - ..Default::default() - }), - Box::new(lambda_expr), - )), - ..Default::default() - } -} - #[allow(clippy::too_many_arguments, clippy::too_many_lines)] pub(crate) fn build_function_or_operation( name: String, diff --git a/compiler/qsc_qasm3/src/compiler.rs b/compiler/qsc_qasm3/src/compiler.rs index fce699a955..cb3dfd13c0 100644 --- a/compiler/qsc_qasm3/src/compiler.rs +++ b/compiler/qsc_qasm3/src/compiler.rs @@ -22,17 +22,18 @@ use crate::{ build_gate_call_with_params_and_callee, build_global_call_with_two_params, build_if_expr_then_block, build_if_expr_then_block_else_block, build_if_expr_then_block_else_expr, build_if_expr_then_expr_else_expr, - build_implicit_return_stmt, build_indexed_assignment_statement, build_lit_angle_expr, - build_lit_bigint_expr, build_lit_bool_expr, build_lit_complex_expr, build_lit_double_expr, - build_lit_int_expr, build_lit_result_array_expr_from_bitstring, build_lit_result_expr, - build_managed_qubit_alloc, build_math_call_from_exprs, build_math_call_no_params, - build_measure_call, build_operation_with_stmts, build_path_ident_expr, build_path_ident_ty, - build_qasm_import_decl, build_qasm_import_items, build_range_expr, build_reset_call, - build_return_expr, build_return_unit, build_stmt_semi_from_expr, - build_stmt_semi_from_expr_with_span, build_top_level_ns_with_items, build_tuple_expr, - build_unary_op_expr, build_unmanaged_qubit_alloc, build_unmanaged_qubit_alloc_array, - build_while_stmt, build_wrapped_block_expr, managed_qubit_alloc_array, - map_qsharp_type_to_ast_ty, wrap_expr_in_parens, + build_implicit_return_stmt, build_index_expr, build_indexed_assignment_statement, + build_lit_angle_expr, build_lit_bigint_expr, build_lit_bool_expr, build_lit_complex_expr, + build_lit_double_expr, build_lit_int_expr, build_lit_result_array_expr_from_bitstring, + build_lit_result_expr, build_managed_qubit_alloc, build_math_call_from_exprs, + build_math_call_no_params, build_measure_call, build_operation_with_stmts, + build_path_ident_expr, build_path_ident_ty, build_qasm_import_decl, + build_qasm_import_items, build_range_expr, build_reset_call, build_return_expr, + build_return_unit, build_stmt_semi_from_expr, build_stmt_semi_from_expr_with_span, + build_top_level_ns_with_items, build_tuple_expr, build_unary_op_expr, + build_unmanaged_qubit_alloc, build_unmanaged_qubit_alloc_array, build_while_stmt, + build_wrapped_block_expr, managed_qubit_alloc_array, map_qsharp_type_to_ast_ty, + wrap_expr_in_parens, }, io::SourceResolver, parser::ast::{list_from_iter, List}, @@ -1221,14 +1222,7 @@ impl QasmCompiler { let ident = build_path_ident_expr(&symbol.name, indexed_ident.name_span, indexed_ident.span); - qsast::Expr { - id: qsast::NodeId::default(), - span, - kind: Box::new(qsast::ExprKind::Index( - Box::new(ident), - Box::new(index[0].clone()), - )), - } + build_index_expr(ident, index[0].clone(), span) } fn compile_unary_op_expr(&mut self, unary: &UnaryOpExpr) -> qsast::Expr { diff --git a/compiler/qsc_qasm3/src/parser/ast/display_utils.rs b/compiler/qsc_qasm3/src/display_utils.rs similarity index 85% rename from compiler/qsc_qasm3/src/parser/ast/display_utils.rs rename to compiler/qsc_qasm3/src/display_utils.rs index 4ffbb29042..0e3177cbb8 100644 --- a/compiler/qsc_qasm3/src/parser/ast/display_utils.rs +++ b/compiler/qsc_qasm3/src/display_utils.rs @@ -48,7 +48,7 @@ where /// Writes a list of elements to the given buffer or stream /// with an additional indentation level. -pub(crate) fn write_indented_list<'write, 'itemref, 'item, T, I>( +pub fn write_indented_list<'write, 'itemref, 'item, T, I>( f: &'write mut impl Write, vals: I, ) -> fmt::Result @@ -70,23 +70,19 @@ where } /// Writes the name and span of a structure to the given buffer or stream. -pub(crate) fn write_header(f: &mut impl Write, name: &str, span: super::Span) -> fmt::Result { +pub fn write_header(f: &mut impl Write, name: &str, span: super::Span) -> fmt::Result { write!(f, "{name} {span}:") } /// Writes the name and span of a structure to the given buffer or stream. /// Inserts a newline afterwards. -pub(crate) fn writeln_header(f: &mut impl Write, name: &str, span: super::Span) -> fmt::Result { +pub fn writeln_header(f: &mut impl Write, name: &str, span: super::Span) -> fmt::Result { writeln!(f, "{name} {span}:") } /// Writes a field of a structure to the given buffer /// or stream with an additional indententation level. -pub(crate) fn write_field( - f: &mut impl Write, - field_name: &str, - val: &T, -) -> fmt::Result { +pub fn write_field(f: &mut impl Write, field_name: &str, val: &T) -> fmt::Result { let mut indent = with_indentation(f); write!(indent, "{field_name}: {val}") } @@ -94,18 +90,14 @@ pub(crate) fn write_field( /// Writes a field of a structure to the given buffer /// or stream with an additional indententation level. /// Inserts a newline afterwards. -pub(crate) fn writeln_field( - f: &mut impl Write, - field_name: &str, - val: &T, -) -> fmt::Result { +pub fn writeln_field(f: &mut impl Write, field_name: &str, val: &T) -> fmt::Result { write_field(f, field_name, val)?; writeln!(f) } /// Writes an optional field of a structure to the given buffer /// or stream with an additional indententation level. -pub(crate) fn write_opt_field( +pub fn write_opt_field( f: &mut impl Write, field_name: &str, opt_val: Option<&T>, @@ -120,7 +112,7 @@ pub(crate) fn write_opt_field( /// Writes an optional field of a structure to the given buffer /// or stream with an additional indententation level. /// Inserts a newline afterwards. -pub(crate) fn writeln_opt_field( +pub fn writeln_opt_field( f: &mut impl Write, field_name: &str, opt_val: Option<&T>, @@ -132,7 +124,7 @@ pub(crate) fn writeln_opt_field( /// Writes a field of a structure to the given buffer /// or stream with an additional indententation level. /// The field must be an iterable. -pub(crate) fn write_list_field<'write, 'itemref, 'item, T, I>( +pub fn write_list_field<'write, 'itemref, 'item, T, I>( f: &mut impl Write, field_name: &str, vals: I, @@ -152,7 +144,7 @@ where /// or stream with an additional indententation level. /// The field must be an iterable. /// Inserts a newline afterwards. -pub(crate) fn writeln_list_field<'write, 'itemref, 'item, T, I>( +pub fn writeln_list_field<'write, 'itemref, 'item, T, I>( f: &mut impl Write, field_name: &str, vals: I, @@ -169,7 +161,7 @@ where /// Writes an optional field of a structure to the given buffer /// or stream with an additional indententation level. /// The field must be an iterable. -pub(crate) fn write_opt_list_field<'write, 'itemref, 'item, T, I>( +pub fn write_opt_list_field<'write, 'itemref, 'item, T, I>( f: &mut impl Write, field_name: &str, opt_vals: Option, @@ -191,7 +183,7 @@ where /// or stream with an additional indententation level. /// The field must be an iterable. /// Inserts a newline afterwards. -pub(crate) fn writeln_opt_list_field<'write, 'itemref, 'item, T, I>( +pub fn writeln_opt_list_field<'write, 'itemref, 'item, T, I>( f: &mut impl Write, field_name: &str, opt_vals: Option, diff --git a/compiler/qsc_qasm3/src/io.rs b/compiler/qsc_qasm3/src/io.rs index fa88bfe55b..d4fe223fc7 100644 --- a/compiler/qsc_qasm3/src/io.rs +++ b/compiler/qsc_qasm3/src/io.rs @@ -44,7 +44,7 @@ pub trait SourceResolver { } } #[cfg(not(feature = "fs"))] - fn resolve

    (&self, path: P) -> miette::Result<(PathBuf, String)> + fn resolve

    (&mut self, path: P) -> miette::Result<(PathBuf, String), Error> where P: AsRef; } diff --git a/compiler/qsc_qasm3/src/lib.rs b/compiler/qsc_qasm3/src/lib.rs index 725b26b0e4..de20197d4d 100644 --- a/compiler/qsc_qasm3/src/lib.rs +++ b/compiler/qsc_qasm3/src/lib.rs @@ -1,15 +1,13 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. -// while we work through the conversion, allow dead code to avoid warnings -#![allow(dead_code)] - mod ast_builder; mod compiler; mod stdlib; pub use compiler::compile_to_qsharp_ast_with_config; pub use stdlib::package_store_with_qasm; mod convert; +pub mod display_utils; pub mod io; mod keyword; mod lex; @@ -36,7 +34,7 @@ pub struct Error(pub ErrorKind); impl Error { #[must_use] pub fn is_syntax_error(&self) -> bool { - matches!(self.0, ErrorKind::Parse(_, _)) + matches!(self.0, ErrorKind::Parser(..)) } #[must_use] @@ -61,8 +59,6 @@ pub enum ErrorKind { #[error(transparent)] #[diagnostic(transparent)] IO(#[from] crate::io::Error), - #[error("QASM3 Parse Error: {0}")] - Parse(String, #[label] Span), #[error(transparent)] #[diagnostic(transparent)] Parser(#[from] crate::parser::Error), @@ -71,11 +67,7 @@ pub enum ErrorKind { Semantic(#[from] crate::semantic::Error), #[error(transparent)] #[diagnostic(transparent)] - ConstEval(#[from] crate::semantic::ast::const_eval::ConstEvalError), - #[error("QASM3 Parse Error: Not Found {0}")] - NotFound(String), - #[error("IO Error: {0}")] - OldIO(String), + ConstEval(#[from] crate::semantic::const_eval::ConstEvalError), } /// Qubit semantics differ between Q# and Qiskit. This enum is used to diff --git a/compiler/qsc_qasm3/src/parse/tests.rs b/compiler/qsc_qasm3/src/parse/tests.rs deleted file mode 100644 index 7b22082e6e..0000000000 --- a/compiler/qsc_qasm3/src/parse/tests.rs +++ /dev/null @@ -1,58 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. - -use crate::tests::{parse, parse_all}; -use miette::Report; - -#[test] -fn simple_programs_can_be_parsed() -> miette::Result<(), Vec> { - let source = r#"OPENQASM 3.0; - include "stdgates.inc"; - qubit q; - "#; - let _ = parse(source)?; - Ok(()) -} - -#[test] -fn programs_with_includes_can_be_parsed() -> miette::Result<(), Vec> { - let source0 = r#"OPENQASM 3.0; - include "stdgates.inc"; - include "source1.qasm"; - qubit q1; - "#; - let source1 = "qubit q2; - "; - let all_sources = [ - ("source0.qasm".into(), source0.into()), - ("source1.qasm".into(), source1.into()), - ]; - - let res = parse_all("source0.qasm", all_sources)?; - assert!(res.source.includes().len() == 1); - Ok(()) -} - -#[test] -fn programs_with_includes_with_includes_can_be_parsed() -> miette::Result<(), Vec> { - let source0 = r#"OPENQASM 3.0; - include "stdgates.inc"; - include "source1.qasm"; - qubit q1; - "#; - let source1 = r#"include "source2.qasm"; - qubit q2; - "#; - let source2 = "qubit q3; - "; - let all_sources = [ - ("source0.qasm".into(), source0.into()), - ("source1.qasm".into(), source1.into()), - ("source2.qasm".into(), source2.into()), - ]; - - let res = parse_all("source0.qasm", all_sources)?; - assert!(res.source.includes().len() == 1); - assert!(res.source.includes()[0].includes().len() == 1); - Ok(()) -} diff --git a/compiler/qsc_qasm3/src/parser/ast.rs b/compiler/qsc_qasm3/src/parser/ast.rs index 34d975947d..2cbf6847e8 100644 --- a/compiler/qsc_qasm3/src/parser/ast.rs +++ b/compiler/qsc_qasm3/src/parser/ast.rs @@ -1,9 +1,7 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. -pub(crate) mod display_utils; - -use display_utils::{ +use crate::display_utils::{ write_field, write_header, write_indented_list, write_list_field, write_opt_field, write_opt_list_field, writeln_field, writeln_header, writeln_list_field, writeln_opt_field, }; diff --git a/compiler/qsc_qasm3/src/parser/completion.rs b/compiler/qsc_qasm3/src/parser/completion.rs index 118b24436b..2166c0a77d 100644 --- a/compiler/qsc_qasm3/src/parser/completion.rs +++ b/compiler/qsc_qasm3/src/parser/completion.rs @@ -23,15 +23,3 @@ pub fn possible_words_at_offset_in_source(input: &str, at_offset: u32) -> WordKi let _ = prgm::parse(&mut scanner); collector.into_words() } - -/// Returns the words that would be valid syntax at a particular offset -/// in the given notebook cell (using the fragments parser). -/// -/// This is useful for providing completions in an editor. -#[must_use] -pub fn possible_words_at_offset_in_fragments(input: &str, at_offset: u32) -> WordKinds { - let mut collector = ValidWordCollector::new(at_offset); - let mut scanner = ParserContext::with_word_collector(input, &mut collector); - let _ = prgm::parse(&mut scanner); - collector.into_words() -} diff --git a/compiler/qsc_qasm3/src/parser/completion/tests.rs b/compiler/qsc_qasm3/src/parser/completion/tests.rs index 13d3ebe40b..f9693b7b09 100644 --- a/compiler/qsc_qasm3/src/parser/completion/tests.rs +++ b/compiler/qsc_qasm3/src/parser/completion/tests.rs @@ -24,12 +24,6 @@ fn check_valid_words(input: &str, expect: &expect_test::Expect) { expect.assert_debug_eq(&w); } -fn check_valid_words_no_source_name(input: &str, expect: &expect_test::Expect) { - let (input, cursor) = get_source_and_cursor(input); - let w = possible_words_at_offset_in_source(&input, cursor); - expect.assert_debug_eq(&w); -} - #[test] fn begin_document() { check_valid_words( diff --git a/compiler/qsc_qasm3/src/parser/expr.rs b/compiler/qsc_qasm3/src/parser/expr.rs index 6c06d521ac..ba5e004027 100644 --- a/compiler/qsc_qasm3/src/parser/expr.rs +++ b/compiler/qsc_qasm3/src/parser/expr.rs @@ -54,7 +54,7 @@ enum OpKind { #[derive(Clone, Copy)] enum OpName { Token(TokenKind), - Keyword(Keyword), + Keyword, } #[derive(Clone, Copy)] @@ -63,8 +63,6 @@ enum Assoc { Right, } -const RANGE_PRECEDENCE: u8 = 1; - pub(super) fn expr(s: &mut ParserContext) -> Result { expr_op(s, 0) } @@ -534,7 +532,7 @@ pub(crate) fn set_expr(s: &mut ParserContext) -> Result { fn op_name(s: &ParserContext) -> OpName { match s.peek().kind { - TokenKind::Keyword(keyword) => OpName::Keyword(keyword), + TokenKind::Keyword(_) => OpName::Keyword, kind => OpName::Token(kind), } } diff --git a/compiler/qsc_qasm3/src/parser/prim.rs b/compiler/qsc_qasm3/src/parser/prim.rs index b5ffce2b2c..f882c47785 100644 --- a/compiler/qsc_qasm3/src/parser/prim.rs +++ b/compiler/qsc_qasm3/src/parser/prim.rs @@ -4,14 +4,13 @@ #[cfg(test)] pub(crate) mod tests; +use super::ast::Ident; use super::{ error::{Error, ErrorKind}, scan::ParserContext, Parser, Result, }; -use crate::{lex::TokenKind, parser::completion::word_kinds::WordKinds}; - -use super::ast::{Ident, IncompletePath, Path, PathKind}; +use crate::lex::TokenKind; use qsc_data_structures::span::{Span, WithSpan}; @@ -21,21 +20,6 @@ pub(super) enum FinalSep { Missing, } -impl FinalSep { - pub(super) fn reify( - self, - mut xs: Vec, - mut as_paren: impl FnMut(T) -> U, - mut as_seq: impl FnMut(Box<[T]>) -> U, - ) -> U { - if self == Self::Missing && xs.len() == 1 { - as_paren(xs.pop().expect("vector should have exactly one item")) - } else { - as_seq(xs.into_boxed_slice()) - } - } -} - pub(super) fn token(s: &mut ParserContext, t: TokenKind) -> Result<()> { if let TokenKind::Keyword(k) = t { s.expect(k.into()); @@ -71,78 +55,6 @@ pub(super) fn ident(s: &mut ParserContext) -> Result { } } -/// A `path` is a dot-separated list of idents like "Foo.Bar.Baz" -/// this can be a namespace name (in an open statement or namespace declaration), -/// a reference to an item, like `Microsoft.Quantum.Diagnostics.DumpMachine`, -/// or a field access. -/// -/// Path parser. If parsing fails, also returns any valid segments -/// that were parsed up to the final `.` token. -pub(super) fn path( - s: &mut ParserContext, - kind: WordKinds, -) -> std::result::Result, (Error, Option>)> { - s.expect(kind); - - let lo = s.peek().span.lo; - let i = ident(s).map_err(|e| (e, None))?; - - let mut parts = vec![i]; - while token(s, TokenKind::Dot).is_ok() { - s.expect(WordKinds::PathSegment); - match ident(s) { - Ok(ident) => parts.push(ident), - Err(error) => { - let trivia_span = s.skip_trivia(); - let keyword = trivia_span.hi == trivia_span.lo - && matches!(s.peek().kind, TokenKind::Keyword(_)); - if keyword { - // Consume any keyword that comes immediately after the final - // dot, assuming it was intended to be part of the path. - s.advance(); - } - - return Err(( - error, - Some(Box::new(IncompletePath { - span: s.span(lo), - segments: parts.into(), - keyword, - })), - )); - } - } - } - - let name = parts.pop().expect("path should have at least one part"); - let namespace = if parts.is_empty() { - None - } else { - Some(parts.into()) - }; - - Ok(Box::new(Path { - span: s.span(lo), - segments: namespace, - name: name.into(), - })) -} - -/// Recovering [`Path`] parser. Parsing only fails if no segments -/// were successfully parsed. If any segments were successfully parsed, -/// returns a [`PathKind::Err`] containing the segments that were -/// successfully parsed up to the final `.` token. -pub(super) fn recovering_path(s: &mut ParserContext, kind: WordKinds) -> Result { - match path(s, kind) { - Ok(path) => Ok(PathKind::Ok(path)), - Err((error, Some(incomplete_path))) => { - s.push_error(error); - Ok(PathKind::Err(Some(incomplete_path))) - } - Err((error, None)) => Err(error), - } -} - /// Optionally parse with the given parser. /// Returns Ok(Some(value)) if the parser succeeded, /// Ok(None) if the parser failed on the first token, @@ -267,29 +179,6 @@ pub(super) fn seq_item( Ok((xs, final_sep)) } -/// Try to parse with the given parser. -/// -/// If the parser fails on the first token, returns the default value. -/// -/// If the parser fails after consuming some tokens, propagates the error. -pub(super) fn parse_or_else( - s: &mut ParserContext, - default: impl FnOnce(Span) -> T, - mut p: impl Parser, -) -> Result { - let lo = s.peek().span.lo; - match p(s) { - Ok(value) => Ok(value), - Err(error) if advanced(s, lo) => Err(error), - Err(error) => { - s.push_error(error); - // The whitespace will become part of the error span - s.skip_trivia(); - Ok(default(s.span(lo))) - } - } -} - /// Try to parse with the given parser. /// /// If the parser fails on the first token, propagates the error. @@ -315,39 +204,6 @@ pub(super) fn recovering( } } -/// Try to parse with the given parser. -/// -/// If the parser fails on the first token, returns the default value. -/// -/// If the parser fails after consuming some tokens, performs -/// recovery by advancing until the next token in `tokens` is found. -/// The recovery token is consumed. -/// -/// This behavior is a combination of [`recovering`] and [`parse_or_else`], -/// and provides the most aggressive error recovery. -pub(super) fn recovering_parse_or_else( - s: &mut ParserContext, - default: impl FnOnce(Span) -> T, - tokens: &[TokenKind], - mut p: impl Parser, -) -> T { - let lo = s.peek().span.lo; - match p(s) { - Ok(value) => value, - Err(error) => { - s.push_error(error); - - if advanced(s, lo) { - s.recover(tokens); - } else { - // The whitespace will become part of the error node span - s.skip_trivia(); - } - default(s.span(lo)) - } - } -} - pub(super) fn recovering_semi(s: &mut ParserContext) { if let Err(error) = token(s, TokenKind::Semicolon) { // no recovery, just move on to the next token @@ -380,11 +236,3 @@ pub(super) fn shorten(from_start: usize, from_end: usize, s: &str) -> &str { fn advanced(s: &ParserContext, from: u32) -> bool { s.peek().span.lo > from } - -fn map_rule_name(name: &'static str, error: Error) -> Error { - Error::new(match error.0 { - ErrorKind::Rule(_, found, span) => ErrorKind::Rule(name, found, span), - ErrorKind::Convert(_, found, span) => ErrorKind::Convert(name, found, span), - kind => kind, - }) -} diff --git a/compiler/qsc_qasm3/src/parser/prim/tests.rs b/compiler/qsc_qasm3/src/parser/prim/tests.rs index 055250057d..59d35df4f9 100644 --- a/compiler/qsc_qasm3/src/parser/prim/tests.rs +++ b/compiler/qsc_qasm3/src/parser/prim/tests.rs @@ -1,11 +1,11 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. -use super::{ident, opt, seq}; +use super::{ident, opt, seq, token}; use crate::{ keyword::Keyword, lex::TokenKind, - parser::ast::PathKind, + parser::ast::{IncompletePath, Path, PathKind}, parser::{ completion::word_kinds::WordKinds, error::{Error, ErrorKind}, @@ -17,8 +17,73 @@ use expect_test::expect; use qsc_data_structures::span::Span; -fn path(s: &mut ParserContext) -> Result { - super::recovering_path(s, WordKinds::empty()) +/// A `path` is a dot-separated list of idents like "Foo.Bar.Baz" +/// this can be a namespace name (in an open statement or namespace declaration), +/// a reference to an item, like `Microsoft.Quantum.Diagnostics.DumpMachine`, +/// or a field access. +/// +/// Path parser. If parsing fails, also returns any valid segments +/// that were parsed up to the final `.` token. +pub(super) fn path( + s: &mut ParserContext, +) -> std::result::Result, (Error, Option>)> { + let lo = s.peek().span.lo; + let i = ident(s).map_err(|e| (e, None))?; + + let mut parts = vec![i]; + while token(s, TokenKind::Dot).is_ok() { + s.expect(WordKinds::PathSegment); + match ident(s) { + Ok(ident) => parts.push(ident), + Err(error) => { + let trivia_span = s.skip_trivia(); + let keyword = trivia_span.hi == trivia_span.lo + && matches!(s.peek().kind, TokenKind::Keyword(_)); + if keyword { + // Consume any keyword that comes immediately after the final + // dot, assuming it was intended to be part of the path. + s.advance(); + } + + return Err(( + error, + Some(Box::new(IncompletePath { + span: s.span(lo), + segments: parts.into(), + keyword, + })), + )); + } + } + } + + let name = parts.pop().expect("path should have at least one part"); + let namespace = if parts.is_empty() { + None + } else { + Some(parts.into()) + }; + + Ok(Box::new(Path { + span: s.span(lo), + segments: namespace, + name: name.into(), + })) +} + +/// Recovering [`Path`] parser. Parsing only fails if no segments +/// were successfully parsed. If any segments were successfully parsed, +/// returns a [`PathKind::Err`] containing the segments that were +/// successfully parsed up to the final `.` token. +fn recovering_path(s: &mut ParserContext) -> Result { + match path(s) { + Ok(path) => Ok(PathKind::Ok(path)), + Err((error, Some(incomplete_path))) => { + s.push_error(error); + Ok(PathKind::Err(Some(incomplete_path))) + } + Err((error, None)) => Err(error), + } } #[test] @@ -88,7 +153,7 @@ fn ident_keyword() { #[test] fn path_single() { check( - path, + recovering_path, "Foo", &expect![[r#" Path [0-3]: @@ -100,7 +165,7 @@ fn path_single() { #[test] fn path_double() { check( - path, + recovering_path, "Foo.Bar", &expect![[r#" Path [0-7]: @@ -113,7 +178,7 @@ fn path_double() { #[test] fn path_triple() { check( - path, + recovering_path, "Foo.Bar.Baz", &expect![[r#" Path [0-11]: @@ -127,7 +192,7 @@ fn path_triple() { #[test] fn path_trailing_dot() { check( - path, + recovering_path, "Foo.Bar.", &expect![[r#" Err IncompletePath [0-8]: segments: @@ -152,7 +217,7 @@ fn path_trailing_dot() { #[test] fn path_followed_by_keyword() { check( - path, + recovering_path, "Foo.Bar.in", &expect![[r#" Err IncompletePath [0-10]: segments: @@ -179,7 +244,7 @@ fn path_followed_by_keyword() { #[test] fn opt_succeed() { check_opt( - |s| opt(s, path), + |s| opt(s, recovering_path), "Foo.Bar", &expect![[r#" Path [0-7]: @@ -191,13 +256,13 @@ fn opt_succeed() { #[test] fn opt_fail_no_consume() { - check_opt(|s| opt(s, path), "123", &expect!["None"]); + check_opt(|s| opt(s, recovering_path), "123", &expect!["None"]); } #[test] fn opt_fail_consume() { check_opt( - |s| opt(s, path), + |s| opt(s, recovering_path), "Foo.$", &expect![[r#" Err IncompletePath [0-5]: segments: diff --git a/compiler/qsc_qasm3/src/parser/scan.rs b/compiler/qsc_qasm3/src/parser/scan.rs index de008383c7..c390041398 100644 --- a/compiler/qsc_qasm3/src/parser/scan.rs +++ b/compiler/qsc_qasm3/src/parser/scan.rs @@ -40,8 +40,6 @@ impl<'a> ParserContext<'a> { } } - // while we work through the conversion, allow dead code to avoid warnings - #[allow(dead_code)] pub fn with_word_collector(input: &'a str, word_collector: &'a mut ValidWordCollector) -> Self { let mut scanner = Scanner::new(input); @@ -76,6 +74,7 @@ impl<'a> ParserContext<'a> { /// Moves the scanner to the start of the current token, /// returning the span of the skipped trivia. + #[cfg(test)] pub(super) fn skip_trivia(&mut self) -> Span { self.scanner.skip_trivia() } @@ -152,6 +151,7 @@ impl<'a> Scanner<'a> { /// Moves the scanner to the start of the current token, /// returning the span of the skipped trivia. + #[cfg(test)] pub(super) fn skip_trivia(&mut self) -> Span { let lo = self.offset; self.offset = self.peek.span.lo; diff --git a/compiler/qsc_qasm3/src/parser/tests.rs b/compiler/qsc_qasm3/src/parser/tests.rs index 99ce264ffd..894e9730a6 100644 --- a/compiler/qsc_qasm3/src/parser/tests.rs +++ b/compiler/qsc_qasm3/src/parser/tests.rs @@ -68,17 +68,6 @@ pub(super) fn check_opt(parser: impl Parser>, input: &str, }); } -#[allow(dead_code)] -pub(super) fn check_vec(parser: impl Parser>, input: &str, expect: &Expect) { - check_map(parser, input, expect, |values| { - values - .iter() - .map(ToString::to_string) - .collect::>() - .join(",\n") - }); -} - pub(super) fn check_seq( parser: impl Parser<(Vec, FinalSep)>, input: &str, diff --git a/compiler/qsc_qasm3/src/semantic.rs b/compiler/qsc_qasm3/src/semantic.rs index 7780a6a159..720982bfa5 100644 --- a/compiler/qsc_qasm3/src/semantic.rs +++ b/compiler/qsc_qasm3/src/semantic.rs @@ -12,6 +12,7 @@ use qsc_frontend::error::WithSource; use std::path::Path; pub(crate) mod ast; +pub(crate) mod const_eval; pub mod error; mod lowerer; pub use error::Error; diff --git a/compiler/qsc_qasm3/src/semantic/ast.rs b/compiler/qsc_qasm3/src/semantic/ast.rs index b0bd15d7c0..3e46a9dd3d 100644 --- a/compiler/qsc_qasm3/src/semantic/ast.rs +++ b/compiler/qsc_qasm3/src/semantic/ast.rs @@ -1,10 +1,8 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. -pub mod const_eval; - use num_bigint::BigInt; -use qsc_data_structures::span::{Span, WithSpan}; +use qsc_data_structures::span::Span; use std::{ fmt::{self, Display, Formatter}, hash::Hash, @@ -12,14 +10,11 @@ use std::{ }; use crate::{ - parser::ast::{ - display_utils::{ - write_field, write_header, write_indented_list, write_list_field, write_opt_field, - write_opt_list_field, writeln_field, writeln_header, writeln_list_field, - writeln_opt_field, - }, - List, + display_utils::{ + write_field, write_header, write_indented_list, write_list_field, write_opt_field, + write_opt_list_field, writeln_field, writeln_header, writeln_list_field, writeln_opt_field, }, + parser::ast::List, semantic::symbols::SymbolId, stdlib::angle::Angle, }; @@ -72,34 +67,6 @@ impl Display for Annotation { } } -/// A path that may or may not have been successfully parsed. -#[derive(Clone, Debug, Hash, PartialEq, Eq)] -pub enum PathKind { - /// A successfully parsed path. - Ok(Box), - /// An invalid path. - Err(Option>), -} - -impl Default for PathKind { - fn default() -> Self { - PathKind::Err(None) - } -} - -impl Display for PathKind { - fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { - match self { - PathKind::Ok(path) => write!(f, "{path}"), - PathKind::Err(Some(incomplete_path)) => { - write!(f, "Err IncompletePath {}:", incomplete_path.span)?; - write_list_field(f, "segments", &incomplete_path.segments) - } - PathKind::Err(None) => write!(f, "Err",), - } - } -} - /// A path that was successfully parsed up to a certain `.`, /// but is missing its final identifier. #[derive(Clone, Debug, PartialEq, Eq, Hash)] @@ -135,12 +102,6 @@ impl Display for Path { } } -impl WithSpan for Path { - fn with_span(self, span: Span) -> Self { - Self { span, ..self } - } -} - #[derive(Clone, Debug)] pub struct MeasureExpr { pub span: Span, @@ -316,12 +277,6 @@ impl Display for HardwareQubit { } } -impl WithSpan for HardwareQubit { - fn with_span(self, span: Span) -> Self { - Self { span, ..self } - } -} - #[derive(Clone, Debug)] pub struct AliasDeclStmt { pub symbol_id: SymbolId, @@ -524,21 +479,6 @@ impl Display for ContinueStmt { } } -#[derive(Clone, Debug)] -pub enum Identifier { - Ident(Box), - IndexedIdent(Box), -} - -impl Display for Identifier { - fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { - match self { - Identifier::Ident(ident) => write!(f, "{ident}"), - Identifier::IndexedIdent(ident) => write!(f, "{ident}"), - } - } -} - #[derive(Clone, Debug, Hash, PartialEq, Eq)] pub struct Ident { pub span: Span, @@ -560,12 +500,6 @@ impl Display for Ident { } } -impl WithSpan for Ident { - fn with_span(self, span: Span) -> Self { - Self { span, ..self } - } -} - #[derive(Clone, Debug)] pub struct IndexedIdent { pub span: Span, @@ -585,12 +519,6 @@ impl Display for IndexedIdent { } } -impl WithSpan for IndexedIdent { - fn with_span(self, span: Span) -> Self { - Self { span, ..self } - } -} - #[derive(Clone, Debug)] pub struct ExprStmt { pub span: Span, @@ -611,12 +539,6 @@ pub struct Expr { pub ty: super::types::Type, } -impl WithSpan for Expr { - fn with_span(self, span: Span) -> Self { - Self { span, ..self } - } -} - impl Display for Expr { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { writeln_header(f, "Expr", self.span)?; @@ -659,12 +581,6 @@ pub struct RangeDefinition { pub step: Option, } -impl WithSpan for RangeDefinition { - fn with_span(self, span: Span) -> Self { - Self { span, ..self } - } -} - impl Display for RangeDefinition { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { writeln_header(f, "RangeDefinition", self.span)?; @@ -708,141 +624,6 @@ impl Display for GateModifierKind { } } -#[derive(Clone, Debug)] -pub struct ClassicalArgument { - pub span: Span, - pub ty: ScalarType, - pub name: Identifier, - pub access: Option, -} - -impl Display for ClassicalArgument { - fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { - if let Some(access) = &self.access { - write!( - f, - "ClassicalArgument {}: {}, {}, {}", - self.span, self.ty, self.name, access - ) - } else { - write!( - f, - "ClassicalArgument {}: {}, {}", - self.span, self.ty, self.name - ) - } - } -} - -#[derive(Clone, Debug)] -pub enum ExternParameter { - Scalar(ScalarType, Span), - Quantum(Option, Span), - ArrayReference(ArrayReferenceType, Span), -} - -impl Display for ExternParameter { - fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { - match self { - ExternParameter::Scalar(ty, span) => { - write!(f, "{span}: {ty}") - } - ExternParameter::Quantum(expr, span) => { - write!(f, "{span}: {expr:?}") - } - ExternParameter::ArrayReference(ty, span) => { - write!(f, "{span}: {ty}") - } - } - } -} - -impl Default for ExternParameter { - fn default() -> Self { - ExternParameter::Scalar(ScalarType::default(), Span::default()) - } -} - -impl WithSpan for ExternParameter { - fn with_span(self, span: Span) -> Self { - match self { - ExternParameter::Scalar(ty, _) => ExternParameter::Scalar(ty, span), - ExternParameter::Quantum(expr, _) => ExternParameter::Quantum(expr, span), - ExternParameter::ArrayReference(ty, _) => ExternParameter::ArrayReference(ty, span), - } - } -} - -#[derive(Clone, Debug, Default)] -pub struct ScalarType { - pub span: Span, - pub kind: ScalarTypeKind, -} - -impl Display for ScalarType { - fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { - write!(f, "ScalarType {}: {}", self.span, self.kind) - } -} - -#[derive(Clone, Debug, Default)] -pub enum ScalarTypeKind { - Bit(BitType), - Int(IntType), - UInt(UIntType), - Float(FloatType), - Complex(ComplexType), - Angle(AngleType), - BoolType, - Duration, - Stretch, - // Any usage of Err should have pushed a parse error - #[default] - Err, -} - -impl Display for ScalarTypeKind { - fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { - match self { - ScalarTypeKind::Int(int) => write!(f, "{int}"), - ScalarTypeKind::UInt(uint) => write!(f, "{uint}"), - ScalarTypeKind::Float(float) => write!(f, "{float}"), - ScalarTypeKind::Complex(complex) => write!(f, "{complex}"), - ScalarTypeKind::Angle(angle) => write!(f, "{angle}"), - ScalarTypeKind::Bit(bit) => write!(f, "{bit}"), - ScalarTypeKind::BoolType => write!(f, "BoolType"), - ScalarTypeKind::Duration => write!(f, "Duration"), - ScalarTypeKind::Stretch => write!(f, "Stretch"), - ScalarTypeKind::Err => write!(f, "Err"), - } - } -} - -#[derive(Clone, Debug)] -pub enum ArrayBaseTypeKind { - Int(IntType), - UInt(UIntType), - Float(FloatType), - Complex(ComplexType), - Angle(AngleType), - BoolType, - Duration, -} - -impl Display for ArrayBaseTypeKind { - fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { - match self { - ArrayBaseTypeKind::Int(int) => write!(f, "ArrayBaseTypeKind {int}"), - ArrayBaseTypeKind::UInt(uint) => write!(f, "ArrayBaseTypeKind {uint}"), - ArrayBaseTypeKind::Float(float) => write!(f, "ArrayBaseTypeKind {float}"), - ArrayBaseTypeKind::Complex(complex) => write!(f, "ArrayBaseTypeKind {complex}"), - ArrayBaseTypeKind::Angle(angle) => write!(f, "ArrayBaseTypeKind {angle}"), - ArrayBaseTypeKind::Duration => write!(f, "ArrayBaseTypeKind DurationType"), - ArrayBaseTypeKind::BoolType => write!(f, "ArrayBaseTypeKind BoolType"), - } - } -} - #[derive(Clone, Debug)] pub struct IntType { pub span: Span, @@ -921,80 +702,6 @@ impl Display for BitType { } } -#[derive(Clone, Debug)] -pub enum TypeDef { - Scalar(ScalarType), - Array(ArrayType), - ArrayReference(ArrayReferenceType), -} - -impl TypeDef { - pub fn span(&self) -> Span { - match self { - TypeDef::Scalar(ident) => ident.span, - TypeDef::Array(array) => array.span, - TypeDef::ArrayReference(array) => array.span, - } - } -} - -impl Display for TypeDef { - fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { - match self { - TypeDef::Scalar(scalar) => write!(f, "{scalar}"), - TypeDef::Array(array) => write!(f, "{array}"), - TypeDef::ArrayReference(array) => write!(f, "{array}"), - } - } -} - -#[derive(Clone, Debug)] -pub struct ArrayType { - pub span: Span, - pub base_type: ArrayBaseTypeKind, - pub dimensions: List, -} - -impl Display for ArrayType { - fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { - writeln_header(f, "ArrayType", self.span)?; - writeln_field(f, "base_type", &self.base_type)?; - write_list_field(f, "dimensions", &self.dimensions) - } -} - -#[derive(Clone, Debug)] -pub struct ArrayReferenceType { - pub span: Span, - pub mutability: AccessControl, - pub base_type: ArrayBaseTypeKind, - pub dimensions: List, -} - -impl Display for ArrayReferenceType { - fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { - writeln_header(f, "ArrayReferenceType", self.span)?; - writeln_field(f, "mutability", &self.mutability)?; - writeln_field(f, "base_type", &self.base_type)?; - writeln_list_field(f, "dimensions", &self.dimensions) - } -} - -#[derive(Clone, Debug)] -pub enum AccessControl { - ReadOnly, - Mutable, -} - -impl Display for AccessControl { - fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { - match self { - AccessControl::ReadOnly => write!(f, "ReadOnly"), - AccessControl::Mutable => write!(f, "Mutable"), - } - } -} - #[derive(Clone, Debug)] pub struct QuantumArgument { pub span: Span, @@ -1225,72 +932,6 @@ impl Display for OutputDeclaration { } } -#[derive(Clone, Debug)] -pub struct ScalarTypedParameter { - pub span: Span, - pub ty: Box, - pub ident: Ident, -} - -impl Display for ScalarTypedParameter { - fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { - writeln_header(f, "ScalarTypedParameter", self.span)?; - writeln_field(f, "ty", &self.ty)?; - write_field(f, "ident", &self.ident) - } -} - -impl WithSpan for ScalarTypedParameter { - fn with_span(self, span: Span) -> Self { - let Self { ty, ident, .. } = self; - Self { span, ty, ident } - } -} - -#[derive(Clone, Debug)] -pub struct QuantumTypedParameter { - pub span: Span, - pub size: Option, - pub ident: Ident, -} - -impl Display for QuantumTypedParameter { - fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { - writeln_header(f, "QuantumTypedParameter", self.span)?; - writeln_opt_field(f, "size", self.size.as_ref())?; - write_field(f, "ident", &self.ident) - } -} - -impl WithSpan for QuantumTypedParameter { - fn with_span(self, span: Span) -> Self { - let Self { size, ident, .. } = self; - Self { span, size, ident } - } -} - -#[derive(Clone, Debug)] -pub struct ArrayTypedParameter { - pub span: Span, - pub ty: Box, - pub ident: Ident, -} - -impl Display for ArrayTypedParameter { - fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { - writeln_header(f, "ArrayTypedParameter", self.span)?; - writeln_field(f, "ty", &self.ty)?; - write_field(f, "ident", &self.ident) - } -} - -impl WithSpan for ArrayTypedParameter { - fn with_span(self, span: Span) -> Self { - let Self { ty, ident, .. } = self; - Self { span, ty, ident } - } -} - #[derive(Clone, Debug)] pub struct DefStmt { pub span: Span, @@ -1703,19 +1344,6 @@ pub enum IndexSetItem { Err, } -/// This is needed to able to use `IndexSetItem` in the `seq` combinator. -impl WithSpan for IndexSetItem { - fn with_span(self, span: Span) -> Self { - match self { - IndexSetItem::RangeDefinition(range) => { - IndexSetItem::RangeDefinition(range.with_span(span)) - } - IndexSetItem::Expr(expr) => IndexSetItem::Expr(expr.with_span(span)), - IndexSetItem::Err => IndexSetItem::Err, - } - } -} - impl Display for IndexSetItem { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { match self { @@ -1726,21 +1354,6 @@ impl Display for IndexSetItem { } } -#[derive(Clone, Debug)] -pub enum IOKeyword { - Input, - Output, -} - -impl Display for IOKeyword { - fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { - match self { - IOKeyword::Input => write!(f, "input"), - IOKeyword::Output => write!(f, "output"), - } - } -} - #[derive(Clone, Copy, Debug)] pub enum TimeUnit { Dt, diff --git a/compiler/qsc_qasm3/src/semantic/ast/const_eval.rs b/compiler/qsc_qasm3/src/semantic/const_eval.rs similarity index 99% rename from compiler/qsc_qasm3/src/semantic/ast/const_eval.rs rename to compiler/qsc_qasm3/src/semantic/const_eval.rs index f7659a01df..9942752c76 100644 --- a/compiler/qsc_qasm3/src/semantic/ast/const_eval.rs +++ b/compiler/qsc_qasm3/src/semantic/const_eval.rs @@ -6,10 +6,11 @@ //! and sizes of arrays. Therefore, those are the only const evaluation //! paths that are implemented. -use super::{ +use super::ast::{ BinOp, BinaryOpExpr, Cast, Expr, ExprKind, FunctionCall, IndexExpr, IndexedIdent, LiteralKind, - SymbolId, UnaryOp, UnaryOpExpr, + UnaryOp, UnaryOpExpr, }; +use super::symbols::SymbolId; use crate::semantic::Lowerer; use crate::stdlib::angle; use crate::{ diff --git a/compiler/qsc_qasm3/src/semantic/lowerer.rs b/compiler/qsc_qasm3/src/semantic/lowerer.rs index 8bbcbba57d..ef4ec4a099 100644 --- a/compiler/qsc_qasm3/src/semantic/lowerer.rs +++ b/compiler/qsc_qasm3/src/semantic/lowerer.rs @@ -4,7 +4,7 @@ use std::ops::ShlAssign; use std::rc::Rc; -use super::ast::const_eval::ConstEvalError; +use super::const_eval::ConstEvalError; use super::symbols::ScopeKind; use super::types::binop_requires_asymmetric_angle_op; use super::types::binop_requires_int_conversion_for_type; diff --git a/compiler/qsc_qasm3/src/semantic/symbols.rs b/compiler/qsc_qasm3/src/semantic/symbols.rs index 0fbb4081e3..174a3d93c5 100644 --- a/compiler/qsc_qasm3/src/semantic/symbols.rs +++ b/compiler/qsc_qasm3/src/semantic/symbols.rs @@ -155,7 +155,7 @@ impl Symbol { impl std::fmt::Display for Symbol { fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { - use crate::parser::ast::display_utils; + use crate::display_utils; display_utils::writeln_header(f, "Symbol", self.span)?; display_utils::writeln_field(f, "name", &self.name)?; display_utils::writeln_field(f, "type", &self.ty)?; diff --git a/compiler/qsc_qasm3/src/semantic/tests.rs b/compiler/qsc_qasm3/src/semantic/tests.rs index 8033310e2b..fcf341ef7a 100644 --- a/compiler/qsc_qasm3/src/semantic/tests.rs +++ b/compiler/qsc_qasm3/src/semantic/tests.rs @@ -9,61 +9,14 @@ pub mod decls; pub mod expression; pub mod statements; -use std::path::Path; -use std::sync::Arc; - +use super::parse_source; use crate::io::InMemorySourceResolver; use crate::io::SourceResolver; - -use super::parse_source; - -use super::QasmSemanticParseResult; - use expect_test::expect; -use miette::Report; - use expect_test::Expect; - -pub(crate) fn parse_all

    ( - path: P, - sources: impl IntoIterator, Arc)>, -) -> miette::Result> -where - P: AsRef, -{ - let mut resolver = InMemorySourceResolver::from_iter(sources); - let (path, source) = resolver - .resolve(path.as_ref()) - .map_err(|e| vec![Report::new(e)])?; - let res = parse_source(source, path, &mut resolver); - if res.source.has_errors() { - let errors = res - .errors() - .into_iter() - .map(|e| Report::new(e.clone())) - .collect(); - Err(errors) - } else { - Ok(res) - } -} - -pub(crate) fn parse(source: S) -> miette::Result> -where - S: AsRef, -{ - let mut resolver = InMemorySourceResolver::from_iter([("test".into(), source.as_ref().into())]); - let res = parse_source(source, "test", &mut resolver); - if res.source.has_errors() { - let errors = res - .errors() - .into_iter() - .map(|e| Report::new(e.clone())) - .collect(); - return Err(errors); - } - Ok(res) -} +use miette::Report; +use std::path::Path; +use std::sync::Arc; pub(super) fn check(input: &str, expect: &Expect) { check_map(input, expect, |p, _| p.to_string()); diff --git a/compiler/qsc_qasm3/src/semantic/tests/expression/binary/complex.rs b/compiler/qsc_qasm3/src/semantic/tests/expression/binary/complex.rs index d070cbb067..489b357a02 100644 --- a/compiler/qsc_qasm3/src/semantic/tests/expression/binary/complex.rs +++ b/compiler/qsc_qasm3/src/semantic/tests/expression/binary/complex.rs @@ -36,6 +36,7 @@ fn addition() { ); } +#[test] fn addition_assign_op() { let input = " input complex[float] a; @@ -46,26 +47,25 @@ fn addition_assign_op() { check_stmt_kinds( input, &expect![[r#" - InputDeclaration [9-32]: - symbol_id: 8 - InputDeclaration [41-64]: - symbol_id: 9 - ClassicalDeclarationStmt [73-93]: - symbol_id: 10 - ty_span: [73-80] - init_expr: Expr [85-92]: - ty: Complex(None, false) - kind: Paren Expr [86-91]: + InputDeclaration [9-32]: + symbol_id: 8 + ClassicalDeclarationStmt [41-57]: + symbol_id: 9 + ty_span: [41-48] + init_expr: Expr [53-56]: + ty: Complex(None, true) + kind: Lit: Complex(0.0, 0.0) + AssignOpStmt [66-73]: + symbol_id: 9 + indices: + op: Add + lhs: Expr [71-72]: ty: Complex(None, false) - kind: BinaryOpExpr: - op: Add - lhs: Expr [86-87]: - ty: Complex(None, false) - kind: SymbolId(8) - rhs: Expr [90-91]: - ty: Complex(None, false) - kind: SymbolId(9) - "#]], + kind: SymbolId(8) + rhs: Expr [71-72]: + ty: Complex(None, false) + kind: SymbolId(8) + "#]], ); } diff --git a/compiler/qsc_qasm3/src/stdlib/angle.rs b/compiler/qsc_qasm3/src/stdlib/angle.rs index 61b007e9b7..43c8f60a99 100644 --- a/compiler/qsc_qasm3/src/stdlib/angle.rs +++ b/compiler/qsc_qasm3/src/stdlib/angle.rs @@ -23,7 +23,6 @@ pub struct Angle { pub size: u32, } -#[allow(dead_code)] impl Angle { pub fn new(value: u64, size: u32) -> Self { Angle { value, size } @@ -66,6 +65,7 @@ impl Angle { angle.cast(size, false) } + #[cfg(test)] fn to_bitstring(self) -> String { format!("{:0width$b}", self.value, width = self.size as usize) } diff --git a/compiler/qsc_qasm3/src/stdlib/compile.rs b/compiler/qsc_qasm3/src/stdlib/compile.rs index af801d567a..f0de74ec24 100644 --- a/compiler/qsc_qasm3/src/stdlib/compile.rs +++ b/compiler/qsc_qasm3/src/stdlib/compile.rs @@ -42,8 +42,6 @@ pub fn package_store_with_qasm( } } -pub const OPENQASM_LIBRARY_URI_SCHEME: &str = "openqasm-library-source"; - pub const STD_LIB: &[(&str, &str)] = &[ ( "openqasm-library-source:QasmStd/Angle.qs", diff --git a/compiler/qsc_qasm3/src/tests.rs b/compiler/qsc_qasm3/src/tests.rs index 47171dbd1d..04baf99fb3 100644 --- a/compiler/qsc_qasm3/src/tests.rs +++ b/compiler/qsc_qasm3/src/tests.rs @@ -117,6 +117,7 @@ where Ok(unit) } +#[allow(dead_code)] pub fn compile_all

    ( path: P, sources: impl IntoIterator, Arc)>, @@ -134,6 +135,7 @@ where compile_all_with_config(path, sources, config) } +#[allow(dead_code)] pub fn compile_all_fragments

    ( path: P, sources: impl IntoIterator, Arc)>, @@ -400,17 +402,6 @@ fn get_last_statement_as_qsharp(package: &Package) -> String { qsharp } -fn get_first_statement_as_qsharp(package: &Package) -> String { - let qsharp = match package.nodes.get(1) { - Some(i) => match i { - TopLevelNode::Namespace(_) => panic!("Expected Stmt, got Namespace"), - TopLevelNode::Stmt(stmt) => gen_qsharp_stmt(stmt.as_ref()), - }, - None => panic!("Expected Stmt, got None"), - }; - qsharp -} - pub struct AstDespanner; impl AstDespanner { #[allow(dead_code)] // false positive lint diff --git a/compiler/qsc_qasm3/src/types.rs b/compiler/qsc_qasm3/src/types.rs index 4228c51d12..2d03e55a75 100644 --- a/compiler/qsc_qasm3/src/types.rs +++ b/compiler/qsc_qasm3/src/types.rs @@ -3,17 +3,6 @@ use std::fmt::{self, Display, Formatter}; -use qsc_data_structures::span::Span; - -#[derive(Clone, Debug, PartialEq)] -pub enum GateModifier { - /// The `adjoint` modifier. - Inv(Span), - Pow(Option, Span), - Ctrl(Option, Span), - NegCtrl(Option, Span), -} - #[derive(Debug, Copy, Clone, PartialEq)] pub struct Complex { pub real: f64, @@ -54,7 +43,6 @@ pub enum Type { #[derive(Debug, Clone, PartialEq, Eq)] pub enum CallableKind { /// A function. - #[allow(dead_code)] Function, /// An operation. Operation, From b73e6e312d14fdebcdcec712c535db07222957af Mon Sep 17 00:00:00 2001 From: orpuente-MS <156957451+orpuente-MS@users.noreply.github.com> Date: Wed, 16 Apr 2025 16:31:58 -0700 Subject: [PATCH 103/108] Turn builtins into keywords (#2304) --- compiler/qsc_qasm3/src/keyword.rs | 3 ++ compiler/qsc_qasm3/src/lex/cooked.rs | 30 +++-------- compiler/qsc_qasm3/src/lex/cooked/tests.rs | 54 +++++++++---------- .../qsc_qasm3/src/parser/completion/tests.rs | 4 +- compiler/qsc_qasm3/src/parser/expr.rs | 2 +- compiler/qsc_qasm3/src/parser/stmt.rs | 10 ++-- .../src/parser/stmt/tests/classical_decl.rs | 4 +- .../parser/stmt/tests/invalid_stmts/decl.rs | 24 +++++---- .../stmt/tests/invalid_stmts/measure.rs | 50 +++++++++-------- 9 files changed, 88 insertions(+), 93 deletions(-) diff --git a/compiler/qsc_qasm3/src/keyword.rs b/compiler/qsc_qasm3/src/keyword.rs index 2a2a07e8e3..f051b0b888 100644 --- a/compiler/qsc_qasm3/src/keyword.rs +++ b/compiler/qsc_qasm3/src/keyword.rs @@ -23,6 +23,7 @@ pub enum Keyword { DefCalGrammar, Default, Delay, + Dim, Else, End, Extern, @@ -71,6 +72,7 @@ impl Keyword { Keyword::DefCalGrammar => "defcalgrammar", Keyword::Default => "default", Keyword::Delay => "delay", + Keyword::Dim => "#dim", Keyword::Else => "else", Keyword::End => "end", Keyword::Extern => "extern", @@ -132,6 +134,7 @@ impl FromStr for Keyword { "defcalgrammar" => Ok(Self::DefCalGrammar), "default" => Ok(Self::Default), "delay" => Ok(Self::Delay), + "dim" => Ok(Self::Dim), "else" => Ok(Self::Else), "end" => Ok(Self::End), "extern" => Ok(Self::Extern), diff --git a/compiler/qsc_qasm3/src/lex/cooked.rs b/compiler/qsc_qasm3/src/lex/cooked.rs index 78f1008158..4bff5990ae 100644 --- a/compiler/qsc_qasm3/src/lex/cooked.rs +++ b/compiler/qsc_qasm3/src/lex/cooked.rs @@ -87,20 +87,21 @@ impl Error { /// A token kind. #[derive(Clone, Copy, Debug, Eq, PartialEq, Sequence)] pub enum TokenKind { + /// `@DOT_SEPARETED_IDENTIFIER REST_OF_LINE` + /// Examples: + /// @Microsoft.SimulatableIntrinsic + /// @Microsoft.Config Base Annotation, + /// `pragma REST_OF_LINE` + /// or + /// `#pragma REST_OF_LINE` Pragma, Keyword(Keyword), Type(Type), // Builtin identifiers and operations GPhase, - Inv, - Pow, - Ctrl, - NegCtrl, - Dim, DurationOf, - Measure, Literal(Literal), @@ -151,13 +152,7 @@ impl Display for TokenKind { TokenKind::Keyword(keyword) => write!(f, "keyword `{keyword}`"), TokenKind::Type(type_) => write!(f, "keyword `{type_}`"), TokenKind::GPhase => write!(f, "gphase"), - TokenKind::Inv => write!(f, "inv"), - TokenKind::Pow => write!(f, "pow"), - TokenKind::Ctrl => write!(f, "ctrl"), - TokenKind::NegCtrl => write!(f, "negctrl"), - TokenKind::Dim => write!(f, "dim"), TokenKind::DurationOf => write!(f, "durationof"), - TokenKind::Measure => write!(f, "measure"), TokenKind::Literal(literal) => write!(f, "literal `{literal}`"), TokenKind::Open(Delim::Brace) => write!(f, "`{{`"), TokenKind::Open(Delim::Bracket) => write!(f, "`[`"), @@ -527,9 +522,6 @@ impl<'a> Lexer<'a> { let ident = &self.input[(token.offset as usize)..(self.offset() as usize)]; let cooked_ident = Self::ident(ident); match cooked_ident { - // A `dim` token without a `#` in front should be - // treated as an identifier and not as a keyword. - TokenKind::Dim => Ok(Some(TokenKind::Identifier)), TokenKind::Keyword(Keyword::Pragma) => { self.eat_to_end_of_line(); Ok(Some(TokenKind::Pragma)) @@ -577,7 +569,7 @@ impl<'a> Lexer<'a> { self.expect(raw::TokenKind::Ident, TokenKind::Identifier)?; let ident = &self.input[(token.offset as usize + 1)..(self.offset() as usize)]; match Self::ident(ident) { - TokenKind::Dim => Ok(Some(TokenKind::Dim)), + TokenKind::Keyword(Keyword::Dim) => Ok(Some(TokenKind::Keyword(Keyword::Dim))), TokenKind::Keyword(Keyword::Pragma) => { self.eat_to_end_of_line(); Ok(Some(TokenKind::Pragma)) @@ -731,13 +723,7 @@ impl<'a> Lexer<'a> { fn ident(ident: &str) -> TokenKind { match ident { "gphase" => TokenKind::GPhase, - "inv" => TokenKind::Inv, - "pow" => TokenKind::Pow, - "ctrl" => TokenKind::Ctrl, - "negctrl" => TokenKind::NegCtrl, - "dim" => TokenKind::Dim, "durationof" => TokenKind::DurationOf, - "measure" => TokenKind::Measure, ident => { if let Ok(keyword) = ident.parse::() { TokenKind::Keyword(keyword) diff --git a/compiler/qsc_qasm3/src/lex/cooked/tests.rs b/compiler/qsc_qasm3/src/lex/cooked/tests.rs index c721b5e97b..2716c3640e 100644 --- a/compiler/qsc_qasm3/src/lex/cooked/tests.rs +++ b/compiler/qsc_qasm3/src/lex/cooked/tests.rs @@ -29,13 +29,7 @@ fn op_string(kind: TokenKind) -> Option { TokenKind::Keyword(keyword) => Some(keyword.to_string()), TokenKind::Type(type_) => Some(type_.to_string()), TokenKind::GPhase => Some("gphase".to_string()), - TokenKind::Inv => Some("inv".to_string()), - TokenKind::Pow => Some("pow".to_string()), - TokenKind::Ctrl => Some("ctrl".to_string()), - TokenKind::NegCtrl => Some("negctrl".to_string()), - TokenKind::Dim => Some("dim".to_string()), TokenKind::DurationOf => Some("durationof".to_string()), - TokenKind::Measure => Some("measure".to_string()), TokenKind::Semicolon => Some(";".to_string()), TokenKind::Arrow => Some("->".to_string()), TokenKind::At => Some("@".to_string()), @@ -1223,18 +1217,20 @@ fn dim() { check( "dim", &expect![[r#" - [ - Ok( - Token { - kind: Identifier, - span: Span { - lo: 0, - hi: 3, + [ + Ok( + Token { + kind: Keyword( + Dim, + ), + span: Span { + lo: 0, + hi: 3, + }, }, - }, - ), - ] - "#]], + ), + ] + "#]], ); } @@ -1243,17 +1239,19 @@ fn sharp_dim() { check( "#dim", &expect![[r#" - [ - Ok( - Token { - kind: Dim, - span: Span { - lo: 0, - hi: 4, + [ + Ok( + Token { + kind: Keyword( + Dim, + ), + span: Span { + lo: 0, + hi: 4, + }, }, - }, - ), - ] - "#]], + ), + ] + "#]], ); } diff --git a/compiler/qsc_qasm3/src/parser/completion/tests.rs b/compiler/qsc_qasm3/src/parser/completion/tests.rs index f9693b7b09..fdb6784a94 100644 --- a/compiler/qsc_qasm3/src/parser/completion/tests.rs +++ b/compiler/qsc_qasm3/src/parser/completion/tests.rs @@ -30,7 +30,7 @@ fn begin_document() { "|OPENQASM 3;", &expect![[r#" WordKinds( - Annotation | Barrier | Box | Break | Cal | Const | Continue | CReg | Def | DefCal | DefCalGrammar | Delay | End | Extern | False | For | Gate | If | Include | Input | Let | OpenQASM | Output | Pragma | QReg | Qubit | Reset | True | Return | Switch | While, + Annotation | Barrier | Box | Break | Cal | Const | Continue | CReg | Ctrl | Def | DefCal | DefCalGrammar | Delay | End | Extern | False | For | Gate | If | Include | Input | Inv | Let | Measure | NegCtrl | OpenQASM | Output | Pow | Pragma | QReg | Qubit | Reset | True | Return | Switch | While, ) "#]], ); @@ -42,7 +42,7 @@ fn end_of_version() { "OPENQASM 3;|", &expect![[r#" WordKinds( - Annotation | Barrier | Box | Break | Cal | Const | Continue | CReg | Def | DefCal | DefCalGrammar | Delay | End | Extern | False | For | Gate | If | Include | Input | Let | Output | Pragma | QReg | Qubit | Reset | True | Return | Switch | While, + Annotation | Barrier | Box | Break | Cal | Const | Continue | CReg | Ctrl | Def | DefCal | DefCalGrammar | Delay | End | Extern | False | For | Gate | If | Include | Input | Inv | Let | Measure | NegCtrl | Output | Pow | Pragma | QReg | Qubit | Reset | True | Return | Switch | While, ) "#]], ); diff --git a/compiler/qsc_qasm3/src/parser/expr.rs b/compiler/qsc_qasm3/src/parser/expr.rs index ba5e004027..a2c14317ae 100644 --- a/compiler/qsc_qasm3/src/parser/expr.rs +++ b/compiler/qsc_qasm3/src/parser/expr.rs @@ -736,7 +736,7 @@ pub(crate) fn expr_list(s: &mut ParserContext) -> Result> { pub(crate) fn measure_expr(s: &mut ParserContext) -> Result { let lo = s.peek().span.lo; - token(s, TokenKind::Measure)?; + token(s, TokenKind::Keyword(Keyword::Measure))?; let measure_token_span = s.span(lo); let operand = gate_operand(s)?; diff --git a/compiler/qsc_qasm3/src/parser/stmt.rs b/compiler/qsc_qasm3/src/parser/stmt.rs index ba99072283..a911f1c669 100644 --- a/compiler/qsc_qasm3/src/parser/stmt.rs +++ b/compiler/qsc_qasm3/src/parser/stmt.rs @@ -588,7 +588,7 @@ fn array_reference_ty(s: &mut ParserContext) -> Result { let base_type = array_base_type(s)?; token(s, TokenKind::Comma)?; - let dimensions = if token(s, TokenKind::Dim).is_ok() { + let dimensions = if token(s, TokenKind::Keyword(Keyword::Dim)).is_ok() { token(s, TokenKind::Eq)?; vec![expr::expr(s)?] } else { @@ -1591,16 +1591,16 @@ fn gate_modifier(s: &mut ParserContext) -> Result { let lo = s.peek().span.lo; let modifier_keyword_span; - let kind = if opt(s, |s| token(s, TokenKind::Inv))?.is_some() { + let kind = if opt(s, |s| token(s, TokenKind::Keyword(Keyword::Inv)))?.is_some() { modifier_keyword_span = s.span(lo); GateModifierKind::Inv - } else if opt(s, |s| token(s, TokenKind::Pow))?.is_some() { + } else if opt(s, |s| token(s, TokenKind::Keyword(Keyword::Pow)))?.is_some() { modifier_keyword_span = s.span(lo); token(s, TokenKind::Open(Delim::Paren))?; let expr = expr::expr(s)?; recovering_token(s, TokenKind::Close(Delim::Paren)); GateModifierKind::Pow(expr) - } else if opt(s, |s| token(s, TokenKind::Ctrl))?.is_some() { + } else if opt(s, |s| token(s, TokenKind::Keyword(Keyword::Ctrl)))?.is_some() { modifier_keyword_span = s.span(lo); let expr = opt(s, |s| { token(s, TokenKind::Open(Delim::Paren))?; @@ -1610,7 +1610,7 @@ fn gate_modifier(s: &mut ParserContext) -> Result { })?; GateModifierKind::Ctrl(expr) } else { - token(s, TokenKind::NegCtrl)?; + token(s, TokenKind::Keyword(Keyword::NegCtrl))?; modifier_keyword_span = s.span(lo); let expr = opt(s, |s| { token(s, TokenKind::Open(Delim::Paren))?; diff --git a/compiler/qsc_qasm3/src/parser/stmt/tests/classical_decl.rs b/compiler/qsc_qasm3/src/parser/stmt/tests/classical_decl.rs index 0b7f4ddb01..dcf6e9fc40 100644 --- a/compiler/qsc_qasm3/src/parser/stmt/tests/classical_decl.rs +++ b/compiler/qsc_qasm3/src/parser/stmt/tests/classical_decl.rs @@ -1115,7 +1115,9 @@ fn const_decl_with_measurement_init_fails() { Open( Brace, ), - Measure, + Keyword( + Measure, + ), Span { lo: 16, hi: 23, diff --git a/compiler/qsc_qasm3/src/parser/stmt/tests/invalid_stmts/decl.rs b/compiler/qsc_qasm3/src/parser/stmt/tests/invalid_stmts/decl.rs index a653c561ec..9732110701 100644 --- a/compiler/qsc_qasm3/src/parser/stmt/tests/invalid_stmts/decl.rs +++ b/compiler/qsc_qasm3/src/parser/stmt/tests/invalid_stmts/decl.rs @@ -744,17 +744,19 @@ fn backed_arrays_cant_use_dim() { parse, "array[uint[8], #dim=2] myvar;", &expect![[r#" - Error( - Rule( - "scalar or array type", - Dim, - Span { - lo: 15, - hi: 19, - }, - ), - ) - "#]], + Error( + Rule( + "scalar or array type", + Keyword( + Dim, + ), + Span { + lo: 15, + hi: 19, + }, + ), + ) + "#]], ); } diff --git a/compiler/qsc_qasm3/src/parser/stmt/tests/invalid_stmts/measure.rs b/compiler/qsc_qasm3/src/parser/stmt/tests/invalid_stmts/measure.rs index b23fb4a5b3..58f77855a1 100644 --- a/compiler/qsc_qasm3/src/parser/stmt/tests/invalid_stmts/measure.rs +++ b/compiler/qsc_qasm3/src/parser/stmt/tests/invalid_stmts/measure.rs @@ -178,34 +178,38 @@ fn measure_cant_be_used_in_sub_expressions() { parse, "a = 2 * measure $0;", &expect![[r#" - Error( - Rule( - "expression", - Measure, - Span { - lo: 8, - hi: 15, - }, - ), - ) - "#]], + Error( + Rule( + "expression", + Keyword( + Measure, + ), + Span { + lo: 8, + hi: 15, + }, + ), + ) + "#]], ); check( parse, "a = (measure $0) + (measure $1);", &expect![[r#" - Error( - Token( - Close( - Paren, + Error( + Token( + Close( + Paren, + ), + Keyword( + Measure, + ), + Span { + lo: 5, + hi: 12, + }, ), - Measure, - Span { - lo: 5, - hi: 12, - }, - ), - ) - "#]], + ) + "#]], ); } From 38fe4799b16d80acc2d5a8873288d895b15685de Mon Sep 17 00:00:00 2001 From: Ian Davis Date: Wed, 16 Apr 2025 16:32:19 -0700 Subject: [PATCH 104/108] Parser io feedback (#2303) --- compiler/qsc_qasm3/src/io.rs | 5 +---- pip/src/interop.rs | 5 ++++- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/compiler/qsc_qasm3/src/io.rs b/compiler/qsc_qasm3/src/io.rs index d4fe223fc7..5493f4e28c 100644 --- a/compiler/qsc_qasm3/src/io.rs +++ b/compiler/qsc_qasm3/src/io.rs @@ -37,10 +37,7 @@ pub trait SourceResolver { self.ctx().add_path_to_include_graph(path.clone()); Ok((path, source)) } - Err(_) => Err(Error(ErrorKind::NotFound(format!( - "Could not resolve include file: {}", - path.display() - )))), + Err(error) => Err(Error(ErrorKind::IO(error.to_string()))), } } #[cfg(not(feature = "fs"))] diff --git a/pip/src/interop.rs b/pip/src/interop.rs index 7fb560eac2..f9b42116b0 100644 --- a/pip/src/interop.rs +++ b/pip/src/interop.rs @@ -63,7 +63,10 @@ where where P: AsRef, { - let path = self.path.join(path); + let path = self + .fs + .resolve_path(self.path.as_path(), path.as_ref()) + .map_err(|e| qsc::qasm::io::Error(qsc::qasm::io::ErrorKind::IO(e.to_string())))?; self.ctx().check_include_errors(&path)?; let (path, source) = self .fs From 6f6810d09fb9fe3ca6c0bb518a0d08cfcbece6ad Mon Sep 17 00:00:00 2001 From: orpuente-MS <156957451+orpuente-MS@users.noreply.github.com> Date: Thu, 17 Apr 2025 11:32:09 -0700 Subject: [PATCH 105/108] Remove completion dead code (#2305) This PR addresses some of the feedback in the PR review. Thanks @minestarks ! --- compiler/qsc_qasm3/src/keyword.rs | 3 --- compiler/qsc_qasm3/src/lex/cooked.rs | 4 ++-- .../qsc_qasm3/src/parser/completion/collector.rs | 5 +++-- .../qsc_qasm3/src/parser/completion/word_kinds.rs | 13 ++++++------- 4 files changed, 11 insertions(+), 14 deletions(-) diff --git a/compiler/qsc_qasm3/src/keyword.rs b/compiler/qsc_qasm3/src/keyword.rs index f051b0b888..fac326a415 100644 --- a/compiler/qsc_qasm3/src/keyword.rs +++ b/compiler/qsc_qasm3/src/keyword.rs @@ -30,7 +30,6 @@ pub enum Keyword { False, For, Gate, - GPhase, If, In, Include, @@ -79,7 +78,6 @@ impl Keyword { Keyword::False => "false", Keyword::For => "for", Keyword::Gate => "gate", - Keyword::GPhase => "gphase", Keyword::If => "if", Keyword::In => "in", Keyword::Include => "include", @@ -141,7 +139,6 @@ impl FromStr for Keyword { "false" => Ok(Self::False), "for" => Ok(Self::For), "gate" => Ok(Self::Gate), - "gphase" => Ok(Self::GPhase), "if" => Ok(Self::If), "in" => Ok(Self::In), "include" => Ok(Self::Include), diff --git a/compiler/qsc_qasm3/src/lex/cooked.rs b/compiler/qsc_qasm3/src/lex/cooked.rs index 4bff5990ae..68e445c317 100644 --- a/compiler/qsc_qasm3/src/lex/cooked.rs +++ b/compiler/qsc_qasm3/src/lex/cooked.rs @@ -89,8 +89,8 @@ impl Error { pub enum TokenKind { /// `@DOT_SEPARETED_IDENTIFIER REST_OF_LINE` /// Examples: - /// @Microsoft.SimulatableIntrinsic - /// @Microsoft.Config Base + /// @qsharp.SimulatableIntrinsic + /// @qsharp.Config Base Annotation, /// `pragma REST_OF_LINE` /// or diff --git a/compiler/qsc_qasm3/src/parser/completion/collector.rs b/compiler/qsc_qasm3/src/parser/completion/collector.rs index 5339058d2f..fd02969ce4 100644 --- a/compiler/qsc_qasm3/src/parser/completion/collector.rs +++ b/compiler/qsc_qasm3/src/parser/completion/collector.rs @@ -57,7 +57,7 @@ //! ``` use super::WordKinds; -use crate::lex::{ClosedBinOp, Token, TokenKind}; +use crate::lex::{Token, TokenKind}; use qsc_data_structures::span::Span; pub(crate) struct ValidWordCollector { @@ -163,7 +163,8 @@ fn cursor_at_token(cursor_offset: u32, next_token: Token, scanner_offset: u32) - // Order matters here as the cases overlap. TokenKind::Identifier | TokenKind::Keyword(_) - | TokenKind::ClosedBinOp(ClosedBinOp::AmpAmp | ClosedBinOp::BarBar) + | TokenKind::GPhase + | TokenKind::DurationOf | TokenKind::Eof => { // next token is a word or eof, so count if cursor touches either side of the token scanner_offset <= cursor_offset && cursor_offset <= next_token.span.hi diff --git a/compiler/qsc_qasm3/src/parser/completion/word_kinds.rs b/compiler/qsc_qasm3/src/parser/completion/word_kinds.rs index 8ad18f770d..fecf011fb8 100644 --- a/compiler/qsc_qasm3/src/parser/completion/word_kinds.rs +++ b/compiler/qsc_qasm3/src/parser/completion/word_kinds.rs @@ -32,15 +32,15 @@ bitflags! { // Begin names. // - /// A path in an expression. Callables, UDT constructors, local variables. + /// A path in an expression. Namespaced annotations and pragmas + /// as suggested by the QASM3 spec. Examples: + /// `pragma qsharp.Profile.Base` + /// `@qsharp.SimulatableIntrinsic` const PathExpr = 1 << 0; /// A path segment that follows a `.` /// A more specific name kind can be inferred from a recovered AST. const PathSegment = 1 << 1; - /// A primitive class. - const PrimitiveClass = 1 << 2; - // // End names. @@ -51,7 +51,7 @@ bitflags! { // /// An annotation, without the leading `@`. - const Annotation = 1 << 3; + const Annotation = 1 << 2; // @@ -82,7 +82,6 @@ bitflags! { const False = keyword_bit(Keyword::False); const For = keyword_bit(Keyword::For); const Gate = keyword_bit(Keyword::Gate); - const GPhase = keyword_bit(Keyword::GPhase); const If = keyword_bit(Keyword::If); const In = keyword_bit(Keyword::In); const Include = keyword_bit(Keyword::Include); @@ -108,7 +107,7 @@ bitflags! { } } -const KEYWORDS_START: u8 = 4; +const KEYWORDS_START: u8 = 3; const fn keyword_bit(k: Keyword) -> u128 { 1 << (k as u8 + KEYWORDS_START) } From 55ce258c4a0c4a2f48eeadcc965d1d390d9bc34e Mon Sep 17 00:00:00 2001 From: orpuente-MS <156957451+orpuente-MS@users.noreply.github.com> Date: Thu, 17 Apr 2025 14:32:30 -0700 Subject: [PATCH 106/108] Rename qsc_qasm3 to qsc_qasm (#2307) Rename `qsc_qasm3` to `qsc_qasm`. --------- Co-authored-by: Ian Davis --- .github/workflows/fuzz.yml | 2 +- Cargo.lock | 6 +- Cargo.toml | 2 +- compiler/qsc/Cargo.toml | 2 +- compiler/qsc/src/qasm.rs | 26 ++-- compiler/{qsc_qasm3 => qsc_qasm}/Cargo.toml | 4 +- compiler/{qsc_qasm3 => qsc_qasm}/README.md | 4 +- .../benches/rgqft_multiplier.rs | 2 +- .../benches/rgqft_multiplier_1q.qasm | 0 .../benches/rgqft_multiplier_4q.qasm | 0 .../src/ast_builder.rs | 0 .../{qsc_qasm3 => qsc_qasm}/src/compiler.rs | 2 +- .../src/compiler/error.rs | 12 +- .../{qsc_qasm3 => qsc_qasm}/src/convert.rs | 0 .../src/display_utils.rs | 0 compiler/{qsc_qasm3 => qsc_qasm}/src/io.rs | 0 .../{qsc_qasm3 => qsc_qasm}/src/io/error.rs | 0 .../{qsc_qasm3 => qsc_qasm}/src/keyword.rs | 0 compiler/{qsc_qasm3 => qsc_qasm}/src/lex.rs | 0 .../{qsc_qasm3 => qsc_qasm}/src/lex/cooked.rs | 10 +- .../src/lex/cooked/tests.rs | 0 .../{qsc_qasm3 => qsc_qasm}/src/lex/raw.rs | 0 .../src/lex/raw/tests.rs | 0 compiler/{qsc_qasm3 => qsc_qasm}/src/lib.rs | 2 +- .../{qsc_qasm3 => qsc_qasm}/src/parser.rs | 0 .../{qsc_qasm3 => qsc_qasm}/src/parser/ast.rs | 0 .../src/parser/completion.rs | 0 .../src/parser/completion/collector.rs | 0 .../src/parser/completion/tests.rs | 0 .../src/parser/completion/word_kinds.rs | 0 .../src/parser/error.rs | 34 ++--- .../src/parser/expr.rs | 0 .../src/parser/expr/tests.rs | 0 .../src/parser/mut_visit.rs | 0 .../src/parser/prgm.rs | 0 .../src/parser/prim.rs | 0 .../src/parser/prim/tests.rs | 0 .../src/parser/scan.rs | 0 .../src/parser/stmt.rs | 0 .../src/parser/stmt/tests.rs | 0 .../src/parser/stmt/tests/alias.rs | 0 .../src/parser/stmt/tests/annotation.rs | 0 .../src/parser/stmt/tests/barrier.rs | 0 .../src/parser/stmt/tests/block.rs | 0 .../src/parser/stmt/tests/box_stmt.rs | 0 .../src/parser/stmt/tests/cal.rs | 0 .../src/parser/stmt/tests/cal_grammar.rs | 0 .../src/parser/stmt/tests/classical_decl.rs | 0 .../src/parser/stmt/tests/def.rs | 0 .../src/parser/stmt/tests/defcal.rs | 0 .../src/parser/stmt/tests/delay.rs | 0 .../src/parser/stmt/tests/expr_stmt.rs | 0 .../src/parser/stmt/tests/extern_decl.rs | 0 .../src/parser/stmt/tests/for_loops.rs | 0 .../src/parser/stmt/tests/gate_call.rs | 0 .../src/parser/stmt/tests/gate_def.rs | 0 .../src/parser/stmt/tests/gphase.rs | 0 .../src/parser/stmt/tests/if_stmt.rs | 0 .../src/parser/stmt/tests/include.rs | 0 .../src/parser/stmt/tests/invalid_stmts.rs | 0 .../parser/stmt/tests/invalid_stmts/branch.rs | 0 .../parser/stmt/tests/invalid_stmts/cal.rs | 0 .../stmt/tests/invalid_stmts/constant.rs | 0 .../parser/stmt/tests/invalid_stmts/decl.rs | 0 .../stmt/tests/invalid_stmts/gate_calls.rs | 0 .../stmt/tests/invalid_stmts/headers.rs | 0 .../src/parser/stmt/tests/invalid_stmts/io.rs | 0 .../parser/stmt/tests/invalid_stmts/loops.rs | 0 .../stmt/tests/invalid_stmts/measure.rs | 0 .../parser/stmt/tests/invalid_stmts/switch.rs | 0 .../parser/stmt/tests/invalid_stmts/tokens.rs | 0 .../src/parser/stmt/tests/io_decl.rs | 0 .../src/parser/stmt/tests/measure.rs | 0 .../src/parser/stmt/tests/old_style_decl.rs | 0 .../src/parser/stmt/tests/pragma.rs | 0 .../src/parser/stmt/tests/quantum_decl.rs | 0 .../src/parser/stmt/tests/reset.rs | 0 .../src/parser/stmt/tests/switch_stmt.rs | 0 .../src/parser/stmt/tests/while_loops.rs | 0 .../src/parser/tests.rs | 0 .../{qsc_qasm3 => qsc_qasm}/src/semantic.rs | 0 .../src/semantic/ast.rs | 0 .../src/semantic/const_eval.rs | 6 +- .../src/semantic/error.rs | 138 +++++++++--------- .../src/semantic/lowerer.rs | 0 .../src/semantic/symbols.rs | 0 .../src/semantic/tests.rs | 12 +- .../src/semantic/tests/assignment.rs | 0 .../src/semantic/tests/decls.rs | 10 +- .../src/semantic/tests/decls/angle.rs | 10 +- .../src/semantic/tests/decls/bit.rs | 0 .../src/semantic/tests/decls/bool.rs | 2 +- .../src/semantic/tests/decls/complex.rs | 0 .../src/semantic/tests/decls/creg.rs | 0 .../src/semantic/tests/decls/duration.rs | 2 +- .../src/semantic/tests/decls/extern_decl.rs | 2 +- .../src/semantic/tests/decls/float.rs | 4 +- .../src/semantic/tests/decls/int.rs | 0 .../src/semantic/tests/decls/qreg.rs | 0 .../src/semantic/tests/decls/stretch.rs | 4 +- .../src/semantic/tests/decls/uint.rs | 0 .../src/semantic/tests/expression.rs | 0 .../src/semantic/tests/expression/binary.rs | 0 .../binary/arithmetic_conversions.rs | 0 .../tests/expression/binary/comparison.rs | 0 .../binary/comparison/bit_to_bit.rs | 0 .../binary/comparison/bool_to_bool.rs | 0 .../binary/comparison/float_to_float.rs | 0 .../binary/comparison/int_to_int.rs | 0 .../binary/comparison/uint_to_uint.rs | 0 .../tests/expression/binary/complex.rs | 0 .../semantic/tests/expression/binary/ident.rs | 0 .../expression/implicit_cast_from_angle.rs | 20 +-- .../expression/implicit_cast_from_bit.rs | 2 +- .../expression/implicit_cast_from_bitarray.rs | 8 +- .../expression/implicit_cast_from_bool.rs | 0 .../expression/implicit_cast_from_float.rs | 4 +- .../expression/implicit_cast_from_int.rs | 0 .../src/semantic/tests/statements.rs | 0 .../src/semantic/tests/statements/box_stmt.rs | 8 +- .../semantic/tests/statements/break_stmt.rs | 6 +- .../tests/statements/continue_stmt.rs | 6 +- .../src/semantic/tests/statements/for_stmt.rs | 2 +- .../src/semantic/tests/statements/if_stmt.rs | 4 +- .../semantic/tests/statements/switch_stmt.rs | 2 +- .../semantic/tests/statements/while_stmt.rs | 0 .../src/semantic/types.rs | 0 .../{qsc_qasm3 => qsc_qasm}/src/stdlib.rs | 0 .../src/stdlib/QasmStd/qsharp.json | 0 .../src/stdlib/QasmStd/src/QasmStd/Angle.qs | 0 .../src/stdlib/QasmStd/src/QasmStd/Convert.qs | 26 ++-- .../stdlib/QasmStd/src/QasmStd/Intrinsic.qs | 0 .../src/stdlib/angle.rs | 0 .../src/stdlib/angle/tests.rs | 0 .../src/stdlib/compile.rs | 0 compiler/{qsc_qasm3 => qsc_qasm}/src/tests.rs | 0 .../src/tests/assignment.rs | 2 +- .../src/tests/assignment/alias.rs | 4 +- .../src/tests/declaration.rs | 0 .../src/tests/declaration/array.rs | 0 .../src/tests/declaration/array/bit.rs | 0 .../src/tests/declaration/array/qubit.rs | 0 .../src/tests/declaration/bit.rs | 0 .../src/tests/declaration/bool.rs | 0 .../src/tests/declaration/complex.rs | 0 .../src/tests/declaration/def.rs | 12 +- .../src/tests/declaration/float.rs | 0 .../src/tests/declaration/gate.rs | 8 +- .../src/tests/declaration/integer.rs | 0 .../src/tests/declaration/io.rs | 0 .../tests/declaration/io/explicit_input.rs | 0 .../tests/declaration/io/explicit_output.rs | 0 .../tests/declaration/io/implicit_output.rs | 0 .../src/tests/declaration/qubit.rs | 0 .../src/tests/declaration/unsigned_integer.rs | 0 .../src/tests/expression.rs | 0 .../src/tests/expression/binary.rs | 0 .../src/tests/expression/binary/angle.rs | 0 .../binary/arithmetic_conversions.rs | 0 .../src/tests/expression/binary/comparison.rs | 14 +- .../src/tests/expression/binary/complex.rs | 0 .../src/tests/expression/binary/ident.rs | 0 .../src/tests/expression/binary/literal.rs | 0 .../binary/literal/multiplication.rs | 0 .../src/tests/expression/bits.rs | 2 +- .../src/tests/expression/function_call.rs | 8 +- .../src/tests/expression/ident.rs | 6 +- .../expression/implicit_cast_from_bit.rs | 0 .../expression/implicit_cast_from_bitarray.rs | 0 .../expression/implicit_cast_from_bool.rs | 0 .../expression/implicit_cast_from_complex.rs | 0 .../expression/implicit_cast_from_float.rs | 0 .../expression/implicit_cast_from_int.rs | 0 .../src/tests/expression/indexed.rs | 0 .../src/tests/expression/unary.rs | 2 +- .../{qsc_qasm3 => qsc_qasm}/src/tests/fuzz.rs | 0 .../src/tests/output.rs | 8 +- .../src/tests/sample_circuits.rs | 0 .../src/tests/sample_circuits/bell_pair.rs | 0 .../tests/sample_circuits/rgqft_multiplier.rs | 0 .../src/tests/scopes.rs | 0 .../src/tests/statement.rs | 0 .../src/tests/statement/annotation.rs | 0 .../src/tests/statement/const_eval.rs | 56 +++---- .../src/tests/statement/end.rs | 0 .../src/tests/statement/for_loop.rs | 0 .../src/tests/statement/gate_call.rs | 8 +- .../src/tests/statement/if_stmt.rs | 0 .../statement/implicit_modified_gate_call.rs | 0 .../src/tests/statement/include.rs | 4 +- .../src/tests/statement/measure.rs | 2 +- .../src/tests/statement/modified_gate_call.rs | 0 .../src/tests/statement/reset.rs | 2 +- .../src/tests/statement/switch.rs | 2 +- .../src/tests/statement/while_loop.rs | 0 compiler/{qsc_qasm3 => qsc_qasm}/src/types.rs | 0 fuzz/Cargo.toml | 4 +- fuzz/fuzz_targets/{qasm3.rs => qasm.rs} | 0 fuzz/seed_inputs/{qasm3 => qasm}/input.qasm | 0 fuzz/seed_inputs/qasm/list.txt | 1 + fuzz/seed_inputs/qasm3/list.txt | 1 - pip/qsharp/_native.pyi | 22 +-- .../interop/qiskit/backends/backend_base.py | 30 ++-- pip/qsharp/interop/qiskit/backends/errors.py | 2 +- .../interop/qiskit/backends/qsharp_backend.py | 11 +- .../interop/qiskit/backends/re_backend.py | 14 +- pip/src/interop.rs | 18 +-- pip/src/interpreter.rs | 16 +- .../interop_qiskit/test_circuits/__init__.py | 10 +- .../interop_qiskit/test_gateset_qasm.py | 2 +- .../interop_qiskit/test_qsharp.py | 2 +- .../interop_qiskit/test_run_sim.py | 4 +- 212 files changed, 330 insertions(+), 333 deletions(-) rename compiler/{qsc_qasm3 => qsc_qasm}/Cargo.toml (94%) rename compiler/{qsc_qasm3 => qsc_qasm}/README.md (94%) rename compiler/{qsc_qasm3 => qsc_qasm}/benches/rgqft_multiplier.rs (98%) rename compiler/{qsc_qasm3 => qsc_qasm}/benches/rgqft_multiplier_1q.qasm (100%) rename compiler/{qsc_qasm3 => qsc_qasm}/benches/rgqft_multiplier_4q.qasm (100%) rename compiler/{qsc_qasm3 => qsc_qasm}/src/ast_builder.rs (100%) rename compiler/{qsc_qasm3 => qsc_qasm}/src/compiler.rs (99%) rename compiler/{qsc_qasm3 => qsc_qasm}/src/compiler/error.rs (77%) rename compiler/{qsc_qasm3 => qsc_qasm}/src/convert.rs (100%) rename compiler/{qsc_qasm3 => qsc_qasm}/src/display_utils.rs (100%) rename compiler/{qsc_qasm3 => qsc_qasm}/src/io.rs (100%) rename compiler/{qsc_qasm3 => qsc_qasm}/src/io/error.rs (100%) rename compiler/{qsc_qasm3 => qsc_qasm}/src/keyword.rs (100%) rename compiler/{qsc_qasm3 => qsc_qasm}/src/lex.rs (100%) rename compiler/{qsc_qasm3 => qsc_qasm}/src/lex/cooked.rs (98%) rename compiler/{qsc_qasm3 => qsc_qasm}/src/lex/cooked/tests.rs (100%) rename compiler/{qsc_qasm3 => qsc_qasm}/src/lex/raw.rs (100%) rename compiler/{qsc_qasm3 => qsc_qasm}/src/lex/raw/tests.rs (100%) rename compiler/{qsc_qasm3 => qsc_qasm}/src/lib.rs (99%) rename compiler/{qsc_qasm3 => qsc_qasm}/src/parser.rs (100%) rename compiler/{qsc_qasm3 => qsc_qasm}/src/parser/ast.rs (100%) rename compiler/{qsc_qasm3 => qsc_qasm}/src/parser/completion.rs (100%) rename compiler/{qsc_qasm3 => qsc_qasm}/src/parser/completion/collector.rs (100%) rename compiler/{qsc_qasm3 => qsc_qasm}/src/parser/completion/tests.rs (100%) rename compiler/{qsc_qasm3 => qsc_qasm}/src/parser/completion/word_kinds.rs (100%) rename compiler/{qsc_qasm3 => qsc_qasm}/src/parser/error.rs (86%) rename compiler/{qsc_qasm3 => qsc_qasm}/src/parser/expr.rs (100%) rename compiler/{qsc_qasm3 => qsc_qasm}/src/parser/expr/tests.rs (100%) rename compiler/{qsc_qasm3 => qsc_qasm}/src/parser/mut_visit.rs (100%) rename compiler/{qsc_qasm3 => qsc_qasm}/src/parser/prgm.rs (100%) rename compiler/{qsc_qasm3 => qsc_qasm}/src/parser/prim.rs (100%) rename compiler/{qsc_qasm3 => qsc_qasm}/src/parser/prim/tests.rs (100%) rename compiler/{qsc_qasm3 => qsc_qasm}/src/parser/scan.rs (100%) rename compiler/{qsc_qasm3 => qsc_qasm}/src/parser/stmt.rs (100%) rename compiler/{qsc_qasm3 => qsc_qasm}/src/parser/stmt/tests.rs (100%) rename compiler/{qsc_qasm3 => qsc_qasm}/src/parser/stmt/tests/alias.rs (100%) rename compiler/{qsc_qasm3 => qsc_qasm}/src/parser/stmt/tests/annotation.rs (100%) rename compiler/{qsc_qasm3 => qsc_qasm}/src/parser/stmt/tests/barrier.rs (100%) rename compiler/{qsc_qasm3 => qsc_qasm}/src/parser/stmt/tests/block.rs (100%) rename compiler/{qsc_qasm3 => qsc_qasm}/src/parser/stmt/tests/box_stmt.rs (100%) rename compiler/{qsc_qasm3 => qsc_qasm}/src/parser/stmt/tests/cal.rs (100%) rename compiler/{qsc_qasm3 => qsc_qasm}/src/parser/stmt/tests/cal_grammar.rs (100%) rename compiler/{qsc_qasm3 => qsc_qasm}/src/parser/stmt/tests/classical_decl.rs (100%) rename compiler/{qsc_qasm3 => qsc_qasm}/src/parser/stmt/tests/def.rs (100%) rename compiler/{qsc_qasm3 => qsc_qasm}/src/parser/stmt/tests/defcal.rs (100%) rename compiler/{qsc_qasm3 => qsc_qasm}/src/parser/stmt/tests/delay.rs (100%) rename compiler/{qsc_qasm3 => qsc_qasm}/src/parser/stmt/tests/expr_stmt.rs (100%) rename compiler/{qsc_qasm3 => qsc_qasm}/src/parser/stmt/tests/extern_decl.rs (100%) rename compiler/{qsc_qasm3 => qsc_qasm}/src/parser/stmt/tests/for_loops.rs (100%) rename compiler/{qsc_qasm3 => qsc_qasm}/src/parser/stmt/tests/gate_call.rs (100%) rename compiler/{qsc_qasm3 => qsc_qasm}/src/parser/stmt/tests/gate_def.rs (100%) rename compiler/{qsc_qasm3 => qsc_qasm}/src/parser/stmt/tests/gphase.rs (100%) rename compiler/{qsc_qasm3 => qsc_qasm}/src/parser/stmt/tests/if_stmt.rs (100%) rename compiler/{qsc_qasm3 => qsc_qasm}/src/parser/stmt/tests/include.rs (100%) rename compiler/{qsc_qasm3 => qsc_qasm}/src/parser/stmt/tests/invalid_stmts.rs (100%) rename compiler/{qsc_qasm3 => qsc_qasm}/src/parser/stmt/tests/invalid_stmts/branch.rs (100%) rename compiler/{qsc_qasm3 => qsc_qasm}/src/parser/stmt/tests/invalid_stmts/cal.rs (100%) rename compiler/{qsc_qasm3 => qsc_qasm}/src/parser/stmt/tests/invalid_stmts/constant.rs (100%) rename compiler/{qsc_qasm3 => qsc_qasm}/src/parser/stmt/tests/invalid_stmts/decl.rs (100%) rename compiler/{qsc_qasm3 => qsc_qasm}/src/parser/stmt/tests/invalid_stmts/gate_calls.rs (100%) rename compiler/{qsc_qasm3 => qsc_qasm}/src/parser/stmt/tests/invalid_stmts/headers.rs (100%) rename compiler/{qsc_qasm3 => qsc_qasm}/src/parser/stmt/tests/invalid_stmts/io.rs (100%) rename compiler/{qsc_qasm3 => qsc_qasm}/src/parser/stmt/tests/invalid_stmts/loops.rs (100%) rename compiler/{qsc_qasm3 => qsc_qasm}/src/parser/stmt/tests/invalid_stmts/measure.rs (100%) rename compiler/{qsc_qasm3 => qsc_qasm}/src/parser/stmt/tests/invalid_stmts/switch.rs (100%) rename compiler/{qsc_qasm3 => qsc_qasm}/src/parser/stmt/tests/invalid_stmts/tokens.rs (100%) rename compiler/{qsc_qasm3 => qsc_qasm}/src/parser/stmt/tests/io_decl.rs (100%) rename compiler/{qsc_qasm3 => qsc_qasm}/src/parser/stmt/tests/measure.rs (100%) rename compiler/{qsc_qasm3 => qsc_qasm}/src/parser/stmt/tests/old_style_decl.rs (100%) rename compiler/{qsc_qasm3 => qsc_qasm}/src/parser/stmt/tests/pragma.rs (100%) rename compiler/{qsc_qasm3 => qsc_qasm}/src/parser/stmt/tests/quantum_decl.rs (100%) rename compiler/{qsc_qasm3 => qsc_qasm}/src/parser/stmt/tests/reset.rs (100%) rename compiler/{qsc_qasm3 => qsc_qasm}/src/parser/stmt/tests/switch_stmt.rs (100%) rename compiler/{qsc_qasm3 => qsc_qasm}/src/parser/stmt/tests/while_loops.rs (100%) rename compiler/{qsc_qasm3 => qsc_qasm}/src/parser/tests.rs (100%) rename compiler/{qsc_qasm3 => qsc_qasm}/src/semantic.rs (100%) rename compiler/{qsc_qasm3 => qsc_qasm}/src/semantic/ast.rs (100%) rename compiler/{qsc_qasm3 => qsc_qasm}/src/semantic/const_eval.rs (99%) rename compiler/{qsc_qasm3 => qsc_qasm}/src/semantic/error.rs (64%) rename compiler/{qsc_qasm3 => qsc_qasm}/src/semantic/lowerer.rs (100%) rename compiler/{qsc_qasm3 => qsc_qasm}/src/semantic/symbols.rs (100%) rename compiler/{qsc_qasm3 => qsc_qasm}/src/semantic/tests.rs (97%) rename compiler/{qsc_qasm3 => qsc_qasm}/src/semantic/tests/assignment.rs (100%) rename compiler/{qsc_qasm3 => qsc_qasm}/src/semantic/tests/decls.rs (92%) rename compiler/{qsc_qasm3 => qsc_qasm}/src/semantic/tests/decls/angle.rs (98%) rename compiler/{qsc_qasm3 => qsc_qasm}/src/semantic/tests/decls/bit.rs (100%) rename compiler/{qsc_qasm3 => qsc_qasm}/src/semantic/tests/decls/bool.rs (98%) rename compiler/{qsc_qasm3 => qsc_qasm}/src/semantic/tests/decls/complex.rs (100%) rename compiler/{qsc_qasm3 => qsc_qasm}/src/semantic/tests/decls/creg.rs (100%) rename compiler/{qsc_qasm3 => qsc_qasm}/src/semantic/tests/decls/duration.rs (95%) rename compiler/{qsc_qasm3 => qsc_qasm}/src/semantic/tests/decls/extern_decl.rs (97%) rename compiler/{qsc_qasm3 => qsc_qasm}/src/semantic/tests/decls/float.rs (99%) rename compiler/{qsc_qasm3 => qsc_qasm}/src/semantic/tests/decls/int.rs (100%) rename compiler/{qsc_qasm3 => qsc_qasm}/src/semantic/tests/decls/qreg.rs (100%) rename compiler/{qsc_qasm3 => qsc_qasm}/src/semantic/tests/decls/stretch.rs (92%) rename compiler/{qsc_qasm3 => qsc_qasm}/src/semantic/tests/decls/uint.rs (100%) rename compiler/{qsc_qasm3 => qsc_qasm}/src/semantic/tests/expression.rs (100%) rename compiler/{qsc_qasm3 => qsc_qasm}/src/semantic/tests/expression/binary.rs (100%) rename compiler/{qsc_qasm3 => qsc_qasm}/src/semantic/tests/expression/binary/arithmetic_conversions.rs (100%) rename compiler/{qsc_qasm3 => qsc_qasm}/src/semantic/tests/expression/binary/comparison.rs (100%) rename compiler/{qsc_qasm3 => qsc_qasm}/src/semantic/tests/expression/binary/comparison/bit_to_bit.rs (100%) rename compiler/{qsc_qasm3 => qsc_qasm}/src/semantic/tests/expression/binary/comparison/bool_to_bool.rs (100%) rename compiler/{qsc_qasm3 => qsc_qasm}/src/semantic/tests/expression/binary/comparison/float_to_float.rs (100%) rename compiler/{qsc_qasm3 => qsc_qasm}/src/semantic/tests/expression/binary/comparison/int_to_int.rs (100%) rename compiler/{qsc_qasm3 => qsc_qasm}/src/semantic/tests/expression/binary/comparison/uint_to_uint.rs (100%) rename compiler/{qsc_qasm3 => qsc_qasm}/src/semantic/tests/expression/binary/complex.rs (100%) rename compiler/{qsc_qasm3 => qsc_qasm}/src/semantic/tests/expression/binary/ident.rs (100%) rename compiler/{qsc_qasm3 => qsc_qasm}/src/semantic/tests/expression/implicit_cast_from_angle.rs (98%) rename compiler/{qsc_qasm3 => qsc_qasm}/src/semantic/tests/expression/implicit_cast_from_bit.rs (99%) rename compiler/{qsc_qasm3 => qsc_qasm}/src/semantic/tests/expression/implicit_cast_from_bitarray.rs (98%) rename compiler/{qsc_qasm3 => qsc_qasm}/src/semantic/tests/expression/implicit_cast_from_bool.rs (100%) rename compiler/{qsc_qasm3 => qsc_qasm}/src/semantic/tests/expression/implicit_cast_from_float.rs (99%) rename compiler/{qsc_qasm3 => qsc_qasm}/src/semantic/tests/expression/implicit_cast_from_int.rs (100%) rename compiler/{qsc_qasm3 => qsc_qasm}/src/semantic/tests/statements.rs (100%) rename compiler/{qsc_qasm3 => qsc_qasm}/src/semantic/tests/statements/box_stmt.rs (89%) rename compiler/{qsc_qasm3 => qsc_qasm}/src/semantic/tests/statements/break_stmt.rs (96%) rename compiler/{qsc_qasm3 => qsc_qasm}/src/semantic/tests/statements/continue_stmt.rs (96%) rename compiler/{qsc_qasm3 => qsc_qasm}/src/semantic/tests/statements/for_stmt.rs (98%) rename compiler/{qsc_qasm3 => qsc_qasm}/src/semantic/tests/statements/if_stmt.rs (98%) rename compiler/{qsc_qasm3 => qsc_qasm}/src/semantic/tests/statements/switch_stmt.rs (98%) rename compiler/{qsc_qasm3 => qsc_qasm}/src/semantic/tests/statements/while_stmt.rs (100%) rename compiler/{qsc_qasm3 => qsc_qasm}/src/semantic/types.rs (100%) rename compiler/{qsc_qasm3 => qsc_qasm}/src/stdlib.rs (100%) rename compiler/{qsc_qasm3 => qsc_qasm}/src/stdlib/QasmStd/qsharp.json (100%) rename compiler/{qsc_qasm3 => qsc_qasm}/src/stdlib/QasmStd/src/QasmStd/Angle.qs (100%) rename compiler/{qsc_qasm3 => qsc_qasm}/src/stdlib/QasmStd/src/QasmStd/Convert.qs (88%) rename compiler/{qsc_qasm3 => qsc_qasm}/src/stdlib/QasmStd/src/QasmStd/Intrinsic.qs (100%) rename compiler/{qsc_qasm3 => qsc_qasm}/src/stdlib/angle.rs (100%) rename compiler/{qsc_qasm3 => qsc_qasm}/src/stdlib/angle/tests.rs (100%) rename compiler/{qsc_qasm3 => qsc_qasm}/src/stdlib/compile.rs (100%) rename compiler/{qsc_qasm3 => qsc_qasm}/src/tests.rs (100%) rename compiler/{qsc_qasm3 => qsc_qasm}/src/tests/assignment.rs (95%) rename compiler/{qsc_qasm3 => qsc_qasm}/src/tests/assignment/alias.rs (92%) rename compiler/{qsc_qasm3 => qsc_qasm}/src/tests/declaration.rs (100%) rename compiler/{qsc_qasm3 => qsc_qasm}/src/tests/declaration/array.rs (100%) rename compiler/{qsc_qasm3 => qsc_qasm}/src/tests/declaration/array/bit.rs (100%) rename compiler/{qsc_qasm3 => qsc_qasm}/src/tests/declaration/array/qubit.rs (100%) rename compiler/{qsc_qasm3 => qsc_qasm}/src/tests/declaration/bit.rs (100%) rename compiler/{qsc_qasm3 => qsc_qasm}/src/tests/declaration/bool.rs (100%) rename compiler/{qsc_qasm3 => qsc_qasm}/src/tests/declaration/complex.rs (100%) rename compiler/{qsc_qasm3 => qsc_qasm}/src/tests/declaration/def.rs (95%) rename compiler/{qsc_qasm3 => qsc_qasm}/src/tests/declaration/float.rs (100%) rename compiler/{qsc_qasm3 => qsc_qasm}/src/tests/declaration/gate.rs (95%) rename compiler/{qsc_qasm3 => qsc_qasm}/src/tests/declaration/integer.rs (100%) rename compiler/{qsc_qasm3 => qsc_qasm}/src/tests/declaration/io.rs (100%) rename compiler/{qsc_qasm3 => qsc_qasm}/src/tests/declaration/io/explicit_input.rs (100%) rename compiler/{qsc_qasm3 => qsc_qasm}/src/tests/declaration/io/explicit_output.rs (100%) rename compiler/{qsc_qasm3 => qsc_qasm}/src/tests/declaration/io/implicit_output.rs (100%) rename compiler/{qsc_qasm3 => qsc_qasm}/src/tests/declaration/qubit.rs (100%) rename compiler/{qsc_qasm3 => qsc_qasm}/src/tests/declaration/unsigned_integer.rs (100%) rename compiler/{qsc_qasm3 => qsc_qasm}/src/tests/expression.rs (100%) rename compiler/{qsc_qasm3 => qsc_qasm}/src/tests/expression/binary.rs (100%) rename compiler/{qsc_qasm3 => qsc_qasm}/src/tests/expression/binary/angle.rs (100%) rename compiler/{qsc_qasm3 => qsc_qasm}/src/tests/expression/binary/arithmetic_conversions.rs (100%) rename compiler/{qsc_qasm3 => qsc_qasm}/src/tests/expression/binary/comparison.rs (97%) rename compiler/{qsc_qasm3 => qsc_qasm}/src/tests/expression/binary/complex.rs (100%) rename compiler/{qsc_qasm3 => qsc_qasm}/src/tests/expression/binary/ident.rs (100%) rename compiler/{qsc_qasm3 => qsc_qasm}/src/tests/expression/binary/literal.rs (100%) rename compiler/{qsc_qasm3 => qsc_qasm}/src/tests/expression/binary/literal/multiplication.rs (100%) rename compiler/{qsc_qasm3 => qsc_qasm}/src/tests/expression/bits.rs (98%) rename compiler/{qsc_qasm3 => qsc_qasm}/src/tests/expression/function_call.rs (97%) rename compiler/{qsc_qasm3 => qsc_qasm}/src/tests/expression/ident.rs (96%) rename compiler/{qsc_qasm3 => qsc_qasm}/src/tests/expression/implicit_cast_from_bit.rs (100%) rename compiler/{qsc_qasm3 => qsc_qasm}/src/tests/expression/implicit_cast_from_bitarray.rs (100%) rename compiler/{qsc_qasm3 => qsc_qasm}/src/tests/expression/implicit_cast_from_bool.rs (100%) rename compiler/{qsc_qasm3 => qsc_qasm}/src/tests/expression/implicit_cast_from_complex.rs (100%) rename compiler/{qsc_qasm3 => qsc_qasm}/src/tests/expression/implicit_cast_from_float.rs (100%) rename compiler/{qsc_qasm3 => qsc_qasm}/src/tests/expression/implicit_cast_from_int.rs (100%) rename compiler/{qsc_qasm3 => qsc_qasm}/src/tests/expression/indexed.rs (100%) rename compiler/{qsc_qasm3 => qsc_qasm}/src/tests/expression/unary.rs (98%) rename compiler/{qsc_qasm3 => qsc_qasm}/src/tests/fuzz.rs (100%) rename compiler/{qsc_qasm3 => qsc_qasm}/src/tests/output.rs (98%) rename compiler/{qsc_qasm3 => qsc_qasm}/src/tests/sample_circuits.rs (100%) rename compiler/{qsc_qasm3 => qsc_qasm}/src/tests/sample_circuits/bell_pair.rs (100%) rename compiler/{qsc_qasm3 => qsc_qasm}/src/tests/sample_circuits/rgqft_multiplier.rs (100%) rename compiler/{qsc_qasm3 => qsc_qasm}/src/tests/scopes.rs (100%) rename compiler/{qsc_qasm3 => qsc_qasm}/src/tests/statement.rs (100%) rename compiler/{qsc_qasm3 => qsc_qasm}/src/tests/statement/annotation.rs (100%) rename compiler/{qsc_qasm3 => qsc_qasm}/src/tests/statement/const_eval.rs (97%) rename compiler/{qsc_qasm3 => qsc_qasm}/src/tests/statement/end.rs (100%) rename compiler/{qsc_qasm3 => qsc_qasm}/src/tests/statement/for_loop.rs (100%) rename compiler/{qsc_qasm3 => qsc_qasm}/src/tests/statement/gate_call.rs (98%) rename compiler/{qsc_qasm3 => qsc_qasm}/src/tests/statement/if_stmt.rs (100%) rename compiler/{qsc_qasm3 => qsc_qasm}/src/tests/statement/implicit_modified_gate_call.rs (100%) rename compiler/{qsc_qasm3 => qsc_qasm}/src/tests/statement/include.rs (99%) rename compiler/{qsc_qasm3 => qsc_qasm}/src/tests/statement/measure.rs (98%) rename compiler/{qsc_qasm3 => qsc_qasm}/src/tests/statement/modified_gate_call.rs (100%) rename compiler/{qsc_qasm3 => qsc_qasm}/src/tests/statement/reset.rs (99%) rename compiler/{qsc_qasm3 => qsc_qasm}/src/tests/statement/switch.rs (99%) rename compiler/{qsc_qasm3 => qsc_qasm}/src/tests/statement/while_loop.rs (100%) rename compiler/{qsc_qasm3 => qsc_qasm}/src/types.rs (100%) rename fuzz/fuzz_targets/{qasm3.rs => qasm.rs} (100%) rename fuzz/seed_inputs/{qasm3 => qasm}/input.qasm (100%) create mode 100644 fuzz/seed_inputs/qasm/list.txt delete mode 100644 fuzz/seed_inputs/qasm3/list.txt diff --git a/.github/workflows/fuzz.yml b/.github/workflows/fuzz.yml index 26938b9963..ef5fb7417a 100644 --- a/.github/workflows/fuzz.yml +++ b/.github/workflows/fuzz.yml @@ -34,7 +34,7 @@ jobs: matrix: os: [ubuntu-latest] # Fuzzing is not supported on Win. The macos is temporarily removed # because of low availability. - target_name: [qsharp, qasm3] + target_name: [qsharp, qasm] runs-on: ${{ matrix.os }} permissions: diff --git a/Cargo.lock b/Cargo.lock index 4d9c5ae568..615b59090d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1011,7 +1011,7 @@ dependencies = [ "qsc_partial_eval", "qsc_passes", "qsc_project", - "qsc_qasm3", + "qsc_qasm", "qsc_rca", "rustc-hash", "thiserror", @@ -1276,7 +1276,7 @@ dependencies = [ ] [[package]] -name = "qsc_qasm3" +name = "qsc_qasm" version = "0.0.0" dependencies = [ "bitflags", @@ -1297,7 +1297,7 @@ dependencies = [ "qsc_hir", "qsc_parse", "qsc_passes", - "qsc_qasm3", + "qsc_qasm", "rustc-hash", "thiserror", ] diff --git a/Cargo.toml b/Cargo.toml index a41f5caaf5..17336fd13a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -18,7 +18,7 @@ members = [ "compiler/qsc_partial_eval", "compiler/qsc_passes", "compiler/qsc_project", - "compiler/qsc_qasm3", + "compiler/qsc_qasm", "compiler/qsc_rir", "fuzz", "katas", diff --git a/compiler/qsc/Cargo.toml b/compiler/qsc/Cargo.toml index 0ae849b56c..3b90ba5d99 100644 --- a/compiler/qsc/Cargo.toml +++ b/compiler/qsc/Cargo.toml @@ -31,7 +31,7 @@ qsc_passes = { path = "../qsc_passes" } qsc_parse = { path = "../qsc_parse" } qsc_partial_eval = { path = "../qsc_partial_eval" } qsc_project = { path = "../qsc_project", features = ["fs"] } -qsc_qasm3 = { path = "../qsc_qasm3", features = ["fs"] } +qsc_qasm = { path = "../qsc_qasm", features = ["fs"] } qsc_rca = { path = "../qsc_rca" } qsc_circuit = { path = "../qsc_circuit" } rustc-hash = { workspace = true } diff --git a/compiler/qsc/src/qasm.rs b/compiler/qsc/src/qasm.rs index 67c1d6db58..a9d5bf1f56 100644 --- a/compiler/qsc/src/qasm.rs +++ b/compiler/qsc/src/qasm.rs @@ -3,28 +3,26 @@ use std::path::Path; -use qsc_qasm3::io::SourceResolver; -pub use qsc_qasm3::CompilerConfig; -pub use qsc_qasm3::OperationSignature; -pub use qsc_qasm3::OutputSemantics; -pub use qsc_qasm3::ProgramType; -pub use qsc_qasm3::QasmCompileUnit; -pub use qsc_qasm3::QubitSemantics; +use qsc_qasm::io::SourceResolver; +pub use qsc_qasm::{ + CompilerConfig, OperationSignature, OutputSemantics, ProgramType, QasmCompileUnit, + QubitSemantics, +}; pub mod io { - pub use qsc_qasm3::io::*; + pub use qsc_qasm::io::*; } pub mod parser { - pub use qsc_qasm3::parser::*; + pub use qsc_qasm::parser::*; } pub mod error { - pub use qsc_qasm3::Error; - pub use qsc_qasm3::ErrorKind; + pub use qsc_qasm::Error; + pub use qsc_qasm::ErrorKind; } pub mod completion { - pub use qsc_qasm3::parser::completion::*; + pub use qsc_qasm::parser::completion::*; } -pub use qsc_qasm3::compile_to_qsharp_ast_with_config; -pub use qsc_qasm3::package_store_with_qasm; +pub use qsc_qasm::compile_to_qsharp_ast_with_config; +pub use qsc_qasm::package_store_with_qasm; #[must_use] pub fn parse_raw_qasm_as_fragments( diff --git a/compiler/qsc_qasm3/Cargo.toml b/compiler/qsc_qasm/Cargo.toml similarity index 94% rename from compiler/qsc_qasm3/Cargo.toml rename to compiler/qsc_qasm/Cargo.toml index c86dab58b2..9c38f943b0 100644 --- a/compiler/qsc_qasm3/Cargo.toml +++ b/compiler/qsc_qasm/Cargo.toml @@ -1,5 +1,5 @@ [package] -name = "qsc_qasm3" +name = "qsc_qasm" authors.workspace = true homepage.workspace = true repository.workspace = true @@ -32,7 +32,7 @@ miette = { workspace = true, features = ["fancy"] } # Self import adding fs feature so that we can test # loading qasm from file. qsc = { path = "../qsc" } -qsc_qasm3 = { path = ".", features = ["fs"] } +qsc_qasm = { path = ".", features = ["fs"] } qsc_codegen = { path = "../qsc_codegen" } [features] diff --git a/compiler/qsc_qasm3/README.md b/compiler/qsc_qasm/README.md similarity index 94% rename from compiler/qsc_qasm3/README.md rename to compiler/qsc_qasm/README.md index 5850c75884..d03c5cc069 100644 --- a/compiler/qsc_qasm3/README.md +++ b/compiler/qsc_qasm/README.md @@ -1,6 +1,6 @@ -# Q# QASM3 Compiler +# Q# QASM Compiler -This crate implements a semantic transformation from OpenQASM 3 to Q#. At a high level it parses the OpenQASM program (and all includes) into an AST. Once this AST is parsed, it is compiled into Q#'s AST. +This crate implements a semantic transformation from OpenQASM to Q#. At a high level it parses the OpenQASM program (and all includes) into an AST. Once this AST is parsed, it is compiled into Q#'s AST. Once the compiler gets to the AST phase, it no longer cares that it is processing Q#. At this point all input is indistinguishable from having been given Q# as input. This allows us to leverage capability analysis, runtime targeting, residual computation, partial evaluation, and code generation to the input. diff --git a/compiler/qsc_qasm3/benches/rgqft_multiplier.rs b/compiler/qsc_qasm/benches/rgqft_multiplier.rs similarity index 98% rename from compiler/qsc_qasm3/benches/rgqft_multiplier.rs rename to compiler/qsc_qasm/benches/rgqft_multiplier.rs index cbf883be5a..62205d9280 100644 --- a/compiler/qsc_qasm3/benches/rgqft_multiplier.rs +++ b/compiler/qsc_qasm/benches/rgqft_multiplier.rs @@ -2,7 +2,7 @@ // Licensed under the MIT License. use criterion::{black_box, criterion_group, criterion_main, Criterion}; -use qsc_qasm3::{ +use qsc_qasm::{ compile_to_qsharp_ast_with_config, io::InMemorySourceResolver, CompilerConfig, OutputSemantics, ProgramType, QasmCompileUnit, QubitSemantics, }; diff --git a/compiler/qsc_qasm3/benches/rgqft_multiplier_1q.qasm b/compiler/qsc_qasm/benches/rgqft_multiplier_1q.qasm similarity index 100% rename from compiler/qsc_qasm3/benches/rgqft_multiplier_1q.qasm rename to compiler/qsc_qasm/benches/rgqft_multiplier_1q.qasm diff --git a/compiler/qsc_qasm3/benches/rgqft_multiplier_4q.qasm b/compiler/qsc_qasm/benches/rgqft_multiplier_4q.qasm similarity index 100% rename from compiler/qsc_qasm3/benches/rgqft_multiplier_4q.qasm rename to compiler/qsc_qasm/benches/rgqft_multiplier_4q.qasm diff --git a/compiler/qsc_qasm3/src/ast_builder.rs b/compiler/qsc_qasm/src/ast_builder.rs similarity index 100% rename from compiler/qsc_qasm3/src/ast_builder.rs rename to compiler/qsc_qasm/src/ast_builder.rs diff --git a/compiler/qsc_qasm3/src/compiler.rs b/compiler/qsc_qasm/src/compiler.rs similarity index 99% rename from compiler/qsc_qasm3/src/compiler.rs rename to compiler/qsc_qasm/src/compiler.rs index cb3dfd13c0..02201406d6 100644 --- a/compiler/qsc_qasm3/src/compiler.rs +++ b/compiler/qsc_qasm/src/compiler.rs @@ -693,7 +693,7 @@ impl QasmCompiler { .filter_map(|annotation| self.compile_annotation(annotation)); // We use the same primitives used for declaring gates, because def declarations - // in QASM3 can take qubits as arguments and call quantum gates. + // in QASM can take qubits as arguments and call quantum gates. Some(build_function_or_operation( name, cargs, diff --git a/compiler/qsc_qasm3/src/compiler/error.rs b/compiler/qsc_qasm/src/compiler/error.rs similarity index 77% rename from compiler/qsc_qasm3/src/compiler/error.rs rename to compiler/qsc_qasm/src/compiler/error.rs index ef665717a5..b0a70b160a 100644 --- a/compiler/qsc_qasm3/src/compiler/error.rs +++ b/compiler/qsc_qasm/src/compiler/error.rs @@ -15,22 +15,22 @@ pub struct Error(pub CompilerErrorKind); #[derive(Clone, Debug, Diagnostic, Eq, Error, PartialEq)] pub enum CompilerErrorKind { #[error("annotations only valid on def and gate statements")] - #[diagnostic(code("Qsc.Qasm3.Compiler.InvalidAnnotationTarget"))] + #[diagnostic(code("Qasm.Compiler.InvalidAnnotationTarget"))] InvalidAnnotationTarget(#[label] Span), #[error("gate expects {0} qubit arguments, but {1} were provided")] - #[diagnostic(code("Qsc.Qasm3.Compiler.InvalidNumberOfQubitArgs"))] + #[diagnostic(code("Qasm.Compiler.InvalidNumberOfQubitArgs"))] InvalidNumberOfQubitArgs(usize, usize, #[label] Span), #[error("{0} are not supported")] - #[diagnostic(code("Qsc.Qasm3.Compiler.NotSupported"))] + #[diagnostic(code("Qasm.Compiler.NotSupported"))] NotSupported(String, #[label] Span), #[error("Qiskit circuits must have output registers")] - #[diagnostic(code("Qsc.Qasm3.Compiler.QiskitEntryPointMissingOutput"))] + #[diagnostic(code("Qasm.Compiler.QiskitEntryPointMissingOutput"))] QiskitEntryPointMissingOutput(#[label] Span), #[error("unexpected annotation: {0}")] - #[diagnostic(code("Qsc.Qasm3.Compiler.UnknownAnnotation"))] + #[diagnostic(code("Qasm.Compiler.UnknownAnnotation"))] UnknownAnnotation(String, #[label] Span), #[error("this statement is not yet handled during OpenQASM 3 import: {0}")] - #[diagnostic(code("Qsc.Qasm3.Compiler.Unimplemented"))] + #[diagnostic(code("Qasm.Compiler.Unimplemented"))] Unimplemented(String, #[label] Span), } diff --git a/compiler/qsc_qasm3/src/convert.rs b/compiler/qsc_qasm/src/convert.rs similarity index 100% rename from compiler/qsc_qasm3/src/convert.rs rename to compiler/qsc_qasm/src/convert.rs diff --git a/compiler/qsc_qasm3/src/display_utils.rs b/compiler/qsc_qasm/src/display_utils.rs similarity index 100% rename from compiler/qsc_qasm3/src/display_utils.rs rename to compiler/qsc_qasm/src/display_utils.rs diff --git a/compiler/qsc_qasm3/src/io.rs b/compiler/qsc_qasm/src/io.rs similarity index 100% rename from compiler/qsc_qasm3/src/io.rs rename to compiler/qsc_qasm/src/io.rs diff --git a/compiler/qsc_qasm3/src/io/error.rs b/compiler/qsc_qasm/src/io/error.rs similarity index 100% rename from compiler/qsc_qasm3/src/io/error.rs rename to compiler/qsc_qasm/src/io/error.rs diff --git a/compiler/qsc_qasm3/src/keyword.rs b/compiler/qsc_qasm/src/keyword.rs similarity index 100% rename from compiler/qsc_qasm3/src/keyword.rs rename to compiler/qsc_qasm/src/keyword.rs diff --git a/compiler/qsc_qasm3/src/lex.rs b/compiler/qsc_qasm/src/lex.rs similarity index 100% rename from compiler/qsc_qasm3/src/lex.rs rename to compiler/qsc_qasm/src/lex.rs diff --git a/compiler/qsc_qasm3/src/lex/cooked.rs b/compiler/qsc_qasm/src/lex/cooked.rs similarity index 98% rename from compiler/qsc_qasm3/src/lex/cooked.rs rename to compiler/qsc_qasm/src/lex/cooked.rs index 68e445c317..5f0abbea92 100644 --- a/compiler/qsc_qasm3/src/lex/cooked.rs +++ b/compiler/qsc_qasm/src/lex/cooked.rs @@ -38,23 +38,23 @@ pub(crate) struct Token { #[derive(Clone, Copy, Debug, Diagnostic, Eq, Error, PartialEq)] pub enum Error { #[error("expected {0} to complete {1}, found {2}")] - #[diagnostic(code("Qasm3.Lex.Incomplete"))] + #[diagnostic(code("Qasm.Lex.Incomplete"))] Incomplete(raw::TokenKind, TokenKind, raw::TokenKind, #[label] Span), #[error("expected {0} to complete {1}, found EOF")] - #[diagnostic(code("Qasm3.Lex.IncompleteEof"))] + #[diagnostic(code("Qasm.Lex.IncompleteEof"))] IncompleteEof(raw::TokenKind, TokenKind, #[label] Span), #[error("unterminated string literal")] - #[diagnostic(code("Qasm3.Lex.UnterminatedString"))] + #[diagnostic(code("Qasm.Lex.UnterminatedString"))] UnterminatedString(#[label] Span), #[error("string literal with an invalid escape sequence")] - #[diagnostic(code("Qasm3.Lex.InvalidEscapeSequence"))] + #[diagnostic(code("Qasm.Lex.InvalidEscapeSequence"))] InvalidEscapeSequence(#[label] Span), #[error("unrecognized character `{0}`")] - #[diagnostic(code("Qasm3.Lex.UnknownChar"))] + #[diagnostic(code("Qasm.Lex.UnknownChar"))] Unknown(char, #[label] Span), } diff --git a/compiler/qsc_qasm3/src/lex/cooked/tests.rs b/compiler/qsc_qasm/src/lex/cooked/tests.rs similarity index 100% rename from compiler/qsc_qasm3/src/lex/cooked/tests.rs rename to compiler/qsc_qasm/src/lex/cooked/tests.rs diff --git a/compiler/qsc_qasm3/src/lex/raw.rs b/compiler/qsc_qasm/src/lex/raw.rs similarity index 100% rename from compiler/qsc_qasm3/src/lex/raw.rs rename to compiler/qsc_qasm/src/lex/raw.rs diff --git a/compiler/qsc_qasm3/src/lex/raw/tests.rs b/compiler/qsc_qasm/src/lex/raw/tests.rs similarity index 100% rename from compiler/qsc_qasm3/src/lex/raw/tests.rs rename to compiler/qsc_qasm/src/lex/raw/tests.rs diff --git a/compiler/qsc_qasm3/src/lib.rs b/compiler/qsc_qasm/src/lib.rs similarity index 99% rename from compiler/qsc_qasm3/src/lib.rs rename to compiler/qsc_qasm/src/lib.rs index de20197d4d..65ecefce82 100644 --- a/compiler/qsc_qasm3/src/lib.rs +++ b/compiler/qsc_qasm/src/lib.rs @@ -127,7 +127,7 @@ impl CompilerConfig { fn namespace(&self) -> Arc { self.namespace .clone() - .unwrap_or_else(|| Arc::from("qasm3_import")) + .unwrap_or_else(|| Arc::from("qasm_import")) } } diff --git a/compiler/qsc_qasm3/src/parser.rs b/compiler/qsc_qasm/src/parser.rs similarity index 100% rename from compiler/qsc_qasm3/src/parser.rs rename to compiler/qsc_qasm/src/parser.rs diff --git a/compiler/qsc_qasm3/src/parser/ast.rs b/compiler/qsc_qasm/src/parser/ast.rs similarity index 100% rename from compiler/qsc_qasm3/src/parser/ast.rs rename to compiler/qsc_qasm/src/parser/ast.rs diff --git a/compiler/qsc_qasm3/src/parser/completion.rs b/compiler/qsc_qasm/src/parser/completion.rs similarity index 100% rename from compiler/qsc_qasm3/src/parser/completion.rs rename to compiler/qsc_qasm/src/parser/completion.rs diff --git a/compiler/qsc_qasm3/src/parser/completion/collector.rs b/compiler/qsc_qasm/src/parser/completion/collector.rs similarity index 100% rename from compiler/qsc_qasm3/src/parser/completion/collector.rs rename to compiler/qsc_qasm/src/parser/completion/collector.rs diff --git a/compiler/qsc_qasm3/src/parser/completion/tests.rs b/compiler/qsc_qasm/src/parser/completion/tests.rs similarity index 100% rename from compiler/qsc_qasm3/src/parser/completion/tests.rs rename to compiler/qsc_qasm/src/parser/completion/tests.rs diff --git a/compiler/qsc_qasm3/src/parser/completion/word_kinds.rs b/compiler/qsc_qasm/src/parser/completion/word_kinds.rs similarity index 100% rename from compiler/qsc_qasm3/src/parser/completion/word_kinds.rs rename to compiler/qsc_qasm/src/parser/completion/word_kinds.rs diff --git a/compiler/qsc_qasm3/src/parser/error.rs b/compiler/qsc_qasm/src/parser/error.rs similarity index 86% rename from compiler/qsc_qasm3/src/parser/error.rs rename to compiler/qsc_qasm/src/parser/error.rs index 1897374012..c97f8a6fa3 100644 --- a/compiler/qsc_qasm3/src/parser/error.rs +++ b/compiler/qsc_qasm/src/parser/error.rs @@ -87,55 +87,55 @@ pub enum ErrorKind { #[diagnostic(transparent)] Lex(lex::Error), #[error("invalid {0} literal")] - #[diagnostic(code("Qasm3.Parse.Literal"))] + #[diagnostic(code("Qasm.Parse.Literal"))] Lit(&'static str, #[label] Span), #[error("unknown escape sequence: `{0}`")] - #[diagnostic(code("Qasm3.Parse.Escape"))] + #[diagnostic(code("Qasm.Parse.Escape"))] Escape(char, #[label] Span), #[error("expected {0}, found {1}")] - #[diagnostic(code("Qasm3.Parse.Token"))] + #[diagnostic(code("Qasm.Parse.Token"))] Token(TokenKind, TokenKind, #[label] Span), #[error("Empty statements are not supported")] - #[diagnostic(code("Qasm3.Parse.EmptyStatement"))] + #[diagnostic(code("Qasm.Parse.EmptyStatement"))] EmptyStatement(#[label] Span), #[error("Annotation missing target statement.")] - #[diagnostic(code("Qasm3.Parse.FloatingAnnotation"))] + #[diagnostic(code("Qasm.Parse.FloatingAnnotation"))] FloatingAnnotation(#[label] Span), #[error("expected {0}, found {1}")] - #[diagnostic(code("Qasm3.Parse.Rule"))] + #[diagnostic(code("Qasm.Parse.Rule"))] Rule(&'static str, TokenKind, #[label] Span), #[error("expected {0}, found {1}")] - #[diagnostic(code("Qasm3.Parse.Convert"))] + #[diagnostic(code("Qasm.Parse.Convert"))] Convert(&'static str, &'static str, #[label] Span), #[error("expected statement to end with a semicolon")] - #[diagnostic(code("Qasm3.Parse.MissingSemi"))] + #[diagnostic(code("Qasm.Parse.MissingSemi"))] MissingSemi(#[label] Span), #[error("expected inputs to be parenthesized")] - #[diagnostic(code("Qasm3.Parse.MissingParens"))] + #[diagnostic(code("Qasm.Parse.MissingParens"))] MissingParens(#[label] Span), #[error("missing entry in sequence")] - #[diagnostic(code("Qasm3.Parse.MissingSeqEntry"))] + #[diagnostic(code("Qasm.Parse.MissingSeqEntry"))] MissingSeqEntry(#[label] Span), #[error("missing switch statement cases")] - #[diagnostic(code("Qasm3.Parse.MissingSwitchCases"))] + #[diagnostic(code("Qasm.Parse.MissingSwitchCases"))] MissingSwitchCases(#[label] Span), #[error("missing switch statement case labels")] - #[diagnostic(code("Qasm3.Parse.MissingSwitchCaseLabels"))] + #[diagnostic(code("Qasm.Parse.MissingSwitchCaseLabels"))] MissingSwitchCaseLabels(#[label] Span), #[error("missing gate call operands")] - #[diagnostic(code("Qasm3.Parse.MissingGateCallOperands"))] + #[diagnostic(code("Qasm.Parse.MissingGateCallOperands"))] MissingGateCallOperands(#[label] Span), #[error("expected an item or closing brace, found {0}")] - #[diagnostic(code("Qasm3.Parse.ExpectedItem"))] + #[diagnostic(code("Qasm.Parse.ExpectedItem"))] ExpectedItem(TokenKind, #[label] Span), #[error("gphase gate requires exactly one angle")] - #[diagnostic(code("Qasm3.Parse.GPhaseInvalidArguments"))] + #[diagnostic(code("Qasm.Parse.GPhaseInvalidArguments"))] GPhaseInvalidArguments(#[label] Span), #[error("invalid gate call designator")] - #[diagnostic(code("Qasm3.Parse.InvalidGateCallDesignator"))] + #[diagnostic(code("Qasm.Parse.InvalidGateCallDesignator"))] InvalidGateCallDesignator(#[label] Span), #[error("multiple index operators are only allowed in assignments")] - #[diagnostic(code("Qasm3.Parse.MultipleIndexOperators"))] + #[diagnostic(code("Qasm.Parse.MultipleIndexOperators"))] MultipleIndexOperators(#[label] Span), #[error(transparent)] #[diagnostic(transparent)] diff --git a/compiler/qsc_qasm3/src/parser/expr.rs b/compiler/qsc_qasm/src/parser/expr.rs similarity index 100% rename from compiler/qsc_qasm3/src/parser/expr.rs rename to compiler/qsc_qasm/src/parser/expr.rs diff --git a/compiler/qsc_qasm3/src/parser/expr/tests.rs b/compiler/qsc_qasm/src/parser/expr/tests.rs similarity index 100% rename from compiler/qsc_qasm3/src/parser/expr/tests.rs rename to compiler/qsc_qasm/src/parser/expr/tests.rs diff --git a/compiler/qsc_qasm3/src/parser/mut_visit.rs b/compiler/qsc_qasm/src/parser/mut_visit.rs similarity index 100% rename from compiler/qsc_qasm3/src/parser/mut_visit.rs rename to compiler/qsc_qasm/src/parser/mut_visit.rs diff --git a/compiler/qsc_qasm3/src/parser/prgm.rs b/compiler/qsc_qasm/src/parser/prgm.rs similarity index 100% rename from compiler/qsc_qasm3/src/parser/prgm.rs rename to compiler/qsc_qasm/src/parser/prgm.rs diff --git a/compiler/qsc_qasm3/src/parser/prim.rs b/compiler/qsc_qasm/src/parser/prim.rs similarity index 100% rename from compiler/qsc_qasm3/src/parser/prim.rs rename to compiler/qsc_qasm/src/parser/prim.rs diff --git a/compiler/qsc_qasm3/src/parser/prim/tests.rs b/compiler/qsc_qasm/src/parser/prim/tests.rs similarity index 100% rename from compiler/qsc_qasm3/src/parser/prim/tests.rs rename to compiler/qsc_qasm/src/parser/prim/tests.rs diff --git a/compiler/qsc_qasm3/src/parser/scan.rs b/compiler/qsc_qasm/src/parser/scan.rs similarity index 100% rename from compiler/qsc_qasm3/src/parser/scan.rs rename to compiler/qsc_qasm/src/parser/scan.rs diff --git a/compiler/qsc_qasm3/src/parser/stmt.rs b/compiler/qsc_qasm/src/parser/stmt.rs similarity index 100% rename from compiler/qsc_qasm3/src/parser/stmt.rs rename to compiler/qsc_qasm/src/parser/stmt.rs diff --git a/compiler/qsc_qasm3/src/parser/stmt/tests.rs b/compiler/qsc_qasm/src/parser/stmt/tests.rs similarity index 100% rename from compiler/qsc_qasm3/src/parser/stmt/tests.rs rename to compiler/qsc_qasm/src/parser/stmt/tests.rs diff --git a/compiler/qsc_qasm3/src/parser/stmt/tests/alias.rs b/compiler/qsc_qasm/src/parser/stmt/tests/alias.rs similarity index 100% rename from compiler/qsc_qasm3/src/parser/stmt/tests/alias.rs rename to compiler/qsc_qasm/src/parser/stmt/tests/alias.rs diff --git a/compiler/qsc_qasm3/src/parser/stmt/tests/annotation.rs b/compiler/qsc_qasm/src/parser/stmt/tests/annotation.rs similarity index 100% rename from compiler/qsc_qasm3/src/parser/stmt/tests/annotation.rs rename to compiler/qsc_qasm/src/parser/stmt/tests/annotation.rs diff --git a/compiler/qsc_qasm3/src/parser/stmt/tests/barrier.rs b/compiler/qsc_qasm/src/parser/stmt/tests/barrier.rs similarity index 100% rename from compiler/qsc_qasm3/src/parser/stmt/tests/barrier.rs rename to compiler/qsc_qasm/src/parser/stmt/tests/barrier.rs diff --git a/compiler/qsc_qasm3/src/parser/stmt/tests/block.rs b/compiler/qsc_qasm/src/parser/stmt/tests/block.rs similarity index 100% rename from compiler/qsc_qasm3/src/parser/stmt/tests/block.rs rename to compiler/qsc_qasm/src/parser/stmt/tests/block.rs diff --git a/compiler/qsc_qasm3/src/parser/stmt/tests/box_stmt.rs b/compiler/qsc_qasm/src/parser/stmt/tests/box_stmt.rs similarity index 100% rename from compiler/qsc_qasm3/src/parser/stmt/tests/box_stmt.rs rename to compiler/qsc_qasm/src/parser/stmt/tests/box_stmt.rs diff --git a/compiler/qsc_qasm3/src/parser/stmt/tests/cal.rs b/compiler/qsc_qasm/src/parser/stmt/tests/cal.rs similarity index 100% rename from compiler/qsc_qasm3/src/parser/stmt/tests/cal.rs rename to compiler/qsc_qasm/src/parser/stmt/tests/cal.rs diff --git a/compiler/qsc_qasm3/src/parser/stmt/tests/cal_grammar.rs b/compiler/qsc_qasm/src/parser/stmt/tests/cal_grammar.rs similarity index 100% rename from compiler/qsc_qasm3/src/parser/stmt/tests/cal_grammar.rs rename to compiler/qsc_qasm/src/parser/stmt/tests/cal_grammar.rs diff --git a/compiler/qsc_qasm3/src/parser/stmt/tests/classical_decl.rs b/compiler/qsc_qasm/src/parser/stmt/tests/classical_decl.rs similarity index 100% rename from compiler/qsc_qasm3/src/parser/stmt/tests/classical_decl.rs rename to compiler/qsc_qasm/src/parser/stmt/tests/classical_decl.rs diff --git a/compiler/qsc_qasm3/src/parser/stmt/tests/def.rs b/compiler/qsc_qasm/src/parser/stmt/tests/def.rs similarity index 100% rename from compiler/qsc_qasm3/src/parser/stmt/tests/def.rs rename to compiler/qsc_qasm/src/parser/stmt/tests/def.rs diff --git a/compiler/qsc_qasm3/src/parser/stmt/tests/defcal.rs b/compiler/qsc_qasm/src/parser/stmt/tests/defcal.rs similarity index 100% rename from compiler/qsc_qasm3/src/parser/stmt/tests/defcal.rs rename to compiler/qsc_qasm/src/parser/stmt/tests/defcal.rs diff --git a/compiler/qsc_qasm3/src/parser/stmt/tests/delay.rs b/compiler/qsc_qasm/src/parser/stmt/tests/delay.rs similarity index 100% rename from compiler/qsc_qasm3/src/parser/stmt/tests/delay.rs rename to compiler/qsc_qasm/src/parser/stmt/tests/delay.rs diff --git a/compiler/qsc_qasm3/src/parser/stmt/tests/expr_stmt.rs b/compiler/qsc_qasm/src/parser/stmt/tests/expr_stmt.rs similarity index 100% rename from compiler/qsc_qasm3/src/parser/stmt/tests/expr_stmt.rs rename to compiler/qsc_qasm/src/parser/stmt/tests/expr_stmt.rs diff --git a/compiler/qsc_qasm3/src/parser/stmt/tests/extern_decl.rs b/compiler/qsc_qasm/src/parser/stmt/tests/extern_decl.rs similarity index 100% rename from compiler/qsc_qasm3/src/parser/stmt/tests/extern_decl.rs rename to compiler/qsc_qasm/src/parser/stmt/tests/extern_decl.rs diff --git a/compiler/qsc_qasm3/src/parser/stmt/tests/for_loops.rs b/compiler/qsc_qasm/src/parser/stmt/tests/for_loops.rs similarity index 100% rename from compiler/qsc_qasm3/src/parser/stmt/tests/for_loops.rs rename to compiler/qsc_qasm/src/parser/stmt/tests/for_loops.rs diff --git a/compiler/qsc_qasm3/src/parser/stmt/tests/gate_call.rs b/compiler/qsc_qasm/src/parser/stmt/tests/gate_call.rs similarity index 100% rename from compiler/qsc_qasm3/src/parser/stmt/tests/gate_call.rs rename to compiler/qsc_qasm/src/parser/stmt/tests/gate_call.rs diff --git a/compiler/qsc_qasm3/src/parser/stmt/tests/gate_def.rs b/compiler/qsc_qasm/src/parser/stmt/tests/gate_def.rs similarity index 100% rename from compiler/qsc_qasm3/src/parser/stmt/tests/gate_def.rs rename to compiler/qsc_qasm/src/parser/stmt/tests/gate_def.rs diff --git a/compiler/qsc_qasm3/src/parser/stmt/tests/gphase.rs b/compiler/qsc_qasm/src/parser/stmt/tests/gphase.rs similarity index 100% rename from compiler/qsc_qasm3/src/parser/stmt/tests/gphase.rs rename to compiler/qsc_qasm/src/parser/stmt/tests/gphase.rs diff --git a/compiler/qsc_qasm3/src/parser/stmt/tests/if_stmt.rs b/compiler/qsc_qasm/src/parser/stmt/tests/if_stmt.rs similarity index 100% rename from compiler/qsc_qasm3/src/parser/stmt/tests/if_stmt.rs rename to compiler/qsc_qasm/src/parser/stmt/tests/if_stmt.rs diff --git a/compiler/qsc_qasm3/src/parser/stmt/tests/include.rs b/compiler/qsc_qasm/src/parser/stmt/tests/include.rs similarity index 100% rename from compiler/qsc_qasm3/src/parser/stmt/tests/include.rs rename to compiler/qsc_qasm/src/parser/stmt/tests/include.rs diff --git a/compiler/qsc_qasm3/src/parser/stmt/tests/invalid_stmts.rs b/compiler/qsc_qasm/src/parser/stmt/tests/invalid_stmts.rs similarity index 100% rename from compiler/qsc_qasm3/src/parser/stmt/tests/invalid_stmts.rs rename to compiler/qsc_qasm/src/parser/stmt/tests/invalid_stmts.rs diff --git a/compiler/qsc_qasm3/src/parser/stmt/tests/invalid_stmts/branch.rs b/compiler/qsc_qasm/src/parser/stmt/tests/invalid_stmts/branch.rs similarity index 100% rename from compiler/qsc_qasm3/src/parser/stmt/tests/invalid_stmts/branch.rs rename to compiler/qsc_qasm/src/parser/stmt/tests/invalid_stmts/branch.rs diff --git a/compiler/qsc_qasm3/src/parser/stmt/tests/invalid_stmts/cal.rs b/compiler/qsc_qasm/src/parser/stmt/tests/invalid_stmts/cal.rs similarity index 100% rename from compiler/qsc_qasm3/src/parser/stmt/tests/invalid_stmts/cal.rs rename to compiler/qsc_qasm/src/parser/stmt/tests/invalid_stmts/cal.rs diff --git a/compiler/qsc_qasm3/src/parser/stmt/tests/invalid_stmts/constant.rs b/compiler/qsc_qasm/src/parser/stmt/tests/invalid_stmts/constant.rs similarity index 100% rename from compiler/qsc_qasm3/src/parser/stmt/tests/invalid_stmts/constant.rs rename to compiler/qsc_qasm/src/parser/stmt/tests/invalid_stmts/constant.rs diff --git a/compiler/qsc_qasm3/src/parser/stmt/tests/invalid_stmts/decl.rs b/compiler/qsc_qasm/src/parser/stmt/tests/invalid_stmts/decl.rs similarity index 100% rename from compiler/qsc_qasm3/src/parser/stmt/tests/invalid_stmts/decl.rs rename to compiler/qsc_qasm/src/parser/stmt/tests/invalid_stmts/decl.rs diff --git a/compiler/qsc_qasm3/src/parser/stmt/tests/invalid_stmts/gate_calls.rs b/compiler/qsc_qasm/src/parser/stmt/tests/invalid_stmts/gate_calls.rs similarity index 100% rename from compiler/qsc_qasm3/src/parser/stmt/tests/invalid_stmts/gate_calls.rs rename to compiler/qsc_qasm/src/parser/stmt/tests/invalid_stmts/gate_calls.rs diff --git a/compiler/qsc_qasm3/src/parser/stmt/tests/invalid_stmts/headers.rs b/compiler/qsc_qasm/src/parser/stmt/tests/invalid_stmts/headers.rs similarity index 100% rename from compiler/qsc_qasm3/src/parser/stmt/tests/invalid_stmts/headers.rs rename to compiler/qsc_qasm/src/parser/stmt/tests/invalid_stmts/headers.rs diff --git a/compiler/qsc_qasm3/src/parser/stmt/tests/invalid_stmts/io.rs b/compiler/qsc_qasm/src/parser/stmt/tests/invalid_stmts/io.rs similarity index 100% rename from compiler/qsc_qasm3/src/parser/stmt/tests/invalid_stmts/io.rs rename to compiler/qsc_qasm/src/parser/stmt/tests/invalid_stmts/io.rs diff --git a/compiler/qsc_qasm3/src/parser/stmt/tests/invalid_stmts/loops.rs b/compiler/qsc_qasm/src/parser/stmt/tests/invalid_stmts/loops.rs similarity index 100% rename from compiler/qsc_qasm3/src/parser/stmt/tests/invalid_stmts/loops.rs rename to compiler/qsc_qasm/src/parser/stmt/tests/invalid_stmts/loops.rs diff --git a/compiler/qsc_qasm3/src/parser/stmt/tests/invalid_stmts/measure.rs b/compiler/qsc_qasm/src/parser/stmt/tests/invalid_stmts/measure.rs similarity index 100% rename from compiler/qsc_qasm3/src/parser/stmt/tests/invalid_stmts/measure.rs rename to compiler/qsc_qasm/src/parser/stmt/tests/invalid_stmts/measure.rs diff --git a/compiler/qsc_qasm3/src/parser/stmt/tests/invalid_stmts/switch.rs b/compiler/qsc_qasm/src/parser/stmt/tests/invalid_stmts/switch.rs similarity index 100% rename from compiler/qsc_qasm3/src/parser/stmt/tests/invalid_stmts/switch.rs rename to compiler/qsc_qasm/src/parser/stmt/tests/invalid_stmts/switch.rs diff --git a/compiler/qsc_qasm3/src/parser/stmt/tests/invalid_stmts/tokens.rs b/compiler/qsc_qasm/src/parser/stmt/tests/invalid_stmts/tokens.rs similarity index 100% rename from compiler/qsc_qasm3/src/parser/stmt/tests/invalid_stmts/tokens.rs rename to compiler/qsc_qasm/src/parser/stmt/tests/invalid_stmts/tokens.rs diff --git a/compiler/qsc_qasm3/src/parser/stmt/tests/io_decl.rs b/compiler/qsc_qasm/src/parser/stmt/tests/io_decl.rs similarity index 100% rename from compiler/qsc_qasm3/src/parser/stmt/tests/io_decl.rs rename to compiler/qsc_qasm/src/parser/stmt/tests/io_decl.rs diff --git a/compiler/qsc_qasm3/src/parser/stmt/tests/measure.rs b/compiler/qsc_qasm/src/parser/stmt/tests/measure.rs similarity index 100% rename from compiler/qsc_qasm3/src/parser/stmt/tests/measure.rs rename to compiler/qsc_qasm/src/parser/stmt/tests/measure.rs diff --git a/compiler/qsc_qasm3/src/parser/stmt/tests/old_style_decl.rs b/compiler/qsc_qasm/src/parser/stmt/tests/old_style_decl.rs similarity index 100% rename from compiler/qsc_qasm3/src/parser/stmt/tests/old_style_decl.rs rename to compiler/qsc_qasm/src/parser/stmt/tests/old_style_decl.rs diff --git a/compiler/qsc_qasm3/src/parser/stmt/tests/pragma.rs b/compiler/qsc_qasm/src/parser/stmt/tests/pragma.rs similarity index 100% rename from compiler/qsc_qasm3/src/parser/stmt/tests/pragma.rs rename to compiler/qsc_qasm/src/parser/stmt/tests/pragma.rs diff --git a/compiler/qsc_qasm3/src/parser/stmt/tests/quantum_decl.rs b/compiler/qsc_qasm/src/parser/stmt/tests/quantum_decl.rs similarity index 100% rename from compiler/qsc_qasm3/src/parser/stmt/tests/quantum_decl.rs rename to compiler/qsc_qasm/src/parser/stmt/tests/quantum_decl.rs diff --git a/compiler/qsc_qasm3/src/parser/stmt/tests/reset.rs b/compiler/qsc_qasm/src/parser/stmt/tests/reset.rs similarity index 100% rename from compiler/qsc_qasm3/src/parser/stmt/tests/reset.rs rename to compiler/qsc_qasm/src/parser/stmt/tests/reset.rs diff --git a/compiler/qsc_qasm3/src/parser/stmt/tests/switch_stmt.rs b/compiler/qsc_qasm/src/parser/stmt/tests/switch_stmt.rs similarity index 100% rename from compiler/qsc_qasm3/src/parser/stmt/tests/switch_stmt.rs rename to compiler/qsc_qasm/src/parser/stmt/tests/switch_stmt.rs diff --git a/compiler/qsc_qasm3/src/parser/stmt/tests/while_loops.rs b/compiler/qsc_qasm/src/parser/stmt/tests/while_loops.rs similarity index 100% rename from compiler/qsc_qasm3/src/parser/stmt/tests/while_loops.rs rename to compiler/qsc_qasm/src/parser/stmt/tests/while_loops.rs diff --git a/compiler/qsc_qasm3/src/parser/tests.rs b/compiler/qsc_qasm/src/parser/tests.rs similarity index 100% rename from compiler/qsc_qasm3/src/parser/tests.rs rename to compiler/qsc_qasm/src/parser/tests.rs diff --git a/compiler/qsc_qasm3/src/semantic.rs b/compiler/qsc_qasm/src/semantic.rs similarity index 100% rename from compiler/qsc_qasm3/src/semantic.rs rename to compiler/qsc_qasm/src/semantic.rs diff --git a/compiler/qsc_qasm3/src/semantic/ast.rs b/compiler/qsc_qasm/src/semantic/ast.rs similarity index 100% rename from compiler/qsc_qasm3/src/semantic/ast.rs rename to compiler/qsc_qasm/src/semantic/ast.rs diff --git a/compiler/qsc_qasm3/src/semantic/const_eval.rs b/compiler/qsc_qasm/src/semantic/const_eval.rs similarity index 99% rename from compiler/qsc_qasm3/src/semantic/const_eval.rs rename to compiler/qsc_qasm/src/semantic/const_eval.rs index 9942752c76..b780029c63 100644 --- a/compiler/qsc_qasm3/src/semantic/const_eval.rs +++ b/compiler/qsc_qasm/src/semantic/const_eval.rs @@ -25,13 +25,13 @@ use thiserror::Error; #[derive(Clone, Debug, Diagnostic, Eq, Error, PartialEq)] pub enum ConstEvalError { #[error("expression must be const")] - #[diagnostic(code("Qsc.Qasm3.Compile.ExprMustBeConst"))] + #[diagnostic(code("Qasm.Compile.ExprMustBeConst"))] ExprMustBeConst(#[label] Span), #[error("uint expression must evaluate to a non-negative value, but it evaluated to {0}")] - #[diagnostic(code("Qsc.Qasm3.Compile.NegativeUIntValue"))] + #[diagnostic(code("Qasm.Compile.NegativeUIntValue"))] NegativeUIntValue(i64, #[label] Span), #[error("{0} doesn't fit in {1}")] - #[diagnostic(code("Qsc.Qasm3.Compile.ValueOverflow"))] + #[diagnostic(code("Qasm.Compile.ValueOverflow"))] ValueOverflow(String, String, #[label] Span), } diff --git a/compiler/qsc_qasm3/src/semantic/error.rs b/compiler/qsc_qasm/src/semantic/error.rs similarity index 64% rename from compiler/qsc_qasm3/src/semantic/error.rs rename to compiler/qsc_qasm/src/semantic/error.rs index 09dd56a24d..c1dd88acec 100644 --- a/compiler/qsc_qasm3/src/semantic/error.rs +++ b/compiler/qsc_qasm/src/semantic/error.rs @@ -20,212 +20,212 @@ pub struct Error(pub SemanticErrorKind); #[derive(Clone, Debug, Diagnostic, Eq, Error, PartialEq)] pub enum SemanticErrorKind { #[error("annotation missing target statement")] - #[diagnostic(code("Qsc.Qasm3.Lowerer.AnnotationWithoutStatement"))] + #[diagnostic(code("Qasm.Lowerer.AnnotationWithoutStatement"))] AnnotationWithoutStatement(#[label] Span), #[error("array literals are only allowed in classical declarations")] - #[diagnostic(code("Qsc.Qasm3.Lowerer.ArrayLiteralInNonClassicalDecl"))] + #[diagnostic(code("Qasm.Lowerer.ArrayLiteralInNonClassicalDecl"))] ArrayLiteralInNonClassicalDecl(#[label] Span), #[error("array size must be a non-negative integer const expression")] - #[diagnostic(code("Qsc.Qasm3.Lowerer.ArraySizeMustBeNonNegativeConstExpr"))] + #[diagnostic(code("Qasm.Lowerer.ArraySizeMustBeNonNegativeConstExpr"))] ArraySizeMustBeNonNegativeConstExpr(#[label] Span), #[error("calibration statements are not supported: {0}")] - #[diagnostic(code("Qsc.Qasm3.Lowerer.CalibrationsNotSupported"))] + #[diagnostic(code("Qasm.Lowerer.CalibrationsNotSupported"))] CalibrationsNotSupported(String, #[label] Span), #[error("cannot alias type {0}. Only qubit and qubit[] can be aliased")] - #[diagnostic(code("Qsc.Qasm3.Lowerer.CannotAliasType"))] + #[diagnostic(code("Qasm.Lowerer.CannotAliasType"))] CannotAliasType(String, #[label] Span), #[error("cannot apply operator {0} to types {1} and {2}")] - #[diagnostic(code("Qsc.Qasm3.Lowerer.CannotApplyOperatorToTypes"))] + #[diagnostic(code("Qasm.Lowerer.CannotApplyOperatorToTypes"))] CannotApplyOperatorToTypes(String, String, String, #[label] Span), #[error("cannot assign a value of {0} type to a classical variable of {1} type")] - #[diagnostic(code("Qsc.Qasm3.Lowerer.CannotAssignToType"))] + #[diagnostic(code("Qasm.Lowerer.CannotAssignToType"))] CannotAssignToType(String, String, #[label] Span), #[error("cannot call an expression that is not a function")] - #[diagnostic(code("Qsc.Qasm3.Lowerer.CannotCallNonFunction"))] + #[diagnostic(code("Qasm.Lowerer.CannotCallNonFunction"))] CannotCallNonFunction(#[label] Span), #[error("cannot call a gate that is not a gate")] - #[diagnostic(code("Qsc.Qasm3.Lowerer.CannotCallNonGate"))] + #[diagnostic(code("Qasm.Lowerer.CannotCallNonGate"))] CannotCallNonGate(#[label] Span), #[error("cannot cast expression of type {0} to type {1}")] - #[diagnostic(code("Qsc.Qasm3.Lowerer.CannotCast"))] + #[diagnostic(code("Qasm.Lowerer.CannotCast"))] CannotCast(String, String, #[label] Span), #[error("cannot cast literal expression of type {0} to type {1}")] - #[diagnostic(code("Qsc.Qasm3.Lowerer.CannotCastLiteral"))] + #[diagnostic(code("Qasm.Lowerer.CannotCastLiteral"))] CannotCastLiteral(String, String, #[label] Span), #[error("cannot index variables of type {0}")] - #[diagnostic(code("Qsc.Qasm3.Lowerer.CannotIndexType"))] + #[diagnostic(code("Qasm.Lowerer.CannotIndexType"))] CannotIndexType(String, #[label] Span), #[error("cannot update const variable {0}")] #[diagnostic(help("mutable variables must be declared without the keyword `const`"))] - #[diagnostic(code("Qsc.Qasm3.Lowerer.CannotUpdateConstVariable"))] + #[diagnostic(code("Qasm.Lowerer.CannotUpdateConstVariable"))] CannotUpdateConstVariable(String, #[label] Span), #[error("cannot cast expression of type {0} to type {1} as it would cause truncation")] - #[diagnostic(code("Qsc.Qasm3.Lowerer.CastWouldCauseTruncation"))] + #[diagnostic(code("Qasm.Lowerer.CastWouldCauseTruncation"))] CastWouldCauseTruncation(String, String, #[label] Span), #[error("invalid classical statement in box")] - #[diagnostic(code("Qsc.Qasm3.Lowerer.ClassicalStmtInBox"))] + #[diagnostic(code("Qasm.Lowerer.ClassicalStmtInBox"))] ClassicalStmtInBox(#[label] Span), #[error("complex numbers in assignment binary expressions are not yet supported")] - #[diagnostic(code("Qsc.Qasm3.Lowerer.ComplexBinaryAssignment"))] + #[diagnostic(code("Qasm.Lowerer.ComplexBinaryAssignment"))] ComplexBinaryAssignment(#[label] Span), #[error("designator must be a positive literal integer")] - #[diagnostic(code("Qsc.Qasm3.Lowerer.DesignatorMustBePositiveIntLiteral"))] + #[diagnostic(code("Qasm.Lowerer.DesignatorMustBePositiveIntLiteral"))] DesignatorMustBePositiveIntLiteral(#[label] Span), #[error("def declarations must be done in global scope")] - #[diagnostic(code("Qsc.Qasm3.Lowerer.DefDeclarationInNonGlobalScope"))] + #[diagnostic(code("Qasm.Lowerer.DefDeclarationInNonGlobalScope"))] DefDeclarationInNonGlobalScope(#[label] Span), #[error("designator is too large")] - #[diagnostic(code("Qsc.Qasm3.Lowerer.DesignatorTooLarge"))] + #[diagnostic(code("Qasm.Lowerer.DesignatorTooLarge"))] DesignatorTooLarge(#[label] Span), #[error("{0} must be a const expression")] - #[diagnostic(code("Qsc.Qasm3.Lowerer.ExprMustBeConst"))] + #[diagnostic(code("Qasm.Lowerer.ExprMustBeConst"))] ExprMustBeConst(String, #[label] Span), #[error("{0} must fit in a u32")] - #[diagnostic(code("Qsc.Qasm3.Lowerer.ExprMustFitInU32"))] + #[diagnostic(code("Qasm.Lowerer.ExprMustFitInU32"))] ExprMustFitInU32(String, #[label] Span), #[error("extern declarations must be done in global scope")] - #[diagnostic(code("Qsc.Qasm3.Lowerer.DefDeclarationInNonGlobalScope"))] + #[diagnostic(code("Qasm.Lowerer.DefDeclarationInNonGlobalScope"))] ExternDeclarationInNonGlobalScope(#[label] Span), #[error("failed to compile all expressions in expression list")] - #[diagnostic(code("Qsc.Qasm3.Lowerer.FailedToCompileExpressionList"))] + #[diagnostic(code("Qasm.Lowerer.FailedToCompileExpressionList"))] FailedToCompileExpressionList(#[label] Span), #[error("for iterable must have a set expression, range expression, or iterable expression")] - #[diagnostic(code("Qsc.Qasm3.Lowerer.ForIterableInvalidExpression"))] + #[diagnostic(code("Qasm.Lowerer.ForIterableInvalidExpression"))] ForIterableInvalidExpression(#[label] Span), #[error("for statements must have a body or statement")] - #[diagnostic(code("Qsc.Qasm3.Lowerer.ForStatementsMustHaveABodyOrStatement"))] + #[diagnostic(code("Qasm.Lowerer.ForStatementsMustHaveABodyOrStatement"))] ForStatementsMustHaveABodyOrStatement(#[label] Span), #[error("if statement missing {0} expression")] - #[diagnostic(code("Qsc.Qasm3.Lowerer.IfStmtMissingExpression"))] + #[diagnostic(code("Qasm.Lowerer.IfStmtMissingExpression"))] IfStmtMissingExpression(String, #[label] Span), #[error("include {0} could not be found")] - #[diagnostic(code("Qsc.Qasm3.Lowerer.IncludeNotFound"))] + #[diagnostic(code("Qasm.Lowerer.IncludeNotFound"))] IncludeNotFound(String, #[label] Span), #[error("include {0} must be declared in global scope")] - #[diagnostic(code("Qsc.Qasm3.Lowerer.IncludeNotInGlobalScope"))] + #[diagnostic(code("Qasm.Lowerer.IncludeNotInGlobalScope"))] IncludeNotInGlobalScope(String, #[label] Span), #[error("include {0} must be declared in global scope")] - #[diagnostic(code("Qsc.Qasm3.Lowerer.IncludeStatementMissingPath"))] + #[diagnostic(code("Qasm.Lowerer.IncludeStatementMissingPath"))] IncludeStatementMissingPath(#[label] Span), #[error("inconsistent types in alias expression: {0}")] - #[diagnostic(code("Qsc.Qasm3.Lowerer.InconsistentTypesInAlias"))] + #[diagnostic(code("Qasm.Lowerer.InconsistentTypesInAlias"))] InconsistentTypesInAlias(String, #[label] Span), #[error("indexed must be a single expression")] - #[diagnostic(code("Qsc.Qasm3.Lowerer.IndexMustBeSingleExpr"))] + #[diagnostic(code("Qasm.Lowerer.IndexMustBeSingleExpr"))] IndexMustBeSingleExpr(#[label] Span), #[error("assigning {0} values to {1} must be in a range that be converted to {1}")] - #[diagnostic(code("Qsc.Qasm3.Lowerer.InvalidCastValueRange"))] + #[diagnostic(code("Qasm.Lowerer.InvalidCastValueRange"))] InvalidCastValueRange(String, String, #[label] Span), #[error("gate operands other than qubits or qubit arrays are not supported")] - #[diagnostic(code("Qsc.Qasm3.Lowerer.InvalidGateOperand"))] + #[diagnostic(code("Qasm.Lowerer.InvalidGateOperand"))] InvalidGateOperand(#[label] Span), #[error("control counts must be integer literals")] - #[diagnostic(code("Qsc.Qasm3.Lowerer.InvalidControlCount"))] + #[diagnostic(code("Qasm.Lowerer.InvalidControlCount"))] InvalidControlCount(#[label] Span), #[error("gate operands other than qubit arrays are not supported")] - #[diagnostic(code("Qsc.Qasm3.Lowerer.InvalidIndexedGateOperand"))] + #[diagnostic(code("Qasm.Lowerer.InvalidIndexedGateOperand"))] InvalidIndexedGateOperand(#[label] Span), #[error("gate expects {0} classical arguments, but {1} were provided")] - #[diagnostic(code("Qsc.Qasm3.Lowerer.InvalidNumberOfClassicalArgs"))] + #[diagnostic(code("Qasm.Lowerer.InvalidNumberOfClassicalArgs"))] InvalidNumberOfClassicalArgs(usize, usize, #[label] Span), #[error("gate expects {0} qubit arguments, but {1} were provided")] - #[diagnostic(code("Qsc.Qasm3.Lowerer.InvalidNumberOfQubitArgs"))] + #[diagnostic(code("Qasm.Lowerer.InvalidNumberOfQubitArgs"))] InvalidNumberOfQubitArgs(usize, usize, #[label] Span), #[error("{0} can only appear in {1} scopes")] - #[diagnostic(code("Qsc.Qasm3.Lowerer.InvalidScope"))] + #[diagnostic(code("Qasm.Lowerer.InvalidScope"))] InvalidScope(String, String, #[label] Span), #[error("measure statements must have a name")] - #[diagnostic(code("Qsc.Qasm3.Lowerer.MeasureExpressionsMustHaveName"))] + #[diagnostic(code("Qasm.Lowerer.MeasureExpressionsMustHaveName"))] MeasureExpressionsMustHaveName(#[label] Span), #[error("measure statements must have a gate operand name")] - #[diagnostic(code("Qsc.Qasm3.Lowerer.MeasureExpressionsMustHaveGateOperand"))] + #[diagnostic(code("Qasm.Lowerer.MeasureExpressionsMustHaveGateOperand"))] MeasureExpressionsMustHaveGateOperand(#[label] Span), #[error("return statements on a non-void subroutine should have a target expression")] - #[diagnostic(code("Qsc.Qasm3.Lowerer.MissingTargetExpressionInReturnStmt"))] + #[diagnostic(code("Qasm.Lowerer.MissingTargetExpressionInReturnStmt"))] MissingTargetExpressionInReturnStmt(#[label] Span), #[error("control counts must be postitive integers")] - #[diagnostic(code("Qsc.Qasm3.Lowerer.NegativeControlCount"))] + #[diagnostic(code("Qasm.Lowerer.NegativeControlCount"))] NegativeControlCount(#[label] Span), #[error("{0} are not supported")] - #[diagnostic(code("Qsc.Qasm3.Lowerer.NotSupported"))] + #[diagnostic(code("Qasm.Lowerer.NotSupported"))] NotSupported(String, #[label] Span), #[error("{0} were introduced in version {1}")] - #[diagnostic(code("Qsc.Qasm3.Lowerer.NotSupportedInThisVersion"))] + #[diagnostic(code("Qasm.Lowerer.NotSupportedInThisVersion"))] NotSupportedInThisVersion(String, String, #[label] Span), #[error("the operator {0} is not valid with lhs {1} and rhs {2}")] - #[diagnostic(code("Qsc.Qasm3.Lowerer.OperatorNotSupportedForTypes"))] + #[diagnostic(code("Qasm.Lowerer.OperatorNotSupportedForTypes"))] OperatorNotSupportedForTypes(String, String, String, #[label] Span), #[error("pow gate modifiers must have an exponent")] - #[diagnostic(code("Qsc.Qasm3.Lowerer.PowModifierMustHaveExponent"))] + #[diagnostic(code("Qasm.Lowerer.PowModifierMustHaveExponent"))] PowModifierMustHaveExponent(#[label] Span), #[error("quantum declarations must be done in global scope")] - #[diagnostic(code("Qsc.Qasm3.Lowerer.QuantumDeclarationInNonGlobalScope"))] + #[diagnostic(code("Qasm.Lowerer.QuantumDeclarationInNonGlobalScope"))] QuantumDeclarationInNonGlobalScope(#[label] Span), #[error("quantum typed values cannot be used in binary expressions")] - #[diagnostic(code("Qsc.Qasm3.Lowerer.QuantumTypesInBinaryExpression"))] + #[diagnostic(code("Qasm.Lowerer.QuantumTypesInBinaryExpression"))] QuantumTypesInBinaryExpression(#[label] Span), #[error("range expressions must have a start")] - #[diagnostic(code("Qsc.Qasm3.Lowerer.RangeExpressionsMustHaveStart"))] + #[diagnostic(code("Qasm.Lowerer.RangeExpressionsMustHaveStart"))] RangeExpressionsMustHaveStart(#[label] Span), #[error("range expressions must have a stop")] - #[diagnostic(code("Qsc.Qasm3.Lowerer.RangeExpressionsMustHaveStop"))] + #[diagnostic(code("Qasm.Lowerer.RangeExpressionsMustHaveStop"))] RangeExpressionsMustHaveStop(#[label] Span), #[error("redefined symbol: {0}")] - #[diagnostic(code("Qsc.Qasm3.Lowerer.RedefinedSymbol"))] + #[diagnostic(code("Qasm.Lowerer.RedefinedSymbol"))] RedefinedSymbol(String, #[label] Span), #[error("reset expression must have a gate operand")] - #[diagnostic(code("Qsc.Qasm3.Lowerer.ResetExpressionMustHaveGateOperand"))] + #[diagnostic(code("Qasm.Lowerer.ResetExpressionMustHaveGateOperand"))] ResetExpressionMustHaveGateOperand(#[label] Span), #[error("reset expression must have a name")] - #[diagnostic(code("Qsc.Qasm3.Lowerer.ResetExpressionMustHaveName"))] + #[diagnostic(code("Qasm.Lowerer.ResetExpressionMustHaveName"))] ResetExpressionMustHaveName(#[label] Span), #[error("cannot return an expression from a void subroutine")] - #[diagnostic(code("Qsc.Qasm3.Lowerer.ReturningExpressionFromVoidSubroutine"))] + #[diagnostic(code("Qasm.Lowerer.ReturningExpressionFromVoidSubroutine"))] ReturningExpressionFromVoidSubroutine(#[label] Span), #[error("return statements are only allowed within subroutines")] - #[diagnostic(code("Qsc.Qasm3.Lowerer.ReturnNotInSubroutine"))] + #[diagnostic(code("Qasm.Lowerer.ReturnNotInSubroutine"))] ReturnNotInSubroutine(#[label] Span), #[error("switch statement must have at least one non-default case")] - #[diagnostic(code("Qsc.Qasm3.Lowerer.SwitchStatementMustHaveAtLeastOneCase"))] + #[diagnostic(code("Qasm.Lowerer.SwitchStatementMustHaveAtLeastOneCase"))] SwitchStatementMustHaveAtLeastOneCase(#[label] Span), #[error("too many controls specified")] - #[diagnostic(code("Qsc.Qasm3.Lowerer.TooManyControls"))] + #[diagnostic(code("Qasm.Lowerer.TooManyControls"))] TooManyControls(#[label] Span), #[error("too many indicies specified")] - #[diagnostic(code("Qsc.Qasm3.Lowerer.TooManyIndices"))] + #[diagnostic(code("Qasm.Lowerer.TooManyIndices"))] TooManyIndices(#[label] Span), #[error("bitwise not `~` is not allowed for instances of {0}")] - #[diagnostic(code("Qsc.Qasm3.Lowerer.TypeDoesNotSupportBitwiseNot"))] + #[diagnostic(code("Qasm.Lowerer.TypeDoesNotSupportBitwiseNot"))] TypeDoesNotSupportBitwiseNot(String, #[label] Span), #[error("unary negation is not allowed for instances of {0}")] - #[diagnostic(code("Qsc.Qasm3.Lowerer.TypeDoesNotSupportedUnaryNegation"))] + #[diagnostic(code("Qasm.Lowerer.TypeDoesNotSupportedUnaryNegation"))] TypeDoesNotSupportedUnaryNegation(String, #[label] Span), #[error("{0} max width is {1} but {2} was provided")] - #[diagnostic(code("Qsc.Qasm3.Lowerer.TypeMaxWidthExceeded"))] + #[diagnostic(code("Qasm.Lowerer.TypeMaxWidthExceeded"))] TypeMaxWidthExceeded(String, usize, usize, #[label] Span), #[error("types differ by dimensions and are incompatible")] - #[diagnostic(code("Qsc.Qasm3.Lowerer.TypeRankError"))] + #[diagnostic(code("Qasm.Lowerer.TypeRankError"))] TypeRankError(#[label] Span), #[error("type width must be a positive integer const expression")] - #[diagnostic(code("Qsc.Qasm3.Lowerer.TypeWidthMustBePositiveIntConstExpr"))] + #[diagnostic(code("Qasm.Lowerer.TypeWidthMustBePositiveIntConstExpr"))] TypeWidthMustBePositiveIntConstExpr(#[label] Span), #[error("undefined symbol: {0}")] - #[diagnostic(code("Qsc.Qasm3.Lowerer.UndefinedSymbol"))] + #[diagnostic(code("Qasm.Lowerer.UndefinedSymbol"))] UndefinedSymbol(String, #[label] Span), #[error("unexpected parser error: {0}")] - #[diagnostic(code("Qsc.Qasm3.Lowerer.UnexpectedParserError"))] + #[diagnostic(code("Qasm.Lowerer.UnexpectedParserError"))] UnexpectedParserError(String, #[label] Span), #[error("this statement is not yet handled during OpenQASM 3 import: {0}")] - #[diagnostic(code("Qsc.Qasm3.Lowerer.Unimplemented"))] + #[diagnostic(code("Qasm.Lowerer.Unimplemented"))] Unimplemented(String, #[label] Span), #[error("unknown index operation kind")] - #[diagnostic(code("Qsc.Qasm3.Lowerer.UnknownIndexedOperatorKind"))] + #[diagnostic(code("Qasm.Lowerer.UnknownIndexedOperatorKind"))] UnknownIndexedOperatorKind(#[label] Span), #[error("unsupported version: '{0}'")] - #[diagnostic(code("Qsc.Qasm3.Lowerer.UnsupportedVersion"))] + #[diagnostic(code("Qasm.Lowerer.UnsupportedVersion"))] UnsupportedVersion(String, #[label] Span), #[error("while statement missing {0} expression")] - #[diagnostic(code("Qsc.Qasm3.Lowerer.WhileStmtMissingExpression"))] + #[diagnostic(code("Qasm.Lowerer.WhileStmtMissingExpression"))] WhileStmtMissingExpression(String, #[label] Span), } diff --git a/compiler/qsc_qasm3/src/semantic/lowerer.rs b/compiler/qsc_qasm/src/semantic/lowerer.rs similarity index 100% rename from compiler/qsc_qasm3/src/semantic/lowerer.rs rename to compiler/qsc_qasm/src/semantic/lowerer.rs diff --git a/compiler/qsc_qasm3/src/semantic/symbols.rs b/compiler/qsc_qasm/src/semantic/symbols.rs similarity index 100% rename from compiler/qsc_qasm3/src/semantic/symbols.rs rename to compiler/qsc_qasm/src/semantic/symbols.rs diff --git a/compiler/qsc_qasm3/src/semantic/tests.rs b/compiler/qsc_qasm/src/semantic/tests.rs similarity index 97% rename from compiler/qsc_qasm3/src/semantic/tests.rs rename to compiler/qsc_qasm/src/semantic/tests.rs index fcf341ef7a..133dcb3c76 100644 --- a/compiler/qsc_qasm3/src/semantic/tests.rs +++ b/compiler/qsc_qasm/src/semantic/tests.rs @@ -256,7 +256,7 @@ fn semantic_errors_map_to_their_corresponding_file_specific_spans() { ty: Err kind: SymbolId(36) - [Qsc.Qasm3.Lowerer.UndefinedSymbol + [Qasm.Lowerer.UndefinedSymbol x undefined symbol: v ,-[source2.qasm:2:14] @@ -264,7 +264,7 @@ fn semantic_errors_map_to_their_corresponding_file_specific_spans() { 2 | bool l = v && l; // undefined y, redefine l : ^ `---- - , Qsc.Qasm3.Lowerer.CannotCast + , Qasm.Lowerer.CannotCast x cannot cast expression of type Err to type Bool(false) ,-[source2.qasm:2:14] @@ -272,7 +272,7 @@ fn semantic_errors_map_to_their_corresponding_file_specific_spans() { 2 | bool l = v && l; // undefined y, redefine l : ^ `---- - , Qsc.Qasm3.Lowerer.RedefinedSymbol + , Qasm.Lowerer.RedefinedSymbol x redefined symbol: l ,-[source2.qasm:2:10] @@ -280,7 +280,7 @@ fn semantic_errors_map_to_their_corresponding_file_specific_spans() { 2 | bool l = v && l; // undefined y, redefine l : ^ `---- - , Qsc.Qasm3.Lowerer.CannotCast + , Qasm.Lowerer.CannotCast x cannot cast expression of type Angle(None, false) to type Float(None, | false) @@ -289,7 +289,7 @@ fn semantic_errors_map_to_their_corresponding_file_specific_spans() { 3 | float k = j + false; // invalid cast : ^ `---- - , Qsc.Qasm3.Lowerer.UndefinedSymbol + , Qasm.Lowerer.UndefinedSymbol x undefined symbol: r ,-[source0.qasm:4:13] @@ -298,7 +298,7 @@ fn semantic_errors_map_to_their_corresponding_file_specific_spans() { : ^ 5 | `---- - , Qsc.Qasm3.Lowerer.CannotCast + , Qasm.Lowerer.CannotCast x cannot cast expression of type Err to type Bit(false) ,-[source0.qasm:4:13] diff --git a/compiler/qsc_qasm3/src/semantic/tests/assignment.rs b/compiler/qsc_qasm/src/semantic/tests/assignment.rs similarity index 100% rename from compiler/qsc_qasm3/src/semantic/tests/assignment.rs rename to compiler/qsc_qasm/src/semantic/tests/assignment.rs diff --git a/compiler/qsc_qasm3/src/semantic/tests/decls.rs b/compiler/qsc_qasm/src/semantic/tests/decls.rs similarity index 92% rename from compiler/qsc_qasm3/src/semantic/tests/decls.rs rename to compiler/qsc_qasm/src/semantic/tests/decls.rs index 230b04acac..38f20f5508 100644 --- a/compiler/qsc_qasm3/src/semantic/tests/decls.rs +++ b/compiler/qsc_qasm/src/semantic/tests/decls.rs @@ -29,7 +29,7 @@ fn duration_and_stretch_types_without_init_exprs() { &expect![[r#" - [Qsc.Qasm3.Compile.NotSupported + [Qasm.Compile.NotSupported x Duration type values are not supported. ,-[test:2:9] @@ -38,7 +38,7 @@ fn duration_and_stretch_types_without_init_exprs() { : ^^^^^^^^ 3 | stretch n; `---- - , Qsc.Qasm3.Compile.NotSupported + , Qasm.Compile.NotSupported x Stretch type values are not supported. ,-[test:3:9] @@ -68,7 +68,7 @@ fn scalar_ty_designator_must_be_positive() { ty: Err kind: Err - [Qsc.Qasm3.Lowerer.TypeWidthMustBePositiveIntConstExpr + [Qasm.Lowerer.TypeWidthMustBePositiveIntConstExpr x type width must be a positive integer const expression ,-[test:1:5] @@ -104,14 +104,14 @@ fn scalar_ty_designator_must_be_castable_to_const_int() { ty: Err kind: Err - [Qsc.Qasm3.Lowerer.CannotCast + [Qasm.Lowerer.CannotCast x cannot cast expression of type Angle(None, true) to type UInt(None, true) ,-[test:1:29] 1 | const angle size = 2.0; int[size] i; : ^^^^ `---- - , Qsc.Qasm3.Lowerer.TypeWidthMustBePositiveIntConstExpr + , Qasm.Lowerer.TypeWidthMustBePositiveIntConstExpr x type width must be a positive integer const expression ,-[test:1:29] diff --git a/compiler/qsc_qasm3/src/semantic/tests/decls/angle.rs b/compiler/qsc_qasm/src/semantic/tests/decls/angle.rs similarity index 98% rename from compiler/qsc_qasm3/src/semantic/tests/decls/angle.rs rename to compiler/qsc_qasm/src/semantic/tests/decls/angle.rs index 6d00b8b7b2..cf0e7ff96e 100644 --- a/compiler/qsc_qasm3/src/semantic/tests/decls/angle.rs +++ b/compiler/qsc_qasm/src/semantic/tests/decls/angle.rs @@ -395,7 +395,7 @@ fn const_lit_decl_signed_int_lit_cast_neg_fails() { ty: Int(None, true) kind: Lit: Int(7) - [Qsc.Qasm3.Lowerer.CannotCast + [Qasm.Lowerer.CannotCast x cannot cast expression of type Int(None, true) to type Angle(None, true) ,-[test:1:18] @@ -423,14 +423,14 @@ fn explicit_zero_width_fails() { ty: Float(None, true) kind: Lit: Float(42.1) - [Qsc.Qasm3.Lowerer.TypeWidthMustBePositiveIntConstExpr + [Qasm.Lowerer.TypeWidthMustBePositiveIntConstExpr x type width must be a positive integer const expression ,-[test:1:7] 1 | angle[0] x = 42.1; : ^ `---- - , Qsc.Qasm3.Lowerer.CannotCastLiteral + , Qasm.Lowerer.CannotCastLiteral x cannot cast literal expression of type Float(None, true) to type Err ,-[test:1:1] @@ -458,14 +458,14 @@ fn explicit_width_over_64_fails() { ty: Float(None, true) kind: Lit: Float(42.1) - [Qsc.Qasm3.Lowerer.TypeMaxWidthExceeded + [Qasm.Lowerer.TypeMaxWidthExceeded x angle max width is 64 but 65 was provided ,-[test:1:7] 1 | const angle[65] x = 42.1; : ^^^^^^^^^ `---- - , Qsc.Qasm3.Lowerer.CannotCastLiteral + , Qasm.Lowerer.CannotCastLiteral x cannot cast literal expression of type Float(None, true) to type Err ,-[test:1:1] diff --git a/compiler/qsc_qasm3/src/semantic/tests/decls/bit.rs b/compiler/qsc_qasm/src/semantic/tests/decls/bit.rs similarity index 100% rename from compiler/qsc_qasm3/src/semantic/tests/decls/bit.rs rename to compiler/qsc_qasm/src/semantic/tests/decls/bit.rs diff --git a/compiler/qsc_qasm3/src/semantic/tests/decls/bool.rs b/compiler/qsc_qasm/src/semantic/tests/decls/bool.rs similarity index 98% rename from compiler/qsc_qasm3/src/semantic/tests/decls/bool.rs rename to compiler/qsc_qasm/src/semantic/tests/decls/bool.rs index f7792018c6..d29a252984 100644 --- a/compiler/qsc_qasm3/src/semantic/tests/decls/bool.rs +++ b/compiler/qsc_qasm/src/semantic/tests/decls/bool.rs @@ -42,7 +42,7 @@ fn array_with_no_init_expr_has_generated_lit_expr() { ty: Err kind: Err - [Qsc.Qasm3.Compile.Unimplemented + [Qasm.Compile.Unimplemented x this statement is not yet handled during OpenQASM 3 import: semantic type | from array type diff --git a/compiler/qsc_qasm3/src/semantic/tests/decls/complex.rs b/compiler/qsc_qasm/src/semantic/tests/decls/complex.rs similarity index 100% rename from compiler/qsc_qasm3/src/semantic/tests/decls/complex.rs rename to compiler/qsc_qasm/src/semantic/tests/decls/complex.rs diff --git a/compiler/qsc_qasm3/src/semantic/tests/decls/creg.rs b/compiler/qsc_qasm/src/semantic/tests/decls/creg.rs similarity index 100% rename from compiler/qsc_qasm3/src/semantic/tests/decls/creg.rs rename to compiler/qsc_qasm/src/semantic/tests/decls/creg.rs diff --git a/compiler/qsc_qasm3/src/semantic/tests/decls/duration.rs b/compiler/qsc_qasm/src/semantic/tests/decls/duration.rs similarity index 95% rename from compiler/qsc_qasm3/src/semantic/tests/decls/duration.rs rename to compiler/qsc_qasm/src/semantic/tests/decls/duration.rs index 776d89e1d4..5d1c745f78 100644 --- a/compiler/qsc_qasm3/src/semantic/tests/decls/duration.rs +++ b/compiler/qsc_qasm/src/semantic/tests/decls/duration.rs @@ -22,7 +22,7 @@ fn with_no_init_expr_has_generated_lit_expr() { ty: Duration(true) kind: Lit: Duration(0.0, Ns) - [Qsc.Qasm3.Lowerer.NotSupported + [Qasm.Lowerer.NotSupported x duration type values are not supported ,-[test:1:1] diff --git a/compiler/qsc_qasm3/src/semantic/tests/decls/extern_decl.rs b/compiler/qsc_qasm/src/semantic/tests/decls/extern_decl.rs similarity index 97% rename from compiler/qsc_qasm3/src/semantic/tests/decls/extern_decl.rs rename to compiler/qsc_qasm/src/semantic/tests/decls/extern_decl.rs index 963790f57c..946ab03779 100644 --- a/compiler/qsc_qasm3/src/semantic/tests/decls/extern_decl.rs +++ b/compiler/qsc_qasm/src/semantic/tests/decls/extern_decl.rs @@ -76,7 +76,7 @@ fn no_allowed_in_non_global_scope() { parameters: return_type: () - [Qsc.Qasm3.Lowerer.DefDeclarationInNonGlobalScope + [Qasm.Lowerer.DefDeclarationInNonGlobalScope x extern declarations must be done in global scope ,-[test:1:3] diff --git a/compiler/qsc_qasm3/src/semantic/tests/decls/float.rs b/compiler/qsc_qasm/src/semantic/tests/decls/float.rs similarity index 99% rename from compiler/qsc_qasm3/src/semantic/tests/decls/float.rs rename to compiler/qsc_qasm/src/semantic/tests/decls/float.rs index 51f72a9b29..104e53ea9d 100644 --- a/compiler/qsc_qasm3/src/semantic/tests/decls/float.rs +++ b/compiler/qsc_qasm/src/semantic/tests/decls/float.rs @@ -436,7 +436,7 @@ fn init_float_with_int_value_greater_than_safely_representable_values() { ty: Int(None, true) kind: Lit: Int(9007199254740993) - [Qsc.Qasm3.Lowerer.InvalidCastValueRange + [Qasm.Lowerer.InvalidCastValueRange x assigning Int(None, true) values to Float(None, false) must be in a range | that be converted to Float(None, false) @@ -444,7 +444,7 @@ fn init_float_with_int_value_greater_than_safely_representable_values() { 1 | float a = 9007199254740993; : ^^^^^^^^^^^^^^^^ `---- - , Qsc.Qasm3.Lowerer.CannotCastLiteral + , Qasm.Lowerer.CannotCastLiteral x cannot cast literal expression of type Int(None, true) to type Float(None, | false) diff --git a/compiler/qsc_qasm3/src/semantic/tests/decls/int.rs b/compiler/qsc_qasm/src/semantic/tests/decls/int.rs similarity index 100% rename from compiler/qsc_qasm3/src/semantic/tests/decls/int.rs rename to compiler/qsc_qasm/src/semantic/tests/decls/int.rs diff --git a/compiler/qsc_qasm3/src/semantic/tests/decls/qreg.rs b/compiler/qsc_qasm/src/semantic/tests/decls/qreg.rs similarity index 100% rename from compiler/qsc_qasm3/src/semantic/tests/decls/qreg.rs rename to compiler/qsc_qasm/src/semantic/tests/decls/qreg.rs diff --git a/compiler/qsc_qasm3/src/semantic/tests/decls/stretch.rs b/compiler/qsc_qasm/src/semantic/tests/decls/stretch.rs similarity index 92% rename from compiler/qsc_qasm3/src/semantic/tests/decls/stretch.rs rename to compiler/qsc_qasm/src/semantic/tests/decls/stretch.rs index 7210bcc943..fa1474ffc7 100644 --- a/compiler/qsc_qasm3/src/semantic/tests/decls/stretch.rs +++ b/compiler/qsc_qasm/src/semantic/tests/decls/stretch.rs @@ -22,14 +22,14 @@ fn with_no_init_expr_has_generated_lit_expr() { ty: Stretch(true) kind: Err - [Qsc.Qasm3.Lowerer.NotSupported + [Qasm.Lowerer.NotSupported x stretch type values are not supported ,-[test:1:1] 1 | stretch a; : ^^^^^^^ `---- - , Qsc.Qasm3.Lowerer.NotSupported + , Qasm.Lowerer.NotSupported x stretch default values are not supported ,-[test:1:1] diff --git a/compiler/qsc_qasm3/src/semantic/tests/decls/uint.rs b/compiler/qsc_qasm/src/semantic/tests/decls/uint.rs similarity index 100% rename from compiler/qsc_qasm3/src/semantic/tests/decls/uint.rs rename to compiler/qsc_qasm/src/semantic/tests/decls/uint.rs diff --git a/compiler/qsc_qasm3/src/semantic/tests/expression.rs b/compiler/qsc_qasm/src/semantic/tests/expression.rs similarity index 100% rename from compiler/qsc_qasm3/src/semantic/tests/expression.rs rename to compiler/qsc_qasm/src/semantic/tests/expression.rs diff --git a/compiler/qsc_qasm3/src/semantic/tests/expression/binary.rs b/compiler/qsc_qasm/src/semantic/tests/expression/binary.rs similarity index 100% rename from compiler/qsc_qasm3/src/semantic/tests/expression/binary.rs rename to compiler/qsc_qasm/src/semantic/tests/expression/binary.rs diff --git a/compiler/qsc_qasm3/src/semantic/tests/expression/binary/arithmetic_conversions.rs b/compiler/qsc_qasm/src/semantic/tests/expression/binary/arithmetic_conversions.rs similarity index 100% rename from compiler/qsc_qasm3/src/semantic/tests/expression/binary/arithmetic_conversions.rs rename to compiler/qsc_qasm/src/semantic/tests/expression/binary/arithmetic_conversions.rs diff --git a/compiler/qsc_qasm3/src/semantic/tests/expression/binary/comparison.rs b/compiler/qsc_qasm/src/semantic/tests/expression/binary/comparison.rs similarity index 100% rename from compiler/qsc_qasm3/src/semantic/tests/expression/binary/comparison.rs rename to compiler/qsc_qasm/src/semantic/tests/expression/binary/comparison.rs diff --git a/compiler/qsc_qasm3/src/semantic/tests/expression/binary/comparison/bit_to_bit.rs b/compiler/qsc_qasm/src/semantic/tests/expression/binary/comparison/bit_to_bit.rs similarity index 100% rename from compiler/qsc_qasm3/src/semantic/tests/expression/binary/comparison/bit_to_bit.rs rename to compiler/qsc_qasm/src/semantic/tests/expression/binary/comparison/bit_to_bit.rs diff --git a/compiler/qsc_qasm3/src/semantic/tests/expression/binary/comparison/bool_to_bool.rs b/compiler/qsc_qasm/src/semantic/tests/expression/binary/comparison/bool_to_bool.rs similarity index 100% rename from compiler/qsc_qasm3/src/semantic/tests/expression/binary/comparison/bool_to_bool.rs rename to compiler/qsc_qasm/src/semantic/tests/expression/binary/comparison/bool_to_bool.rs diff --git a/compiler/qsc_qasm3/src/semantic/tests/expression/binary/comparison/float_to_float.rs b/compiler/qsc_qasm/src/semantic/tests/expression/binary/comparison/float_to_float.rs similarity index 100% rename from compiler/qsc_qasm3/src/semantic/tests/expression/binary/comparison/float_to_float.rs rename to compiler/qsc_qasm/src/semantic/tests/expression/binary/comparison/float_to_float.rs diff --git a/compiler/qsc_qasm3/src/semantic/tests/expression/binary/comparison/int_to_int.rs b/compiler/qsc_qasm/src/semantic/tests/expression/binary/comparison/int_to_int.rs similarity index 100% rename from compiler/qsc_qasm3/src/semantic/tests/expression/binary/comparison/int_to_int.rs rename to compiler/qsc_qasm/src/semantic/tests/expression/binary/comparison/int_to_int.rs diff --git a/compiler/qsc_qasm3/src/semantic/tests/expression/binary/comparison/uint_to_uint.rs b/compiler/qsc_qasm/src/semantic/tests/expression/binary/comparison/uint_to_uint.rs similarity index 100% rename from compiler/qsc_qasm3/src/semantic/tests/expression/binary/comparison/uint_to_uint.rs rename to compiler/qsc_qasm/src/semantic/tests/expression/binary/comparison/uint_to_uint.rs diff --git a/compiler/qsc_qasm3/src/semantic/tests/expression/binary/complex.rs b/compiler/qsc_qasm/src/semantic/tests/expression/binary/complex.rs similarity index 100% rename from compiler/qsc_qasm3/src/semantic/tests/expression/binary/complex.rs rename to compiler/qsc_qasm/src/semantic/tests/expression/binary/complex.rs diff --git a/compiler/qsc_qasm3/src/semantic/tests/expression/binary/ident.rs b/compiler/qsc_qasm/src/semantic/tests/expression/binary/ident.rs similarity index 100% rename from compiler/qsc_qasm3/src/semantic/tests/expression/binary/ident.rs rename to compiler/qsc_qasm/src/semantic/tests/expression/binary/ident.rs diff --git a/compiler/qsc_qasm3/src/semantic/tests/expression/implicit_cast_from_angle.rs b/compiler/qsc_qasm/src/semantic/tests/expression/implicit_cast_from_angle.rs similarity index 98% rename from compiler/qsc_qasm3/src/semantic/tests/expression/implicit_cast_from_angle.rs rename to compiler/qsc_qasm/src/semantic/tests/expression/implicit_cast_from_angle.rs index 7d4ef8e844..7f8c8cdee3 100644 --- a/compiler/qsc_qasm3/src/semantic/tests/expression/implicit_cast_from_angle.rs +++ b/compiler/qsc_qasm/src/semantic/tests/expression/implicit_cast_from_angle.rs @@ -154,7 +154,7 @@ fn to_implicit_int_implicitly_fails() { ty: Angle(None, false) kind: SymbolId(8) - [Qsc.Qasm3.Lowerer.CannotCast + [Qasm.Lowerer.CannotCast x cannot cast expression of type Angle(None, false) to type Int(None, false) ,-[test:3:17] @@ -197,7 +197,7 @@ fn to_explicit_int_implicitly_fails() { ty: Angle(None, false) kind: SymbolId(8) - [Qsc.Qasm3.Lowerer.CannotCast + [Qasm.Lowerer.CannotCast x cannot cast expression of type Angle(None, false) to type Int(Some(32), | false) @@ -241,7 +241,7 @@ fn to_implicit_uint_implicitly_fails() { ty: Angle(None, false) kind: SymbolId(8) - [Qsc.Qasm3.Lowerer.CannotCast + [Qasm.Lowerer.CannotCast x cannot cast expression of type Angle(None, false) to type UInt(None, | false) @@ -293,7 +293,7 @@ fn negative_lit_to_implicit_uint_implicitly_fails() { ty: Angle(None, false) kind: SymbolId(8) - [Qsc.Qasm3.Lowerer.CannotCast + [Qasm.Lowerer.CannotCast x cannot cast expression of type Angle(None, false) to type UInt(None, | false) @@ -337,7 +337,7 @@ fn to_explicit_uint_implicitly_fails() { ty: Angle(None, false) kind: SymbolId(8) - [Qsc.Qasm3.Lowerer.CannotCast + [Qasm.Lowerer.CannotCast x cannot cast expression of type Angle(None, false) to type UInt(Some(32), | false) @@ -381,7 +381,7 @@ fn to_explicit_bigint_implicitly_fails() { ty: Angle(None, false) kind: SymbolId(8) - [Qsc.Qasm3.Lowerer.CannotCast + [Qasm.Lowerer.CannotCast x cannot cast expression of type Angle(None, false) to type Int(Some(65), | false) @@ -425,7 +425,7 @@ fn to_implicit_float_implicitly_fails() { ty: Angle(None, false) kind: SymbolId(8) - [Qsc.Qasm3.Lowerer.CannotCast + [Qasm.Lowerer.CannotCast x cannot cast expression of type Angle(None, false) to type Float(None, | false) @@ -469,7 +469,7 @@ fn to_explicit_float_implicitly_fails() { ty: Angle(None, false) kind: SymbolId(8) - [Qsc.Qasm3.Lowerer.CannotCast + [Qasm.Lowerer.CannotCast x cannot cast expression of type Angle(None, false) to type Float(Some(32), | false) @@ -513,7 +513,7 @@ fn to_implicit_complex_implicitly_fails() { ty: Angle(None, false) kind: SymbolId(8) - [Qsc.Qasm3.Lowerer.CannotCast + [Qasm.Lowerer.CannotCast x cannot cast expression of type Angle(None, false) to type Complex(None, | false) @@ -557,7 +557,7 @@ fn to_explicit_complex_implicitly_fails() { ty: Angle(None, false) kind: SymbolId(8) - [Qsc.Qasm3.Lowerer.CannotCast + [Qasm.Lowerer.CannotCast x cannot cast expression of type Angle(None, false) to type | Complex(Some(32), false) diff --git a/compiler/qsc_qasm3/src/semantic/tests/expression/implicit_cast_from_bit.rs b/compiler/qsc_qasm/src/semantic/tests/expression/implicit_cast_from_bit.rs similarity index 99% rename from compiler/qsc_qasm3/src/semantic/tests/expression/implicit_cast_from_bit.rs rename to compiler/qsc_qasm/src/semantic/tests/expression/implicit_cast_from_bit.rs index b8e1d735ed..a2c855f323 100644 --- a/compiler/qsc_qasm3/src/semantic/tests/expression/implicit_cast_from_bit.rs +++ b/compiler/qsc_qasm/src/semantic/tests/expression/implicit_cast_from_bit.rs @@ -355,7 +355,7 @@ fn to_implicit_float_implicitly_fails() { ty: Bit(false) kind: SymbolId(8) - [Qsc.Qasm3.Lowerer.CannotCast + [Qasm.Lowerer.CannotCast x cannot cast expression of type Bit(false) to type Float(None, false) ,-[test:3:19] diff --git a/compiler/qsc_qasm3/src/semantic/tests/expression/implicit_cast_from_bitarray.rs b/compiler/qsc_qasm/src/semantic/tests/expression/implicit_cast_from_bitarray.rs similarity index 98% rename from compiler/qsc_qasm3/src/semantic/tests/expression/implicit_cast_from_bitarray.rs rename to compiler/qsc_qasm/src/semantic/tests/expression/implicit_cast_from_bitarray.rs index 99054a4d8b..544b02b7d2 100644 --- a/compiler/qsc_qasm3/src/semantic/tests/expression/implicit_cast_from_bitarray.rs +++ b/compiler/qsc_qasm/src/semantic/tests/expression/implicit_cast_from_bitarray.rs @@ -228,7 +228,7 @@ fn to_int_with_higher_width_implicitly_fails() { ty: BitArray(One(5), false) kind: SymbolId(9) - [Qsc.Qasm3.Lowerer.CannotCast + [Qasm.Lowerer.CannotCast x cannot cast expression of type BitArray(One(5), false) to type | Int(Some(6), false) @@ -271,7 +271,7 @@ fn to_int_with_higher_width_decl_implicitly_fails() { ty: BitArray(One(5), false) kind: SymbolId(8) - [Qsc.Qasm3.Lowerer.CannotCast + [Qasm.Lowerer.CannotCast x cannot cast expression of type BitArray(One(5), false) to type | Int(Some(6), false) @@ -320,7 +320,7 @@ fn to_int_with_lower_width_implicitly_fails() { ty: BitArray(One(5), false) kind: SymbolId(9) - [Qsc.Qasm3.Lowerer.CannotCast + [Qasm.Lowerer.CannotCast x cannot cast expression of type BitArray(One(5), false) to type | Int(Some(4), false) @@ -364,7 +364,7 @@ fn to_int_with_lower_width_decl_implicitly_fails() { ty: BitArray(One(5), false) kind: SymbolId(8) - [Qsc.Qasm3.Lowerer.CannotCast + [Qasm.Lowerer.CannotCast x cannot cast expression of type BitArray(One(5), false) to type | Int(Some(4), false) diff --git a/compiler/qsc_qasm3/src/semantic/tests/expression/implicit_cast_from_bool.rs b/compiler/qsc_qasm/src/semantic/tests/expression/implicit_cast_from_bool.rs similarity index 100% rename from compiler/qsc_qasm3/src/semantic/tests/expression/implicit_cast_from_bool.rs rename to compiler/qsc_qasm/src/semantic/tests/expression/implicit_cast_from_bool.rs diff --git a/compiler/qsc_qasm3/src/semantic/tests/expression/implicit_cast_from_float.rs b/compiler/qsc_qasm/src/semantic/tests/expression/implicit_cast_from_float.rs similarity index 99% rename from compiler/qsc_qasm3/src/semantic/tests/expression/implicit_cast_from_float.rs rename to compiler/qsc_qasm/src/semantic/tests/expression/implicit_cast_from_float.rs index 4bfe0d0925..ac7c04da94 100644 --- a/compiler/qsc_qasm3/src/semantic/tests/expression/implicit_cast_from_float.rs +++ b/compiler/qsc_qasm/src/semantic/tests/expression/implicit_cast_from_float.rs @@ -35,7 +35,7 @@ fn to_bit_implicitly_fails() { ty: Float(None, false) kind: SymbolId(8) - [Qsc.Qasm3.Lowerer.CannotCast + [Qasm.Lowerer.CannotCast x cannot cast expression of type Float(None, false) to type Bit(false) ,-[test:3:17] @@ -78,7 +78,7 @@ fn explicit_width_to_bit_implicitly_fails() { ty: Float(Some(64), false) kind: SymbolId(8) - [Qsc.Qasm3.Lowerer.CannotCast + [Qasm.Lowerer.CannotCast x cannot cast expression of type Float(Some(64), false) to type Bit(false) ,-[test:3:17] diff --git a/compiler/qsc_qasm3/src/semantic/tests/expression/implicit_cast_from_int.rs b/compiler/qsc_qasm/src/semantic/tests/expression/implicit_cast_from_int.rs similarity index 100% rename from compiler/qsc_qasm3/src/semantic/tests/expression/implicit_cast_from_int.rs rename to compiler/qsc_qasm/src/semantic/tests/expression/implicit_cast_from_int.rs diff --git a/compiler/qsc_qasm3/src/semantic/tests/statements.rs b/compiler/qsc_qasm/src/semantic/tests/statements.rs similarity index 100% rename from compiler/qsc_qasm3/src/semantic/tests/statements.rs rename to compiler/qsc_qasm/src/semantic/tests/statements.rs diff --git a/compiler/qsc_qasm3/src/semantic/tests/statements/box_stmt.rs b/compiler/qsc_qasm/src/semantic/tests/statements/box_stmt.rs similarity index 89% rename from compiler/qsc_qasm3/src/semantic/tests/statements/box_stmt.rs rename to compiler/qsc_qasm/src/semantic/tests/statements/box_stmt.rs index 1a78ce96f8..6383192124 100644 --- a/compiler/qsc_qasm3/src/semantic/tests/statements/box_stmt.rs +++ b/compiler/qsc_qasm/src/semantic/tests/statements/box_stmt.rs @@ -18,7 +18,7 @@ fn with_invalid_instruction_fails() { annotations: kind: Err - [Qsc.Qasm3.Lowerer.ClassicalStmtInBox + [Qasm.Lowerer.ClassicalStmtInBox x invalid classical statement in box ,-[test:2:9] @@ -27,7 +27,7 @@ fn with_invalid_instruction_fails() { : ^^^^^^ 3 | } `---- - , Qsc.Qasm3.Lowerer.Unimplemented + , Qasm.Lowerer.Unimplemented x this statement is not yet handled during OpenQASM 3 import: box stmt ,-[test:1:1] @@ -51,14 +51,14 @@ fn with_duration_fails() { annotations: kind: Err - [Qsc.Qasm3.Lowerer.NotSupported + [Qasm.Lowerer.NotSupported x Box with duration are not supported ,-[test:1:6] 1 | box [4us] { } : ^^^ `---- - , Qsc.Qasm3.Lowerer.Unimplemented + , Qasm.Lowerer.Unimplemented x this statement is not yet handled during OpenQASM 3 import: box stmt ,-[test:1:1] diff --git a/compiler/qsc_qasm3/src/semantic/tests/statements/break_stmt.rs b/compiler/qsc_qasm/src/semantic/tests/statements/break_stmt.rs similarity index 96% rename from compiler/qsc_qasm3/src/semantic/tests/statements/break_stmt.rs rename to compiler/qsc_qasm/src/semantic/tests/statements/break_stmt.rs index aa487a2df4..3a5fb1995a 100644 --- a/compiler/qsc_qasm3/src/semantic/tests/statements/break_stmt.rs +++ b/compiler/qsc_qasm/src/semantic/tests/statements/break_stmt.rs @@ -79,7 +79,7 @@ fn break_in_non_loop_scope_fails() { annotations: kind: Err - [Qsc.Qasm3.Lowerer.InvalidScope + [Qasm.Lowerer.InvalidScope x break can only appear in loop scopes ,-[test:1:1] @@ -123,7 +123,7 @@ fn intermediate_def_scope_fails() { annotations: kind: Err - [Qsc.Qasm3.Lowerer.DefDeclarationInNonGlobalScope + [Qasm.Lowerer.DefDeclarationInNonGlobalScope x def declarations must be done in global scope ,-[test:3:13] @@ -132,7 +132,7 @@ fn intermediate_def_scope_fails() { : ^^^^^^^^^^^^^^^^^^ 4 | } `---- - , Qsc.Qasm3.Lowerer.InvalidScope + , Qasm.Lowerer.InvalidScope x break can only appear in loop scopes ,-[test:3:23] diff --git a/compiler/qsc_qasm3/src/semantic/tests/statements/continue_stmt.rs b/compiler/qsc_qasm/src/semantic/tests/statements/continue_stmt.rs similarity index 96% rename from compiler/qsc_qasm3/src/semantic/tests/statements/continue_stmt.rs rename to compiler/qsc_qasm/src/semantic/tests/statements/continue_stmt.rs index 9a154037f0..9dab4ea4fa 100644 --- a/compiler/qsc_qasm3/src/semantic/tests/statements/continue_stmt.rs +++ b/compiler/qsc_qasm/src/semantic/tests/statements/continue_stmt.rs @@ -79,7 +79,7 @@ fn continue_in_non_loop_scope_fails() { annotations: kind: Err - [Qsc.Qasm3.Lowerer.InvalidScope + [Qasm.Lowerer.InvalidScope x continue can only appear in loop scopes ,-[test:1:1] @@ -123,7 +123,7 @@ fn intermediate_def_scope_fails() { annotations: kind: Err - [Qsc.Qasm3.Lowerer.DefDeclarationInNonGlobalScope + [Qasm.Lowerer.DefDeclarationInNonGlobalScope x def declarations must be done in global scope ,-[test:3:13] @@ -132,7 +132,7 @@ fn intermediate_def_scope_fails() { : ^^^^^^^^^^^^^^^^^^^^^ 4 | } `---- - , Qsc.Qasm3.Lowerer.InvalidScope + , Qasm.Lowerer.InvalidScope x continue can only appear in loop scopes ,-[test:3:23] diff --git a/compiler/qsc_qasm3/src/semantic/tests/statements/for_stmt.rs b/compiler/qsc_qasm/src/semantic/tests/statements/for_stmt.rs similarity index 98% rename from compiler/qsc_qasm3/src/semantic/tests/statements/for_stmt.rs rename to compiler/qsc_qasm/src/semantic/tests/statements/for_stmt.rs index 665f2853b0..245f84bf67 100644 --- a/compiler/qsc_qasm3/src/semantic/tests/statements/for_stmt.rs +++ b/compiler/qsc_qasm/src/semantic/tests/statements/for_stmt.rs @@ -30,7 +30,7 @@ fn shadowing_loop_variable_in_single_stmt_body_fails() { ty: Int(None, false) kind: Lit: Int(2) - [Qsc.Qasm3.Lowerer.RedefinedSymbol + [Qasm.Lowerer.RedefinedSymbol x redefined symbol: x ,-[test:3:13] diff --git a/compiler/qsc_qasm3/src/semantic/tests/statements/if_stmt.rs b/compiler/qsc_qasm/src/semantic/tests/statements/if_stmt.rs similarity index 98% rename from compiler/qsc_qasm3/src/semantic/tests/statements/if_stmt.rs rename to compiler/qsc_qasm/src/semantic/tests/statements/if_stmt.rs index a700f322c1..c027f578e9 100644 --- a/compiler/qsc_qasm3/src/semantic/tests/statements/if_stmt.rs +++ b/compiler/qsc_qasm/src/semantic/tests/statements/if_stmt.rs @@ -39,7 +39,7 @@ fn if_branch_doesnt_create_its_own_scope() { kind: Lit: Int(1) else_body: - [Qsc.Qasm3.Lowerer.RedefinedSymbol + [Qasm.Lowerer.RedefinedSymbol x redefined symbol: a ,-[test:3:19] @@ -90,7 +90,7 @@ fn else_branch_doesnt_create_its_own_scope() { ty: Int(None, false) kind: Lit: Int(1) - [Qsc.Qasm3.Lowerer.RedefinedSymbol + [Qasm.Lowerer.RedefinedSymbol x redefined symbol: a ,-[test:4:14] diff --git a/compiler/qsc_qasm3/src/semantic/tests/statements/switch_stmt.rs b/compiler/qsc_qasm/src/semantic/tests/statements/switch_stmt.rs similarity index 98% rename from compiler/qsc_qasm3/src/semantic/tests/statements/switch_stmt.rs rename to compiler/qsc_qasm/src/semantic/tests/statements/switch_stmt.rs index 63a1aee080..dcc4463b00 100644 --- a/compiler/qsc_qasm3/src/semantic/tests/statements/switch_stmt.rs +++ b/compiler/qsc_qasm/src/semantic/tests/statements/switch_stmt.rs @@ -30,7 +30,7 @@ fn not_supported_before_version_3_1() { block: Block [43-45]: default_case: - [Qsc.Qasm3.Lowerer.NotSupportedInThisVersion + [Qasm.Lowerer.NotSupportedInThisVersion x switch statements were introduced in version 3.1 ,-[test:3:5] diff --git a/compiler/qsc_qasm3/src/semantic/tests/statements/while_stmt.rs b/compiler/qsc_qasm/src/semantic/tests/statements/while_stmt.rs similarity index 100% rename from compiler/qsc_qasm3/src/semantic/tests/statements/while_stmt.rs rename to compiler/qsc_qasm/src/semantic/tests/statements/while_stmt.rs diff --git a/compiler/qsc_qasm3/src/semantic/types.rs b/compiler/qsc_qasm/src/semantic/types.rs similarity index 100% rename from compiler/qsc_qasm3/src/semantic/types.rs rename to compiler/qsc_qasm/src/semantic/types.rs diff --git a/compiler/qsc_qasm3/src/stdlib.rs b/compiler/qsc_qasm/src/stdlib.rs similarity index 100% rename from compiler/qsc_qasm3/src/stdlib.rs rename to compiler/qsc_qasm/src/stdlib.rs diff --git a/compiler/qsc_qasm3/src/stdlib/QasmStd/qsharp.json b/compiler/qsc_qasm/src/stdlib/QasmStd/qsharp.json similarity index 100% rename from compiler/qsc_qasm3/src/stdlib/QasmStd/qsharp.json rename to compiler/qsc_qasm/src/stdlib/QasmStd/qsharp.json diff --git a/compiler/qsc_qasm3/src/stdlib/QasmStd/src/QasmStd/Angle.qs b/compiler/qsc_qasm/src/stdlib/QasmStd/src/QasmStd/Angle.qs similarity index 100% rename from compiler/qsc_qasm3/src/stdlib/QasmStd/src/QasmStd/Angle.qs rename to compiler/qsc_qasm/src/stdlib/QasmStd/src/QasmStd/Angle.qs diff --git a/compiler/qsc_qasm3/src/stdlib/QasmStd/src/QasmStd/Convert.qs b/compiler/qsc_qasm/src/stdlib/QasmStd/src/QasmStd/Convert.qs similarity index 88% rename from compiler/qsc_qasm3/src/stdlib/QasmStd/src/QasmStd/Convert.qs rename to compiler/qsc_qasm/src/stdlib/QasmStd/src/QasmStd/Convert.qs index eec43ab655..22dfb0c015 100644 --- a/compiler/qsc_qasm3/src/stdlib/QasmStd/src/QasmStd/Convert.qs +++ b/compiler/qsc_qasm/src/stdlib/QasmStd/src/QasmStd/Convert.qs @@ -3,7 +3,7 @@ import Std.Math.AbsI; -/// The POW function is used to implement the `pow` modifier in QASM3 for integers. +/// The POW function is used to implement the `pow` modifier in QASM for integers. operation __Pow__<'T>(N: Int, op: ('T => Unit is Adj + Ctl), target : 'T) : Unit { let op = if N > 0 { () => op(target) } else { () => Adjoint op(target) }; for _ in 1..AbsI(N) { @@ -11,7 +11,7 @@ operation __Pow__<'T>(N: Int, op: ('T => Unit is Adj + Ctl), target : 'T) : Unit } } -/// The ``BARRIER`` function is used to implement the `barrier` statement in QASM3. +/// The ``BARRIER`` function is used to implement the `barrier` statement in QASM. /// The `@SimulatableIntrinsic` attribute is used to mark the operation for QIR /// generation. /// Q# doesn't support barriers, so this is a no-op. We need to figure out what @@ -20,13 +20,13 @@ operation __Pow__<'T>(N: Int, op: ('T => Unit is Adj + Ctl), target : 'T) : Unit operation __quantum__qis__barrier__body() : Unit {} -/// The ``BOOL_AS_RESULT`` function is used to implement the cast expr in QASM3 for bool to bit. -/// This already exists in the Q# library, but is defined as a marker for casts from QASM3. +/// The ``BOOL_AS_RESULT`` function is used to implement the cast expr in QASM for bool to bit. +/// This already exists in the Q# library, but is defined as a marker for casts from QASM. function __BoolAsResult__(input: Bool) : Result { Microsoft.Quantum.Convert.BoolAsResult(input) } -/// The ``BOOL_AS_INT`` function is used to implement the cast expr in QASM3 for bool to int. +/// The ``BOOL_AS_INT`` function is used to implement the cast expr in QASM for bool to int. function __BoolAsInt__(value: Bool) : Int { if value { 1 @@ -35,7 +35,7 @@ function __BoolAsInt__(value: Bool) : Int { } } -/// The ``BOOL_AS_BIGINT`` function is used to implement the cast expr in QASM3 for bool to big int. +/// The ``BOOL_AS_BIGINT`` function is used to implement the cast expr in QASM for bool to big int. function __BoolAsBigInt__(value: Bool) : BigInt { if value { @@ -45,7 +45,7 @@ function __BoolAsBigInt__(value: Bool) : BigInt { } } -/// The ``BOOL_AS_DOUBLE`` function is used to implement the cast expr in QASM3 for bool to int. +/// The ``BOOL_AS_DOUBLE`` function is used to implement the cast expr in QASM for bool to int. function __BoolAsDouble__(value: Bool) : Double { if value { @@ -55,13 +55,13 @@ function __BoolAsDouble__(value: Bool) : Double { } } -/// The ``RESULT_AS_BOOL`` function is used to implement the cast expr in QASM3 for bit to bool. -/// This already exists in the Q# library, but is defined as a marker for casts from QASM3. +/// The ``RESULT_AS_BOOL`` function is used to implement the cast expr in QASM for bit to bool. +/// This already exists in the Q# library, but is defined as a marker for casts from QASM. function __ResultAsBool__(input: Result) : Bool { Microsoft.Quantum.Convert.ResultAsBool(input) } -/// The ``RESULT_AS_INT`` function is used to implement the cast expr in QASM3 for bit to bool. +/// The ``RESULT_AS_INT`` function is used to implement the cast expr in QASM for bit to bool. function __ResultAsInt__(input: Result) : Int { if Microsoft.Quantum.Convert.ResultAsBool(input) { 1 @@ -70,7 +70,7 @@ function __ResultAsInt__(input: Result) : Int { } } -/// The ``RESULT_AS_BIGINT`` function is used to implement the cast expr in QASM3 for bit to bool. +/// The ``RESULT_AS_BIGINT`` function is used to implement the cast expr in QASM for bit to bool. function __ResultAsBigInt__(input: Result) : BigInt { if Microsoft.Quantum.Convert.ResultAsBool(input) { 1L @@ -79,7 +79,7 @@ function __ResultAsBigInt__(input: Result) : BigInt { } } -/// The ``INT_AS_RESULT_ARRAY_BE`` function is used to implement the cast expr in QASM3 for int to bit[]. +/// The ``INT_AS_RESULT_ARRAY_BE`` function is used to implement the cast expr in QASM for int to bit[]. /// with big-endian order. This is needed for round-trip conversion for bin ops. function __IntAsResultArrayBE__(number : Int, bits : Int) : Result[] { mutable runningValue = number; @@ -91,7 +91,7 @@ function __IntAsResultArrayBE__(number : Int, bits : Int) : Result[] { Microsoft.Quantum.Arrays.Reversed(result) } -/// The ``RESULT_ARRAY_AS_INT_BE`` function is used to implement the cast expr in QASM3 for bit[] to uint. +/// The ``RESULT_ARRAY_AS_INT_BE`` function is used to implement the cast expr in QASM for bit[] to uint. /// with big-endian order. This is needed for round-trip conversion for bin ops. function __ResultArrayAsIntBE__(results : Result[]) : Int { Microsoft.Quantum.Convert.ResultArrayAsInt(Microsoft.Quantum.Arrays.Reversed(results)) diff --git a/compiler/qsc_qasm3/src/stdlib/QasmStd/src/QasmStd/Intrinsic.qs b/compiler/qsc_qasm/src/stdlib/QasmStd/src/QasmStd/Intrinsic.qs similarity index 100% rename from compiler/qsc_qasm3/src/stdlib/QasmStd/src/QasmStd/Intrinsic.qs rename to compiler/qsc_qasm/src/stdlib/QasmStd/src/QasmStd/Intrinsic.qs diff --git a/compiler/qsc_qasm3/src/stdlib/angle.rs b/compiler/qsc_qasm/src/stdlib/angle.rs similarity index 100% rename from compiler/qsc_qasm3/src/stdlib/angle.rs rename to compiler/qsc_qasm/src/stdlib/angle.rs diff --git a/compiler/qsc_qasm3/src/stdlib/angle/tests.rs b/compiler/qsc_qasm/src/stdlib/angle/tests.rs similarity index 100% rename from compiler/qsc_qasm3/src/stdlib/angle/tests.rs rename to compiler/qsc_qasm/src/stdlib/angle/tests.rs diff --git a/compiler/qsc_qasm3/src/stdlib/compile.rs b/compiler/qsc_qasm/src/stdlib/compile.rs similarity index 100% rename from compiler/qsc_qasm3/src/stdlib/compile.rs rename to compiler/qsc_qasm/src/stdlib/compile.rs diff --git a/compiler/qsc_qasm3/src/tests.rs b/compiler/qsc_qasm/src/tests.rs similarity index 100% rename from compiler/qsc_qasm3/src/tests.rs rename to compiler/qsc_qasm/src/tests.rs diff --git a/compiler/qsc_qasm3/src/tests/assignment.rs b/compiler/qsc_qasm/src/tests/assignment.rs similarity index 95% rename from compiler/qsc_qasm3/src/tests/assignment.rs rename to compiler/qsc_qasm/src/tests/assignment.rs index 0b5a3cce26..472deb327d 100644 --- a/compiler/qsc_qasm3/src/tests/assignment.rs +++ b/compiler/qsc_qasm/src/tests/assignment.rs @@ -49,7 +49,7 @@ fn quantum() -> miette::Result<(), Vec> { } #[test] -#[ignore = "qasm3 parser does not support old-style decls yet"] +#[ignore = "qasm parser does not support old-style decls yet"] fn classical_old_style_decls() -> miette::Result<(), Vec> { let source = " bit[2] a; diff --git a/compiler/qsc_qasm3/src/tests/assignment/alias.rs b/compiler/qsc_qasm/src/tests/assignment/alias.rs similarity index 92% rename from compiler/qsc_qasm3/src/tests/assignment/alias.rs rename to compiler/qsc_qasm/src/tests/assignment/alias.rs index eca2f60e2c..4cfc5a79dc 100644 --- a/compiler/qsc_qasm3/src/tests/assignment/alias.rs +++ b/compiler/qsc_qasm/src/tests/assignment/alias.rs @@ -38,7 +38,7 @@ fn quantum() -> miette::Result<(), Vec> { } #[test] -#[ignore = "qasm3 parser does not support old-style decls yet"] +#[ignore = "qasm parser does not support old-style decls yet"] fn classical_old_style_decls() -> miette::Result<(), Vec> { let source = " creg a[2]; @@ -52,7 +52,7 @@ fn classical_old_style_decls() -> miette::Result<(), Vec> { } #[test] -#[ignore = "qasm3 parser does not support old-style decls yet"] +#[ignore = "qasm parser does not support old-style decls yet"] fn quantum_old_style_decls() -> miette::Result<(), Vec> { let source = " qreg q1[5]; diff --git a/compiler/qsc_qasm3/src/tests/declaration.rs b/compiler/qsc_qasm/src/tests/declaration.rs similarity index 100% rename from compiler/qsc_qasm3/src/tests/declaration.rs rename to compiler/qsc_qasm/src/tests/declaration.rs diff --git a/compiler/qsc_qasm3/src/tests/declaration/array.rs b/compiler/qsc_qasm/src/tests/declaration/array.rs similarity index 100% rename from compiler/qsc_qasm3/src/tests/declaration/array.rs rename to compiler/qsc_qasm/src/tests/declaration/array.rs diff --git a/compiler/qsc_qasm3/src/tests/declaration/array/bit.rs b/compiler/qsc_qasm/src/tests/declaration/array/bit.rs similarity index 100% rename from compiler/qsc_qasm3/src/tests/declaration/array/bit.rs rename to compiler/qsc_qasm/src/tests/declaration/array/bit.rs diff --git a/compiler/qsc_qasm3/src/tests/declaration/array/qubit.rs b/compiler/qsc_qasm/src/tests/declaration/array/qubit.rs similarity index 100% rename from compiler/qsc_qasm3/src/tests/declaration/array/qubit.rs rename to compiler/qsc_qasm/src/tests/declaration/array/qubit.rs diff --git a/compiler/qsc_qasm3/src/tests/declaration/bit.rs b/compiler/qsc_qasm/src/tests/declaration/bit.rs similarity index 100% rename from compiler/qsc_qasm3/src/tests/declaration/bit.rs rename to compiler/qsc_qasm/src/tests/declaration/bit.rs diff --git a/compiler/qsc_qasm3/src/tests/declaration/bool.rs b/compiler/qsc_qasm/src/tests/declaration/bool.rs similarity index 100% rename from compiler/qsc_qasm3/src/tests/declaration/bool.rs rename to compiler/qsc_qasm/src/tests/declaration/bool.rs diff --git a/compiler/qsc_qasm3/src/tests/declaration/complex.rs b/compiler/qsc_qasm/src/tests/declaration/complex.rs similarity index 100% rename from compiler/qsc_qasm3/src/tests/declaration/complex.rs rename to compiler/qsc_qasm/src/tests/declaration/complex.rs diff --git a/compiler/qsc_qasm3/src/tests/declaration/def.rs b/compiler/qsc_qasm/src/tests/declaration/def.rs similarity index 95% rename from compiler/qsc_qasm3/src/tests/declaration/def.rs rename to compiler/qsc_qasm/src/tests/declaration/def.rs index 315eb94ab6..318a2d68aa 100644 --- a/compiler/qsc_qasm3/src/tests/declaration/def.rs +++ b/compiler/qsc_qasm/src/tests/declaration/def.rs @@ -124,7 +124,7 @@ fn return_expr_on_void_function_fails() { }; expect![[r#" - [Qsc.Qasm3.Lowerer.ReturningExpressionFromVoidSubroutine + [Qasm.Lowerer.ReturningExpressionFromVoidSubroutine x cannot return an expression from a void subroutine ,-[Test.qasm:3:20] @@ -150,7 +150,7 @@ fn missing_return_expr_on_non_void_function_fails() { }; expect![[r#" - [Qsc.Qasm3.Lowerer.MissingTargetExpressionInReturnStmt + [Qasm.Lowerer.MissingTargetExpressionInReturnStmt x return statements on a non-void subroutine should have a target expression ,-[Test.qasm:3:13] @@ -198,7 +198,7 @@ fn capturing_non_const_external_variable_fails() { }; expect![[r#" - [Qsc.Qasm3.Lowerer.UndefinedSymbol + [Qasm.Lowerer.UndefinedSymbol x undefined symbol: a ,-[Test.qasm:4:20] @@ -207,7 +207,7 @@ fn capturing_non_const_external_variable_fails() { : ^ 5 | } `---- - , Qsc.Qasm3.Lowerer.CannotCast + , Qasm.Lowerer.CannotCast x cannot cast expression of type Err to type Int(None, false) ,-[Test.qasm:4:20] @@ -234,7 +234,7 @@ fn capturing_non_const_evaluatable_external_variable_fails() { }; expect![[r#" - [Qsc.Qasm3.Compile.NegativeUIntValue + [Qasm.Compile.NegativeUIntValue x uint expression must evaluate to a non-negative value, but it evaluated | to -3 @@ -244,7 +244,7 @@ fn capturing_non_const_evaluatable_external_variable_fails() { : ^^^^ 3 | def f() -> int { `---- - , Qsc.Qasm3.Lowerer.ExprMustBeConst + , Qasm.Lowerer.ExprMustBeConst x a captured variable must be a const expression ,-[Test.qasm:4:20] diff --git a/compiler/qsc_qasm3/src/tests/declaration/float.rs b/compiler/qsc_qasm/src/tests/declaration/float.rs similarity index 100% rename from compiler/qsc_qasm3/src/tests/declaration/float.rs rename to compiler/qsc_qasm/src/tests/declaration/float.rs diff --git a/compiler/qsc_qasm3/src/tests/declaration/gate.rs b/compiler/qsc_qasm/src/tests/declaration/gate.rs similarity index 95% rename from compiler/qsc_qasm3/src/tests/declaration/gate.rs rename to compiler/qsc_qasm/src/tests/declaration/gate.rs index 219312632e..f51be04f0a 100644 --- a/compiler/qsc_qasm3/src/tests/declaration/gate.rs +++ b/compiler/qsc_qasm/src/tests/declaration/gate.rs @@ -120,7 +120,7 @@ fn capturing_non_const_external_variable_fails() { }; expect![[r#" - [Qsc.Qasm3.Lowerer.UndefinedSymbol + [Qasm.Lowerer.UndefinedSymbol x undefined symbol: a ,-[Test.qasm:4:21] @@ -129,7 +129,7 @@ fn capturing_non_const_external_variable_fails() { : ^ 5 | } `---- - , Qsc.Qasm3.Lowerer.CannotCast + , Qasm.Lowerer.CannotCast x cannot cast expression of type Err to type Int(None, false) ,-[Test.qasm:4:21] @@ -156,7 +156,7 @@ fn capturing_non_const_evaluatable_external_variable_fails() { }; expect![[r#" - [Qsc.Qasm3.Compile.NegativeUIntValue + [Qasm.Compile.NegativeUIntValue x uint expression must evaluate to a non-negative value, but it evaluated | to -3 @@ -166,7 +166,7 @@ fn capturing_non_const_evaluatable_external_variable_fails() { : ^^^^ 3 | gate my_gate q { `---- - , Qsc.Qasm3.Lowerer.ExprMustBeConst + , Qasm.Lowerer.ExprMustBeConst x a captured variable must be a const expression ,-[Test.qasm:4:21] diff --git a/compiler/qsc_qasm3/src/tests/declaration/integer.rs b/compiler/qsc_qasm/src/tests/declaration/integer.rs similarity index 100% rename from compiler/qsc_qasm3/src/tests/declaration/integer.rs rename to compiler/qsc_qasm/src/tests/declaration/integer.rs diff --git a/compiler/qsc_qasm3/src/tests/declaration/io.rs b/compiler/qsc_qasm/src/tests/declaration/io.rs similarity index 100% rename from compiler/qsc_qasm3/src/tests/declaration/io.rs rename to compiler/qsc_qasm/src/tests/declaration/io.rs diff --git a/compiler/qsc_qasm3/src/tests/declaration/io/explicit_input.rs b/compiler/qsc_qasm/src/tests/declaration/io/explicit_input.rs similarity index 100% rename from compiler/qsc_qasm3/src/tests/declaration/io/explicit_input.rs rename to compiler/qsc_qasm/src/tests/declaration/io/explicit_input.rs diff --git a/compiler/qsc_qasm3/src/tests/declaration/io/explicit_output.rs b/compiler/qsc_qasm/src/tests/declaration/io/explicit_output.rs similarity index 100% rename from compiler/qsc_qasm3/src/tests/declaration/io/explicit_output.rs rename to compiler/qsc_qasm/src/tests/declaration/io/explicit_output.rs diff --git a/compiler/qsc_qasm3/src/tests/declaration/io/implicit_output.rs b/compiler/qsc_qasm/src/tests/declaration/io/implicit_output.rs similarity index 100% rename from compiler/qsc_qasm3/src/tests/declaration/io/implicit_output.rs rename to compiler/qsc_qasm/src/tests/declaration/io/implicit_output.rs diff --git a/compiler/qsc_qasm3/src/tests/declaration/qubit.rs b/compiler/qsc_qasm/src/tests/declaration/qubit.rs similarity index 100% rename from compiler/qsc_qasm3/src/tests/declaration/qubit.rs rename to compiler/qsc_qasm/src/tests/declaration/qubit.rs diff --git a/compiler/qsc_qasm3/src/tests/declaration/unsigned_integer.rs b/compiler/qsc_qasm/src/tests/declaration/unsigned_integer.rs similarity index 100% rename from compiler/qsc_qasm3/src/tests/declaration/unsigned_integer.rs rename to compiler/qsc_qasm/src/tests/declaration/unsigned_integer.rs diff --git a/compiler/qsc_qasm3/src/tests/expression.rs b/compiler/qsc_qasm/src/tests/expression.rs similarity index 100% rename from compiler/qsc_qasm3/src/tests/expression.rs rename to compiler/qsc_qasm/src/tests/expression.rs diff --git a/compiler/qsc_qasm3/src/tests/expression/binary.rs b/compiler/qsc_qasm/src/tests/expression/binary.rs similarity index 100% rename from compiler/qsc_qasm3/src/tests/expression/binary.rs rename to compiler/qsc_qasm/src/tests/expression/binary.rs diff --git a/compiler/qsc_qasm3/src/tests/expression/binary/angle.rs b/compiler/qsc_qasm/src/tests/expression/binary/angle.rs similarity index 100% rename from compiler/qsc_qasm3/src/tests/expression/binary/angle.rs rename to compiler/qsc_qasm/src/tests/expression/binary/angle.rs diff --git a/compiler/qsc_qasm3/src/tests/expression/binary/arithmetic_conversions.rs b/compiler/qsc_qasm/src/tests/expression/binary/arithmetic_conversions.rs similarity index 100% rename from compiler/qsc_qasm3/src/tests/expression/binary/arithmetic_conversions.rs rename to compiler/qsc_qasm/src/tests/expression/binary/arithmetic_conversions.rs diff --git a/compiler/qsc_qasm3/src/tests/expression/binary/comparison.rs b/compiler/qsc_qasm/src/tests/expression/binary/comparison.rs similarity index 97% rename from compiler/qsc_qasm3/src/tests/expression/binary/comparison.rs rename to compiler/qsc_qasm/src/tests/expression/binary/comparison.rs index a14625c864..d656a0c381 100644 --- a/compiler/qsc_qasm3/src/tests/expression/binary/comparison.rs +++ b/compiler/qsc_qasm/src/tests/expression/binary/comparison.rs @@ -24,7 +24,7 @@ fn int_var_comparisons_can_be_translated() -> miette::Result<(), Vec> { let qsharp = compile_qasm_to_qsharp_file(source)?; expect![[r#" - namespace qasm3_import { + namespace qasm_import { import QasmStd.Angle.*; import QasmStd.Convert.*; import QasmStd.Intrinsic.*; @@ -60,7 +60,7 @@ fn uint_var_comparisons_can_be_translated() -> miette::Result<(), Vec> { let qsharp = compile_qasm_to_qsharp_file(source)?; expect![[r#" - namespace qasm3_import { + namespace qasm_import { import QasmStd.Angle.*; import QasmStd.Convert.*; import QasmStd.Intrinsic.*; @@ -96,7 +96,7 @@ fn bit_var_comparisons_can_be_translated() -> miette::Result<(), Vec> { let qsharp = compile_qasm_to_qsharp_file(source)?; expect![[r#" - namespace qasm3_import { + namespace qasm_import { import QasmStd.Angle.*; import QasmStd.Convert.*; import QasmStd.Intrinsic.*; @@ -132,7 +132,7 @@ fn bitarray_var_comparisons_can_be_translated() -> miette::Result<(), Vec miette::Result<(), Vec< let qsharp = compile_qasm_to_qsharp_file(source)?; expect![[r#" - namespace qasm3_import { + namespace qasm_import { import QasmStd.Angle.*; import QasmStd.Convert.*; import QasmStd.Intrinsic.*; @@ -214,7 +214,7 @@ fn float_var_comparisons_can_be_translated() -> miette::Result<(), Vec> let qsharp = compile_qasm_to_qsharp_file(source)?; expect![[r#" - namespace qasm3_import { + namespace qasm_import { import QasmStd.Angle.*; import QasmStd.Convert.*; import QasmStd.Intrinsic.*; @@ -252,7 +252,7 @@ fn bool_var_comparisons_can_be_translated() -> miette::Result<(), Vec> { let qsharp = compile_qasm_to_qsharp_file(source)?; expect![[r#" - namespace qasm3_import { + namespace qasm_import { import QasmStd.Angle.*; import QasmStd.Convert.*; import QasmStd.Intrinsic.*; diff --git a/compiler/qsc_qasm3/src/tests/expression/binary/complex.rs b/compiler/qsc_qasm/src/tests/expression/binary/complex.rs similarity index 100% rename from compiler/qsc_qasm3/src/tests/expression/binary/complex.rs rename to compiler/qsc_qasm/src/tests/expression/binary/complex.rs diff --git a/compiler/qsc_qasm3/src/tests/expression/binary/ident.rs b/compiler/qsc_qasm/src/tests/expression/binary/ident.rs similarity index 100% rename from compiler/qsc_qasm3/src/tests/expression/binary/ident.rs rename to compiler/qsc_qasm/src/tests/expression/binary/ident.rs diff --git a/compiler/qsc_qasm3/src/tests/expression/binary/literal.rs b/compiler/qsc_qasm/src/tests/expression/binary/literal.rs similarity index 100% rename from compiler/qsc_qasm3/src/tests/expression/binary/literal.rs rename to compiler/qsc_qasm/src/tests/expression/binary/literal.rs diff --git a/compiler/qsc_qasm3/src/tests/expression/binary/literal/multiplication.rs b/compiler/qsc_qasm/src/tests/expression/binary/literal/multiplication.rs similarity index 100% rename from compiler/qsc_qasm3/src/tests/expression/binary/literal/multiplication.rs rename to compiler/qsc_qasm/src/tests/expression/binary/literal/multiplication.rs diff --git a/compiler/qsc_qasm3/src/tests/expression/bits.rs b/compiler/qsc_qasm/src/tests/expression/bits.rs similarity index 98% rename from compiler/qsc_qasm3/src/tests/expression/bits.rs rename to compiler/qsc_qasm/src/tests/expression/bits.rs index 6751290904..04890be817 100644 --- a/compiler/qsc_qasm3/src/tests/expression/bits.rs +++ b/compiler/qsc_qasm/src/tests/expression/bits.rs @@ -26,7 +26,7 @@ fn bit_array_bits_and_register_ops() -> miette::Result<(), Vec> { "#; let qsharp = compile_qasm_to_qsharp_file(source)?; expect![[r#" - namespace qasm3_import { + namespace qasm_import { import QasmStd.Angle.*; import QasmStd.Convert.*; import QasmStd.Intrinsic.*; diff --git a/compiler/qsc_qasm3/src/tests/expression/function_call.rs b/compiler/qsc_qasm/src/tests/expression/function_call.rs similarity index 97% rename from compiler/qsc_qasm3/src/tests/expression/function_call.rs rename to compiler/qsc_qasm/src/tests/expression/function_call.rs index 41b29a9f6b..8290f62538 100644 --- a/compiler/qsc_qasm3/src/tests/expression/function_call.rs +++ b/compiler/qsc_qasm/src/tests/expression/function_call.rs @@ -141,7 +141,7 @@ fn funcall_with_too_few_arguments_generates_error() { }; expect![[r#" - [Qsc.Qasm3.Lowerer.InvalidNumberOfClassicalArgs + [Qasm.Lowerer.InvalidNumberOfClassicalArgs x gate expects 1 classical arguments, but 0 were provided ,-[Test.qasm:6:9] @@ -169,7 +169,7 @@ fn funcall_with_too_many_arguments_generates_error() { }; expect![[r#" - [Qsc.Qasm3.Lowerer.InvalidNumberOfClassicalArgs + [Qasm.Lowerer.InvalidNumberOfClassicalArgs x gate expects 1 classical arguments, but 2 were provided ,-[Test.qasm:6:9] @@ -248,7 +248,7 @@ fn classical_decl_initialized_with_incompatible_funcall_errors() { }; expect![[r#" - [Qsc.Qasm3.Lowerer.CannotCast + [Qasm.Lowerer.CannotCast x cannot cast expression of type Float(None, false) to type Bit(false) ,-[Test.qasm:6:17] @@ -304,7 +304,7 @@ fn funcall_implicit_arg_cast_uint_to_qubit_errors() { }; expect![[r#" - [Qsc.Qasm3.Lowerer.CannotCast + [Qasm.Lowerer.CannotCast x cannot cast expression of type Int(None, true) to type QubitArray(One(2)) ,-[Test.qasm:6:24] diff --git a/compiler/qsc_qasm3/src/tests/expression/ident.rs b/compiler/qsc_qasm/src/tests/expression/ident.rs similarity index 96% rename from compiler/qsc_qasm3/src/tests/expression/ident.rs rename to compiler/qsc_qasm/src/tests/expression/ident.rs index 53b7719c04..52e7da2382 100644 --- a/compiler/qsc_qasm3/src/tests/expression/ident.rs +++ b/compiler/qsc_qasm/src/tests/expression/ident.rs @@ -18,7 +18,7 @@ fn unresolved_idenfiers_raise_symbol_error() { let errs: Vec<_> = errors.iter().map(|e| format!("{e:?}")).collect(); let errs_string = errs.join("\n"); expect![[r#" - Qsc.Qasm3.Lowerer.UndefinedSymbol + Qasm.Lowerer.UndefinedSymbol x undefined symbol: t ,-[Test.qasm:2:19] @@ -28,7 +28,7 @@ fn unresolved_idenfiers_raise_symbol_error() { 3 | `---- - Qsc.Qasm3.Lowerer.CannotCast + Qasm.Lowerer.CannotCast x cannot cast expression of type Err to type Float(None, false) ,-[Test.qasm:2:19] @@ -54,7 +54,7 @@ fn redefining_symbols_in_same_scope_raise_symbol_error() { panic!("Expected an error"); }; assert_eq!(1, errors.len(), "Expected one error"); - expect!["redefined symbol: x"].assert_eq(&errors[0].to_string()); + expect![["redefined symbol: x"]].assert_eq(&errors[0].to_string()); } #[test] diff --git a/compiler/qsc_qasm3/src/tests/expression/implicit_cast_from_bit.rs b/compiler/qsc_qasm/src/tests/expression/implicit_cast_from_bit.rs similarity index 100% rename from compiler/qsc_qasm3/src/tests/expression/implicit_cast_from_bit.rs rename to compiler/qsc_qasm/src/tests/expression/implicit_cast_from_bit.rs diff --git a/compiler/qsc_qasm3/src/tests/expression/implicit_cast_from_bitarray.rs b/compiler/qsc_qasm/src/tests/expression/implicit_cast_from_bitarray.rs similarity index 100% rename from compiler/qsc_qasm3/src/tests/expression/implicit_cast_from_bitarray.rs rename to compiler/qsc_qasm/src/tests/expression/implicit_cast_from_bitarray.rs diff --git a/compiler/qsc_qasm3/src/tests/expression/implicit_cast_from_bool.rs b/compiler/qsc_qasm/src/tests/expression/implicit_cast_from_bool.rs similarity index 100% rename from compiler/qsc_qasm3/src/tests/expression/implicit_cast_from_bool.rs rename to compiler/qsc_qasm/src/tests/expression/implicit_cast_from_bool.rs diff --git a/compiler/qsc_qasm3/src/tests/expression/implicit_cast_from_complex.rs b/compiler/qsc_qasm/src/tests/expression/implicit_cast_from_complex.rs similarity index 100% rename from compiler/qsc_qasm3/src/tests/expression/implicit_cast_from_complex.rs rename to compiler/qsc_qasm/src/tests/expression/implicit_cast_from_complex.rs diff --git a/compiler/qsc_qasm3/src/tests/expression/implicit_cast_from_float.rs b/compiler/qsc_qasm/src/tests/expression/implicit_cast_from_float.rs similarity index 100% rename from compiler/qsc_qasm3/src/tests/expression/implicit_cast_from_float.rs rename to compiler/qsc_qasm/src/tests/expression/implicit_cast_from_float.rs diff --git a/compiler/qsc_qasm3/src/tests/expression/implicit_cast_from_int.rs b/compiler/qsc_qasm/src/tests/expression/implicit_cast_from_int.rs similarity index 100% rename from compiler/qsc_qasm3/src/tests/expression/implicit_cast_from_int.rs rename to compiler/qsc_qasm/src/tests/expression/implicit_cast_from_int.rs diff --git a/compiler/qsc_qasm3/src/tests/expression/indexed.rs b/compiler/qsc_qasm/src/tests/expression/indexed.rs similarity index 100% rename from compiler/qsc_qasm3/src/tests/expression/indexed.rs rename to compiler/qsc_qasm/src/tests/expression/indexed.rs diff --git a/compiler/qsc_qasm3/src/tests/expression/unary.rs b/compiler/qsc_qasm/src/tests/expression/unary.rs similarity index 98% rename from compiler/qsc_qasm3/src/tests/expression/unary.rs rename to compiler/qsc_qasm/src/tests/expression/unary.rs index da6127bb22..5898809044 100644 --- a/compiler/qsc_qasm3/src/tests/expression/unary.rs +++ b/compiler/qsc_qasm/src/tests/expression/unary.rs @@ -18,7 +18,7 @@ fn bitwise_not_int_fails() { }; expect![[r#" - [Qsc.Qasm3.Lowerer.TypeDoesNotSupportedUnaryNegation + [Qasm.Lowerer.TypeDoesNotSupportedUnaryNegation x unary negation is not allowed for instances of Int(None, false) ,-[Test.qasm:3:18] diff --git a/compiler/qsc_qasm3/src/tests/fuzz.rs b/compiler/qsc_qasm/src/tests/fuzz.rs similarity index 100% rename from compiler/qsc_qasm3/src/tests/fuzz.rs rename to compiler/qsc_qasm/src/tests/fuzz.rs diff --git a/compiler/qsc_qasm3/src/tests/output.rs b/compiler/qsc_qasm/src/tests/output.rs similarity index 98% rename from compiler/qsc_qasm3/src/tests/output.rs rename to compiler/qsc_qasm/src/tests/output.rs index f3cd433dbc..69fef17b86 100644 --- a/compiler/qsc_qasm3/src/tests/output.rs +++ b/compiler/qsc_qasm/src/tests/output.rs @@ -39,7 +39,7 @@ fn using_re_semantics_removes_output() -> miette::Result<(), Vec> { fail_on_compilation_errors(&unit); let qsharp = gen_qsharp(&unit.package.expect("no package found")); expect![[r#" - namespace qasm3_import { + namespace qasm_import { import QasmStd.Angle.*; import QasmStd.Convert.*; import QasmStd.Intrinsic.*; @@ -90,7 +90,7 @@ fn using_qasm_semantics_captures_all_classical_decls_as_output() -> miette::Resu fail_on_compilation_errors(&unit); let qsharp = gen_qsharp(&unit.package.expect("no package found")); expect![[r#" - namespace qasm3_import { + namespace qasm_import { import QasmStd.Angle.*; import QasmStd.Convert.*; import QasmStd.Intrinsic.*; @@ -141,7 +141,7 @@ fn using_qiskit_semantics_only_bit_array_is_captured_and_reversed( fail_on_compilation_errors(&unit); let qsharp = gen_qsharp(&unit.package.expect("no package found")); expect![[r#" - namespace qasm3_import { + namespace qasm_import { import QasmStd.Angle.*; import QasmStd.Convert.*; import QasmStd.Intrinsic.*; @@ -200,7 +200,7 @@ c2[2] = measure q[4]; let package = unit.package.expect("no package found"); let qsharp = gen_qsharp(&package.clone()); expect![[r#" - namespace qasm3_import { + namespace qasm_import { import QasmStd.Angle.*; import QasmStd.Convert.*; import QasmStd.Intrinsic.*; diff --git a/compiler/qsc_qasm3/src/tests/sample_circuits.rs b/compiler/qsc_qasm/src/tests/sample_circuits.rs similarity index 100% rename from compiler/qsc_qasm3/src/tests/sample_circuits.rs rename to compiler/qsc_qasm/src/tests/sample_circuits.rs diff --git a/compiler/qsc_qasm3/src/tests/sample_circuits/bell_pair.rs b/compiler/qsc_qasm/src/tests/sample_circuits/bell_pair.rs similarity index 100% rename from compiler/qsc_qasm3/src/tests/sample_circuits/bell_pair.rs rename to compiler/qsc_qasm/src/tests/sample_circuits/bell_pair.rs diff --git a/compiler/qsc_qasm3/src/tests/sample_circuits/rgqft_multiplier.rs b/compiler/qsc_qasm/src/tests/sample_circuits/rgqft_multiplier.rs similarity index 100% rename from compiler/qsc_qasm3/src/tests/sample_circuits/rgqft_multiplier.rs rename to compiler/qsc_qasm/src/tests/sample_circuits/rgqft_multiplier.rs diff --git a/compiler/qsc_qasm3/src/tests/scopes.rs b/compiler/qsc_qasm/src/tests/scopes.rs similarity index 100% rename from compiler/qsc_qasm3/src/tests/scopes.rs rename to compiler/qsc_qasm/src/tests/scopes.rs diff --git a/compiler/qsc_qasm3/src/tests/statement.rs b/compiler/qsc_qasm/src/tests/statement.rs similarity index 100% rename from compiler/qsc_qasm3/src/tests/statement.rs rename to compiler/qsc_qasm/src/tests/statement.rs diff --git a/compiler/qsc_qasm3/src/tests/statement/annotation.rs b/compiler/qsc_qasm/src/tests/statement/annotation.rs similarity index 100% rename from compiler/qsc_qasm3/src/tests/statement/annotation.rs rename to compiler/qsc_qasm/src/tests/statement/annotation.rs diff --git a/compiler/qsc_qasm3/src/tests/statement/const_eval.rs b/compiler/qsc_qasm/src/tests/statement/const_eval.rs similarity index 97% rename from compiler/qsc_qasm3/src/tests/statement/const_eval.rs rename to compiler/qsc_qasm/src/tests/statement/const_eval.rs index 794a9daa31..01cd4c12d9 100644 --- a/compiler/qsc_qasm3/src/tests/statement/const_eval.rs +++ b/compiler/qsc_qasm/src/tests/statement/const_eval.rs @@ -76,7 +76,7 @@ fn non_const_exprs_fail_in_bitarray_size_position() { let errs: Vec<_> = errs.iter().map(|e| format!("{e:?}")).collect(); let errs_string = errs.join("\n"); expect![[r#" - Qsc.Qasm3.Compile.ExprMustBeConst + Qasm.Compile.ExprMustBeConst x expression must be const ,-[Test.qasm:5:13] @@ -86,7 +86,7 @@ fn non_const_exprs_fail_in_bitarray_size_position() { 6 | bit[c] r2; `---- - Qsc.Qasm3.Lowerer.ExprMustBeConst + Qasm.Lowerer.ExprMustBeConst x designator must be a const expression ,-[Test.qasm:5:13] @@ -96,7 +96,7 @@ fn non_const_exprs_fail_in_bitarray_size_position() { 6 | bit[c] r2; `---- - Qsc.Qasm3.Compile.ExprMustBeConst + Qasm.Compile.ExprMustBeConst x expression must be const ,-[Test.qasm:6:13] @@ -106,7 +106,7 @@ fn non_const_exprs_fail_in_bitarray_size_position() { 7 | `---- - Qsc.Qasm3.Lowerer.ExprMustBeConst + Qasm.Lowerer.ExprMustBeConst x designator must be a const expression ,-[Test.qasm:6:13] @@ -467,7 +467,7 @@ fn binary_op_shl_creg_fails() { let errs: Vec<_> = errs.iter().map(|e| format!("{e:?}")).collect(); let errs_string = errs.join("\n"); expect![[r#" - Qasm3.Parse.Rule + Qasm.Parse.Rule x expected scalar or array type, found keyword `creg` ,-[Test.qasm:2:15] @@ -477,7 +477,7 @@ fn binary_op_shl_creg_fails() { 3 | const creg b[3] = a << 2; `---- - Qasm3.Parse.Rule + Qasm.Parse.Rule x expected scalar or array type, found keyword `creg` ,-[Test.qasm:3:15] @@ -487,7 +487,7 @@ fn binary_op_shl_creg_fails() { 4 | bit[b] r; `---- - Qsc.Qasm3.Lowerer.UndefinedSymbol + Qasm.Lowerer.UndefinedSymbol x undefined symbol: b ,-[Test.qasm:4:13] @@ -497,7 +497,7 @@ fn binary_op_shl_creg_fails() { 5 | `---- - Qsc.Qasm3.Lowerer.CannotCast + Qasm.Lowerer.CannotCast x cannot cast expression of type Err to type UInt(None, true) ,-[Test.qasm:4:13] @@ -507,7 +507,7 @@ fn binary_op_shl_creg_fails() { 5 | `---- - Qsc.Qasm3.Compile.ExprMustBeConst + Qasm.Compile.ExprMustBeConst x expression must be const ,-[Test.qasm:4:13] @@ -517,7 +517,7 @@ fn binary_op_shl_creg_fails() { 5 | `---- - Qsc.Qasm3.Lowerer.ExprMustBeConst + Qasm.Lowerer.ExprMustBeConst x designator must be a const expression ,-[Test.qasm:4:13] @@ -640,7 +640,7 @@ fn binary_op_shr_creg_fails() { let errs: Vec<_> = errs.iter().map(|e| format!("{e:?}")).collect(); let errs_string = errs.join("\n"); expect![[r#" - Qasm3.Parse.Rule + Qasm.Parse.Rule x expected scalar or array type, found keyword `creg` ,-[Test.qasm:2:15] @@ -650,7 +650,7 @@ fn binary_op_shr_creg_fails() { 3 | const creg b[4] = a >> 2; `---- - Qasm3.Parse.Rule + Qasm.Parse.Rule x expected scalar or array type, found keyword `creg` ,-[Test.qasm:3:15] @@ -660,7 +660,7 @@ fn binary_op_shr_creg_fails() { 4 | bit[b] r; `---- - Qsc.Qasm3.Lowerer.UndefinedSymbol + Qasm.Lowerer.UndefinedSymbol x undefined symbol: b ,-[Test.qasm:4:13] @@ -670,7 +670,7 @@ fn binary_op_shr_creg_fails() { 5 | `---- - Qsc.Qasm3.Lowerer.CannotCast + Qasm.Lowerer.CannotCast x cannot cast expression of type Err to type UInt(None, true) ,-[Test.qasm:4:13] @@ -680,7 +680,7 @@ fn binary_op_shr_creg_fails() { 5 | `---- - Qsc.Qasm3.Compile.ExprMustBeConst + Qasm.Compile.ExprMustBeConst x expression must be const ,-[Test.qasm:4:13] @@ -690,7 +690,7 @@ fn binary_op_shr_creg_fails() { 5 | `---- - Qsc.Qasm3.Lowerer.ExprMustBeConst + Qasm.Lowerer.ExprMustBeConst x designator must be a const expression ,-[Test.qasm:4:13] @@ -1995,7 +1995,7 @@ fn binary_op_err_type_fails() { let errs: Vec<_> = errs.iter().map(|e| format!("{e:?}")).collect(); let errs_string = errs.join("\n"); expect![[r#" - Qsc.Qasm3.Lowerer.UndefinedSymbol + Qasm.Lowerer.UndefinedSymbol x undefined symbol: a ,-[Test.qasm:2:13] @@ -2005,7 +2005,7 @@ fn binary_op_err_type_fails() { 3 | `---- - Qsc.Qasm3.Lowerer.UndefinedSymbol + Qasm.Lowerer.UndefinedSymbol x undefined symbol: b ,-[Test.qasm:2:17] @@ -2015,7 +2015,7 @@ fn binary_op_err_type_fails() { 3 | `---- - Qsc.Qasm3.Lowerer.CannotCast + Qasm.Lowerer.CannotCast x cannot cast expression of type Err to type UInt(None, true) ,-[Test.qasm:2:13] @@ -2025,7 +2025,7 @@ fn binary_op_err_type_fails() { 3 | `---- - Qsc.Qasm3.Compile.ExprMustBeConst + Qasm.Compile.ExprMustBeConst x expression must be const ,-[Test.qasm:2:13] @@ -2035,7 +2035,7 @@ fn binary_op_err_type_fails() { 3 | `---- - Qsc.Qasm3.Lowerer.ExprMustBeConst + Qasm.Lowerer.ExprMustBeConst x designator must be a const expression ,-[Test.qasm:2:13] @@ -2045,7 +2045,7 @@ fn binary_op_err_type_fails() { 3 | `---- - Qsc.Qasm3.Lowerer.CannotCastLiteral + Qasm.Lowerer.CannotCastLiteral x cannot cast literal expression of type Int(None, true) to type Err ,-[Test.qasm:2:9] @@ -2070,7 +2070,7 @@ fn fuzzer_issue_2294() { let errs: Vec<_> = errs.iter().map(|e| format!("{e:?}")).collect(); let errs_string = errs.join("\n"); expect![[r#" - Qasm3.Parse.Token + Qasm.Parse.Token x expected `;`, found EOF ,-[Test.qasm:3:5] @@ -2078,7 +2078,7 @@ fn fuzzer_issue_2294() { 3 | `---- - Qasm3.Parse.MissingGateCallOperands + Qasm.Parse.MissingGateCallOperands x missing gate call operands ,-[Test.qasm:2:9] @@ -2088,7 +2088,7 @@ fn fuzzer_issue_2294() { 3 | `---- - Qsc.Qasm3.Lowerer.UndefinedSymbol + Qasm.Lowerer.UndefinedSymbol x undefined symbol: _ ,-[Test.qasm:2:16] @@ -2098,7 +2098,7 @@ fn fuzzer_issue_2294() { 3 | `---- - Qsc.Qasm3.Lowerer.CannotCast + Qasm.Lowerer.CannotCast x cannot cast expression of type Err to type Float(None, true) ,-[Test.qasm:2:16] @@ -2108,7 +2108,7 @@ fn fuzzer_issue_2294() { 3 | `---- - Qsc.Qasm3.Compile.ExprMustBeConst + Qasm.Compile.ExprMustBeConst x expression must be const ,-[Test.qasm:2:16] @@ -2118,7 +2118,7 @@ fn fuzzer_issue_2294() { 3 | `---- - Qsc.Qasm3.Lowerer.ExprMustBeConst + Qasm.Lowerer.ExprMustBeConst x ctrl modifier argument must be a const expression ,-[Test.qasm:2:14] diff --git a/compiler/qsc_qasm3/src/tests/statement/end.rs b/compiler/qsc_qasm/src/tests/statement/end.rs similarity index 100% rename from compiler/qsc_qasm3/src/tests/statement/end.rs rename to compiler/qsc_qasm/src/tests/statement/end.rs diff --git a/compiler/qsc_qasm3/src/tests/statement/for_loop.rs b/compiler/qsc_qasm/src/tests/statement/for_loop.rs similarity index 100% rename from compiler/qsc_qasm3/src/tests/statement/for_loop.rs rename to compiler/qsc_qasm/src/tests/statement/for_loop.rs diff --git a/compiler/qsc_qasm3/src/tests/statement/gate_call.rs b/compiler/qsc_qasm/src/tests/statement/gate_call.rs similarity index 98% rename from compiler/qsc_qasm3/src/tests/statement/gate_call.rs rename to compiler/qsc_qasm/src/tests/statement/gate_call.rs index bfb14bec7e..b640e15448 100644 --- a/compiler/qsc_qasm3/src/tests/statement/gate_call.rs +++ b/compiler/qsc_qasm/src/tests/statement/gate_call.rs @@ -245,7 +245,7 @@ fn cx_called_with_one_qubit_generates_error() { }; expect![[r#" - [Qsc.Qasm3.Lowerer.InvalidNumberOfQubitArgs + [Qasm.Lowerer.InvalidNumberOfQubitArgs x gate expects 2 qubit arguments, but 1 were provided ,-[Test.qasm:4:9] @@ -271,7 +271,7 @@ fn cx_called_with_too_many_qubits_generates_error() { }; expect![[r#" - [Qsc.Qasm3.Lowerer.InvalidNumberOfQubitArgs + [Qasm.Lowerer.InvalidNumberOfQubitArgs x gate expects 2 qubit arguments, but 3 were provided ,-[Test.qasm:4:9] @@ -297,7 +297,7 @@ fn rx_gate_with_no_angles_generates_error() { }; expect![[r#" - [Qsc.Qasm3.Lowerer.InvalidNumberOfClassicalArgs + [Qasm.Lowerer.InvalidNumberOfClassicalArgs x gate expects 1 classical arguments, but 0 were provided ,-[Test.qasm:4:9] @@ -343,7 +343,7 @@ fn rx_gate_with_too_many_angles_generates_error() { }; expect![[r#" - [Qsc.Qasm3.Lowerer.InvalidNumberOfClassicalArgs + [Qasm.Lowerer.InvalidNumberOfClassicalArgs x gate expects 1 classical arguments, but 2 were provided ,-[Test.qasm:4:9] diff --git a/compiler/qsc_qasm3/src/tests/statement/if_stmt.rs b/compiler/qsc_qasm/src/tests/statement/if_stmt.rs similarity index 100% rename from compiler/qsc_qasm3/src/tests/statement/if_stmt.rs rename to compiler/qsc_qasm/src/tests/statement/if_stmt.rs diff --git a/compiler/qsc_qasm3/src/tests/statement/implicit_modified_gate_call.rs b/compiler/qsc_qasm/src/tests/statement/implicit_modified_gate_call.rs similarity index 100% rename from compiler/qsc_qasm3/src/tests/statement/implicit_modified_gate_call.rs rename to compiler/qsc_qasm/src/tests/statement/implicit_modified_gate_call.rs diff --git a/compiler/qsc_qasm3/src/tests/statement/include.rs b/compiler/qsc_qasm/src/tests/statement/include.rs similarity index 99% rename from compiler/qsc_qasm3/src/tests/statement/include.rs rename to compiler/qsc_qasm/src/tests/statement/include.rs index 76a0d2d74a..ee9854ea5b 100644 --- a/compiler/qsc_qasm3/src/tests/statement/include.rs +++ b/compiler/qsc_qasm/src/tests/statement/include.rs @@ -39,7 +39,7 @@ fn programs_with_includes_can_be_parsed() -> miette::Result<(), Vec> { let r = compile_all_with_config("source0.qasm", all_sources, config)?; let qsharp = qsharp_from_qasm_compilation(r)?; expect![[r#" - namespace qasm3_import { + namespace qasm_import { import QasmStd.Angle.*; import QasmStd.Convert.*; import QasmStd.Intrinsic.*; @@ -84,7 +84,7 @@ fn programs_with_includes_with_includes_can_be_compiled() -> miette::Result<(), let r = compile_all_with_config("source0.qasm", all_sources, config)?; let qsharp = qsharp_from_qasm_compilation(r)?; expect![[r#" - namespace qasm3_import { + namespace qasm_import { import QasmStd.Angle.*; import QasmStd.Convert.*; import QasmStd.Intrinsic.*; diff --git a/compiler/qsc_qasm3/src/tests/statement/measure.rs b/compiler/qsc_qasm/src/tests/statement/measure.rs similarity index 98% rename from compiler/qsc_qasm3/src/tests/statement/measure.rs rename to compiler/qsc_qasm/src/tests/statement/measure.rs index e1984bce2a..77c768c3e6 100644 --- a/compiler/qsc_qasm3/src/tests/statement/measure.rs +++ b/compiler/qsc_qasm/src/tests/statement/measure.rs @@ -110,7 +110,7 @@ fn measuring_hardware_qubits_generates_an_error() { } expect![[r#" - Qsc.Qasm3.Compiler.NotSupported + Qasm.Compiler.NotSupported x hardware qubit operands are not supported ,-[Test.qasm:3:21] diff --git a/compiler/qsc_qasm3/src/tests/statement/modified_gate_call.rs b/compiler/qsc_qasm/src/tests/statement/modified_gate_call.rs similarity index 100% rename from compiler/qsc_qasm3/src/tests/statement/modified_gate_call.rs rename to compiler/qsc_qasm/src/tests/statement/modified_gate_call.rs diff --git a/compiler/qsc_qasm3/src/tests/statement/reset.rs b/compiler/qsc_qasm/src/tests/statement/reset.rs similarity index 99% rename from compiler/qsc_qasm3/src/tests/statement/reset.rs rename to compiler/qsc_qasm/src/tests/statement/reset.rs index d0ef520368..69957f09f2 100644 --- a/compiler/qsc_qasm3/src/tests/statement/reset.rs +++ b/compiler/qsc_qasm/src/tests/statement/reset.rs @@ -34,7 +34,7 @@ fn reset_calls_are_generated_from_qasm() -> miette::Result<(), Vec> { fail_on_compilation_errors(&unit); let qsharp = gen_qsharp(&unit.package.expect("no package found")); expect![[r#" - namespace qasm3_import { + namespace qasm_import { import QasmStd.Angle.*; import QasmStd.Convert.*; import QasmStd.Intrinsic.*; diff --git a/compiler/qsc_qasm3/src/tests/statement/switch.rs b/compiler/qsc_qasm/src/tests/statement/switch.rs similarity index 99% rename from compiler/qsc_qasm3/src/tests/statement/switch.rs rename to compiler/qsc_qasm/src/tests/statement/switch.rs index cafd8d2627..330b3271d2 100644 --- a/compiler/qsc_qasm3/src/tests/statement/switch.rs +++ b/compiler/qsc_qasm/src/tests/statement/switch.rs @@ -190,7 +190,7 @@ fn spec_case_3() -> miette::Result<(), Vec> { let qsharp = compile_qasm_to_qsharp_file(source)?; expect![[r#" - namespace qasm3_import { + namespace qasm_import { import QasmStd.Angle.*; import QasmStd.Convert.*; import QasmStd.Intrinsic.*; diff --git a/compiler/qsc_qasm3/src/tests/statement/while_loop.rs b/compiler/qsc_qasm/src/tests/statement/while_loop.rs similarity index 100% rename from compiler/qsc_qasm3/src/tests/statement/while_loop.rs rename to compiler/qsc_qasm/src/tests/statement/while_loop.rs diff --git a/compiler/qsc_qasm3/src/types.rs b/compiler/qsc_qasm/src/types.rs similarity index 100% rename from compiler/qsc_qasm3/src/types.rs rename to compiler/qsc_qasm/src/types.rs diff --git a/fuzz/Cargo.toml b/fuzz/Cargo.toml index 552c9e4d18..8240cddc8a 100644 --- a/fuzz/Cargo.toml +++ b/fuzz/Cargo.toml @@ -32,7 +32,7 @@ test = false doc = false [[bin]] -name = "qasm3" -path = "fuzz_targets/qasm3.rs" +name = "qasm" +path = "fuzz_targets/qasm.rs" test = false doc = false diff --git a/fuzz/fuzz_targets/qasm3.rs b/fuzz/fuzz_targets/qasm.rs similarity index 100% rename from fuzz/fuzz_targets/qasm3.rs rename to fuzz/fuzz_targets/qasm.rs diff --git a/fuzz/seed_inputs/qasm3/input.qasm b/fuzz/seed_inputs/qasm/input.qasm similarity index 100% rename from fuzz/seed_inputs/qasm3/input.qasm rename to fuzz/seed_inputs/qasm/input.qasm diff --git a/fuzz/seed_inputs/qasm/list.txt b/fuzz/seed_inputs/qasm/list.txt new file mode 100644 index 0000000000..a885415178 --- /dev/null +++ b/fuzz/seed_inputs/qasm/list.txt @@ -0,0 +1 @@ +fuzz/seed_inputs/qasm/input.qasm diff --git a/fuzz/seed_inputs/qasm3/list.txt b/fuzz/seed_inputs/qasm3/list.txt deleted file mode 100644 index 08acb8e3f9..0000000000 --- a/fuzz/seed_inputs/qasm3/list.txt +++ /dev/null @@ -1 +0,0 @@ -fuzz/seed_inputs/qasm3/input.qasm diff --git a/pip/qsharp/_native.pyi b/pip/qsharp/_native.pyi index d7017186de..6e64ee44ca 100644 --- a/pip/qsharp/_native.pyi +++ b/pip/qsharp/_native.pyi @@ -375,7 +375,7 @@ def physical_estimates(logical_resources: str, params: str) -> str: """ ... -def resource_estimate_qasm3( +def resource_estimate_qasm( source: str, job_params: str, read_file: Callable[[str], Tuple[str, str]], @@ -385,7 +385,7 @@ def resource_estimate_qasm3( **kwargs ) -> str: """ - Estimates the resource requirements for executing QASM3 source code. + Estimates the resource requirements for executing QASM source code. Note: This call while exported is not intended to be used directly by the user. @@ -393,7 +393,7 @@ def resource_estimate_qasm3( callbacks and other Python specific details. Args: - source (str): The QASM3 source code to estimate the resource requirements for. + source (str): The QASM source code to estimate the resource requirements for. job_params (str): The parameters for the job. read_file (Callable[[str], Tuple[str, str]]): A callable that reads a file and returns its content and path. list_directory (Callable[[str], List[Dict[str, str]]]): A callable that lists the contents of a directory. @@ -403,11 +403,11 @@ def resource_estimate_qasm3( - name (str): The name of the circuit. This is used as the entry point for the program. Defaults to 'program'. - search_path (str): The optional search path for resolving imports. Returns: - str: The estimated resource requirements for executing the QASM3 source code. + str: The estimated resource requirements for executing the QASM source code. """ ... -def run_qasm3( +def run_qasm( source: str, output_fn: Callable[[Output], None], read_file: Callable[[str], Tuple[str, str]], @@ -417,7 +417,7 @@ def run_qasm3( **kwargs ) -> Any: """ - Executes QASM3 source code using the specified target profile. + Executes QASM source code using the specified target profile. Note: This call while exported is not intended to be used directly by the user. @@ -425,7 +425,7 @@ def run_qasm3( callbacks and other Python specific details. Args: - source (str): The QASM3 source code to execute. + source (str): The QASM source code to execute. output_fn (Callable[[Output], None]): The function to handle the output of the execution. read_file (Callable[[str], Tuple[str, str]]): The function to read a file and return its contents. list_directory (Callable[[str], List[Dict[str, str]]]): The function to list the contents of a directory. @@ -443,7 +443,7 @@ def run_qasm3( """ ... -def compile_qasm3_to_qir( +def compile_qasm_to_qir( source: str, read_file: Callable[[str], Tuple[str, str]], list_directory: Callable[[str], List[Dict[str, str]]], @@ -460,7 +460,7 @@ def compile_qasm3_to_qir( callbacks and other Python specific details. Args: - source (str): The QASM3 source code to estimate the resource requirements for. + source (str): The QASM source code to estimate the resource requirements for. read_file (Callable[[str], Tuple[str, str]]): A callable that reads a file and returns its content and path. list_directory (Callable[[str], List[Dict[str, str]]]): A callable that lists the contents of a directory. resolve_path (Callable[[str, str], str]): A callable that resolves a file path given a base path and a relative path. @@ -476,7 +476,7 @@ def compile_qasm3_to_qir( """ ... -def compile_qasm3_to_qsharp( +def compile_qasm_to_qsharp( source: str, read_file: Callable[[str], Tuple[str, str]], list_directory: Callable[[str], List[Dict[str, str]]], @@ -493,7 +493,7 @@ def compile_qasm3_to_qsharp( callbacks and other Python specific details. Args: - source (str): The QASM3 source code to estimate the resource requirements for. + source (str): The QASM source code to estimate the resource requirements for. read_file (Callable[[str], Tuple[str, str]]): A callable that reads a file and returns its content and path. list_directory (Callable[[str], List[Dict[str, str]]]): A callable that lists the contents of a directory. resolve_path (Callable[[str, str], str]): A callable that resolves a file path given a base path and a relative path. diff --git a/pip/qsharp/interop/qiskit/backends/backend_base.py b/pip/qsharp/interop/qiskit/backends/backend_base.py index d54beba15a..e0ed4061ab 100644 --- a/pip/qsharp/interop/qiskit/backends/backend_base.py +++ b/pip/qsharp/interop/qiskit/backends/backend_base.py @@ -294,7 +294,7 @@ def _submit_job(self, run_input: List[QuantumCircuit], **input_params) -> QsJob: pass def _compile(self, run_input: List[QuantumCircuit], **options) -> List[Compilation]: - # for each run input, convert to qasm3 + # for each run input, convert to qasm compilations = [] for circuit in run_input: args = options.copy() @@ -302,7 +302,7 @@ def _compile(self, run_input: List[QuantumCircuit], **options) -> List[Compilati circuit, QuantumCircuit ), "Input must be a QuantumCircuit." start = monotonic() - qasm = self._qasm3(circuit, **args) + qasm = self._qasm(circuit, **args) end = monotonic() time_taken = end - start @@ -407,7 +407,7 @@ def transpile(self, circuit: QuantumCircuit, **options) -> QuantumCircuit: transpiled_circuit = self._transpile(circuit, **options) return transpiled_circuit - def _qasm3(self, circuit: QuantumCircuit, **options) -> str: + def _qasm(self, circuit: QuantumCircuit, **options) -> str: """Converts a Qiskit QuantumCircuit to QASM 3 for the current backend. Args: @@ -419,7 +419,7 @@ def _qasm3(self, circuit: QuantumCircuit, **options) -> str: 'includes', 'search_path'. Returns: - str: The converted QASM3 code as a string. Any supplied includes + str: The converted QASM code as a string. Any supplied includes are emitted as include statements at the top of the program. :raises QasmError: If there is an error generating or parsing QASM. @@ -455,14 +455,14 @@ def _qsharp(self, circuit: QuantumCircuit, **kwargs) -> str: 'includes', 'search_path'. - output_semantics (OutputSemantics, optional): The output semantics for the compilation. Returns: - str: The converted QASM3 code as a string. Any supplied includes + str: The converted QASM code as a string. Any supplied includes are emitted as include statements at the top of the program. :raises QSharpError: If there is an error evaluating the source code. :raises QasmError: If there is an error generating, parsing, or compiling QASM. """ - qasm3_source = self._qasm3(circuit, **kwargs) + qasm_source = self._qasm(circuit, **kwargs) args = { "name": kwargs.get("name", circuit.name), @@ -476,7 +476,7 @@ def _qsharp(self, circuit: QuantumCircuit, **kwargs) -> str: ): args["output_semantics"] = output_semantics - qsharp_source = self._qasm3_to_qsharp(qasm3_source, **args) + qsharp_source = self._qasm_to_qsharp(qasm_source, **args) return qsharp_source def qir( @@ -506,7 +506,7 @@ def qir( if target_profile == TargetProfile.Unrestricted: raise ValueError(str(Errors.UNRESTRICTED_INVALID_QIR_TARGET)) - qasm3_source = self._qasm3(circuit, **kwargs) + qasm_source = self._qasm(circuit, **kwargs) args = { "name": name, @@ -524,18 +524,18 @@ def qir( ): args["output_semantics"] = output_semantics - return self._qasm3_to_qir(qasm3_source, **args) + return self._qasm_to_qir(qasm_source, **args) - def _qasm3_to_qir( + def _qasm_to_qir( self, source: str, **kwargs, ) -> str: - from ...._native import compile_qasm3_to_qir + from ...._native import compile_qasm_to_qir from ...._fs import read_file, list_directory, resolve from ...._http import fetch_github - return compile_qasm3_to_qir( + return compile_qasm_to_qir( source, read_file, list_directory, @@ -544,16 +544,16 @@ def _qasm3_to_qir( **kwargs, ) - def _qasm3_to_qsharp( + def _qasm_to_qsharp( self, source: str, **kwargs, ) -> str: - from ...._native import compile_qasm3_to_qsharp + from ...._native import compile_qasm_to_qsharp from ...._fs import read_file, list_directory, resolve from ...._http import fetch_github - return compile_qasm3_to_qsharp( + return compile_qasm_to_qsharp( source, read_file, list_directory, diff --git a/pip/qsharp/interop/qiskit/backends/errors.py b/pip/qsharp/interop/qiskit/backends/errors.py index 9fa54c9ecc..6468eddcd9 100644 --- a/pip/qsharp/interop/qiskit/backends/errors.py +++ b/pip/qsharp/interop/qiskit/backends/errors.py @@ -18,7 +18,7 @@ def __str__(self): elif self == Errors.RUN_TERMINATED_WITHOUT_OUTPUT: return "Run terminated without valid output." elif self == Errors.FAILED_TO_EXPORT_QASM: - return "Failed to export QASM3 source." + return "Failed to export QASM source." elif self == Errors.MISSING_NUMBER_OF_SHOTS: return "The number of shots must be specified." elif self == Errors.INPUT_MUST_BE_QC: diff --git a/pip/qsharp/interop/qiskit/backends/qsharp_backend.py b/pip/qsharp/interop/qiskit/backends/qsharp_backend.py index 3a47d9b1cb..298b166404 100644 --- a/pip/qsharp/interop/qiskit/backends/qsharp_backend.py +++ b/pip/qsharp/interop/qiskit/backends/qsharp_backend.py @@ -143,7 +143,7 @@ def _execute(self, programs: List[Compilation], **input_params) -> Dict[str, Any exec_results: List[Tuple[Compilation, Dict[str, Any]]] = [ ( program, - _run_qasm3(program.qasm, vars(self.options).copy(), **input_params), + _run_qasm(program.qasm, vars(self.options).copy(), **input_params), ) for program in programs ] @@ -202,7 +202,7 @@ def _submit_job( return job -def _run_qasm3( +def _run_qasm( qasm: str, default_options: Options, **options, @@ -221,7 +221,7 @@ def _run_qasm3( - target_profile (TargetProfile): The target profile to use for the compilation. - output_semantics (OutputSemantics, optional): The output semantics for the compilation. - name (str): The name of the circuit. This is used as the entry point for the program. Defaults to 'program'. - - search_path (str): The optional search path for resolving qasm3 imports. + - search_path (str): The optional search path for resolving qasm imports. - shots (int): The number of shots to run the program for. Defaults to 1. - seed (int): The seed to use for the random number generator. - output_fn (Optional[Callable[[Output], None]]): A callback function that will be called with each output. Defaults to None. @@ -232,8 +232,7 @@ def _run_qasm3( :raises QasmError: If there is an error generating, parsing, or compiling QASM. """ - from ...._native import Output - from ...._native import run_qasm3 + from ...._native import Output, run_qasm from ...._fs import read_file, list_directory, resolve from ...._http import fetch_github @@ -263,7 +262,7 @@ def value_or_default(key: str) -> Any: if seed := value_or_default("seed"): args["seed"] = seed - return run_qasm3( + return run_qasm( qasm, output_fn, read_file, diff --git a/pip/qsharp/interop/qiskit/backends/re_backend.py b/pip/qsharp/interop/qiskit/backends/re_backend.py index 54d90d6892..f88ae68d9f 100644 --- a/pip/qsharp/interop/qiskit/backends/re_backend.py +++ b/pip/qsharp/interop/qiskit/backends/re_backend.py @@ -19,7 +19,7 @@ from ..execution import DetaultExecutor from ...._fs import read_file, list_directory, resolve from ...._http import fetch_github -from ...._native import resource_estimate_qasm3 +from ...._native import resource_estimate_qasm from .... import TargetProfile from ....estimator import ( EstimatorResult, @@ -56,7 +56,7 @@ def __init__( - params (EstimatorParams): Configuration values for resource estimation. - name (str): The name of the circuit. This is used as the entry point for the program. The circuit name will be used if not specified. - - search_path (str): Path to search in for qasm3 imports. Defaults to '.'. + - search_path (str): Path to search in for qasm imports. Defaults to '.'. - executor(ThreadPoolExecutor or other Executor): The executor to be used to submit the job. Defaults to SynchronousExecutor. """ @@ -104,7 +104,7 @@ def run( **options: Additional options for the execution. - name (str): The name of the circuit. This is used as the entry point for the program. The circuit name will be used if not specified. - - search_path (str): Path to search in for qasm3 imports. Defaults to '.'. + - search_path (str): Path to search in for qasm imports. Defaults to '.'. - target_profile (TargetProfile): The target profile to use for the backend. - executor(ThreadPoolExecutor or other Executor): The executor to be used to submit the job. @@ -124,13 +124,13 @@ def run( options["params"] = params return self._run(run_input, **options) - def _estimate_qasm3( + def _estimate_qasm( self, source: str, **input_params, ) -> Dict[str, Any]: """ - Estimates the resource usage of a QASM3 source code. + Estimates the resource usage of a QASM source code. """ params = input_params.pop("params", None) if params is None: @@ -148,7 +148,7 @@ def _estimate_qasm3( "search_path": input_params.pop("search_path", "."), } kwargs.update(input_params) - res_str = resource_estimate_qasm3( + res_str = resource_estimate_qasm( source, param_str, read_file, @@ -162,7 +162,7 @@ def _estimate_qasm3( def _execute(self, programs: List[Compilation], **input_params) -> Dict: exec_results = [ - (program, self._estimate_qasm3(program.qasm, **input_params)) + (program, self._estimate_qasm(program.qasm, **input_params)) for program in programs ] success = ( diff --git a/pip/src/interop.rs b/pip/src/interop.rs index f9b42116b0..368cba06e1 100644 --- a/pip/src/interop.rs +++ b/pip/src/interop.rs @@ -28,7 +28,7 @@ use crate::interpreter::{ use resource_estimator as re; /// `SourceResolver` implementation that uses the provided `FileSystem` -/// to resolve qasm3 include statements. +/// to resolve qasm include statements. pub(crate) struct ImportResolver where T: FileSystem, @@ -87,7 +87,7 @@ where #[pyo3( signature = (source, callback=None, read_file=None, list_directory=None, resolve_path=None, fetch_github=None, **kwargs) )] -pub fn run_qasm3( +pub fn run_qasm( py: Python, source: &str, callback: Option, @@ -163,7 +163,7 @@ pub(crate) fn run_ast( #[pyo3( signature = (source, job_params, read_file, list_directory, resolve_path, fetch_github, **kwargs) )] -pub(crate) fn resource_estimate_qasm3( +pub(crate) fn resource_estimate_qasm( py: Python, source: &str, job_params: &str, @@ -192,7 +192,7 @@ pub(crate) fn resource_estimate_qasm3( false, )?; - match crate::interop::estimate_qasm3(package, source_map, job_params) { + match crate::interop::estimate_qasm(package, source_map, job_params) { Ok(estimate) => Ok(estimate), Err(errors) if matches!(errors[0], re::Error::Interpreter(_)) => { Err(QSharpError::new_err(format_errors( @@ -226,7 +226,7 @@ pub(crate) fn resource_estimate_qasm3( #[pyo3( signature = (source, read_file, list_directory, resolve_path, fetch_github, **kwargs) )] -pub(crate) fn compile_qasm3_to_qir( +pub(crate) fn compile_qasm_to_qir( py: Python, source: &str, read_file: Option, @@ -327,7 +327,7 @@ fn generate_qir_from_ast>( #[pyo3( signature = (source, read_file, list_directory, resolve_path, fetch_github, **kwargs) )] -pub(crate) fn compile_qasm3_to_qsharp( +pub(crate) fn compile_qasm_to_qsharp( py: Python, source: &str, read_file: Option, @@ -439,10 +439,10 @@ fn map_qirgen_errors(errors: Vec) -> PyErr { QSharpError::new_err(message) } -/// Estimates the resources required to run a QASM3 program +/// Estimates the resources required to run a QASM program /// represented by the provided AST. The source map is used for /// error reporting during compilation or runtime. -fn estimate_qasm3( +fn estimate_qasm( ast_package: Package, source_map: SourceMap, params: &str, @@ -467,7 +467,7 @@ fn into_estimation_errors(errors: Vec) -> Vec>() } -/// Formats a list of QASM3 errors into a single string. +/// Formats a list of QASM errors into a single string. pub(crate) fn format_qasm_errors(errors: Vec>) -> String { errors .into_iter() diff --git a/pip/src/interpreter.rs b/pip/src/interpreter.rs index 7cab5dc7fb..225ab5cd83 100644 --- a/pip/src/interpreter.rs +++ b/pip/src/interpreter.rs @@ -5,8 +5,8 @@ use crate::{ displayable_output::{DisplayableMatrix, DisplayableOutput, DisplayableState}, fs::file_system, interop::{ - compile_qasm3_to_qir, compile_qasm3_to_qsharp, compile_qasm_enriching_errors, - map_entry_compilation_errors, resource_estimate_qasm3, run_ast, run_qasm3, ImportResolver, + compile_qasm_enriching_errors, compile_qasm_to_qir, compile_qasm_to_qsharp, + map_entry_compilation_errors, resource_estimate_qasm, run_ast, run_qasm, ImportResolver, }, noisy_simulator::register_noisy_simulator_submodule, }; @@ -79,12 +79,12 @@ fn _native<'a>(py: Python<'a>, m: &Bound<'a, PyModule>) -> PyResult<()> { m.add_function(wrap_pyfunction!(physical_estimates, m)?)?; m.add("QSharpError", py.get_type::())?; register_noisy_simulator_submodule(py, m)?; - // QASM3 interop + // QASM interop m.add("QasmError", py.get_type::())?; - m.add_function(wrap_pyfunction!(resource_estimate_qasm3, m)?)?; - m.add_function(wrap_pyfunction!(run_qasm3, m)?)?; - m.add_function(wrap_pyfunction!(compile_qasm3_to_qir, m)?)?; - m.add_function(wrap_pyfunction!(compile_qasm3_to_qsharp, m)?)?; + m.add_function(wrap_pyfunction!(resource_estimate_qasm, m)?)?; + m.add_function(wrap_pyfunction!(run_qasm, m)?)?; + m.add_function(wrap_pyfunction!(compile_qasm_to_qir, m)?)?; + m.add_function(wrap_pyfunction!(compile_qasm_to_qsharp, m)?)?; Ok(()) } @@ -648,7 +648,7 @@ impl Interpreter { #[pyo3( signature = (source, callback=None, read_file=None, list_directory=None, resolve_path=None, fetch_github=None, **kwargs) )] - pub fn _run_qasm3( + pub fn _run_qasm( &mut self, py: Python, source: &str, diff --git a/pip/tests-integration/interop_qiskit/test_circuits/__init__.py b/pip/tests-integration/interop_qiskit/test_circuits/__init__.py index 0cb3408937..ab3c703eb6 100644 --- a/pip/tests-integration/interop_qiskit/test_circuits/__init__.py +++ b/pip/tests-integration/interop_qiskit/test_circuits/__init__.py @@ -15,14 +15,14 @@ def generate_repro_information( message += "\n" try: - qasm3_source = backend._qasm3(circuit, **options) - message += "QASM3 source:" + qasm_source = backend._qasm(circuit, **options) + message += "QASM source:" message += "\n" - message += str(qasm3_source) + message += str(qasm_source) except Exception as ex: # if the conversion fails, print the circuit as a string - # as a fallback since we won't have the qasm3 source - message += "\nFailed converting QuantumCircuit to QASM3:\n" + # as a fallback since we won't have the qasm source + message += "\nFailed converting QuantumCircuit to QASM:\n" message += str(ex) message += "\n" message += "QuantumCircuit rendered:" diff --git a/pip/tests-integration/interop_qiskit/test_gateset_qasm.py b/pip/tests-integration/interop_qiskit/test_gateset_qasm.py index 68245e33ce..1fbe79006a 100644 --- a/pip/tests-integration/interop_qiskit/test_gateset_qasm.py +++ b/pip/tests-integration/interop_qiskit/test_gateset_qasm.py @@ -19,7 +19,7 @@ def run_transpile_test( if "optimization_level" not in options: # Use no optimization so gate transpilation is consistent options["optimization_level"] = 0 - info = QSharpBackend()._qasm3(circuit, **options) + info = QSharpBackend()._qasm(circuit, **options) lines = info.splitlines() # remove the first four lines, which are the header # OPENQASM 3.0; diff --git a/pip/tests-integration/interop_qiskit/test_qsharp.py b/pip/tests-integration/interop_qiskit/test_qsharp.py index a92bdb0385..7f8b0a0ce8 100644 --- a/pip/tests-integration/interop_qiskit/test_qsharp.py +++ b/pip/tests-integration/interop_qiskit/test_qsharp.py @@ -26,7 +26,7 @@ def test_qsharp_smoke() -> None: backend = QSharpBackend() res = backend._qsharp(circuit) assert res is not None - assert "qasm3_import" in res + assert "qasm_import" in res assert "operation smoke() : Result[]" in res assert "Microsoft.Quantum.Arrays.Reversed" in res diff --git a/pip/tests-integration/interop_qiskit/test_run_sim.py b/pip/tests-integration/interop_qiskit/test_run_sim.py index 21d540e844..627a7d9399 100644 --- a/pip/tests-integration/interop_qiskit/test_run_sim.py +++ b/pip/tests-integration/interop_qiskit/test_run_sim.py @@ -26,8 +26,8 @@ # Then load the QASM3 and convert it back to a circuit. # This is to ensure that the QASM3 conversion is semantically correct. def round_trip_circuit(circuit, backend): - qasm3 = backend._qasm3(circuit) - circuit = from_qasm3(qasm3) + qasm = backend._qasm(circuit) + circuit = from_qasm3(qasm) return circuit From 46e7d51844a66d3d5efe22159aff3b535f1078b0 Mon Sep 17 00:00:00 2001 From: orpuente-MS <156957451+orpuente-MS@users.noreply.github.com> Date: Thu, 17 Apr 2025 14:56:10 -0700 Subject: [PATCH 107/108] Rename error diagnostic codes (#2309) Rename error diagnostic codes. --- compiler/qsc_qasm/src/parser/error.rs | 34 +++++++++---------- compiler/qsc_qasm/src/semantic/const_eval.rs | 6 ++-- .../qsc_qasm/src/tests/declaration/def.rs | 2 +- .../qsc_qasm/src/tests/declaration/gate.rs | 2 +- .../src/tests/statement/const_eval.rs | 24 ++++++------- 5 files changed, 34 insertions(+), 34 deletions(-) diff --git a/compiler/qsc_qasm/src/parser/error.rs b/compiler/qsc_qasm/src/parser/error.rs index c97f8a6fa3..93d9112f93 100644 --- a/compiler/qsc_qasm/src/parser/error.rs +++ b/compiler/qsc_qasm/src/parser/error.rs @@ -87,55 +87,55 @@ pub enum ErrorKind { #[diagnostic(transparent)] Lex(lex::Error), #[error("invalid {0} literal")] - #[diagnostic(code("Qasm.Parse.Literal"))] + #[diagnostic(code("Qasm.Parser.Literal"))] Lit(&'static str, #[label] Span), #[error("unknown escape sequence: `{0}`")] - #[diagnostic(code("Qasm.Parse.Escape"))] + #[diagnostic(code("Qasm.Parser.Escape"))] Escape(char, #[label] Span), #[error("expected {0}, found {1}")] - #[diagnostic(code("Qasm.Parse.Token"))] + #[diagnostic(code("Qasm.Parser.Token"))] Token(TokenKind, TokenKind, #[label] Span), #[error("Empty statements are not supported")] - #[diagnostic(code("Qasm.Parse.EmptyStatement"))] + #[diagnostic(code("Qasm.Parser.EmptyStatement"))] EmptyStatement(#[label] Span), #[error("Annotation missing target statement.")] - #[diagnostic(code("Qasm.Parse.FloatingAnnotation"))] + #[diagnostic(code("Qasm.Parser.FloatingAnnotation"))] FloatingAnnotation(#[label] Span), #[error("expected {0}, found {1}")] - #[diagnostic(code("Qasm.Parse.Rule"))] + #[diagnostic(code("Qasm.Parser.Rule"))] Rule(&'static str, TokenKind, #[label] Span), #[error("expected {0}, found {1}")] - #[diagnostic(code("Qasm.Parse.Convert"))] + #[diagnostic(code("Qasm.Parser.Convert"))] Convert(&'static str, &'static str, #[label] Span), #[error("expected statement to end with a semicolon")] - #[diagnostic(code("Qasm.Parse.MissingSemi"))] + #[diagnostic(code("Qasm.Parser.MissingSemi"))] MissingSemi(#[label] Span), #[error("expected inputs to be parenthesized")] - #[diagnostic(code("Qasm.Parse.MissingParens"))] + #[diagnostic(code("Qasm.Parser.MissingParens"))] MissingParens(#[label] Span), #[error("missing entry in sequence")] - #[diagnostic(code("Qasm.Parse.MissingSeqEntry"))] + #[diagnostic(code("Qasm.Parser.MissingSeqEntry"))] MissingSeqEntry(#[label] Span), #[error("missing switch statement cases")] - #[diagnostic(code("Qasm.Parse.MissingSwitchCases"))] + #[diagnostic(code("Qasm.Parser.MissingSwitchCases"))] MissingSwitchCases(#[label] Span), #[error("missing switch statement case labels")] - #[diagnostic(code("Qasm.Parse.MissingSwitchCaseLabels"))] + #[diagnostic(code("Qasm.Parser.MissingSwitchCaseLabels"))] MissingSwitchCaseLabels(#[label] Span), #[error("missing gate call operands")] - #[diagnostic(code("Qasm.Parse.MissingGateCallOperands"))] + #[diagnostic(code("Qasm.Parser.MissingGateCallOperands"))] MissingGateCallOperands(#[label] Span), #[error("expected an item or closing brace, found {0}")] - #[diagnostic(code("Qasm.Parse.ExpectedItem"))] + #[diagnostic(code("Qasm.Parser.ExpectedItem"))] ExpectedItem(TokenKind, #[label] Span), #[error("gphase gate requires exactly one angle")] - #[diagnostic(code("Qasm.Parse.GPhaseInvalidArguments"))] + #[diagnostic(code("Qasm.Parser.GPhaseInvalidArguments"))] GPhaseInvalidArguments(#[label] Span), #[error("invalid gate call designator")] - #[diagnostic(code("Qasm.Parse.InvalidGateCallDesignator"))] + #[diagnostic(code("Qasm.Parser.InvalidGateCallDesignator"))] InvalidGateCallDesignator(#[label] Span), #[error("multiple index operators are only allowed in assignments")] - #[diagnostic(code("Qasm.Parse.MultipleIndexOperators"))] + #[diagnostic(code("Qasm.Parser.MultipleIndexOperators"))] MultipleIndexOperators(#[label] Span), #[error(transparent)] #[diagnostic(transparent)] diff --git a/compiler/qsc_qasm/src/semantic/const_eval.rs b/compiler/qsc_qasm/src/semantic/const_eval.rs index b780029c63..d43931ddd5 100644 --- a/compiler/qsc_qasm/src/semantic/const_eval.rs +++ b/compiler/qsc_qasm/src/semantic/const_eval.rs @@ -25,13 +25,13 @@ use thiserror::Error; #[derive(Clone, Debug, Diagnostic, Eq, Error, PartialEq)] pub enum ConstEvalError { #[error("expression must be const")] - #[diagnostic(code("Qasm.Compile.ExprMustBeConst"))] + #[diagnostic(code("Qasm.Compiler.ExprMustBeConst"))] ExprMustBeConst(#[label] Span), #[error("uint expression must evaluate to a non-negative value, but it evaluated to {0}")] - #[diagnostic(code("Qasm.Compile.NegativeUIntValue"))] + #[diagnostic(code("Qasm.Compiler.NegativeUIntValue"))] NegativeUIntValue(i64, #[label] Span), #[error("{0} doesn't fit in {1}")] - #[diagnostic(code("Qasm.Compile.ValueOverflow"))] + #[diagnostic(code("Qasm.Compiler.ValueOverflow"))] ValueOverflow(String, String, #[label] Span), } diff --git a/compiler/qsc_qasm/src/tests/declaration/def.rs b/compiler/qsc_qasm/src/tests/declaration/def.rs index 318a2d68aa..96028ca038 100644 --- a/compiler/qsc_qasm/src/tests/declaration/def.rs +++ b/compiler/qsc_qasm/src/tests/declaration/def.rs @@ -234,7 +234,7 @@ fn capturing_non_const_evaluatable_external_variable_fails() { }; expect![[r#" - [Qasm.Compile.NegativeUIntValue + [Qasm.Compiler.NegativeUIntValue x uint expression must evaluate to a non-negative value, but it evaluated | to -3 diff --git a/compiler/qsc_qasm/src/tests/declaration/gate.rs b/compiler/qsc_qasm/src/tests/declaration/gate.rs index f51be04f0a..a38043758c 100644 --- a/compiler/qsc_qasm/src/tests/declaration/gate.rs +++ b/compiler/qsc_qasm/src/tests/declaration/gate.rs @@ -156,7 +156,7 @@ fn capturing_non_const_evaluatable_external_variable_fails() { }; expect![[r#" - [Qasm.Compile.NegativeUIntValue + [Qasm.Compiler.NegativeUIntValue x uint expression must evaluate to a non-negative value, but it evaluated | to -3 diff --git a/compiler/qsc_qasm/src/tests/statement/const_eval.rs b/compiler/qsc_qasm/src/tests/statement/const_eval.rs index 01cd4c12d9..8451088e90 100644 --- a/compiler/qsc_qasm/src/tests/statement/const_eval.rs +++ b/compiler/qsc_qasm/src/tests/statement/const_eval.rs @@ -76,7 +76,7 @@ fn non_const_exprs_fail_in_bitarray_size_position() { let errs: Vec<_> = errs.iter().map(|e| format!("{e:?}")).collect(); let errs_string = errs.join("\n"); expect![[r#" - Qasm.Compile.ExprMustBeConst + Qasm.Compiler.ExprMustBeConst x expression must be const ,-[Test.qasm:5:13] @@ -96,7 +96,7 @@ fn non_const_exprs_fail_in_bitarray_size_position() { 6 | bit[c] r2; `---- - Qasm.Compile.ExprMustBeConst + Qasm.Compiler.ExprMustBeConst x expression must be const ,-[Test.qasm:6:13] @@ -467,7 +467,7 @@ fn binary_op_shl_creg_fails() { let errs: Vec<_> = errs.iter().map(|e| format!("{e:?}")).collect(); let errs_string = errs.join("\n"); expect![[r#" - Qasm.Parse.Rule + Qasm.Parser.Rule x expected scalar or array type, found keyword `creg` ,-[Test.qasm:2:15] @@ -477,7 +477,7 @@ fn binary_op_shl_creg_fails() { 3 | const creg b[3] = a << 2; `---- - Qasm.Parse.Rule + Qasm.Parser.Rule x expected scalar or array type, found keyword `creg` ,-[Test.qasm:3:15] @@ -507,7 +507,7 @@ fn binary_op_shl_creg_fails() { 5 | `---- - Qasm.Compile.ExprMustBeConst + Qasm.Compiler.ExprMustBeConst x expression must be const ,-[Test.qasm:4:13] @@ -640,7 +640,7 @@ fn binary_op_shr_creg_fails() { let errs: Vec<_> = errs.iter().map(|e| format!("{e:?}")).collect(); let errs_string = errs.join("\n"); expect![[r#" - Qasm.Parse.Rule + Qasm.Parser.Rule x expected scalar or array type, found keyword `creg` ,-[Test.qasm:2:15] @@ -650,7 +650,7 @@ fn binary_op_shr_creg_fails() { 3 | const creg b[4] = a >> 2; `---- - Qasm.Parse.Rule + Qasm.Parser.Rule x expected scalar or array type, found keyword `creg` ,-[Test.qasm:3:15] @@ -680,7 +680,7 @@ fn binary_op_shr_creg_fails() { 5 | `---- - Qasm.Compile.ExprMustBeConst + Qasm.Compiler.ExprMustBeConst x expression must be const ,-[Test.qasm:4:13] @@ -2025,7 +2025,7 @@ fn binary_op_err_type_fails() { 3 | `---- - Qasm.Compile.ExprMustBeConst + Qasm.Compiler.ExprMustBeConst x expression must be const ,-[Test.qasm:2:13] @@ -2070,7 +2070,7 @@ fn fuzzer_issue_2294() { let errs: Vec<_> = errs.iter().map(|e| format!("{e:?}")).collect(); let errs_string = errs.join("\n"); expect![[r#" - Qasm.Parse.Token + Qasm.Parser.Token x expected `;`, found EOF ,-[Test.qasm:3:5] @@ -2078,7 +2078,7 @@ fn fuzzer_issue_2294() { 3 | `---- - Qasm.Parse.MissingGateCallOperands + Qasm.Parser.MissingGateCallOperands x missing gate call operands ,-[Test.qasm:2:9] @@ -2108,7 +2108,7 @@ fn fuzzer_issue_2294() { 3 | `---- - Qasm.Compile.ExprMustBeConst + Qasm.Compiler.ExprMustBeConst x expression must be const ,-[Test.qasm:2:16] From 37f1b3bb10fab577466239c8c35ed911bbd6f948 Mon Sep 17 00:00:00 2001 From: orpuente-MS <156957451+orpuente-MS@users.noreply.github.com> Date: Thu, 17 Apr 2025 15:21:43 -0700 Subject: [PATCH 108/108] Remove source resolver default impl (#2310) --- Cargo.lock | 1 - compiler/qsc/Cargo.toml | 2 +- compiler/qsc_qasm/Cargo.toml | 4 ---- compiler/qsc_qasm/src/io.rs | 22 ---------------------- 4 files changed, 1 insertion(+), 28 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 615b59090d..1597690d71 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1297,7 +1297,6 @@ dependencies = [ "qsc_hir", "qsc_parse", "qsc_passes", - "qsc_qasm", "rustc-hash", "thiserror", ] diff --git a/compiler/qsc/Cargo.toml b/compiler/qsc/Cargo.toml index 3b90ba5d99..5ed1a26ca3 100644 --- a/compiler/qsc/Cargo.toml +++ b/compiler/qsc/Cargo.toml @@ -31,7 +31,7 @@ qsc_passes = { path = "../qsc_passes" } qsc_parse = { path = "../qsc_parse" } qsc_partial_eval = { path = "../qsc_partial_eval" } qsc_project = { path = "../qsc_project", features = ["fs"] } -qsc_qasm = { path = "../qsc_qasm", features = ["fs"] } +qsc_qasm = { path = "../qsc_qasm" } qsc_rca = { path = "../qsc_rca" } qsc_circuit = { path = "../qsc_circuit" } rustc-hash = { workspace = true } diff --git a/compiler/qsc_qasm/Cargo.toml b/compiler/qsc_qasm/Cargo.toml index 9c38f943b0..a1fdbf68dc 100644 --- a/compiler/qsc_qasm/Cargo.toml +++ b/compiler/qsc_qasm/Cargo.toml @@ -32,12 +32,8 @@ miette = { workspace = true, features = ["fancy"] } # Self import adding fs feature so that we can test # loading qasm from file. qsc = { path = "../qsc" } -qsc_qasm = { path = ".", features = ["fs"] } qsc_codegen = { path = "../qsc_codegen" } -[features] -fs = [] - [lints] workspace = true diff --git a/compiler/qsc_qasm/src/io.rs b/compiler/qsc_qasm/src/io.rs index 5493f4e28c..18b49fc3e9 100644 --- a/compiler/qsc_qasm/src/io.rs +++ b/compiler/qsc_qasm/src/io.rs @@ -19,28 +19,6 @@ use rustc_hash::FxHashMap; pub trait SourceResolver { fn ctx(&mut self) -> &mut SourceResolverContext; - #[cfg(feature = "fs")] - fn resolve

    (&mut self, path: P) -> miette::Result<(PathBuf, String), Error> - where - P: AsRef, - { - let path = std::fs::canonicalize(path).map_err(|e| { - Error(ErrorKind::IO(format!( - "Could not resolve include file path: {e}" - ))) - })?; - - self.ctx().check_include_errors(&path)?; - - match std::fs::read_to_string(&path) { - Ok(source) => { - self.ctx().add_path_to_include_graph(path.clone()); - Ok((path, source)) - } - Err(error) => Err(Error(ErrorKind::IO(error.to_string()))), - } - } - #[cfg(not(feature = "fs"))] fn resolve

    (&mut self, path: P) -> miette::Result<(PathBuf, String), Error> where P: AsRef;