cranelift_isle/
codegen.rs

1//! Generate Rust code from a series of Sequences.
2
3use crate::files::Files;
4use crate::sema::{
5    BuiltinType, ExternalSig, ReturnKind, Term, TermEnv, TermId, Type, TypeEnv, TypeId,
6};
7use crate::serialize::{Block, ControlFlow, EvalStep, MatchArm};
8use crate::stablemapset::StableSet;
9use crate::trie_again::{Binding, BindingId, Constraint, RuleSet};
10use std::fmt::Write;
11use std::slice::Iter;
12use std::sync::Arc;
13
14/// Options for code generation.
15#[derive(Clone, Debug, Default)]
16pub struct CodegenOptions {
17    /// Do not include the `#![allow(...)]` pragmas in the generated
18    /// source. Useful if it must be include!()'d elsewhere.
19    pub exclude_global_allow_pragmas: bool,
20
21    /// Prefixes to remove when printing file names in generaed files. This
22    /// helps keep codegen deterministic.
23    pub prefixes: Vec<Prefix>,
24
25    /// Emit `log::debug!` and `log::trace!` invocations in the generated code to help
26    /// debug rule matching and execution.
27    ///
28    /// In Cranelift this is typically controlled by a cargo feature on the
29    /// crate that includes the generated code (e.g. `cranelift-codegen`).
30    pub emit_logging: bool,
31}
32
33/// A path prefix which should be replaced when printing file names.
34#[derive(Clone, Debug)]
35pub struct Prefix {
36    /// Prefix to strip
37    pub prefix: String,
38
39    /// Name replacing the stripped prefix.
40    pub name: String,
41}
42
43/// Emit Rust source code for the given type and term environments.
44pub fn codegen(
45    files: Arc<Files>,
46    typeenv: &TypeEnv,
47    termenv: &TermEnv,
48    terms: &[(TermId, RuleSet)],
49    options: &CodegenOptions,
50) -> String {
51    Codegen::compile(files, typeenv, termenv, terms).generate_rust(options)
52}
53
54#[derive(Clone, Debug)]
55struct Codegen<'a> {
56    files: Arc<Files>,
57    typeenv: &'a TypeEnv,
58    termenv: &'a TermEnv,
59    terms: &'a [(TermId, RuleSet)],
60}
61
62enum Nested<'a> {
63    Cases(Iter<'a, EvalStep>),
64    Arms(BindingId, Iter<'a, MatchArm>),
65}
66
67struct BodyContext<'a, W> {
68    out: &'a mut W,
69    ruleset: &'a RuleSet,
70    indent: String,
71    is_ref: StableSet<BindingId>,
72    is_bound: StableSet<BindingId>,
73    term_name: &'a str,
74    emit_logging: bool,
75}
76
77impl<'a, W: Write> BodyContext<'a, W> {
78    fn new(out: &'a mut W, ruleset: &'a RuleSet, term_name: &'a str, emit_logging: bool) -> Self {
79        Self {
80            out,
81            ruleset,
82            indent: Default::default(),
83            is_ref: Default::default(),
84            is_bound: Default::default(),
85            term_name,
86            emit_logging,
87        }
88    }
89
90    fn enter_scope(&mut self) -> StableSet<BindingId> {
91        let new = self.is_bound.clone();
92        std::mem::replace(&mut self.is_bound, new)
93    }
94
95    fn begin_block(&mut self) -> std::fmt::Result {
96        self.indent.push_str("    ");
97        writeln!(self.out, " {{")
98    }
99
100    fn end_block(&mut self, last_line: &str, scope: StableSet<BindingId>) -> std::fmt::Result {
101        if !last_line.is_empty() {
102            writeln!(self.out, "{}{}", &self.indent, last_line)?;
103        }
104        self.is_bound = scope;
105        self.end_block_without_newline()?;
106        writeln!(self.out)
107    }
108
109    fn end_block_without_newline(&mut self) -> std::fmt::Result {
110        self.indent.truncate(self.indent.len() - 4);
111        write!(self.out, "{}}}", &self.indent)
112    }
113
114    fn set_ref(&mut self, binding: BindingId, is_ref: bool) {
115        if is_ref {
116            self.is_ref.insert(binding);
117        } else {
118            debug_assert!(!self.is_ref.contains(&binding));
119        }
120    }
121}
122
123impl<'a> Codegen<'a> {
124    fn compile(
125        files: Arc<Files>,
126        typeenv: &'a TypeEnv,
127        termenv: &'a TermEnv,
128        terms: &'a [(TermId, RuleSet)],
129    ) -> Codegen<'a> {
130        Codegen {
131            files,
132            typeenv,
133            termenv,
134            terms,
135        }
136    }
137
138    fn generate_rust(&self, options: &CodegenOptions) -> String {
139        let mut code = String::new();
140
141        self.generate_header(&mut code, options);
142        self.generate_ctx_trait(&mut code);
143        self.generate_internal_types(&mut code);
144        self.generate_internal_term_constructors(&mut code, options)
145            .unwrap();
146
147        code
148    }
149
150    fn generate_header(&self, code: &mut String, options: &CodegenOptions) {
151        writeln!(code, "// GENERATED BY ISLE. DO NOT EDIT!").unwrap();
152        writeln!(code, "//").unwrap();
153        writeln!(
154            code,
155            "// Generated automatically from the instruction-selection DSL code in:",
156        )
157        .unwrap();
158        for file in &self.files.file_names {
159            writeln!(code, "// - {file}").unwrap();
160        }
161
162        if !options.exclude_global_allow_pragmas {
163            writeln!(
164                code,
165                "\n#![allow(dead_code, unreachable_code, unreachable_patterns)]"
166            )
167            .unwrap();
168            writeln!(
169                code,
170                "#![allow(unused_imports, unused_variables, non_snake_case, unused_mut)]"
171            )
172            .unwrap();
173            writeln!(
174                code,
175                "#![allow(irrefutable_let_patterns, unused_assignments, non_camel_case_types)]"
176            )
177            .unwrap();
178        }
179
180        writeln!(code, "\nuse super::*;  // Pulls in all external types.").unwrap();
181        writeln!(code, "use core::marker::PhantomData;").unwrap();
182    }
183
184    fn generate_trait_sig(&self, code: &mut String, indent: &str, sig: &ExternalSig) {
185        let ret_tuple = format!(
186            "{open_paren}{rets}{close_paren}",
187            open_paren = if sig.ret_tys.len() != 1 { "(" } else { "" },
188            rets = sig
189                .ret_tys
190                .iter()
191                .map(|&ty| self.type_name(ty, /* by_ref = */ false))
192                .collect::<Vec<_>>()
193                .join(", "),
194            close_paren = if sig.ret_tys.len() != 1 { ")" } else { "" },
195        );
196
197        if sig.ret_kind == ReturnKind::Iterator {
198            writeln!(
199                code,
200                "{indent}type {name}_returns: Default + IntoContextIter<Context = Self, Output = {output}>;",
201                indent = indent,
202                name = sig.func_name,
203                output = ret_tuple,
204            )
205            .unwrap();
206        }
207
208        let ret_ty = match sig.ret_kind {
209            ReturnKind::Plain => ret_tuple,
210            ReturnKind::Option => format!("Option<{ret_tuple}>"),
211            ReturnKind::Iterator => format!("()"),
212        };
213
214        writeln!(
215            code,
216            "{indent}fn {name}(&mut self, {params}) -> {ret_ty};",
217            indent = indent,
218            name = sig.func_name,
219            params = sig
220                .param_tys
221                .iter()
222                .enumerate()
223                .map(|(i, &ty)| format!("arg{}: {}", i, self.type_name(ty, /* by_ref = */ true)))
224                .chain(if sig.ret_kind == ReturnKind::Iterator {
225                    Some(format!("returns: &mut Self::{}_returns", sig.func_name))
226                } else {
227                    None
228                })
229                .collect::<Vec<_>>()
230                .join(", "),
231            ret_ty = ret_ty,
232        )
233        .unwrap();
234    }
235
236    fn generate_ctx_trait(&self, code: &mut String) {
237        writeln!(code).unwrap();
238        writeln!(
239            code,
240            "/// Context during lowering: an implementation of this trait"
241        )
242        .unwrap();
243        writeln!(
244            code,
245            "/// must be provided with all external constructors and extractors."
246        )
247        .unwrap();
248        writeln!(
249            code,
250            "/// A mutable borrow is passed along through all lowering logic."
251        )
252        .unwrap();
253        writeln!(code, "pub trait Context {{").unwrap();
254        for term in &self.termenv.terms {
255            if term.has_external_extractor() {
256                let ext_sig = term.extractor_sig(self.typeenv).unwrap();
257                self.generate_trait_sig(code, "    ", &ext_sig);
258            }
259            if term.has_external_constructor() {
260                let ext_sig = term.constructor_sig(self.typeenv).unwrap();
261                self.generate_trait_sig(code, "    ", &ext_sig);
262            }
263        }
264        writeln!(code, "}}").unwrap();
265        writeln!(
266            code,
267            r#"
268pub trait ContextIter {{
269    type Context;
270    type Output;
271    fn next(&mut self, ctx: &mut Self::Context) -> Option<Self::Output>;
272    fn size_hint(&self) -> (usize, Option<usize>) {{ (0, None) }}
273}}
274
275pub trait IntoContextIter {{
276    type Context;
277    type Output;
278    type IntoIter: ContextIter<Context = Self::Context, Output = Self::Output>;
279    fn into_context_iter(self) -> Self::IntoIter;
280}}
281
282pub trait Length {{
283    fn len(&self) -> usize;
284}}
285
286impl<T> Length for alloc::vec::Vec<T> {{
287    fn len(&self) -> usize {{
288        alloc::vec::Vec::len(self)
289    }}
290}}
291
292pub struct ContextIterWrapper<I, C> {{
293    iter: I,
294    _ctx: core::marker::PhantomData<C>,
295}}
296impl<I: Default, C> Default for ContextIterWrapper<I, C> {{
297    fn default() -> Self {{
298        ContextIterWrapper {{
299            iter: I::default(),
300            _ctx: core::marker::PhantomData
301        }}
302    }}
303}}
304impl<I, C> core::ops::Deref for ContextIterWrapper<I, C> {{
305    type Target = I;
306    fn deref(&self) -> &I {{
307        &self.iter
308    }}
309}}
310impl<I, C> core::ops::DerefMut for ContextIterWrapper<I, C> {{
311    fn deref_mut(&mut self) -> &mut I {{
312        &mut self.iter
313    }}
314}}
315impl<I: Iterator, C: Context> From<I> for ContextIterWrapper<I, C> {{
316    fn from(iter: I) -> Self {{
317        Self {{ iter, _ctx: core::marker::PhantomData }}
318    }}
319}}
320impl<I: Iterator, C: Context> ContextIter for ContextIterWrapper<I, C> {{
321    type Context = C;
322    type Output = I::Item;
323    fn next(&mut self, _ctx: &mut Self::Context) -> Option<Self::Output> {{
324        self.iter.next()
325    }}
326    fn size_hint(&self) -> (usize, Option<usize>) {{
327        self.iter.size_hint()
328    }}
329}}
330impl<I: IntoIterator, C: Context> IntoContextIter for ContextIterWrapper<I, C> {{
331    type Context = C;
332    type Output = I::Item;
333    type IntoIter = ContextIterWrapper<I::IntoIter, C>;
334    fn into_context_iter(self) -> Self::IntoIter {{
335        ContextIterWrapper {{
336            iter: self.iter.into_iter(),
337            _ctx: core::marker::PhantomData
338        }}
339    }}
340}}
341impl<T, E: Extend<T>, C> Extend<T> for ContextIterWrapper<E, C> {{
342    fn extend<I: IntoIterator<Item = T>>(&mut self, iter: I) {{
343        self.iter.extend(iter);
344    }}
345}}
346impl<L: Length, C> Length for ContextIterWrapper<L, C> {{
347    fn len(&self) -> usize {{
348        self.iter.len()
349    }}
350}}
351           "#,
352        )
353        .unwrap();
354    }
355
356    fn generate_internal_types(&self, code: &mut String) {
357        for ty in &self.typeenv.types {
358            match ty {
359                &Type::Enum {
360                    name,
361                    is_extern,
362                    is_nodebug,
363                    ref variants,
364                    pos,
365                    ..
366                } if !is_extern => {
367                    let name = &self.typeenv.syms[name.index()];
368                    writeln!(
369                        code,
370                        "\n/// Internal type {}: defined at {}.",
371                        name,
372                        pos.pretty_print_line(&self.files)
373                    )
374                    .unwrap();
375
376                    // Generate the `derive`s.
377                    let debug_derive = if is_nodebug { "" } else { ", Debug" };
378                    if variants.iter().all(|v| v.fields.is_empty()) {
379                        writeln!(code, "#[derive(Copy, Clone, PartialEq, Eq{debug_derive})]")
380                            .unwrap();
381                    } else {
382                        writeln!(code, "#[derive(Clone{debug_derive})]").unwrap();
383                    }
384
385                    writeln!(code, "pub enum {name} {{").unwrap();
386                    for variant in variants {
387                        let name = &self.typeenv.syms[variant.name.index()];
388                        if variant.fields.is_empty() {
389                            writeln!(code, "    {name},").unwrap();
390                        } else {
391                            writeln!(code, "    {name} {{").unwrap();
392                            for field in &variant.fields {
393                                let name = &self.typeenv.syms[field.name.index()];
394                                let ty_name =
395                                    self.typeenv.types[field.ty.index()].name(self.typeenv);
396                                writeln!(code, "        {name}: {ty_name},").unwrap();
397                            }
398                            writeln!(code, "    }},").unwrap();
399                        }
400                    }
401                    writeln!(code, "}}").unwrap();
402                }
403                _ => {}
404            }
405        }
406    }
407
408    fn type_name(&self, typeid: TypeId, by_ref: bool) -> String {
409        match self.typeenv.types[typeid.index()] {
410            Type::Builtin(bt) => String::from(bt.name()),
411            Type::Primitive(_, sym, _) => self.typeenv.syms[sym.index()].clone(),
412            Type::Enum { name, .. } => {
413                let r = if by_ref { "&" } else { "" };
414                format!("{}{}", r, self.typeenv.syms[name.index()])
415            }
416        }
417    }
418
419    fn generate_internal_term_constructors(
420        &self,
421        code: &mut String,
422        options: &CodegenOptions,
423    ) -> std::fmt::Result {
424        for &(termid, ref ruleset) in self.terms.iter() {
425            let root = crate::serialize::serialize(ruleset);
426
427            let termdata = &self.termenv.terms[termid.index()];
428            let term_name = &self.typeenv.syms[termdata.name.index()];
429            let mut ctx = BodyContext::new(code, ruleset, term_name, options.emit_logging);
430            writeln!(ctx.out)?;
431            writeln!(
432                ctx.out,
433                "{}// Generated as internal constructor for term {}.",
434                &ctx.indent, term_name,
435            )?;
436
437            let sig = termdata.constructor_sig(self.typeenv).unwrap();
438            writeln!(
439                ctx.out,
440                "{}pub fn {}<C: Context>(",
441                &ctx.indent, sig.func_name
442            )?;
443
444            writeln!(ctx.out, "{}    ctx: &mut C,", &ctx.indent)?;
445            for (i, &ty) in sig.param_tys.iter().enumerate() {
446                let (is_ref, ty) = self.ty(ty);
447                write!(ctx.out, "{}    arg{}: ", &ctx.indent, i)?;
448                write!(ctx.out, "{}{}", if is_ref { "&" } else { "" }, ty)?;
449                if let Some(binding) = ctx.ruleset.find_binding(&Binding::Argument {
450                    index: i.try_into().unwrap(),
451                }) {
452                    ctx.set_ref(binding, is_ref);
453                }
454                writeln!(ctx.out, ",")?;
455            }
456
457            let (_, ret) = self.ty(sig.ret_tys[0]);
458
459            if let ReturnKind::Iterator = sig.ret_kind {
460                writeln!(
461                    ctx.out,
462                    "{}    returns: &mut (impl Extend<{}> + Length),",
463                    &ctx.indent, ret
464                )?;
465            }
466
467            write!(ctx.out, "{}) -> ", &ctx.indent)?;
468            match sig.ret_kind {
469                ReturnKind::Iterator => write!(ctx.out, "()")?,
470                ReturnKind::Option => write!(ctx.out, "Option<{ret}>")?,
471                ReturnKind::Plain => write!(ctx.out, "{ret}")?,
472            };
473
474            let last_expr = if let Some(EvalStep {
475                check: ControlFlow::Return { .. },
476                ..
477            }) = root.steps.last()
478            {
479                // If there's an outermost fallback, no need for another `return` statement.
480                String::new()
481            } else {
482                match sig.ret_kind {
483                    ReturnKind::Iterator => String::new(),
484                    ReturnKind::Option => "None".to_string(),
485                    ReturnKind::Plain => format!(
486                        "unreachable!(\"no rule matched for term {{}} at {{}}; should it be partial?\", {:?}, {:?})",
487                        term_name,
488                        termdata.decl_pos.pretty_print_line(&self.files)
489                    ),
490                }
491            };
492
493            let scope = ctx.enter_scope();
494            self.emit_block(&mut ctx, &root, sig.ret_kind, &last_expr, scope)?;
495        }
496        Ok(())
497    }
498
499    fn ty(&self, typeid: TypeId) -> (bool, String) {
500        let ty = &self.typeenv.types[typeid.index()];
501        let name = ty.name(self.typeenv);
502        let is_ref = match ty {
503            Type::Builtin(_) | Type::Primitive(..) => false,
504            Type::Enum { .. } => true,
505        };
506        (is_ref, String::from(name))
507    }
508
509    fn validate_block(ret_kind: ReturnKind, block: &Block) -> Nested<'_> {
510        if !matches!(ret_kind, ReturnKind::Iterator) {
511            // Loops are only allowed if we're returning an iterator.
512            assert!(
513                !block
514                    .steps
515                    .iter()
516                    .any(|c| matches!(c.check, ControlFlow::Loop { .. }))
517            );
518
519            // Unless we're returning an iterator, a case which returns a result must be the last
520            // case in a block.
521            if let Some(result_pos) = block
522                .steps
523                .iter()
524                .position(|c| matches!(c.check, ControlFlow::Return { .. }))
525            {
526                assert_eq!(block.steps.len() - 1, result_pos);
527            }
528        }
529
530        Nested::Cases(block.steps.iter())
531    }
532
533    fn emit_block<W: Write>(
534        &self,
535        ctx: &mut BodyContext<W>,
536        block: &Block,
537        ret_kind: ReturnKind,
538        last_expr: &str,
539        scope: StableSet<BindingId>,
540    ) -> std::fmt::Result {
541        let mut stack = Vec::new();
542        ctx.begin_block()?;
543        stack.push((Self::validate_block(ret_kind, block), last_expr, scope));
544
545        while let Some((mut nested, last_line, scope)) = stack.pop() {
546            match &mut nested {
547                Nested::Cases(cases) => {
548                    let Some(case) = cases.next() else {
549                        ctx.end_block(last_line, scope)?;
550                        continue;
551                    };
552                    // Iterator isn't done, put it back on the stack.
553                    stack.push((nested, last_line, scope));
554
555                    for &expr in case.bind_order.iter() {
556                        let iter_return = match &ctx.ruleset.bindings[expr.index()] {
557                            Binding::Extractor { term, .. } => {
558                                let termdata = &self.termenv.terms[term.index()];
559                                let sig = termdata.extractor_sig(self.typeenv).unwrap();
560                                if sig.ret_kind == ReturnKind::Iterator {
561                                    if termdata.has_external_extractor() {
562                                        Some(format!("C::{}_returns", sig.func_name))
563                                    } else {
564                                        Some(format!("ContextIterWrapper::<ConstructorVec<_>, _>"))
565                                    }
566                                } else {
567                                    None
568                                }
569                            }
570                            Binding::Constructor { term, .. } => {
571                                let termdata = &self.termenv.terms[term.index()];
572                                let sig = termdata.constructor_sig(self.typeenv).unwrap();
573                                if sig.ret_kind == ReturnKind::Iterator {
574                                    if termdata.has_external_constructor() {
575                                        Some(format!("C::{}_returns", sig.func_name))
576                                    } else {
577                                        Some(format!("ContextIterWrapper::<ConstructorVec<_>, _>"))
578                                    }
579                                } else {
580                                    None
581                                }
582                            }
583                            _ => None,
584                        };
585                        if let Some(ty) = iter_return {
586                            writeln!(
587                                ctx.out,
588                                "{}let mut v{} = {}::default();",
589                                &ctx.indent,
590                                expr.index(),
591                                ty
592                            )?;
593                            write!(ctx.out, "{}", &ctx.indent)?;
594                        } else {
595                            write!(ctx.out, "{}let v{} = ", &ctx.indent, expr.index())?;
596                        }
597                        self.emit_expr(ctx, expr)?;
598                        writeln!(ctx.out, ";")?;
599                        ctx.is_bound.insert(expr);
600                    }
601
602                    match &case.check {
603                        // Use a shorthand notation if there's only one match arm.
604                        ControlFlow::Match { source, arms } if arms.len() == 1 => {
605                            let arm = &arms[0];
606                            let scope = ctx.enter_scope();
607                            match arm.constraint {
608                                Constraint::ConstBool { .. }
609                                | Constraint::ConstInt { .. }
610                                | Constraint::ConstPrim { .. } => {
611                                    write!(ctx.out, "{}if ", &ctx.indent)?;
612                                    self.emit_expr(ctx, *source)?;
613                                    write!(ctx.out, " == ")?;
614                                    self.emit_constraint(ctx, *source, arm)?;
615                                }
616                                Constraint::Variant { .. } | Constraint::Some => {
617                                    write!(ctx.out, "{}if let ", &ctx.indent)?;
618                                    self.emit_constraint(ctx, *source, arm)?;
619                                    write!(ctx.out, " = ")?;
620                                    self.emit_source(ctx, *source, arm.constraint)?;
621                                }
622                            }
623                            ctx.begin_block()?;
624                            stack.push((Self::validate_block(ret_kind, &arm.body), "", scope));
625                        }
626
627                        ControlFlow::Match { source, arms } => {
628                            let scope = ctx.enter_scope();
629                            write!(ctx.out, "{}match ", &ctx.indent)?;
630                            self.emit_source(ctx, *source, arms[0].constraint)?;
631                            ctx.begin_block()?;
632
633                            // Always add a catchall arm, because we
634                            // don't do exhaustiveness checking on the
635                            // match arms.
636                            stack.push((Nested::Arms(*source, arms.iter()), "_ => {}", scope));
637                        }
638
639                        ControlFlow::Equal { a, b, body } => {
640                            let scope = ctx.enter_scope();
641                            write!(ctx.out, "{}if ", &ctx.indent)?;
642                            self.emit_expr(ctx, *a)?;
643                            write!(ctx.out, " == ")?;
644                            self.emit_expr(ctx, *b)?;
645                            ctx.begin_block()?;
646                            stack.push((Self::validate_block(ret_kind, body), "", scope));
647                        }
648
649                        ControlFlow::Loop { result, body } => {
650                            let source = match &ctx.ruleset.bindings[result.index()] {
651                                Binding::Iterator { source } => source,
652                                _ => unreachable!("Loop from a non-Iterator"),
653                            };
654                            let scope = ctx.enter_scope();
655
656                            writeln!(
657                                ctx.out,
658                                "{}let mut v{} = v{}.into_context_iter();",
659                                &ctx.indent,
660                                source.index(),
661                                source.index(),
662                            )?;
663
664                            write!(
665                                ctx.out,
666                                "{}while let Some(v{}) = v{}.next(ctx)",
667                                &ctx.indent,
668                                result.index(),
669                                source.index()
670                            )?;
671                            ctx.is_bound.insert(*result);
672                            ctx.begin_block()?;
673                            stack.push((Self::validate_block(ret_kind, body), "", scope));
674                        }
675
676                        &ControlFlow::Return { pos, result } => {
677                            writeln!(
678                                ctx.out,
679                                "{}// Rule at {}.",
680                                &ctx.indent,
681                                pos.pretty_print_line(&self.files)
682                            )?;
683                            if ctx.emit_logging {
684                                // Produce a valid Rust string literal with escapes.
685                                let pp = pos.pretty_print_line(&self.files);
686                                writeln!(
687                                    ctx.out,
688                                    "{}log::debug!(\"ISLE {{}} {{}}\", {:?}, {:?});",
689                                    &ctx.indent, ctx.term_name, pp
690                                )?;
691                            }
692                            write!(ctx.out, "{}", &ctx.indent)?;
693                            match ret_kind {
694                                ReturnKind::Plain | ReturnKind::Option => {
695                                    write!(ctx.out, "return ")?
696                                }
697                                ReturnKind::Iterator => write!(ctx.out, "returns.extend(Some(")?,
698                            }
699                            self.emit_expr(ctx, result)?;
700                            if ctx.is_ref.contains(&result) {
701                                write!(ctx.out, ".clone()")?;
702                            }
703                            match ret_kind {
704                                ReturnKind::Plain | ReturnKind::Option => writeln!(ctx.out, ";")?,
705                                ReturnKind::Iterator => {
706                                    writeln!(ctx.out, "));")?;
707                                    writeln!(
708                                        ctx.out,
709                                        "{}if returns.len() >= MAX_ISLE_RETURNS {{ return; }}",
710                                        ctx.indent
711                                    )?;
712                                }
713                            }
714                        }
715                    }
716                }
717
718                Nested::Arms(source, arms) => {
719                    let Some(arm) = arms.next() else {
720                        ctx.end_block(last_line, scope)?;
721                        continue;
722                    };
723                    let source = *source;
724                    // Iterator isn't done, put it back on the stack.
725                    stack.push((nested, last_line, scope));
726
727                    let scope = ctx.enter_scope();
728                    write!(ctx.out, "{}", &ctx.indent)?;
729                    self.emit_constraint(ctx, source, arm)?;
730                    write!(ctx.out, " =>")?;
731                    ctx.begin_block()?;
732                    stack.push((Self::validate_block(ret_kind, &arm.body), "", scope));
733                }
734            }
735        }
736
737        Ok(())
738    }
739
740    fn emit_expr<W: Write>(&self, ctx: &mut BodyContext<W>, result: BindingId) -> std::fmt::Result {
741        if ctx.is_bound.contains(&result) {
742            return write!(ctx.out, "v{}", result.index());
743        }
744
745        let binding = &ctx.ruleset.bindings[result.index()];
746
747        let mut call =
748            |term: TermId,
749             parameters: &[BindingId],
750
751             get_sig: fn(&Term, &TypeEnv) -> Option<ExternalSig>| {
752                let termdata = &self.termenv.terms[term.index()];
753                let sig = get_sig(termdata, self.typeenv).unwrap();
754                if let &[ret_ty] = &sig.ret_tys[..] {
755                    let (is_ref, _) = self.ty(ret_ty);
756                    if is_ref {
757                        ctx.set_ref(result, true);
758                        write!(ctx.out, "&")?;
759                    }
760                }
761                write!(ctx.out, "{}(ctx", sig.full_name)?;
762                debug_assert_eq!(parameters.len(), sig.param_tys.len());
763                for (&parameter, &arg_ty) in parameters.iter().zip(sig.param_tys.iter()) {
764                    let (is_ref, _) = self.ty(arg_ty);
765                    write!(ctx.out, ", ")?;
766                    let (before, after) = match (is_ref, ctx.is_ref.contains(&parameter)) {
767                        (false, true) => ("", ".clone()"),
768                        (true, false) => ("&", ""),
769                        _ => ("", ""),
770                    };
771                    write!(ctx.out, "{before}")?;
772                    self.emit_expr(ctx, parameter)?;
773                    write!(ctx.out, "{after}")?;
774                }
775                if let ReturnKind::Iterator = sig.ret_kind {
776                    write!(ctx.out, ", &mut v{}", result.index())?;
777                }
778                write!(ctx.out, ")")
779            };
780
781        match binding {
782            &Binding::ConstBool { val, .. } => self.emit_bool(ctx, val),
783            &Binding::ConstInt { val, ty } => self.emit_int(ctx, val, ty),
784            Binding::ConstPrim { val } => write!(ctx.out, "{}", &self.typeenv.syms[val.index()]),
785            Binding::Argument { index } => write!(ctx.out, "arg{}", index.index()),
786            Binding::Extractor { term, parameter } => {
787                call(*term, std::slice::from_ref(parameter), Term::extractor_sig)
788            }
789            Binding::Constructor {
790                term, parameters, ..
791            } => call(*term, &parameters[..], Term::constructor_sig),
792
793            Binding::MakeVariant {
794                ty,
795                variant,
796                fields,
797            } => {
798                let (name, variants) = match &self.typeenv.types[ty.index()] {
799                    Type::Enum { name, variants, .. } => (name, variants),
800                    _ => unreachable!("MakeVariant with primitive type"),
801                };
802                let variant = &variants[variant.index()];
803                write!(
804                    ctx.out,
805                    "{}::{}",
806                    &self.typeenv.syms[name.index()],
807                    &self.typeenv.syms[variant.name.index()]
808                )?;
809                if !fields.is_empty() {
810                    ctx.begin_block()?;
811                    for (field, value) in variant.fields.iter().zip(fields.iter()) {
812                        write!(
813                            ctx.out,
814                            "{}{}: ",
815                            &ctx.indent,
816                            &self.typeenv.syms[field.name.index()],
817                        )?;
818                        self.emit_expr(ctx, *value)?;
819                        if ctx.is_ref.contains(value) {
820                            write!(ctx.out, ".clone()")?;
821                        }
822                        writeln!(ctx.out, ",")?;
823                    }
824                    ctx.end_block_without_newline()?;
825                }
826                Ok(())
827            }
828
829            &Binding::MakeSome { inner } => {
830                write!(ctx.out, "Some(")?;
831                self.emit_expr(ctx, inner)?;
832                write!(ctx.out, ")")
833            }
834            &Binding::MatchSome { source } => {
835                self.emit_expr(ctx, source)?;
836                write!(ctx.out, "?")
837            }
838            &Binding::MatchTuple { source, field } => {
839                self.emit_expr(ctx, source)?;
840                write!(ctx.out, ".{}", field.index())
841            }
842
843            // These are not supposed to happen. If they do, make the generated code fail to compile
844            // so this is easier to debug than if we panic during codegen.
845            &Binding::MatchVariant { source, field, .. } => {
846                self.emit_expr(ctx, source)?;
847                write!(ctx.out, ".{} /*FIXME*/", field.index())
848            }
849            &Binding::Iterator { source } => {
850                self.emit_expr(ctx, source)?;
851                write!(ctx.out, ".next() /*FIXME*/")
852            }
853        }
854    }
855
856    fn emit_source<W: Write>(
857        &self,
858        ctx: &mut BodyContext<W>,
859        source: BindingId,
860        constraint: Constraint,
861    ) -> std::fmt::Result {
862        if let Constraint::Variant { .. } = constraint {
863            if !ctx.is_ref.contains(&source) {
864                write!(ctx.out, "&")?;
865            }
866        }
867        self.emit_expr(ctx, source)
868    }
869
870    fn emit_constraint<W: Write>(
871        &self,
872        ctx: &mut BodyContext<W>,
873        source: BindingId,
874        arm: &MatchArm,
875    ) -> std::fmt::Result {
876        let MatchArm {
877            constraint,
878            bindings,
879            ..
880        } = arm;
881        for binding in bindings.iter() {
882            if let &Some(binding) = binding {
883                ctx.is_bound.insert(binding);
884            }
885        }
886        match *constraint {
887            Constraint::ConstBool { val, .. } => self.emit_bool(ctx, val),
888            Constraint::ConstInt { val, ty } => self.emit_int(ctx, val, ty),
889            Constraint::ConstPrim { val } => {
890                write!(ctx.out, "{}", &self.typeenv.syms[val.index()])
891            }
892            Constraint::Variant { ty, variant, .. } => {
893                let (name, variants) = match &self.typeenv.types[ty.index()] {
894                    Type::Enum { name, variants, .. } => (name, variants),
895                    _ => unreachable!("Variant constraint on primitive type"),
896                };
897                let variant = &variants[variant.index()];
898                write!(
899                    ctx.out,
900                    "&{}::{}",
901                    &self.typeenv.syms[name.index()],
902                    &self.typeenv.syms[variant.name.index()]
903                )?;
904                if !bindings.is_empty() {
905                    ctx.begin_block()?;
906                    let mut skipped_some = false;
907                    for (&binding, field) in bindings.iter().zip(variant.fields.iter()) {
908                        if let Some(binding) = binding {
909                            write!(
910                                ctx.out,
911                                "{}{}: ",
912                                &ctx.indent,
913                                &self.typeenv.syms[field.name.index()]
914                            )?;
915                            let (is_ref, _) = self.ty(field.ty);
916                            if is_ref {
917                                ctx.set_ref(binding, true);
918                                write!(ctx.out, "ref ")?;
919                            }
920                            writeln!(ctx.out, "v{},", binding.index())?;
921                        } else {
922                            skipped_some = true;
923                        }
924                    }
925                    if skipped_some {
926                        writeln!(ctx.out, "{}..", &ctx.indent)?;
927                    }
928                    ctx.end_block_without_newline()?;
929                }
930                Ok(())
931            }
932            Constraint::Some => {
933                write!(ctx.out, "Some(")?;
934                if let Some(binding) = bindings[0] {
935                    ctx.set_ref(binding, ctx.is_ref.contains(&source));
936                    write!(ctx.out, "v{}", binding.index())?;
937                } else {
938                    write!(ctx.out, "_")?;
939                }
940                write!(ctx.out, ")")
941            }
942        }
943    }
944
945    fn emit_bool<W: Write>(
946        &self,
947        ctx: &mut BodyContext<W>,
948        val: bool,
949    ) -> Result<(), std::fmt::Error> {
950        write!(ctx.out, "{val}")
951    }
952
953    fn emit_int<W: Write>(
954        &self,
955        ctx: &mut BodyContext<W>,
956        val: i128,
957        ty: TypeId,
958    ) -> Result<(), std::fmt::Error> {
959        let ty_data = &self.typeenv.types[ty.index()];
960        match ty_data {
961            Type::Builtin(BuiltinType::Int(ty)) if ty.is_signed() => write!(ctx.out, "{val}_{ty}"),
962            Type::Builtin(BuiltinType::Int(ty)) => write!(ctx.out, "{val:#x}_{ty}"),
963            _ => write!(ctx.out, "{val:#x}"),
964        }
965    }
966}