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