microsoft/qdk

Public

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

CodeCommitsIssuesPull requestsActionsInsightsSecurity
copilot/fix-2145

Branches

Tags

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

Clone

HTTPS

Download ZIP

compiler/qsc/src/incremental.rs

329lines · 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 dependencies: &Dependencies,
89 ) -> Result<Self, Errors> {
90 let mut dependencies = dependencies.iter().map(Clone::clone).collect::<Vec<_>>();
91
92 dependencies.push((source_package_id, None));
93
94 let frontend = qsc_frontend::incremental::Compiler::new(
95 &store,
96 &dependencies[..],
97 capabilities,
98 language_features,
99 );
100 let store = store.open();
101
102 Ok(Self {
103 store,
104 source_package_id,
105 frontend,
106 passes: PassContext::default(),
107 })
108 }
109
110 /// Compiles Q# fragments. Fragments are Q# code that can contain
111 /// top-level statements as well as namespaces. A notebook cell
112 /// or an interpreter entry is an example of fragments.
113 ///
114 /// This method returns the AST and HIR packages that were created as a result of
115 /// the compilation, however it does *not* update the current compilation.
116 ///
117 /// The caller can use the returned packages to perform passes,
118 /// get information about the newly added items, or do other modifications.
119 /// It is then the caller's responsibility to merge
120 /// these packages into the current `CompileUnit` using the `update()` method.
121 pub fn compile_fragments_fail_fast(
122 &mut self,
123 source_name: &str,
124 source_contents: &str,
125 ) -> Result<Increment, Errors> {
126 self.compile_fragments(source_name, source_contents, fail_on_error)
127 }
128
129 /// Compiles Q# ast fragments. Fragments are Q# code that can contain
130 /// top-level statements as well as namespaces. A notebook cell
131 /// or an interpreter entry is an example of fragments.
132 ///
133 /// This method returns the AST and HIR packages that were created as a result of
134 /// the compilation, however it does *not* update the current compilation.
135 ///
136 /// The caller can use the returned packages to perform passes,
137 /// get information about the newly added items, or do other modifications.
138 /// It is then the caller's responsibility to merge
139 /// these packages into the current `CompileUnit` using the `update()` method.
140 pub fn compile_ast_fragments_fail_fast(
141 &mut self,
142 source_name: &str,
143 source_contents: &str,
144 package: ast::Package,
145 ) -> Result<Increment, Errors> {
146 self.compile_ast_fragments(source_name, source_contents, package, fail_on_error)
147 }
148
149 /// Compiles Q# fragments. See [`compile_fragments_fail_fast`] for more details.
150 ///
151 /// This method calls an accumulator function with any errors returned
152 /// from each of the stages (parsing, lowering).
153 /// If the accumulator succeeds, compilation continues.
154 /// If the accumulator returns an error, compilation stops and the
155 /// error is returned to the caller.
156 pub fn compile_fragments<F>(
157 &mut self,
158 source_name: &str,
159 source_contents: &str,
160 mut accumulate_errors: F,
161 ) -> Result<Increment, Errors>
162 where
163 F: FnMut(Errors) -> Result<(), Errors>,
164 {
165 let (core, unit) = self.store.get_open_mut();
166
167 let mut errors = false;
168 let mut increment =
169 self.frontend
170 .compile_fragments(unit, source_name, source_contents, |e| {
171 errors = errors || !e.is_empty();
172 accumulate_errors(into_errors(e))
173 })?;
174
175 // Even if we don't fail fast, skip passes if there were compilation errors.
176 if !errors {
177 let pass_errors = self.passes.run_default_passes(
178 &mut increment.hir,
179 &mut unit.assigner,
180 core,
181 PackageType::Lib,
182 );
183
184 accumulate_errors(into_errors_with_source(pass_errors, &unit.sources))?;
185 }
186
187 Ok(increment)
188 }
189
190 /// Compiles Q# ast fragments. See [`compile_ast_fragments_fail_fast`] for more details.
191 ///
192 /// This method calls an accumulator function with any errors returned
193 /// from each of the stages (parsing, lowering).
194 /// If the accumulator succeeds, compilation continues.
195 /// If the accumulator returns an error, compilation stops and the
196 /// error is returned to the caller.
197 pub fn compile_ast_fragments<F>(
198 &mut self,
199 source_name: &str,
200 source_contents: &str,
201 package: ast::Package,
202 mut accumulate_errors: F,
203 ) -> Result<Increment, Errors>
204 where
205 F: FnMut(Errors) -> Result<(), Errors>,
206 {
207 let (core, unit) = self.store.get_open_mut();
208
209 let mut errors = false;
210 let mut increment = self.frontend.compile_ast_fragments(
211 unit,
212 source_name,
213 source_contents,
214 package,
215 |e| {
216 errors = errors || !e.is_empty();
217 accumulate_errors(into_errors(e))
218 },
219 )?;
220
221 // Even if we don't fail fast, skip passes if there were compilation errors.
222 if !errors {
223 let pass_errors = self.passes.run_default_passes(
224 &mut increment.hir,
225 &mut unit.assigner,
226 core,
227 PackageType::Lib,
228 );
229
230 accumulate_errors(into_errors_with_source(pass_errors, &unit.sources))?;
231 }
232
233 Ok(increment)
234 }
235
236 /// Compiles an entry expression.
237 ///
238 /// This method returns the AST and HIR packages that were created as a result of
239 /// the compilation, however it does *not* update the current compilation.
240 ///
241 /// The caller can use the returned packages to perform passes,
242 /// get information about the newly added items, or do other modifications.
243 /// It is then the caller's responsibility to merge
244 /// these packages into the current `CompileUnit` using the `update()` method.
245 pub fn compile_entry_expr(&mut self, expr: &str) -> Result<Increment, Errors> {
246 let (core, unit) = self.store.get_open_mut();
247
248 let mut increment = self
249 .frontend
250 .compile_entry_expr(unit, expr)
251 .map_err(into_errors)?;
252
253 let pass_errors = self.passes.run_default_passes(
254 &mut increment.hir,
255 &mut unit.assigner,
256 core,
257 PackageType::Lib,
258 );
259
260 if !pass_errors.is_empty() {
261 return Err(into_errors_with_source(pass_errors, &unit.sources));
262 }
263
264 Ok(increment)
265 }
266
267 /// Updates the current compilation with the AST and HIR packages,
268 /// and any associated context, returned from a previous incremental compilation.
269 /// Entry expressions are ignored.
270 pub fn update(&mut self, new: Increment) {
271 let (_, unit) = self.store.get_open_mut();
272
273 self.frontend.update(unit, new);
274 }
275
276 /// Returns a reference to the underlying package store.
277 #[must_use]
278 pub fn package_store(&self) -> &PackageStore {
279 self.store.package_store()
280 }
281
282 /// Returns ID of the current `CompileUnit`.
283 #[must_use]
284 pub fn package_id(&self) -> PackageId {
285 self.store.open_package_id()
286 }
287
288 /// Returns the ID of the source package created from the sources
289 /// passed in during inital creation.
290 #[must_use]
291 pub fn source_package_id(&self) -> PackageId {
292 self.source_package_id
293 }
294
295 /// Consumes the incremental compiler and returns an immutable package store.
296 /// This method can be used to finalize the compilation.
297 #[must_use]
298 pub fn into_package_store(self) -> (PackageStore, PackageId) {
299 self.store.into_package_store()
300 }
301}
302
303fn into_errors_with_source<T>(errors: Vec<T>, sources: &SourceMap) -> Errors
304where
305 compile::ErrorKind: From<T>,
306{
307 errors
308 .into_iter()
309 .map(|e| WithSource::from_map(sources, e.into()))
310 .collect()
311}
312
313fn into_errors<T>(errors: Vec<WithSource<T>>) -> Errors
314where
315 compile::ErrorKind: From<T>,
316 T: Diagnostic + Send + Sync,
317{
318 errors
319 .into_iter()
320 .map(qsc_frontend::error::WithSource::into_with_source)
321 .collect()
322}
323
324fn fail_on_error(errors: Errors) -> Result<(), Errors> {
325 if !errors.is_empty() {
326 return Err(errors);
327 }
328 Ok(())
329}
330