Fork of daniellemaywood.uk/gleam — Wasm codegen work
2

Configure Feed

Select the types of activity you want to include in your feed.

Translate more parts of a case expression

author
Danielle Maywood
committer
nandi
date (Jul 26, 2026, 12:11 PM -0700) commit 969373cc parent 831ff127 change-id nwkwmtoq
+580 -29
+344 -18
compiler-core/src/cranelift/mir.rs
··· 7 7 8 8 use crate::{ast, exhaustiveness, parse, type_}; 9 9 10 - #[derive(Debug, Default)] 10 + #[derive(Debug, PartialEq, Eq, Default)] 11 11 pub struct Module { 12 - functions: Vec<Function>, 12 + pub functions: Vec<Function>, 13 13 } 14 14 15 - #[derive(Debug)] 15 + #[derive(Debug, PartialEq, Eq)] 16 16 pub struct Function { 17 17 pub name: EcoString, 18 18 pub return_type: Type, ··· 20 20 pub body: Expression, 21 21 } 22 22 23 - #[derive(Debug)] 23 + #[derive(Debug, PartialEq, Eq)] 24 24 pub struct FunctionParameter { 25 25 pub type_: Type, 26 26 pub name: Option<EcoString>, 27 27 } 28 28 29 - #[derive(Debug, Default)] 29 + #[derive(Debug, Default, PartialEq, Eq)] 30 30 pub struct Translator { 31 31 module: Module, 32 32 } 33 33 34 - #[derive(Debug, Clone, Copy)] 34 + #[derive(Debug, Clone, Copy, PartialEq, Eq)] 35 35 pub enum Type { 36 36 Int, 37 37 Float, ··· 39 39 Generic, 40 40 } 41 41 42 - #[derive(Debug)] 42 + #[derive(Debug, PartialEq, Eq)] 43 43 pub enum Expression { 44 44 Block(Vec<Expression>), 45 45 ··· 356 356 } 357 357 } 358 358 359 - fn translate_assignment(&mut self, assignment: &ast::TypedAssignment) -> Expression { 360 - dbg!(&assignment.compiled_case); 361 - 359 + fn translate_assignment(&mut self, _assignment: &ast::TypedAssignment) -> Expression { 362 360 todo!() 363 361 } 364 362 ··· 392 390 Expression::Block(block) 393 391 } 394 392 exhaustiveness::Decision::Guard { 395 - guard: _, 396 - if_true: _, 397 - if_false: _, 393 + guard, 394 + if_true, 395 + if_false, 398 396 } => { 399 - todo!() 397 + let mut block = vec![]; 398 + 399 + let guard = clauses[*guard] 400 + .guard 401 + .as_ref() 402 + .expect("expected clause to have a guard"); 403 + 404 + let referenced_in_guard = guard.referenced_variables(); 405 + 406 + let (guard_bindings, if_true_bindings): (Vec<_>, Vec<_>) = if_true 407 + .bindings 408 + .iter() 409 + .partition(|(binding, _)| referenced_in_guard.contains(binding)); 410 + 411 + for (binding, value) in guard_bindings { 412 + block.push(self.translate_binding(subjects, binding.clone(), value)); 413 + } 414 + 415 + let cond = self.translate_guard(guard); 416 + 417 + let if_true = { 418 + let mut block = vec![]; 419 + 420 + for (binding, value) in if_true_bindings { 421 + block.push(self.translate_binding(subjects, binding.clone(), value)); 422 + } 423 + 424 + block.push(self.translate_expression(&clauses[if_true.clause_index].then)); 425 + block 426 + }; 427 + 428 + let if_false = self.translate_decision(subjects, clauses, if_false); 429 + 430 + block.push(Expression::If { 431 + cond: Box::new(cond), 432 + then: Box::new(Expression::Block(if_true)), 433 + else_: Box::new(if_false), 434 + }); 435 + 436 + Expression::Block(block) 400 437 } 401 438 exhaustiveness::Decision::Switch { 402 439 var, ··· 406 443 } => { 407 444 let fallback = self.translate_decision(subjects, clauses, fallback); 408 445 409 - choices 410 - .iter() 411 - .rfold(fallback, |fallback, (check, then)| Expression::If { 446 + DoubleEndedIterator::rfold(choices.iter(), fallback, |fallback, (check, then)| { 447 + Expression::If { 412 448 cond: Box::new(self.translate_runtime_check( 413 449 check, 414 450 Expression::Var { ··· 417 453 )), 418 454 then: Box::new(self.translate_decision(subjects, clauses, then)), 419 455 else_: Box::new(fallback), 420 - }) 456 + } 457 + }) 421 458 } 422 459 exhaustiveness::Decision::Fail => todo!(), 423 460 } 424 461 } 425 462 463 + fn translate_guard(&mut self, guard: &ast::TypedClauseGuard) -> Expression { 464 + match guard { 465 + ast::ClauseGuard::Block { 466 + location: _, 467 + value: _, 468 + } => todo!(), 469 + ast::ClauseGuard::Equals { 470 + location: _, 471 + left, 472 + right, 473 + } => { 474 + let lhs = self.translate_guard(left); 475 + let rhs = self.translate_guard(right); 476 + 477 + Expression::Equals { 478 + lhs: Box::new(lhs), 479 + rhs: Box::new(rhs), 480 + } 481 + } 482 + ast::ClauseGuard::NotEquals { 483 + location: _, 484 + left: _, 485 + right: _, 486 + } => todo!(), 487 + ast::ClauseGuard::GtInt { 488 + location: _, 489 + left: _, 490 + right: _, 491 + } => todo!(), 492 + ast::ClauseGuard::GtEqInt { 493 + location: _, 494 + left: _, 495 + right: _, 496 + } => todo!(), 497 + ast::ClauseGuard::LtInt { 498 + location: _, 499 + left: _, 500 + right: _, 501 + } => todo!(), 502 + ast::ClauseGuard::LtEqInt { 503 + location: _, 504 + left: _, 505 + right: _, 506 + } => todo!(), 507 + ast::ClauseGuard::GtFloat { 508 + location: _, 509 + left: _, 510 + right: _, 511 + } => todo!(), 512 + ast::ClauseGuard::GtEqFloat { 513 + location: _, 514 + left: _, 515 + right: _, 516 + } => todo!(), 517 + ast::ClauseGuard::LtFloat { 518 + location: _, 519 + left: _, 520 + right: _, 521 + } => todo!(), 522 + ast::ClauseGuard::LtEqFloat { 523 + location: _, 524 + left: _, 525 + right: _, 526 + } => todo!(), 527 + ast::ClauseGuard::AddInt { 528 + location: _, 529 + left: _, 530 + right: _, 531 + } => todo!(), 532 + ast::ClauseGuard::AddFloat { 533 + location: _, 534 + left: _, 535 + right: _, 536 + } => todo!(), 537 + ast::ClauseGuard::SubInt { 538 + location: _, 539 + left: _, 540 + right: _, 541 + } => todo!(), 542 + ast::ClauseGuard::SubFloat { 543 + location: _, 544 + left: _, 545 + right: _, 546 + } => todo!(), 547 + ast::ClauseGuard::MultInt { 548 + location: _, 549 + left: _, 550 + right: _, 551 + } => todo!(), 552 + ast::ClauseGuard::MultFloat { 553 + location: _, 554 + left: _, 555 + right: _, 556 + } => todo!(), 557 + ast::ClauseGuard::DivInt { 558 + location: _, 559 + left: _, 560 + right: _, 561 + } => todo!(), 562 + ast::ClauseGuard::DivFloat { 563 + location: _, 564 + left: _, 565 + right: _, 566 + } => todo!(), 567 + ast::ClauseGuard::RemainderInt { 568 + location: _, 569 + left: _, 570 + right: _, 571 + } => todo!(), 572 + ast::ClauseGuard::Or { 573 + location: _, 574 + left: _, 575 + right: _, 576 + } => todo!(), 577 + ast::ClauseGuard::And { 578 + location: _, 579 + left: _, 580 + right: _, 581 + } => todo!(), 582 + ast::ClauseGuard::Not { 583 + location: _, 584 + expression: _, 585 + } => todo!(), 586 + ast::ClauseGuard::Var { 587 + location: _, 588 + type_: _, 589 + name, 590 + definition_location: _, 591 + } => Expression::Var { name: name.clone() }, 592 + ast::ClauseGuard::TupleIndex { 593 + location: _, 594 + index: _, 595 + type_: _, 596 + tuple: _, 597 + } => todo!(), 598 + ast::ClauseGuard::FieldAccess { 599 + label_location: _, 600 + index: _, 601 + label: _, 602 + type_: _, 603 + container: _, 604 + } => todo!(), 605 + ast::ClauseGuard::ModuleSelect { 606 + location: _, 607 + type_: _, 608 + label: _, 609 + module_name: _, 610 + module_alias: _, 611 + literal: _, 612 + } => todo!(), 613 + ast::ClauseGuard::Constant(constant) => self.translate_constant(constant), 614 + } 615 + } 616 + 617 + fn translate_constant(&mut self, constant: &ast::TypedConstant) -> Expression { 618 + match constant { 619 + ast::Constant::Int { 620 + location: _, 621 + value: _, 622 + int_value, 623 + } => Expression::Int { 624 + value: int_value.clone(), 625 + }, 626 + ast::Constant::Float { 627 + location: _, 628 + value: _, 629 + } => todo!(), 630 + ast::Constant::String { 631 + location: _, 632 + value: _, 633 + } => todo!(), 634 + ast::Constant::Tuple { 635 + location: _, 636 + elements: _, 637 + } => todo!(), 638 + ast::Constant::List { 639 + location: _, 640 + elements: _, 641 + type_: _, 642 + } => todo!(), 643 + ast::Constant::Record { 644 + location: _, 645 + module: _, 646 + name: _, 647 + arguments: _, 648 + tag: _, 649 + type_: _, 650 + field_map: _, 651 + record_constructor: _, 652 + } => todo!(), 653 + ast::Constant::BitArray { 654 + location: _, 655 + segments: _, 656 + } => todo!(), 657 + ast::Constant::Var { 658 + location: _, 659 + module: _, 660 + name: _, 661 + constructor: _, 662 + type_: _, 663 + } => todo!(), 664 + ast::Constant::StringConcatenation { 665 + location: _, 666 + left: _, 667 + right: _, 668 + } => todo!(), 669 + ast::Constant::Invalid { 670 + location: _, 671 + type_: _, 672 + } => todo!(), 673 + } 674 + } 675 + 426 676 fn translate_binding( 427 677 &mut self, 428 678 subjects: &[EcoString], ··· 483 733 } 484 734 } 485 735 } 736 + 737 + #[cfg(test)] 738 + mod tests { 739 + use super::*; 740 + use crate::analyse::TargetSupport; 741 + use crate::build::{Origin, Target}; 742 + use crate::config::PackageConfig; 743 + use crate::line_numbers::LineNumbers; 744 + use crate::parse::parse_module; 745 + use crate::type_::PRELUDE_MODULE_NAME; 746 + use crate::uid::UniqueIdGenerator; 747 + use crate::warning::{TypeWarningEmitter, WarningEmitter}; 748 + use camino::Utf8PathBuf; 749 + 750 + fn compile_module(src: &str) -> ast::TypedModule { 751 + use crate::type_::build_prelude; 752 + let parsed = parse_module(Utf8PathBuf::from("test/path"), src, &WarningEmitter::null()) 753 + .expect("syntax error"); 754 + let ast = parsed.module; 755 + let ids = UniqueIdGenerator::new(); 756 + let mut config = PackageConfig::default(); 757 + config.name = "thepackage".into(); 758 + let mut modules = im::HashMap::new(); 759 + let _ = modules.insert(PRELUDE_MODULE_NAME.into(), build_prelude(&ids)); 760 + let line_numbers = LineNumbers::new(src); 761 + 762 + crate::analyse::ModuleAnalyzerConstructor::<()> { 763 + target: Target::Erlang, 764 + ids: &ids, 765 + origin: Origin::Src, 766 + importable_modules: &modules, 767 + warnings: &TypeWarningEmitter::null(), 768 + direct_dependencies: &std::collections::HashMap::new(), 769 + dev_dependencies: &std::collections::HashSet::new(), 770 + target_support: TargetSupport::Enforced, 771 + package_config: &config, 772 + } 773 + .infer_module(ast, line_numbers, "".into()) 774 + .expect("should successfully infer") 775 + } 776 + 777 + fn translate(src: &str) -> Module { 778 + let typed_module = compile_module(src); 779 + 780 + Translator::default().translate(&typed_module) 781 + } 782 + 783 + #[test] 784 + fn translates_samples() { 785 + insta::assert_debug_snapshot!(translate( 786 + r#"pub fn add(lhs: Int, rhs: Int) -> Int { 787 + lhs + rhs 788 + }"# 789 + )); 790 + 791 + insta::assert_debug_snapshot!(translate( 792 + r#"pub fn some_case(n: Int) -> Int { 793 + case n { 794 + 0 -> 2 795 + 1 -> 4 796 + _ -> 6 797 + } 798 + }"# 799 + )); 800 + 801 + insta::assert_debug_snapshot!(translate( 802 + r#"pub fn some_case(n: Int) -> Int { 803 + case n { 804 + y if y == 0 -> 2 805 + 1 -> 4 806 + _ -> 6 807 + } 808 + }"# 809 + )); 810 + } 811 + }
+75
compiler-core/src/cranelift/snapshots/gleam_core__cranelift__mir__tests__translates_samples-2.snap
··· 1 + --- 2 + source: compiler-core/src/cranelift/mir.rs 3 + expression: "translate(r#\"pub fn some_case(n: Int) -> Int {\n case n {\n 0 -> 2\n 1 -> 4\n _ -> 6\n }\n }\"#)" 4 + --- 5 + Module { 6 + functions: [ 7 + Function { 8 + name: "some_case", 9 + return_type: Int, 10 + parameters: [ 11 + FunctionParameter { 12 + type_: Int, 13 + name: Some( 14 + "n", 15 + ), 16 + }, 17 + ], 18 + body: Block( 19 + [ 20 + Block( 21 + [ 22 + Set { 23 + name: "_tmp0", 24 + value: Var { 25 + name: "n", 26 + }, 27 + }, 28 + If { 29 + cond: Equals { 30 + lhs: Var { 31 + name: "_tmp0", 32 + }, 33 + rhs: Int { 34 + value: 0, 35 + }, 36 + }, 37 + then: Block( 38 + [ 39 + Int { 40 + value: 2, 41 + }, 42 + ], 43 + ), 44 + else_: If { 45 + cond: Equals { 46 + lhs: Var { 47 + name: "_tmp0", 48 + }, 49 + rhs: Int { 50 + value: 1, 51 + }, 52 + }, 53 + then: Block( 54 + [ 55 + Int { 56 + value: 4, 57 + }, 58 + ], 59 + ), 60 + else_: Block( 61 + [ 62 + Int { 63 + value: 6, 64 + }, 65 + ], 66 + ), 67 + }, 68 + }, 69 + ], 70 + ), 71 + ], 72 + ), 73 + }, 74 + ], 75 + }
+85
compiler-core/src/cranelift/snapshots/gleam_core__cranelift__mir__tests__translates_samples-3.snap
··· 1 + --- 2 + source: compiler-core/src/cranelift/mir.rs 3 + expression: "translate(r#\"pub fn some_case(n: Int) -> Int {\n case n {\n y if y == 0 -> 2\n 1 -> 4\n _ -> 6\n }\n }\"#)" 4 + --- 5 + Module { 6 + functions: [ 7 + Function { 8 + name: "some_case", 9 + return_type: Int, 10 + parameters: [ 11 + FunctionParameter { 12 + type_: Int, 13 + name: Some( 14 + "n", 15 + ), 16 + }, 17 + ], 18 + body: Block( 19 + [ 20 + Block( 21 + [ 22 + Set { 23 + name: "_tmp0", 24 + value: Var { 25 + name: "n", 26 + }, 27 + }, 28 + Block( 29 + [ 30 + Set { 31 + name: "y", 32 + value: Var { 33 + name: "_tmp0", 34 + }, 35 + }, 36 + If { 37 + cond: Equals { 38 + lhs: Var { 39 + name: "y", 40 + }, 41 + rhs: Int { 42 + value: 0, 43 + }, 44 + }, 45 + then: Block( 46 + [ 47 + Int { 48 + value: 2, 49 + }, 50 + ], 51 + ), 52 + else_: If { 53 + cond: Equals { 54 + lhs: Var { 55 + name: "_tmp0", 56 + }, 57 + rhs: Int { 58 + value: 1, 59 + }, 60 + }, 61 + then: Block( 62 + [ 63 + Int { 64 + value: 4, 65 + }, 66 + ], 67 + ), 68 + else_: Block( 69 + [ 70 + Int { 71 + value: 6, 72 + }, 73 + ], 74 + ), 75 + }, 76 + }, 77 + ], 78 + ), 79 + ], 80 + ), 81 + ], 82 + ), 83 + }, 84 + ], 85 + }
+38
compiler-core/src/cranelift/snapshots/gleam_core__cranelift__mir__tests__translates_samples.snap
··· 1 + --- 2 + source: compiler-core/src/cranelift/mir.rs 3 + expression: "translate(r#\"pub fn add(lhs: Int, rhs: Int) -> Int {\n lhs + rhs\n }\"#)" 4 + --- 5 + Module { 6 + functions: [ 7 + Function { 8 + name: "add", 9 + return_type: Int, 10 + parameters: [ 11 + FunctionParameter { 12 + type_: Int, 13 + name: Some( 14 + "lhs", 15 + ), 16 + }, 17 + FunctionParameter { 18 + type_: Int, 19 + name: Some( 20 + "rhs", 21 + ), 22 + }, 23 + ], 24 + body: Block( 25 + [ 26 + IntAdd { 27 + lhs: Var { 28 + name: "lhs", 29 + }, 30 + rhs: Var { 31 + name: "rhs", 32 + }, 33 + }, 34 + ], 35 + ), 36 + }, 37 + ], 38 + }
+3 -1
compiler-core/src/package_interface/snapshots/gleam_core__package_interface__tests__generic_function.snap
··· 35 35 "gleam": true, 36 36 "uses-erlang-externals": false, 37 37 "uses-javascript-externals": false, 38 + "uses-cranelift-externals": false, 38 39 "can-run-on-erlang": true, 39 - "can-run-on-javascript": true 40 + "can-run-on-javascript": true, 41 + "can-run-on-cranelift": true 40 42 }, 41 43 "parameters": [], 42 44 "return": {
+3 -1
compiler-core/src/package_interface/snapshots/gleam_core__package_interface__tests__imported_aliased_type_keeps_original_name.snap
··· 20 20 "gleam": true, 21 21 "uses-erlang-externals": false, 22 22 "uses-javascript-externals": false, 23 + "uses-cranelift-externals": false, 23 24 "can-run-on-erlang": true, 24 - "can-run-on-javascript": true 25 + "can-run-on-javascript": true, 26 + "can-run-on-cranelift": true 25 27 }, 26 28 "parameters": [], 27 29 "return": {
+3 -1
compiler-core/src/package_interface/snapshots/gleam_core__package_interface__tests__imported_type.snap
··· 20 20 "gleam": true, 21 21 "uses-erlang-externals": false, 22 22 "uses-javascript-externals": false, 23 + "uses-cranelift-externals": false, 23 24 "can-run-on-erlang": true, 24 - "can-run-on-javascript": true 25 + "can-run-on-javascript": true, 26 + "can-run-on-cranelift": true 25 27 }, 26 28 "parameters": [], 27 29 "return": {
+3 -1
compiler-core/src/package_interface/snapshots/gleam_core__package_interface__tests__labelled_function_parameters.snap
··· 20 20 "gleam": true, 21 21 "uses-erlang-externals": false, 22 22 "uses-javascript-externals": false, 23 + "uses-cranelift-externals": false, 23 24 "can-run-on-erlang": true, 24 - "can-run-on-javascript": true 25 + "can-run-on-javascript": true, 26 + "can-run-on-cranelift": true 25 27 }, 26 28 "parameters": [ 27 29 {
+3 -1
compiler-core/src/package_interface/snapshots/gleam_core__package_interface__tests__multiple_type_variables.snap
··· 27 27 "gleam": true, 28 28 "uses-erlang-externals": false, 29 29 "uses-javascript-externals": false, 30 + "uses-cranelift-externals": false, 30 31 "can-run-on-erlang": true, 31 - "can-run-on-javascript": true 32 + "can-run-on-javascript": true, 33 + "can-run-on-cranelift": true 32 34 }, 33 35 "parameters": [ 34 36 {
+3 -1
compiler-core/src/package_interface/snapshots/gleam_core__package_interface__tests__package_documentation_is_included.snap
··· 23 23 "gleam": true, 24 24 "uses-erlang-externals": false, 25 25 "uses-javascript-externals": false, 26 + "uses-cranelift-externals": false, 26 27 "can-run-on-erlang": true, 27 - "can-run-on-javascript": true 28 + "can-run-on-javascript": true, 29 + "can-run-on-cranelift": true 28 30 }, 29 31 "parameters": [], 30 32 "return": {
+12 -4
compiler-core/src/package_interface/snapshots/gleam_core__package_interface__tests__prelude_types.snap
··· 19 19 "gleam": true, 20 20 "uses-erlang-externals": false, 21 21 "uses-javascript-externals": false, 22 + "uses-cranelift-externals": false, 22 23 "can-run-on-erlang": true, 23 - "can-run-on-javascript": true 24 + "can-run-on-javascript": true, 25 + "can-run-on-cranelift": true 24 26 }, 25 27 "type": { 26 28 "kind": "named", ··· 37 39 "gleam": true, 38 40 "uses-erlang-externals": false, 39 41 "uses-javascript-externals": false, 42 + "uses-cranelift-externals": false, 40 43 "can-run-on-erlang": true, 41 - "can-run-on-javascript": true 44 + "can-run-on-javascript": true, 45 + "can-run-on-cranelift": true 42 46 }, 43 47 "type": { 44 48 "kind": "named", ··· 55 59 "gleam": true, 56 60 "uses-erlang-externals": false, 57 61 "uses-javascript-externals": false, 62 + "uses-cranelift-externals": false, 58 63 "can-run-on-erlang": true, 59 - "can-run-on-javascript": true 64 + "can-run-on-javascript": true, 65 + "can-run-on-cranelift": true 60 66 }, 61 67 "type": { 62 68 "kind": "named", ··· 73 79 "gleam": true, 74 80 "uses-erlang-externals": false, 75 81 "uses-javascript-externals": false, 82 + "uses-cranelift-externals": false, 76 83 "can-run-on-erlang": true, 77 - "can-run-on-javascript": true 84 + "can-run-on-javascript": true, 85 + "can-run-on-cranelift": true 78 86 }, 79 87 "type": { 80 88 "kind": "named",
+4
compiler-core/src/parse/snapshots/gleam_core__parse__tests__const_string_concat.snap
··· 36 36 gleam: true, 37 37 can_run_on_erlang: true, 38 38 can_run_on_javascript: true, 39 + can_run_on_cranelift: true, 39 40 uses_erlang_externals: false, 40 41 uses_javascript_externals: false, 42 + uses_cranelift_externals: false, 41 43 }, 42 44 }, 43 45 ), ··· 87 89 gleam: true, 88 90 can_run_on_erlang: true, 89 91 can_run_on_javascript: true, 92 + can_run_on_cranelift: true, 90 93 uses_erlang_externals: false, 91 94 uses_javascript_externals: false, 95 + uses_cranelift_externals: false, 92 96 }, 93 97 }, 94 98 ),
+3
compiler-core/src/parse/snapshots/gleam_core__parse__tests__record_access_no_label.snap
··· 158 158 documentation: None, 159 159 external_erlang: None, 160 160 external_javascript: None, 161 + external_cranelift: None, 161 162 implementations: Implementations { 162 163 gleam: true, 163 164 can_run_on_erlang: true, 164 165 can_run_on_javascript: true, 166 + can_run_on_cranelift: true, 165 167 uses_erlang_externals: false, 166 168 uses_javascript_externals: false, 169 + uses_cranelift_externals: false, 167 170 }, 168 171 purity: Pure, 169 172 },
+1 -1
test/project_cranelift/src/project_cranelift.gleam
··· 1 1 pub fn add(lhs: Int, rhs: Int) -> Int { 2 2 case lhs { 3 - 0 -> 10 3 + x if x == 0 -> 10 4 4 _ -> rhs 5 5 } 6 6 }