microsoft/qdk
Publicmirrored from https://github.com/microsoft/qdkAvailable
compiler/qsc_passes/src/baseprofck.rs
127lines · modecode
| 1 | // Copyright (c) Microsoft Corporation. |
| 2 | // Licensed under the MIT License. |
| 3 | |
| 4 | #[cfg(test)] |
| 5 | mod tests; |
| 6 | |
| 7 | use miette::Diagnostic; |
| 8 | use qsc_data_structures::span::Span; |
| 9 | use 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 | }; |
| 17 | use thiserror::Error; |
| 18 | |
| 19 | #[derive(Clone, Debug, Diagnostic, Error)] |
| 20 | pub 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] |
| 51 | pub 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 | |
| 63 | struct Checker { |
| 64 | errors: Vec<Error>, |
| 65 | } |
| 66 | |
| 67 | impl<'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 | |
| 110 | fn 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 | |
| 119 | fn 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 | |