use rustc_hash::FxHashSet as HashSet;
use syntax::program::Definition;
use crate::Emitter;
use crate::is_order_sensitive;
use crate::types::coercion::Coercion;
use crate::types::emitter::Position;
use crate::utils::Staged;
use crate::write_line;
use syntax::ast::{Expression, Visibility};
use syntax::types::Type;
impl Emitter<'_> {
pub(crate) fn emit_doc(&self, doc: &Option<String>) -> String {
match doc {
Some(text) => {
let lines: Vec<String> = text
.lines()
.map(|line| {
if line.is_empty() {
"//".to_string()
} else {
format!("// {}", line)
}
})
.collect();
if lines.is_empty() {
String::new()
} else {
format!("{}\n", lines.join("\n"))
}
}
None => String::new(),
}
}
pub(crate) fn emit_top_item(&mut self, item: &Expression) -> String {
match item {
Expression::Function {
doc,
visibility,
name_span,
..
} => {
if self.ctx.unused.is_unused_definition(name_span) {
return String::new();
}
let is_public = matches!(visibility, Visibility::Public);
let function = item.to_function_definition();
let doc_comment = self.emit_doc(doc);
let code = self.emit_function(&function, None, is_public);
format!("{}{}", doc_comment, code)
}
Expression::Struct {
doc,
attributes,
name,
generics,
fields,
kind,
..
} => {
let doc_comment = self.emit_doc(doc);
let code = self.emit_struct_definition(name, generics, fields, kind, attributes);
format!("{}{}", doc_comment, code)
}
Expression::Enum {
doc,
attributes,
name,
generics,
..
} => {
let doc_comment = self.emit_doc(doc);
let code = self
.emit_enum(name, generics, attributes)
.unwrap_or_default();
format!("{}{}", doc_comment, code)
}
Expression::ValueEnum { .. } => String::new(),
Expression::TypeAlias {
doc,
name,
generics,
ty,
..
} => {
let doc_comment = self.emit_doc(doc);
let code = self.emit_type_alias(name, generics, ty);
format!("{}{}", doc_comment, code)
}
Expression::Interface {
doc,
name,
method_signatures,
parents,
generics,
visibility,
..
} => {
let doc_comment = self.emit_doc(doc);
let is_public = matches!(visibility, Visibility::Public);
let code =
self.emit_interface(name, method_signatures, parents, generics, is_public);
format!("{}{}", doc_comment, code)
}
Expression::ImplBlock {
receiver_name,
ty,
methods,
generics,
..
} => self.emit_impl_block(receiver_name, ty, methods, generics),
Expression::Const {
doc,
identifier,
expression,
ty,
..
} => {
let doc_comment = self.emit_doc(doc);
let code = self.emit_const(identifier, expression, ty);
format!("{}{}", doc_comment, code)
}
_ => String::new(),
}
}
pub(crate) fn declare_result_var(&mut self, output: &mut String, ty: &Type) -> String {
let result_var = self.fresh_var(None);
write_line!(output, "var {} {}", result_var, self.go_type_as_string(ty));
self.declare(&result_var);
result_var
}
pub(crate) fn emit_value(&mut self, output: &mut String, expression: &Expression) -> String {
if let Some(strategy) = self.classify_go_fn_value(expression) {
if self.go_fn_matches_lowered_slot(expression, &strategy) {
return self.emit_operand(output, expression);
}
return self.emit_go_fn_wrapper(output, expression, &strategy);
}
if self.is_go_array_return_value(expression) {
return self.emit_array_return_wrapper(output, expression);
}
self.emit_operand(output, expression)
}
fn go_fn_matches_lowered_slot(
&self,
expression: &Expression,
strategy: &crate::GoCallStrategy,
) -> bool {
if self.suppress_go_fn_short_circuit {
return false;
}
let fn_ty = expression.get_type();
let Type::Function { return_type, .. } = fn_ty.unwrap_forall() else {
return false;
};
let Some(shape) = self.classify_direct_emission(return_type) else {
return false;
};
use crate::GoCallStrategy as G;
use crate::types::abi::AbiShape as A;
match (strategy, &shape) {
(G::Result, A::ResultTuple | A::BareError)
| (G::Partial, A::PartialTuple)
| (G::CommaOk, A::CommaOk)
| (G::NullableReturn, A::NullableReturn) => true,
(G::Tuple { arity: a }, A::Tuple { arity: b }) => a == b,
_ => false,
}
}
pub(crate) fn emit_composite_value(
&mut self,
output: &mut String,
expression: &Expression,
) -> String {
if expression.get_type().is_unit()
&& matches!(expression.unwrap_parens(), Expression::Call { .. })
{
let call_str = self.emit_value(output, expression);
if !call_str.is_empty() {
write_line!(output, "{call_str}");
}
return "struct{}{}".to_string();
}
self.emit_value(output, expression)
}
pub(crate) fn emit_operand(&mut self, output: &mut String, expression: &Expression) -> String {
match expression {
Expression::Literal { literal, ty, .. } => self.emit_literal(output, literal, ty),
Expression::Identifier { value, ty, .. } => self.emit_identifier(value, ty),
Expression::Binary {
operator,
left,
right,
..
} => self.emit_binary_expression(output, operator, left, right),
Expression::Unary {
operator,
expression,
..
} => self.emit_unary_expression(output, operator, expression),
Expression::Call { ty, .. } => {
if let Some(strategy) = self.resolve_go_call_strategy(expression) {
self.emit_go_wrapped_call(output, expression, &strategy, ty)
} else if let Expression::Call {
expression: callee, ..
} = expression
&& let Some(shape) = self.classify_callee_abi(callee)
{
self.flags.needs_stdlib = true;
let call_str = self.emit_call(output, expression, Some(ty));
self.emit_callee_abi_wrapping(output, &shape, &call_str, ty)
} else {
self.emit_call(output, expression, Some(ty))
}
}
Expression::DotAccess {
expression,
member,
ty,
dot_access_kind,
receiver_coercion,
..
} => self.emit_dot_access(
output,
expression,
member,
ty,
*dot_access_kind,
*receiver_coercion,
),
Expression::IndexedAccess {
expression, index, ..
} => self.emit_index_access(output, expression, index),
Expression::StructCall {
name,
field_assignments,
spread,
ty,
..
} => self.emit_struct_call(output, name, field_assignments, spread, ty),
Expression::Paren { expression, .. } => {
let inner = self.emit_operand(output, expression);
format!("({})", inner)
}
Expression::Reference {
expression: inner,
ty,
..
} => self.emit_reference(output, inner, ty),
Expression::Task { expression, .. } => {
self.emit_async_wrapper(output, "go", expression)
}
Expression::Defer { expression, .. } => {
self.emit_async_wrapper(output, "defer", expression)
}
Expression::RawGo { text } => text.clone(),
Expression::Unit { .. } => "struct{}{}".to_string(),
Expression::NoOp => String::new(),
Expression::Lambda {
params, body, ty, ..
} => self.emit_lambda(params, body, ty),
Expression::Function {
params, body, ty, ..
} => self.emit_lambda(params, body, ty),
Expression::Propagate { expression, .. } => {
self.emit_propagate(output, expression, None)
}
Expression::TryBlock { items, ty, .. } => self.emit_try_block(output, items, ty),
Expression::RecoverBlock { items, ty, .. } => {
self.emit_recover_block(output, items, ty)
}
Expression::Tuple { elements, ty, .. } => self.emit_tuple_value(output, elements, ty),
Expression::If { ty, .. }
| Expression::Match { ty, .. }
| Expression::Select { ty, .. } => {
self.emit_branching_as_operand(output, expression, ty)
}
Expression::IfLet { .. } => {
unreachable!("IfLet should be desugared to Match before emit")
}
Expression::Block { ty, items, .. } => {
self.emit_block_as_operand(output, expression, ty, items)
}
Expression::Loop {
body,
ty,
needs_label,
..
} => {
let result_var = self.declare_result_var(output, ty);
self.push_loop(result_var.clone());
self.emit_labeled_loop(output, "for {\n", body, *needs_label);
self.pop_loop();
result_var
}
Expression::Return {
expression: return_expression,
..
} => {
self.emit_return(output, return_expression);
String::new()
}
Expression::Range {
start,
end,
inclusive,
ty,
..
} => self.emit_range_value(output, start, end, *inclusive, ty),
Expression::Cast {
expression,
target_type,
ty,
..
} => self.emit_cast(output, expression, target_type, ty),
Expression::Assignment { target, value, .. } => {
self.emit_assignment_operand(output, target, value);
"struct{}{}".to_string()
}
_ => unreachable!("unexpected expression in emit: {:?}", expression),
}
}
fn emit_tuple_value(
&mut self,
output: &mut String,
elements: &[Expression],
ty: &Type,
) -> String {
let inferred_slot_types: Vec<Type> = match ty {
Type::Tuple(slots) => slots.clone(),
_ => Vec::new(),
};
let slot_types = self.resolve_tuple_slot_types(inferred_slot_types);
let stages: Vec<Staged> = elements
.iter()
.enumerate()
.map(|(i, e)| {
let prev = std::mem::replace(
&mut self.current_slot_expected_ty,
slot_types.get(i).cloned(),
);
let staged = self.stage_composite(e);
self.current_slot_expected_ty = prev;
staged
})
.collect();
let elem_expressions = self.sequence(output, stages, "_v");
let mut wrapped_expressions: Vec<String> = Vec::with_capacity(elem_expressions.len());
for (i, (expr, emitted)) in elements.iter().zip(elem_expressions).enumerate() {
let value = match slot_types.get(i) {
Some(slot) => {
let coercion = Coercion::resolve(self, &expr.get_type(), slot);
coercion.apply(self, output, emitted)
}
None => emitted,
};
wrapped_expressions.push(value);
}
let elem_expressions = wrapped_expressions;
self.flags.needs_stdlib = true;
let arity = elem_expressions.len();
let needs_explicit_type_args =
!slot_types.is_empty() && slot_types.iter().any(|t| self.as_interface(t).is_some());
if !needs_explicit_type_args {
return format!(
"lisette.MakeTuple{}({})",
arity,
elem_expressions.join(", ")
);
}
let slot_ty_strs: Vec<String> = slot_types
.iter()
.map(|t| self.go_type_as_string(t))
.collect();
format!(
"lisette.MakeTuple{}[{}]({})",
arity,
slot_ty_strs.join(", "),
elem_expressions.join(", ")
)
}
fn emit_branching_as_operand(
&mut self,
output: &mut String,
expression: &Expression,
ty: &Type,
) -> String {
let result_var = self.declare_result_var(output, ty);
let saved_target_ty = self.assign_target_ty.replace(ty.clone());
self.with_position(Position::Assign(result_var.clone()), |this| {
this.emit_branching_directly(output, expression);
});
self.assign_target_ty = saved_target_ty;
result_var
}
fn emit_cast(
&mut self,
output: &mut String,
expression: &Expression,
target_type: &syntax::ast::Annotation,
ty: &Type,
) -> String {
let inner = self.emit_operand(output, expression);
if let Type::Nominal { id, .. } = &self.peel_alias(ty)
&& matches!(
self.ctx.definitions.get(id.as_str()),
Some(Definition::Interface { .. })
)
{
let source_ty = expression.get_type();
let coercion = Coercion::resolve(self, &source_ty, ty);
return coercion.apply(self, output, inner);
}
let go_type = self.annotation_to_go_type(target_type);
format!("{}({})", go_type, inner)
}
fn emit_reference(&mut self, output: &mut String, inner: &Expression, ty: &Type) -> String {
if inner.get_type().is_unit() && matches!(inner.unwrap_parens(), Expression::Call { .. }) {
let emitted = self.emit_operand(output, inner.unwrap_parens());
if !emitted.is_empty() {
write_line!(output, "{}", emitted);
}
let tmp = self.fresh_var(Some("ref"));
self.declare(&tmp);
write_line!(output, "{} := struct{{}}{{}}", tmp);
return format!("&{}", tmp);
}
let emitted = self.emit_value(output, inner);
if inner.get_type() == *ty {
emitted
} else if self.is_go_unaddressable(inner)
|| matches!(inner.get_type(), Type::Function { .. })
{
let tmp = self.fresh_var(Some("ref"));
self.declare(&tmp);
write_line!(output, "{} := {}", tmp, emitted);
format!("&{}", tmp)
} else {
format!("&{}", emitted)
}
}
pub(crate) fn contains_newtype_access(&self, expression: &Expression) -> bool {
let mut current = expression;
while let Expression::DotAccess {
expression: inner,
member,
..
} = current
{
if member.parse::<usize>().is_ok()
&& self.is_newtype_struct(&inner.get_type().strip_refs())
{
return true;
}
current = inner;
}
false
}
fn emit_assignment_operand(
&mut self,
output: &mut String,
target: &Expression,
value: &Expression,
) {
let rhs_staged = self.stage_composite(value);
let target_str = if is_order_sensitive(target) {
self.emit_left_value_capturing(output, target, !rhs_staged.setup.is_empty())
} else {
self.emit_left_value(output, target)
};
output.push_str(&rhs_staged.setup);
if let Expression::DotAccess {
expression: receiver,
ty,
..
} = target
&& Self::is_go_imported_type(&receiver.get_type())
&& self.is_go_nullable(ty)
{
let coercion = Coercion::resolve_unwrap_go_nullable(self, &value.get_type());
let unwrapped = coercion.apply(self, output, rhs_staged.value);
write_line!(output, "{} = {}", target_str, unwrapped);
} else {
write_line!(output, "{} = {}", target_str, rhs_staged.value);
}
}
fn emit_range_value(
&mut self,
output: &mut String,
start: &Option<Box<Expression>>,
end: &Option<Box<Expression>>,
_inclusive: bool,
ty: &Type,
) -> String {
let type_string = self.go_type_as_string(ty);
let mut stages: Vec<Staged> = Vec::new();
let has_start = start.is_some();
if let Some(s) = start {
stages.push(self.stage_operand(s));
}
if let Some(e) = end {
stages.push(self.stage_operand(e));
}
if stages.is_empty() {
return "struct{}{}".to_string();
}
let values = self.sequence(output, stages, "_range");
let mut fields = Vec::new();
if has_start {
fields.push(("Start".to_string(), values[0].clone()));
if values.len() > 1 {
fields.push(("End".to_string(), values[1].clone()));
}
} else {
fields.push(("End".to_string(), values[0].clone()));
}
self.emit_struct_literal(&type_string, &fields)
}
fn emit_block_as_operand(
&mut self,
output: &mut String,
expression: &Expression,
ty: &Type,
items: &[Expression],
) -> String {
if ty.is_never() {
self.emit_block(output, expression);
return String::new();
}
if ty.is_unit() || matches!(ty, Type::Var { .. } | Type::Forall { .. }) {
self.emit_block(output, expression);
return String::new();
}
let result_var = self.declare_result_var(output, ty);
let needs_braces = items.len() > 1;
if needs_braces {
output.push_str("{\n");
}
self.emit_block_to_var_with_braces(output, expression, &result_var, needs_braces);
if needs_braces {
output.push_str("}\n");
}
result_var
}
pub(crate) fn with_fresh_scope<R>(&mut self, f: impl FnOnce(&mut Self) -> R) -> R {
let saved_declared = std::mem::take(&mut self.scope.declared);
self.scope.declared = vec![HashSet::default()];
let saved_scope_depth = self.scope.scope_depth;
self.scope.scope_depth = 0;
self.scope.bindings.save();
let result = f(self);
self.scope.bindings.restore();
self.scope.declared = saved_declared;
self.scope.scope_depth = saved_scope_depth;
result
}
fn emit_async_wrapper(
&mut self,
output: &mut String,
keyword: &str,
expression: &Expression,
) -> String {
if let Expression::Block { .. } = expression {
self.with_fresh_scope(|emitter| {
write_line!(output, "{} func() {{", keyword);
emitter.emit_block(output, expression);
output.push_str("}()\n");
});
String::new()
} else if let Some(call_str) = self.emit_go_call_discarded(output, expression) {
format!("{} {}", keyword, call_str)
} else {
let inner = self.emit_value(output, expression);
format!("{} {}", keyword, inner)
}
}
}
impl Emitter<'_> {
fn is_go_unaddressable(&self, expression: &Expression) -> bool {
match expression.unwrap_parens() {
Expression::Call { .. } => true,
Expression::Identifier { value, ty, .. }
if !matches!(ty.unwrap_forall(), Type::Function { .. }) =>
{
if self.scope.bindings.get(value).is_some() {
return false;
}
if let Type::Nominal { id, .. } = ty {
matches!(
self.ctx.definitions.get(id.as_str()),
Some(Definition::Enum { .. })
)
} else {
false
}
}
Expression::DotAccess { expression, ty, .. }
if !matches!(ty.unwrap_forall(), Type::Function { .. }) =>
{
if let Type::Nominal { id, .. } = ty {
if !matches!(
self.ctx.definitions.get(id.as_str()),
Some(Definition::Enum { .. })
) {
return false;
}
let receiver_ty = expression.get_type();
if let Type::Nominal {
id: receiver_id, ..
} = &receiver_ty
{
matches!(
self.ctx.definitions.get(receiver_id.as_str()),
Some(Definition::Enum { .. } | Definition::TypeAlias { .. })
)
} else {
false
}
} else {
false
}
}
_ => false,
}
}
}