Skip to content

Commit fb6c167

Browse files
committed
Add durationof support
1 parent 9dd43b5 commit fb6c167

File tree

24 files changed

+2131
-510
lines changed

24 files changed

+2131
-510
lines changed

source/compiler/qsc_qasm/src/compiler.rs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -925,6 +925,11 @@ impl QasmCompiler {
925925
)
926926
}
927927

928+
fn compile_durationof_call_expr(&mut self, expr: &semast::DurationofCallExpr) -> qsast::Expr {
929+
self.push_unsupported_error_message("durationof call", expr.span);
930+
err_expr(expr.span)
931+
}
932+
928933
fn compile_gate_call_stmt(&mut self, stmt: &semast::GateCall) -> Option<qsast::Stmt> {
929934
if let Some(duration) = &stmt.duration {
930935
self.push_unsupported_error_message("gate call duration", duration.span);
@@ -1397,6 +1402,9 @@ impl QasmCompiler {
13971402
semast::ExprKind::Paren(pexpr) => self.compile_paren_expr(pexpr, expr.span),
13981403
semast::ExprKind::Measure(mexpr) => self.compile_measure_expr(mexpr, &expr.ty),
13991404
semast::ExprKind::SizeofCall(sizeof_call) => self.compile_sizeof_call_expr(sizeof_call),
1405+
semast::ExprKind::DurationofCall(duration_call) => {
1406+
self.compile_durationof_call_expr(duration_call)
1407+
}
14001408
}
14011409
}
14021410

source/compiler/qsc_qasm/src/parser/ast.rs

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -581,6 +581,21 @@ impl Display for Block {
581581
}
582582
}
583583

584+
#[derive(Clone, Debug)]
585+
pub struct DurationofCall {
586+
pub span: Span,
587+
pub name_span: Span,
588+
pub scope: Block,
589+
}
590+
591+
impl Display for DurationofCall {
592+
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
593+
writeln_header(f, "DurationofCall", self.span)?;
594+
writeln_field(f, "name_span", &self.name_span)?;
595+
write_field(f, "scope", &self.scope)
596+
}
597+
}
598+
584599
#[derive(Clone, Debug)]
585600
pub enum IdentOrIndexedIdent {
586601
Ident(Ident),
@@ -1650,6 +1665,7 @@ pub enum ExprKind {
16501665
Cast(Cast),
16511666
IndexExpr(IndexExpr),
16521667
Paren(Expr),
1668+
DurationOf(DurationofCall),
16531669
}
16541670

16551671
impl Display for ExprKind {
@@ -1664,6 +1680,7 @@ impl Display for ExprKind {
16641680
ExprKind::Cast(expr) => write!(f, "{expr}"),
16651681
ExprKind::IndexExpr(expr) => write!(f, "{expr}"),
16661682
ExprKind::Paren(expr) => write!(f, "Paren {expr}"),
1683+
ExprKind::DurationOf(duration) => write!(f, "{duration}"),
16671684
}
16681685
}
16691686
}

