#![deny(missing_docs)]
use std::collections::{HashMap, HashSet};
use std::fmt::Write;
use t_ree::{
declaration::{Declaration, FunctionDefinition, Module, Newtype, Parameter, ParameterList},
expression::{Binding, Block, Expression, ExpressionKind, Statement},
types::{FloatWidth, IntWidth, Mutability, Signedness, Type},
};
fn element_byte_size(element: &Type) -> usize {
match element {
Type::Int(width, _) => width.byte_size(),
Type::Float(width) => width.byte_size(),
Type::Bool => 1,
Type::Pointer(..) => 8,
_ => 4,
}
}
pub fn compile(module: &Module) -> String {
let mut emitter = Emitter::new();
emitter.emit_module(module);
emitter.output
}
struct Emitter {
output: String,
indent_level: usize,
labels: HashMap<String, Vec<Parameter>>,
newtypes: HashMap<String, Type>,
auto_ref_variables: HashSet<String>,
pointer_param_variables: HashSet<String>,
label_param_names: HashMap<String, String>,
in_main: bool,
temp_counter: usize,
function_defers: Vec<Statement>,
shadow_scopes: Vec<HashMap<String, String>>,
shadow_counter: usize,
has_stdio: bool,
}
impl Emitter {
fn new() -> Self {
Self {
output: String::new(),
indent_level: 0,
labels: HashMap::new(),
newtypes: HashMap::new(),
auto_ref_variables: HashSet::new(),
pointer_param_variables: HashSet::new(),
label_param_names: HashMap::new(),
function_defers: Vec::new(),
in_main: false,
temp_counter: 0,
shadow_scopes: Vec::new(),
shadow_counter: 0,
has_stdio: false,
}
}
fn push(&mut self, text: &str) {
self.output.push_str(text);
}
fn newline(&mut self) {
self.output.push('\n');
}
fn indent(&mut self) {
for _ in 0..self.indent_level {
self.push(" ");
}
}
fn emit_module(&mut self, module: &Module) {
self.push("#include <stdint.h>\n");
self.push("#include <stdbool.h>\n");
self.push("#include <stddef.h>\n");
self.has_stdio = self.uses_print(module)
|| module.iter().any(|d| {
matches!(d, Declaration::Import(path) if path == "stdio.h" || path.ends_with("/stdio.h"))
});
if self.has_stdio {
self.push("#include <stdio.h>\n");
}
self.newline();
for declaration in module {
if let Declaration::Type(newtype) = declaration {
self.newtypes
.insert(newtype.name.clone(), newtype.inner_type.clone());
if matches!(&newtype.inner_type, Type::Tuple(fields) if !fields.is_empty())
|| matches!(&newtype.inner_type, Type::Enum(_))
{
writeln!(self.output, "struct {};", newtype.name).unwrap();
}
}
}
self.newline();
for declaration in module {
self.emit_declaration(declaration);
self.newline();
}
}
fn uses_print(&self, module: &Module) -> bool {
module.iter().any(|decl| {
if let Declaration::Function(func) = decl {
self.block_uses_print(&func.body)
} else {
false
}
})
}
fn block_uses_print(&self, block: &Block) -> bool {
block
.statements
.iter()
.any(|s| self.statement_uses_print(s))
|| block
.result
.as_ref()
.is_some_and(|e| self.expression_uses_print(e))
}
fn statement_uses_print(&self, statement: &Statement) -> bool {
match statement {
Statement::Expression(e) | Statement::Return(Some(e)) => self.expression_uses_print(e),
Statement::Let { value, .. } => self.expression_uses_print(value),
Statement::Assign(target, value) => {
self.expression_uses_print(target) || self.expression_uses_print(value)
}
Statement::Label {
initial_arguments, ..
} => initial_arguments
.iter()
.any(|e| self.expression_uses_print(e)),
Statement::Jump { arguments, .. } => {
arguments.iter().any(|e| self.expression_uses_print(e))
}
Statement::Return(None) | Statement::MultiReplace { .. } => false,
Statement::Defer(inner) => self.statement_uses_print(inner),
}
}
fn expression_uses_print(&self, expression: &Expression) -> bool {
match &expression.kind {
ExpressionKind::Print(_) => true,
ExpressionKind::Block(block) => self.block_uses_print(block),
ExpressionKind::If {
condition,
then_branch,
else_branch,
} => {
self.expression_uses_print(condition)
|| self.block_uses_print(then_branch)
|| else_branch
.as_ref()
.is_some_and(|b| self.block_uses_print(b))
}
ExpressionKind::Match { value, arms } => {
self.expression_uses_print(value)
|| arms.iter().any(|arm| self.block_uses_print(&arm.body))
}
ExpressionKind::Call(callee, args) => {
self.expression_uses_print(callee)
|| args.iter().any(|e| self.expression_uses_print(e))
}
ExpressionKind::BinaryOperation(_, left, right) => {
self.expression_uses_print(left) || self.expression_uses_print(right)
}
ExpressionKind::UnaryOperation(_, operand)
| ExpressionKind::Dereference(operand)
| ExpressionKind::Convert(operand, _)
| ExpressionKind::Transmute(operand, _) => self.expression_uses_print(operand),
ExpressionKind::Replace(target, value) => {
self.expression_uses_print(target) || self.expression_uses_print(value)
}
_ => false,
}
}
fn emit_type(&mut self, ty: &Type) {
match ty {
Type::Never => self.push("void"),
Type::Bool => self.push("bool"),
Type::Int(IntWidth::WSize, Signedness::Unsigned) => self.push("size_t"),
Type::Int(IntWidth::WSize, Signedness::Signed) => self.push("ptrdiff_t"),
Type::Int(width, signedness) => {
if matches!(signedness, Signedness::Unsigned) {
self.push("u");
}
write!(self.output, "int{}_t", width.bit_size()).unwrap();
}
Type::Float(FloatWidth::W32) => self.push("float"),
Type::Float(FloatWidth::W64) => self.push("double"),
Type::Pointer(mutability, inner) => {
if matches!(mutability, Mutability::Shared) {
self.push("const ");
}
self.emit_type(inner);
self.push(" *");
}
Type::Array(element, size) => {
self.emit_type(element);
write!(self.output, "[{size}]").unwrap();
}
Type::Vector(element, lanes) => {
let byte_size = lanes * element_byte_size(element);
self.emit_type(element);
write!(self.output, " __attribute__((vector_size({byte_size})))").unwrap();
}
Type::Slice(mutability, inner) => {
self.push("struct { ");
if matches!(mutability, Mutability::Shared) {
self.push("const ");
}
self.emit_type(inner);
self.push(" *data; size_t length; }");
}
Type::Tuple(fields) if fields.is_empty() => self.push("void"),
Type::Tuple(fields) => {
self.push("struct { ");
for (index, field) in fields.iter().enumerate() {
self.emit_type(field);
write!(self.output, " _{index}; ").unwrap();
}
self.push("}");
}
Type::Enum(variants) => {
self.push("struct { uint8_t tag; union { ");
for variant in variants {
self.emit_type(variant);
if let Type::Named(name) = variant {
write!(self.output, " {name}; ").unwrap();
}
}
self.push("} value; }");
}
Type::Named(name) => {
let needs_struct = self.newtypes.get(name).is_some_and(|inner| {
matches!(inner, Type::Tuple(fields) if !fields.is_empty())
|| matches!(inner, Type::Enum(_))
|| matches!(inner, Type::Array(..))
});
if needs_struct {
self.push("struct ");
}
self.push(name);
}
Type::Function(signature) => {
self.emit_type(&signature.return_type);
self.push(" (*)(");
for (index, param) in signature.parameters.iter().enumerate() {
if index > 0 {
self.push(", ");
}
self.emit_type(param);
}
self.push(")");
}
}
}
fn emit_typed_name(&mut self, ty: &Type, name: &str) {
match ty {
Type::Array(element, size) => {
self.emit_type(element);
write!(self.output, " {name}[{size}]").unwrap();
}
Type::Function(signature) => {
self.emit_type(&signature.return_type);
write!(self.output, " (*{name})(").unwrap();
for (index, param) in signature.parameters.iter().enumerate() {
if index > 0 {
self.push(", ");
}
self.emit_type(param);
}
self.push(")");
}
_ => {
self.emit_type(ty);
write!(self.output, " {name}").unwrap();
}
}
}
fn emit_parameter_list(&mut self, params: &ParameterList) {
self.push("(");
let parameters = params.parameters();
if parameters.is_empty() {
self.push("void");
} else {
self.emit_parameters(parameters);
if matches!(params, ParameterList::Variadic(_)) {
self.push(", ...");
}
}
self.push(")");
}
fn emit_parameters(&mut self, parameters: &[Parameter]) {
for (index, param) in parameters.iter().enumerate() {
if index > 0 {
self.push(", ");
}
if let Some(ty) = ¶m.parameter_type {
let c_name = if self.newtypes.contains_key(¶m.name)
&& *ty != Type::Named(param.name.clone())
{
format!("_{}", param.name)
} else {
param.name.clone()
};
if c_name != param.name
&& let Some(scope) = self.shadow_scopes.last_mut()
{
scope.insert(param.name.clone(), c_name.clone());
}
self.emit_typed_name(ty, &c_name);
if matches!(ty, Type::Pointer(..))
|| self
.newtypes
.get(¶m.name)
.is_some_and(|inner| matches!(inner, Type::Pointer(..)))
{
self.auto_ref_variables.insert(c_name.clone());
self.pointer_param_variables.insert(c_name);
}
}
}
}
fn emit_declaration(&mut self, declaration: &Declaration) {
match declaration {
Declaration::Type(newtype) => {
self.newtypes
.insert(newtype.name.clone(), newtype.inner_type.clone());
self.emit_newtype(newtype);
}
Declaration::Function(function) => self.emit_function(function, function.public),
Declaration::Extern(extern_function) => {
if !(self.has_stdio && Self::is_stdio_function(&extern_function.name)) {
self.emit_type(&extern_function.return_type);
write!(self.output, " {}", extern_function.name).unwrap();
self.emit_parameter_list(&extern_function.parameters);
self.push(";\n");
}
}
Declaration::Constant(constant) => {
if !constant.public {
self.push("static ");
}
self.push("const ");
self.emit_typed_name(&constant.constant_type, &constant.name);
self.push(" = ");
self.emit_expression(&constant.value);
self.push(";\n");
}
Declaration::Import(path) => {
if !(self.has_stdio && (path == "stdio.h" || path.ends_with("/stdio.h"))) {
writeln!(self.output, "#include \"{path}\"").unwrap();
}
}
}
}
fn emit_newtype(&mut self, newtype: &Newtype) {
match &newtype.inner_type {
Type::Tuple(fields) if !fields.is_empty() => {
writeln!(self.output, "struct {} {{", newtype.name).unwrap();
self.indent_level += 1;
for field_type in fields {
self.indent();
let field_name = if let Type::Named(name) = field_type {
name.clone()
} else {
format!("_{}", self.output.len())
};
self.emit_typed_name(field_type, &field_name);
self.push(";\n");
}
self.indent_level -= 1;
self.push("};\n");
}
Type::Enum(variants) => {
writeln!(self.output, "struct {} {{", newtype.name).unwrap();
self.indent_level += 1;
self.indent();
self.push("uint8_t tag;\n");
self.indent();
self.push("union {\n");
self.indent_level += 1;
for variant in variants {
self.indent();
let variant_name = if let Type::Named(name) = variant {
name.clone()
} else {
format!("_{}", self.output.len())
};
self.emit_typed_name(variant, &variant_name);
self.push(";\n");
}
self.indent_level -= 1;
self.indent();
self.push("} value;\n");
self.indent_level -= 1;
self.push("};\n");
}
Type::Array(element, size) => {
writeln!(self.output, "struct {} {{", newtype.name).unwrap();
self.indent_level += 1;
self.indent();
self.emit_type(element);
writeln!(self.output, " data[{size}];").unwrap();
self.indent_level -= 1;
self.push("};\n");
}
_ => {
self.push("typedef ");
self.emit_typed_name(&newtype.inner_type, &newtype.name);
self.push(";\n");
}
}
}
fn emit_label_declarations(&mut self) {
for (label_name, params) in &self.labels.clone() {
for param in params {
if let Some(ty) = ¶m.parameter_type {
let qualified = format!("{label_name}_{}", param.name);
self.indent();
self.emit_typed_name(ty, &qualified);
self.push(";\n");
}
}
}
}
fn register_label(&mut self, name: &str, parameters: &[Parameter]) {
self.labels.insert(name.to_string(), parameters.to_vec());
for param in parameters {
self.label_param_names
.insert(param.name.clone(), format!("{name}_{}", param.name));
}
}
fn collect_labels(&mut self, block: &Block) {
for statement in &block.statements {
self.collect_labels_statement(statement);
}
if let Some(result) = &block.result {
self.collect_labels_expression(result);
}
}
fn collect_labels_statement(&mut self, statement: &Statement) {
match statement {
Statement::Label {
name, parameters, ..
} => self.register_label(name, parameters),
Statement::Expression(expression) => self.collect_labels_expression(expression),
Statement::Defer(inner) => self.collect_labels_statement(inner),
_ => {}
}
}
fn collect_labels_expression(&mut self, expression: &Expression) {
match &expression.kind {
ExpressionKind::Block(block) => self.collect_labels(block),
ExpressionKind::If {
then_branch,
else_branch,
..
} => {
self.collect_labels(then_branch);
if let Some(else_block) = else_branch {
self.collect_labels(else_block);
}
}
ExpressionKind::Match { arms, .. } => {
for arm in arms {
self.collect_labels(&arm.body);
}
}
_ => {}
}
}
fn push_shadow_scope(&mut self) {
self.shadow_scopes.push(HashMap::new());
}
fn pop_shadow_scope(&mut self) {
self.shadow_scopes.pop();
}
fn register_shadow(&mut self, name: &str) -> String {
let already_declared = self
.shadow_scopes
.iter()
.any(|scope| scope.contains_key(name));
let emit_name = if already_declared {
self.shadow_counter += 1;
format!("{name}${}", self.shadow_counter)
} else {
name.into()
};
if let Some(scope) = self.shadow_scopes.last_mut() {
scope.insert(name.into(), emit_name.clone());
}
emit_name
}
fn resolve_shadow<'a>(&'a self, name: &'a str) -> &'a str {
for scope in self.shadow_scopes.iter().rev() {
if let Some(resolved) = scope.get(name) {
return resolved;
}
}
name
}
fn emit_function(&mut self, function: &FunctionDefinition, public: bool) {
self.labels.clear();
self.auto_ref_variables.clear();
self.pointer_param_variables.clear();
self.label_param_names.clear();
self.shadow_scopes.clear();
self.shadow_counter = 0;
self.push_shadow_scope();
self.collect_labels(&function.body);
let is_main = function.name == "main";
let is_void = function.return_type == Type::Tuple(Vec::new());
if !is_main && !public {
self.push("static ");
}
if is_main {
self.push("int32_t");
} else {
self.emit_type(&function.return_type);
}
write!(self.output, " {}(", function.name).unwrap();
if function.parameters.is_empty() {
self.push("void");
} else {
self.emit_parameters(&function.parameters);
}
self.push(") {\n");
self.indent_level += 1;
self.in_main = is_main;
self.emit_label_declarations();
let has_result = function.body.result.is_some();
if is_void || (is_main && !has_result) {
self.emit_function_body_void(&function.body);
} else {
self.emit_function_body(&function.body);
}
let ends_with_return = function
.body
.statements
.last()
.is_some_and(|s| matches!(s, Statement::Return(_)));
if is_main && (is_void || !has_result) && !ends_with_return {
self.indent();
self.push("return 0;\n");
}
self.in_main = false;
self.indent_level -= 1;
self.push("}\n");
}
fn emit_function_body(&mut self, block: &Block) {
self.function_defers = Self::collect_defers(&block.statements)
.into_iter()
.cloned()
.collect();
for statement in &block.statements {
self.emit_statement(statement);
}
let defers = std::mem::take(&mut self.function_defers);
for defer in defers.iter().rev() {
self.emit_statement(defer);
}
if let Some(result) = &block.result {
self.indent();
self.push("return ");
self.emit_expression(result);
self.push(";\n");
}
self.function_defers.clear();
}
fn emit_function_body_void(&mut self, block: &Block) {
self.function_defers = Self::collect_defers(&block.statements)
.into_iter()
.cloned()
.collect();
for statement in &block.statements {
self.emit_statement(statement);
}
if let Some(result) = &block.result {
self.indent();
self.emit_expression(result);
self.push(";\n");
}
let defers = std::mem::take(&mut self.function_defers);
for defer in defers.iter().rev() {
self.emit_statement(defer);
}
}
fn emit_block_content(&mut self, block: &Block) {
let defers = Self::collect_defers(&block.statements);
for statement in &block.statements {
self.emit_statement(statement);
}
if let Some(result) = &block.result {
self.indent();
self.emit_expression(result);
self.push(";\n");
}
self.emit_defers(&defers);
}
fn emit_statement(&mut self, statement: &Statement) {
self.indent();
match statement {
Statement::Expression(expression) => {
if let ExpressionKind::Replace(target, value) = &expression.kind {
self.emit_assign_target(target);
self.push(" = ");
self.emit_expression(value);
} else {
self.emit_expression(expression);
}
self.push(";\n");
}
Statement::Let {
name,
binding,
declared_type,
value,
} => {
let mut value_output = String::new();
std::mem::swap(&mut self.output, &mut value_output);
self.emit_expression(value);
let wrap_array_early = self.is_array_newtype(declared_type.as_ref())
&& matches!(value.kind, ExpressionKind::ArrayLiteral(_));
std::mem::swap(&mut self.output, &mut value_output);
let effective_name = if self.newtypes.contains_key(name) {
format!("_{name}")
} else {
name.clone()
};
let emit_name = self.register_shadow(&effective_name);
if effective_name != *name
&& let Some(scope) = self.shadow_scopes.last_mut()
{
scope.insert(name.clone(), emit_name.clone());
}
if matches!(binding, Binding::Variable | Binding::Reference) {
self.auto_ref_variables.insert(emit_name.clone());
}
let is_const = matches!(binding, Binding::Value | Binding::Reference);
let ty = declared_type.as_ref().or(value.resolved_type.as_ref());
if is_const && matches!(ty, Some(Type::Pointer(..))) {
self.emit_type(ty.unwrap());
write!(self.output, " const {emit_name}").unwrap();
} else {
if is_const {
self.push("const ");
}
if let Some(ty) = ty {
self.emit_typed_name(ty, &emit_name);
} else {
write!(self.output, "auto {emit_name}").unwrap();
}
}
self.push(" = ");
if wrap_array_early {
self.push("{");
}
self.push(&value_output);
if wrap_array_early {
self.push("}");
}
self.push(";\n");
}
Statement::Assign(target, value) => {
self.emit_assign_target(target);
self.push(" = ");
self.emit_expression(value);
self.push(";\n");
}
Statement::Return(value) => {
if !self.function_defers.is_empty() {
let defers = self.function_defers.clone();
for defer in defers.iter().rev() {
self.emit_statement(defer);
}
}
if let Some(value) = value {
self.push("return ");
self.emit_expression(value);
self.push(";\n");
} else if self.in_main {
self.push("return 0;\n");
} else {
self.push("return;\n");
}
}
Statement::Label {
name,
parameters,
initial_arguments,
} => {
for (param, arg) in parameters.iter().zip(initial_arguments) {
let qualified = format!("{name}_{}", param.name);
self.push(&qualified);
self.push(" = ");
self.emit_expression(arg);
self.push(";\n");
self.indent();
}
self.output.truncate(self.output.trim_end().len());
writeln!(self.output, "\n{name}:;").unwrap();
}
Statement::Jump { label, arguments } => {
if !arguments.is_empty() {
let Some(params) = self.labels.get(label).cloned() else {
eprintln!("internal error: label '{label}' not found in function");
return;
};
let base = self.temp_counter;
self.temp_counter += params.len();
for (index, (param, arg)) in params.iter().zip(arguments).enumerate() {
let temp_id = base + index;
if let Some(ty) = ¶m.parameter_type {
self.emit_type(ty);
}
write!(self.output, " _tmp_{temp_id} = ").unwrap();
self.emit_expression(arg);
self.push(";\n");
self.indent();
}
for (index, param) in params.iter().enumerate() {
let temp_id = base + index;
let qualified = self
.label_param_names
.get(¶m.name)
.cloned()
.unwrap_or_else(|| param.name.clone());
write!(self.output, "{qualified} = _tmp_{temp_id}").unwrap();
self.push(";\n");
self.indent();
}
}
writeln!(self.output, "goto {label};").unwrap();
}
Statement::MultiReplace {
bindings,
targets,
values,
} => {
let base = self.temp_counter;
self.temp_counter += values.len();
for (index, value) in values.iter().enumerate() {
let temp_id = base + index;
if let Some(ty) = &value.resolved_type {
self.emit_type(ty);
} else {
self.push("auto");
}
write!(self.output, " _swap_{temp_id} = ").unwrap();
self.emit_expression(value);
self.push(";\n");
self.indent();
}
for (index, binding) in bindings.iter().enumerate() {
if let Some((name, bind_mode)) = binding {
let is_const = matches!(bind_mode, Binding::Value | Binding::Reference);
if matches!(bind_mode, Binding::Variable | Binding::Reference) {
self.auto_ref_variables.insert(name.clone());
}
if is_const {
self.push("const ");
}
if let Some(ty) = &values[index].resolved_type {
self.emit_type(ty);
}
write!(self.output, " {name} = ").unwrap();
self.emit_place(&targets[index]);
self.push(";\n");
self.indent();
}
}
for (index, target) in targets.iter().enumerate() {
let temp_id = base + index;
self.emit_assign_target(target);
writeln!(self.output, " = _swap_{temp_id};").unwrap();
if index + 1 < targets.len() {
self.indent();
}
}
}
Statement::Defer(_) => {}
}
}
fn collect_defers(statements: &[Statement]) -> Vec<&Statement> {
let mut defers = Vec::new();
for statement in statements {
if let Statement::Defer(inner) = statement {
defers.push(inner.as_ref());
}
}
defers
}
fn emit_defers(&mut self, defers: &[&Statement]) {
for statement in defers.iter().rev() {
self.emit_statement(statement);
}
}
fn is_direct_auto_ref(&self, expression: &Expression) -> bool {
matches!(&expression.kind, ExpressionKind::Variable(name) if self.auto_ref_variables.contains(self.resolve_shadow(name)))
}
fn is_auto_ref_chain(&self, expression: &Expression) -> bool {
match &expression.kind {
ExpressionKind::Variable(name) => {
self.auto_ref_variables.contains(self.resolve_shadow(name))
}
ExpressionKind::Field(inner, _) | ExpressionKind::Index(inner, _) => {
self.is_auto_ref_chain(inner)
}
_ => false,
}
}
fn resolve_variable_name(&self, name: &str) -> String {
self.label_param_names
.get(name)
.cloned()
.unwrap_or_else(|| self.resolve_shadow(name).into())
}
fn is_pointer_param(&self, expression: &Expression) -> bool {
if let ExpressionKind::Variable(name) = &expression.kind {
let resolved = self.resolve_variable_name(name);
self.pointer_param_variables.contains(&resolved)
} else {
false
}
}
fn emit_assign_target(&mut self, expression: &Expression) {
if let ExpressionKind::Variable(name) = &expression.kind {
let resolved = self.resolve_variable_name(name);
if self.pointer_param_variables.contains(&resolved) {
write!(self.output, "(*{resolved})").unwrap();
return;
}
}
if let ExpressionKind::Dereference(inner) = &expression.kind
&& let ExpressionKind::Variable(name) = &inner.kind
{
let resolved = self.resolve_variable_name(name);
if self.pointer_param_variables.contains(&resolved) {
write!(self.output, "(*{resolved})").unwrap();
return;
}
}
self.emit_place(expression);
}
fn emit_place(&mut self, expression: &Expression) {
match &expression.kind {
ExpressionKind::Variable(name) => {
let resolved = self.resolve_variable_name(name);
self.push(&resolved);
}
ExpressionKind::Field(inner, field) => {
let effective_inner = if let ExpressionKind::Field(grandchild, inner_field) =
&inner.kind
{
if self.is_identity_downcast(grandchild.resolved_type.as_ref(), inner_field) {
grandchild.as_ref()
} else {
inner.as_ref()
}
} else {
inner.as_ref()
};
if self.is_identity_downcast(effective_inner.resolved_type.as_ref(), field) {
self.emit_place(effective_inner);
} else if self.is_typedef_downcast(effective_inner.resolved_type.as_ref()) {
self.push("(*(");
if let Some(resolved_type) = expression.resolved_type.as_ref() {
self.emit_type(resolved_type);
}
self.push("*)&");
self.emit_place(effective_inner);
self.push(")");
} else {
self.emit_place(effective_inner);
let use_arrow = if let ExpressionKind::Variable(name) = &effective_inner.kind {
let resolved = self.resolve_variable_name(name);
self.pointer_param_variables.contains(&resolved)
} else {
false
};
if use_arrow
|| (self.is_pointer_type(effective_inner.resolved_type.as_ref())
&& !self.is_direct_auto_ref(effective_inner))
{
self.push("->");
} else {
self.push(".");
}
self.push(field);
}
}
ExpressionKind::Index(array, index) => {
self.emit_place(array);
if self.is_array_newtype(array.resolved_type.as_ref()) {
self.push(".data");
}
self.push("[");
self.emit_expression(index);
self.push("]");
}
ExpressionKind::Dereference(inner)
if self.is_auto_ref_chain(inner) && !self.is_pointer_param(inner) =>
{
self.emit_place(inner);
}
ExpressionKind::Dereference(inner) => {
self.push("(*");
self.emit_expression(inner);
self.push(")");
}
_ => self.emit_expression(expression),
}
}
}
mod emit;
#[cfg(test)]
mod tests;