Fork of daniellemaywood.uk/gleam — Wasm codegen work
48 kB
1395 lines
1use std::{collections::HashMap, ops::Deref};
2
3use ecow::{EcoString, eco_format};
4use num_bigint::BigInt;
5use vec1::Vec1;
6
7use crate::{
8 cranelift::mir::{ast::*, visit::Visit},
9 exhaustiveness, parse, type_,
10};
11
12pub mod ast;
13pub mod lower;
14pub mod visit;
15
16struct FlattenBlockPass;
17
18impl<'mir, T> Visit<'mir, T> for FlattenBlockPass {
19 fn visit_expression(&mut self, expression: &'mir mut Expression<T>) {
20 visit::visit_expression(self, expression);
21
22 if let Expression::Block(inner) = expression
23 && inner.len() == 1
24 {
25 *expression = inner.pop().expect("block should've been poppable");
26 }
27 }
28
29 fn visit_expression_block(&mut self, expressions: &'mir mut Vec<Expression<T>>) {
30 visit::visit_expression_block(self, expressions);
31
32 *expressions = std::mem::take(expressions)
33 .into_iter()
34 .flat_map(|expr| match expr {
35 Expression::Block(inner) => inner,
36 _ => vec![expr],
37 })
38 .collect();
39 }
40}
41
42struct RemoveUnusedPass;
43
44impl<'mir, T> Visit<'mir, T> for RemoveUnusedPass {
45 fn visit_expression_block(&mut self, expressions: &'mir mut Vec<Expression<T>>) {
46 visit::visit_expression_block(self, expressions);
47
48 let last_index = expressions.len() - 1;
49
50 *expressions = std::mem::take(expressions)
51 .into_iter()
52 .enumerate()
53 .filter_map(|(index, expr)| match expr {
54 Expression::Var { .. }
55 | Expression::FunctionRef { .. }
56 | Expression::Int { .. }
57 | Expression::Float { .. }
58 | Expression::Bool { .. }
59 | Expression::String { .. }
60 if index != last_index =>
61 {
62 None
63 }
64
65 _ => Some(expr),
66 })
67 .collect();
68 }
69}
70
71#[derive(Default)]
72struct RemoveUselessSetPass {
73 replacements: HashMap<EcoString, EcoString>,
74}
75
76impl<'mir, T> Visit<'mir, T> for RemoveUselessSetPass {
77 fn visit_expression(&mut self, expression: &'mir mut Expression<T>) {
78 visit::visit_expression(self, expression);
79
80 if let Expression::Set { name, value } = expression {
81 if let Expression::Var(var) = value.as_ref() {
82 _ = self
83 .replacements
84 .insert(name.name.clone(), var.name.clone());
85
86 _ = var;
87 _ = name;
88 }
89 }
90 }
91
92 fn visit_expression_block(&mut self, expressions: &'mir mut Vec<Expression<T>>) {
93 visit::visit_expression_block(self, expressions);
94
95 *expressions = std::mem::take(expressions)
96 .into_iter()
97 .filter_map(|expr| match expr {
98 Expression::Set { name, value: _ }
99 if self.replacements.contains_key(&name.name) =>
100 {
101 None
102 }
103 _ => Some(expr),
104 })
105 .collect();
106 }
107
108 fn visit_expression_var(&mut self, var: &'mir mut Var<T>) {
109 if let Some(replacement) = self.replacements.get(&var.name) {
110 var.name = replacement.clone();
111 }
112 }
113}
114
115#[derive(Debug)]
116pub struct Translator<'ast> {
117 module: &'ast crate::ast::TypedModule,
118}
119
120impl<'ast> Translator<'ast> {
121 pub fn new(module: &'ast crate::ast::TypedModule) -> Self {
122 Self { module }
123 }
124
125 pub fn translate(self) -> Module<IncompleteType> {
126 let mut module = Module {
127 name: self.module.name.clone(),
128 functions: vec![],
129 };
130
131 for definition in &self.module.definitions {
132 match definition {
133 crate::ast::Definition::Function(function) => {
134 module.functions.push(self.translate_function(function));
135 }
136 crate::ast::Definition::TypeAlias(_type_alias) => todo!(),
137 crate::ast::Definition::CustomType(_custom_type) => {}
138 crate::ast::Definition::Import(_import) => todo!(),
139 crate::ast::Definition::ModuleConstant(_module_constant) => todo!(),
140 }
141 }
142
143 FlattenBlockPass.visit_module(&mut module);
144 RemoveUnusedPass.visit_module(&mut module);
145 RemoveUselessSetPass::default().visit_module(&mut module);
146
147 module
148 }
149
150 fn translate_function(&self, function: &crate::ast::TypedFunction) -> Function<IncompleteType> {
151 let (_, name) = function.name.clone().expect("function should have a name");
152
153 let return_type = Self::translate_type(&function.return_type);
154
155 let mut translator = BodyTranslator::default();
156 let arguments: Vec<_> = Iterator::collect(function.arguments.iter().map(|argument| {
157 argument.get_variable_name().map(|name| {
158 translator.make_var(name.clone(), Self::translate_type(&argument.type_))
159 })
160 }));
161
162 let parameters = Iterator::collect(function.arguments.iter().zip(arguments.iter()).map(
163 |(argument, name)| {
164 let type_ = Self::translate_type(&argument.type_);
165
166 FunctionParameter {
167 name: name.clone().map(|name| name.name),
168 type_,
169 }
170 },
171 ));
172
173 let body = translator.translate(&function.body);
174
175 Function {
176 name,
177 body,
178 return_type,
179 parameters,
180 }
181 }
182
183 fn translate_type(type_: &type_::Type) -> IncompleteType {
184 match type_ {
185 type_::Type::Named {
186 publicity: _,
187 package: _,
188 module,
189 name,
190 arguments,
191 inferred_variant: _,
192 } => match (module.as_str(), name.as_str()) {
193 ("gleam", "Int") => IncompleteType::Int,
194 ("gleam", "Float") => IncompleteType::Float,
195 ("gleam", "Bool") => IncompleteType::Bool,
196 ("gleam", "String") => IncompleteType::String,
197 ("gleam", "List") => {
198 let list_type = Self::translate_type(&arguments[0]);
199
200 IncompleteType::List(Box::new(list_type))
201 }
202 (_, _) => IncompleteType::Struct {
203 elements: arguments
204 .iter()
205 .map(|type_| Self::translate_type(type_))
206 .collect(),
207 },
208 },
209 type_::Type::Fn { arguments, return_ } => IncompleteType::Func {
210 arguments: arguments
211 .iter()
212 .map(|arg| Self::translate_type(arg))
213 .collect(),
214 returns: Box::new(Self::translate_type(return_)),
215 },
216 type_::Type::Var { type_ } => match type_.borrow().deref() {
217 type_::TypeVar::Link { type_ } => Self::translate_type(type_),
218 type_::TypeVar::Unbound { id } | type_::TypeVar::Generic { id } => {
219 IncompleteType::Generic { id: *id }
220 }
221 },
222 type_::Type::Tuple { elements } => IncompleteType::Struct {
223 elements: elements
224 .iter()
225 .map(|elem| Self::translate_type(elem))
226 .collect(),
227 },
228 }
229 }
230}
231
232#[derive(Debug, Default)]
233pub struct BodyTranslator {
234 variables: HashMap<EcoString, Var<IncompleteType>>,
235 variable_count: usize,
236}
237
238#[derive(Debug, Clone, Copy, PartialEq, Eq)]
239enum DecisionKind {
240 Case,
241 Let,
242}
243
244impl BodyTranslator {
245 fn make_tmp_var(&mut self, type_: IncompleteType) -> Var<IncompleteType> {
246 self.make_var("_tmp".into(), type_)
247 }
248
249 fn make_var(&mut self, name: EcoString, type_: IncompleteType) -> Var<IncompleteType> {
250 self.variable_count += 1;
251
252 let entry = eco_format!("{}${}", name.clone(), self.variable_count);
253 let var = Var {
254 name: entry,
255 type_: type_,
256 };
257
258 _ = self.variables.insert(name.clone(), var.clone());
259
260 var
261 }
262
263 fn get_var(&mut self, name: &EcoString) -> Var<IncompleteType> {
264 self.variables
265 .get(name)
266 .cloned()
267 .unwrap_or_else(|| panic!("variable '{name}' not found"))
268 }
269
270 fn in_var_scope<T>(&mut self, func: impl Fn(&mut Self) -> T) -> T {
271 let snapshot = self.variables.clone();
272 let result = func(self);
273 self.variables = snapshot;
274 result
275 }
276
277 fn translate(mut self, body: &Vec1<crate::ast::TypedStatement>) -> Expression<IncompleteType> {
278 self.translate_statements(body)
279 }
280
281 fn translate_statements(
282 &mut self,
283 statements: &[crate::ast::TypedStatement],
284 ) -> Expression<IncompleteType> {
285 Expression::Block(Iterator::collect(
286 statements.iter().map(|stmt| self.translate_statement(stmt)),
287 ))
288 }
289
290 fn translate_statement(
291 &mut self,
292 statement: &crate::ast::TypedStatement,
293 ) -> Expression<IncompleteType> {
294 match statement {
295 crate::ast::Statement::Expression(expression) => self.translate_expression(expression),
296 crate::ast::Statement::Assignment(assignment) => self.translate_assignment(assignment),
297 crate::ast::Statement::Use(_) => todo!(),
298 crate::ast::Statement::Assert(_) => todo!(),
299 }
300 }
301
302 fn translate_expression(
303 &mut self,
304 expression: &crate::ast::TypedExpr,
305 ) -> Expression<IncompleteType> {
306 match expression {
307 crate::ast::TypedExpr::Int {
308 location: _,
309 type_: _,
310 value: _,
311 int_value,
312 } => Expression::Int {
313 value: int_value.clone(),
314 },
315 crate::ast::TypedExpr::Float {
316 location: _,
317 type_: _,
318 value,
319 } => Expression::Float {
320 value: value.clone(),
321 },
322 crate::ast::TypedExpr::String {
323 location: _,
324 type_: _,
325 value,
326 } => Expression::String {
327 value: value.clone(),
328 },
329 crate::ast::TypedExpr::Block {
330 location: _,
331 statements,
332 } => self.in_var_scope(|this| this.translate_statements(statements)),
333 crate::ast::TypedExpr::Pipeline {
334 location: _,
335 first_value,
336 assignments,
337 finally,
338 finally_kind: _,
339 } => {
340 let mut block = vec![];
341
342 let value = self.translate_expression(&first_value.value);
343 block.push(Expression::Set {
344 name: self.make_var(
345 first_value.name.clone(),
346 Translator::translate_type(&first_value.type_()),
347 ),
348 value: Box::new(value),
349 });
350
351 for (assignment, _) in assignments {
352 let value = self.translate_expression(&assignment.value);
353 block.push(Expression::Set {
354 name: self.make_var(
355 assignment.name.clone(),
356 Translator::translate_type(&assignment.value.type_()),
357 ),
358 value: Box::new(value),
359 });
360 }
361
362 block.push(self.translate_expression(finally));
363
364 Expression::Block(block)
365 }
366 crate::ast::TypedExpr::Var {
367 location: _,
368 constructor,
369 name,
370 } => match &constructor.variant {
371 type_::ValueConstructorVariant::LocalVariable {
372 location: _,
373 origin: _,
374 } => Expression::Var(self.get_var(name)),
375 type_::ValueConstructorVariant::ModuleConstant {
376 documentation: _,
377 location: _,
378 module: _,
379 name: _,
380 literal: _,
381 implementations: _,
382 } => todo!(),
383 type_::ValueConstructorVariant::LocalConstant { literal: _ } => todo!(),
384 type_::ValueConstructorVariant::ModuleFn {
385 name,
386 field_map: _,
387 module,
388 arity,
389 location: _,
390 documentation: _,
391 implementations: _,
392 external_erlang: _,
393 external_javascript: _,
394 external_cranelift: _,
395 purity: _,
396 } => Expression::FunctionRef {
397 module: module.clone(),
398 name: name.clone(),
399 arity: *arity,
400 type_: Translator::translate_type(&constructor.type_),
401 },
402 type_::ValueConstructorVariant::Record {
403 name,
404 arity,
405 field_map: _,
406 location: _,
407 module,
408 variants_count: _,
409 variant_index: _,
410 documentation: _,
411 } => match (module.as_str(), name.as_str()) {
412 ("gleam", "True") => Expression::Bool { value: true },
413 ("gleam", "False") => Expression::Bool { value: false },
414 (_, _) => Expression::FunctionRef {
415 module: module.clone(),
416 name: name.clone(),
417 arity: usize::from(*arity),
418 type_: Translator::translate_type(&constructor.type_),
419 },
420 },
421 },
422 crate::ast::TypedExpr::Fn {
423 location: _,
424 type_: _,
425 kind: _,
426 arguments: _,
427 body: _,
428 return_annotation: _,
429 purity: _,
430 } => todo!(),
431 crate::ast::TypedExpr::List {
432 location: _,
433 type_,
434 elements,
435 tail,
436 } => {
437 let items = elements
438 .iter()
439 .map(|element| self.translate_expression(element))
440 .collect();
441
442 let tail = tail
443 .as_ref()
444 .map(|tail| Box::new(self.translate_expression(tail)));
445
446 Expression::List {
447 items,
448 tail,
449 type_: Translator::translate_type(type_),
450 }
451 }
452 crate::ast::TypedExpr::Call {
453 location: _,
454 type_,
455 fun,
456 arguments,
457 } => {
458 let target = self.translate_expression(fun);
459 let args = arguments
460 .iter()
461 .map(|arg| self.translate_expression(&arg.value))
462 .collect();
463
464 Expression::Call {
465 target: Box::new(target),
466 args,
467 type_: Translator::translate_type(type_),
468 }
469 }
470 crate::ast::TypedExpr::BinOp {
471 location: _,
472 type_: _,
473 name,
474 name_location: _,
475 left,
476 right,
477 } => {
478 let lhs = Box::new(self.translate_expression(left));
479 let rhs = Box::new(self.translate_expression(right));
480
481 match name {
482 crate::ast::BinOp::And => Expression::If {
483 cond: lhs,
484 then: rhs,
485 else_: Box::new(Expression::Bool { value: false }),
486 },
487 crate::ast::BinOp::Or => Expression::If {
488 cond: lhs,
489 then: Box::new(Expression::Bool { value: true }),
490 else_: rhs,
491 },
492 crate::ast::BinOp::Eq => Expression::Equals { lhs, rhs },
493 crate::ast::BinOp::NotEq => Expression::NotEquals { lhs, rhs },
494 crate::ast::BinOp::LtInt => Expression::IntLt { lhs, rhs },
495 crate::ast::BinOp::LtEqInt => Expression::IntLtEq { lhs, rhs },
496 crate::ast::BinOp::LtFloat => Expression::FloatLt { lhs, rhs },
497 crate::ast::BinOp::LtEqFloat => Expression::FloatLtEq { lhs, rhs },
498 crate::ast::BinOp::GtEqInt => Expression::IntGtEq { lhs, rhs },
499 crate::ast::BinOp::GtInt => Expression::IntGt { lhs, rhs },
500 crate::ast::BinOp::GtEqFloat => Expression::FloatGtEq { lhs, rhs },
501 crate::ast::BinOp::GtFloat => Expression::FloatGt { lhs, rhs },
502 crate::ast::BinOp::AddInt => Expression::IntAdd { lhs, rhs },
503 crate::ast::BinOp::AddFloat => Expression::FloatAdd { lhs, rhs },
504 crate::ast::BinOp::SubInt => Expression::IntSub { lhs, rhs },
505 crate::ast::BinOp::SubFloat => Expression::FloatSub { lhs, rhs },
506 crate::ast::BinOp::MultInt => Expression::IntMul { lhs, rhs },
507 crate::ast::BinOp::MultFloat => Expression::FloatMul { lhs, rhs },
508 crate::ast::BinOp::DivInt => Expression::IntDiv { lhs, rhs },
509 crate::ast::BinOp::DivFloat => Expression::FloatDiv { lhs, rhs },
510 crate::ast::BinOp::RemainderInt => Expression::IntRem { lhs, rhs },
511 crate::ast::BinOp::Concatenate => Expression::StringConcat { lhs, rhs },
512 }
513 }
514 crate::ast::TypedExpr::Case {
515 location: _,
516 type_: _,
517 subjects,
518 clauses,
519 compiled_case,
520 } => {
521 let mut block = vec![];
522
523 let subject_vars: Vec<_> = Iterator::collect(subjects.iter().map(|subject| {
524 let name = self.make_tmp_var(Translator::translate_type(&subject.type_()));
525 let value = self.translate_expression(subject);
526
527 block.push(Expression::Set {
528 name: name.clone(),
529 value: Box::new(value),
530 });
531
532 return name;
533 }));
534
535 block.push(self.translate_decision(
536 DecisionKind::Case,
537 &subject_vars,
538 clauses,
539 &compiled_case.tree,
540 ));
541
542 Expression::Block(block)
543 }
544 crate::ast::TypedExpr::RecordAccess {
545 location: _,
546 field_start: _,
547 type_,
548 label: _,
549 index,
550 record,
551 } => Expression::StructAccess {
552 value: Box::new(self.translate_expression(record)),
553 index: *index,
554 type_: Translator::translate_type(type_),
555 },
556 crate::ast::TypedExpr::ModuleSelect {
557 location: _,
558 field_start: _,
559 type_: _,
560 label: _,
561 module_name: _,
562 module_alias: _,
563 constructor: _,
564 } => todo!(),
565 crate::ast::TypedExpr::Tuple {
566 location: _,
567 type_,
568 elements,
569 } => Expression::Struct {
570 tag: None,
571 items: elements
572 .iter()
573 .map(|element| self.translate_expression(element))
574 .collect(),
575 type_: Translator::translate_type(type_),
576 },
577 crate::ast::TypedExpr::TupleIndex {
578 location: _,
579 type_,
580 index,
581 tuple,
582 } => Expression::StructAccess {
583 value: Box::new(self.translate_expression(tuple)),
584 index: *index,
585 type_: Translator::translate_type(type_),
586 },
587 crate::ast::TypedExpr::Todo {
588 location: _,
589 message: _,
590 kind: _,
591 type_: _,
592 } => todo!(),
593 crate::ast::TypedExpr::Panic {
594 location: _,
595 message: _,
596 type_: _,
597 } => todo!(),
598 crate::ast::TypedExpr::Echo {
599 location: _,
600 type_: _,
601 expression: _,
602 message: _,
603 } => todo!(),
604 crate::ast::TypedExpr::BitArray {
605 location: _,
606 type_: _,
607 segments: _,
608 } => todo!(),
609 crate::ast::TypedExpr::RecordUpdate {
610 location: _,
611 type_,
612 record_assignment,
613 constructor,
614 arguments,
615 } => {
616 let mut block = vec![];
617
618 if let Some(assignment) = record_assignment {
619 block.push(self.translate_assignment(assignment));
620 }
621
622 let constructor = self.translate_expression(constructor);
623 let arguments = arguments
624 .iter()
625 .map(|argument| self.translate_expression(&argument.value))
626 .collect();
627
628 block.push(Expression::Call {
629 target: Box::new(constructor),
630 args: arguments,
631 type_: Translator::translate_type(type_),
632 });
633
634 Expression::Block(block)
635 }
636 crate::ast::TypedExpr::NegateBool { location: _, value } => Expression::Equals {
637 lhs: Box::new(self.translate_expression(value)),
638 rhs: Box::new(Expression::Bool { value: false }),
639 },
640 crate::ast::TypedExpr::NegateInt { location: _, value } => Expression::IntSub {
641 lhs: Box::new(Expression::Int {
642 value: BigInt::from(0),
643 }),
644 rhs: Box::new(self.translate_expression(value)),
645 },
646 crate::ast::TypedExpr::Invalid {
647 location: _,
648 type_: _,
649 } => todo!(),
650 }
651 }
652
653 fn translate_assignment(
654 &mut self,
655 assignment: &crate::ast::TypedAssignment,
656 ) -> Expression<IncompleteType> {
657 let mut block = vec![];
658
659 let value_var = self.make_tmp_var(Translator::translate_type(&assignment.value.type_()));
660 let value = self.translate_expression(&assignment.value);
661
662 block.push(Expression::Set {
663 name: value_var.clone(),
664 value: Box::new(value),
665 });
666
667 block.push(self.translate_decision(
668 DecisionKind::Let,
669 &[value_var.clone()],
670 &[],
671 &assignment.compiled_case.tree,
672 ));
673
674 block.push(Expression::Var(value_var));
675
676 Expression::Block(block)
677 }
678
679 fn translate_bound_value_type(&mut self, value: &exhaustiveness::BoundValue) -> IncompleteType {
680 match value {
681 exhaustiveness::BoundValue::Variable(variable) => {
682 Translator::translate_type(&variable.type_)
683 }
684 exhaustiveness::BoundValue::LiteralString(_) => IncompleteType::String,
685 exhaustiveness::BoundValue::LiteralInt(_) => IncompleteType::Int,
686 exhaustiveness::BoundValue::LiteralFloat(_) => IncompleteType::Float,
687 exhaustiveness::BoundValue::BitArraySlice {
688 bit_array: _,
689 read_action: _,
690 } => todo!(),
691 }
692 }
693
694 fn translate_decision(
695 &mut self,
696 kind: DecisionKind,
697 subjects: &[Var<IncompleteType>],
698 clauses: &[crate::ast::TypedClause],
699 decision: &exhaustiveness::Decision,
700 ) -> Expression<IncompleteType> {
701 match decision {
702 exhaustiveness::Decision::Run { body } => {
703 let mut block = vec![];
704
705 for (binding, value) in &body.bindings {
706 let binding_type = self.translate_bound_value_type(value);
707 let binding = self.make_var(binding.clone(), binding_type);
708
709 block.push(self.translate_binding(subjects, binding.clone(), value));
710 }
711
712 if kind == DecisionKind::Case {
713 block.push(self.translate_expression(&clauses[body.clause_index].then));
714 }
715
716 Expression::Block(block)
717 }
718 exhaustiveness::Decision::Guard {
719 guard,
720 if_true,
721 if_false,
722 } => {
723 let mut block = vec![];
724
725 let guard = clauses[*guard]
726 .guard
727 .as_ref()
728 .expect("expected clause to have a guard");
729
730 let referenced_in_guard = guard.referenced_variables();
731
732 let (guard_bindings, if_true_bindings): (Vec<_>, Vec<_>) = if_true
733 .bindings
734 .iter()
735 .partition(|(binding, _)| referenced_in_guard.contains(binding));
736
737 for (binding, value) in guard_bindings {
738 let binding_type = self.translate_bound_value_type(value);
739 let binding = self.make_var(binding.clone(), binding_type);
740
741 block.push(self.translate_binding(subjects, binding, value));
742 }
743
744 let cond = self.translate_guard(guard);
745
746 let if_true = {
747 let mut block = vec![];
748
749 for (binding, value) in if_true_bindings {
750 let binding_type = self.translate_bound_value_type(value);
751 let binding = self.make_var(binding.clone(), binding_type);
752
753 block.push(self.translate_binding(subjects, binding.clone(), value));
754 }
755
756 block.push(self.translate_expression(&clauses[if_true.clause_index].then));
757 block
758 };
759
760 let if_false = self.translate_decision(kind, subjects, clauses, if_false);
761
762 block.push(Expression::If {
763 cond: Box::new(cond),
764 then: Box::new(Expression::Block(if_true)),
765 else_: Box::new(if_false),
766 });
767
768 Expression::Block(block)
769 }
770 exhaustiveness::Decision::Switch {
771 var,
772 choices,
773 fallback,
774 fallback_check: _,
775 } => {
776 let fallback = self.translate_decision(kind, subjects, clauses, fallback);
777
778 DoubleEndedIterator::rfold(choices.iter(), fallback, |fallback, (check, then)| {
779 Expression::If {
780 cond: Box::new(self.translate_runtime_check(
781 check,
782 Expression::Var(subjects[var.id].clone()),
783 )),
784 then: Box::new(self.translate_decision(kind, subjects, clauses, then)),
785 else_: Box::new(fallback),
786 }
787 })
788 }
789 exhaustiveness::Decision::Fail => todo!(),
790 }
791 }
792
793 fn translate_guard(
794 &mut self,
795 guard: &crate::ast::TypedClauseGuard,
796 ) -> Expression<IncompleteType> {
797 match guard {
798 crate::ast::ClauseGuard::Block { location: _, value } => self.translate_guard(value),
799 crate::ast::ClauseGuard::Equals {
800 location: _,
801 left,
802 right,
803 } => {
804 let lhs = self.translate_guard(left);
805 let rhs = self.translate_guard(right);
806
807 Expression::Equals {
808 lhs: Box::new(lhs),
809 rhs: Box::new(rhs),
810 }
811 }
812 crate::ast::ClauseGuard::NotEquals {
813 location: _,
814 left,
815 right,
816 } => Expression::NotEquals {
817 lhs: Box::new(self.translate_guard(left)),
818 rhs: Box::new(self.translate_guard(right)),
819 },
820 crate::ast::ClauseGuard::GtInt {
821 location: _,
822 left,
823 right,
824 } => Expression::IntGt {
825 lhs: Box::new(self.translate_guard(left)),
826 rhs: Box::new(self.translate_guard(right)),
827 },
828 crate::ast::ClauseGuard::GtEqInt {
829 location: _,
830 left,
831 right,
832 } => Expression::IntGtEq {
833 lhs: Box::new(self.translate_guard(left)),
834 rhs: Box::new(self.translate_guard(right)),
835 },
836 crate::ast::ClauseGuard::LtInt {
837 location: _,
838 left,
839 right,
840 } => Expression::IntLt {
841 lhs: Box::new(self.translate_guard(left)),
842 rhs: Box::new(self.translate_guard(right)),
843 },
844 crate::ast::ClauseGuard::LtEqInt {
845 location: _,
846 left,
847 right,
848 } => Expression::IntLtEq {
849 lhs: Box::new(self.translate_guard(left)),
850 rhs: Box::new(self.translate_guard(right)),
851 },
852 crate::ast::ClauseGuard::GtFloat {
853 location: _,
854 left,
855 right,
856 } => Expression::FloatGt {
857 lhs: Box::new(self.translate_guard(left)),
858 rhs: Box::new(self.translate_guard(right)),
859 },
860 crate::ast::ClauseGuard::GtEqFloat {
861 location: _,
862 left,
863 right,
864 } => Expression::FloatGtEq {
865 lhs: Box::new(self.translate_guard(left)),
866 rhs: Box::new(self.translate_guard(right)),
867 },
868 crate::ast::ClauseGuard::LtFloat {
869 location: _,
870 left,
871 right,
872 } => Expression::FloatLt {
873 lhs: Box::new(self.translate_guard(left)),
874 rhs: Box::new(self.translate_guard(right)),
875 },
876 crate::ast::ClauseGuard::LtEqFloat {
877 location: _,
878 left,
879 right,
880 } => Expression::FloatLtEq {
881 lhs: Box::new(self.translate_guard(left)),
882 rhs: Box::new(self.translate_guard(right)),
883 },
884 crate::ast::ClauseGuard::AddInt {
885 location: _,
886 left,
887 right,
888 } => Expression::IntAdd {
889 lhs: Box::new(self.translate_guard(left)),
890 rhs: Box::new(self.translate_guard(right)),
891 },
892 crate::ast::ClauseGuard::AddFloat {
893 location: _,
894 left,
895 right,
896 } => Expression::FloatAdd {
897 lhs: Box::new(self.translate_guard(left)),
898 rhs: Box::new(self.translate_guard(right)),
899 },
900 crate::ast::ClauseGuard::SubInt {
901 location: _,
902 left,
903 right,
904 } => Expression::IntSub {
905 lhs: Box::new(self.translate_guard(left)),
906 rhs: Box::new(self.translate_guard(right)),
907 },
908 crate::ast::ClauseGuard::SubFloat {
909 location: _,
910 left,
911 right,
912 } => Expression::FloatSub {
913 lhs: Box::new(self.translate_guard(left)),
914 rhs: Box::new(self.translate_guard(right)),
915 },
916 crate::ast::ClauseGuard::MultInt {
917 location: _,
918 left,
919 right,
920 } => Expression::IntMul {
921 lhs: Box::new(self.translate_guard(left)),
922 rhs: Box::new(self.translate_guard(right)),
923 },
924 crate::ast::ClauseGuard::MultFloat {
925 location: _,
926 left,
927 right,
928 } => Expression::FloatMul {
929 lhs: Box::new(self.translate_guard(left)),
930 rhs: Box::new(self.translate_guard(right)),
931 },
932 crate::ast::ClauseGuard::DivInt {
933 location: _,
934 left,
935 right,
936 } => Expression::IntDiv {
937 lhs: Box::new(self.translate_guard(left)),
938 rhs: Box::new(self.translate_guard(right)),
939 },
940 crate::ast::ClauseGuard::DivFloat {
941 location: _,
942 left,
943 right,
944 } => Expression::FloatDiv {
945 lhs: Box::new(self.translate_guard(left)),
946 rhs: Box::new(self.translate_guard(right)),
947 },
948 crate::ast::ClauseGuard::RemainderInt {
949 location: _,
950 left,
951 right,
952 } => Expression::IntRem {
953 lhs: Box::new(self.translate_guard(left)),
954 rhs: Box::new(self.translate_guard(right)),
955 },
956 crate::ast::ClauseGuard::Or {
957 location: _,
958 left,
959 right,
960 } => Expression::If {
961 cond: Box::new(self.translate_guard(left)),
962 then: Box::new(Expression::Bool { value: true }),
963 else_: Box::new(self.translate_guard(right)),
964 },
965 crate::ast::ClauseGuard::And {
966 location: _,
967 left,
968 right,
969 } => Expression::If {
970 cond: Box::new(self.translate_guard(left)),
971 then: Box::new(self.translate_guard(right)),
972 else_: Box::new(Expression::Bool { value: false }),
973 },
974 crate::ast::ClauseGuard::Not {
975 location: _,
976 expression,
977 } => Expression::Equals {
978 lhs: Box::new(self.translate_guard(expression)),
979 rhs: Box::new(Expression::Bool { value: false }),
980 },
981 crate::ast::ClauseGuard::Var {
982 location: _,
983 type_: _,
984 name,
985 definition_location: _,
986 } => Expression::Var(self.get_var(name)),
987 crate::ast::ClauseGuard::TupleIndex {
988 location: _,
989 index,
990 type_,
991 tuple,
992 } => Expression::StructAccess {
993 value: Box::new(self.translate_guard(tuple)),
994 index: *index,
995 type_: Translator::translate_type(type_),
996 },
997 crate::ast::ClauseGuard::FieldAccess {
998 label_location: _,
999 index,
1000 label: _,
1001 type_,
1002 container,
1003 } => Expression::StructAccess {
1004 value: Box::new(self.translate_guard(container)),
1005 index: index.expect("field access expected an index"),
1006 type_: Translator::translate_type(type_),
1007 },
1008 crate::ast::ClauseGuard::ModuleSelect {
1009 location: _,
1010 type_: _,
1011 label: _,
1012 module_name: _,
1013 module_alias: _,
1014 literal: _,
1015 } => todo!(),
1016 crate::ast::ClauseGuard::Constant(constant) => self.translate_constant(constant),
1017 }
1018 }
1019
1020 fn translate_constant(
1021 &mut self,
1022 constant: &crate::ast::TypedConstant,
1023 ) -> Expression<IncompleteType> {
1024 match constant {
1025 crate::ast::Constant::Int {
1026 location: _,
1027 value: _,
1028 int_value,
1029 } => Expression::Int {
1030 value: int_value.clone(),
1031 },
1032 crate::ast::Constant::Float { location: _, value } => Expression::Float {
1033 value: value.clone(),
1034 },
1035 crate::ast::Constant::String { location: _, value } => Expression::String {
1036 value: value.clone(),
1037 },
1038 crate::ast::Constant::Tuple {
1039 location: _,
1040 elements,
1041 } => Expression::Struct {
1042 tag: None,
1043 items: elements
1044 .iter()
1045 .map(|element| self.translate_constant(element))
1046 .collect(),
1047 type_: Translator::translate_type(&constant.type_()),
1048 },
1049 crate::ast::Constant::List {
1050 location: _,
1051 elements,
1052 type_,
1053 } => Expression::List {
1054 items: elements
1055 .iter()
1056 .map(|element| self.translate_constant(element))
1057 .collect(),
1058 tail: None,
1059 type_: Translator::translate_type(type_),
1060 },
1061 crate::ast::Constant::Record {
1062 location: _,
1063 module: _,
1064 name: _,
1065 arguments: _,
1066 tag: _,
1067 type_: _,
1068 field_map: _,
1069 record_constructor: _,
1070 } => todo!(),
1071 crate::ast::Constant::BitArray {
1072 location: _,
1073 segments: _,
1074 } => todo!(),
1075 crate::ast::Constant::Var {
1076 location: _,
1077 module: _,
1078 name: _,
1079 constructor: _,
1080 type_: _,
1081 } => todo!(),
1082 crate::ast::Constant::StringConcatenation {
1083 location: _,
1084 left,
1085 right,
1086 } => Expression::StringConcat {
1087 lhs: Box::new(self.translate_constant(left)),
1088 rhs: Box::new(self.translate_constant(right)),
1089 },
1090 crate::ast::Constant::Invalid {
1091 location: _,
1092 type_: _,
1093 } => todo!(),
1094 }
1095 }
1096
1097 fn translate_binding(
1098 &mut self,
1099 subjects: &[Var<IncompleteType>],
1100 binding: Var<IncompleteType>,
1101 value: &exhaustiveness::BoundValue,
1102 ) -> Expression<IncompleteType> {
1103 let value = match value {
1104 exhaustiveness::BoundValue::Variable(variable) => {
1105 Expression::Var(subjects[variable.id].clone())
1106 }
1107 exhaustiveness::BoundValue::LiteralString(_eco_string) => todo!(),
1108 exhaustiveness::BoundValue::LiteralInt(big_int) => Expression::Int {
1109 value: big_int.clone(),
1110 },
1111 exhaustiveness::BoundValue::LiteralFloat(_eco_string) => todo!(),
1112 exhaustiveness::BoundValue::BitArraySlice {
1113 bit_array: _,
1114 read_action: _,
1115 } => todo!(),
1116 };
1117
1118 Expression::Set {
1119 name: binding,
1120 value: Box::new(value),
1121 }
1122 }
1123
1124 fn translate_runtime_check(
1125 &mut self,
1126 check: &exhaustiveness::RuntimeCheck,
1127 against: Expression<IncompleteType>,
1128 ) -> Expression<IncompleteType> {
1129 match check {
1130 exhaustiveness::RuntimeCheck::Int { value } => {
1131 let value = parse::parse_int_value(&value).expect("unable to parse integer value");
1132
1133 Expression::Equals {
1134 lhs: Box::new(against),
1135 rhs: Box::new(Expression::Int { value }),
1136 }
1137 }
1138 exhaustiveness::RuntimeCheck::Float { value } => Expression::Equals {
1139 lhs: Box::new(against),
1140 rhs: Box::new(Expression::Float {
1141 value: value.clone(),
1142 }),
1143 },
1144 exhaustiveness::RuntimeCheck::String { value } => Expression::Equals {
1145 lhs: Box::new(against),
1146 rhs: Box::new(Expression::String {
1147 value: value.clone(),
1148 }),
1149 },
1150 exhaustiveness::RuntimeCheck::StringPrefix { prefix: _, rest: _ } => todo!(),
1151 exhaustiveness::RuntimeCheck::Tuple {
1152 size: _,
1153 elements: _,
1154 } => todo!(),
1155 exhaustiveness::RuntimeCheck::BitArray { test: _ } => todo!(),
1156 exhaustiveness::RuntimeCheck::Variant {
1157 match_: _,
1158 index,
1159 labels: _,
1160 fields: _,
1161 } => Expression::Equals {
1162 lhs: Box::new(Expression::Int {
1163 value: BigInt::from(*index),
1164 }),
1165 rhs: Box::new(Expression::StructTag {
1166 value: Box::new(against),
1167 }),
1168 },
1169 exhaustiveness::RuntimeCheck::NonEmptyList { first: _, rest: _ } => todo!(),
1170 exhaustiveness::RuntimeCheck::EmptyList => todo!(),
1171 }
1172 }
1173}
1174
1175#[cfg(test)]
1176mod tests {
1177 use std::collections::{HashMap, HashSet};
1178
1179 use super::*;
1180 use crate::analyse::TargetSupport;
1181 use crate::build::{Origin, Target};
1182 use crate::config::PackageConfig;
1183 use crate::line_numbers::LineNumbers;
1184 use crate::parse::parse_module;
1185 use crate::type_::PRELUDE_MODULE_NAME;
1186 use crate::uid::UniqueIdGenerator;
1187 use crate::warning::{TypeWarningEmitter, WarningEmitter};
1188 use camino::Utf8PathBuf;
1189
1190 fn compile_module(src: &str) -> crate::ast::TypedModule {
1191 use crate::type_::build_prelude;
1192 let parsed = parse_module(Utf8PathBuf::from("test/path"), src, &WarningEmitter::null())
1193 .expect("syntax error");
1194 let ast = parsed.module;
1195 let ids = UniqueIdGenerator::new();
1196 let mut config = PackageConfig::default();
1197 config.name = "thepackage".into();
1198 let mut modules = im::HashMap::new();
1199 let _ = modules.insert(PRELUDE_MODULE_NAME.into(), build_prelude(&ids));
1200 let line_numbers = LineNumbers::new(src);
1201
1202 crate::analyse::ModuleAnalyzerConstructor::<()> {
1203 target: Target::Erlang,
1204 ids: &ids,
1205 origin: Origin::Src,
1206 importable_modules: &modules,
1207 warnings: &TypeWarningEmitter::null(),
1208 direct_dependencies: &HashMap::new(),
1209 dev_dependencies: &HashSet::new(),
1210 target_support: TargetSupport::Enforced,
1211 package_config: &config,
1212 }
1213 .infer_module(ast, line_numbers, "".into())
1214 .expect("should successfully infer")
1215 }
1216
1217 fn translate(src: &str) -> Module<IncompleteType> {
1218 let typed_module = compile_module(src);
1219
1220 Translator::new(&typed_module).translate()
1221 }
1222
1223 #[test]
1224 fn translates_samples() {
1225 insta::assert_debug_snapshot!(translate(
1226 r#"pub fn add(lhs: Int, rhs: Int) -> Int {
1227 lhs + rhs
1228 }"#
1229 ));
1230
1231 insta::assert_debug_snapshot!(translate(
1232 r#"pub fn some_case(n: Int) -> Int {
1233 case n {
1234 0 -> 2
1235 1 -> 4
1236 _ -> 6
1237 }
1238 }"#
1239 ));
1240
1241 insta::assert_debug_snapshot!(translate(
1242 r#"pub fn some_case(n: Int) -> Int {
1243 case n {
1244 y if y == 0 -> 2
1245 1 -> 4
1246 _ -> 6
1247 }
1248 }"#
1249 ));
1250
1251 insta::assert_debug_snapshot!(translate(
1252 r#"pub fn fib(n: Int) -> Int {
1253 case n {
1254 0 -> 0
1255 1 -> 1
1256 _ -> fib(n - 1) + fib(n - 2)
1257 }
1258 }"#
1259 ));
1260
1261 insta::assert_debug_snapshot!(translate(
1262 r#"pub fn maths() {
1263 let _ = 1 + 0
1264 let _ = 2 - 1
1265 let _ = 3 * 2
1266 let _ = 4 / 3
1267 let _ = 5 % 4
1268
1269 let _ = 1.0 +. 0.0
1270 let _ = 2.0 -. 1.0
1271 let _ = 3.0 *. 2.0
1272 let _ = 4.0 /. 3.0
1273
1274 let _ = 1 == 2 && 2 == 4
1275 let _ = 1 == 2 || 2 == 4
1276 }"#
1277 ));
1278
1279 insta::assert_debug_snapshot!(translate(
1280 r#"pub fn booleans() {
1281 let _ = True
1282 let _ = False
1283
1284 let _ = True == False
1285 let _ = False != True
1286
1287 let _ = 1 > 2
1288 let _ = 1 >= 2
1289 let _ = 1 < 2
1290 let _ = 1 <= 2
1291
1292 let _ = 1.0 >. 2.0
1293 let _ = 1.0 >=. 2.0
1294 let _ = 1.0 <. 2.0
1295 let _ = 1.0 <=. 2.0
1296 }"#
1297 ));
1298
1299 insta::assert_debug_snapshot!(translate(
1300 r#"pub fn guard_operations(n: Int, f: Float) -> Int {
1301 case n, f {
1302 x, y if x == 0 -> 1
1303 x, y if x != 1 -> 2
1304 x, y if x > 2 -> 3
1305 x, y if x >= 3 -> 4
1306 x, y if x < 4 -> 5
1307 x, y if x <= 5 -> 6
1308 x, y if y >. 1.0 -> 7
1309 x, y if y >=. 2.0 -> 8
1310 x, y if y <. 3.0 -> 9
1311 x, y if y <=. 4.0 -> 10
1312 x, y if x > 0 && x < 10 -> 11
1313 x, y if x < 0 || x > 100 -> 12
1314 x, y if !{ x == 0 } -> 13
1315 x, y if x + 1 > 5 -> 14
1316 x, y if x - 1 < 0 -> 15
1317 x, y if x * 2 == 10 -> 16
1318 x, y if x / 2 == 1 -> 17
1319 x, y if y +. 1.0 >. 5.0 -> 18
1320 x, y if y -. 1.0 <. 0.0 -> 19
1321 x, y if y *. 2.0 == 4.0 -> 20
1322 x, y if y /. 2.0 >=. 1.0 -> 21
1323 x, y if x % 2 == 0 -> 22
1324 _, _ -> 0
1325 }
1326 }"#
1327 ));
1328
1329 insta::assert_debug_snapshot!(translate(
1330 r#"pub fn strings() {
1331 let _ = "hello"
1332 let _ = "hello, " <> "world!"
1333 }"#
1334 ));
1335
1336 insta::assert_debug_snapshot!(translate(
1337 r#"pub fn tuples() {
1338 let a = #(1, 2, 3)
1339 let _ = a.1
1340 }"#
1341 ));
1342
1343 insta::assert_debug_snapshot!(translate(
1344 r#"pub type Wibble {
1345 Wibble(a: Int, b: Int)
1346 }
1347
1348 pub fn types(w: Wibble) {
1349 let x = Wibble(a: 10, b: 20)
1350
1351 w.a + x.b
1352 }"#
1353 ));
1354
1355 insta::assert_debug_snapshot!(translate(
1356 r#"pub fn add(lhs: Int, rhs: Int) -> Int {
1357 lhs + rhs
1358 }
1359
1360 pub fn pipe() {
1361 10 |> add(20) |> add(30)
1362 }"#
1363 ));
1364
1365 insta::assert_debug_snapshot!(translate(
1366 r#"pub fn pipe() {
1367 [1, 2, 3, ..[4, 5, 6]]
1368 }"#
1369 ));
1370
1371 insta::assert_debug_snapshot!(translate(
1372 r#"pub type Wibble {
1373 Wibble
1374 Wobble
1375 }
1376
1377 pub fn check(wibble: Wibble) {
1378 case wibble {
1379 Wibble -> 0
1380 Wobble -> 1
1381 }
1382 }"#
1383 ));
1384
1385 insta::assert_debug_snapshot!(translate(
1386 r#"pub type Wibble {
1387 Wibble(x: Int, y: Int, z: Int)
1388 }
1389
1390 pub fn record_update(wibble: Wibble) {
1391 Wibble(..wibble, y: 20)
1392 }"#
1393 ));
1394 }
1395}