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_frontend/src/compile.rs

550lines · modecode

1// Copyright (c) Microsoft Corporation.
2// Licensed under the MIT License.
3
4#[cfg(test)]
5mod tests;
6
7pub mod preprocess;
8
9use crate::{
10 lower::{self, Lowerer},
11 resolve::{self, GlobalScope, Locals, Names, Resolver},
12 typeck::{self, Checker, Table},
13};
14
15use miette::{Diagnostic, Report};
16use preprocess::TrackedName;
17use qsc_ast::{
18 assigner::Assigner as AstAssigner,
19 ast::{self, TopLevelNode},
20 mut_visit::MutVisitor,
21 validate::Validator as AstValidator,
22 visit::Visitor as _,
23};
24use qsc_data_structures::{
25 error::WithSource,
26 index_map::{self, IndexMap},
27 language_features::LanguageFeatures,
28 source::{SourceContents, SourceMap, SourceName},
29 span::Span,
30 target::{Profile, TargetCapabilityFlags},
31};
32use qsc_hir::{
33 assigner::Assigner as HirAssigner,
34 global::{self},
35 hir::{self, PackageId},
36 validate::Validator as HirValidator,
37 visit::Visitor as _,
38};
39use std::{fmt::Debug, sync::Arc};
40use thiserror::Error;
41
42#[derive(Debug)]
43pub struct CompileUnit {
44 pub package: hir::Package,
45 pub ast: AstPackage,
46 pub assigner: HirAssigner,
47 pub sources: SourceMap,
48 pub errors: Vec<Error>,
49 pub dropped_names: Vec<TrackedName>,
50}
51
52impl CompileUnit {
53 #[must_use]
54 pub fn new(package_id: PackageId) -> Self {
55 Self {
56 package: hir::Package::new(package_id),
57 ast: Default::default(),
58 assigner: Default::default(),
59 sources: Default::default(),
60 errors: Default::default(),
61 dropped_names: Default::default(),
62 }
63 }
64
65 pub fn expose(&mut self) {
66 for (_item_id, item) in self.package.items.iter_mut() {
67 item.visibility = hir::Visibility::Public;
68 }
69 }
70
71 pub fn package_id(&self) -> PackageId {
72 self.package.package_id
73 }
74}
75
76#[derive(Debug, Default)]
77pub struct AstPackage {
78 pub package: ast::Package,
79 pub tys: Table,
80 pub names: Names,
81 pub locals: Locals,
82 pub globals: GlobalScope,
83}
84
85// the arc<str> is only `None` for the legacy stdlib, core, and an interpreter special case
86pub type Dependencies = [(PackageId, Option<Arc<str>>)];
87
88#[derive(Clone, Debug, Diagnostic, Error)]
89#[diagnostic(transparent)]
90#[error(transparent)]
91pub struct Error(pub(super) ErrorKind);
92
93#[derive(Clone, Debug, Diagnostic, Error)]
94#[diagnostic(transparent)]
95pub(super) enum ErrorKind {
96 #[error("syntax error")]
97 Parse(#[from] qsc_parse::Error),
98 #[error("name error")]
99 Resolve(#[from] resolve::Error),
100 #[error("type error")]
101 Type(#[from] typeck::Error),
102 #[error(transparent)]
103 Lower(#[from] lower::Error),
104}
105
106pub struct PackageStore {
107 core: global::Table,
108 units: IndexMap<PackageId, CompileUnit>,
109 next_id: PackageId,
110}
111
112impl Debug for PackageStore {
113 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
114 write!(f, "package store with {} units", self.units.iter().count())
115 }
116}
117
118impl PackageStore {
119 #[must_use]
120 pub fn new(core: CompileUnit) -> Self {
121 let table = global::iter_package(PackageId::CORE, &core.package).collect();
122 let mut units = IndexMap::new();
123 units.insert(PackageId::CORE, core);
124 Self {
125 core: table,
126 units,
127 next_id: PackageId::CORE.successor(),
128 }
129 }
130
131 #[must_use]
132 pub fn core(&self) -> &global::Table {
133 &self.core
134 }
135
136 #[must_use]
137 pub fn peek_package_id(&self) -> PackageId {
138 self.next_id
139 }
140
141 pub fn insert(&mut self, unit: CompileUnit) -> PackageId {
142 let id = self.next_id;
143 assert_eq!(
144 id,
145 unit.package_id(),
146 "The id of the inserted unit should match the next_id of the store."
147 );
148 self.next_id = id.successor();
149 self.units.insert(id, unit);
150 id
151 }
152
153 #[must_use]
154 pub fn get(&self, id: PackageId) -> Option<&CompileUnit> {
155 self.units.get(id)
156 }
157
158 #[must_use]
159 pub fn iter(&self) -> Iter<'_> {
160 Iter(self.units.iter())
161 }
162
163 /// "Opens" the package store. This inserts an empty
164 /// package into the store, which will be considered
165 /// the open package and which can be incrementally updated.
166 #[must_use]
167 pub fn open(mut self) -> OpenPackageStore {
168 let id = self.next_id;
169 self.next_id = id.successor();
170 self.units.insert(id, CompileUnit::new(id));
171
172 OpenPackageStore {
173 store: self,
174 open: id,
175 }
176 }
177
178 #[must_use]
179 pub fn is_empty(&self) -> bool {
180 self.units.is_empty()
181 }
182}
183
184impl<'a> IntoIterator for &'a PackageStore {
185 type IntoIter = Iter<'a>;
186 type Item = (qsc_hir::hir::PackageId, &'a CompileUnit);
187 fn into_iter(self) -> Self::IntoIter {
188 self.iter()
189 }
190}
191
192/// A package store that contains one mutable `CompileUnit`.
193pub struct OpenPackageStore {
194 store: PackageStore,
195 open: PackageId,
196}
197
198impl OpenPackageStore {
199 /// Returns a reference to the underlying, immutable,
200 /// package store.
201 #[must_use]
202 pub fn package_store(&self) -> &PackageStore {
203 &self.store
204 }
205
206 /// Returns the ID of the open package.
207 #[must_use]
208 pub fn open_package_id(&self) -> PackageId {
209 self.open
210 }
211
212 /// Returns a mutable reference to the open package,
213 /// along with a reference to the core library that can be used
214 /// to perform passes.
215 #[must_use]
216 pub fn get_open_mut(&mut self) -> (&global::Table, &mut CompileUnit) {
217 let id = self.open;
218
219 (
220 &self.store.core,
221 self.store
222 .units
223 .get_mut(id)
224 .expect("open package id should exist in store"),
225 )
226 }
227
228 /// Consumes the `OpenPackageStore` and returns a `PackageStore`
229 /// along with the id of the formerly open package.
230 #[must_use]
231 pub fn into_package_store(self) -> (PackageStore, PackageId) {
232 (self.store, self.open)
233 }
234}
235
236pub struct Iter<'a>(index_map::Iter<'a, PackageId, CompileUnit>);
237
238impl<'a> Iterator for Iter<'a> {
239 type Item = (PackageId, &'a CompileUnit);
240
241 fn next(&mut self) -> Option<Self::Item> {
242 self.0.next()
243 }
244}
245
246impl DoubleEndedIterator for Iter<'_> {
247 fn next_back(&mut self) -> Option<Self::Item> {
248 self.0.next_back()
249 }
250}
251
252pub(super) struct Offsetter(pub(super) u32);
253
254impl MutVisitor for Offsetter {
255 fn visit_span(&mut self, span: &mut Span) {
256 span.lo += self.0;
257 span.hi += self.0;
258 }
259}
260
261#[must_use]
262pub fn compile(
263 store: &PackageStore,
264 dependencies: &Dependencies,
265 sources: SourceMap,
266 capabilities: TargetCapabilityFlags,
267 language_features: LanguageFeatures,
268) -> CompileUnit {
269 let (ast_package, parse_errors) = parse_all(&sources, language_features);
270
271 compile_ast(
272 store,
273 dependencies,
274 ast_package,
275 sources,
276 capabilities,
277 parse_errors,
278 )
279}
280
281#[allow(clippy::module_name_repetitions)]
282pub fn compile_ast(
283 store: &PackageStore,
284 dependencies: &Dependencies,
285 mut ast_package: ast::Package,
286 sources: SourceMap,
287 capabilities: TargetCapabilityFlags,
288 parse_errors: Vec<qsc_parse::Error>,
289) -> CompileUnit {
290 let mut cond_compile = preprocess::Conditional::new(capabilities);
291 cond_compile.visit_package(&mut ast_package);
292 let dropped_names = cond_compile.into_names();
293
294 let mut remove_spans = preprocess::RemoveCircuitSpans::new(&sources);
295 remove_spans.visit_package(&mut ast_package);
296
297 let mut ast_assigner = AstAssigner::new();
298 ast_assigner.visit_package(&mut ast_package);
299 AstValidator::default().visit_package(&ast_package);
300 let mut hir_assigner = HirAssigner::new();
301 let ResolveResult {
302 names,
303 locals,
304 globals,
305 errors: name_errors,
306 } = resolve_all(
307 store,
308 dependencies,
309 &mut hir_assigner,
310 &ast_package,
311 dropped_names.clone(),
312 );
313 let (tys, ty_errors) = typeck_all(store, dependencies, &ast_package, &names);
314 let package_id = store.peek_package_id();
315 let mut lowerer = Lowerer::new(package_id);
316 let package = lowerer
317 .with(&mut hir_assigner, &names, &tys)
318 .lower_package(&ast_package);
319 HirValidator::default().visit_package(&package);
320 let lower_errors = lowerer.drain_errors();
321
322 let errors = parse_errors
323 .into_iter()
324 .map(Into::into)
325 .chain(name_errors.into_iter().map(Into::into))
326 .chain(ty_errors.into_iter().map(Into::into))
327 .chain(lower_errors.into_iter().map(Into::into))
328 .map(Error)
329 .collect();
330
331 CompileUnit {
332 package,
333 ast: AstPackage {
334 package: ast_package,
335 tys,
336 names,
337 locals,
338 globals,
339 },
340 assigner: hir_assigner,
341 sources,
342 errors,
343 dropped_names,
344 }
345}
346
347/// Compiles the core library.
348///
349/// # Panics
350///
351/// Panics if the core library does not compile without errors.
352#[must_use]
353pub fn core() -> CompileUnit {
354 let store = PackageStore {
355 core: global::Table::default(),
356 units: IndexMap::new(),
357 next_id: PackageId::CORE,
358 };
359
360 let core: Vec<(SourceName, SourceContents)> = library::CORE_LIB
361 .iter()
362 .map(|(name, contents)| ((*name).into(), (*contents).into()))
363 .collect();
364 let sources = SourceMap::new(core, None);
365
366 let mut unit = compile(
367 &store,
368 &[],
369 sources,
370 TargetCapabilityFlags::empty(),
371 LanguageFeatures::default(),
372 );
373 assert_no_errors(&unit.sources, &mut unit.errors);
374 unit
375}
376
377/// Compiles the standard library.
378///
379/// # Panics
380///
381/// Panics if the standard library does not compile without errors.
382#[must_use]
383pub fn std(store: &PackageStore, capabilities: TargetCapabilityFlags) -> CompileUnit {
384 let std: Vec<(SourceName, SourceContents)> = library::STD_LIB
385 .iter()
386 .map(|(name, contents)| ((*name).into(), (*contents).into()))
387 .collect();
388 let sources = SourceMap::new(std, None);
389
390 let mut unit = compile(
391 store,
392 &[(PackageId::CORE, None)],
393 sources,
394 capabilities,
395 LanguageFeatures::default(),
396 );
397 assert_no_errors(&unit.sources, &mut unit.errors);
398 unit
399}
400
401#[must_use]
402pub fn parse_all(
403 sources: &SourceMap,
404 features: LanguageFeatures,
405) -> (ast::Package, Vec<qsc_parse::Error>) {
406 let mut namespaces = Vec::new();
407 let mut errors = Vec::new();
408 for source in sources.relative_sources() {
409 let (source_namespaces, source_errors) =
410 qsc_parse::namespaces(&source.contents, Some(&source.name), features);
411 for mut namespace in source_namespaces {
412 Offsetter(source.offset).visit_namespace(&mut namespace);
413 namespaces.push(TopLevelNode::Namespace(namespace));
414 }
415
416 append_parse_errors(&mut errors, source.offset, source_errors);
417 }
418
419 let entry = sources
420 .entry()
421 .as_ref()
422 .filter(|source| !source.contents.is_empty())
423 .map(|source| {
424 let (mut entry, entry_errors) = qsc_parse::expr(&source.contents, features);
425 Offsetter(source.offset).visit_expr(&mut entry);
426 append_parse_errors(&mut errors, source.offset, entry_errors);
427 entry
428 });
429
430 let package = ast::Package {
431 id: ast::NodeId::default(),
432 nodes: namespaces.into_boxed_slice(),
433 entry,
434 };
435
436 (package, errors)
437}
438
439#[must_use]
440pub fn get_target_profile_from_entry_point(
441 sources: &[(Arc<str>, Arc<str>)],
442) -> Option<(Profile, Span)> {
443 let (ast_package, parse_errors) = parse_all(
444 &SourceMap::new(sources.iter().cloned(), None),
445 LanguageFeatures::default(),
446 );
447
448 if !parse_errors.is_empty() {
449 return None;
450 }
451
452 let mut check = preprocess::DetectEntryPointProfile::new();
453 check.visit_package(&ast_package);
454 check.profile
455}
456
457pub(crate) struct ResolveResult {
458 pub names: Names,
459 pub locals: Locals,
460 pub globals: GlobalScope,
461 pub errors: Vec<resolve::Error>,
462}
463
464fn resolve_all(
465 store: &PackageStore,
466 dependencies: &Dependencies,
467 assigner: &mut HirAssigner,
468 package: &ast::Package,
469 mut dropped_names: Vec<TrackedName>,
470) -> ResolveResult {
471 let mut globals = resolve::GlobalTable::new();
472 let mut errors = Vec::new();
473 if let Some(unit) = store.get(PackageId::CORE) {
474 globals.add_external_package(PackageId::CORE, &unit.package, store, None);
475 dropped_names.extend(unit.dropped_names.iter().cloned());
476 }
477
478 for (id, alias) in dependencies {
479 let unit = store
480 .get(*id)
481 .expect("dependency should be in package store before compilation");
482 globals.add_external_package(*id, &unit.package, store, alias.as_deref());
483 dropped_names.extend(unit.dropped_names.iter().cloned());
484 }
485
486 // bind all declarations in the package, but don't resolve imports/exports yet
487 let package_id = store.peek_package_id();
488 errors.extend(globals.add_local_package(assigner, package, package_id));
489 let mut resolver = Resolver::new(package_id, globals, dropped_names);
490
491 // resolve all symbols, binding imports/export names as they're resolved
492 resolver.resolve(assigner, package);
493 let (names, globals, locals, mut resolver_errors) = resolver.into_result();
494
495 errors.append(&mut resolver_errors);
496
497 ResolveResult {
498 names,
499 locals,
500 globals,
501 errors,
502 }
503}
504
505fn typeck_all(
506 store: &PackageStore,
507 dependencies: &Dependencies,
508 package: &ast::Package,
509 names: &Names,
510) -> (typeck::Table, Vec<typeck::Error>) {
511 let mut globals = typeck::GlobalTable::new();
512 if let Some(unit) = store.get(PackageId::CORE) {
513 globals.add_external_package(PackageId::CORE, &unit.package, store);
514 }
515
516 for (id, _alias) in dependencies {
517 let unit = store
518 .get(*id)
519 .expect("dependency should be added to package store before compilation");
520 // we can ignore the dependency alias here, because the
521 // typechecker doesn't do any name resolution -- it only operates on item ids.
522 // because of this, the typechecker doesn't actually need to care about visibility
523 // or the names of items at all.
524 globals.add_external_package(*id, &unit.package, store);
525 }
526
527 let mut checker = Checker::new(globals);
528 checker.check_package(names, package);
529 checker.into_table()
530}
531
532fn append_parse_errors(
533 errors: &mut Vec<qsc_parse::Error>,
534 offset: u32,
535 other: Vec<qsc_parse::Error>,
536) {
537 for error in other {
538 errors.push(error.with_offset(offset));
539 }
540}
541
542fn assert_no_errors(sources: &SourceMap, errors: &mut Vec<Error>) {
543 if !errors.is_empty() {
544 for error in errors.drain(..) {
545 eprintln!("{:?}", Report::new(WithSource::from_map(sources, error)));
546 }
547
548 panic!("could not compile package");
549 }
550}
551