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