microsoft/qdk

Public

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

CodeCommitsIssuesPull requestsActionsInsightsSecurity
fedimser/permutation

Branches

Tags

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

Clone

HTTPS

Download ZIP

source/compiler/qsc_frontend/src/compile.rs

545lines · 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
179impl<'a> IntoIterator for &'a PackageStore {
180 type IntoIter = Iter<'a>;
181 type Item = (qsc_hir::hir::PackageId, &'a CompileUnit);
182 fn into_iter(self) -> Self::IntoIter {
183 self.iter()
184 }
185}
186
187/// A package store that contains one mutable `CompileUnit`.
188pub struct OpenPackageStore {
189 store: PackageStore,
190 open: PackageId,
191}
192
193impl OpenPackageStore {
194 /// Returns a reference to the underlying, immutable,
195 /// package store.
196 #[must_use]
197 pub fn package_store(&self) -> &PackageStore {
198 &self.store
199 }
200
201 /// Returns the ID of the open package.
202 #[must_use]
203 pub fn open_package_id(&self) -> PackageId {
204 self.open
205 }
206
207 /// Returns a mutable reference to the open package,
208 /// along with a reference to the core library that can be used
209 /// to perform passes.
210 #[must_use]
211 pub fn get_open_mut(&mut self) -> (&global::Table, &mut CompileUnit) {
212 let id = self.open;
213
214 (
215 &self.store.core,
216 self.store
217 .units
218 .get_mut(id)
219 .expect("open package id should exist in store"),
220 )
221 }
222
223 /// Consumes the `OpenPackageStore` and returns a `PackageStore`
224 /// along with the id of the formerly open package.
225 #[must_use]
226 pub fn into_package_store(self) -> (PackageStore, PackageId) {
227 (self.store, self.open)
228 }
229}
230
231pub struct Iter<'a>(index_map::Iter<'a, PackageId, CompileUnit>);
232
233impl<'a> Iterator for Iter<'a> {
234 type Item = (PackageId, &'a CompileUnit);
235
236 fn next(&mut self) -> Option<Self::Item> {
237 self.0.next()
238 }
239}
240
241impl DoubleEndedIterator for Iter<'_> {
242 fn next_back(&mut self) -> Option<Self::Item> {
243 self.0.next_back()
244 }
245}
246
247pub(super) struct Offsetter(pub(super) u32);
248
249impl MutVisitor for Offsetter {
250 fn visit_span(&mut self, span: &mut Span) {
251 span.lo += self.0;
252 span.hi += self.0;
253 }
254}
255
256#[must_use]
257pub fn compile(
258 store: &PackageStore,
259 dependencies: &Dependencies,
260 sources: SourceMap,
261 capabilities: TargetCapabilityFlags,
262 language_features: LanguageFeatures,
263) -> CompileUnit {
264 let (ast_package, parse_errors) = parse_all(&sources, language_features);
265
266 compile_ast(
267 store,
268 dependencies,
269 ast_package,
270 sources,
271 capabilities,
272 parse_errors,
273 )
274}
275
276#[allow(clippy::module_name_repetitions)]
277pub fn compile_ast(
278 store: &PackageStore,
279 dependencies: &Dependencies,
280 mut ast_package: ast::Package,
281 sources: SourceMap,
282 capabilities: TargetCapabilityFlags,
283 parse_errors: Vec<qsc_parse::Error>,
284) -> CompileUnit {
285 let mut cond_compile = preprocess::Conditional::new(capabilities);
286 cond_compile.visit_package(&mut ast_package);
287 let dropped_names = cond_compile.into_names();
288
289 let mut remove_spans = preprocess::RemoveCircuitSpans::new(&sources);
290 remove_spans.visit_package(&mut ast_package);
291
292 let mut ast_assigner = AstAssigner::new();
293 ast_assigner.visit_package(&mut ast_package);
294 AstValidator::default().visit_package(&ast_package);
295 let mut hir_assigner = HirAssigner::new();
296 let ResolveResult {
297 names,
298 locals,
299 globals,
300 errors: name_errors,
301 } = resolve_all(
302 store,
303 dependencies,
304 &mut hir_assigner,
305 &ast_package,
306 dropped_names.clone(),
307 );
308 let (tys, ty_errors) = typeck_all(store, dependencies, &ast_package, &names);
309 let package_id = store.peek_package_id();
310 let mut lowerer = Lowerer::new(package_id);
311 let package = lowerer
312 .with(&mut hir_assigner, &names, &tys)
313 .lower_package(&ast_package);
314 HirValidator::default().visit_package(&package);
315 let lower_errors = lowerer.drain_errors();
316
317 let errors = parse_errors
318 .into_iter()
319 .map(Into::into)
320 .chain(name_errors.into_iter().map(Into::into))
321 .chain(ty_errors.into_iter().map(Into::into))
322 .chain(lower_errors.into_iter().map(Into::into))
323 .map(Error)
324 .collect();
325
326 CompileUnit {
327 package,
328 ast: AstPackage {
329 package: ast_package,
330 tys,
331 names,
332 locals,
333 globals,
334 },
335 assigner: hir_assigner,
336 sources,
337 errors,
338 dropped_names,
339 }
340}
341
342/// Compiles the core library.
343///
344/// # Panics
345///
346/// Panics if the core library does not compile without errors.
347#[must_use]
348pub fn core() -> CompileUnit {
349 let store = PackageStore {
350 core: global::Table::default(),
351 units: IndexMap::new(),
352 next_id: PackageId::CORE,
353 };
354
355 let core: Vec<(SourceName, SourceContents)> = library::CORE_LIB
356 .iter()
357 .map(|(name, contents)| ((*name).into(), (*contents).into()))
358 .collect();
359 let sources = SourceMap::new(core, None);
360
361 let mut unit = compile(
362 &store,
363 &[],
364 sources,
365 TargetCapabilityFlags::empty(),
366 LanguageFeatures::default(),
367 );
368 assert_no_errors(&unit.sources, &mut unit.errors);
369 unit
370}
371
372/// Compiles the standard library.
373///
374/// # Panics
375///
376/// Panics if the standard library does not compile without errors.
377#[must_use]
378pub fn std(store: &PackageStore, capabilities: TargetCapabilityFlags) -> CompileUnit {
379 let std: Vec<(SourceName, SourceContents)> = library::STD_LIB
380 .iter()
381 .map(|(name, contents)| ((*name).into(), (*contents).into()))
382 .collect();
383 let sources = SourceMap::new(std, None);
384
385 let mut unit = compile(
386 store,
387 &[(PackageId::CORE, None)],
388 sources,
389 capabilities,
390 LanguageFeatures::default(),
391 );
392 assert_no_errors(&unit.sources, &mut unit.errors);
393 unit
394}
395
396#[must_use]
397pub fn parse_all(
398 sources: &SourceMap,
399 features: LanguageFeatures,
400) -> (ast::Package, Vec<qsc_parse::Error>) {
401 let mut namespaces = Vec::new();
402 let mut errors = Vec::new();
403 for source in sources.relative_sources() {
404 let (source_namespaces, source_errors) =
405 qsc_parse::namespaces(&source.contents, Some(&source.name), features);
406 for mut namespace in source_namespaces {
407 Offsetter(source.offset).visit_namespace(&mut namespace);
408 namespaces.push(TopLevelNode::Namespace(namespace));
409 }
410
411 append_parse_errors(&mut errors, source.offset, source_errors);
412 }
413
414 let entry = sources
415 .entry()
416 .as_ref()
417 .filter(|source| !source.contents.is_empty())
418 .map(|source| {
419 let (mut entry, entry_errors) = qsc_parse::expr(&source.contents, features);
420 Offsetter(source.offset).visit_expr(&mut entry);
421 append_parse_errors(&mut errors, source.offset, entry_errors);
422 entry
423 });
424
425 let package = ast::Package {
426 id: ast::NodeId::default(),
427 nodes: namespaces.into_boxed_slice(),
428 entry,
429 };
430
431 (package, errors)
432}
433
434#[must_use]
435pub fn get_target_profile_from_entry_point(
436 sources: &[(Arc<str>, Arc<str>)],
437) -> Option<(Profile, Span)> {
438 let (ast_package, parse_errors) = parse_all(
439 &SourceMap::new(sources.iter().cloned(), None),
440 LanguageFeatures::default(),
441 );
442
443 if !parse_errors.is_empty() {
444 return None;
445 }
446
447 let mut check = preprocess::DetectEntryPointProfile::new();
448 check.visit_package(&ast_package);
449 check.profile
450}
451
452pub(crate) struct ResolveResult {
453 pub names: Names,
454 pub locals: Locals,
455 pub globals: GlobalScope,
456 pub errors: Vec<resolve::Error>,
457}
458
459fn resolve_all(
460 store: &PackageStore,
461 dependencies: &Dependencies,
462 assigner: &mut HirAssigner,
463 package: &ast::Package,
464 mut dropped_names: Vec<TrackedName>,
465) -> ResolveResult {
466 let mut globals = resolve::GlobalTable::new();
467 let mut errors = Vec::new();
468 if let Some(unit) = store.get(PackageId::CORE) {
469 globals.add_external_package(PackageId::CORE, &unit.package, store, None);
470 dropped_names.extend(unit.dropped_names.iter().cloned());
471 }
472
473 for (id, alias) in dependencies {
474 let unit = store
475 .get(*id)
476 .expect("dependency should be in package store before compilation");
477 globals.add_external_package(*id, &unit.package, store, alias.as_deref());
478 dropped_names.extend(unit.dropped_names.iter().cloned());
479 }
480
481 // bind all declarations in the package, but don't resolve imports/exports yet
482 let package_id = store.peek_package_id();
483 errors.extend(globals.add_local_package(assigner, package, package_id));
484 let mut resolver = Resolver::new(package_id, globals, dropped_names);
485
486 // resolve all symbols, binding imports/export names as they're resolved
487 resolver.resolve(assigner, package);
488 let (names, globals, locals, mut resolver_errors) = resolver.into_result();
489
490 errors.append(&mut resolver_errors);
491
492 ResolveResult {
493 names,
494 locals,
495 globals,
496 errors,
497 }
498}
499
500fn typeck_all(
501 store: &PackageStore,
502 dependencies: &Dependencies,
503 package: &ast::Package,
504 names: &Names,
505) -> (typeck::Table, Vec<typeck::Error>) {
506 let mut globals = typeck::GlobalTable::new();
507 if let Some(unit) = store.get(PackageId::CORE) {
508 globals.add_external_package(PackageId::CORE, &unit.package, store);
509 }
510
511 for (id, _alias) in dependencies {
512 let unit = store
513 .get(*id)
514 .expect("dependency should be added to package store before compilation");
515 // we can ignore the dependency alias here, because the
516 // typechecker doesn't do any name resolution -- it only operates on item ids.
517 // because of this, the typechecker doesn't actually need to care about visibility
518 // or the names of items at all.
519 globals.add_external_package(*id, &unit.package, store);
520 }
521
522 let mut checker = Checker::new(globals);
523 checker.check_package(names, package);
524 checker.into_table()
525}
526
527fn append_parse_errors(
528 errors: &mut Vec<qsc_parse::Error>,
529 offset: u32,
530 other: Vec<qsc_parse::Error>,
531) {
532 for error in other {
533 errors.push(error.with_offset(offset));
534 }
535}
536
537fn assert_no_errors(sources: &SourceMap, errors: &mut Vec<Error>) {
538 if !errors.is_empty() {
539 for error in errors.drain(..) {
540 eprintln!("{:?}", Report::new(WithSource::from_map(sources, error)));
541 }
542
543 panic!("could not compile package");
544 }
545}
546