source/compiler/qsc_qasm/src/parser/completion/tests.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ fn begin_document() {
3030
"|OPENQASM 3;",
3131
&expect![[r#"
3232
WordKinds(
33-
PathExpr | 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,
33+
PathExpr | Annotation | Durationof | 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,
3434
)
3535
"#]],
3636
);
@@ -42,7 +42,7 @@ fn end_of_version() {
4242
"OPENQASM 3;|",
4343
&expect![[r#"
4444
WordKinds(
45-
PathExpr | 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,
45+
PathExpr | Annotation | Durationof | 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,
4646
)
4747
"#]],
4848
);

source/compiler/qsc_qasm/src/parser/completion/word_kinds.rs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,8 @@ bitflags! {
5858
// End hardcoded identifiers.
5959
//
6060

61+
const Durationof = 1 << 3; // `durationof` call, e.g. `durationof (scope)`
62+
6163
//
6264
// Begin keywords.
6365
//
@@ -107,7 +109,7 @@ bitflags! {
107109
}
108110
}
109111

110-
const KEYWORDS_START: u8 = 3;
112+
const KEYWORDS_START: u8 = 4;
111113
const fn keyword_bit(k: Keyword) -> u128 {
112114
1 << (k as u8 + KEYWORDS_START)
113115
}

source/compiler/qsc_qasm/src/parser/expr.rs

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ use crate::{
1717
ClosedBinOp, Delim, Radix, Token, TokenKind,
1818
cooked::{ComparisonOp, Literal, TimingLiteralKind},
1919
},
20+
parser::{ast::DurationofCall, stmt::parse_block},
2021
};
2122

2223
use crate::parser::Result;
@@ -133,6 +134,8 @@ fn expr_base(s: &mut ParserContext) -> Result<Expr> {
133134
})
134135
} else if token(s, TokenKind::Open(Delim::Paren)).is_ok() {
135136
paren_expr(s, lo)
137+
} else if let Some(expr) = opt(s, duration_of)? {
138+
Ok(expr)
136139
} else {
137140
match opt(s, scalar_or_array_type) {
138141
Err(err) => Err(err),
@@ -819,3 +822,24 @@ pub fn alias_expr(s: &mut ParserContext) -> Result<List<Expr>> {
819822
}
820823
Ok(list_from_iter(exprs))
821824
}
825+
826+
/// Grammar: `DURATIONOF LPAREN scope RPAREN`
827+
fn duration_of(s: &mut ParserContext) -> Result<Expr> {
828+
let lo = s.peek().span.lo;
829+
s.expect(WordKinds::Durationof);
830+
token(s, TokenKind::DurationOf)?;
831+
let name_span = s.span(lo);
832+
token(s, TokenKind::Open(Delim::Paren))?;
833+
let scope = parse_block(s)?;
834+
recovering_token(s, TokenKind::Close(Delim::Paren));
835+
let duration = DurationofCall {
836+
span: s.span(lo),
837+
name_span,
838+
scope,
839+
};
840+
let expr = Expr {
841+
span: s.span(lo),
842+
kind: Box::new(ExprKind::DurationOf(duration)),
843+
};
844+
Ok(expr)
845+
}

source/compiler/qsc_qasm/src/parser/expr/tests.rs

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1190,3 +1190,24 @@ fn addition_of_casts() {
11901190
arg: Expr [13-14]: Lit: Int(1)"#]],
11911191
);
11921192
}
1193+
1194+
#[test]
1195+
fn duration_of() {
1196+
check_expr(
1197+
"durationof({x $0;})",
1198+
&expect![[r#"
1199+
Expr [0-19]: DurationofCall [0-19]:
1200+
name_span: [0-10]
1201+
scope: Block [11-18]:
1202+
Stmt [12-17]:
1203+
annotations: <empty>
1204+
kind: GateCall [12-17]:
1205+
modifiers: <empty>
1206+
name: Ident [12-13] "x"
1207+
args: <empty>
1208+
duration: <none>
1209+
qubits:
1210+
GateOperand [14-16]:
1211+
kind: HardwareQubit [14-16]: 0"#]],
1212+
);
1213+
}

source/compiler/qsc_qasm/src/parser/mut_visit.rs

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33

44
use qsc_data_structures::span::Span;
55

6-
use crate::parser::ast::{DefParameter, DefParameterType, QubitType};
6+
use crate::parser::ast::{DefParameter, DefParameterType, DurationofCall, QubitType};
77

88
use super::ast::{
99
AccessControl, AliasDeclStmt, Annotation, ArrayBaseTypeKind, ArrayReferenceType, ArrayType,
@@ -187,6 +187,10 @@ pub trait MutVisitor: Sized {
187187
walk_cast_expr(self, expr);
188188
}
189189

190+
fn visit_duration_of_expr(&mut self, expr: &mut DurationofCall) {
191+
walk_duration_of_expr(self, expr);
192+
}
193+
190194
fn visit_index_expr(&mut self, expr: &mut IndexExpr) {
191195
walk_index_expr(self, expr);
192196
}
@@ -607,6 +611,7 @@ pub fn walk_expr(vis: &mut impl MutVisitor, expr: &mut Expr) {
607611
super::ast::ExprKind::Cast(cast) => vis.visit_cast_expr(cast),
608612
super::ast::ExprKind::IndexExpr(index_expr) => vis.visit_index_expr(index_expr),
609613
super::ast::ExprKind::Paren(expr) => vis.visit_expr(expr),
614+
super::ast::ExprKind::DurationOf(expr) => vis.visit_duration_of_expr(expr),
610615
}
611616
}
612617

@@ -640,6 +645,12 @@ pub fn walk_cast_expr(vis: &mut impl MutVisitor, expr: &mut Cast) {
640645
vis.visit_expr(&mut expr.arg);
641646
}
642647

648+
pub fn walk_duration_of_expr(vis: &mut impl MutVisitor, expr: &mut DurationofCall) {
649+
vis.visit_span(&mut expr.span);
650+
vis.visit_span(&mut expr.name_span);
651+
vis.visit_block(&mut expr.scope);
652+
}
653+
643654
pub fn walk_index_expr(vis: &mut impl MutVisitor, expr: &mut IndexExpr) {
644655
vis.visit_span(&mut expr.span);
645656
vis.visit_expr(&mut expr.collection);

source/compiler/qsc_qasm/src/semantic/ast.rs

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1093,6 +1093,7 @@ pub enum ExprKind {
10931093
Paren(Expr),
10941094
Measure(MeasureExpr),
10951095
SizeofCall(SizeofCallExpr),
1096+
DurationofCall(DurationofCallExpr),
10961097
}
10971098

10981099
impl Display for ExprKind {
@@ -1110,6 +1111,7 @@ impl Display for ExprKind {
11101111
ExprKind::Paren(expr) => write!(f, "Paren {expr}"),
11111112
ExprKind::Measure(expr) => write!(f, "{expr}"),
11121113
ExprKind::SizeofCall(call) => write!(f, "{call}"),
1114+
ExprKind::DurationofCall(call) => write!(f, "{call}"),
11131115
}
11141116
}
11151117
}
@@ -1223,6 +1225,22 @@ impl Display for SizeofCallExpr {
12231225
}
12241226
}
12251227

1228+
#[derive(Clone, Debug)]
1229+
1230+
pub struct DurationofCallExpr {
1231+
pub span: Span,
1232+
pub fn_name_span: Span,
1233+
pub scope: Block,
1234+
}
1235+
1236+
impl Display for DurationofCallExpr {
1237+
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
1238+
writeln_header(f, "DurationofCallExpr", self.span)?;
1239+
writeln_field(f, "fn_name_span", &self.fn_name_span)?;
1240+
write_field(f, "scope", &self.scope)
1241+
}
1242+
}
1243+
12261244
/// The information in this struct is aimed to be consumed
12271245
/// by the language service. The result of the computation
12281246
/// is already stored in the [`Expr::const_value`] field by

source/compiler/qsc_qasm/src/semantic/const_eval.rs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -128,6 +128,15 @@ impl Expr {
128128
// in [`Lowerer::lower_sizeof_call_expr`].
129129
None
130130
}
131+
ExprKind::DurationofCall(_) => {
132+
// We can't evaluate `durationof` calls, and we can't evaluate the
133+
// blocks within them to derive the duration. For type and semantic
134+
// checks we assume that the `durationof` call is a const expression
135+
// with a 0 duration value.
136+
let value = Duration::default();
137+
let kind = LiteralKind::Duration(value);
138+
Some(kind)
139+
}
131140
ExprKind::Err => None,
132141
}
133142
}

