blob: 85896d999d414173f6aec2edc8c464f1f80d933c [file] [log] [blame]
//! Generate Rust code from a series of Sequences.
use crate::ir::{ExprInst, InstId, PatternInst, Value};
use crate::log;
use crate::sema::{ExternalSig, ReturnKind, TermEnv, TermId, Type, TypeEnv, TypeId, Variant};
use crate::trie::{TrieEdge, TrieNode, TrieSymbol};
use crate::{StableMap, StableSet};
use std::borrow::Cow;
use std::collections::BTreeMap;
use std::fmt::Write;
/// Options for code generation.
#[derive(Clone, Debug, Default)]
pub struct CodegenOptions {
/// Do not include the `#![allow(...)]` pragmas in the generated
/// source. Useful if it must be include!()'d elsewhere.
pub exclude_global_allow_pragmas: bool,
}
/// Emit Rust source code for the given type and term environments.
pub fn codegen(
typeenv: &TypeEnv,
termenv: &TermEnv,
tries: &BTreeMap<TermId, TrieNode>,
options: &CodegenOptions,
) -> String {
Codegen::compile(typeenv, termenv, tries).generate_rust(options)
}
#[derive(Clone, Debug)]
struct Codegen<'a> {
typeenv: &'a TypeEnv,
termenv: &'a TermEnv,
functions_by_term: &'a BTreeMap<TermId, TrieNode>,
}
#[derive(Clone, Debug, Default)]
struct BodyContext {
/// For each value: (is_ref, ty).
values: StableMap<Value, (bool, TypeId)>,
}
impl<'a> Codegen<'a> {
fn compile(
typeenv: &'a TypeEnv,
termenv: &'a TermEnv,
tries: &'a BTreeMap<TermId, TrieNode>,
) -> Codegen<'a> {
Codegen {
typeenv,
termenv,
functions_by_term: tries,
}
}
fn generate_rust(&self, options: &CodegenOptions) -> String {
let mut code = String::new();
self.generate_header(&mut code, options);
self.generate_ctx_trait(&mut code);
self.generate_internal_types(&mut code);
self.generate_internal_term_constructors(&mut code);
code
}
fn generate_header(&self, code: &mut String, options: &CodegenOptions) {
writeln!(code, "// GENERATED BY ISLE. DO NOT EDIT!").unwrap();
writeln!(code, "//").unwrap();
writeln!(
code,
"// Generated automatically from the instruction-selection DSL code in:",
)
.unwrap();
for file in &self.typeenv.filenames {
writeln!(code, "// - {}", file).unwrap();
}
if !options.exclude_global_allow_pragmas {
writeln!(
code,
"\n#![allow(dead_code, unreachable_code, unreachable_patterns)]"
)
.unwrap();
writeln!(
code,
"#![allow(unused_imports, unused_variables, non_snake_case, unused_mut)]"
)
.unwrap();
writeln!(
code,
"#![allow(irrefutable_let_patterns, unused_assignments, non_camel_case_types)]"
)
.unwrap();
}
writeln!(code, "\nuse super::*; // Pulls in all external types.").unwrap();
writeln!(code, "use std::marker::PhantomData;").unwrap();
}
fn generate_trait_sig(&self, code: &mut String, indent: &str, sig: &ExternalSig) {
let ret_tuple = format!(
"{open_paren}{rets}{close_paren}",
open_paren = if sig.ret_tys.len() != 1 { "(" } else { "" },
rets = sig
.ret_tys
.iter()
.map(|&ty| self.type_name(ty, /* by_ref = */ false))
.collect::<Vec<_>>()
.join(", "),
close_paren = if sig.ret_tys.len() != 1 { ")" } else { "" },
);
if sig.ret_kind == ReturnKind::Iterator {
writeln!(
code,
"{indent}type {name}_iter: ContextIter<Context = Self, Output = {output}>;",
indent = indent,
name = sig.func_name,
output = ret_tuple,
)
.unwrap();
}
let ret_ty = match sig.ret_kind {
ReturnKind::Plain => ret_tuple,
ReturnKind::Option => format!("Option<{}>", ret_tuple),
ReturnKind::Iterator => format!("Self::{}_iter", sig.func_name),
};
writeln!(
code,
"{indent}fn {name}(&mut self, {params}) -> {ret_ty};",
indent = indent,
name = sig.func_name,
params = sig
.param_tys
.iter()
.enumerate()
.map(|(i, &ty)| format!("arg{}: {}", i, self.type_name(ty, /* by_ref = */ true)))
.collect::<Vec<_>>()
.join(", "),
ret_ty = ret_ty,
)
.unwrap();
}
fn generate_ctx_trait(&self, code: &mut String) {
writeln!(code, "").unwrap();
writeln!(
code,
"/// Context during lowering: an implementation of this trait"
)
.unwrap();
writeln!(
code,
"/// must be provided with all external constructors and extractors."
)
.unwrap();
writeln!(
code,
"/// A mutable borrow is passed along through all lowering logic."
)
.unwrap();
writeln!(code, "pub trait Context {{").unwrap();
for term in &self.termenv.terms {
if term.has_external_extractor() {
let ext_sig = term.extractor_sig(self.typeenv).unwrap();
self.generate_trait_sig(code, " ", &ext_sig);
}
if term.has_external_constructor() {
let ext_sig = term.constructor_sig(self.typeenv).unwrap();
self.generate_trait_sig(code, " ", &ext_sig);
}
}
writeln!(code, "}}").unwrap();
writeln!(
code,
r#"
pub trait ContextIter {{
type Context;
type Output;
fn next(&mut self, ctx: &mut Self::Context) -> Option<Self::Output>;
}}
pub struct ContextIterWrapper<Item, I: Iterator < Item = Item>, C: Context> {{
iter: I,
_ctx: PhantomData<C>,
}}
impl<Item, I: Iterator<Item = Item>, C: Context> From<I> for ContextIterWrapper<Item, I, C> {{
fn from(iter: I) -> Self {{
Self {{ iter, _ctx: PhantomData }}
}}
}}
impl<Item, I: Iterator<Item = Item>, C: Context> ContextIter for ContextIterWrapper<Item, I, C> {{
type Context = C;
type Output = Item;
fn next(&mut self, _ctx: &mut Self::Context) -> Option<Self::Output> {{
self.iter.next()
}}
}}
"#,
)
.unwrap();
}
fn generate_internal_types(&self, code: &mut String) {
for ty in &self.typeenv.types {
match ty {
&Type::Enum {
name,
is_extern,
is_nodebug,
ref variants,
pos,
..
} if !is_extern => {
let name = &self.typeenv.syms[name.index()];
writeln!(
code,
"\n/// Internal type {}: defined at {}.",
name,
pos.pretty_print_line(&self.typeenv.filenames[..])
)
.unwrap();
// Generate the `derive`s.
let debug_derive = if is_nodebug { "" } else { ", Debug" };
if variants.iter().all(|v| v.fields.is_empty()) {
writeln!(
code,
"#[derive(Copy, Clone, PartialEq, Eq{})]",
debug_derive
)
.unwrap();
} else {
writeln!(code, "#[derive(Clone{})]", debug_derive).unwrap();
}
writeln!(code, "pub enum {} {{", name).unwrap();
for variant in variants {
let name = &self.typeenv.syms[variant.name.index()];
if variant.fields.is_empty() {
writeln!(code, " {},", name).unwrap();
} else {
writeln!(code, " {} {{", name).unwrap();
for field in &variant.fields {
let name = &self.typeenv.syms[field.name.index()];
let ty_name =
self.typeenv.types[field.ty.index()].name(&self.typeenv);
writeln!(code, " {}: {},", name, ty_name).unwrap();
}
writeln!(code, " }},").unwrap();
}
}
writeln!(code, "}}").unwrap();
}
_ => {}
}
}
}
fn type_name(&self, typeid: TypeId, by_ref: bool) -> String {
match &self.typeenv.types[typeid.index()] {
&Type::Primitive(_, sym, _) => self.typeenv.syms[sym.index()].clone(),
&Type::Enum { name, .. } => {
let r = if by_ref { "&" } else { "" };
format!("{}{}", r, self.typeenv.syms[name.index()])
}
}
}
fn value_name(&self, value: &Value) -> String {
match value {
&Value::Pattern { inst, output } => format!("pattern{}_{}", inst.index(), output),
&Value::Expr { inst, output } => format!("expr{}_{}", inst.index(), output),
}
}
fn ty_prim(&self, ty: TypeId) -> bool {
self.typeenv.types[ty.index()].is_prim()
}
fn value_binder(&self, value: &Value, is_ref: bool, ty: TypeId) -> String {
let prim = self.ty_prim(ty);
if prim || !is_ref {
format!("{}", self.value_name(value))
} else {
format!("ref {}", self.value_name(value))
}
}
fn value_by_ref(&self, value: &Value, ctx: &BodyContext) -> String {
let raw_name = self.value_name(value);
let &(is_ref, ty) = ctx.values.get(value).unwrap();
let prim = self.ty_prim(ty);
if is_ref || prim {
raw_name
} else {
format!("&{}", raw_name)
}
}
fn value_by_val(&self, value: &Value, ctx: &BodyContext) -> String {
let raw_name = self.value_name(value);
let &(is_ref, _) = ctx.values.get(value).unwrap();
if is_ref {
format!("{}.clone()", raw_name)
} else {
raw_name
}
}
fn define_val(&self, value: &Value, ctx: &mut BodyContext, is_ref: bool, ty: TypeId) {
let is_ref = !self.ty_prim(ty) && is_ref;
ctx.values.insert(value.clone(), (is_ref, ty));
}
fn const_int(&self, val: i128, ty: TypeId) -> String {
let is_bool = match &self.typeenv.types[ty.index()] {
&Type::Primitive(_, name, _) => &self.typeenv.syms[name.index()] == "bool",
_ => unreachable!(),
};
if is_bool {
format!("{}", val != 0)
} else {
let ty_name = self.type_name(ty, /* by_ref = */ false);
if ty_name == "i128" {
format!("{}i128", val)
} else {
format!("{}i128 as {}", val, ty_name)
}
}
}
fn generate_internal_term_constructors(&self, code: &mut String) {
for (&termid, trie) in self.functions_by_term {
let termdata = &self.termenv.terms[termid.index()];
// Skip terms that are enum variants or that have external
// constructors/extractors.
if !termdata.has_constructor() || termdata.has_external_constructor() {
continue;
}
let sig = termdata.constructor_sig(self.typeenv).unwrap();
let args = sig
.param_tys
.iter()
.enumerate()
.map(|(i, &ty)| format!("arg{}: {}", i, self.type_name(ty, true)))
.collect::<Vec<_>>()
.join(", ");
assert_eq!(sig.ret_tys.len(), 1);
let ret = self.type_name(sig.ret_tys[0], false);
let ret = match sig.ret_kind {
ReturnKind::Iterator => format!("impl ContextIter<Context = C, Output = {}>", ret),
ReturnKind::Option => format!("Option<{}>", ret),
ReturnKind::Plain => ret,
};
let term_name = &self.typeenv.syms[termdata.name.index()];
writeln!(
code,
"\n// Generated as internal constructor for term {}.",
term_name,
)
.unwrap();
writeln!(
code,
"pub fn {}<C: Context>(ctx: &mut C, {}) -> {} {{",
sig.func_name, args, ret,
)
.unwrap();
if sig.ret_kind == ReturnKind::Iterator {
writeln!(code, "let mut returns = ConstructorVec::new();").unwrap();
}
let mut body_ctx: BodyContext = Default::default();
let returned = self.generate_body(
code,
/* depth = */ 0,
trie,
" ",
&mut body_ctx,
sig.ret_kind,
);
if !returned {
let ret_expr = match sig.ret_kind {
ReturnKind::Plain => Cow::from(format!(
"unreachable!(\"no rule matched for term {{}} at {{}}; should it be partial?\", {:?}, {:?})",
term_name,
termdata
.decl_pos
.pretty_print_line(&self.typeenv.filenames[..])
)),
ReturnKind::Option => Cow::from("None"),
ReturnKind::Iterator => {
Cow::from("ContextIterWrapper::from(returns.into_iter())")
}
};
write!(code, " return {};", ret_expr).unwrap();
}
writeln!(code, "}}").unwrap();
}
}
fn generate_expr_inst(
&self,
code: &mut String,
id: InstId,
inst: &ExprInst,
indent: &str,
ctx: &mut BodyContext,
returns: &mut Vec<(usize, String)>,
) -> bool {
log!("generate_expr_inst: {:?}", inst);
let mut new_scope = false;
match inst {
&ExprInst::ConstInt { ty, val } => {
let value = Value::Expr {
inst: id,
output: 0,
};
self.define_val(&value, ctx, /* is_ref = */ false, ty);
let name = self.value_name(&value);
let ty_name = self.type_name(ty, /* by_ref = */ false);
writeln!(
code,
"{}let {}: {} = {};",
indent,
name,
ty_name,
self.const_int(val, ty)
)
.unwrap();
}
&ExprInst::ConstPrim { ty, val } => {
let value = Value::Expr {
inst: id,
output: 0,
};
self.define_val(&value, ctx, /* is_ref = */ false, ty);
let name = self.value_name(&value);
let ty_name = self.type_name(ty, /* by_ref = */ false);
writeln!(
code,
"{}let {}: {} = {};",
indent,
name,
ty_name,
self.typeenv.syms[val.index()],
)
.unwrap();
}
&ExprInst::CreateVariant {
ref inputs,
ty,
variant,
} => {
let variantinfo = match &self.typeenv.types[ty.index()] {
&Type::Primitive(..) => panic!("CreateVariant with primitive type"),
&Type::Enum { ref variants, .. } => &variants[variant.index()],
};
let mut input_fields = vec![];
for ((input_value, _), field) in inputs.iter().zip(variantinfo.fields.iter()) {
let field_name = &self.typeenv.syms[field.name.index()];
let value_expr = self.value_by_val(input_value, ctx);
input_fields.push(format!("{}: {}", field_name, value_expr));
}
let output = Value::Expr {
inst: id,
output: 0,
};
let outputname = self.value_name(&output);
let full_variant_name = format!(
"{}::{}",
self.type_name(ty, false),
self.typeenv.syms[variantinfo.name.index()]
);
if input_fields.is_empty() {
writeln!(
code,
"{}let {} = {};",
indent, outputname, full_variant_name
)
.unwrap();
} else {
writeln!(
code,
"{}let {} = {} {{",
indent, outputname, full_variant_name
)
.unwrap();
for input_field in input_fields {
writeln!(code, "{} {},", indent, input_field).unwrap();
}
writeln!(code, "{}}};", indent).unwrap();
}
self.define_val(&output, ctx, /* is_ref = */ false, ty);
}
&ExprInst::Construct {
ref inputs,
term,
infallible,
multi,
..
} => {
let mut input_exprs = vec![];
for (input_value, input_ty) in inputs {
let value_expr = if self.typeenv.types[input_ty.index()].is_prim() {
self.value_by_val(input_value, ctx)
} else {
self.value_by_ref(input_value, ctx)
};
input_exprs.push(value_expr);
}
let output = Value::Expr {
inst: id,
output: 0,
};
let outputname = self.value_name(&output);
let termdata = &self.termenv.terms[term.index()];
let sig = termdata.constructor_sig(self.typeenv).unwrap();
assert_eq!(input_exprs.len(), sig.param_tys.len());
if !multi {
let fallible_try = if infallible { "" } else { "?" };
writeln!(
code,
"{}let {} = {}(ctx, {}){};",
indent,
outputname,
sig.full_name,
input_exprs.join(", "),
fallible_try,
)
.unwrap();
} else {
writeln!(
code,
"{}let mut iter = {}(ctx, {});",
indent,
sig.full_name,
input_exprs.join(", "),
)
.unwrap();
writeln!(
code,
"{}while let Some({}) = iter.next(ctx) {{",
indent, outputname,
)
.unwrap();
new_scope = true;
}
self.define_val(&output, ctx, /* is_ref = */ false, termdata.ret_ty);
}
&ExprInst::Return {
index, ref value, ..
} => {
let value_expr = self.value_by_val(value, ctx);
returns.push((index, value_expr));
}
}
new_scope
}
fn match_variant_binders(
&self,
variant: &Variant,
arg_tys: &[TypeId],
id: InstId,
ctx: &mut BodyContext,
) -> Vec<String> {
arg_tys
.iter()
.zip(variant.fields.iter())
.enumerate()
.map(|(i, (&ty, field))| {
let value = Value::Pattern {
inst: id,
output: i,
};
let valuename = self.value_binder(&value, /* is_ref = */ true, ty);
let fieldname = &self.typeenv.syms[field.name.index()];
self.define_val(&value, ctx, /* is_ref = */ true, field.ty);
format!("{}: {}", fieldname, valuename)
})
.collect::<Vec<_>>()
}
/// Returns a `bool` indicating whether this pattern inst is
/// infallible, and the number of scopes opened.
fn generate_pattern_inst(
&self,
code: &mut String,
id: InstId,
inst: &PatternInst,
indent: &str,
ctx: &mut BodyContext,
) -> (bool, usize) {
match inst {
&PatternInst::Arg { index, ty } => {
let output = Value::Pattern {
inst: id,
output: 0,
};
let outputname = self.value_name(&output);
let is_ref = match &self.typeenv.types[ty.index()] {
&Type::Primitive(..) => false,
_ => true,
};
writeln!(code, "{}let {} = arg{};", indent, outputname, index).unwrap();
self.define_val(
&Value::Pattern {
inst: id,
output: 0,
},
ctx,
is_ref,
ty,
);
(true, 0)
}
&PatternInst::MatchEqual { ref a, ref b, .. } => {
let a = self.value_by_ref(a, ctx);
let b = self.value_by_ref(b, ctx);
writeln!(code, "{}if {} == {} {{", indent, a, b).unwrap();
(false, 1)
}
&PatternInst::MatchInt {
ref input,
int_val,
ty,
..
} => {
let int_val = self.const_int(int_val, ty);
let input = self.value_by_val(input, ctx);
writeln!(code, "{}if {} == {} {{", indent, input, int_val).unwrap();
(false, 1)
}
&PatternInst::MatchPrim { ref input, val, .. } => {
let input = self.value_by_val(input, ctx);
let sym = &self.typeenv.syms[val.index()];
writeln!(code, "{}if {} == {} {{", indent, input, sym).unwrap();
(false, 1)
}
&PatternInst::MatchVariant {
ref input,
input_ty,
variant,
ref arg_tys,
} => {
let input = self.value_by_ref(input, ctx);
let variants = match &self.typeenv.types[input_ty.index()] {
&Type::Primitive(..) => panic!("primitive type input to MatchVariant"),
&Type::Enum { ref variants, .. } => variants,
};
let ty_name = self.type_name(input_ty, /* is_ref = */ true);
let variant = &variants[variant.index()];
let variantname = &self.typeenv.syms[variant.name.index()];
let args = self.match_variant_binders(variant, &arg_tys[..], id, ctx);
let args = if args.is_empty() {
"".to_string()
} else {
format!("{{ {} }}", args.join(", "))
};
writeln!(
code,
"{}if let {}::{} {} = {} {{",
indent, ty_name, variantname, args, input
)
.unwrap();
(false, 1)
}
&PatternInst::Extract {
ref inputs,
ref output_tys,
term,
infallible,
multi,
..
} => {
let termdata = &self.termenv.terms[term.index()];
let sig = termdata.extractor_sig(self.typeenv).unwrap();
let input_values = inputs
.iter()
.map(|input| self.value_by_ref(input, ctx))
.collect::<Vec<_>>();
let output_binders = output_tys
.iter()
.enumerate()
.map(|(i, &ty)| {
let output_val = Value::Pattern {
inst: id,
output: i,
};
self.define_val(&output_val, ctx, /* is_ref = */ false, ty);
self.value_binder(&output_val, /* is_ref = */ false, ty)
})
.collect::<Vec<_>>();
let bind_pattern = format!(
"{open_paren}{vars}{close_paren}",
open_paren = if output_binders.len() == 1 { "" } else { "(" },
vars = output_binders.join(", "),
close_paren = if output_binders.len() == 1 { "" } else { ")" }
);
let etor_call = format!(
"{name}(ctx, {args})",
name = sig.full_name,
args = input_values.join(", ")
);
if multi {
writeln!(code, "{indent}let mut iter = {etor_call};").unwrap();
writeln!(
code,
"{indent}while let Some({bind_pattern}) = iter.next(ctx) {{",
)
.unwrap();
(false, 1)
} else if infallible {
writeln!(code, "{indent}let {bind_pattern} = {etor_call};").unwrap();
(true, 0)
} else {
writeln!(code, "{indent}if let Some({bind_pattern}) = {etor_call} {{").unwrap();
(false, 1)
}
}
&PatternInst::Expr {
ref seq, output_ty, ..
} if seq.is_const_int().is_some() => {
let (ty, val) = seq.is_const_int().unwrap();
assert_eq!(ty, output_ty);
let output = Value::Pattern {
inst: id,
output: 0,
};
writeln!(
code,
"{}let {} = {};",
indent,
self.value_name(&output),
self.const_int(val, ty),
)
.unwrap();
self.define_val(&output, ctx, /* is_ref = */ false, ty);
(true, 0)
}
&PatternInst::Expr {
ref seq, output_ty, ..
} => {
let closure_name = format!("closure{}", id.index());
writeln!(code, "{}let mut {} = || {{", indent, closure_name).unwrap();
let subindent = format!("{} ", indent);
let mut subctx = ctx.clone();
let mut returns = vec![];
for (id, inst) in seq.insts.iter().enumerate() {
let id = InstId(id);
let new_scope = self.generate_expr_inst(
code,
id,
inst,
&subindent,
&mut subctx,
&mut returns,
);
assert!(!new_scope);
}
assert_eq!(returns.len(), 1);
writeln!(code, "{}return Some({});", subindent, returns[0].1).unwrap();
writeln!(code, "{}}};", indent).unwrap();
let output = Value::Pattern {
inst: id,
output: 0,
};
writeln!(
code,
"{}if let Some({}) = {}() {{",
indent,
self.value_binder(&output, /* is_ref = */ false, output_ty),
closure_name
)
.unwrap();
self.define_val(&output, ctx, /* is_ref = */ false, output_ty);
(false, 1)
}
}
}
fn generate_body(
&self,
code: &mut String,
depth: usize,
trie: &TrieNode,
indent: &str,
ctx: &mut BodyContext,
ret_kind: ReturnKind,
) -> bool {
log!("generate_body:\n{}", trie.pretty());
let mut returned = false;
match trie {
&TrieNode::Empty => {}
&TrieNode::Leaf { ref output, .. } => {
writeln!(
code,
"{}// Rule at {}.",
indent,
output.pos.pretty_print_line(&self.typeenv.filenames[..])
)
.unwrap();
// If this is a leaf node, generate the ExprSequence and return.
let mut returns = vec![];
let mut scopes = 0;
let mut indent = indent.to_string();
let orig_indent = indent.clone();
for (id, inst) in output.insts.iter().enumerate() {
let id = InstId(id);
let new_scope =
self.generate_expr_inst(code, id, inst, &indent[..], ctx, &mut returns);
if new_scope {
scopes += 1;
indent.push_str(" ");
}
}
assert_eq!(returns.len(), 1);
let (before, after) = match ret_kind {
ReturnKind::Plain => ("return ", ""),
ReturnKind::Option => ("return Some(", ")"),
ReturnKind::Iterator => ("returns.push(", ")"),
};
writeln!(code, "{}{}{}{};", indent, before, returns[0].1, after).unwrap();
for _ in 0..scopes {
writeln!(code, "{}}}", orig_indent).unwrap();
}
returned = ret_kind != ReturnKind::Iterator;
}
&TrieNode::Decision { ref edges } => {
// If this is a decision node, generate each match op
// in turn (in priority order). Gather together
// adjacent MatchVariant ops with the same input and
// disjoint variants in order to create a `match`
// rather than a chain of if-lets.
let mut i = 0;
while i < edges.len() {
// Gather adjacent match variants so that we can turn these
// into a `match` rather than a sequence of `if let`s.
let mut last = i;
let mut adjacent_variants = StableSet::new();
let mut adjacent_variant_input = None;
log!(
"edge: prio = {:?}, symbol = {:?}",
edges[i].prio,
edges[i].symbol
);
while last < edges.len() {
match &edges[last].symbol {
&TrieSymbol::Match {
op: PatternInst::MatchVariant { input, variant, .. },
} => {
if adjacent_variant_input.is_none() {
adjacent_variant_input = Some(input);
}
if adjacent_variant_input == Some(input)
&& !adjacent_variants.contains(&variant)
{
adjacent_variants.insert(variant);
last += 1;
} else {
break;
}
}
_ => {
break;
}
}
}
// Now `edges[i..last]` is a run of adjacent `MatchVariants`
// (possibly an empty one). Only use a `match` form if there
// are at least two adjacent options.
if last - i > 1 {
self.generate_body_matches(
code,
depth,
&edges[i..last],
indent,
ctx,
ret_kind,
);
i = last;
continue;
} else {
let &TrieEdge {
ref symbol,
ref node,
..
} = &edges[i];
i += 1;
match symbol {
&TrieSymbol::EndOfMatch => {
returned = self.generate_body(
code,
depth + 1,
node,
indent,
ctx,
ret_kind,
);
}
&TrieSymbol::Match { ref op } => {
let id = InstId(depth);
let (infallible, new_scopes) =
self.generate_pattern_inst(code, id, op, indent, ctx);
let mut subindent = indent.to_string();
for _ in 0..new_scopes {
subindent.push_str(" ");
}
let sub_returned = self.generate_body(
code,
depth + 1,
node,
&subindent[..],
ctx,
ret_kind,
);
for _ in 0..new_scopes {
writeln!(code, "{}}}", indent).unwrap();
}
if infallible && sub_returned {
returned = true;
break;
}
}
}
}
}
}
}
returned
}
fn generate_body_matches(
&self,
code: &mut String,
depth: usize,
edges: &[TrieEdge],
indent: &str,
ctx: &mut BodyContext,
ret_kind: ReturnKind,
) {
let (input, input_ty) = match &edges[0].symbol {
&TrieSymbol::Match {
op:
PatternInst::MatchVariant {
input, input_ty, ..
},
} => (input, input_ty),
_ => unreachable!(),
};
let (input_ty_sym, variants) = match &self.typeenv.types[input_ty.index()] {
&Type::Enum {
ref name,
ref variants,
..
} => (name, variants),
_ => unreachable!(),
};
let input_ty_name = &self.typeenv.syms[input_ty_sym.index()];
// Emit the `match`.
writeln!(
code,
"{}match {} {{",
indent,
self.value_by_ref(&input, ctx)
)
.unwrap();
// Emit each case.
for &TrieEdge {
ref symbol,
ref node,
..
} in edges
{
let id = InstId(depth);
let (variant, arg_tys) = match symbol {
&TrieSymbol::Match {
op:
PatternInst::MatchVariant {
variant,
ref arg_tys,
..
},
} => (variant, arg_tys),
_ => unreachable!(),
};
let variantinfo = &variants[variant.index()];
let variantname = &self.typeenv.syms[variantinfo.name.index()];
let fields = self.match_variant_binders(variantinfo, arg_tys, id, ctx);
let fields = if fields.is_empty() {
"".to_string()
} else {
format!("{{ {} }}", fields.join(", "))
};
writeln!(
code,
"{} &{}::{} {} => {{",
indent, input_ty_name, variantname, fields,
)
.unwrap();
let subindent = format!("{} ", indent);
self.generate_body(code, depth + 1, node, &subindent, ctx, ret_kind);
writeln!(code, "{} }}", indent).unwrap();
}
// Always add a catchall, because we don't do exhaustiveness
// checking on the MatchVariants.
writeln!(code, "{} _ => {{}}", indent).unwrap();
writeln!(code, "{}}}", indent).unwrap();
}
}