microsoft/qdk

Public

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

CodeCommitsIssuesPull requestsActionsInsightsSecurity
v1.8.0

Branches

Tags

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

Clone

HTTPS

Download ZIP

compiler/qsc/src/incremental.rs

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