use rustc_hash::FxHashSet as HashSet;
use crate::Emitter;
use crate::go::types::emitter::Position;
use crate::go::utils::{group_params, optimize_function_body, requires_temp_var};
use crate::go::write_line;
use syntax::ast::{Binding, Expression, Pattern, TypedPattern};
use syntax::types::Type;
impl Emitter<'_> {
pub(crate) fn emit_function_body(
&mut self,
output: &mut String,
body: &Expression,
should_return: bool,
) {
let items: &[Expression] = if let Expression::Block { items, .. } = body {
items
} else {
std::slice::from_ref(body)
};
let Some((last, rest)) = items.split_last() else {
return;
};
for item in rest {
self.emit_statement(output, item);
}
let is_statement_only = matches!(
last,
Expression::Assignment { .. } | Expression::Let { .. } | Expression::Const { .. }
);
let needs_return = should_return
&& !matches!(last, Expression::Return { .. })
&& !is_statement_only
&& !last.get_type().is_unit()
&& !last.get_type().is_never();
if !needs_return {
self.emit_statement(output, last);
if should_return && last.get_type().is_never() && !Self::is_go_never(last) {
output.push_str("panic(\"unreachable\")\n");
}
if should_return
&& is_statement_only
&& self
.current_return_context
.as_ref()
.is_some_and(|ty| !ty.is_unit())
{
let return_ty = self.current_return_context.as_ref().unwrap();
let zero = self.zero_value(return_ty);
write_line!(output, "return {}", zero);
}
return;
}
if self.emit_wrapped_return(output, last) {
return;
}
self.with_position(Position::Tail, |this| {
if !requires_temp_var(last) {
let expression = this.emit_value(output, last);
output.push_str(&this.wrap_value(&expression));
} else {
match last {
Expression::If { .. }
| Expression::Match { .. }
| Expression::Select { .. } => {
this.emit_branching_directly(output, last);
}
Expression::IfLet { .. } => {
unreachable!("IfLet should be desugared to Match before emit")
}
Expression::Block { .. }
| Expression::Loop { .. }
| Expression::Propagate { .. } => {
let expression = this.emit_operand(output, last);
output.push_str(&this.wrap_value(&expression));
}
_ => unreachable!("requires_temp_var returned true for unexpected expression"),
}
}
});
}
pub(crate) fn emit_lambda(
&mut self,
params: &[Binding],
body: &Expression,
ty: &Type,
) -> String {
let saved_declared = std::mem::take(&mut self.scope.declared);
let saved_scope_depth = self.scope.scope_depth;
self.scope.declared = vec![HashSet::default()];
self.scope.scope_depth = 0;
self.scope.bindings.save();
let mut destructure_bindings: Vec<(String, &Pattern, Option<&TypedPattern>)> = vec![];
let param_pairs: Vec<(String, String)> = params
.iter()
.map(|p| {
let name = if let Pattern::Identifier { identifier, .. } = &p.pattern {
if let Some(go_name) = self.go_name_for_binding(&p.pattern) {
let go_id = self.scope.bindings.add(identifier, go_name);
self.declare(&go_id);
go_id
} else {
self.scope.bindings.add(identifier, "_");
"_".to_string()
}
} else if matches!(&p.pattern, Pattern::WildCard { .. }) {
"_".to_string()
} else {
let temp_name = self.fresh_var(Some("arg"));
self.declare(&temp_name);
destructure_bindings.push((
temp_name.clone(),
&p.pattern,
p.typed_pattern.as_ref(),
));
temp_name
};
(name, self.go_type_as_string(&p.ty))
})
.collect();
let has_return = matches!(ty, Type::Function { return_type, .. }
if { let resolved = return_type.resolve(); !resolved.is_unit() && !resolved.is_variable() });
let return_ty_string = if has_return {
match ty {
Type::Function { return_type, .. } => {
format!(" {}", self.go_type_as_string(return_type))
}
_ => String::new(),
}
} else {
String::new()
};
let should_return = has_return;
let saved_return_context = self.current_return_context.clone();
if let Type::Function { return_type, .. } = ty {
self.current_return_context = Some(return_type.as_ref().clone());
}
let mut body_string = String::new();
for (temp_name, pattern, typed) in &destructure_bindings {
self.emit_pattern_bindings(&mut body_string, temp_name, pattern, *typed);
}
self.emit_function_body(&mut body_string, body, should_return);
optimize_function_body(&mut body_string);
self.scope.declared = saved_declared;
self.scope.scope_depth = saved_scope_depth;
self.scope.bindings.restore();
self.current_return_context = saved_return_context;
format!(
"func({}){} {{\n{}}}",
group_params(¶m_pairs),
return_ty_string,
body_string
)
}
pub(crate) fn is_go_never(expression: &Expression) -> bool {
match expression {
Expression::Return { .. } => true,
Expression::Call { expression, .. } => {
matches!(&**expression, Expression::Identifier { value, .. } if value == "panic")
}
_ => false,
}
}
}