use rustc_hash::FxHashSet as HashSet;
use crate::Emitter;
use crate::names::go_name;
use crate::types::emitter::Position;
use crate::types::native::NativeGoType;
use crate::utils::{group_params, optimize_function_body, receiver_name, requires_temp_var};
use crate::write_line;
use syntax::ast::{
Annotation, Binding, Expression, FunctionDefinition, Generic, Pattern, Span, 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_non_returning_tail(output, last, should_return, is_statement_only);
return;
}
if self.try_emit_lowered_tail_return(output, last) {
return;
}
if self.emit_wrapped_return(output, last) {
return;
}
self.emit_returning_tail(output, last);
}
fn emit_non_returning_tail(
&mut self,
output: &mut String,
last: &Expression,
should_return: bool,
is_statement_only: bool,
) {
self.emit_statement(output, last);
if should_return && last.get_type().is_never() && !Self::is_go_never(last) {
output.push_str("panic(\"unreachable\")\n");
}
let last_is_unit_expr = !is_statement_only
&& !matches!(last, Expression::Return { .. })
&& last.get_type().is_unit();
if should_return
&& (is_statement_only || last_is_unit_expr)
&& self
.current_return_context
.as_ref()
.is_some_and(|ctx| !ctx.ty.is_unit())
{
let return_ty = self.current_return_context.as_ref().unwrap().ty.clone();
let zero = self.zero_value(&return_ty);
write_line!(output, "return {}", zero);
}
}
fn emit_returning_tail(&mut self, output: &mut String, last: &Expression) {
self.with_position(Position::Tail, |this| {
if !requires_temp_var(last) {
let expression = this.emit_value(output, last);
let return_ty = this
.current_return_context
.as_ref()
.map(|ctx| ctx.ty.clone());
let expression =
this.apply_type_coercion(output, return_ty.as_ref(), last, expression);
output.push_str(&this.wrap_value(&expression));
return;
}
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 !return_type.is_unit() && !return_type.is_variable());
let suppress_lowering = self.suppress_go_fn_short_circuit;
let return_ty_string = if has_return {
match ty {
Type::Function { return_type, .. } => {
if !suppress_lowering
&& let Some(shape) = self.classify_direct_emission(return_type)
{
format!(" {}", self.render_lowered_return_ty(&shape, return_type))
} else {
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(if suppress_lowering {
crate::ReturnContext::tagged(return_type.as_ref().clone())
} else {
crate::ReturnContext::new(return_type.as_ref().clone())
});
}
let saved_suppress = std::mem::replace(&mut self.suppress_go_fn_short_circuit, false);
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;
self.suppress_go_fn_short_circuit = saved_suppress;
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,
}
}
pub(crate) fn emit_function(
&mut self,
function_definition: &FunctionDefinition,
receiver: Option<(String, Type)>,
is_public: bool,
) -> String {
if matches!(*function_definition.body, Expression::NoOp) {
return String::new();
}
let directive = self.maybe_line_directive(&function_definition.name_span);
let saved_return_context = self.current_return_context.clone();
self.current_return_context = Some(crate::ReturnContext::new(
function_definition.return_type.clone(),
));
let (function_definition, receiver) =
self.change_go_builtin_methods(function_definition, receiver);
let (params_to_process, receiver_override) =
self.extract_receiver(&function_definition, receiver.is_some());
let mut parts = vec!["func".to_string()];
let (_, receiver_part) =
self.emit_receiver_part(params_to_process, &receiver, receiver_override.as_ref());
if let Some(part) = receiver_part {
parts.push(part);
}
let function_name = if is_public {
go_name::capitalize_first(&function_definition.name)
} else if receiver.is_some() {
go_name::escape_keyword(&function_definition.name).into_owned()
} else {
go_name::escape_reserved(&function_definition.name).into_owned()
};
parts.push(function_name);
let generic_names: Vec<&str> = function_definition
.generics
.iter()
.map(|g| g.name.as_ref())
.collect();
let sig_types = params_to_process
.iter()
.map(|p| &p.ty)
.chain(std::iter::once(&function_definition.return_type));
let mut map_key_generics = Self::collect_map_key_generics(sig_types, &generic_names);
for name in &generic_names {
if !map_key_generics.contains(*name)
&& Self::body_has_map_key_generic(&function_definition.body, name)
{
map_key_generics.insert(name.to_string());
}
}
let generics_str =
self.generics_to_string_with_map_keys(&function_definition.generics, &map_key_generics);
if !generics_str.is_empty() {
parts.push(generics_str);
}
let saved_absorbed =
self.detect_absorbed_ref_generics(params_to_process, &function_definition.generics);
let (params_string, deferred_patterns) = self.emit_function_params(params_to_process);
parts.push(params_string);
let return_ty = if function_definition.return_type.is_unit() {
String::new()
} else if let Some(shape) = self.classify_direct_emission(&function_definition.return_type)
{
self.render_lowered_return_ty(&shape, &function_definition.return_type)
} else {
self.go_type_as_string(&function_definition.return_type)
};
if !return_ty.is_empty() {
parts.push(return_ty);
}
let signature = parts.join(" ");
let mut body = String::new();
for (var_name, pattern, typed) in deferred_patterns {
self.emit_pattern_bindings(&mut body, &var_name, &pattern, typed.as_ref());
}
self.emit_function_body(
&mut body,
&function_definition.body,
!function_definition.return_type.is_unit(),
);
optimize_function_body(&mut body);
self.current_return_context = saved_return_context;
self.module.absorbed_ref_generics = saved_absorbed;
let trimmed_body = body.trim_end();
if trimmed_body.is_empty() {
format!("{}{} {{}}", directive, signature)
} else {
format!("{}{} {{\n{}\n}}", directive, signature, trimmed_body)
}
}
fn change_go_builtin_methods(
&mut self,
function_definition: &FunctionDefinition,
receiver: Option<(String, Type)>,
) -> (FunctionDefinition, Option<(String, Type)>) {
let Some((receiver_name, receiver_type)) = receiver else {
return (function_definition.clone(), None);
};
let Some(native) = NativeGoType::from_type(&receiver_type) else {
return (
function_definition.clone(),
Some((receiver_name, receiver_type)),
);
};
let mut new_function_definition = function_definition.clone();
new_function_definition.name =
format!("{}.{}", native.lisette_name(), function_definition.name).into();
let self_binding = Binding {
pattern: Pattern::Identifier {
identifier: receiver_name.into(),
span: Span::dummy(),
},
annotation: Some(Annotation::Unknown),
typed_pattern: None,
ty: receiver_type,
mutable: false,
};
new_function_definition.params.insert(0, self_binding);
(new_function_definition, None)
}
fn emit_receiver_part(
&mut self,
params_to_process: &[Binding],
receiver: &Option<(String, Type)>,
receiver_override: Option<&Type>,
) -> (Option<String>, Option<String>) {
let Some((_, receiver_ty)) = receiver else {
return (None, None);
};
let param_names: Vec<String> = params_to_process
.iter()
.filter_map(|param| {
if let Pattern::Identifier { identifier, .. } = ¶m.pattern {
Some(identifier.to_string())
} else {
None
}
})
.collect();
let actual_ty = receiver_override.unwrap_or(receiver_ty);
let ty_string = self.go_type_as_string(actual_ty);
let mut receiver_var = receiver_name(&ty_string);
if param_names.contains(&receiver_var) {
receiver_var = format!("{}{}", receiver_var, receiver_var);
let mut counter = 2;
while param_names.contains(&receiver_var) {
receiver_var = format!("{}{}", receiver_name(&ty_string), counter);
counter += 1;
}
}
let receiver_part = format!("({} {})", receiver_var, ty_string);
self.scope.bindings.add("self", receiver_var.clone());
self.declare(&receiver_var);
(Some(receiver_var), Some(receiver_part))
}
fn detect_absorbed_ref_generics(
&mut self,
params: &[Binding],
generics: &[Generic],
) -> HashSet<String> {
let saved = self.module.absorbed_ref_generics.clone();
self.module.absorbed_ref_generics.clear();
let bounded_generics: HashSet<&str> = generics
.iter()
.filter(|g| !g.bounds.is_empty())
.map(|g| g.name.as_ref())
.collect();
for param in params.iter() {
if param.ty.is_ref()
&& let Some(inner) = param.ty.inner()
&& let Type::Parameter(name) = &inner
&& bounded_generics.contains(name.as_ref())
{
self.module.absorbed_ref_generics.insert(name.to_string());
}
}
saved
}
fn emit_function_params(
&mut self,
params_to_process: &[Binding],
) -> (String, Vec<(String, Pattern, Option<TypedPattern>)>) {
let mut deferred_patterns = Vec::new();
let mut params = Vec::new();
for param in params_to_process {
let name = match ¶m.pattern {
Pattern::Identifier { identifier, .. } => {
if let Some(go_name) = self.go_name_for_binding(¶m.pattern) {
let go_id = self.scope.bindings.add(identifier, go_name);
self.declare(&go_id);
go_id
} else {
"_".to_string()
}
}
Pattern::WildCard { .. } => "_".to_string(),
_ => {
let var = self.fresh_var(Some("arg"));
self.declare(&var);
deferred_patterns.push((
var.clone(),
param.pattern.clone(),
param.typed_pattern.clone(),
));
var
}
};
let param_type = {
if param.ty.is_ref()
&& let Some(inner) = param.ty.inner()
&& let Type::Parameter(name) = &inner
&& self.module.absorbed_ref_generics.contains(name.as_ref())
{
inner
} else {
param.ty.clone()
}
};
params.push((name, self.go_type_as_string(¶m_type)));
}
(format!("({})", group_params(¶ms)), deferred_patterns)
}
fn extract_receiver<'a>(
&mut self,
function_definition: &'a FunctionDefinition,
has_receiver: bool,
) -> (&'a [Binding], Option<Type>) {
let default = (&function_definition.params[..], None);
if !has_receiver || function_definition.params.is_empty() {
return default;
}
let Pattern::Identifier { identifier, .. } = &function_definition.params[0].pattern else {
return default;
};
if identifier != "self" {
return default;
}
let receiver_ty = &function_definition.params[0].ty;
let _ty_str = self.go_type_as_string(receiver_ty);
(&function_definition.params[1..], Some(receiver_ty.clone()))
}
}