source/compiler/qsc_qasm/src/semantic/lowerer.rs

Lines changed: 33 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -833,6 +833,7 @@ impl Lowerer {
833833
syntax::ExprKind::Lit(lit) => self.lower_lit_expr(lit, None),
834834
syntax::ExprKind::Paren(pexpr) => self.lower_paren_expr(pexpr, expr.span),
835835
syntax::ExprKind::UnaryOp(expr) => self.lower_unary_op_expr(expr),
836+
syntax::ExprKind::DurationOf(expr) => self.lower_duration_of_expr(expr),
836837
}
837838
}
838839

@@ -1120,6 +1121,17 @@ impl Lowerer {
11201121
}
11211122
}
11221123

1124+
fn lower_duration_of_expr(&mut self, expr: &syntax::DurationofCall) -> semantic::Expr {
1125+
let scope = self.lower_block(&expr.scope);
1126+
let ty = Type::Duration(true);
1127+
let kind = semantic::ExprKind::DurationofCall(semantic::DurationofCallExpr {
1128+
span: expr.span,
1129+
fn_name_span: expr.name_span,
1130+
scope,
1131+
});
1132+
semantic::Expr::new(expr.span, kind, ty)
1133+
}
1134+
11231135
fn lower_annotations(annotations: &[Box<syntax::Annotation>]) -> Vec<semantic::Annotation> {
11241136
annotations
11251137
.iter()
@@ -1277,7 +1289,10 @@ impl Lowerer {
12771289
None => self.cast_expr_with_target_type_or_default(None, &ty, stmt_span),
12781290
};
12791291

1280-
if init_expr.ty.is_const() {
1292+
// If the type is a duration subtype, we need to evaluate the init_expr
1293+
// to get the const value and set it in the symbol.
1294+
// This is because duration subtypes can only be const.
1295+
if type_is_duration_subtype(&init_expr.ty) {
12811296
init_expr = init_expr.with_const_value(self);
12821297
symbol = symbol.with_const_expr(Rc::new(init_expr.clone()));
12831298
}
@@ -3439,6 +3454,7 @@ impl Lowerer {
34393454
}
34403455
Type::BitArray(size, _) => Self::cast_bitarray_expr_to_type(*size, ty, expr),
34413456
Type::Array(..) => Self::cast_array_expr_to_type(ty, expr),
3457+
Type::Duration(..) | Type::Stretch(..) => cast_duration_subtype_expr_to_type(ty, expr),
34423458
_ => None,
34433459
}
34443460
}
@@ -3894,22 +3910,10 @@ impl Lowerer {
38943910
let left_type = lhs.ty.clone();
38953911
let right_type = rhs.ty.clone();
38963912

3897-
// // The spec says that only these operators are allowed on durations and stretches.
3898-
// if !matches!(
3899-
// op,
3900-
// syntax::BinOp::Add | syntax::BinOp::Sub | syntax::BinOp::Mul | syntax::BinOp::Div
3901-
// ) {
3902-
// return unsupported_binop(op, left_type, right_type, span);
3903-
// }
3904-
39053913
// <https://openqasm.com/language/types.html#converting-duration-to-other-types>
39063914
// Division of two durations results in a machine-precision float
39073915
// No other operations between durations are allowed.
39083916
if both_types_are_duration_subtypes(&left_type, &right_type) {
3909-
// if op == syntax::BinOp::Mul {
3910-
// return unsupported_binop(op, left_type, right_type, span);
3911-
// }
3912-
39133917
let ty = if op == syntax::BinOp::Div {
39143918
// Division of two durations results in a machine-precision float
39153919
Type::Float(None, true)
@@ -4543,6 +4547,22 @@ fn cast_complex_expr_to_type(ty: &Type, rhs: &semantic::Expr) -> Option<semantic
45434547
None
45444548
}
45454549

4550+
fn cast_duration_subtype_expr_to_type(ty: &Type, rhs: &semantic::Expr) -> Option<semantic::Expr> {
4551+
assert!(matches!(rhs.ty, Type::Duration(..) | Type::Stretch(..)));
4552+
4553+
// we know the rhs is a duration or a stretch
4554+
// if the target type is a duration subtype, we can 'cast' it
4555+
if type_is_duration_subtype(ty) {
4556+
// duration can only 'cast' to a duration subtype.
4557+
// We aren't going to actually cast, we just
4558+
// change the type of the expression.
4559+
let mut expr = rhs.clone();
4560+
expr.ty = ty.clone();
4561+
return Some(expr);
4562+
}
4563+
None
4564+
}
4565+
45464566
fn get_identifier_name(identifier: &syntax::IdentOrIndexedIdent) -> Arc<str> {
45474567
match identifier {
45484568
syntax::IdentOrIndexedIdent::Ident(ident) => ident.name.clone(),

0 commit comments

Comments
 (0)