1use core::fmt;
2use core::mem::take;
3use core::slice;
4
5use tracing::instrument_ast;
6
7use crate::alloc::prelude::*;
8use crate::alloc::BTreeMap;
9use crate::ast::{self, Spanned};
10use crate::compile::ir;
11use crate::compile::{self, Assembly, ErrorKind, ItemId, ModId, Options, WithSpan};
12use crate::hir;
13use crate::query::{ConstFn, Query, Used};
14use crate::runtime::{
15 ConstValue, ConstValueKind, Inline, Inst, InstAddress, InstArithmeticOp, InstBitwiseOp, InstOp,
16 InstRange, InstShiftOp, InstTarget, InstValue, InstVariant, Label, Output, PanicReason,
17 Protocol, TypeCheck,
18};
19use crate::shared::FixedVec;
20use crate::{Hash, SourceId};
21
22use super::{Address, Any, Break, Breaks, Linear, Needs, ScopeHandle, Scopes};
23
24macro_rules! converge {
25 ($expr:expr $(, $method:ident($($diverge:expr),* $(,)?))?) => {
26 match $expr {
27 Asm {
28 outcome: Outcome::Converge(data),
29 ..
30 } => data,
31 Asm {
32 span,
33 outcome: Outcome::Diverge,
34 } => {
35 $($($diverge.$method()?;)*)*
36
37 return Ok(Asm {
38 span,
39 outcome: Outcome::Diverge,
40 })
41 }
42 }
43 };
44}
45
46enum Pattern {
47 Irrefutable,
48 Refutable,
49}
50
51pub(crate) struct Ctxt<'a, 'hir, 'arena> {
53 pub(crate) source_id: SourceId,
55 pub(crate) q: Query<'a, 'arena>,
57 pub(crate) asm: &'a mut Assembly,
59 pub(crate) scopes: &'a Scopes<'hir>,
61 pub(crate) contexts: Vec<&'hir dyn Spanned>,
63 pub(crate) breaks: Breaks<'hir>,
65 pub(crate) options: &'a Options,
67 pub(crate) select_branches: Vec<(Label, &'hir hir::ExprSelectBranch<'hir>)>,
69 pub(crate) drop: Vec<InstAddress>,
71}
72
73impl<'hir> Ctxt<'_, 'hir, '_> {
74 fn drop_dangling(&mut self, span: &dyn Spanned) -> compile::Result<()> {
75 self.scopes
76 .drain_dangling_into(&mut self.drop)
77 .with_span(span)?;
78
79 let mut drop_set = self.q.unit.drop_set();
80
81 for addr in self.drop.drain(..).rev() {
82 drop_set.push(addr)?;
83 }
84
85 if let Some(set) = drop_set.finish()? {
86 self.asm.push(Inst::Drop { set }, span)?;
87 }
88
89 Ok(())
90 }
91
92 pub(crate) fn context(&self) -> Option<&'hir dyn Spanned> {
94 self.contexts.last().copied()
95 }
96
97 pub(crate) fn call_const_fn(
99 &mut self,
100 span: &dyn Spanned,
101 from_module: ModId,
102 from_item: ItemId,
103 query_const_fn: &ConstFn,
104 args: &[hir::Expr<'_>],
105 ) -> compile::Result<ConstValue> {
106 if query_const_fn.ir_fn.args.len() != args.len() {
107 return Err(compile::Error::new(
108 span,
109 ErrorKind::BadArgumentCount {
110 expected: query_const_fn.ir_fn.args.len(),
111 actual: args.len(),
112 },
113 ));
114 }
115
116 let mut compiler = ir::Ctxt {
117 source_id: self.source_id,
118 q: self.q.borrow(),
119 };
120
121 let mut compiled = Vec::new();
122
123 for (hir, name) in args.iter().zip(&query_const_fn.ir_fn.args) {
125 compiled.try_push((ir::compiler::expr(hir, &mut compiler)?, name))?;
126 }
127
128 let mut interpreter = ir::Interpreter {
129 budget: ir::Budget::new(1_000_000),
130 scopes: ir::Scopes::new()?,
131 module: from_module,
132 item: from_item,
133 q: self.q.borrow(),
134 };
135
136 for (ir, name) in compiled {
137 let value = interpreter.eval_value(&ir, Used::Used)?;
138 interpreter.scopes.decl(*name, value).with_span(span)?;
139 }
140
141 interpreter.module = query_const_fn.item_meta.module;
142 interpreter.item = query_const_fn.item_meta.item;
143 let value = interpreter.eval_value(&query_const_fn.ir_fn.ir, Used::Used)?;
144 Ok(crate::from_value(value).with_span(span)?)
145 }
146}
147
148enum Outcome<T> {
149 Converge(T),
150 Diverge,
151}
152
153#[must_use = "Assembly should be checked for convergence to reduce code generation"]
154struct Asm<'hir, T = ()> {
155 span: &'hir dyn Spanned,
156 outcome: Outcome<T>,
157}
158
159impl<'hir, T> Asm<'hir, T> {
160 #[inline]
161 fn new(span: &'hir dyn Spanned, data: T) -> Self {
162 Self {
163 span,
164 outcome: Outcome::Converge(data),
165 }
166 }
167
168 #[inline]
169 fn diverge(span: &'hir dyn Spanned) -> Self {
170 Self {
171 span,
172 outcome: Outcome::Diverge,
173 }
174 }
175
176 #[inline]
178 fn ignore(self) {}
179}
180
181impl<T> Asm<'_, T> {
182 #[inline]
184 fn into_converging(self) -> Option<T> {
185 match self.outcome {
186 Outcome::Converge(data) => Some(data),
187 Outcome::Diverge => None,
188 }
189 }
190
191 #[inline]
193 fn diverging(self) -> bool {
194 matches!(self.outcome, Outcome::Diverge)
195 }
196
197 #[inline]
199 fn converging(self) -> bool {
200 matches!(self.outcome, Outcome::Converge(..))
201 }
202}
203
204impl fmt::Debug for Asm<'_> {
205 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
206 f.debug_struct("Asm")
207 .field("span", &self.span.span())
208 .finish()
209 }
210}
211
212#[instrument_ast(span = hir)]
214pub(crate) fn fn_from_item_fn<'hir>(
215 cx: &mut Ctxt<'_, 'hir, '_>,
216 hir: &'hir hir::ItemFn<'hir>,
217 instance_fn: bool,
218) -> compile::Result<()> {
219 let mut first = true;
220
221 let mut arguments = cx.scopes.linear(hir, hir.args.len())?;
222
223 for (arg, needs) in hir.args.iter().zip(&mut arguments) {
224 match arg {
225 hir::FnArg::SelfValue(span, name) => {
226 if !instance_fn || !first {
227 return Err(compile::Error::new(span, ErrorKind::UnsupportedSelf));
228 }
229
230 cx.scopes.define(span, *name, needs)?;
231 }
232 hir::FnArg::Pat(pat) => {
233 let asm = pattern_panic(cx, pat, move |cx, false_label| {
234 fn_arg_pat(cx, pat, needs, false_label)
235 })?;
236
237 asm.ignore();
238 }
239 }
240
241 first = false;
242 }
243
244 if hir.body.value.is_some() {
245 return_(cx, hir, &hir.body, block_without_scope)?.ignore();
246 } else {
247 let mut needs = Any::ignore(&hir.body);
248
249 if block_without_scope(cx, &hir.body, &mut needs)?.converging() {
250 cx.asm.push(Inst::ReturnUnit, hir)?;
251 }
252 }
253
254 arguments.free()?;
255 cx.scopes.pop_last(hir)?;
256 Ok(())
257}
258
259#[instrument_ast(span = hir.block.span)]
261pub(crate) fn async_block_secondary<'hir>(
262 cx: &mut Ctxt<'_, 'hir, '_>,
263 hir: &'hir hir::AsyncBlock<'hir>,
264) -> compile::Result<()> {
265 let linear = cx.scopes.linear(&hir.block, hir.captures.len())?;
266
267 for (name, needs) in hir.captures.iter().copied().zip(&linear) {
268 cx.scopes.define(&hir.block, name, needs)?;
269 }
270
271 return_(cx, &hir.block, hir.block, block_without_scope)?.ignore();
272
273 linear.free()?;
274 cx.scopes.pop_last(&hir.block)?;
275 Ok(())
276}
277
278#[instrument_ast(span = hir)]
280pub(crate) fn expr_closure_secondary<'hir>(
281 cx: &mut Ctxt<'_, 'hir, '_>,
282 hir: &'hir hir::ExprClosure<'hir>,
283) -> compile::Result<()> {
284 let mut arguments = cx.scopes.linear(hir, hir.args.len())?;
285 let environment = cx.scopes.linear(hir, hir.captures.len())?;
286
287 if !hir.captures.is_empty() {
288 cx.asm.push(
289 Inst::Environment {
290 addr: environment.addr(),
291 count: hir.captures.len(),
292 out: environment.addr().output(),
293 },
294 hir,
295 )?;
296
297 for (capture, needs) in hir.captures.iter().copied().zip(&environment) {
298 cx.scopes.define(hir, capture, needs)?;
299 }
300 }
301
302 for (arg, needs) in hir.args.iter().zip(&mut arguments) {
303 match arg {
304 hir::FnArg::SelfValue(span, _) => {
305 return Err(compile::Error::new(span, ErrorKind::UnsupportedSelf))
306 }
307 hir::FnArg::Pat(pat) => {
308 let asm = pattern_panic(cx, pat, move |cx, false_label| {
309 fn_arg_pat(cx, pat, needs, false_label)
310 })?;
311
312 asm.ignore();
313 }
314 }
315 }
316
317 return_(cx, hir, hir.body, expr)?.ignore();
318
319 environment.free()?;
320 arguments.free()?;
321 cx.scopes.pop_last(hir)?;
322 Ok(())
323}
324
325#[instrument_ast(span = pat)]
326fn fn_arg_pat<'a, 'hir>(
327 cx: &mut Ctxt<'a, 'hir, '_>,
328 pat: &'hir hir::PatBinding<'hir>,
329 needs: &mut dyn Needs<'a, 'hir>,
330 false_label: &Label,
331) -> compile::Result<Asm<'hir, Pattern>> {
332 let Some(addr) = needs.try_as_addr()? else {
333 return Err(compile::Error::msg(
334 needs.span(),
335 "Expected need to be populated outside of pattern",
336 ));
337 };
338
339 let addr = addr.addr();
340
341 let mut load = |cx: &mut Ctxt<'a, 'hir, '_>, needs: &mut dyn Needs<'a, 'hir>| {
342 needs.assign_addr(cx, addr)?;
343 Ok(Asm::new(pat, ()))
344 };
345
346 let out = match pat.names {
347 [name] => pat_binding_with_single(cx, pat, &pat.pat, *name, false_label, &mut load, needs)?,
348 _ => pat_binding(cx, pat, false_label, &mut load)?,
349 };
350
351 Ok(out)
352}
353
354fn return_<'a, 'hir, T>(
356 cx: &mut Ctxt<'a, 'hir, '_>,
357 span: &'hir dyn Spanned,
358 hir: T,
359 asm: impl FnOnce(&mut Ctxt<'a, 'hir, '_>, T, &mut dyn Needs<'a, 'hir>) -> compile::Result<Asm<'hir>>,
360) -> compile::Result<Asm<'hir>> {
361 let mut needs = cx.scopes.defer(span).with_name("return value");
362 converge!(asm(cx, hir, &mut needs)?, free(needs));
363
364 cx.asm.push(
365 Inst::Return {
366 addr: needs.addr()?.addr(),
367 },
368 span,
369 )?;
370
371 needs.free()?;
372 Ok(Asm::new(span, ()))
373}
374
375fn pattern_panic<'a, 'hir, 'arena, F>(
376 cx: &mut Ctxt<'a, 'hir, 'arena>,
377 span: &'hir dyn Spanned,
378 f: F,
379) -> compile::Result<Asm<'hir>>
380where
381 F: FnOnce(&mut Ctxt<'a, 'hir, 'arena>, &Label) -> compile::Result<Asm<'hir, Pattern>>,
382{
383 let false_label = cx.asm.new_label("pattern_panic");
384
385 if matches!(converge!(f(cx, &false_label)?), Pattern::Refutable) {
386 cx.q.diagnostics
387 .let_pattern_might_panic(cx.source_id, span, cx.context())?;
388
389 let match_label = cx.asm.new_label("patter_match");
390
391 cx.asm.jump(&match_label, span)?;
392 cx.asm.label(&false_label)?;
393 cx.asm.push(
394 Inst::Panic {
395 reason: PanicReason::UnmatchedPattern,
396 },
397 span,
398 )?;
399
400 cx.asm.label(&match_label)?;
401 }
402
403 Ok(Asm::new(span, ()))
404}
405
406#[instrument_ast(span = hir)]
410fn pat_binding<'a, 'hir>(
411 cx: &mut Ctxt<'a, 'hir, '_>,
412 hir: &'hir hir::PatBinding<'hir>,
413 false_label: &Label,
414 load: &mut dyn FnMut(
415 &mut Ctxt<'a, 'hir, '_>,
416 &mut dyn Needs<'a, 'hir>,
417 ) -> compile::Result<Asm<'hir>>,
418) -> compile::Result<Asm<'hir, Pattern>> {
419 let mut linear = cx.scopes.linear(hir, hir.names.len())?;
420 let pat = pat_binding_with(cx, hir, &hir.pat, hir.names, false_label, load, &mut linear)?;
421 linear.forget()?;
422 Ok(pat)
423}
424
425#[instrument_ast(span = span)]
426fn pat_binding_with<'a, 'hir>(
427 cx: &mut Ctxt<'a, 'hir, '_>,
428 span: &'hir dyn Spanned,
429 pat: &'hir hir::Pat<'hir>,
430 names: &[hir::Variable],
431 false_label: &Label,
432 load: &mut dyn FnMut(
433 &mut Ctxt<'a, 'hir, '_>,
434 &mut dyn Needs<'a, 'hir>,
435 ) -> compile::Result<Asm<'hir>>,
436 linear: &mut [Address<'a, 'hir>],
437) -> compile::Result<Asm<'hir, Pattern>> {
438 let mut bindings = BTreeMap::<_, &mut dyn Needs<'a, 'hir>>::new();
439
440 for (name, needs) in names.iter().copied().zip(linear.iter_mut()) {
441 bindings.try_insert(name, needs).with_span(span)?;
442 }
443
444 let asm = self::pat(cx, pat, false_label, load, &mut bindings)?;
445
446 if let Some(key) = bindings.into_keys().next() {
447 return Err(compile::Error::msg(
448 span,
449 format!("Unbound name in pattern: {key:?}"),
450 ));
451 }
452
453 for (name, needs) in names.iter().copied().zip(linear.iter()) {
454 cx.scopes.define(needs.span(), name, needs)?;
455 }
456
457 Ok(asm)
458}
459
460#[instrument_ast(span = span)]
461fn pat_binding_with_single<'a, 'hir>(
462 cx: &mut Ctxt<'a, 'hir, '_>,
463 span: &'hir dyn Spanned,
464 pat: &'hir hir::Pat<'hir>,
465 name: hir::Variable,
466 false_label: &Label,
467 load: &mut dyn FnMut(
468 &mut Ctxt<'a, 'hir, '_>,
469 &mut dyn Needs<'a, 'hir>,
470 ) -> compile::Result<Asm<'hir>>,
471 needs: &mut dyn Needs<'a, 'hir>,
472) -> compile::Result<Asm<'hir, Pattern>> {
473 let mut bindings = Some::<(_, &mut dyn Needs<'a, 'hir>)>((name, needs));
474
475 let asm = self::pat(cx, pat, false_label, load, &mut bindings)?;
476
477 if let Some((name, _)) = bindings {
478 return Err(compile::Error::msg(
479 span,
480 format!("Unbound name in pattern: {name:?}"),
481 ));
482 }
483
484 let Some(addr) = needs.try_as_addr()? else {
485 return Err(compile::Error::msg(
486 needs.span(),
487 "Expected need to be populated by pattern",
488 ));
489 };
490
491 cx.scopes.define(needs.span(), name, addr)?;
492 Ok(asm)
493}
494
495trait Bindings<K, T> {
496 fn remove(&mut self, name: &K) -> Option<T>;
497}
498
499impl<K, T> Bindings<K, T> for BTreeMap<K, T>
500where
501 K: Ord,
502{
503 #[inline]
504 fn remove(&mut self, name: &K) -> Option<T> {
505 BTreeMap::remove(self, name)
506 }
507}
508
509impl<K, T> Bindings<K, T> for Option<(K, T)>
510where
511 K: PartialEq,
512{
513 #[inline]
514 fn remove(&mut self, name: &K) -> Option<T> {
515 let (current, value) = self.take()?;
516
517 if current != *name {
518 *self = Some((current, value));
519 return None;
520 }
521
522 Some(value)
523 }
524}
525
526#[instrument_ast(span = hir)]
530fn pat<'a, 'hir>(
531 cx: &mut Ctxt<'a, 'hir, '_>,
532 hir: &'hir hir::Pat<'hir>,
533 false_label: &Label,
534 load: &mut dyn FnMut(
535 &mut Ctxt<'a, 'hir, '_>,
536 &mut dyn Needs<'a, 'hir>,
537 ) -> compile::Result<Asm<'hir>>,
538 bindings: &mut dyn Bindings<hir::Variable, &mut dyn Needs<'a, 'hir>>,
539) -> compile::Result<Asm<'hir, Pattern>> {
540 let span = hir;
541
542 match hir.kind {
543 hir::PatKind::Ignore => {
544 converge!(load(cx, &mut Any::ignore(hir))?);
546 Ok(Asm::new(span, Pattern::Irrefutable))
547 }
548 hir::PatKind::Path(kind) => match *kind {
549 hir::PatPathKind::Kind(kind) => {
550 let mut needs = cx.scopes.defer(hir);
551 converge!(load(cx, &mut needs)?, free(needs));
552
553 let cond = cx.scopes.alloc(hir)?;
554 let inst = pat_sequence_kind_to_inst(*kind, needs.addr()?.addr(), cond.output());
555
556 cx.asm.push(inst, hir)?;
557 cx.asm.jump_if_not(cond.addr(), false_label, hir)?;
558
559 cond.free()?;
560 needs.free()?;
561 Ok(Asm::new(span, Pattern::Refutable))
562 }
563 hir::PatPathKind::Ident(name) => {
564 let Some(binding) = bindings.remove(&name) else {
565 return Err(compile::Error::msg(hir, format!("No binding for {name:?}")));
566 };
567
568 converge!(load(cx, binding)?);
569 Ok(Asm::new(span, Pattern::Irrefutable))
570 }
571 },
572 hir::PatKind::Lit(hir) => Ok(pat_lit(cx, hir, false_label, load)?),
573 hir::PatKind::Sequence(hir) => pat_sequence(cx, hir, span, false_label, load, bindings),
574 hir::PatKind::Object(hir) => pat_object(cx, hir, span, false_label, load, bindings),
575 }
576}
577
578#[instrument_ast(span = hir)]
580fn pat_lit<'a, 'hir>(
581 cx: &mut Ctxt<'a, 'hir, '_>,
582 hir: &'hir hir::Expr<'_>,
583 false_label: &Label,
584 load: &mut dyn FnMut(
585 &mut Ctxt<'a, 'hir, '_>,
586 &mut dyn Needs<'a, 'hir>,
587 ) -> compile::Result<Asm<'hir>>,
588) -> compile::Result<Asm<'hir, Pattern>> {
589 let mut needs = cx.scopes.defer(hir);
590 converge!(load(cx, &mut needs)?, free(needs));
591 let cond = cx.scopes.alloc(hir)?;
592
593 let Some(inst) = pat_lit_inst(cx, hir, needs.addr()?.addr(), cond.addr())? else {
594 return Err(compile::Error::new(hir, ErrorKind::UnsupportedPatternExpr));
595 };
596
597 cx.asm.push(inst, hir)?;
598 cx.asm.jump_if_not(cond.addr(), false_label, hir)?;
599 cond.free()?;
600 needs.free()?;
601 Ok(Asm::new(hir, Pattern::Refutable))
602}
603
604#[instrument_ast(span = hir)]
605fn pat_lit_inst(
606 cx: &mut Ctxt<'_, '_, '_>,
607 hir: &hir::Expr<'_>,
608 addr: InstAddress,
609 cond: InstAddress,
610) -> compile::Result<Option<Inst>> {
611 let hir::ExprKind::Lit(lit) = hir.kind else {
612 return Ok(None);
613 };
614
615 let out = cond.output();
616
617 let inst = match lit {
618 hir::Lit::Char(value) => Inst::EqChar { addr, value, out },
619 hir::Lit::Str(string) => Inst::EqString {
620 addr,
621 slot: cx.q.unit.new_static_string(hir, string)?,
622 out,
623 },
624 hir::Lit::ByteStr(bytes) => Inst::EqBytes {
625 addr,
626 slot: cx.q.unit.new_static_bytes(hir, bytes)?,
627 out,
628 },
629 hir::Lit::Unsigned(value) => Inst::EqUnsigned { addr, value, out },
630 hir::Lit::Signed(value) => Inst::EqSigned { addr, value, out },
631 hir::Lit::Bool(value) => Inst::EqBool { addr, value, out },
632 _ => return Ok(None),
633 };
634
635 Ok(Some(inst))
636}
637
638#[instrument_ast(span = hir)]
640fn condition<'a, 'hir>(
641 cx: &mut Ctxt<'a, 'hir, '_>,
642 hir: &hir::Condition<'hir>,
643 then_label: &Label,
644 false_label: &Label,
645 linear: &mut [Address<'a, 'hir>],
646) -> compile::Result<Asm<'hir, (ScopeHandle, Pattern)>> {
647 match *hir {
648 hir::Condition::Expr(hir) => {
649 let scope = cx.scopes.child(hir)?;
650 let mut addr = cx.scopes.alloc(hir)?.with_name("expression condition");
651
652 let asm = if expr(cx, hir, &mut addr)?.converging() {
653 cx.asm.jump_if(addr.addr(), then_label, hir)?;
654 addr.free()?;
655 Asm::new(hir, (scope, Pattern::Irrefutable))
656 } else {
657 addr.free()?;
658 cx.scopes.pop(hir, scope)?;
659 Asm::diverge(hir)
660 };
661
662 Ok(asm)
663 }
664 hir::Condition::ExprLet(hir) => {
665 let span = hir;
666
667 let scope = cx.scopes.child(span)?;
668
669 let mut load = |cx: &mut Ctxt<'a, 'hir, '_>, needs: &mut dyn Needs<'a, 'hir>| {
670 expr(cx, &hir.expr, needs)
671 };
672
673 let asm = pat_binding_with(
674 cx,
675 &hir.pat,
676 &hir.pat.pat,
677 hir.pat.names,
678 false_label,
679 &mut load,
680 linear,
681 )?;
682
683 if let Some(pat) = asm.into_converging() {
684 cx.asm.jump(then_label, span)?;
685 Ok(Asm::new(span, (scope, pat)))
686 } else {
687 cx.scopes.pop(span, scope)?;
688 Ok(Asm::diverge(span))
689 }
690 }
691 }
692}
693
694#[instrument_ast(span = span)]
696fn pat_sequence<'a, 'hir>(
697 cx: &mut Ctxt<'a, 'hir, '_>,
698 hir: &hir::PatSequence<'hir>,
699 span: &'hir dyn Spanned,
700 false_label: &Label,
701 load: &mut dyn FnMut(
702 &mut Ctxt<'a, 'hir, '_>,
703 &mut dyn Needs<'a, 'hir>,
704 ) -> compile::Result<Asm<'hir>>,
705 bindings: &mut dyn Bindings<hir::Variable, &mut dyn Needs<'a, 'hir>>,
706) -> compile::Result<Asm<'hir, Pattern>> {
707 let mut addr = cx.scopes.defer(span).with_name("loaded pattern sequence");
708 converge!(load(cx, &mut addr)?, free(addr));
709
710 let addr = addr.into_addr()?;
711 let cond = cx.scopes.alloc(span)?.with_name("loaded pattern condition");
712
713 if matches!(
714 hir.kind,
715 hir::PatSequenceKind::Anonymous {
716 type_check: TypeCheck::Tuple,
717 count: 0,
718 is_open: false
719 }
720 ) {
721 cx.asm.push(
722 Inst::IsUnit {
723 addr: addr.addr(),
724 out: cond.output(),
725 },
726 span,
727 )?;
728
729 cx.asm.jump_if_not(cond.addr(), false_label, span)?;
730 } else {
731 let inst = pat_sequence_kind_to_inst(hir.kind, addr.addr(), cond.output());
732 cx.asm.push(inst, span)?;
733 cx.asm.jump_if_not(cond.addr(), false_label, span)?;
734
735 for (index, p) in hir.items.iter().enumerate() {
736 let mut load = |cx: &mut Ctxt<'a, 'hir, '_>, needs: &mut dyn Needs<'a, 'hir>| {
737 cx.asm.push(
738 Inst::TupleIndexGetAt {
739 addr: addr.addr(),
740 index,
741 out: needs.alloc_output()?,
742 },
743 p,
744 )?;
745 Ok(Asm::new(p, ()))
746 };
747
748 converge!(
749 self::pat(cx, p, false_label, &mut load, bindings)?,
750 free(cond, addr)
751 );
752 }
753 }
754
755 cond.free()?;
756 addr.free()?;
757 Ok(Asm::new(span, Pattern::Refutable))
758}
759
760fn pat_sequence_kind_to_inst(kind: hir::PatSequenceKind, addr: InstAddress, out: Output) -> Inst {
761 match kind {
762 hir::PatSequenceKind::Type { hash } => Inst::MatchType { hash, addr, out },
763 hir::PatSequenceKind::BuiltInVariant { type_check } => Inst::MatchBuiltIn {
764 type_check,
765 addr,
766 out,
767 },
768 hir::PatSequenceKind::Variant {
769 enum_hash,
770 variant_hash,
771 } => Inst::MatchVariant {
772 enum_hash,
773 variant_hash,
774 addr,
775 out,
776 },
777 hir::PatSequenceKind::Anonymous {
778 type_check,
779 count,
780 is_open,
781 } => Inst::MatchSequence {
782 type_check,
783 len: count,
784 exact: !is_open,
785 addr,
786 out,
787 },
788 }
789}
790
791#[instrument_ast(span = span)]
793fn pat_object<'a, 'hir>(
794 cx: &mut Ctxt<'a, 'hir, '_>,
795 hir: &hir::PatObject<'hir>,
796 span: &'hir dyn Spanned,
797 false_label: &Label,
798 load: &mut dyn FnMut(
799 &mut Ctxt<'a, 'hir, '_>,
800 &mut dyn Needs<'a, 'hir>,
801 ) -> compile::Result<Asm<'hir>>,
802 bindings: &mut dyn Bindings<hir::Variable, &mut dyn Needs<'a, 'hir>>,
803) -> compile::Result<Asm<'hir, Pattern>> {
804 let mut needs = cx.scopes.defer(span);
805 converge!(load(cx, &mut needs)?, free(needs));
806 let addr = needs.addr()?;
807
808 let cond = cx.scopes.alloc(span)?;
809
810 let mut string_slots = Vec::new();
811
812 for binding in hir.bindings {
813 string_slots.try_push(cx.q.unit.new_static_string(span, binding.key())?)?;
814 }
815
816 let inst = match hir.kind {
817 hir::PatSequenceKind::Type { hash } => Inst::MatchType {
818 hash,
819 addr: addr.addr(),
820 out: cond.output(),
821 },
822 hir::PatSequenceKind::BuiltInVariant { type_check } => Inst::MatchBuiltIn {
823 type_check,
824 addr: addr.addr(),
825 out: cond.output(),
826 },
827 hir::PatSequenceKind::Variant {
828 enum_hash,
829 variant_hash,
830 } => Inst::MatchVariant {
831 enum_hash,
832 variant_hash,
833 addr: addr.addr(),
834 out: cond.output(),
835 },
836 hir::PatSequenceKind::Anonymous { is_open, .. } => {
837 let keys =
838 cx.q.unit
839 .new_static_object_keys_iter(span, hir.bindings.iter().map(|b| b.key()))?;
840
841 Inst::MatchObject {
842 slot: keys,
843 exact: !is_open,
844 addr: addr.addr(),
845 out: cond.output(),
846 }
847 }
848 };
849
850 cx.asm.push(inst, span)?;
853 cx.asm.jump_if_not(cond.addr(), false_label, span)?;
854 cond.free()?;
855
856 for (binding, slot) in hir.bindings.iter().zip(string_slots) {
857 match binding {
858 hir::Binding::Binding(span, _, p) => {
859 let mut load =
860 move |cx: &mut Ctxt<'a, 'hir, '_>, needs: &mut dyn Needs<'a, 'hir>| {
861 cx.asm.push(
862 Inst::ObjectIndexGetAt {
863 addr: addr.addr(),
864 slot,
865 out: needs.alloc_output()?,
866 },
867 span,
868 )?;
869 Ok(Asm::new(span, ()))
870 };
871
872 converge!(
873 self::pat(cx, p, false_label, &mut load, bindings)?,
874 free(needs)
875 );
876 }
877 hir::Binding::Ident(span, name, id) => {
878 let Some(binding) = bindings.remove(id) else {
879 return Err(compile::Error::msg(
880 binding,
881 format!("No binding for {name:?}"),
882 ));
883 };
884
885 cx.asm.push(
886 Inst::ObjectIndexGetAt {
887 addr: addr.addr(),
888 slot,
889 out: binding.output()?,
890 },
891 &span,
892 )?;
893 }
894 }
895 }
896
897 needs.free()?;
898 Ok(Asm::new(span, Pattern::Refutable))
899}
900
901#[instrument_ast(span = hir)]
903fn block<'a, 'hir>(
904 cx: &mut Ctxt<'a, 'hir, '_>,
905 hir: &'hir hir::Block<'hir>,
906 needs: &mut dyn Needs<'a, 'hir>,
907) -> compile::Result<Asm<'hir>> {
908 let break_label = if let Some(label) = hir.label {
909 let break_label = cx.asm.new_label("block_break");
910
911 cx.breaks.push(Break {
912 label: Some(label),
913 continue_label: None,
914 break_label: break_label.try_clone()?,
915 output: Some(needs.alloc_output()?),
916 drop: None,
917 })?;
918
919 Some(break_label)
920 } else {
921 None
922 };
923
924 let scope = cx.scopes.child(hir)?;
925 let asm = block_without_scope(cx, hir, needs)?;
926 cx.scopes.pop(hir, scope)?;
927
928 cx.drop_dangling(hir)?;
929
930 if let Some(break_label) = break_label {
931 cx.asm.label(&break_label)?;
932 cx.breaks.pop();
933 }
934
935 Ok(asm)
936}
937
938#[instrument_ast(span = hir)]
940fn block_without_scope<'a, 'hir>(
941 cx: &mut Ctxt<'a, 'hir, '_>,
942 hir: &'hir hir::Block<'hir>,
943 needs: &mut dyn Needs<'a, 'hir>,
944) -> compile::Result<Asm<'hir>> {
945 let mut diverge = None;
946 cx.contexts.try_push(hir)?;
947
948 for stmt in hir.statements {
949 let mut needs = Any::ignore(hir).with_name("statement ignore");
950
951 if let Some(cause) = diverge {
952 cx.q.diagnostics.unreachable(cx.source_id, stmt, cause)?;
953 continue;
954 }
955
956 let asm = match stmt {
957 hir::Stmt::Local(hir) => local(cx, hir, &mut needs)?,
958 hir::Stmt::Expr(hir) => expr(cx, hir, &mut needs)?,
959 };
960
961 if asm.diverging() && diverge.is_none() {
962 diverge = Some(stmt);
963 }
964 }
965
966 if let Some(cause) = diverge {
967 if let Some(e) = hir.value {
968 cx.q.diagnostics.unreachable(cx.source_id, e, cause)?;
969 }
970 } else if let Some(e) = hir.value {
971 if expr(cx, e, needs)?.diverging() {
972 diverge = Some(e);
973 }
974 } else if let Some(out) = needs.try_alloc_output()? {
975 cx.asm.push(Inst::unit(out), hir)?;
976 }
977
978 cx.contexts
979 .pop()
980 .ok_or("Missing parent context")
981 .with_span(hir)?;
982
983 if diverge.is_some() {
984 return Ok(Asm::diverge(hir));
985 }
986
987 Ok(Asm::new(hir, ()))
988}
989
990#[instrument_ast(span = format)]
992fn builtin_format<'a, 'hir>(
993 cx: &mut Ctxt<'a, 'hir, '_>,
994 format: &'hir hir::BuiltInFormat<'hir>,
995 needs: &mut dyn Needs<'a, 'hir>,
996) -> compile::Result<Asm<'hir>> {
997 use crate::runtime::format;
998
999 let fill = format.spec.fill.unwrap_or(' ');
1000 let align = format.spec.align.unwrap_or_default();
1001 let flags = format.spec.flags.unwrap_or_default();
1002 let width = format.spec.width;
1003 let precision = format.spec.precision;
1004 let format_type = format.spec.format_type.unwrap_or_default();
1005
1006 let spec = format::FormatSpec::new(flags, fill, align, width, precision, format_type);
1007
1008 converge!(expr(cx, format.value, needs)?);
1009
1010 if let Some(addr) = needs.try_alloc_addr()? {
1011 cx.asm.push(
1012 Inst::Format {
1013 addr: addr.addr(),
1014 spec,
1015 out: addr.output(),
1016 },
1017 format,
1018 )?;
1019 }
1020
1021 Ok(Asm::new(format, ()))
1022}
1023
1024#[instrument_ast(span = hir)]
1026fn builtin_template<'a, 'hir>(
1027 cx: &mut Ctxt<'a, 'hir, '_>,
1028 hir: &'hir hir::BuiltInTemplate<'hir>,
1029 needs: &mut dyn Needs<'a, 'hir>,
1030) -> compile::Result<Asm<'hir>> {
1031 let span = hir;
1032
1033 let mut size_hint = 0;
1034 let mut expansions = 0;
1035
1036 let mut linear = cx.scopes.linear(hir, hir.exprs.len())?;
1037
1038 let mut converge = true;
1039
1040 for (hir, addr) in hir.exprs.iter().zip(&mut linear) {
1041 if let hir::ExprKind::Lit(hir::Lit::Str(s)) = hir.kind {
1042 size_hint += s.len();
1043 let slot = cx.q.unit.new_static_string(span, s)?;
1044 cx.asm.push(
1045 Inst::String {
1046 slot,
1047 out: addr.output(),
1048 },
1049 span,
1050 )?;
1051
1052 continue;
1053 }
1054
1055 expansions += 1;
1056
1057 if expr(cx, hir, addr)?.diverging() {
1058 converge = false;
1059 break;
1060 }
1061 }
1062
1063 if hir.from_literal && expansions == 0 {
1064 cx.q.diagnostics
1065 .template_without_expansions(cx.source_id, span, cx.context())?;
1066 }
1067
1068 if converge {
1069 cx.asm.push(
1070 Inst::StringConcat {
1071 addr: linear.addr(),
1072 len: hir.exprs.len(),
1073 size_hint,
1074 out: needs.alloc_output()?,
1075 },
1076 span,
1077 )?;
1078 }
1079
1080 linear.free()?;
1081
1082 if converge {
1083 Ok(Asm::new(span, ()))
1084 } else {
1085 Ok(Asm::diverge(span))
1086 }
1087}
1088
1089#[instrument_ast(span = span)]
1091fn const_<'a, 'hir>(
1092 cx: &mut Ctxt<'a, 'hir, '_>,
1093 value: &ConstValue,
1094 span: &'hir dyn Spanned,
1095 needs: &mut dyn Needs<'a, 'hir>,
1096) -> compile::Result<()> {
1097 let Some(addr) = needs.try_alloc_addr()? else {
1098 cx.q.diagnostics
1099 .not_used(cx.source_id, span, cx.context())?;
1100 return Ok(());
1101 };
1102
1103 let out = addr.output();
1104
1105 match *value.as_kind() {
1106 ConstValueKind::Inline(value) => match value {
1107 Inline::Empty => {
1108 return Err(compile::Error::msg(
1109 span,
1110 "Empty inline constant value is not supported",
1111 ));
1112 }
1113 Inline::Unit => {
1114 cx.asm.push(Inst::unit(out), span)?;
1115 }
1116 Inline::Char(v) => {
1117 cx.asm.push(Inst::char(v, out), span)?;
1118 }
1119 Inline::Signed(v) => {
1120 cx.asm.push(Inst::signed(v, out), span)?;
1121 }
1122 Inline::Unsigned(v) => {
1123 cx.asm.push(Inst::unsigned(v, out), span)?;
1124 }
1125 Inline::Float(v) => {
1126 cx.asm.push(Inst::float(v, out), span)?;
1127 }
1128 Inline::Bool(v) => {
1129 cx.asm.push(Inst::bool(v, out), span)?;
1130 }
1131 Inline::Type(v) => {
1132 cx.asm.push(Inst::ty(v, out), span)?;
1133 }
1134 Inline::Ordering(v) => {
1135 cx.asm.push(Inst::ordering(v, out), span)?;
1136 }
1137 Inline::Hash(v) => {
1138 cx.asm.push(Inst::hash(v, out), span)?;
1139 }
1140 },
1141 ConstValueKind::String(ref s) => {
1142 let slot = cx.q.unit.new_static_string(span, s)?;
1143 cx.asm.push(Inst::String { slot, out }, span)?;
1144 }
1145 ConstValueKind::Bytes(ref b) => {
1146 let slot = cx.q.unit.new_static_bytes(span, b)?;
1147 cx.asm.push(Inst::Bytes { slot, out }, span)?;
1148 }
1149 ConstValueKind::Option(ref option) => match option {
1150 Some(value) => {
1151 const_(cx, value, span, addr)?;
1152
1153 cx.asm.push(
1154 Inst::Variant {
1155 variant: InstVariant::Some,
1156 addr: addr.addr(),
1157 out,
1158 },
1159 span,
1160 )?;
1161 }
1162 None => {
1163 cx.asm.push(
1164 Inst::Variant {
1165 variant: InstVariant::None,
1166 addr: addr.addr(),
1167 out,
1168 },
1169 span,
1170 )?;
1171 }
1172 },
1173 ConstValueKind::Vec(ref vec) => {
1174 let mut linear = cx.scopes.linear(span, vec.len())?;
1175
1176 for (value, needs) in vec.iter().zip(&mut linear) {
1177 const_(cx, value, span, needs)?;
1178 }
1179
1180 cx.asm.push(
1181 Inst::Vec {
1182 addr: linear.addr(),
1183 count: vec.len(),
1184 out,
1185 },
1186 span,
1187 )?;
1188
1189 linear.free_non_dangling()?;
1190 }
1191 ConstValueKind::Tuple(ref tuple) => {
1192 let mut linear = cx.scopes.linear(span, tuple.len())?;
1193
1194 for (value, needs) in tuple.iter().zip(&mut linear) {
1195 const_(cx, value, span, needs)?;
1196 }
1197
1198 cx.asm.push(
1199 Inst::Tuple {
1200 addr: linear.addr(),
1201 count: tuple.len(),
1202 out,
1203 },
1204 span,
1205 )?;
1206
1207 linear.free_non_dangling()?;
1208 }
1209 ConstValueKind::Object(ref object) => {
1210 let mut linear = cx.scopes.linear(span, object.len())?;
1211
1212 let mut entries = object.iter().try_collect::<Vec<_>>()?;
1213 entries.sort_by_key(|k| k.0);
1214
1215 for ((_, value), needs) in entries.iter().copied().zip(&mut linear) {
1216 const_(cx, value, span, needs)?;
1217 }
1218
1219 let slot =
1220 cx.q.unit
1221 .new_static_object_keys_iter(span, entries.iter().map(|e| e.0))?;
1222
1223 cx.asm.push(
1224 Inst::Object {
1225 addr: linear.addr(),
1226 slot,
1227 out,
1228 },
1229 span,
1230 )?;
1231
1232 linear.free_non_dangling()?;
1233 }
1234 ConstValueKind::Struct(hash, ref values) => {
1235 let mut linear = cx.scopes.linear(span, values.len())?;
1236
1237 for (value, needs) in values.iter().zip(&mut linear) {
1238 const_(cx, value, span, needs)?;
1239 }
1240
1241 cx.asm.push(
1242 Inst::ConstConstruct {
1243 addr: linear.addr(),
1244 hash,
1245 count: values.len(),
1246 out,
1247 },
1248 span,
1249 )?;
1250 }
1251 }
1252
1253 Ok(())
1254}
1255
1256#[instrument_ast(span = hir)]
1258fn expr<'a, 'hir>(
1259 cx: &mut Ctxt<'a, 'hir, '_>,
1260 hir: &'hir hir::Expr<'hir>,
1261 needs: &mut dyn Needs<'a, 'hir>,
1262) -> compile::Result<Asm<'hir>> {
1263 let span = hir;
1264
1265 let asm = match hir.kind {
1266 hir::ExprKind::Variable(name) => {
1267 let var = cx.scopes.get(&mut cx.q, span, name)?;
1268 needs.assign_addr(cx, var.addr)?;
1269 Asm::new(span, ())
1270 }
1271 hir::ExprKind::Type(ty) => {
1272 if let Some(out) = needs.try_alloc_output()? {
1273 cx.asm.push(
1274 Inst::Store {
1275 value: InstValue::Type(ty),
1276 out,
1277 },
1278 span,
1279 )?;
1280 }
1281
1282 Asm::new(span, ())
1283 }
1284 hir::ExprKind::Fn(hash) => {
1285 if let Some(out) = needs.try_alloc_output()? {
1286 cx.asm.push(Inst::LoadFn { hash, out }, span)?;
1287 }
1288
1289 Asm::new(span, ())
1290 }
1291 hir::ExprKind::For(hir) => expr_for(cx, hir, span, needs)?,
1292 hir::ExprKind::Loop(hir) => expr_loop(cx, hir, span, needs)?,
1293 hir::ExprKind::Let(hir) => expr_let(cx, hir, needs)?,
1294 hir::ExprKind::Group(hir) => expr(cx, hir, needs)?,
1295 hir::ExprKind::Unary(hir) => expr_unary(cx, hir, span, needs)?,
1296 hir::ExprKind::Assign(hir) => expr_assign(cx, hir, span, needs)?,
1297 hir::ExprKind::Binary(hir) => expr_binary(cx, hir, span, needs)?,
1298 hir::ExprKind::If(hir) => expr_if(cx, hir, span, needs)?,
1299 hir::ExprKind::Index(hir) => expr_index(cx, hir, span, needs)?,
1300 hir::ExprKind::Break(hir) => expr_break(cx, hir, span)?,
1301 hir::ExprKind::Continue(hir) => expr_continue(cx, hir, span, needs)?,
1302 hir::ExprKind::Yield(hir) => expr_yield(cx, hir, span, needs)?,
1303 hir::ExprKind::Block(hir) => block(cx, hir, needs)?,
1304 hir::ExprKind::Return(hir) => expr_return(cx, hir, span)?,
1305 hir::ExprKind::Match(hir) => expr_match(cx, hir, span, needs)?,
1306 hir::ExprKind::Await(hir) => expr_await(cx, hir, span, needs)?,
1307 hir::ExprKind::Try(hir) => expr_try(cx, hir, span, needs)?,
1308 hir::ExprKind::Select(hir) => expr_select(cx, hir, span, needs)?,
1309 hir::ExprKind::Call(hir) => expr_call(cx, hir, span, needs)?,
1310 hir::ExprKind::FieldAccess(hir) => expr_field_access(cx, hir, span, needs)?,
1311 hir::ExprKind::CallClosure(hir) => expr_call_closure(cx, hir, span, needs)?,
1312 hir::ExprKind::Lit(hir) => lit(cx, hir, span, needs)?,
1313 hir::ExprKind::Tuple(hir) => expr_tuple(cx, hir, span, needs)?,
1314 hir::ExprKind::Vec(hir) => expr_vec(cx, hir, span, needs)?,
1315 hir::ExprKind::Object(hir) => expr_object(cx, hir, span, needs)?,
1316 hir::ExprKind::Range(hir) => expr_range(cx, hir, span, needs)?,
1317 hir::ExprKind::Template(template) => builtin_template(cx, template, needs)?,
1318 hir::ExprKind::Format(format) => builtin_format(cx, format, needs)?,
1319 hir::ExprKind::AsyncBlock(hir) => expr_async_block(cx, hir, span, needs)?,
1320 hir::ExprKind::Const(id) => const_item(cx, id, span, needs)?,
1321 hir::ExprKind::Path => {
1322 return Err(compile::Error::msg(
1323 span,
1324 "Path expression is not supported here",
1325 ))
1326 }
1327 };
1328
1329 Ok(asm)
1330}
1331
1332#[instrument_ast(span = span)]
1334fn expr_assign<'a, 'hir>(
1335 cx: &mut Ctxt<'a, 'hir, '_>,
1336 hir: &'hir hir::ExprAssign<'hir>,
1337 span: &'hir dyn Spanned,
1338 needs: &mut dyn Needs<'a, 'hir>,
1339) -> compile::Result<Asm<'hir>> {
1340 let supported = match hir.lhs.kind {
1341 hir::ExprKind::Variable(name) => {
1343 let var = cx.scopes.get(&mut cx.q, span, name)?;
1344 let mut needs = Address::assigned(var.span, cx.scopes, var.addr);
1345 converge!(expr(cx, &hir.rhs, &mut needs)?, free(needs));
1346 needs.free()?;
1347 true
1348 }
1349 hir::ExprKind::FieldAccess(field_access) => {
1351 let mut target = cx.scopes.defer(&field_access.expr);
1352 let mut value = cx.scopes.defer(&hir.rhs);
1353
1354 let asm = expr_array(
1355 cx,
1356 span,
1357 [(&field_access.expr, &mut target), (&hir.rhs, &mut value)],
1358 )?;
1359
1360 match field_access.expr_field {
1362 hir::ExprField::Ident(ident) => {
1363 if let Some([target, value]) = asm.into_converging() {
1364 let slot = cx.q.unit.new_static_string(span, ident)?;
1365
1366 cx.asm.push(
1367 Inst::ObjectIndexSet {
1368 target: target.addr(),
1369 slot,
1370 value: value.addr(),
1371 },
1372 span,
1373 )?;
1374 }
1375 }
1376 hir::ExprField::Index(index) => {
1377 if let Some([target, value]) = asm.into_converging() {
1378 cx.asm.push(
1379 Inst::TupleIndexSet {
1380 target: target.addr(),
1381 index,
1382 value: value.addr(),
1383 },
1384 span,
1385 )?;
1386 }
1387 }
1388 _ => {
1389 return Err(compile::Error::new(span, ErrorKind::BadFieldAccess));
1390 }
1391 };
1392
1393 target.free()?;
1394 value.free()?;
1395 true
1396 }
1397 hir::ExprKind::Index(expr_index_get) => {
1398 let mut target = cx.scopes.defer(&expr_index_get.target);
1399 let mut index = cx.scopes.defer(&expr_index_get.index);
1400 let mut value = cx.scopes.defer(&hir.rhs);
1401
1402 let asm = expr_array(
1403 cx,
1404 span,
1405 [
1406 (&expr_index_get.target, &mut target),
1407 (&expr_index_get.index, &mut index),
1408 (&hir.rhs, &mut value),
1409 ],
1410 )?;
1411
1412 if let Some([target, index, value]) = asm.into_converging() {
1413 cx.asm.push(
1414 Inst::IndexSet {
1415 target: target.addr(),
1416 index: index.addr(),
1417 value: value.addr(),
1418 },
1419 span,
1420 )?;
1421 }
1422
1423 value.free()?;
1424 index.free()?;
1425 target.free()?;
1426 true
1427 }
1428 _ => false,
1429 };
1430
1431 if !supported {
1432 return Err(compile::Error::new(span, ErrorKind::UnsupportedAssignExpr));
1433 }
1434
1435 if let Some(out) = needs.try_alloc_output()? {
1436 cx.asm.push(Inst::unit(out), span)?;
1437 }
1438
1439 Ok(Asm::new(span, ()))
1440}
1441
1442#[instrument_ast(span = hir)]
1444fn expr_await<'a, 'hir>(
1445 cx: &mut Ctxt<'a, 'hir, '_>,
1446 hir: &'hir hir::Expr<'hir>,
1447 span: &'hir dyn Spanned,
1448 needs: &mut dyn Needs<'a, 'hir>,
1449) -> compile::Result<Asm<'hir>> {
1450 let mut addr = cx.scopes.defer(span);
1451 converge!(expr(cx, hir, &mut addr)?, free(addr));
1452
1453 cx.asm.push(
1454 Inst::Await {
1455 addr: addr.addr()?.addr(),
1456 out: needs.alloc_output()?,
1457 },
1458 span,
1459 )?;
1460
1461 addr.free()?;
1462 Ok(Asm::new(span, ()))
1463}
1464
1465#[instrument_ast(span = span)]
1467fn expr_binary<'a, 'hir>(
1468 cx: &mut Ctxt<'a, 'hir, '_>,
1469 hir: &'hir hir::ExprBinary<'hir>,
1470 span: &'hir dyn Spanned,
1471 needs: &mut dyn Needs<'a, 'hir>,
1472) -> compile::Result<Asm<'hir>> {
1473 if hir.op.is_assign() {
1475 return compile_assign_binop(cx, &hir.lhs, &hir.rhs, &hir.op, span, needs);
1476 }
1477
1478 if hir.op.is_conditional() {
1479 return compile_conditional_binop(cx, &hir.lhs, &hir.rhs, &hir.op, span, needs);
1480 }
1481
1482 let mut a = cx.scopes.defer(span);
1483 let mut b = cx.scopes.defer(span);
1484
1485 let asm = expr_array(cx, span, [(&hir.lhs, &mut a), (&hir.rhs, &mut b)])?;
1486
1487 if let Some([a, b]) = asm.into_converging() {
1488 let a = a.addr();
1489 let b = b.addr();
1490 let out = needs.alloc_output()?;
1491
1492 let inst = match hir.op {
1493 ast::BinOp::Eq(..) => Inst::Op {
1494 op: InstOp::Eq,
1495 a,
1496 b,
1497 out,
1498 },
1499 ast::BinOp::Neq(..) => Inst::Op {
1500 op: InstOp::Neq,
1501 a,
1502 b,
1503 out,
1504 },
1505 ast::BinOp::Lt(..) => Inst::Op {
1506 op: InstOp::Lt,
1507 a,
1508 b,
1509 out,
1510 },
1511 ast::BinOp::Gt(..) => Inst::Op {
1512 op: InstOp::Gt,
1513 a,
1514 b,
1515 out,
1516 },
1517 ast::BinOp::Lte(..) => Inst::Op {
1518 op: InstOp::Le,
1519 a,
1520 b,
1521 out,
1522 },
1523 ast::BinOp::Gte(..) => Inst::Op {
1524 op: InstOp::Ge,
1525 a,
1526 b,
1527 out,
1528 },
1529 ast::BinOp::As(..) => Inst::Op {
1530 op: InstOp::As,
1531 a,
1532 b,
1533 out,
1534 },
1535 ast::BinOp::Is(..) => Inst::Op {
1536 op: InstOp::Is,
1537 a,
1538 b,
1539 out,
1540 },
1541 ast::BinOp::IsNot(..) => Inst::Op {
1542 op: InstOp::IsNot,
1543 a,
1544 b,
1545 out,
1546 },
1547 ast::BinOp::And(..) => Inst::Op {
1548 op: InstOp::And,
1549 a,
1550 b,
1551 out,
1552 },
1553 ast::BinOp::Or(..) => Inst::Op {
1554 op: InstOp::Or,
1555 a,
1556 b,
1557 out,
1558 },
1559 ast::BinOp::Add(..) => Inst::Arithmetic {
1560 op: InstArithmeticOp::Add,
1561 a,
1562 b,
1563 out,
1564 },
1565 ast::BinOp::Sub(..) => Inst::Arithmetic {
1566 op: InstArithmeticOp::Sub,
1567 a,
1568 b,
1569 out,
1570 },
1571 ast::BinOp::Div(..) => Inst::Arithmetic {
1572 op: InstArithmeticOp::Div,
1573 a,
1574 b,
1575 out,
1576 },
1577 ast::BinOp::Mul(..) => Inst::Arithmetic {
1578 op: InstArithmeticOp::Mul,
1579 a,
1580 b,
1581 out,
1582 },
1583 ast::BinOp::Rem(..) => Inst::Arithmetic {
1584 op: InstArithmeticOp::Rem,
1585 a,
1586 b,
1587 out,
1588 },
1589 ast::BinOp::BitAnd(..) => Inst::Bitwise {
1590 op: InstBitwiseOp::BitAnd,
1591 a,
1592 b,
1593 out,
1594 },
1595 ast::BinOp::BitXor(..) => Inst::Bitwise {
1596 op: InstBitwiseOp::BitXor,
1597 a,
1598 b,
1599 out,
1600 },
1601 ast::BinOp::BitOr(..) => Inst::Bitwise {
1602 op: InstBitwiseOp::BitOr,
1603 a,
1604 b,
1605 out,
1606 },
1607 ast::BinOp::Shl(..) => Inst::Shift {
1608 op: InstShiftOp::Shl,
1609 a,
1610 b,
1611 out,
1612 },
1613 ast::BinOp::Shr(..) => Inst::Shift {
1614 op: InstShiftOp::Shr,
1615 a,
1616 b,
1617 out,
1618 },
1619
1620 op => {
1621 return Err(compile::Error::new(
1622 span,
1623 ErrorKind::UnsupportedBinaryOp { op },
1624 ));
1625 }
1626 };
1627
1628 cx.asm.push(inst, span)?;
1629 }
1630
1631 a.free()?;
1632 b.free()?;
1633 Ok(Asm::new(span, ()))
1634}
1635
1636fn compile_conditional_binop<'a, 'hir>(
1637 cx: &mut Ctxt<'a, 'hir, '_>,
1638 lhs: &'hir hir::Expr<'hir>,
1639 rhs: &'hir hir::Expr<'hir>,
1640 bin_op: &ast::BinOp,
1641 span: &'hir dyn Spanned,
1642 needs: &mut dyn Needs<'a, 'hir>,
1643) -> compile::Result<Asm<'hir>> {
1644 converge!(expr(cx, lhs, needs)?);
1645
1646 let end_label = cx.asm.new_label("conditional_end");
1647 let addr = needs.addr()?;
1648
1649 match bin_op {
1650 ast::BinOp::And(..) => {
1651 cx.asm.jump_if_not(addr.addr(), &end_label, lhs)?;
1652 }
1653 ast::BinOp::Or(..) => {
1654 cx.asm.jump_if(addr.addr(), &end_label, lhs)?;
1655 }
1656 op => {
1657 return Err(compile::Error::new(
1658 span,
1659 ErrorKind::UnsupportedBinaryOp { op: *op },
1660 ));
1661 }
1662 }
1663
1664 expr(cx, rhs, needs)?.ignore();
1667 cx.asm.label(&end_label)?;
1668 Ok(Asm::new(span, ()))
1669}
1670
1671fn compile_assign_binop<'a, 'hir>(
1672 cx: &mut Ctxt<'a, 'hir, '_>,
1673 lhs: &'hir hir::Expr<'hir>,
1674 rhs: &'hir hir::Expr<'hir>,
1675 bin_op: &ast::BinOp,
1676 span: &'hir dyn Spanned,
1677 needs: &mut dyn Needs<'a, 'hir>,
1678) -> compile::Result<Asm<'hir>> {
1679 let (target, value) = match lhs.kind {
1680 hir::ExprKind::Variable(name) => {
1682 let var = cx.scopes.get(&mut cx.q, lhs, name)?;
1683
1684 let mut value = cx.scopes.defer(rhs);
1685 converge!(expr(cx, rhs, &mut value)?, free(value));
1686 let value = value.into_addr()?;
1687
1688 let inst_target = InstTarget::Address(var.addr);
1689
1690 (inst_target, value)
1691 }
1692 hir::ExprKind::FieldAccess(field_access) => {
1694 let mut target = cx.scopes.defer(&field_access.expr);
1695 converge!(expr(cx, &field_access.expr, &mut target)?, free(target));
1696 let target = target.into_addr()?;
1697
1698 let mut value = cx.scopes.defer(rhs);
1699 converge!(expr(cx, rhs, &mut value)?, free(target, value));
1700 let value = value.into_addr()?;
1701
1702 let inst_target = match field_access.expr_field {
1704 hir::ExprField::Index(index) => InstTarget::TupleField(target.addr(), index),
1705 hir::ExprField::Ident(ident) => {
1706 let slot = cx.q.unit.new_static_string(&field_access.expr, ident)?;
1707 InstTarget::Field(target.addr(), slot)
1708 }
1709 _ => {
1710 return Err(compile::Error::new(span, ErrorKind::BadFieldAccess));
1711 }
1712 };
1713
1714 target.free()?;
1715 (inst_target, value)
1716 }
1717 _ => {
1718 return Err(compile::Error::new(span, ErrorKind::UnsupportedBinaryExpr));
1719 }
1720 };
1721
1722 let inst = match bin_op {
1723 ast::BinOp::AddAssign(..) => Inst::AssignArithmetic {
1724 op: InstArithmeticOp::Add,
1725 target,
1726 rhs: value.addr(),
1727 },
1728 ast::BinOp::SubAssign(..) => Inst::AssignArithmetic {
1729 op: InstArithmeticOp::Sub,
1730 target,
1731 rhs: value.addr(),
1732 },
1733 ast::BinOp::MulAssign(..) => Inst::AssignArithmetic {
1734 op: InstArithmeticOp::Mul,
1735 target,
1736 rhs: value.addr(),
1737 },
1738 ast::BinOp::DivAssign(..) => Inst::AssignArithmetic {
1739 op: InstArithmeticOp::Div,
1740 target,
1741 rhs: value.addr(),
1742 },
1743 ast::BinOp::RemAssign(..) => Inst::AssignArithmetic {
1744 op: InstArithmeticOp::Rem,
1745 target,
1746 rhs: value.addr(),
1747 },
1748 ast::BinOp::BitAndAssign(..) => Inst::AssignBitwise {
1749 op: InstBitwiseOp::BitAnd,
1750 target,
1751 rhs: value.addr(),
1752 },
1753 ast::BinOp::BitXorAssign(..) => Inst::AssignBitwise {
1754 op: InstBitwiseOp::BitXor,
1755 target,
1756 rhs: value.addr(),
1757 },
1758 ast::BinOp::BitOrAssign(..) => Inst::AssignBitwise {
1759 op: InstBitwiseOp::BitOr,
1760 target,
1761 rhs: value.addr(),
1762 },
1763 ast::BinOp::ShlAssign(..) => Inst::AssignShift {
1764 op: InstShiftOp::Shl,
1765 target,
1766 rhs: value.addr(),
1767 },
1768 ast::BinOp::ShrAssign(..) => Inst::AssignShift {
1769 op: InstShiftOp::Shr,
1770 target,
1771 rhs: value.addr(),
1772 },
1773 _ => {
1774 return Err(compile::Error::new(span, ErrorKind::UnsupportedBinaryExpr));
1775 }
1776 };
1777
1778 cx.asm.push(inst, span)?;
1779
1780 if let Some(out) = needs.try_alloc_output()? {
1781 cx.asm.push(Inst::unit(out), span)?;
1782 }
1783
1784 value.free()?;
1785 Ok(Asm::new(span, ()))
1786}
1787
1788#[instrument_ast(span = span)]
1790fn expr_async_block<'a, 'hir>(
1791 cx: &mut Ctxt<'a, 'hir, '_>,
1792 hir: &hir::ExprAsyncBlock<'hir>,
1793 span: &'hir dyn Spanned,
1794 needs: &mut dyn Needs<'a, 'hir>,
1795) -> compile::Result<Asm<'hir>> {
1796 let linear = cx.scopes.linear(span, hir.captures.len())?;
1797
1798 for (capture, needs) in hir.captures.iter().copied().zip(&linear) {
1799 let out = needs.output();
1800
1801 if hir.do_move {
1802 let var = cx.scopes.take(&mut cx.q, span, capture)?;
1803 var.move_(cx.asm, span, Some(&"capture"), out)?;
1804 } else {
1805 let var = cx.scopes.get(&mut cx.q, span, capture)?;
1806 var.copy(cx.asm, span, Some(&"capture"), out)?;
1807 }
1808 }
1809
1810 cx.asm.push_with_comment(
1811 Inst::Call {
1812 hash: hir.hash,
1813 addr: linear.addr(),
1814 args: hir.captures.len(),
1815 out: needs.alloc_output()?,
1816 },
1817 span,
1818 &"async block",
1819 )?;
1820
1821 linear.free_non_dangling()?;
1822 Ok(Asm::new(span, ()))
1823}
1824
1825#[instrument_ast(span = span)]
1827fn const_item<'a, 'hir>(
1828 cx: &mut Ctxt<'a, 'hir, '_>,
1829 hash: Hash,
1830 span: &'hir dyn Spanned,
1831 needs: &mut dyn Needs<'a, 'hir>,
1832) -> compile::Result<Asm<'hir>> {
1833 let Some(const_value) = cx.q.get_const_value(hash) else {
1834 return Err(compile::Error::msg(
1835 span,
1836 try_format!("Missing constant value for hash {hash}"),
1837 ));
1838 };
1839
1840 let const_value = const_value.try_clone().with_span(span)?;
1841 const_(cx, &const_value, span, needs)?;
1842 Ok(Asm::new(span, ()))
1843}
1844
1845#[instrument_ast(span = span)]
1849fn expr_break<'hir>(
1850 cx: &mut Ctxt<'_, 'hir, '_>,
1851 hir: &hir::ExprBreak<'hir>,
1852 span: &'hir dyn Spanned,
1853) -> compile::Result<Asm<'hir>> {
1854 let (break_label, output) = match hir.label {
1855 Some(label) => {
1856 let l = cx.breaks.walk_until_label(span, label, &mut cx.drop)?;
1857 (l.break_label.try_clone()?, l.output)
1858 }
1859 None => {
1860 let Some(l) = cx.breaks.last() else {
1861 return Err(compile::Error::new(span, ErrorKind::BreakUnsupported));
1862 };
1863
1864 cx.drop.clear();
1865 cx.drop.try_extend(l.drop).with_span(span)?;
1866 (l.break_label.try_clone()?, l.output)
1867 }
1868 };
1869
1870 if let Some(hir) = hir.expr {
1871 let Some(output) = output else {
1872 return Err(compile::Error::new(span, ErrorKind::BreakUnsupportedValue));
1873 };
1874
1875 let mut needs = match output.as_addr() {
1876 Some(addr) => Any::assigned(span, cx.scopes, addr),
1877 None => Any::ignore(span),
1878 };
1879
1880 converge!(expr(cx, hir, &mut needs)?, free(needs));
1881 needs.free()?;
1882 } else if let Some(out) = output {
1883 cx.asm.push(Inst::unit(out), span)?;
1884 }
1885
1886 let mut drop_set = cx.q.unit.drop_set();
1887
1888 for addr in cx.drop.drain(..) {
1890 drop_set.push(addr)?;
1891 }
1892
1893 if let Some(set) = drop_set.finish()? {
1894 cx.asm.push(Inst::Drop { set }, span)?;
1895 }
1896
1897 cx.asm.jump(&break_label, span)?;
1898 Ok(Asm::diverge(span))
1899}
1900
1901#[instrument_ast(span = span)]
1903fn expr_call<'a, 'hir>(
1904 cx: &mut Ctxt<'a, 'hir, '_>,
1905 hir: &hir::ExprCall<'hir>,
1906 span: &'hir dyn Spanned,
1907 needs: &mut dyn Needs<'a, 'hir>,
1908) -> compile::Result<Asm<'hir>> {
1909 let args = hir.args.len();
1910
1911 match hir.call {
1912 hir::Call::Var { name, .. } => {
1913 let linear = converge!(exprs(cx, span, hir.args)?);
1914
1915 let var = cx.scopes.get(&mut cx.q, span, name)?;
1916
1917 cx.asm.push(
1918 Inst::CallFn {
1919 function: var.addr,
1920 addr: linear.addr(),
1921 args: hir.args.len(),
1922 out: needs.alloc_output()?,
1923 },
1924 span,
1925 )?;
1926
1927 linear.free_non_dangling()?;
1928 }
1929 hir::Call::Associated { target, hash } => {
1930 let linear = converge!(exprs_2(cx, span, slice::from_ref(target), hir.args)?);
1931
1932 cx.asm.push(
1933 Inst::CallAssociated {
1934 hash,
1935 addr: linear.addr(),
1936 args: args + 1,
1937 out: needs.alloc_output()?,
1938 },
1939 span,
1940 )?;
1941
1942 linear.free_non_dangling()?;
1943 }
1944 hir::Call::Meta { hash } => {
1945 let linear = converge!(exprs(cx, span, hir.args)?);
1946
1947 cx.asm.push(
1948 Inst::Call {
1949 hash,
1950 addr: linear.addr(),
1951 args: hir.args.len(),
1952 out: needs.alloc_output()?,
1953 },
1954 span,
1955 )?;
1956
1957 linear.free_non_dangling()?;
1958 }
1959 hir::Call::Expr { expr: e } => {
1960 let mut function = cx.scopes.defer(span);
1961 converge!(expr(cx, e, &mut function)?, free(function));
1962 let linear = converge!(exprs(cx, span, hir.args)?, free(function));
1963
1964 cx.asm.push(
1965 Inst::CallFn {
1966 function: function.addr()?.addr(),
1967 addr: linear.addr(),
1968 args: hir.args.len(),
1969 out: needs.alloc_output()?,
1970 },
1971 span,
1972 )?;
1973
1974 linear.free_non_dangling()?;
1975 function.free()?;
1976 }
1977 hir::Call::ConstFn {
1978 from_module,
1979 from_item,
1980 id,
1981 } => {
1982 let const_fn = cx.q.const_fn_for(id).with_span(span)?;
1983 let value = cx.call_const_fn(span, from_module, from_item, &const_fn, hir.args)?;
1984 const_(cx, &value, span, needs)?;
1985 }
1986 }
1987
1988 Ok(Asm::new(span, ()))
1989}
1990
1991#[instrument_ast(span = span)]
1993fn expr_array<'a, 'hir, 'needs, const N: usize>(
1994 cx: &mut Ctxt<'a, 'hir, '_>,
1995 span: &'hir dyn Spanned,
1996 array: [(&'hir hir::Expr<'hir>, &'needs mut dyn Needs<'a, 'hir>); N],
1997) -> compile::Result<Asm<'hir, [&'needs Address<'a, 'hir>; N]>> {
1998 let mut out = FixedVec::new();
1999
2000 for (expr, needs) in array {
2001 converge!(self::expr(cx, expr, needs)?);
2002 let addr = needs.addr()?;
2003 out.try_push(addr).with_span(span)?;
2004 }
2005
2006 Ok(Asm::new(span, out.into_inner()))
2007}
2008
2009#[instrument_ast(span = span)]
2010fn exprs<'a, 'hir>(
2011 cx: &mut Ctxt<'a, 'hir, '_>,
2012 span: &'hir dyn Spanned,
2013 args: &'hir [hir::Expr<'hir>],
2014) -> compile::Result<Asm<'hir, Linear<'a, 'hir>>> {
2015 exprs_2(cx, span, args, &[])
2016}
2017
2018#[instrument_ast(span = span)]
2019fn exprs_with<'a, 'hir, T>(
2020 cx: &mut Ctxt<'a, 'hir, '_>,
2021 span: &'hir dyn Spanned,
2022 args: &'hir [T],
2023 map: fn(&'hir T) -> &'hir hir::Expr<'hir>,
2024) -> compile::Result<Asm<'hir, Linear<'a, 'hir>>> {
2025 exprs_2_with(cx, span, args, &[], map)
2026}
2027
2028#[instrument_ast(span = span)]
2030fn exprs_2<'a, 'hir>(
2031 cx: &mut Ctxt<'a, 'hir, '_>,
2032 span: &'hir dyn Spanned,
2033 a: &'hir [hir::Expr<'hir>],
2034 b: &'hir [hir::Expr<'hir>],
2035) -> compile::Result<Asm<'hir, Linear<'a, 'hir>>> {
2036 exprs_2_with(cx, span, a, b, |e| e)
2037}
2038
2039fn exprs_2_with<'a, 'hir, T>(
2040 cx: &mut Ctxt<'a, 'hir, '_>,
2041 span: &'hir dyn Spanned,
2042 a: &'hir [T],
2043 b: &'hir [T],
2044 map: fn(&'hir T) -> &'hir hir::Expr<'hir>,
2045) -> compile::Result<Asm<'hir, Linear<'a, 'hir>>> {
2046 let mut linear;
2047
2048 match (a, b) {
2049 ([], []) => {
2050 linear = Linear::empty();
2051 }
2052 ([e], []) | ([], [e]) => {
2053 let e = map(e);
2054 let mut needs = cx.scopes.alloc(e)?;
2055 converge!(expr(cx, e, &mut needs)?, free(needs));
2056 linear = Linear::single(needs);
2057 }
2058 _ => {
2059 let len = a.len() + b.len();
2060
2061 linear = cx.scopes.linear(span, len)?;
2062
2063 let mut diverge = false;
2064
2065 for (e, needs) in a.iter().chain(b.iter()).zip(&mut linear) {
2066 if expr(cx, map(e), needs)?.diverging() {
2067 diverge = true;
2068 break;
2069 };
2070 }
2071
2072 if diverge {
2073 linear.free()?;
2074 return Ok(Asm::diverge(span));
2075 }
2076 }
2077 }
2078
2079 Ok(Asm::new(span, linear))
2080}
2081
2082#[instrument_ast(span = span)]
2084fn expr_call_closure<'a, 'hir>(
2085 cx: &mut Ctxt<'a, 'hir, '_>,
2086 hir: &hir::ExprCallClosure<'hir>,
2087 span: &'hir dyn Spanned,
2088 needs: &mut dyn Needs<'a, 'hir>,
2089) -> compile::Result<Asm<'hir>> {
2090 let Some(out) = needs.try_alloc_output()? else {
2091 cx.q.diagnostics
2092 .not_used(cx.source_id, span, cx.context())?;
2093 return Ok(Asm::new(span, ()));
2094 };
2095
2096 tracing::trace!(?hir.captures, "assemble call closure");
2097
2098 let linear = cx.scopes.linear(span, hir.captures.len())?;
2099
2100 for (capture, needs) in hir.captures.iter().copied().zip(&linear) {
2102 let out = needs.output();
2103
2104 if hir.do_move {
2105 let var = cx.scopes.take(&mut cx.q, span, capture)?;
2106 var.move_(cx.asm, span, Some(&"capture"), out)?;
2107 } else {
2108 let var = cx.scopes.get(&mut cx.q, span, capture)?;
2109 var.copy(cx.asm, span, Some(&"capture"), out)?;
2110 }
2111 }
2112
2113 cx.asm.push(
2114 Inst::Closure {
2115 hash: hir.hash,
2116 addr: linear.addr(),
2117 count: hir.captures.len(),
2118 out,
2119 },
2120 span,
2121 )?;
2122
2123 linear.free()?;
2124 Ok(Asm::new(span, ()))
2125}
2126
2127#[instrument_ast(span = span)]
2129fn expr_continue<'a, 'hir>(
2130 cx: &mut Ctxt<'a, 'hir, '_>,
2131 hir: &hir::ExprContinue<'hir>,
2132 span: &'hir dyn Spanned,
2133 _: &mut dyn Needs<'a, 'hir>,
2134) -> compile::Result<Asm<'hir>> {
2135 let last_loop = if let Some(label) = hir.label {
2136 cx.breaks.find_label(span, label)?
2137 } else {
2138 let Some(current_loop) = cx.breaks.last() else {
2139 return Err(compile::Error::new(span, ErrorKind::ContinueUnsupported));
2140 };
2141
2142 current_loop
2143 };
2144
2145 let Some(label) = &last_loop.continue_label else {
2146 return Err(compile::Error::new(
2147 span,
2148 ErrorKind::ContinueUnsupportedBlock,
2149 ));
2150 };
2151
2152 cx.asm.jump(label, span)?;
2153 Ok(Asm::new(span, ()))
2154}
2155
2156#[instrument_ast(span = span)]
2158fn expr_field_access<'a, 'hir>(
2159 cx: &mut Ctxt<'a, 'hir, '_>,
2160 hir: &'hir hir::ExprFieldAccess<'hir>,
2161 span: &'hir dyn Spanned,
2162 needs: &mut dyn Needs<'a, 'hir>,
2163) -> compile::Result<Asm<'hir>> {
2164 if let (hir::ExprKind::Variable(name), hir::ExprField::Index(index)) =
2170 (hir.expr.kind, hir.expr_field)
2171 {
2172 let var = cx.scopes.get(&mut cx.q, span, name)?;
2173
2174 cx.asm.push_with_comment(
2175 Inst::TupleIndexGetAt {
2176 addr: var.addr,
2177 index,
2178 out: needs.alloc_output()?,
2179 },
2180 span,
2181 &var,
2182 )?;
2183
2184 return Ok(Asm::new(span, ()));
2185 }
2186
2187 let mut addr = cx.scopes.defer(span);
2188
2189 if expr(cx, &hir.expr, &mut addr)?.converging() {
2190 let addr = addr.addr()?;
2191
2192 match hir.expr_field {
2193 hir::ExprField::Index(index) => {
2194 cx.asm.push(
2195 Inst::TupleIndexGetAt {
2196 addr: addr.addr(),
2197 index,
2198 out: needs.alloc_output()?,
2199 },
2200 span,
2201 )?;
2202 }
2203 hir::ExprField::Ident(field) => {
2204 let slot = cx.q.unit.new_static_string(span, field)?;
2205
2206 cx.asm.push(
2207 Inst::ObjectIndexGetAt {
2208 addr: addr.addr(),
2209 slot,
2210 out: needs.alloc_output()?,
2211 },
2212 span,
2213 )?;
2214 }
2215 _ => return Err(compile::Error::new(span, ErrorKind::BadFieldAccess)),
2216 }
2217 }
2218
2219 addr.free()?;
2220 Ok(Asm::new(span, ()))
2221}
2222
2223#[instrument_ast(span = span)]
2225fn expr_for<'a, 'hir>(
2226 cx: &mut Ctxt<'a, 'hir, '_>,
2227 hir: &'hir hir::ExprFor<'hir>,
2228 span: &'hir dyn Spanned,
2229 needs: &mut dyn Needs<'a, 'hir>,
2230) -> compile::Result<Asm<'hir>> {
2231 let mut iter = cx.scopes.defer(span).with_name("iter");
2232
2233 if !expr(cx, &hir.iter, &mut iter)?.converging() {
2234 iter.free()?;
2235 cx.q.diagnostics
2236 .unreachable(cx.source_id, &hir.body, &hir.iter)?;
2237 return Ok(Asm::diverge(span));
2238 }
2239
2240 let continue_label = cx.asm.new_label("for_continue");
2241 let end_label = cx.asm.new_label("for_end");
2242 let break_label = cx.asm.new_label("for_break");
2243
2244 let iter = iter.into_addr()?;
2246 let into_iter = cx.scopes.alloc(span)?.with_name("into_iter");
2247 let binding = cx.scopes.alloc(&hir.binding)?.with_name("binding");
2248
2249 cx.asm.push_with_comment(
2251 Inst::Copy {
2252 addr: iter.addr(),
2253 out: into_iter.output(),
2254 },
2255 span,
2256 &"Protocol::INTO_ITER",
2257 )?;
2258
2259 cx.asm.push_with_comment(
2260 Inst::CallAssociated {
2261 addr: into_iter.addr(),
2262 hash: Protocol::INTO_ITER.hash,
2263 args: 1,
2264 out: into_iter.output(),
2265 },
2266 &hir.iter,
2267 &"Protocol::INTO_ITER",
2268 )?;
2269
2270 let next_offset = if cx.options.memoize_instance_fn {
2272 let offset = cx.scopes.alloc(&hir.iter)?.with_name("memoized next");
2273
2274 cx.asm.push_with_comment(
2275 Inst::LoadInstanceFn {
2276 addr: into_iter.addr(),
2277 hash: Protocol::NEXT.hash,
2278 out: offset.output(),
2279 },
2280 &hir.iter,
2281 &"Protocol::NEXT",
2282 )?;
2283
2284 Some(offset)
2285 } else {
2286 None
2287 };
2288
2289 cx.asm.label(&continue_label)?;
2290
2291 cx.breaks.push(Break {
2292 label: hir.label,
2293 continue_label: Some(continue_label.try_clone()?),
2294 break_label: break_label.try_clone()?,
2295 output: None,
2296 drop: Some(into_iter.addr()),
2297 })?;
2298
2299 let into_iter_copy = cx.scopes.alloc(span)?.with_name("into_iter_copy");
2300
2301 cx.asm.push(
2302 Inst::Copy {
2303 addr: into_iter.addr(),
2304 out: into_iter_copy.output(),
2305 },
2306 span,
2307 )?;
2308
2309 if let Some(next_offset) = &next_offset {
2311 cx.asm.push(
2312 Inst::CallFn {
2313 function: next_offset.addr(),
2314 addr: into_iter_copy.addr(),
2315 args: 1,
2316 out: binding.output(),
2317 },
2318 span,
2319 )?;
2320 } else {
2321 cx.asm.push_with_comment(
2322 Inst::CallAssociated {
2323 addr: into_iter_copy.addr(),
2324 hash: Protocol::NEXT.hash,
2325 args: 1,
2326 out: binding.output(),
2327 },
2328 span,
2329 &"Protocol::NEXT",
2330 )?;
2331 }
2332
2333 into_iter_copy.free()?;
2334
2335 cx.asm
2337 .iter_next(binding.addr(), &end_label, &hir.binding, binding.output())?;
2338
2339 let inner_loop_scope = cx.scopes.child(&hir.body)?;
2340 let mut bindings = cx.scopes.linear(&hir.binding, hir.binding.names.len())?;
2341
2342 let mut load = |cx: &mut Ctxt<'a, 'hir, '_>, needs: &mut dyn Needs<'a, 'hir>| {
2343 needs.assign_addr(cx, binding.addr())?;
2344 Ok(Asm::new(&hir.binding, ()))
2345 };
2346
2347 let asm = pattern_panic(cx, &hir.binding, |cx, false_label| {
2348 pat_binding_with(
2349 cx,
2350 &hir.binding,
2351 &hir.binding.pat,
2352 hir.binding.names,
2353 false_label,
2354 &mut load,
2355 &mut bindings,
2356 )
2357 })?;
2358
2359 asm.ignore();
2360
2361 let asm = block(cx, &hir.body, &mut Any::ignore(span))?;
2362 bindings.free()?;
2363 cx.scopes.pop(span, inner_loop_scope)?;
2364
2365 if asm.converging() {
2366 cx.asm.jump(&continue_label, span)?;
2367 }
2368
2369 cx.asm.label(&end_label)?;
2370
2371 let mut drop_set = cx.q.unit.drop_set();
2372 drop_set.push(into_iter.addr())?;
2373
2374 if let Some(set) = drop_set.finish()? {
2378 cx.asm.push(Inst::Drop { set }, span)?;
2379 }
2380
2381 cx.asm.label(&break_label)?;
2382
2383 if let Some(out) = needs.try_alloc_output()? {
2384 cx.asm.push(Inst::unit(out), span)?;
2385 }
2386
2387 if let Some(next_offset) = next_offset {
2388 next_offset.free()?;
2389 }
2390
2391 binding.free()?;
2392 into_iter.free()?;
2393 iter.free()?;
2394
2395 cx.breaks.pop();
2396
2397 Ok(Asm::new(span, ()))
2398}
2399
2400#[instrument_ast(span = span)]
2402fn expr_if<'a, 'hir>(
2403 cx: &mut Ctxt<'a, 'hir, '_>,
2404 hir: &hir::Conditional<'hir>,
2405 span: &'hir dyn Spanned,
2406 needs: &mut dyn Needs<'a, 'hir>,
2407) -> compile::Result<Asm<'hir>> {
2408 let output_addr = if hir.fallback.is_none() {
2409 needs.try_alloc_output()?
2410 } else {
2411 None
2412 };
2413
2414 let end_label = cx.asm.new_label("if_end");
2415
2416 let values = hir
2417 .branches
2418 .iter()
2419 .flat_map(|c| c.condition.count())
2420 .max()
2421 .unwrap_or(0);
2422
2423 let mut linear = cx.scopes.linear(span, values)?;
2424 let mut branches = Vec::new();
2425
2426 for branch in hir.branches {
2427 let then_label = cx.asm.new_label("if_branch");
2428 let false_label = cx.asm.new_label("if_false");
2429
2430 if let Some((scope, pat)) =
2431 condition(cx, branch.condition, &then_label, &false_label, &mut linear)?
2432 .into_converging()
2433 {
2434 if matches!(pat, Pattern::Refutable) {
2435 cx.asm.label(&false_label)?;
2436 }
2437
2438 let scope = cx.scopes.dangle(branch, scope)?;
2439 branches.try_push((branch, then_label, scope))?;
2440 }
2441 }
2442
2443 let asm = if let Some(b) = hir.fallback {
2445 block(cx, b, needs)?
2446 } else if let Some(out) = output_addr {
2447 cx.asm.push(Inst::unit(out), span)?;
2448 Asm::new(span, ())
2449 } else {
2450 Asm::new(span, ())
2451 };
2452
2453 if asm.converging() {
2454 cx.asm.jump(&end_label, span)?;
2455 }
2456
2457 let mut it = branches.into_iter().peekable();
2458
2459 while let Some((branch, label, scope)) = it.next() {
2460 cx.asm.label(&label)?;
2461
2462 let scope = cx.scopes.restore(scope);
2463
2464 let asm = if hir.fallback.is_none() {
2465 let asm = block(cx, &branch.block, &mut Any::ignore(branch))?;
2466
2467 if asm.converging() {
2468 if let Some(out) = output_addr {
2469 cx.asm.push(Inst::unit(out), span)?;
2470 }
2471
2472 Asm::new(span, ())
2473 } else {
2474 Asm::diverge(span)
2475 }
2476 } else {
2477 block(cx, &branch.block, needs)?
2478 };
2479
2480 cx.scopes.pop(branch, scope)?;
2481
2482 if asm.converging() && it.peek().is_some() {
2483 cx.asm.jump(&end_label, branch)?;
2484 }
2485 }
2486
2487 cx.asm.label(&end_label)?;
2488 linear.free()?;
2489 Ok(Asm::new(span, ()))
2490}
2491
2492#[instrument_ast(span = span)]
2494fn expr_index<'a, 'hir>(
2495 cx: &mut Ctxt<'a, 'hir, '_>,
2496 hir: &'hir hir::ExprIndex<'hir>,
2497 span: &'hir dyn Spanned,
2498 needs: &mut dyn Needs<'a, 'hir>,
2499) -> compile::Result<Asm<'hir>> {
2500 let mut target = cx.scopes.defer(span);
2501 let mut index = cx.scopes.defer(span);
2502
2503 if let Some([target, index]) = expr_array(
2504 cx,
2505 span,
2506 [(&hir.target, &mut target), (&hir.index, &mut index)],
2507 )?
2508 .into_converging()
2509 {
2510 cx.asm.push(
2511 Inst::IndexGet {
2512 index: index.addr(),
2513 target: target.addr(),
2514 out: needs.alloc_output()?,
2515 },
2516 span,
2517 )?;
2518 }
2519
2520 index.free()?;
2521 target.free()?;
2522 Ok(Asm::new(span, ()))
2523}
2524
2525#[instrument_ast(span = hir)]
2527fn expr_let<'a, 'hir>(
2528 cx: &mut Ctxt<'a, 'hir, '_>,
2529 hir: &'hir hir::ExprLet<'hir>,
2530 needs: &mut dyn Needs<'a, 'hir>,
2531) -> compile::Result<Asm<'hir>> {
2532 let mut load =
2533 |cx: &mut Ctxt<'a, 'hir, '_>, needs: &mut dyn Needs<'a, 'hir>| expr(cx, &hir.expr, needs);
2534
2535 converge!(pattern_panic(cx, &hir.pat, move |cx, false_label| {
2536 pat_binding(cx, &hir.pat, false_label, &mut load)
2537 })?);
2538
2539 if let Some(out) = needs.try_alloc_output()? {
2541 cx.asm.push(Inst::unit(out), hir)?;
2542 }
2543
2544 Ok(Asm::new(hir, ()))
2545}
2546
2547#[instrument_ast(span = span)]
2548fn expr_match<'a, 'hir>(
2549 cx: &mut Ctxt<'a, 'hir, '_>,
2550 hir: &'hir hir::ExprMatch<'hir>,
2551 span: &'hir dyn Spanned,
2552 needs: &mut dyn Needs<'a, 'hir>,
2553) -> compile::Result<Asm<'hir>> {
2554 let mut value = cx.scopes.defer(span);
2555 converge!(expr(cx, hir.expr, &mut value)?, free(value));
2556 let value = value.into_addr()?;
2557
2558 let end_label = cx.asm.new_label("match_end");
2559 let mut branches = Vec::new();
2560
2561 let count = hir
2562 .branches
2563 .iter()
2564 .map(|b| b.pat.names.len())
2565 .max()
2566 .unwrap_or_default();
2567
2568 let mut linear = cx.scopes.linear(span, count)?;
2569 let mut is_irrefutable = false;
2570
2571 for branch in hir.branches {
2572 let span = branch;
2573
2574 let branch_label = cx.asm.new_label("match_branch");
2575 let match_false = cx.asm.new_label("match_false");
2576
2577 let pattern_scope = cx.scopes.child(span)?;
2578
2579 let mut load = |cx: &mut Ctxt<'a, 'hir, '_>, needs: &mut dyn Needs<'a, 'hir>| {
2580 needs.assign_addr(cx, value.addr())?;
2581 Ok(Asm::new(branch, ()))
2582 };
2583
2584 let asm = pat_binding_with(
2585 cx,
2586 &branch.pat,
2587 &branch.pat.pat,
2588 branch.pat.names,
2589 &match_false,
2590 &mut load,
2591 &mut linear,
2592 )?;
2593
2594 if let Some(pat) = asm.into_converging() {
2595 let mut converges = true;
2596
2597 if let Some(condition) = branch.condition {
2598 let scope = cx.scopes.child(condition)?;
2599 let mut cond = cx.scopes.alloc(condition)?.with_name("match condition");
2600
2601 if expr(cx, condition, &mut cond)?.converging() {
2602 cx.asm.jump_if_not(cond.addr(), &match_false, condition)?;
2603 cx.asm.jump(&branch_label, condition)?;
2604 } else {
2605 converges = false;
2606 }
2607
2608 cond.free()?;
2609 cx.scopes.pop(span, scope)?;
2610 } else {
2611 is_irrefutable = matches!(pat, Pattern::Irrefutable);
2615 }
2616
2617 if converges {
2618 cx.asm.jump(&branch_label, span)?;
2619 let pattern_scope = cx.scopes.dangle(span, pattern_scope)?;
2620 branches.try_push((branch_label, pattern_scope))?;
2621 } else {
2622 is_irrefutable = matches!(pat, Pattern::Irrefutable);
2625 cx.scopes.pop(span, pattern_scope)?;
2626 }
2627 }
2628
2629 if is_irrefutable {
2630 break;
2631 }
2632
2633 cx.asm.label(&match_false)?;
2634 }
2635
2636 if !is_irrefutable {
2637 if let Some(out) = needs.try_alloc_output()? {
2638 cx.asm.push(Inst::unit(out), span)?;
2639 }
2640
2641 cx.asm.jump(&end_label, span)?;
2642 }
2643
2644 let mut it = hir.branches.iter().zip(branches).peekable();
2645
2646 while let Some((branch, (label, scope))) = it.next() {
2647 let span = branch;
2648
2649 cx.asm.label(&label)?;
2650 let scope = cx.scopes.restore(scope);
2651
2652 if expr(cx, &branch.body, needs)?.converging() && it.peek().is_some() {
2653 cx.asm.jump(&end_label, span)?;
2654 }
2655
2656 cx.scopes.pop(span, scope)?;
2657 }
2658
2659 cx.asm.label(&end_label)?;
2660
2661 value.free()?;
2662 linear.free()?;
2663 Ok(Asm::new(span, ()))
2664}
2665
2666#[instrument_ast(span = span)]
2668fn expr_object<'a, 'hir>(
2669 cx: &mut Ctxt<'a, 'hir, '_>,
2670 hir: &hir::ExprObject<'hir>,
2671 span: &'hir dyn Spanned,
2672 needs: &mut dyn Needs<'a, 'hir>,
2673) -> compile::Result<Asm<'hir>> {
2674 if let Some(linear) =
2675 exprs_with(cx, span, hir.assignments, |hir| &hir.assign)?.into_converging()
2676 {
2677 match hir.kind {
2678 hir::ExprObjectKind::Struct { hash } => {
2679 cx.asm.push(
2680 Inst::Struct {
2681 addr: linear.addr(),
2682 hash,
2683 out: needs.alloc_output()?,
2684 },
2685 span,
2686 )?;
2687 }
2688 hir::ExprObjectKind::ExternalType { hash, args } => {
2689 reorder_field_assignments(cx, hir, linear.addr(), span)?;
2690
2691 cx.asm.push(
2692 Inst::Call {
2693 hash,
2694 addr: linear.addr(),
2695 args,
2696 out: needs.alloc_output()?,
2697 },
2698 span,
2699 )?;
2700 }
2701 hir::ExprObjectKind::Anonymous => {
2702 let slot = cx
2703 .q
2704 .unit
2705 .new_static_object_keys_iter(span, hir.assignments.iter().map(|a| a.key.1))?;
2706
2707 cx.asm.push(
2708 Inst::Object {
2709 addr: linear.addr(),
2710 slot,
2711 out: needs.alloc_output()?,
2712 },
2713 span,
2714 )?;
2715 }
2716 };
2717
2718 linear.free_non_dangling()?;
2719 }
2720
2721 Ok(Asm::new(span, ()))
2722}
2723
2724fn reorder_field_assignments<'hir>(
2727 cx: &mut Ctxt<'_, 'hir, '_>,
2728 hir: &hir::ExprObject<'hir>,
2729 base: InstAddress,
2730 span: &dyn Spanned,
2731) -> compile::Result<()> {
2732 let mut order = Vec::try_with_capacity(hir.assignments.len())?;
2733
2734 for assign in hir.assignments {
2735 let Some(position) = assign.position else {
2736 return Err(compile::Error::msg(
2737 span,
2738 try_format!("Missing position for field assignment {}", assign.key.1),
2739 ));
2740 };
2741
2742 order.try_push(position)?;
2743 }
2744
2745 let base = base.offset();
2746
2747 for a in 0..hir.assignments.len() {
2748 loop {
2749 let Some(&b) = order.get(a) else {
2750 return Err(compile::Error::msg(span, "Order out-of-bounds"));
2751 };
2752
2753 if a == b {
2754 break;
2755 }
2756
2757 order.swap(a, b);
2758
2759 let (Some(a), Some(b)) = (base.checked_add(a), base.checked_add(b)) else {
2760 return Err(compile::Error::msg(
2761 span,
2762 "Field repositioning out-of-bounds",
2763 ));
2764 };
2765
2766 let a = InstAddress::new(a);
2767 let b = InstAddress::new(b);
2768 cx.asm.push(Inst::Swap { a, b }, span)?;
2769 }
2770 }
2771
2772 Ok(())
2773}
2774
2775#[instrument_ast(span = span)]
2777fn expr_range<'a, 'hir>(
2778 cx: &mut Ctxt<'a, 'hir, '_>,
2779 hir: &'hir hir::ExprRange<'hir>,
2780 span: &'hir dyn Spanned,
2781 needs: &mut dyn Needs<'a, 'hir>,
2782) -> compile::Result<Asm<'hir>> {
2783 let range;
2784 let vars;
2785
2786 match hir {
2787 hir::ExprRange::RangeFrom { start } => {
2788 let mut s = cx.scopes.defer(start);
2789 converge!(expr(cx, start, &mut s)?, free(s));
2790
2791 let start = s.into_addr()?;
2792
2793 range = InstRange::RangeFrom {
2794 start: start.addr(),
2795 };
2796 vars = [Some(start), None];
2797 }
2798 hir::ExprRange::RangeFull => {
2799 range = InstRange::RangeFull;
2800 vars = [None, None];
2801 }
2802 hir::ExprRange::RangeInclusive { start, end } => {
2803 let mut s = cx.scopes.defer(start);
2804 converge!(expr(cx, start, &mut s)?, free(s));
2805
2806 let mut e = cx.scopes.defer(end);
2807 converge!(expr(cx, end, &mut e)?, free(s, e));
2808
2809 let start = s.into_addr()?;
2810 let end = e.into_addr()?;
2811
2812 range = InstRange::RangeInclusive {
2813 start: start.addr(),
2814 end: end.addr(),
2815 };
2816 vars = [Some(start), Some(end)];
2817 }
2818 hir::ExprRange::RangeToInclusive { end } => {
2819 let mut e = cx.scopes.defer(end);
2820 converge!(expr(cx, end, &mut e)?, free(e));
2821
2822 let end = e.into_addr()?;
2823
2824 range = InstRange::RangeToInclusive { end: end.addr() };
2825 vars = [Some(end), None];
2826 }
2827 hir::ExprRange::RangeTo { end } => {
2828 let mut e = cx.scopes.defer(end);
2829 converge!(expr(cx, end, &mut e)?, free(e));
2830
2831 let end = e.into_addr()?;
2832
2833 range = InstRange::RangeTo { end: end.addr() };
2834 vars = [Some(end), None];
2835 }
2836 hir::ExprRange::Range { start, end } => {
2837 let mut s = cx.scopes.defer(start);
2838 converge!(expr(cx, start, &mut s)?, free(s));
2839
2840 let mut e = cx.scopes.defer(end);
2841 converge!(expr(cx, end, &mut e)?, free(s, e));
2842
2843 let start = s.into_addr()?;
2844 let end = e.into_addr()?;
2845
2846 range = InstRange::Range {
2847 start: start.addr(),
2848 end: end.addr(),
2849 };
2850 vars = [Some(start), Some(end)];
2851 }
2852 };
2853
2854 if let Some(out) = needs.try_alloc_output()? {
2855 cx.asm.push(Inst::Range { range, out }, span)?;
2856 }
2857
2858 for var in vars.into_iter().flatten() {
2859 var.free()?;
2860 }
2861
2862 Ok(Asm::new(span, ()))
2863}
2864
2865#[instrument_ast(span = span)]
2867fn expr_return<'hir>(
2868 cx: &mut Ctxt<'_, 'hir, '_>,
2869 hir: Option<&'hir hir::Expr<'hir>>,
2870 span: &'hir dyn Spanned,
2871) -> compile::Result<Asm<'hir>> {
2872 if let Some(e) = hir {
2873 converge!(return_(cx, span, e, expr)?);
2874 } else {
2875 cx.asm.push(Inst::ReturnUnit, span)?;
2876 }
2877
2878 Ok(Asm::diverge(span))
2879}
2880
2881fn expr_select_inner<'a, 'hir>(
2883 cx: &mut Ctxt<'a, 'hir, '_>,
2884 hir: &hir::ExprSelect<'hir>,
2885 span: &'hir dyn Spanned,
2886 needs: &mut dyn Needs<'a, 'hir>,
2887) -> compile::Result<Asm<'hir>> {
2888 let mut default_branch = None;
2889
2890 let end_label = cx.asm.new_label("select_end");
2891
2892 for branch in hir.branches {
2893 let label = cx.asm.new_label("select_branch");
2894 cx.select_branches.try_push((label, branch))?;
2895 }
2896
2897 if let Some(def) = hir.default {
2898 let label = cx.asm.new_label("select_default");
2899 default_branch = Some((def, label));
2900 }
2901
2902 let linear = converge!(exprs(cx, span, hir.exprs)?);
2903
2904 let mut value_addr = cx.scopes.alloc(span)?;
2905
2906 let select_label = cx.asm.new_label("select");
2907 cx.asm.label(&select_label)?;
2908
2909 cx.asm.push(
2910 Inst::Select {
2911 addr: linear.addr(),
2912 len: hir.exprs.len(),
2913 value: value_addr.output(),
2914 },
2915 span,
2916 )?;
2917
2918 for (label, _) in &cx.select_branches {
2919 cx.asm.jump(label, span)?;
2920 }
2921
2922 if let Some((_, label)) = &default_branch {
2923 cx.asm.jump(label, span)?;
2924 } else {
2925 if let Some(out) = needs.try_alloc_output()? {
2926 cx.asm.push(
2927 Inst::Copy {
2928 addr: value_addr.addr(),
2929 out,
2930 },
2931 span,
2932 )?;
2933 }
2934
2935 if !cx.select_branches.is_empty() || default_branch.is_some() {
2936 cx.asm.jump(&end_label, span)?;
2937 }
2938 }
2939
2940 let mut branches = take(&mut cx.select_branches);
2941
2942 for (label, branch) in branches.drain(..) {
2943 cx.asm.label(&label)?;
2944
2945 let scope = cx.scopes.child(&branch.body)?;
2946
2947 if fn_arg_pat(cx, &branch.pat, &mut value_addr, &select_label)?.converging()
2948 && expr(cx, &branch.body, needs)?.converging()
2949 {
2950 cx.asm.jump(&end_label, span)?;
2951 }
2952
2953 cx.scopes.pop(&branch.body, scope)?;
2954 }
2955
2956 cx.select_branches = branches;
2957
2958 if let Some((branch, label)) = default_branch {
2959 cx.asm.label(&label)?;
2960 expr(cx, branch, needs)?.ignore();
2961 }
2962
2963 cx.asm.label(&end_label)?;
2964
2965 let mut drop_set = cx.q.unit.drop_set();
2966
2967 for addr in &linear {
2969 drop_set.push(addr.addr())?;
2970 }
2971
2972 if let Some(set) = drop_set.finish()? {
2973 cx.asm.push(Inst::Drop { set }, span)?;
2974 }
2975
2976 value_addr.free()?;
2977 linear.free()?;
2978 Ok(Asm::new(span, ()))
2979}
2980
2981#[instrument_ast(span = span)]
2982fn expr_select<'a, 'hir>(
2983 cx: &mut Ctxt<'a, 'hir, '_>,
2984 hir: &hir::ExprSelect<'hir>,
2985 span: &'hir dyn Spanned,
2986 needs: &mut dyn Needs<'a, 'hir>,
2987) -> compile::Result<Asm<'hir>> {
2988 cx.contexts.try_push(span)?;
2989 cx.select_branches.clear();
2990
2991 let asm = expr_select_inner(cx, hir, span, needs)?;
2992
2993 cx.contexts
2994 .pop()
2995 .ok_or("Missing parent context")
2996 .with_span(span)?;
2997
2998 Ok(asm)
2999}
3000
3001#[instrument_ast(span = span)]
3003fn expr_try<'a, 'hir>(
3004 cx: &mut Ctxt<'a, 'hir, '_>,
3005 hir: &'hir hir::Expr<'hir>,
3006 span: &'hir dyn Spanned,
3007 needs: &mut dyn Needs<'a, 'hir>,
3008) -> compile::Result<Asm<'hir>> {
3009 let mut e = cx.scopes.defer(span);
3010 converge!(expr(cx, hir, &mut e)?);
3011
3012 cx.asm.push(
3013 Inst::Try {
3014 addr: e.addr()?.addr(),
3015 out: needs.alloc_output()?,
3016 },
3017 span,
3018 )?;
3019
3020 e.free()?;
3021 Ok(Asm::new(span, ()))
3022}
3023
3024#[instrument_ast(span = span)]
3026fn expr_tuple<'a, 'hir>(
3027 cx: &mut Ctxt<'a, 'hir, '_>,
3028 hir: &hir::ExprSeq<'hir>,
3029 span: &'hir dyn Spanned,
3030 needs: &mut dyn Needs<'a, 'hir>,
3031) -> compile::Result<Asm<'hir>> {
3032 macro_rules! tuple {
3033 ($variant:ident $(, $var:ident, $expr:ident)* $(,)?) => {{
3034 $(let mut $var = cx.scopes.defer(span);)*
3035
3036 let asm = expr_array(cx, span, [$(($expr, &mut $var)),*])?;
3037
3038 let [$($expr),*] = converge!(asm, free($($var),*));
3039
3040 cx.asm.push(
3041 Inst::$variant {
3042 addr: [$($expr.addr(),)*],
3043 out: needs.alloc_output()?,
3044 },
3045 span,
3046 )?;
3047
3048 $($var.free()?;)*
3049 }};
3050 }
3051
3052 match hir.items {
3053 [] => {
3054 cx.asm.push(Inst::unit(needs.alloc_output()?), span)?;
3055 }
3056 [e1] => tuple!(Tuple1, v1, e1),
3057 [e1, e2] => tuple!(Tuple2, v1, e1, v2, e2),
3058 [e1, e2, e3] => tuple!(Tuple3, v1, e1, v2, e2, v3, e3),
3059 [e1, e2, e3, e4] => tuple!(Tuple4, v1, e1, v2, e2, v3, e3, v4, e4),
3060 _ => {
3061 let linear = converge!(exprs(cx, span, hir.items)?);
3062
3063 if let Some(out) = needs.try_alloc_output()? {
3064 cx.asm.push(
3065 Inst::Tuple {
3066 addr: linear.addr(),
3067 count: hir.items.len(),
3068 out,
3069 },
3070 span,
3071 )?;
3072
3073 linear.free_non_dangling()?;
3074 } else {
3075 linear.free()?;
3076 }
3077 }
3078 }
3079
3080 Ok(Asm::new(span, ()))
3081}
3082
3083#[instrument_ast(span = span)]
3085fn expr_unary<'a, 'hir>(
3086 cx: &mut Ctxt<'a, 'hir, '_>,
3087 hir: &'hir hir::ExprUnary<'hir>,
3088 span: &'hir dyn Spanned,
3089 needs: &mut dyn Needs<'a, 'hir>,
3090) -> compile::Result<Asm<'hir>> {
3091 let mut addr = cx.scopes.defer(span);
3092 converge!(expr(cx, &hir.expr, &mut addr)?, free(addr));
3093 let addr = addr.into_addr()?;
3094
3095 match hir.op {
3096 ast::UnOp::Not(..) => {
3097 cx.asm.push(
3098 Inst::Not {
3099 addr: addr.addr(),
3100 out: needs.alloc_output()?,
3101 },
3102 span,
3103 )?;
3104 }
3105 ast::UnOp::Neg(..) => {
3106 cx.asm.push(
3107 Inst::Neg {
3108 addr: addr.addr(),
3109 out: needs.alloc_output()?,
3110 },
3111 span,
3112 )?;
3113 }
3114 op => {
3115 return Err(compile::Error::new(
3116 span,
3117 ErrorKind::UnsupportedUnaryOp { op },
3118 ));
3119 }
3120 }
3121
3122 addr.free()?;
3123 Ok(Asm::new(span, ()))
3124}
3125
3126#[instrument_ast(span = span)]
3128fn expr_vec<'a, 'hir>(
3129 cx: &mut Ctxt<'a, 'hir, '_>,
3130 hir: &hir::ExprSeq<'hir>,
3131 span: &'hir dyn Spanned,
3132 needs: &mut dyn Needs<'a, 'hir>,
3133) -> compile::Result<Asm<'hir>> {
3134 let mut linear = cx.scopes.linear(span, hir.items.len())?;
3135 let count = hir.items.len();
3136
3137 for (e, needs) in hir.items.iter().zip(&mut linear) {
3138 converge!(expr(cx, e, needs)?, free(linear));
3139 }
3140
3141 if let Some(out) = needs.try_alloc_addr()? {
3142 cx.asm.push(
3143 Inst::Vec {
3144 addr: linear.addr(),
3145 count,
3146 out: out.output(),
3147 },
3148 span,
3149 )?;
3150
3151 linear.free_non_dangling()?;
3152 } else {
3153 linear.free()?;
3154 }
3155
3156 Ok(Asm::new(span, ()))
3157}
3158
3159#[instrument_ast(span = span)]
3161fn expr_loop<'a, 'hir>(
3162 cx: &mut Ctxt<'a, 'hir, '_>,
3163 hir: &'hir hir::ExprLoop<'hir>,
3164 span: &'hir dyn Spanned,
3165 needs: &mut dyn Needs<'a, 'hir>,
3166) -> compile::Result<Asm<'hir>> {
3167 let continue_label = cx.asm.new_label("while_continue");
3168 let then_label = cx.asm.new_label("while_then");
3169 let end_label = cx.asm.new_label("while_end");
3170 let break_label = cx.asm.new_label("while_break");
3171
3172 cx.breaks.push(Break {
3173 label: hir.label,
3174 continue_label: Some(continue_label.try_clone()?),
3175 break_label: break_label.try_clone()?,
3176 output: Some(needs.alloc_output()?),
3177 drop: None,
3178 })?;
3179
3180 cx.asm.label(&continue_label)?;
3181
3182 let count = hir.condition.and_then(|c| c.count()).unwrap_or_default();
3183 let mut linear = cx.scopes.linear(span, count)?;
3184
3185 let condition_scope = if let Some(hir) = hir.condition {
3186 if let Some((scope, _)) =
3187 condition(cx, hir, &then_label, &end_label, &mut linear)?.into_converging()
3188 {
3189 cx.asm.jump(&end_label, span)?;
3190 cx.asm.label(&then_label)?;
3191 Some(scope)
3192 } else {
3193 None
3194 }
3195 } else {
3196 None
3197 };
3198
3199 block(cx, &hir.body, &mut Any::ignore(span))?.ignore();
3201
3202 if let Some(scope) = condition_scope {
3203 cx.scopes.pop(span, scope)?;
3204 }
3205
3206 cx.asm.jump(&continue_label, span)?;
3207 cx.asm.label(&end_label)?;
3208
3209 if let Some(out) = needs.try_alloc_output()? {
3210 cx.asm.push(Inst::unit(out), span)?;
3211 }
3212
3213 cx.asm.label(&break_label)?;
3214
3215 linear.free()?;
3216 cx.breaks.pop();
3217 Ok(Asm::new(span, ()))
3218}
3219
3220#[instrument_ast(span = span)]
3222fn expr_yield<'a, 'hir>(
3223 cx: &mut Ctxt<'a, 'hir, '_>,
3224 hir: Option<&'hir hir::Expr<'hir>>,
3225 span: &'hir dyn Spanned,
3226 needs: &mut dyn Needs<'a, 'hir>,
3227) -> compile::Result<Asm<'hir>> {
3228 let out = needs.alloc_output()?;
3229
3230 if let Some(e) = hir {
3231 let mut addr = cx.scopes.alloc(span)?.with_name("yield argument");
3232 converge!(expr(cx, e, &mut addr)?, free(addr));
3233
3234 cx.asm.push(
3235 Inst::Yield {
3236 addr: addr.addr(),
3237 out,
3238 },
3239 span,
3240 )?;
3241
3242 addr.free()?;
3243 } else {
3244 cx.asm.push(Inst::YieldUnit { out }, span)?;
3245 }
3246
3247 Ok(Asm::new(span, ()))
3248}
3249
3250#[instrument_ast(span = span)]
3252fn lit<'a, 'hir>(
3253 cx: &mut Ctxt<'a, 'hir, '_>,
3254 hir: hir::Lit<'_>,
3255 span: &'hir dyn Spanned,
3256 needs: &mut dyn Needs<'a, 'hir>,
3257) -> compile::Result<Asm<'hir>> {
3258 let Some(addr) = needs.try_alloc_addr()? else {
3260 cx.q.diagnostics
3261 .not_used(cx.source_id, span, cx.context())?;
3262 return Ok(Asm::new(span, ()));
3263 };
3264
3265 let out = addr.output();
3266
3267 match hir {
3268 hir::Lit::Bool(v) => {
3269 cx.asm.push(Inst::bool(v, out), span)?;
3270 }
3271 hir::Lit::Char(v) => {
3272 cx.asm.push(Inst::char(v, out), span)?;
3273 }
3274 hir::Lit::Unsigned(v) => {
3275 cx.asm.push(Inst::unsigned(v, out), span)?;
3276 }
3277 hir::Lit::Signed(v) => {
3278 cx.asm.push(Inst::signed(v, out), span)?;
3279 }
3280 hir::Lit::Float(v) => {
3281 cx.asm.push(Inst::float(v, out), span)?;
3282 }
3283 hir::Lit::Str(string) => {
3284 let slot = cx.q.unit.new_static_string(span, string)?;
3285 cx.asm.push(Inst::String { slot, out }, span)?;
3286 }
3287 hir::Lit::ByteStr(bytes) => {
3288 let slot = cx.q.unit.new_static_bytes(span, bytes)?;
3289 cx.asm.push(Inst::Bytes { slot, out }, span)?;
3290 }
3291 };
3292
3293 Ok(Asm::new(span, ()))
3294}
3295
3296#[instrument_ast(span = hir)]
3298fn local<'a, 'hir>(
3299 cx: &mut Ctxt<'a, 'hir, '_>,
3300 hir: &'hir hir::Local<'hir>,
3301 needs: &mut dyn Needs<'a, 'hir>,
3302) -> compile::Result<Asm<'hir>> {
3303 let mut load =
3304 |cx: &mut Ctxt<'a, 'hir, '_>, needs: &mut dyn Needs<'a, 'hir>| expr(cx, &hir.expr, needs);
3305
3306 converge!(pattern_panic(cx, &hir.pat, |cx, false_label| {
3307 pat_binding(cx, &hir.pat, false_label, &mut load)
3308 })?);
3309
3310 if let Some(out) = needs.try_alloc_output()? {
3312 cx.asm.push(Inst::unit(out), hir)?;
3313 }
3314
3315 Ok(Asm::new(hir, ()))
3316}