Fork of daniellemaywood.uk/gleam — Wasm codegen work
53 kB
1519 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: 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::Tuple {
624 items: elements
625 .iter()
626 .map(|element| self.translate_expression(element))
627 .collect(),
628 type_: Translator::translate_type(type_),
629 },
630 crate::ast::TypedExpr::TupleIndex {
631 location: _,
632 type_,
633 index,
634 tuple,
635 } => Expression::TupleAccess {
636 value: Box::new(self.translate_expression(tuple)),
637 index: *index,
638 type_: Translator::translate_type(type_),
639 },
640 crate::ast::TypedExpr::Todo {
641 location: _,
642 message: _,
643 kind: _,
644 type_: _,
645 } => todo!(),
646 crate::ast::TypedExpr::Panic {
647 location: _,
648 message: _,
649 type_: _,
650 } => todo!(),
651 crate::ast::TypedExpr::Echo {
652 location: _,
653 type_: _,
654 expression: _,
655 message: _,
656 } => todo!(),
657 crate::ast::TypedExpr::BitArray {
658 location: _,
659 type_: _,
660 segments: _,
661 } => todo!(),
662 crate::ast::TypedExpr::RecordUpdate {
663 location: _,
664 type_,
665 record_assignment,
666 constructor,
667 arguments,
668 } => {
669 let mut block = vec![];
670
671 if let Some(assignment) = record_assignment {
672 block.push(self.translate_assignment(assignment));
673 }
674
675 let constructor = self.translate_expression(constructor);
676 let arguments = arguments
677 .iter()
678 .map(|argument| self.translate_expression(&argument.value))
679 .collect();
680
681 block.push(Expression::Call {
682 target: Box::new(constructor),
683 args: arguments,
684 type_: Translator::translate_type(type_),
685 });
686
687 Expression::Block(block)
688 }
689 crate::ast::TypedExpr::NegateBool { location: _, value } => Expression::Equals {
690 lhs: Box::new(self.translate_expression(value)),
691 rhs: Box::new(Expression::Bool { value: false }),
692 },
693 crate::ast::TypedExpr::NegateInt { location: _, value } => Expression::IntSub {
694 lhs: Box::new(Expression::Int {
695 value: BigInt::from(0),
696 }),
697 rhs: Box::new(self.translate_expression(value)),
698 },
699 crate::ast::TypedExpr::Invalid {
700 location: _,
701 type_: _,
702 } => todo!(),
703 }
704 }
705
706 fn translate_assignment(
707 &mut self,
708 assignment: &crate::ast::TypedAssignment,
709 ) -> Expression<IncompleteType> {
710 self.in_decision_scope(|this| {
711 let mut block = vec![];
712
713 let value_var =
714 this.make_tmp_var(Translator::translate_type(&assignment.value.type_()));
715 let value = this.translate_expression(&assignment.value);
716
717 block.push(Expression::Set {
718 name: value_var.clone(),
719 value: Box::new(value),
720 });
721
722 _ = this.decision_map.insert(0, value_var.clone());
723
724 block.push(this.translate_decision(
725 DecisionKind::Let,
726 &[],
727 &assignment.compiled_case.tree,
728 ));
729
730 block.push(Expression::Var(value_var));
731
732 Expression::Block(block)
733 })
734 }
735
736 fn translate_bound_value_type(&mut self, value: &exhaustiveness::BoundValue) -> IncompleteType {
737 match value {
738 exhaustiveness::BoundValue::Variable(variable) => {
739 Translator::translate_type(&variable.type_)
740 }
741 exhaustiveness::BoundValue::LiteralString(_) => IncompleteType::String,
742 exhaustiveness::BoundValue::LiteralInt(_) => IncompleteType::Int,
743 exhaustiveness::BoundValue::LiteralFloat(_) => IncompleteType::Float,
744 exhaustiveness::BoundValue::BitArraySlice {
745 bit_array: _,
746 read_action: _,
747 } => todo!(),
748 }
749 }
750
751 fn translate_decision(
752 &mut self,
753 kind: DecisionKind,
754 clauses: &[crate::ast::TypedClause],
755 decision: &exhaustiveness::Decision,
756 ) -> Expression<IncompleteType> {
757 match decision {
758 exhaustiveness::Decision::Run { body } => {
759 let mut block = vec![];
760
761 for (binding, value) in &body.bindings {
762 let binding_type = self.translate_bound_value_type(value);
763 let binding = self.make_var(binding.clone(), binding_type);
764
765 block.push(self.translate_binding(binding.clone(), value));
766 }
767
768 if kind == DecisionKind::Case {
769 block.push(self.translate_expression(&clauses[body.clause_index].then));
770 }
771
772 Expression::Block(block)
773 }
774 exhaustiveness::Decision::Guard {
775 guard,
776 if_true,
777 if_false,
778 } => {
779 let mut block = vec![];
780
781 let guard = clauses[*guard]
782 .guard
783 .as_ref()
784 .expect("expected clause to have a guard");
785
786 let referenced_in_guard = guard.referenced_variables();
787
788 let (guard_bindings, if_true_bindings): (Vec<_>, Vec<_>) = if_true
789 .bindings
790 .iter()
791 .partition(|(binding, _)| referenced_in_guard.contains(binding));
792
793 for (binding, value) in guard_bindings {
794 let binding_type = self.translate_bound_value_type(value);
795 let binding = self.make_var(binding.clone(), binding_type);
796
797 block.push(self.translate_binding(binding, value));
798 }
799
800 let cond = self.translate_guard(guard);
801
802 let if_true = {
803 let mut block = vec![];
804
805 for (binding, value) in if_true_bindings {
806 let binding_type = self.translate_bound_value_type(value);
807 let binding = self.make_var(binding.clone(), binding_type);
808
809 block.push(self.translate_binding(binding.clone(), value));
810 }
811
812 block.push(self.translate_expression(&clauses[if_true.clause_index].then));
813 block
814 };
815
816 let if_false = self.translate_decision(kind, clauses, if_false);
817
818 block.push(Expression::If {
819 cond: Box::new(cond),
820 then: Box::new(Expression::Block(if_true)),
821 else_: Box::new(if_false),
822 });
823
824 Expression::Block(block)
825 }
826 exhaustiveness::Decision::Switch {
827 var,
828 choices,
829 fallback,
830 fallback_check,
831 } => {
832 let fallback =
833 self.translate_fallback(kind, var, clauses, fallback, fallback_check);
834
835 DoubleEndedIterator::rfold(choices.iter(), fallback, |fallback, (check, then)| {
836 Expression::If {
837 cond: Box::new(
838 self.translate_runtime_check(
839 check,
840 Expression::Var(
841 self.decision_map
842 .get(&var.id)
843 .cloned()
844 .expect("expected variable to be defined"),
845 ),
846 ),
847 ),
848 then: Box::new(self.translate_decision(kind, clauses, then)),
849 else_: Box::new(fallback),
850 }
851 })
852 }
853 exhaustiveness::Decision::Fail => todo!(),
854 }
855 }
856
857 fn translate_fallback(
858 &mut self,
859 kind: DecisionKind,
860 var: &exhaustiveness::Variable,
861 clauses: &[crate::ast::TypedClause],
862 decision: &exhaustiveness::Decision,
863 check: &exhaustiveness::FallbackCheck,
864 ) -> Expression<IncompleteType> {
865 match check {
866 exhaustiveness::FallbackCheck::InfiniteCatchAll
867 | exhaustiveness::FallbackCheck::CatchAll { .. } => {
868 self.translate_decision(kind, clauses, decision)
869 }
870 exhaustiveness::FallbackCheck::RuntimeCheck { check } => {
871 let cond = self.translate_runtime_check(
872 check,
873 Expression::Var(
874 self.decision_map
875 .get(&var.id)
876 .cloned()
877 .expect("expected variable to be defined"),
878 ),
879 );
880
881 let then = self.translate_decision(kind, clauses, decision);
882
883 Expression::If {
884 cond: Box::new(cond),
885 then: Box::new(then.clone()),
886 else_: Box::new(Expression::Panic {
887 type_: then.type_().clone(),
888 }),
889 }
890 }
891 }
892 }
893
894 fn translate_guard(
895 &mut self,
896 guard: &crate::ast::TypedClauseGuard,
897 ) -> Expression<IncompleteType> {
898 match guard {
899 crate::ast::ClauseGuard::Block { location: _, value } => self.translate_guard(value),
900 crate::ast::ClauseGuard::Equals {
901 location: _,
902 left,
903 right,
904 } => {
905 let lhs = self.translate_guard(left);
906 let rhs = self.translate_guard(right);
907
908 Expression::Equals {
909 lhs: Box::new(lhs),
910 rhs: Box::new(rhs),
911 }
912 }
913 crate::ast::ClauseGuard::NotEquals {
914 location: _,
915 left,
916 right,
917 } => Expression::NotEquals {
918 lhs: Box::new(self.translate_guard(left)),
919 rhs: Box::new(self.translate_guard(right)),
920 },
921 crate::ast::ClauseGuard::GtInt {
922 location: _,
923 left,
924 right,
925 } => Expression::IntGt {
926 lhs: Box::new(self.translate_guard(left)),
927 rhs: Box::new(self.translate_guard(right)),
928 },
929 crate::ast::ClauseGuard::GtEqInt {
930 location: _,
931 left,
932 right,
933 } => Expression::IntGtEq {
934 lhs: Box::new(self.translate_guard(left)),
935 rhs: Box::new(self.translate_guard(right)),
936 },
937 crate::ast::ClauseGuard::LtInt {
938 location: _,
939 left,
940 right,
941 } => Expression::IntLt {
942 lhs: Box::new(self.translate_guard(left)),
943 rhs: Box::new(self.translate_guard(right)),
944 },
945 crate::ast::ClauseGuard::LtEqInt {
946 location: _,
947 left,
948 right,
949 } => Expression::IntLtEq {
950 lhs: Box::new(self.translate_guard(left)),
951 rhs: Box::new(self.translate_guard(right)),
952 },
953 crate::ast::ClauseGuard::GtFloat {
954 location: _,
955 left,
956 right,
957 } => Expression::FloatGt {
958 lhs: Box::new(self.translate_guard(left)),
959 rhs: Box::new(self.translate_guard(right)),
960 },
961 crate::ast::ClauseGuard::GtEqFloat {
962 location: _,
963 left,
964 right,
965 } => Expression::FloatGtEq {
966 lhs: Box::new(self.translate_guard(left)),
967 rhs: Box::new(self.translate_guard(right)),
968 },
969 crate::ast::ClauseGuard::LtFloat {
970 location: _,
971 left,
972 right,
973 } => Expression::FloatLt {
974 lhs: Box::new(self.translate_guard(left)),
975 rhs: Box::new(self.translate_guard(right)),
976 },
977 crate::ast::ClauseGuard::LtEqFloat {
978 location: _,
979 left,
980 right,
981 } => Expression::FloatLtEq {
982 lhs: Box::new(self.translate_guard(left)),
983 rhs: Box::new(self.translate_guard(right)),
984 },
985 crate::ast::ClauseGuard::AddInt {
986 location: _,
987 left,
988 right,
989 } => Expression::IntAdd {
990 lhs: Box::new(self.translate_guard(left)),
991 rhs: Box::new(self.translate_guard(right)),
992 },
993 crate::ast::ClauseGuard::AddFloat {
994 location: _,
995 left,
996 right,
997 } => Expression::FloatAdd {
998 lhs: Box::new(self.translate_guard(left)),
999 rhs: Box::new(self.translate_guard(right)),
1000 },
1001 crate::ast::ClauseGuard::SubInt {
1002 location: _,
1003 left,
1004 right,
1005 } => Expression::IntSub {
1006 lhs: Box::new(self.translate_guard(left)),
1007 rhs: Box::new(self.translate_guard(right)),
1008 },
1009 crate::ast::ClauseGuard::SubFloat {
1010 location: _,
1011 left,
1012 right,
1013 } => Expression::FloatSub {
1014 lhs: Box::new(self.translate_guard(left)),
1015 rhs: Box::new(self.translate_guard(right)),
1016 },
1017 crate::ast::ClauseGuard::MultInt {
1018 location: _,
1019 left,
1020 right,
1021 } => Expression::IntMul {
1022 lhs: Box::new(self.translate_guard(left)),
1023 rhs: Box::new(self.translate_guard(right)),
1024 },
1025 crate::ast::ClauseGuard::MultFloat {
1026 location: _,
1027 left,
1028 right,
1029 } => Expression::FloatMul {
1030 lhs: Box::new(self.translate_guard(left)),
1031 rhs: Box::new(self.translate_guard(right)),
1032 },
1033 crate::ast::ClauseGuard::DivInt {
1034 location: _,
1035 left,
1036 right,
1037 } => Expression::IntDiv {
1038 lhs: Box::new(self.translate_guard(left)),
1039 rhs: Box::new(self.translate_guard(right)),
1040 },
1041 crate::ast::ClauseGuard::DivFloat {
1042 location: _,
1043 left,
1044 right,
1045 } => Expression::FloatDiv {
1046 lhs: Box::new(self.translate_guard(left)),
1047 rhs: Box::new(self.translate_guard(right)),
1048 },
1049 crate::ast::ClauseGuard::RemainderInt {
1050 location: _,
1051 left,
1052 right,
1053 } => Expression::IntRem {
1054 lhs: Box::new(self.translate_guard(left)),
1055 rhs: Box::new(self.translate_guard(right)),
1056 },
1057 crate::ast::ClauseGuard::Or {
1058 location: _,
1059 left,
1060 right,
1061 } => Expression::If {
1062 cond: Box::new(self.translate_guard(left)),
1063 then: Box::new(Expression::Bool { value: true }),
1064 else_: Box::new(self.translate_guard(right)),
1065 },
1066 crate::ast::ClauseGuard::And {
1067 location: _,
1068 left,
1069 right,
1070 } => Expression::If {
1071 cond: Box::new(self.translate_guard(left)),
1072 then: Box::new(self.translate_guard(right)),
1073 else_: Box::new(Expression::Bool { value: false }),
1074 },
1075 crate::ast::ClauseGuard::Not {
1076 location: _,
1077 expression,
1078 } => Expression::Equals {
1079 lhs: Box::new(self.translate_guard(expression)),
1080 rhs: Box::new(Expression::Bool { value: false }),
1081 },
1082 crate::ast::ClauseGuard::Var {
1083 location: _,
1084 type_: _,
1085 name,
1086 definition_location: _,
1087 } => Expression::Var(self.get_var(name)),
1088 crate::ast::ClauseGuard::TupleIndex {
1089 location: _,
1090 index,
1091 type_,
1092 tuple,
1093 } => Expression::StructAccess {
1094 value: Box::new(self.translate_guard(tuple)),
1095 index: *index,
1096 type_: Translator::translate_type(type_),
1097 },
1098 crate::ast::ClauseGuard::FieldAccess {
1099 label_location: _,
1100 index,
1101 label: _,
1102 type_,
1103 container,
1104 } => Expression::StructAccess {
1105 value: Box::new(self.translate_guard(container)),
1106 index: index.expect("field access expected an index"),
1107 type_: Translator::translate_type(type_),
1108 },
1109 crate::ast::ClauseGuard::ModuleSelect {
1110 location: _,
1111 type_: _,
1112 label: _,
1113 module_name: _,
1114 module_alias: _,
1115 literal: _,
1116 } => todo!(),
1117 crate::ast::ClauseGuard::Constant(constant) => self.translate_constant(constant),
1118 }
1119 }
1120
1121 fn translate_constant(
1122 &mut self,
1123 constant: &crate::ast::TypedConstant,
1124 ) -> Expression<IncompleteType> {
1125 match constant {
1126 crate::ast::Constant::Int {
1127 location: _,
1128 value: _,
1129 int_value,
1130 } => Expression::Int {
1131 value: int_value.clone(),
1132 },
1133 crate::ast::Constant::Float { location: _, value } => Expression::Float {
1134 value: value.clone(),
1135 },
1136 crate::ast::Constant::String { location: _, value } => Expression::String {
1137 value: value.clone(),
1138 },
1139 crate::ast::Constant::Tuple {
1140 location: _,
1141 elements,
1142 } => Expression::Tuple {
1143 items: elements
1144 .iter()
1145 .map(|element| self.translate_constant(element))
1146 .collect(),
1147 type_: Translator::translate_type(&constant.type_()),
1148 },
1149 crate::ast::Constant::List {
1150 location: _,
1151 elements,
1152 type_,
1153 } => Expression::List {
1154 items: elements
1155 .iter()
1156 .map(|element| self.translate_constant(element))
1157 .collect(),
1158 tail: None,
1159 type_: Translator::translate_type(type_),
1160 },
1161 crate::ast::Constant::Record {
1162 location: _,
1163 module: _,
1164 name: _,
1165 arguments: _,
1166 tag: _,
1167 type_: _,
1168 field_map: _,
1169 record_constructor: _,
1170 } => todo!(),
1171 crate::ast::Constant::BitArray {
1172 location: _,
1173 segments: _,
1174 } => todo!(),
1175 crate::ast::Constant::Var {
1176 location: _,
1177 module: _,
1178 name: _,
1179 constructor: _,
1180 type_: _,
1181 } => todo!(),
1182 crate::ast::Constant::StringConcatenation {
1183 location: _,
1184 left,
1185 right,
1186 } => Expression::StringConcat {
1187 lhs: Box::new(self.translate_constant(left)),
1188 rhs: Box::new(self.translate_constant(right)),
1189 },
1190 crate::ast::Constant::Invalid {
1191 location: _,
1192 type_: _,
1193 } => todo!(),
1194 }
1195 }
1196
1197 fn translate_binding(
1198 &mut self,
1199 binding: Var<IncompleteType>,
1200 value: &exhaustiveness::BoundValue,
1201 ) -> Expression<IncompleteType> {
1202 let value = match value {
1203 exhaustiveness::BoundValue::Variable(variable) => Expression::Var(
1204 self.decision_map
1205 .get(&variable.id)
1206 .cloned()
1207 .expect("expected variable to be defined"),
1208 ),
1209 exhaustiveness::BoundValue::LiteralString(_eco_string) => todo!(),
1210 exhaustiveness::BoundValue::LiteralInt(big_int) => Expression::Int {
1211 value: big_int.clone(),
1212 },
1213 exhaustiveness::BoundValue::LiteralFloat(_eco_string) => todo!(),
1214 exhaustiveness::BoundValue::BitArraySlice {
1215 bit_array: _,
1216 read_action: _,
1217 } => todo!(),
1218 };
1219
1220 Expression::Set {
1221 name: binding,
1222 value: Box::new(value),
1223 }
1224 }
1225
1226 fn translate_runtime_check(
1227 &mut self,
1228 check: &exhaustiveness::RuntimeCheck,
1229 against: Expression<IncompleteType>,
1230 ) -> Expression<IncompleteType> {
1231 match check {
1232 exhaustiveness::RuntimeCheck::Int { value } => {
1233 let value = parse::parse_int_value(&value).expect("unable to parse integer value");
1234
1235 Expression::Equals {
1236 lhs: Box::new(against),
1237 rhs: Box::new(Expression::Int { value }),
1238 }
1239 }
1240 exhaustiveness::RuntimeCheck::Float { value } => Expression::Equals {
1241 lhs: Box::new(against),
1242 rhs: Box::new(Expression::Float {
1243 value: value.clone(),
1244 }),
1245 },
1246 exhaustiveness::RuntimeCheck::String { value } => Expression::Equals {
1247 lhs: Box::new(against),
1248 rhs: Box::new(Expression::String {
1249 value: value.clone(),
1250 }),
1251 },
1252 exhaustiveness::RuntimeCheck::StringPrefix { prefix: _, rest: _ } => todo!(),
1253 exhaustiveness::RuntimeCheck::Tuple {
1254 size: _,
1255 elements: _,
1256 } => todo!(),
1257 exhaustiveness::RuntimeCheck::BitArray { test: _ } => todo!(),
1258 exhaustiveness::RuntimeCheck::Variant {
1259 match_: _,
1260 index,
1261 labels: _,
1262 fields,
1263 } => {
1264 let mut block = vec![];
1265
1266 for (index, field) in fields.iter().enumerate() {
1267 let var_type = Translator::translate_type(&field.type_);
1268 let var = self.make_tmp_var(var_type.clone());
1269
1270 _ = self.decision_map.insert(field.id, var.clone());
1271
1272 block.push(Expression::Set {
1273 name: var,
1274 value: Box::new(Expression::StructAccess {
1275 value: Box::new(against.clone()),
1276 index: index as u64,
1277 type_: var_type,
1278 }),
1279 });
1280 }
1281
1282 block.push(Expression::Equals {
1283 lhs: Box::new(Expression::Int {
1284 value: BigInt::from(*index),
1285 }),
1286 rhs: Box::new(Expression::StructTag {
1287 value: Box::new(against),
1288 }),
1289 });
1290
1291 Expression::Block(block)
1292 }
1293 exhaustiveness::RuntimeCheck::NonEmptyList { first: _, rest: _ } => todo!(),
1294 exhaustiveness::RuntimeCheck::EmptyList => todo!(),
1295 }
1296 }
1297}
1298
1299#[cfg(test)]
1300mod tests {
1301 use std::collections::{HashMap, HashSet};
1302
1303 use super::*;
1304 use crate::analyse::TargetSupport;
1305 use crate::build::{Origin, Target};
1306 use crate::config::PackageConfig;
1307 use crate::line_numbers::LineNumbers;
1308 use crate::parse::parse_module;
1309 use crate::type_::PRELUDE_MODULE_NAME;
1310 use crate::uid::UniqueIdGenerator;
1311 use crate::warning::{TypeWarningEmitter, WarningEmitter};
1312 use camino::Utf8PathBuf;
1313
1314 fn compile_module(src: &str) -> crate::ast::TypedModule {
1315 use crate::type_::build_prelude;
1316 let parsed = parse_module(Utf8PathBuf::from("test/path"), src, &WarningEmitter::null())
1317 .expect("syntax error");
1318 let ast = parsed.module;
1319 let ids = UniqueIdGenerator::new();
1320 let mut config = PackageConfig::default();
1321 config.name = "thepackage".into();
1322 let mut modules = im::HashMap::new();
1323 let _ = modules.insert(PRELUDE_MODULE_NAME.into(), build_prelude(&ids));
1324 let line_numbers = LineNumbers::new(src);
1325
1326 crate::analyse::ModuleAnalyzerConstructor::<()> {
1327 target: Target::Erlang,
1328 ids: &ids,
1329 origin: Origin::Src,
1330 importable_modules: &modules,
1331 warnings: &TypeWarningEmitter::null(),
1332 direct_dependencies: &HashMap::new(),
1333 dev_dependencies: &HashSet::new(),
1334 target_support: TargetSupport::Enforced,
1335 package_config: &config,
1336 }
1337 .infer_module(ast, line_numbers, "".into())
1338 .expect("should successfully infer")
1339 }
1340
1341 fn translate(src: &str) -> Module<IncompleteType> {
1342 let typed_module = compile_module(src);
1343
1344 Translator::new(&typed_module).translate()
1345 }
1346
1347 #[test]
1348 fn translates_samples() {
1349 insta::assert_debug_snapshot!(translate(
1350 r#"pub fn add(lhs: Int, rhs: Int) -> Int {
1351 lhs + rhs
1352 }"#
1353 ));
1354
1355 insta::assert_debug_snapshot!(translate(
1356 r#"pub fn some_case(n: Int) -> Int {
1357 case n {
1358 0 -> 2
1359 1 -> 4
1360 _ -> 6
1361 }
1362 }"#
1363 ));
1364
1365 insta::assert_debug_snapshot!(translate(
1366 r#"pub fn some_case(n: Int) -> Int {
1367 case n {
1368 y if y == 0 -> 2
1369 1 -> 4
1370 _ -> 6
1371 }
1372 }"#
1373 ));
1374
1375 insta::assert_debug_snapshot!(translate(
1376 r#"pub fn fib(n: Int) -> Int {
1377 case n {
1378 0 -> 0
1379 1 -> 1
1380 _ -> fib(n - 1) + fib(n - 2)
1381 }
1382 }"#
1383 ));
1384
1385 insta::assert_debug_snapshot!(translate(
1386 r#"pub fn maths() {
1387 let _ = 1 + 0
1388 let _ = 2 - 1
1389 let _ = 3 * 2
1390 let _ = 4 / 3
1391 let _ = 5 % 4
1392
1393 let _ = 1.0 +. 0.0
1394 let _ = 2.0 -. 1.0
1395 let _ = 3.0 *. 2.0
1396 let _ = 4.0 /. 3.0
1397
1398 let _ = 1 == 2 && 2 == 4
1399 let _ = 1 == 2 || 2 == 4
1400 }"#
1401 ));
1402
1403 insta::assert_debug_snapshot!(translate(
1404 r#"pub fn booleans() {
1405 let _ = True
1406 let _ = False
1407
1408 let _ = True == False
1409 let _ = False != True
1410
1411 let _ = 1 > 2
1412 let _ = 1 >= 2
1413 let _ = 1 < 2
1414 let _ = 1 <= 2
1415
1416 let _ = 1.0 >. 2.0
1417 let _ = 1.0 >=. 2.0
1418 let _ = 1.0 <. 2.0
1419 let _ = 1.0 <=. 2.0
1420 }"#
1421 ));
1422
1423 insta::assert_debug_snapshot!(translate(
1424 r#"pub fn guard_operations(n: Int, f: Float) -> Int {
1425 case n, f {
1426 x, y if x == 0 -> 1
1427 x, y if x != 1 -> 2
1428 x, y if x > 2 -> 3
1429 x, y if x >= 3 -> 4
1430 x, y if x < 4 -> 5
1431 x, y if x <= 5 -> 6
1432 x, y if y >. 1.0 -> 7
1433 x, y if y >=. 2.0 -> 8
1434 x, y if y <. 3.0 -> 9
1435 x, y if y <=. 4.0 -> 10
1436 x, y if x > 0 && x < 10 -> 11
1437 x, y if x < 0 || x > 100 -> 12
1438 x, y if !{ x == 0 } -> 13
1439 x, y if x + 1 > 5 -> 14
1440 x, y if x - 1 < 0 -> 15
1441 x, y if x * 2 == 10 -> 16
1442 x, y if x / 2 == 1 -> 17
1443 x, y if y +. 1.0 >. 5.0 -> 18
1444 x, y if y -. 1.0 <. 0.0 -> 19
1445 x, y if y *. 2.0 == 4.0 -> 20
1446 x, y if y /. 2.0 >=. 1.0 -> 21
1447 x, y if x % 2 == 0 -> 22
1448 _, _ -> 0
1449 }
1450 }"#
1451 ));
1452
1453 insta::assert_debug_snapshot!(translate(
1454 r#"pub fn strings() {
1455 let _ = "hello"
1456 let _ = "hello, " <> "world!"
1457 }"#
1458 ));
1459
1460 insta::assert_debug_snapshot!(translate(
1461 r#"pub fn tuples() {
1462 let a = #(1, 2, 3)
1463 let _ = a.1
1464 }"#
1465 ));
1466
1467 insta::assert_debug_snapshot!(translate(
1468 r#"pub type Wibble {
1469 Wibble(a: Int, b: Int)
1470 }
1471
1472 pub fn types(w: Wibble) {
1473 let x = Wibble(a: 10, b: 20)
1474
1475 w.a + x.b
1476 }"#
1477 ));
1478
1479 insta::assert_debug_snapshot!(translate(
1480 r#"pub fn add(lhs: Int, rhs: Int) -> Int {
1481 lhs + rhs
1482 }
1483
1484 pub fn pipe() {
1485 10 |> add(20) |> add(30)
1486 }"#
1487 ));
1488
1489 insta::assert_debug_snapshot!(translate(
1490 r#"pub fn pipe() {
1491 [1, 2, 3, ..[4, 5, 6]]
1492 }"#
1493 ));
1494
1495 insta::assert_debug_snapshot!(translate(
1496 r#"pub type Wibble {
1497 Wibble
1498 Wobble
1499 }
1500
1501 pub fn check(wibble: Wibble) {
1502 case wibble {
1503 Wibble -> 0
1504 Wobble -> 1
1505 }
1506 }"#
1507 ));
1508
1509 insta::assert_debug_snapshot!(translate(
1510 r#"pub type Wibble {
1511 Wibble(x: Int, y: Int, z: Int)
1512 }
1513
1514 pub fn record_update(wibble: Wibble) {
1515 Wibble(..wibble, y: 20)
1516 }"#
1517 ));
1518 }
1519}