1use crate::alloc;
2use crate::alloc::prelude::*;
3use crate::ast::{Span, Spanned};
4use crate::compile::v1;
5use crate::compile::{
6 self, Assembly, CompileVisitor, Context, ErrorKind, Location, Options, Pool, Prelude,
7 SourceLoader, UnitBuilder,
8};
9use crate::hir;
10use crate::indexing::FunctionAst;
11use crate::macros::Storage;
12use crate::parse::Resolve;
13use crate::query::{Build, BuildEntry, Query, SecondaryBuild, Used};
14use crate::runtime::unit::UnitEncoder;
15use crate::shared::{Consts, Gen};
16use crate::worker::{LoadFileKind, Task, Worker};
17use crate::{Diagnostics, Sources};
18
19pub(crate) fn compile(
21 unit: &mut UnitBuilder,
22 prelude: &Prelude,
23 sources: &mut Sources,
24 pool: &mut Pool,
25 context: &Context,
26 visitor: &mut dyn CompileVisitor,
27 diagnostics: &mut Diagnostics,
28 source_loader: &mut dyn SourceLoader,
29 options: &Options,
30 unit_storage: &mut dyn UnitEncoder,
31) -> alloc::Result<()> {
32 let gen = Gen::new();
34 let const_arena = hir::Arena::new();
35 let mut consts = Consts::default();
36 let mut storage = Storage::default();
37 let mut inner = Default::default();
38
39 let q = Query::new(
40 unit,
41 prelude,
42 &const_arena,
43 &mut consts,
44 &mut storage,
45 sources,
46 pool,
47 visitor,
48 diagnostics,
49 source_loader,
50 options,
51 &gen,
52 context,
53 &mut inner,
54 );
55
56 let mut worker = Worker::new(q);
58
59 for source_id in worker.q.sources.source_ids() {
61 let (root_item_id, mod_item) = match worker.q.insert_root_mod(source_id, Span::empty()) {
62 Ok(result) => result,
63 Err(error) => {
64 worker.q.diagnostics.error(source_id, error)?;
65 continue;
66 }
67 };
68
69 let result = worker.queue.try_push_back(Task::LoadFile {
70 kind: LoadFileKind::Root,
71 source_id,
72 mod_item,
73 mod_item_id: root_item_id,
74 });
75
76 if let Err(error) = result {
77 worker
78 .q
79 .diagnostics
80 .error(source_id, compile::Error::from(error))?;
81 }
82 }
83
84 worker.index()?;
85
86 if worker.q.diagnostics.has_error() {
87 return Ok(());
88 }
89
90 loop {
91 while let Some(entry) = worker.q.next_build_entry() {
92 tracing::trace!(item = ?worker.q.pool.item(entry.item_meta.item), "next build entry");
93 let source_id = entry.item_meta.location.source_id;
94
95 let task = CompileBuildEntry {
96 options,
97 q: worker.q.borrow(),
98 };
99
100 if let Err(error) = task.compile(entry, unit_storage) {
101 worker.q.diagnostics.error(source_id, error)?;
102 }
103 }
104
105 let mut errors = Vec::new();
106
107 if worker.q.queue_unused_entries(&mut errors)? {
108 break;
109 }
110
111 for (source_id, error) in errors {
112 worker.q.diagnostics.error(source_id, error)?;
113 }
114 }
115
116 Ok(())
117}
118
119struct CompileBuildEntry<'a, 'arena> {
120 options: &'a Options,
121 q: Query<'a, 'arena>,
122}
123
124impl<'arena> CompileBuildEntry<'_, 'arena> {
125 fn compiler1<'a, 'hir>(
126 &'a mut self,
127 location: Location,
128 span: &'hir dyn Spanned,
129 asm: &'a mut Assembly,
130 scopes: &'a mut v1::Scopes<'hir>,
131 ) -> alloc::Result<v1::Ctxt<'a, 'hir, 'arena>> {
132 Ok(v1::Ctxt {
133 source_id: location.source_id,
134 q: self.q.borrow(),
135 asm,
136 scopes,
137 contexts: try_vec![span],
138 breaks: self::v1::Breaks::new(),
139 options: self.options,
140 select_branches: Vec::new(),
141 drop: Vec::new(),
142 })
143 }
144
145 #[tracing::instrument(skip_all)]
146 fn compile(
147 mut self,
148 entry: BuildEntry,
149 unit_storage: &mut dyn UnitEncoder,
150 ) -> compile::Result<()> {
151 use self::v1::assemble;
152
153 let BuildEntry { item_meta, build } = entry;
154
155 let location = item_meta.location;
156
157 match build {
158 Build::Query => {
159 tracing::trace!("query: {}", self.q.pool.item(item_meta.item));
160
161 let used = if self.q.is_used(&item_meta) {
162 Used::Used
163 } else {
164 Used::Unused
165 };
166
167 if self
168 .q
169 .query_meta(&item_meta.location, item_meta.item, used)?
170 .is_none()
171 {
172 return Err(compile::Error::new(
173 item_meta.location.span,
174 ErrorKind::MissingItem {
175 item: self.q.pool.item(item_meta.item).try_to_owned()?,
176 },
177 ));
178 }
179 }
180 Build::Function(f) => {
181 let mut asm = self.q.unit.new_assembly(location);
182
183 tracing::trace!("function: {}", self.q.pool.item(item_meta.item));
184
185 let type_hash = if let Some(item) = f.impl_item.filter(|_| f.is_instance) {
189 Some(self.q.pool.item_type_hash(item))
190 } else {
191 None
192 };
193
194 let debug_args = format_ast_args(self.q.sources, location, false, &f.args)?;
195 let span: &dyn Spanned = &f.ast;
196
197 let arena = hir::Arena::new();
198 let mut secondary_builds = Vec::new();
199
200 let mut cx = hir::Ctxt::with_query(
201 &arena,
202 self.q.borrow(),
203 item_meta.location.source_id,
204 &mut secondary_builds,
205 )?;
206
207 let hir = match &f.ast {
208 FunctionAst::Bare(node) => {
209 #[cfg(feature = "std")]
210 if cx.q.options.print_tree {
211 node.print_with_sources(
212 format_args!("Bare function {}", cx.q.pool.item(item_meta.item)),
213 cx.q.sources,
214 )?;
215 }
216
217 node.parse(|p| hir::lowering2::bare(&mut cx, p))?
218 }
219 FunctionAst::Node(node, _) => {
220 #[cfg(feature = "std")]
221 if cx.q.options.print_tree {
222 node.print_with_sources(
223 format_args!("Node function {}", cx.q.pool.item(item_meta.item)),
224 cx.q.sources,
225 )?;
226 }
227
228 node.parse(|p| hir::lowering2::item_fn(&mut cx, p, f.impl_item.is_some()))?
229 }
230 FunctionAst::Item(ast, _) => hir::lowering::item_fn(&mut cx, ast)?,
231 FunctionAst::Empty(ast, span) => hir::lowering::empty_fn(&mut cx, ast, &span)?,
232 };
233
234 let count = hir.args.len();
235
236 let mut scopes = self::v1::Scopes::new(location.source_id)?;
237 let mut c = self.compiler1(location, span, &mut asm, &mut scopes)?;
238 assemble::fn_from_item_fn(&mut c, &hir, f.is_instance)?;
239 let size = c.scopes.size();
240
241 if !self.q.is_used(&item_meta) {
242 self.q
243 .diagnostics
244 .not_used(location.source_id, span, None)?;
245 } else {
246 let instance = match (type_hash, &f.ast) {
247 (Some(type_hash), FunctionAst::Item(_, name)) => {
248 let name = name.resolve(resolve_context!(self.q))?;
249 Some((type_hash, name))
250 }
251 (Some(type_hash), FunctionAst::Node(_, Some(name))) => {
252 let name = name.resolve(resolve_context!(self.q))?;
253 Some((type_hash, name))
254 }
255 _ => None,
256 };
257
258 let item = self.q.pool.item(item_meta.item);
259
260 self.q.unit.new_function(
261 location,
262 item,
263 instance,
264 count,
265 None,
266 asm,
267 f.call,
268 debug_args,
269 unit_storage,
270 size,
271 )?;
272 }
273
274 for build in secondary_builds {
275 let item_meta = build.item_meta;
276
277 let mut asm = self.q.unit.new_assembly(item_meta.location);
278
279 match build.build {
280 SecondaryBuild::Closure(c) => {
281 tracing::trace!("closure: {}", self.q.pool.item(item_meta.item));
282
283 let debug_args =
284 format_hir_args(self.q.sources, location, true, c.hir.args.iter())?;
285
286 let mut scopes = self::v1::Scopes::new(location.source_id)?;
287 let mut cx = self.compiler1(location, c.hir, &mut asm, &mut scopes)?;
288 assemble::expr_closure_secondary(&mut cx, c.hir)?;
289 let size = cx.scopes.size();
290
291 if !self.q.is_used(&item_meta) {
292 self.q.diagnostics.not_used(
293 location.source_id,
294 &location.span,
295 None,
296 )?;
297 } else {
298 let captures =
299 (!c.hir.captures.is_empty()).then_some(c.hir.captures.len());
300
301 let args = c
302 .hir
303 .args
304 .len()
305 .saturating_add(usize::from(captures.is_some()));
306
307 self.q.unit.new_function(
308 location,
309 self.q.pool.item(item_meta.item),
310 None,
311 args,
312 captures,
313 asm,
314 c.call,
315 debug_args,
316 unit_storage,
317 size,
318 )?;
319 }
320 }
321 SecondaryBuild::AsyncBlock(b) => {
322 tracing::trace!("async block: {}", self.q.pool.item(item_meta.item));
323
324 let mut scopes = self::v1::Scopes::new(location.source_id)?;
325 let mut cx = self.compiler1(location, b.hir, &mut asm, &mut scopes)?;
326 assemble::async_block_secondary(&mut cx, b.hir)?;
327 let size = cx.scopes.size();
328
329 if !self.q.is_used(&item_meta) {
330 self.q.diagnostics.not_used(
331 location.source_id,
332 &location.span,
333 None,
334 )?;
335 } else {
336 let args = b.hir.captures.len();
337
338 self.q.unit.new_function(
339 location,
340 self.q.pool.item(item_meta.item),
341 None,
342 args,
343 None,
344 asm,
345 b.call,
346 Default::default(),
347 unit_storage,
348 size,
349 )?;
350 }
351 }
352 }
353 }
354 }
355 Build::Unused => {
356 tracing::trace!("unused: {}", self.q.pool.item(item_meta.item));
357
358 if !item_meta.visibility.is_public() {
359 self.q
360 .diagnostics
361 .not_used(location.source_id, &location.span, None)?;
362 }
363 }
364 Build::Import(import) => {
365 tracing::trace!("import: {}", self.q.pool.item(item_meta.item));
366
367 let used = if self.q.is_used(&item_meta) {
368 Used::Used
369 } else {
370 Used::Unused
371 };
372
373 let result =
375 self.q
376 .import(&location, item_meta.module, item_meta.item, used, used)?;
377
378 if !self.q.is_used(&item_meta) {
379 self.q
380 .diagnostics
381 .not_used(location.source_id, &location.span, None)?;
382 }
383
384 let missing = match result {
385 Some(item_id) => {
386 let item = self.q.pool.item(item_id);
387
388 if self.q.context.contains_prefix(item)? || self.q.contains_prefix(item)? {
389 None
390 } else {
391 Some(item_id)
392 }
393 }
394 None => Some(import.entry.target),
395 };
396
397 if let Some(item) = missing {
398 return Err(compile::Error::new(
399 location,
400 ErrorKind::MissingItem {
401 item: self.q.pool.item(item).try_to_owned()?,
402 },
403 ));
404 }
405 }
406 Build::ReExport => {
407 tracing::trace!("re-export: {}", self.q.pool.item(item_meta.item));
408
409 let used = if self.q.is_used(&item_meta) {
410 Used::Used
411 } else {
412 Used::Unused
413 };
414
415 let Some(import) =
416 self.q
417 .import(&location, item_meta.module, item_meta.item, used, used)?
418 else {
419 return Err(compile::Error::new(
420 location.span,
421 ErrorKind::MissingItem {
422 item: self.q.pool.item(item_meta.item).try_to_owned()?,
423 },
424 ));
425 };
426
427 self.q.unit.new_function_reexport(
428 location,
429 self.q.pool.item(item_meta.item),
430 self.q.pool.item(import),
431 )?;
432 }
433 }
434
435 Ok(())
436 }
437}
438
439fn format_hir_args<'hir, I>(
440 sources: &Sources,
441 location: Location,
442 environment: bool,
443 arguments: I,
444) -> compile::Result<Box<[Box<str>]>>
445where
446 I: IntoIterator<Item = &'hir hir::FnArg<'hir>>,
447{
448 let mut args = Vec::new();
449
450 for arg in arguments {
451 match arg {
452 hir::FnArg::SelfValue(..) => {
453 args.try_push(Box::try_from("self")?)?;
454 }
455 hir::FnArg::Pat(pat) => {
456 let span = pat.span();
457
458 if let Some(s) = sources.source(location.source_id, span) {
459 args.try_push(Box::try_from(s)?)?;
460 } else {
461 args.try_push(Box::try_from("*")?)?;
462 }
463 }
464 }
465 }
466
467 if environment {
468 args.try_push(Box::try_from("environment")?)?;
469 }
470
471 Ok(args.try_into_boxed_slice()?)
472}
473
474fn format_ast_args<'a, I>(
475 sources: &Sources,
476 location: Location,
477 environment: bool,
478 arguments: I,
479) -> compile::Result<Box<[Box<str>]>>
480where
481 I: IntoIterator<Item = &'a Span>,
482{
483 let mut args = Vec::new();
484
485 for &span in arguments {
486 if let Some(s) = sources.source(location.source_id, span) {
487 args.try_push(Box::try_from(s)?)?;
488 } else {
489 args.try_push(Box::try_from("*")?)?;
490 }
491 }
492
493 if environment {
494 args.try_push(Box::try_from("environment")?)?;
495 }
496
497 Ok(args.try_into_boxed_slice()?)
498}