microsoft/qdk

Public

mirrored from https://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_frontend/src/compile.rs

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