microsoft/qdk

Public

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

CodeCommitsIssuesPull requestsActionsInsightsSecurity
iadavis/pipeline-issue-debugging

Branches

Tags

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

Clone

HTTPS

Download ZIP

source/compiler/qsc/src/incremental.rs

331lines · modecode

1// Copyright (c) Microsoft Corporation.
2// Licensed under the MIT License.
3
4use crate::compile::{self, compile};
5use miette::Diagnostic;
6
7use qsc_ast::ast;
8use qsc_data_structures::{
9 error::WithSource, language_features::LanguageFeatures, source::SourceMap,
10 target::TargetCapabilityFlags,
11};
12
13use qsc_frontend::{
14 compile::{Dependencies, OpenPackageStore, PackageStore},
15 incremental::Increment,
16};
17use qsc_hir::hir::PackageId;
18use qsc_passes::{PackageType, PassContext};
19
20/// An incremental Q# compiler.
21pub struct Compiler {
22 /// A package store that contains the current, mutable, `CompileUnit`
23 /// as well as all its immutable dependencies.
24 store: OpenPackageStore,
25 /// The ID of the source package. The source package
26 /// is made up of the initial sources passed in when creating the compiler.
27 source_package_id: PackageId,
28 /// Context for passes that is reused across incremental compilations.
29 passes: PassContext,
30 /// The frontend incremental compiler.
31 frontend: qsc_frontend::incremental::Compiler,
32}
33
34/// An incremental compiler error.
35pub type Errors = Vec<compile::Error>;
36
37impl Compiler {
38 /// Creates a new incremental compiler, compiling the passed in sources.
39 /// # Errors
40 /// If compiling the sources fails, compiler errors are returned.
41 pub fn new(
42 sources: SourceMap,
43 package_type: PackageType,
44 capabilities: TargetCapabilityFlags,
45 language_features: LanguageFeatures,
46 mut store: PackageStore,
47 dependencies: &Dependencies,
48 ) -> Result<Self, Errors> {
49 let (mut unit, errors) = compile(
50 &store,
51 dependencies,
52 sources,
53 package_type,
54 capabilities,
55 language_features,
56 );
57 if !errors.is_empty() {
58 return Err(errors);
59 }
60
61 // make the user code fully public, so increments on top of this can access them
62 unit.expose();
63
64 let mut dependencies = dependencies.iter().map(Clone::clone).collect::<Vec<_>>();
65
66 let source_package_id = store.insert(unit);
67 dependencies.push((source_package_id, None));
68
69 let frontend = qsc_frontend::incremental::Compiler::new(
70 &store,
71 &dependencies[..],
72 capabilities,
73 language_features,
74 );
75 let store = store.open();
76
77 Ok(Self {
78 store,
79 source_package_id,
80 frontend,
81 passes: PassContext::default(),
82 })
83 }
84
85 pub fn with_package_store(
86 store: PackageStore,
87 source_package_id: PackageId,
88 capabilities: TargetCapabilityFlags,
89 language_features: LanguageFeatures,
90 dependencies: &Dependencies,
91 ) -> Result<Self, Errors> {
92 let mut dependencies = dependencies.iter().map(Clone::clone).collect::<Vec<_>>();
93
94 dependencies.push((source_package_id, None));
95
96 let frontend = qsc_frontend::incremental::Compiler::new(
97 &store,
98 &dependencies[..],
99 capabilities,
100 language_features,
101 );
102 let store = store.open();
103
104 Ok(Self {
105 store,
106 source_package_id,
107 frontend,
108 passes: PassContext::default(),
109 })
110 }
111
112 /// Compiles Q# fragments. Fragments are Q# code that can contain
113 /// top-level statements as well as namespaces. A notebook cell
114 /// or an interpreter entry is an example of fragments.
115 ///
116 /// This method returns the AST and HIR packages that were created as a result of
117 /// the compilation, however it does *not* update the current compilation.
118 ///
119 /// The caller can use the returned packages to perform passes,
120 /// get information about the newly added items, or do other modifications.
121 /// It is then the caller's responsibility to merge
122 /// these packages into the current `CompileUnit` using the `update()` method.
123 pub fn compile_fragments_fail_fast(
124 &mut self,
125 source_name: &str,
126 source_contents: &str,
127 ) -> Result<Increment, Errors> {
128 self.compile_fragments(source_name, source_contents, fail_on_error)
129 }
130
131 /// Compiles Q# ast fragments. Fragments are Q# code that can contain
132 /// top-level statements as well as namespaces. A notebook cell
133 /// or an interpreter entry is an example of fragments.
134 ///
135 /// This method returns the AST and HIR packages that were created as a result of
136 /// the compilation, however it does *not* update the current compilation.
137 ///
138 /// The caller can use the returned packages to perform passes,
139 /// get information about the newly added items, or do other modifications.
140 /// It is then the caller's responsibility to merge
141 /// these packages into the current `CompileUnit` using the `update()` method.
142 pub fn compile_ast_fragments_fail_fast(
143 &mut self,
144 source_name: &str,
145 source_contents: &str,
146 package: ast::Package,
147 ) -> Result<Increment, Errors> {
148 self.compile_ast_fragments(source_name, source_contents, package, fail_on_error)
149 }
150
151 /// Compiles Q# fragments. See [`compile_fragments_fail_fast`] for more details.
152 ///
153 /// This method calls an accumulator function with any errors returned
154 /// from each of the stages (parsing, lowering).
155 /// If the accumulator succeeds, compilation continues.
156 /// If the accumulator returns an error, compilation stops and the
157 /// error is returned to the caller.
158 pub fn compile_fragments<F>(
159 &mut self,
160 source_name: &str,
161 source_contents: &str,
162 mut accumulate_errors: F,
163 ) -> Result<Increment, Errors>
164 where
165 F: FnMut(Errors) -> Result<(), Errors>,
166 {
167 let (core, unit) = self.store.get_open_mut();
168
169 let mut errors = false;
170 let mut increment =
171 self.frontend
172 .compile_fragments(unit, source_name, source_contents, |e| {
173 errors = errors || !e.is_empty();
174 accumulate_errors(into_errors(e))
175 })?;
176
177 // Even if we don't fail fast, skip passes if there were compilation errors.
178 if !errors {
179 let pass_errors = self.passes.run_default_passes(
180 &mut increment.hir,
181 &mut unit.assigner,
182 core,
183 PackageType::Lib,
184 );
185
186 accumulate_errors(into_errors_with_source(pass_errors, &unit.sources))?;
187 }
188
189 Ok(increment)
190 }
191
192 /// Compiles Q# ast fragments. See [`compile_ast_fragments_fail_fast`] for more details.
193 ///
194 /// This method calls an accumulator function with any errors returned
195 /// from each of the stages (parsing, lowering).
196 /// If the accumulator succeeds, compilation continues.
197 /// If the accumulator returns an error, compilation stops and the
198 /// error is returned to the caller.
199 pub fn compile_ast_fragments<F>(
200 &mut self,
201 source_name: &str,
202 source_contents: &str,
203 package: ast::Package,
204 mut accumulate_errors: F,
205 ) -> Result<Increment, Errors>
206 where
207 F: FnMut(Errors) -> Result<(), Errors>,
208 {
209 let (core, unit) = self.store.get_open_mut();
210
211 let mut errors = false;
212 let mut increment = self.frontend.compile_ast_fragments(
213 unit,
214 source_name,
215 source_contents,
216 package,
217 |e| {
218 errors = errors || !e.is_empty();
219 accumulate_errors(into_errors(e))
220 },
221 )?;
222
223 // Even if we don't fail fast, skip passes if there were compilation errors.
224 if !errors {
225 let pass_errors = self.passes.run_default_passes(
226 &mut increment.hir,
227 &mut unit.assigner,
228 core,
229 PackageType::Lib,
230 );
231
232 accumulate_errors(into_errors_with_source(pass_errors, &unit.sources))?;
233 }
234
235 Ok(increment)
236 }
237
238 /// Compiles an entry expression.
239 ///
240 /// This method returns the AST and HIR packages that were created as a result of
241 /// the compilation, however it does *not* update the current compilation.
242 ///
243 /// The caller can use the returned packages to perform passes,
244 /// get information about the newly added items, or do other modifications.
245 /// It is then the caller's responsibility to merge
246 /// these packages into the current `CompileUnit` using the `update()` method.
247 pub fn compile_entry_expr(&mut self, expr: &str) -> Result<Increment, Errors> {
248 let (core, unit) = self.store.get_open_mut();
249
250 let mut increment = self
251 .frontend
252 .compile_entry_expr(unit, expr)
253 .map_err(into_errors)?;
254
255 let pass_errors = self.passes.run_default_passes(
256 &mut increment.hir,
257 &mut unit.assigner,
258 core,
259 PackageType::Lib,
260 );
261
262 if !pass_errors.is_empty() {
263 return Err(into_errors_with_source(pass_errors, &unit.sources));
264 }
265
266 Ok(increment)
267 }
268
269 /// Updates the current compilation with the AST and HIR packages,
270 /// and any associated context, returned from a previous incremental compilation.
271 /// Entry expressions are ignored.
272 pub fn update(&mut self, new: Increment) {
273 let (_, unit) = self.store.get_open_mut();
274
275 self.frontend.update(unit, new);
276 }
277
278 /// Returns a reference to the underlying package store.
279 #[must_use]
280 pub fn package_store(&self) -> &PackageStore {
281 self.store.package_store()
282 }
283
284 /// Returns ID of the current `CompileUnit`.
285 #[must_use]
286 pub fn package_id(&self) -> PackageId {
287 self.store.open_package_id()
288 }
289
290 /// Returns the ID of the source package created from the sources
291 /// passed in during initial creation.
292 #[must_use]
293 pub fn source_package_id(&self) -> PackageId {
294 self.source_package_id
295 }
296
297 /// Consumes the incremental compiler and returns an immutable package store.
298 /// This method can be used to finalize the compilation.
299 #[must_use]
300 pub fn into_package_store(self) -> (PackageStore, PackageId) {
301 self.store.into_package_store()
302 }
303}
304
305fn into_errors_with_source<T>(errors: Vec<T>, sources: &SourceMap) -> Errors
306where
307 compile::ErrorKind: From<T>,
308{
309 errors
310 .into_iter()
311 .map(|e| WithSource::from_map(sources, e.into()))
312 .collect()
313}
314
315fn into_errors<T>(errors: Vec<WithSource<T>>) -> Errors
316where
317 compile::ErrorKind: From<T>,
318 T: Diagnostic + Send + Sync,
319{
320 errors
321 .into_iter()
322 .map(qsc_data_structures::error::WithSource::into_with_source)
323 .collect()
324}
325
326fn fail_on_error(errors: Errors) -> Result<(), Errors> {
327 if !errors.is_empty() {
328 return Err(errors);
329 }
330 Ok(())
331}
332