microsoft/qdk

Public

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

CodeCommitsIssuesPull requestsActionsInsightsSecurity
v1.2.0

Branches

Tags

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

Clone

HTTPS

Download ZIP

compiler/qsc/src/incremental.rs

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