microsoft/qdk

Public

mirrored fromhttps://github.com/microsoft/qdkAvailable

CodeCommitsIssuesPull requestsActionsInsightsSecurity
v1.0.33

Branches

Tags

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

Clone

HTTPS

Download ZIP

compiler/qsc/src/incremental.rs

221lines · modecode

1// Copyright (c) Microsoft Corporation.
2// Licensed under the MIT License.
3
4use crate::compile::{self, compile, core, std};
5use miette::Diagnostic;
6use qsc_frontend::{
7 compile::{OpenPackageStore, PackageStore, RuntimeCapabilityFlags, SourceMap},
8 error::WithSource,
9 incremental::Increment,
10};
11use qsc_hir::hir::PackageId;
12use qsc_passes::{PackageType, PassContext};
13
14/// An incremental Q# compiler.
15pub struct Compiler {
16 /// A package store that contains the current, mutable, `CompileUnit`
17 /// as well as all its immutable dependencies.
18 store: OpenPackageStore,
19 /// The ID of the source package. The source package
20 /// is made up of the initial sources passed in when creating the compiler.
21 source_package_id: PackageId,
22 /// Context for passes that is reused across incremental compilations.
23 passes: PassContext,
24 /// The frontend incremental compiler.
25 frontend: qsc_frontend::incremental::Compiler,
26}
27
28/// An incremental compiler error.
29pub type Errors = Vec<compile::Error>;
30
31impl Compiler {
32 /// Creates a new incremental compiler, compiling the passed in sources.
33 /// # Errors
34 /// If compiling the sources fails, compiler errors are returned.
35 pub fn new(
36 include_std: bool,
37 sources: SourceMap,
38 package_type: PackageType,
39 capabilities: RuntimeCapabilityFlags,
40 ) -> Result<Self, Errors> {
41 let core = core();
42 let mut store = PackageStore::new(core);
43 let mut dependencies = Vec::new();
44 if include_std {
45 let std = std(&store, capabilities);
46 let id = store.insert(std);
47 dependencies.push(id);
48 }
49
50 let (unit, errors) = compile(&store, &dependencies, sources, package_type, capabilities);
51 if !errors.is_empty() {
52 return Err(errors);
53 }
54
55 let source_package_id = store.insert(unit);
56 dependencies.push(source_package_id);
57
58 let frontend = qsc_frontend::incremental::Compiler::new(&store, dependencies, capabilities);
59 let store = store.open();
60
61 Ok(Self {
62 store,
63 source_package_id,
64 frontend,
65 passes: PassContext::new(capabilities),
66 })
67 }
68
69 /// Compiles Q# fragments. Fragments are Q# code that can contain
70 /// top-level statements as well as namespaces. A notebook cell
71 /// or an interpreter entry is an example of fragments.
72 ///
73 /// This method returns the AST and HIR packages that were created as a result of
74 /// the compilation, however it does *not* update the current compilation.
75 ///
76 /// The caller can use the returned packages to perform passes,
77 /// get information about the newly added items, or do other modifications.
78 /// It is then the caller's responsibility to merge
79 /// these packages into the current `CompileUnit` using the `update()` method.
80 pub fn compile_fragments_fail_fast(
81 &mut self,
82 source_name: &str,
83 source_contents: &str,
84 ) -> Result<Increment, Errors> {
85 self.compile_fragments(source_name, source_contents, fail_on_error)
86 }
87
88 /// Compiles Q# fragments. See [`compile_fragments_fail_fast`] for more details.
89 ///
90 /// This method calls an accumulator function with any errors returned
91 /// from each of the stages (parsing, lowering).
92 /// If the accumulator succeeds, compilation continues.
93 /// If the accumulator returns an error, compilation stops and the
94 /// error is returned to the caller.
95 pub fn compile_fragments<F>(
96 &mut self,
97 source_name: &str,
98 source_contents: &str,
99 mut accumulate_errors: F,
100 ) -> Result<Increment, Errors>
101 where
102 F: FnMut(Errors) -> Result<(), Errors>,
103 {
104 let (core, unit) = self.store.get_open_mut();
105
106 let mut errors = false;
107 let mut increment =
108 self.frontend
109 .compile_fragments(unit, source_name, source_contents, |e| {
110 errors = errors || !e.is_empty();
111 accumulate_errors(into_errors(e))
112 })?;
113
114 // Even if we don't fail fast, skip passes if there were compilation errors.
115 if !errors {
116 let pass_errors = self.passes.run_default_passes(
117 &mut increment.hir,
118 &mut unit.assigner,
119 core,
120 PackageType::Lib,
121 );
122
123 accumulate_errors(into_errors_with_source(pass_errors, &unit.sources))?;
124 }
125
126 Ok(increment)
127 }
128
129 /// Compiles an entry expression.
130 ///
131 /// This method returns the AST and HIR packages that were created as a result of
132 /// the compilation, however it does *not* update the current compilation.
133 ///
134 /// The caller can use the returned packages to perform passes,
135 /// get information about the newly added items, or do other modifications.
136 /// It is then the caller's responsibility to merge
137 /// these packages into the current `CompileUnit` using the `update()` method.
138 pub fn compile_expr(&mut self, expr: &str) -> Result<Increment, Errors> {
139 let (core, unit) = self.store.get_open_mut();
140
141 let mut increment = self
142 .frontend
143 .compile_expr(unit, "<entry>", expr)
144 .map_err(into_errors)?;
145
146 let pass_errors = self.passes.run_default_passes(
147 &mut increment.hir,
148 &mut unit.assigner,
149 core,
150 PackageType::Lib,
151 );
152
153 if !pass_errors.is_empty() {
154 return Err(into_errors_with_source(pass_errors, &unit.sources));
155 }
156
157 Ok(increment)
158 }
159
160 /// Updates the current compilation with the AST and HIR packages,
161 /// and any associated context, returned from a previous incremental compilation.
162 pub fn update(&mut self, new: Increment) {
163 let (_, unit) = self.store.get_open_mut();
164
165 self.frontend.update(unit, new);
166 }
167
168 /// Returns a reference to the underlying package store.
169 #[must_use]
170 pub fn package_store(&self) -> &PackageStore {
171 self.store.package_store()
172 }
173
174 /// Returns ID of the current `CompileUnit`.
175 #[must_use]
176 pub fn package_id(&self) -> PackageId {
177 self.store.open_package_id()
178 }
179
180 /// Returns the ID of the source package created from the sources
181 /// passed in during inital creation.
182 #[must_use]
183 pub fn source_package_id(&self) -> PackageId {
184 self.source_package_id
185 }
186
187 /// Consumes the incremental compiler and returns an immutable package store.
188 /// This method can be used to finalize the compilation.
189 #[must_use]
190 pub fn into_package_store(self) -> (PackageStore, PackageId) {
191 self.store.into_package_store()
192 }
193}
194
195fn into_errors_with_source<T>(errors: Vec<T>, sources: &SourceMap) -> Errors
196where
197 compile::ErrorKind: From<T>,
198{
199 errors
200 .into_iter()
201 .map(|e| WithSource::from_map(sources, e.into()))
202 .collect()
203}
204
205fn into_errors<T>(errors: Vec<WithSource<T>>) -> Errors
206where
207 compile::ErrorKind: From<T>,
208 T: Diagnostic + Send + Sync,
209{
210 errors
211 .into_iter()
212 .map(qsc_frontend::error::WithSource::into_with_source)
213 .collect()
214}
215
216fn fail_on_error(errors: Errors) -> Result<(), Errors> {
217 if !errors.is_empty() {
218 return Err(errors);
219 }
220 Ok(())
221}
222