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