microsoft/qdk

Public

mirrored from https://github.com/microsoft/qdkAvailable

CodeCommitsIssuesPull requestsActionsInsightsSecurity
dmitryv/select-updated

Branches

Tags

  • No tags available.
0Branches0Tags
Go to file
Add file
Code

Clone

HTTPS

Download ZIP

compiler/qsc_passes/src/baseprofck.rs

127lines · modecode

1// Copyright (c) Microsoft Corporation.
2// Licensed under the MIT License.
3
4#[cfg(test)]
5mod tests;
6
7use miette::Diagnostic;
8use qsc_data_structures::span::Span;
9use qsc_hir::{
10 hir::{
11 BinOp, CallableKind, Expr, ExprKind, Item, ItemKind, Lit, Package, SpecBody, SpecGen,
12 StmtKind,
13 },
14 ty::{Prim, Ty},
15 visit::{walk_expr, walk_item, walk_package, Visitor},
16};
17use thiserror::Error;
18
19#[derive(Clone, Debug, Diagnostic, Error)]
20pub enum Error {
21 #[error("cannot compare measurement results")]
22 #[diagnostic(help(
23 "comparing measurement results is not supported when performing base profile QIR generation"
24 ))]
25 #[diagnostic(code("Qsc.BaseProfCk.ResultComparison"))]
26 ResultComparison(#[label] Span),
27
28 #[error("result literals are not supported")]
29 #[diagnostic(help(
30 "result literals `One` and `Zero` are not supported when performing base profile QIR generation"
31 ))]
32 #[diagnostic(code("Qsc.BaseProfCk.ResultLiteral"))]
33 ResultLiteral(#[label] Span),
34
35 #[error("non-Result return type in entry expression")]
36 #[diagnostic(help(
37 "returning types other than Result from the entry expression is not supported when performing base profile QIR generation"
38 ))]
39 #[diagnostic(code("Qsc.BaseProfCk.ReturnNonResult"))]
40 ReturnNonResult(#[label] Span),
41
42 #[error("intrinsic operations that return types other than Result or Unit are not supported")]
43 #[diagnostic(help(
44 "intrinsic operations that return types other than Result or Unit are not supported when performing base profile QIR generation"
45 ))]
46 #[diagnostic(code("Qsc.BaseProfCk.UnsupportedIntrinsic"))]
47 UnsupportedIntrinsic(#[label] Span),
48}
49
50#[must_use]
51pub fn check_base_profile_compliance(package: &Package) -> Vec<Error> {
52 let mut checker = Checker { errors: Vec::new() };
53 if let Some(entry) = &package.entry {
54 if any_non_result_ty(&entry.ty) {
55 checker.errors.push(Error::ReturnNonResult(entry.span));
56 }
57 }
58 checker.visit_package(package);
59
60 checker.errors
61}
62
63struct Checker {
64 errors: Vec<Error>,
65}
66
67impl<'a> Visitor<'a> for Checker {
68 fn visit_package(&mut self, package: &'a Package) {
69 if let Some(StmtKind::Expr(expr)) = &package.stmts.last().map(|stmt| &stmt.kind) {
70 if any_non_result_ty(&expr.ty) {
71 self.errors.push(Error::ReturnNonResult(expr.span));
72 }
73 }
74
75 walk_package(self, package);
76 }
77
78 fn visit_item(&mut self, item: &'a Item) {
79 match &item.kind {
80 ItemKind::Callable(callable)
81 if callable.kind == CallableKind::Operation
82 && callable.body.body == SpecBody::Gen(SpecGen::Intrinsic)
83 && callable.output != Ty::Prim(Prim::Result)
84 && callable.output != Ty::Prim(Prim::Qubit)
85 && callable.output != Ty::UNIT =>
86 {
87 self.errors
88 .push(Error::UnsupportedIntrinsic(callable.name.span));
89 }
90 _ => {}
91 }
92
93 walk_item(self, item);
94 }
95
96 fn visit_expr(&mut self, expr: &'a Expr) {
97 match &expr.kind {
98 ExprKind::BinOp(BinOp::Eq | BinOp::Neq, lhs, _) if any_result_ty(&lhs.ty) => {
99 self.errors.push(Error::ResultComparison(expr.span));
100 }
101 ExprKind::Lit(Lit::Result(_)) => {
102 self.errors.push(Error::ResultLiteral(expr.span));
103 }
104 _ => {}
105 }
106 walk_expr(self, expr);
107 }
108}
109
110fn any_result_ty(ty: &Ty) -> bool {
111 match ty {
112 Ty::Array(ty) => any_result_ty(ty),
113 Ty::Prim(Prim::Result) => true,
114 Ty::Tuple(tys) => tys.iter().any(any_result_ty),
115 _ => false,
116 }
117}
118
119fn any_non_result_ty(ty: &Ty) -> bool {
120 match ty {
121 Ty::Array(ty) => any_non_result_ty(ty),
122 Ty::Prim(Prim::Result) => false,
123 Ty::Tuple(tys) if tys.is_empty() => true,
124 Ty::Tuple(tys) => tys.iter().any(any_non_result_ty),
125 _ => true,
126 }
127}
128