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

Configure Feed

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

Rebase wasm work onto gleam-lang/gleam main and fix 1.18 API drift.

Port Target::Wasm exhaustiveness, external_wasm plumbing, and MIR/formatter
matches for TypedDefinitions, ClauseGuard::BinaryOperator, and related AST
changes after landing on v1.18.0-rc2.

author
nandi
date (Jul 26, 2026, 12:36 PM -0700) commit 82114d29 parent 971d5dd6 change-id ztsnxloq
+499 -223
+143 -3
Cargo.lock
··· 551 551 dependencies = [ 552 552 "serde", 553 553 "termcolor", 554 - "unicode-width", 554 + "unicode-width 0.1.11", 555 555 ] 556 556 557 557 [[package]] ··· 1265 1265 version = "1.18.0-rc2" 1266 1266 dependencies = [ 1267 1267 "age", 1268 + "anyhow", 1268 1269 "askama", 1269 1270 "async-trait", 1270 1271 "base16", ··· 1318 1319 "tracing", 1319 1320 "unicode-segmentation", 1320 1321 "vec1", 1322 + "wasm-encoder 0.239.0", 1323 + "wat", 1324 + "wit-component", 1325 + "wit-parser", 1321 1326 "xxhash-rust", 1322 1327 ] 1323 1328 ··· 1825 1830 dependencies = [ 1826 1831 "equivalent", 1827 1832 "hashbrown 0.16.1", 1833 + "serde", 1834 + "serde_core", 1828 1835 ] 1829 1836 1830 1837 [[package]] ··· 1990 1997 checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" 1991 1998 1992 1999 [[package]] 2000 + name = "leb128fmt" 2001 + version = "0.1.0" 2002 + source = "registry+https://github.com/rust-lang/crates.io-index" 2003 + checksum = "09edd9e8b54e49e587e4f6295a7d29c3ea94d469cb40ab8ca70b288248a81db2" 2004 + 2005 + [[package]] 1993 2006 name = "libc" 1994 2007 version = "0.2.184" 1995 2008 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 2164 2177 source = "registry+https://github.com/rust-lang/crates.io-index" 2165 2178 checksum = "7957b9740744892f114936ab4a57b3f487491bbeafaf8083688b16841a4240e5" 2166 2179 dependencies = [ 2167 - "windows-sys 0.59.0", 2180 + "windows-sys 0.61.2", 2168 2181 ] 2169 2182 2170 2183 [[package]] ··· 3032 3045 "security-framework", 3033 3046 "security-framework-sys", 3034 3047 "webpki-root-certs", 3035 - "windows-sys 0.59.0", 3048 + "windows-sys 0.61.2", 3036 3049 ] 3037 3050 3038 3051 [[package]] ··· 4002 4015 checksum = "e51733f11c9c4f72aa0c160008246859e340b00807569a0da0e7a1079b27ba85" 4003 4016 4004 4017 [[package]] 4018 + name = "unicode-width" 4019 + version = "0.2.2" 4020 + source = "registry+https://github.com/rust-lang/crates.io-index" 4021 + checksum = "b4ac048d71ede7ee76d585517add45da530660ef4390e49b098733c6e897f254" 4022 + 4023 + [[package]] 4024 + name = "unicode-xid" 4025 + version = "0.2.6" 4026 + source = "registry+https://github.com/rust-lang/crates.io-index" 4027 + checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853" 4028 + 4029 + [[package]] 4005 4030 name = "universal-hash" 4006 4031 version = "0.5.1" 4007 4032 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 4222 4247 checksum = "684365b586a9a6256c1cc3544eee8680de48d6041142f581776ec7b139622ae9" 4223 4248 4224 4249 [[package]] 4250 + name = "wasm-encoder" 4251 + version = "0.239.0" 4252 + source = "registry+https://github.com/rust-lang/crates.io-index" 4253 + checksum = "5be00faa2b4950c76fe618c409d2c3ea5a3c9422013e079482d78544bb2d184c" 4254 + dependencies = [ 4255 + "leb128fmt", 4256 + "wasmparser 0.239.0", 4257 + ] 4258 + 4259 + [[package]] 4260 + name = "wasm-encoder" 4261 + version = "0.246.2" 4262 + source = "registry+https://github.com/rust-lang/crates.io-index" 4263 + checksum = "61fb705ce81adde29d2a8e99d87995e39a6e927358c91398f374474746070ef7" 4264 + dependencies = [ 4265 + "leb128fmt", 4266 + "wasmparser 0.246.2", 4267 + ] 4268 + 4269 + [[package]] 4270 + name = "wasm-metadata" 4271 + version = "0.239.0" 4272 + source = "registry+https://github.com/rust-lang/crates.io-index" 4273 + checksum = "20b3ec880a9ac69ccd92fbdbcf46ee833071cf09f82bb005b2327c7ae6025ae2" 4274 + dependencies = [ 4275 + "anyhow", 4276 + "indexmap", 4277 + "wasm-encoder 0.239.0", 4278 + "wasmparser 0.239.0", 4279 + ] 4280 + 4281 + [[package]] 4282 + name = "wasmparser" 4283 + version = "0.239.0" 4284 + source = "registry+https://github.com/rust-lang/crates.io-index" 4285 + checksum = "8c9d90bb93e764f6beabf1d02028c70a2156a6583e63ac4218dd07ef733368b0" 4286 + dependencies = [ 4287 + "bitflags", 4288 + "hashbrown 0.15.3", 4289 + "indexmap", 4290 + "semver 1.0.23", 4291 + ] 4292 + 4293 + [[package]] 4294 + name = "wasmparser" 4295 + version = "0.246.2" 4296 + source = "registry+https://github.com/rust-lang/crates.io-index" 4297 + checksum = "71cde4757396defafd25417cfb36aa3161027d06d865b0c24baaae229aac005d" 4298 + dependencies = [ 4299 + "bitflags", 4300 + "indexmap", 4301 + "semver 1.0.23", 4302 + ] 4303 + 4304 + [[package]] 4305 + name = "wast" 4306 + version = "246.0.2" 4307 + source = "registry+https://github.com/rust-lang/crates.io-index" 4308 + checksum = "fe3fe8e3bf88ad96d031b4181ddbd64634b17cb0d06dfc3de589ef43591a9a62" 4309 + dependencies = [ 4310 + "bumpalo", 4311 + "leb128fmt", 4312 + "memchr", 4313 + "unicode-width 0.2.2", 4314 + "wasm-encoder 0.246.2", 4315 + ] 4316 + 4317 + [[package]] 4318 + name = "wat" 4319 + version = "1.246.2" 4320 + source = "registry+https://github.com/rust-lang/crates.io-index" 4321 + checksum = "4bd7fda1199b94fff395c2d19a153f05dbe7807630316fa9673367666fd2ad8c" 4322 + dependencies = [ 4323 + "wast", 4324 + ] 4325 + 4326 + [[package]] 4225 4327 name = "web-sys" 4226 4328 version = "0.3.99" 4227 4329 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 4469 4571 checksum = "6f42320e61fe2cfd34354ecb597f86f413484a798ba44a8ca1165c58d42da6c1" 4470 4572 dependencies = [ 4471 4573 "bitflags", 4574 + ] 4575 + 4576 + [[package]] 4577 + name = "wit-component" 4578 + version = "0.239.0" 4579 + source = "registry+https://github.com/rust-lang/crates.io-index" 4580 + checksum = "88a866b19dba2c94d706ec58c92a4c62ab63e482b4c935d2a085ac94caecb136" 4581 + dependencies = [ 4582 + "anyhow", 4583 + "bitflags", 4584 + "indexmap", 4585 + "log", 4586 + "serde", 4587 + "serde_derive", 4588 + "serde_json", 4589 + "wasm-encoder 0.239.0", 4590 + "wasm-metadata", 4591 + "wasmparser 0.239.0", 4592 + "wat", 4593 + "wit-parser", 4594 + ] 4595 + 4596 + [[package]] 4597 + name = "wit-parser" 4598 + version = "0.239.0" 4599 + source = "registry+https://github.com/rust-lang/crates.io-index" 4600 + checksum = "55c92c939d667b7bf0c6bf2d1f67196529758f99a2a45a3355cc56964fd5315d" 4601 + dependencies = [ 4602 + "anyhow", 4603 + "id-arena", 4604 + "indexmap", 4605 + "log", 4606 + "semver 1.0.23", 4607 + "serde", 4608 + "serde_derive", 4609 + "serde_json", 4610 + "unicode-xid", 4611 + "wasmparser 0.239.0", 4472 4612 ] 4473 4613 4474 4614 [[package]]
+1
compiler-cli/src/build_lock.rs
··· 24 24 let target = match target { 25 25 Target::Erlang => "erlang", 26 26 Target::JavaScript => "javascript", 27 + Target::Wasm => "wasm", 27 28 }; 28 29 Ok(Self { 29 30 directory,
+1 -1
compiler-cli/src/export.rs
··· 314 314 )?; 315 315 316 316 let out = paths.root().join("extension.wasm"); 317 - crate::fs::write_bytes(&out, &component)?; 317 + fs::write_bytes(&out, &component)?; 318 318 crate::cli::print_exported(&package_name); 319 319 println!( 320 320 "
+1
compiler-core/src/build.rs
··· 83 83 match self { 84 84 Target::Erlang => "Erlang", 85 85 Target::JavaScript => "JavaScript", 86 + Target::Wasm => "Wasm", 86 87 } 87 88 } 88 89
+2 -2
compiler-core/src/build/package_compiler.rs
··· 7 7 use crate::analyse::{ModuleAnalyzerConstructor, TargetSupport}; 8 8 use crate::build::package_loader::CacheFiles; 9 9 10 - use crate::error::{DefinedModuleOrigin, FailedModule, SkipReason, SkippedModule}; 11 10 use crate::codegen::Wasm; 11 + use crate::error::{DefinedModuleOrigin, FailedModule, SkipReason, SkippedModule}; 12 12 use crate::inline; 13 13 use crate::io::files_with_extension; 14 14 use crate::line_numbers::{self, LineNumbers}; ··· 27 27 config::PackageConfig, 28 28 dep_tree, error, 29 29 io::{BeamCompilerIO, CommandExecutor, FileSystemReader, FileSystemWriter, Stdio}, 30 + metadata, 30 31 parse::extra::ModuleExtra, 31 32 paths, type_, 32 33 uid::UniqueIdGenerator, 33 34 warning::{TypeWarningEmitter, WarningEmitter}, 34 35 }; 35 - use crate::{inline, metadata}; 36 36 use askama::Template; 37 37 use ecow::EcoString; 38 38 use itertools::Itertools;
+2
compiler-core/src/error.rs
··· 4472 4472 match current_target { 4473 4473 Target::Erlang => "Erlang", 4474 4474 Target::JavaScript => "JavaScript", 4475 + Target::Wasm => "Wasm", 4475 4476 } 4476 4477 ); 4477 4478 let hint = wrap("Did you mean to build for a different target?"); ··· 4500 4501 let target = match target { 4501 4502 Target::Erlang => "Erlang", 4502 4503 Target::JavaScript => "JavaScript", 4504 + Target::Wasm => "Wasm", 4503 4505 }; 4504 4506 let text = wrap_format!( 4505 4507 "The `{name}` function is public but doesn't have an \
+2
compiler-core/src/metadata.rs
··· 251 251 implementations, 252 252 external_erlang, 253 253 external_javascript, 254 + external_wasm, 254 255 purity, 255 256 } => ValueConstructorVariant::ModuleFn { 256 257 name, ··· 262 263 implementations, 263 264 external_erlang, 264 265 external_javascript, 266 + external_wasm, 265 267 purity, 266 268 }, 267 269 ValueConstructorVariant::Record {
+1
compiler-core/src/paths.rs
··· 110 110 let target = match target { 111 111 Target::Erlang => "erlang", 112 112 Target::JavaScript => "javascript", 113 + Target::Wasm => "wasm", 113 114 }; 114 115 self.build_directory_for_mode(mode).join(target) 115 116 }
+106 -207
compiler-core/src/wasm/mir.rs
··· 5 5 6 6 use crate::{ 7 7 wasm::mir::{ast::*, visit::Visit}, 8 - exhaustiveness, parse, type_, 8 + exhaustiveness, type_, 9 9 }; 10 10 11 11 pub mod ast; ··· 127 127 functions: vec![], 128 128 }; 129 129 130 - for definition in &self.module.definitions { 131 - match definition { 132 - crate::ast::Definition::Function(function) => { 133 - module.functions.push(self.translate_function(function)); 134 - } 135 - // Type aliases and imports do not produce runtime code. 136 - crate::ast::Definition::TypeAlias(_type_alias) => {} 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) => {} 143 - // Module constants are not yet lowered to MIR. 144 - crate::ast::Definition::ModuleConstant(_module_constant) => {} 145 - } 130 + for function in &self.module.definitions.functions { 131 + module.functions.push(self.translate_function(function)); 146 132 } 133 + for custom_type in &self.module.definitions.custom_types { 134 + module 135 + .functions 136 + .append(&mut self.translate_custom_type(custom_type)); 137 + } 138 + // Type aliases, imports, and module constants are not lowered to MIR. 147 139 148 140 FlattenBlockPass.visit_module(&mut module); 149 141 RemoveUnusedPass.visit_module(&mut module); ··· 391 383 location: _, 392 384 type_: _, 393 385 value, 386 + float_value: _, 394 387 } => Expression::Float { 395 388 value: value.clone(), 396 389 }, ··· 455 448 literal: _, 456 449 implementations: _, 457 450 } => todo!(), 458 - type_::ValueConstructorVariant::LocalConstant { literal: _ } => todo!(), 459 451 type_::ValueConstructorVariant::ModuleFn { 460 452 name, 461 453 field_map: _, ··· 535 527 type_, 536 528 fun, 537 529 arguments, 530 + open_parenthesis: _, 538 531 } => { 539 532 let args: Vec<_> = arguments 540 533 .iter() ··· 573 566 crate::ast::TypedExpr::BinOp { 574 567 location: _, 575 568 type_: _, 576 - name, 577 - name_location: _, 569 + operator: name, 570 + operator_start: _, 578 571 left, 579 572 right, 580 573 } => { ··· 742 735 } => todo!(), 743 736 crate::ast::TypedExpr::RecordUpdate { 744 737 location: _, 738 + spread_start: _, 745 739 type_, 746 - record_assignment, 740 + updated_record, 741 + updated_record_assigned_name, 747 742 constructor, 748 743 arguments, 749 744 } => { 750 745 let mut block = vec![]; 751 746 752 - if let Some(assignment) = record_assignment { 753 - block.push(self.translate_assignment(assignment)); 747 + // If the record value itself needs to be bound before the 748 + // update (non-variable base), define it first. 749 + if let Some(name) = updated_record_assigned_name { 750 + let value = self.translate_expression(updated_record); 751 + let var = self.make_var(name.clone(), value.type_().clone()); 752 + block.push(Expression::Set { 753 + name: var, 754 + value: Box::new(value), 755 + }); 754 756 } 755 757 758 + // A record update is simply a constructor call with filled args. 756 759 let constructor = self.translate_expression(constructor); 757 760 let arguments = arguments 758 761 .iter() ··· 767 770 768 771 Expression::Block(block) 769 772 } 773 + crate::ast::TypedExpr::PositionalAccess { .. } => todo!(), 770 774 crate::ast::TypedExpr::NegateBool { location: _, value } => Expression::Equals { 771 775 lhs: Box::new(self.translate_expression(value)), 772 776 rhs: Box::new(Expression::Bool { value: false }), ··· 833 837 bit_array: _, 834 838 read_action: _, 835 839 } => todo!(), 840 + exhaustiveness::BoundValue::StringSlice { 841 + subject: _, 842 + prefix: _, 843 + } => IncompleteType::String, 836 844 } 837 845 } 838 846 ··· 985 993 ) -> Expression<IncompleteType> { 986 994 match guard { 987 995 crate::ast::ClauseGuard::Block { location: _, value } => self.translate_guard(value), 988 - crate::ast::ClauseGuard::Equals { 996 + crate::ast::ClauseGuard::BinaryOperator { 989 997 location: _, 998 + operator, 999 + operator_start: _, 990 1000 left, 991 1001 right, 992 1002 } => { 993 - let lhs = self.translate_guard(left); 994 - let rhs = self.translate_guard(right); 995 - 996 - Expression::Equals { 997 - lhs: Box::new(lhs), 998 - rhs: Box::new(rhs), 1003 + let lhs = Box::new(self.translate_guard(left)); 1004 + let rhs = Box::new(self.translate_guard(right)); 1005 + match operator { 1006 + crate::ast::BinOp::And => Expression::If { 1007 + cond: lhs, 1008 + then: rhs, 1009 + else_: Box::new(Expression::Bool { value: false }), 1010 + }, 1011 + crate::ast::BinOp::Or => Expression::If { 1012 + cond: lhs, 1013 + then: Box::new(Expression::Bool { value: true }), 1014 + else_: rhs, 1015 + }, 1016 + crate::ast::BinOp::Eq => Expression::Equals { lhs, rhs }, 1017 + crate::ast::BinOp::NotEq => Expression::NotEquals { lhs, rhs }, 1018 + crate::ast::BinOp::LtInt => Expression::IntLt { lhs, rhs }, 1019 + crate::ast::BinOp::LtEqInt => Expression::IntLtEq { lhs, rhs }, 1020 + crate::ast::BinOp::LtFloat => Expression::FloatLt { lhs, rhs }, 1021 + crate::ast::BinOp::LtEqFloat => Expression::FloatLtEq { lhs, rhs }, 1022 + crate::ast::BinOp::GtInt => Expression::IntGt { lhs, rhs }, 1023 + crate::ast::BinOp::GtEqInt => Expression::IntGtEq { lhs, rhs }, 1024 + crate::ast::BinOp::GtFloat => Expression::FloatGt { lhs, rhs }, 1025 + crate::ast::BinOp::GtEqFloat => Expression::FloatGtEq { lhs, rhs }, 1026 + crate::ast::BinOp::AddInt => Expression::IntAdd { lhs, rhs }, 1027 + crate::ast::BinOp::AddFloat => Expression::FloatAdd { lhs, rhs }, 1028 + crate::ast::BinOp::SubInt => Expression::IntSub { lhs, rhs }, 1029 + crate::ast::BinOp::SubFloat => Expression::FloatSub { lhs, rhs }, 1030 + crate::ast::BinOp::MultInt => Expression::IntMul { lhs, rhs }, 1031 + crate::ast::BinOp::MultFloat => Expression::FloatMul { lhs, rhs }, 1032 + crate::ast::BinOp::DivInt => Expression::IntDiv { lhs, rhs }, 1033 + crate::ast::BinOp::DivFloat => Expression::FloatDiv { lhs, rhs }, 1034 + crate::ast::BinOp::RemainderInt => Expression::IntRem { lhs, rhs }, 1035 + crate::ast::BinOp::Concatenate => Expression::StringConcat { lhs, rhs }, 999 1036 } 1000 1037 } 1001 - crate::ast::ClauseGuard::NotEquals { 1002 - location: _, 1003 - left, 1004 - right, 1005 - } => Expression::NotEquals { 1006 - lhs: Box::new(self.translate_guard(left)), 1007 - rhs: Box::new(self.translate_guard(right)), 1008 - }, 1009 - crate::ast::ClauseGuard::GtInt { 1010 - location: _, 1011 - left, 1012 - right, 1013 - } => Expression::IntGt { 1014 - lhs: Box::new(self.translate_guard(left)), 1015 - rhs: Box::new(self.translate_guard(right)), 1016 - }, 1017 - crate::ast::ClauseGuard::GtEqInt { 1018 - location: _, 1019 - left, 1020 - right, 1021 - } => Expression::IntGtEq { 1022 - lhs: Box::new(self.translate_guard(left)), 1023 - rhs: Box::new(self.translate_guard(right)), 1024 - }, 1025 - crate::ast::ClauseGuard::LtInt { 1026 - location: _, 1027 - left, 1028 - right, 1029 - } => Expression::IntLt { 1030 - lhs: Box::new(self.translate_guard(left)), 1031 - rhs: Box::new(self.translate_guard(right)), 1032 - }, 1033 - crate::ast::ClauseGuard::LtEqInt { 1034 - location: _, 1035 - left, 1036 - right, 1037 - } => Expression::IntLtEq { 1038 - lhs: Box::new(self.translate_guard(left)), 1039 - rhs: Box::new(self.translate_guard(right)), 1040 - }, 1041 - crate::ast::ClauseGuard::GtFloat { 1042 - location: _, 1043 - left, 1044 - right, 1045 - } => Expression::FloatGt { 1046 - lhs: Box::new(self.translate_guard(left)), 1047 - rhs: Box::new(self.translate_guard(right)), 1048 - }, 1049 - crate::ast::ClauseGuard::GtEqFloat { 1050 - location: _, 1051 - left, 1052 - right, 1053 - } => Expression::FloatGtEq { 1054 - lhs: Box::new(self.translate_guard(left)), 1055 - rhs: Box::new(self.translate_guard(right)), 1056 - }, 1057 - crate::ast::ClauseGuard::LtFloat { 1058 - location: _, 1059 - left, 1060 - right, 1061 - } => Expression::FloatLt { 1062 - lhs: Box::new(self.translate_guard(left)), 1063 - rhs: Box::new(self.translate_guard(right)), 1064 - }, 1065 - crate::ast::ClauseGuard::LtEqFloat { 1066 - location: _, 1067 - left, 1068 - right, 1069 - } => Expression::FloatLtEq { 1070 - lhs: Box::new(self.translate_guard(left)), 1071 - rhs: Box::new(self.translate_guard(right)), 1072 - }, 1073 - crate::ast::ClauseGuard::AddInt { 1074 - location: _, 1075 - left, 1076 - right, 1077 - } => Expression::IntAdd { 1078 - lhs: Box::new(self.translate_guard(left)), 1079 - rhs: Box::new(self.translate_guard(right)), 1080 - }, 1081 - crate::ast::ClauseGuard::AddFloat { 1082 - location: _, 1083 - left, 1084 - right, 1085 - } => Expression::FloatAdd { 1086 - lhs: Box::new(self.translate_guard(left)), 1087 - rhs: Box::new(self.translate_guard(right)), 1088 - }, 1089 - crate::ast::ClauseGuard::SubInt { 1090 - location: _, 1091 - left, 1092 - right, 1093 - } => Expression::IntSub { 1094 - lhs: Box::new(self.translate_guard(left)), 1095 - rhs: Box::new(self.translate_guard(right)), 1096 - }, 1097 - crate::ast::ClauseGuard::SubFloat { 1098 - location: _, 1099 - left, 1100 - right, 1101 - } => Expression::FloatSub { 1102 - lhs: Box::new(self.translate_guard(left)), 1103 - rhs: Box::new(self.translate_guard(right)), 1104 - }, 1105 - crate::ast::ClauseGuard::MultInt { 1106 - location: _, 1107 - left, 1108 - right, 1109 - } => Expression::IntMul { 1110 - lhs: Box::new(self.translate_guard(left)), 1111 - rhs: Box::new(self.translate_guard(right)), 1112 - }, 1113 - crate::ast::ClauseGuard::MultFloat { 1114 - location: _, 1115 - left, 1116 - right, 1117 - } => Expression::FloatMul { 1118 - lhs: Box::new(self.translate_guard(left)), 1119 - rhs: Box::new(self.translate_guard(right)), 1120 - }, 1121 - crate::ast::ClauseGuard::DivInt { 1122 - location: _, 1123 - left, 1124 - right, 1125 - } => Expression::IntDiv { 1126 - lhs: Box::new(self.translate_guard(left)), 1127 - rhs: Box::new(self.translate_guard(right)), 1128 - }, 1129 - crate::ast::ClauseGuard::DivFloat { 1130 - location: _, 1131 - left, 1132 - right, 1133 - } => Expression::FloatDiv { 1134 - lhs: Box::new(self.translate_guard(left)), 1135 - rhs: Box::new(self.translate_guard(right)), 1136 - }, 1137 - crate::ast::ClauseGuard::RemainderInt { 1138 - location: _, 1139 - left, 1140 - right, 1141 - } => Expression::IntRem { 1142 - lhs: Box::new(self.translate_guard(left)), 1143 - rhs: Box::new(self.translate_guard(right)), 1144 - }, 1145 - crate::ast::ClauseGuard::Or { 1146 - location: _, 1147 - left, 1148 - right, 1149 - } => Expression::If { 1150 - cond: Box::new(self.translate_guard(left)), 1151 - then: Box::new(Expression::Bool { value: true }), 1152 - else_: Box::new(self.translate_guard(right)), 1153 - }, 1154 - crate::ast::ClauseGuard::And { 1155 - location: _, 1156 - left, 1157 - right, 1158 - } => Expression::If { 1159 - cond: Box::new(self.translate_guard(left)), 1160 - then: Box::new(self.translate_guard(right)), 1161 - else_: Box::new(Expression::Bool { value: false }), 1162 - }, 1163 1038 crate::ast::ClauseGuard::Not { 1164 1039 location: _, 1165 1040 expression, ··· 1172 1047 type_: _, 1173 1048 name, 1174 1049 definition_location: _, 1050 + origin: _, 1175 1051 } => Expression::Var(self.get_var(name)), 1176 1052 crate::ast::ClauseGuard::TupleIndex { 1177 1053 location: _, ··· 1196 1072 }, 1197 1073 crate::ast::ClauseGuard::ModuleSelect { 1198 1074 location: _, 1075 + field_start: _, 1076 + definition_location: _, 1199 1077 type_: _, 1200 1078 label: _, 1201 1079 module_name: _, ··· 1203 1081 literal: _, 1204 1082 } => todo!(), 1205 1083 crate::ast::ClauseGuard::Constant(constant) => self.translate_constant(constant), 1084 + crate::ast::ClauseGuard::Invalid { .. } => { 1085 + unreachable!("invalid guard made it to code generation") 1086 + } 1206 1087 } 1207 1088 } 1208 1089 ··· 1218 1099 } => Expression::Int { 1219 1100 value: int_value.clone(), 1220 1101 }, 1221 - crate::ast::Constant::Float { location: _, value } => Expression::Float { 1102 + crate::ast::Constant::Float { 1103 + location: _, 1104 + value, 1105 + float_value: _, 1106 + } => Expression::Float { 1222 1107 value: value.clone(), 1223 1108 }, 1224 1109 crate::ast::Constant::String { location: _, value } => Expression::String { ··· 1227 1112 crate::ast::Constant::Tuple { 1228 1113 location: _, 1229 1114 elements, 1115 + type_, 1230 1116 } => Expression::Tuple { 1231 1117 items: elements 1232 1118 .iter() 1233 1119 .map(|element| self.translate_constant(element)) 1234 1120 .collect(), 1235 - type_: Translator::translate_type(&constant.type_()), 1121 + type_: Translator::translate_type(type_), 1236 1122 }, 1237 1123 crate::ast::Constant::List { 1238 1124 location: _, 1239 1125 elements, 1240 1126 type_, 1127 + tail: _, 1241 1128 } => Expression::List { 1242 1129 items: elements 1243 1130 .iter() ··· 1251 1138 module: _, 1252 1139 name: _, 1253 1140 arguments: _, 1254 - tag: _, 1255 1141 type_: _, 1256 1142 field_map: _, 1257 1143 record_constructor: _, 1258 1144 } => todo!(), 1145 + crate::ast::Constant::RecordUpdate { .. } => todo!(), 1259 1146 crate::ast::Constant::BitArray { 1260 1147 location: _, 1261 1148 segments: _, ··· 1278 1165 crate::ast::Constant::Invalid { 1279 1166 location: _, 1280 1167 type_: _, 1168 + extra_information: _, 1169 + } => todo!(), 1170 + crate::ast::Constant::Todo { 1171 + location: _, 1172 + type_: _, 1173 + message: _, 1281 1174 } => todo!(), 1282 1175 } 1283 1176 } ··· 1294 1187 .cloned() 1295 1188 .expect("expected variable to be defined"), 1296 1189 ), 1297 - exhaustiveness::BoundValue::LiteralString(_eco_string) => todo!(), 1190 + exhaustiveness::BoundValue::LiteralString(eco_string) => Expression::String { 1191 + value: eco_string.clone(), 1192 + }, 1298 1193 exhaustiveness::BoundValue::LiteralInt(big_int) => Expression::Int { 1299 1194 value: big_int.clone(), 1300 1195 }, 1301 - exhaustiveness::BoundValue::LiteralFloat(_eco_string) => todo!(), 1196 + exhaustiveness::BoundValue::LiteralFloat(eco_string) => Expression::Float { 1197 + value: eco_string.clone(), 1198 + }, 1302 1199 exhaustiveness::BoundValue::BitArraySlice { 1303 1200 bit_array: _, 1304 1201 read_action: _, 1305 1202 } => todo!(), 1203 + exhaustiveness::BoundValue::StringSlice { 1204 + subject: _, 1205 + prefix: _, 1206 + } => todo!(), 1306 1207 }; 1307 1208 1308 1209 Expression::Set { ··· 1317 1218 against: Expression<IncompleteType>, 1318 1219 ) -> Expression<IncompleteType> { 1319 1220 match check { 1320 - exhaustiveness::RuntimeCheck::Int { value } => { 1321 - let value = parse::parse_int_value(&value).expect("unable to parse integer value"); 1322 - 1323 - Expression::Equals { 1324 - lhs: Box::new(against), 1325 - rhs: Box::new(Expression::Int { value }), 1326 - } 1327 - } 1328 - exhaustiveness::RuntimeCheck::Float { value } => Expression::Equals { 1221 + exhaustiveness::RuntimeCheck::Int { int_value } => Expression::Equals { 1222 + lhs: Box::new(against), 1223 + rhs: Box::new(Expression::Int { 1224 + value: int_value.clone(), 1225 + }), 1226 + }, 1227 + exhaustiveness::RuntimeCheck::Float { float_value } => Expression::Equals { 1329 1228 lhs: Box::new(against), 1330 1229 rhs: Box::new(Expression::Float { 1331 - value: value.clone(), 1230 + value: eco_format!("{}", float_value.value()), 1332 1231 }), 1333 1232 }, 1334 1233 exhaustiveness::RuntimeCheck::String { value } => Expression::Equals {
+209 -10
compiler-core/src/wasm/zed.rs
··· 251 251 linked, 252 252 realloc_idx, 253 253 )?, 254 + // Match zed_extension_api defaults: Ok(None), not Err. 255 + "language-server-initialization-options" 256 + | "language-server-workspace-configuration" 257 + | "language-server-additional-initialization-options" 258 + | "language-server-additional-workspace-configuration" 259 + | "context-server-configuration" => { 260 + compile_ok_none(resolve, func, &import_index, &sig) 261 + } 262 + // Match defaults: Ok([]) 263 + "labels-for-completions" 264 + | "labels-for-symbols" 265 + | "complete-slash-command-argument" 266 + | "suggest-docs-packages" => { 267 + compile_ok_empty_list(resolve, func, &import_index, &sig) 268 + } 254 269 other if other.starts_with("language-server-") 255 - || other == "labels-for-completions" 256 - || other == "labels-for-symbols" 257 - || other == "complete-slash-command-argument" 258 270 || other == "run-slash-command" 259 271 || other == "context-server-command" 260 - || other == "context-server-configuration" 261 - || other == "suggest-docs-packages" 262 272 || other == "index-docs" 263 273 || other.starts_with("dap-") 264 274 || other == "get-dap-binary" 265 275 || other == "run-dap-locator" => 266 276 { 267 - compile_default_result_export(&sig, other) 277 + compile_default_result_export(resolve, func, &import_index, &sig, other) 268 278 } 269 - _ => compile_default_result_export(&sig, &func.name), 279 + _ => compile_default_result_export(resolve, func, &import_index, &sig, &func.name), 270 280 }; 271 281 272 282 let _ = codes.function(&body); ··· 417 427 let ty_idx = types.len(); 418 428 let _ = types.ty().function(vec![ValType::I32], vec![]); 419 429 let _ = imports.import(&module, &name, EntityType::Function(ty_idx)); 420 - _ = import_index.insert(name, *import_count); 430 + _ = import_index.insert(name.clone(), *import_count); 431 + // Alias by resource name for easy lookup (e.g. "worktree" -> drop). 432 + if let Some(res_name) = ty.name.as_deref() { 433 + _ = import_index.insert(format!("[resource-drop]{res_name}"), *import_count); 434 + _ = import_index.insert(format!("{res_name}_drop"), *import_count); 435 + } 421 436 *import_count += 1; 422 437 Ok(()) 423 438 } ··· 631 646 let _ = f.instructions().i32_const(0); // env len 632 647 let _ = f.instructions().i32_store(mem(24)); 633 648 649 + // Release borrow<worktree> (param index 2). Leaving it triggers: 650 + // "borrow handles still remain at the end of the call". 651 + if let Some(drop_wt) = import_index 652 + .get("[resource-drop]worktree") 653 + .or_else(|| import_index.get("worktree_drop")) 654 + .copied() 655 + { 656 + let _ = f.instructions().local_get(2); 657 + let _ = f.instructions().call(drop_wt); 658 + } 659 + 634 660 let _ = f.instructions().i32_const(RET_AREA); 635 661 let _ = f.instructions().end(); 636 662 Ok(f) 637 663 } 638 664 665 + /// Emit resource-handle drops for an export body (no-op when params are indirect). 666 + fn emit_param_drops( 667 + f: &mut Function, 668 + resolve: &Resolve, 669 + func: &WitFunction, 670 + import_index: &HashMap<String, u32>, 671 + sig: &wit_parser::abi::WasmSignature, 672 + ) { 673 + if sig.indirect_params { 674 + return; 675 + } 676 + let drops = resource_handle_drops(resolve, func, import_index, sig.params.len() as u32); 677 + for (local, drop_fn) in &drops { 678 + let _ = f.instructions().local_get(*local); 679 + let _ = f.instructions().call(*drop_fn); 680 + } 681 + } 682 + 683 + /// `Ok(None)` for `result<option<_>, string>` (LSP init/workspace config defaults). 684 + fn compile_ok_none( 685 + resolve: &Resolve, 686 + func: &WitFunction, 687 + import_index: &HashMap<String, u32>, 688 + sig: &wit_parser::abi::WasmSignature, 689 + ) -> Function { 690 + let mut f = Function::new_with_locals_types([]); 691 + emit_param_drops(&mut f, resolve, func, import_index, sig); 692 + // result disc = 0 (ok) 693 + let _ = f.instructions().i32_const(RET_AREA); 694 + let _ = f.instructions().i32_const(0); 695 + let _ = f.instructions().i32_store(mem(0)); 696 + // option disc = 0 (none) 697 + let _ = f.instructions().i32_const(RET_AREA); 698 + let _ = f.instructions().i32_const(0); 699 + let _ = f.instructions().i32_store(mem(4)); 700 + let _ = f.instructions().i32_const(RET_AREA); 701 + let _ = f.instructions().end(); 702 + f 703 + } 704 + 705 + /// `Ok([])` for `result<list<_>, string>`. 706 + fn compile_ok_empty_list( 707 + resolve: &Resolve, 708 + func: &WitFunction, 709 + import_index: &HashMap<String, u32>, 710 + sig: &wit_parser::abi::WasmSignature, 711 + ) -> Function { 712 + let mut f = Function::new_with_locals_types([]); 713 + emit_param_drops(&mut f, resolve, func, import_index, sig); 714 + // result disc = 0 (ok) 715 + let _ = f.instructions().i32_const(RET_AREA); 716 + let _ = f.instructions().i32_const(0); 717 + let _ = f.instructions().i32_store(mem(0)); 718 + // list ptr (unused) + len 0 719 + let _ = f.instructions().i32_const(RET_AREA); 720 + let _ = f.instructions().i32_const(0); 721 + let _ = f.instructions().i32_store(mem(4)); 722 + let _ = f.instructions().i32_const(RET_AREA); 723 + let _ = f.instructions().i32_const(0); 724 + let _ = f.instructions().i32_store(mem(8)); 725 + let _ = f.instructions().i32_const(RET_AREA); 726 + let _ = f.instructions().end(); 727 + f 728 + } 729 + 639 730 /// Default export: return `Err("not implemented")` in the CABI result layout 640 731 /// when the function returns a pointer; otherwise no-op / zero. 732 + /// 733 + /// Also drops any `borrow`/`own` resource handles received as parameters so 734 + /// the component model borrow table is clean on return. 641 735 fn compile_default_result_export( 736 + resolve: &Resolve, 737 + func: &WitFunction, 738 + import_index: &HashMap<String, u32>, 642 739 sig: &wit_parser::abi::WasmSignature, 643 740 _name: &str, 644 741 ) -> Function { 645 - let params: Vec<ValType> = sig.params.iter().map(wasm_type).collect(); 646 742 let results: Vec<ValType> = sig.results.iter().map(wasm_type).collect(); 647 743 let mut f = Function::new_with_locals_types([]); 744 + emit_param_drops(&mut f, resolve, func, import_index, sig); 648 745 649 746 if results.is_empty() { 650 747 let _ = f.instructions().end(); ··· 690 787 } 691 788 } 692 789 let _ = f.instructions().end(); 693 - let _ = params; 694 790 f 791 + } 792 + 793 + /// Map flat CABI param indices that are resource handles to their drop imports. 794 + /// Only handles plain `borrow`/`own` (not nested in option/list) for stubs. 795 + fn resource_handle_drops( 796 + resolve: &Resolve, 797 + func: &WitFunction, 798 + import_index: &HashMap<String, u32>, 799 + param_locals: u32, 800 + ) -> Vec<(u32, u32)> { 801 + use wit_parser::{Handle, Type, TypeDefKind}; 802 + let mut out = Vec::new(); 803 + let mut idx = 0u32; 804 + for (_name, ty) in &func.params { 805 + if let Some(res) = as_plain_handle_resource(resolve, ty) { 806 + if idx < param_locals { 807 + if let Some(drop_fn) = drop_for_resource(resolve, res, import_index) { 808 + out.push((idx, drop_fn)); 809 + } 810 + } 811 + idx += 1; 812 + continue; 813 + } 814 + // Advance by this parameter's flat width (GuestImport flattening is 815 + // enough for counting slots; we only emit drops for plain handles). 816 + idx = idx.saturating_add(flat_width(resolve, ty)); 817 + if idx > param_locals { 818 + break; 819 + } 820 + } 821 + out 822 + } 823 + 824 + fn as_plain_handle_resource( 825 + resolve: &Resolve, 826 + ty: &wit_parser::Type, 827 + ) -> Option<wit_parser::TypeId> { 828 + use wit_parser::{Handle, Type, TypeDefKind}; 829 + match ty { 830 + Type::Id(id) => match &resolve.types[*id].kind { 831 + TypeDefKind::Handle(Handle::Borrow(res) | Handle::Own(res)) => Some(*res), 832 + TypeDefKind::Type(inner) => as_plain_handle_resource(resolve, inner), 833 + _ => None, 834 + }, 835 + _ => None, 836 + } 837 + } 838 + 839 + fn drop_for_resource( 840 + resolve: &Resolve, 841 + res: wit_parser::TypeId, 842 + import_index: &HashMap<String, u32>, 843 + ) -> Option<u32> { 844 + let name = resolve.types[res].name.as_deref()?; 845 + import_index 846 + .get(&format!("[resource-drop]{name}")) 847 + .or_else(|| import_index.get(&format!("{name}_drop"))) 848 + .copied() 849 + } 850 + 851 + /// Flat core-param width for a single interface type (approx., for index tracking). 852 + fn flat_width(resolve: &Resolve, ty: &wit_parser::Type) -> u32 { 853 + use wit_parser::{Handle, Type, TypeDefKind}; 854 + match ty { 855 + Type::Bool 856 + | Type::U8 857 + | Type::U16 858 + | Type::U32 859 + | Type::S8 860 + | Type::S16 861 + | Type::S32 862 + | Type::Char 863 + | Type::F32 864 + | Type::ErrorContext => 1, 865 + Type::U64 | Type::S64 | Type::F64 => 1, // may be i64 but still one local 866 + Type::String => 2, 867 + Type::Id(id) => match &resolve.types[*id].kind { 868 + TypeDefKind::Handle(_) => 1, 869 + TypeDefKind::Resource => 1, 870 + TypeDefKind::Enum(_) | TypeDefKind::Flags(_) => 1, 871 + TypeDefKind::Type(inner) => flat_width(resolve, inner), 872 + TypeDefKind::Option(inner) => 1 + flat_width(resolve, inner), 873 + TypeDefKind::Result(r) => { 874 + let ok = r.ok.as_ref().map(|t| flat_width(resolve, t)).unwrap_or(0); 875 + let err = r.err.as_ref().map(|t| flat_width(resolve, t)).unwrap_or(0); 876 + 1 + ok.max(err) 877 + } 878 + TypeDefKind::List(_) => 2, 879 + TypeDefKind::Record(r) => r.fields.iter().map(|f| flat_width(resolve, &f.ty)).sum(), 880 + TypeDefKind::Tuple(t) => t.types.iter().map(|t| flat_width(resolve, t)).sum(), 881 + TypeDefKind::Variant(v) => { 882 + 1 + v 883 + .cases 884 + .iter() 885 + .map(|c| c.ty.as_ref().map(|t| flat_width(resolve, t)).unwrap_or(0)) 886 + .max() 887 + .unwrap_or(0) 888 + } 889 + TypeDefKind::FixedSizeList(inner, n) => flat_width(resolve, inner) * (*n as u32), 890 + TypeDefKind::Future(_) | TypeDefKind::Stream(_) => 1, 891 + TypeDefKind::Unknown => 1, 892 + }, 893 + } 695 894 } 696 895 697 896 /// Public entry used by the CLI / package compiler for zed-extension builds.
+26
format/src/lib.rs
··· 222 222 Some(Target::JavaScript) => { 223 223 docvec![arena, "@target(javascript)", LINE_DOCUMENT, document] 224 224 } 225 + Some(Target::Wasm) => { 226 + docvec![arena, "@target(wasm)", LINE_DOCUMENT, document] 227 + } 225 228 }; 226 229 let document = document.group(arena); 227 230 match comments { ··· 1036 1039 documentation: _, 1037 1040 external_erlang, 1038 1041 external_javascript, 1042 + external_wasm, 1039 1043 implementations: _, 1040 1044 purity: _, 1041 1045 } = function; ··· 1045 1049 .set_internal(*publicity) 1046 1050 .set_external_erlang(external_erlang) 1047 1051 .set_external_javascript(external_javascript) 1052 + .set_external_wasm(external_wasm) 1048 1053 .to_doc(arena); 1049 1054 1050 1055 // Fn name and args ··· 4128 4133 struct AttributesPrinter<'a> { 4129 4134 external_erlang: &'a Option<(EcoString, EcoString, SrcSpan)>, 4130 4135 external_javascript: &'a Option<(EcoString, EcoString, SrcSpan)>, 4136 + external_wasm: &'a Option<(EcoString, EcoString, SrcSpan)>, 4131 4137 deprecation: &'a Deprecation, 4132 4138 internal: bool, 4133 4139 } ··· 4137 4143 Self { 4138 4144 external_erlang: &None, 4139 4145 external_javascript: &None, 4146 + external_wasm: &None, 4140 4147 deprecation: &Deprecation::NotDeprecated, 4141 4148 internal: false, 4142 4149 } ··· 4155 4162 external: &'a Option<(EcoString, EcoString, SrcSpan)>, 4156 4163 ) -> Self { 4157 4164 self.external_javascript = external; 4165 + self 4166 + } 4167 + 4168 + pub fn set_external_wasm( 4169 + mut self, 4170 + external: &'a Option<(EcoString, EcoString, SrcSpan)>, 4171 + ) -> Self { 4172 + self.external_wasm = external; 4158 4173 self 4159 4174 } 4160 4175 ··· 4199 4214 attributes.push(docvec![ 4200 4215 arena, 4201 4216 EXTERNAL_JAVASCRIPT_QUOTE_DOCUMENT, 4217 + module, 4218 + QUOTE_COMMA_SPACE_QUOTE_DOCUMENT, 4219 + function, 4220 + QUOTE_CLOSE_PAREN_DOCUMENT 4221 + ]) 4222 + }; 4223 + 4224 + if let Some((module, function, _)) = self.external_wasm { 4225 + attributes.push(docvec![ 4226 + arena, 4227 + EXTERNAL_WASM_QUOTE_DOCUMENT, 4202 4228 module, 4203 4229 QUOTE_COMMA_SPACE_QUOTE_DOCUMENT, 4204 4230 function,
+5
pretty-arena/src/lib.rs
··· 712 712 "@external(javascript, \"", 713 713 23 714 714 ); 715 + const_str!( 716 + EXTERNAL_WASM_QUOTE_DOCUMENT, 717 + "@external(wasm, \"", 718 + 17 719 + ); 715 720 const_str!(QUOTE_COMMA_SPACE_QUOTE_DOCUMENT, "\", \"", 4); 716 721 const_str!(INTERNAL_ATTRIBUTE_DOCUMENT, "@internal", 9); 717 722 const_str!(INTERNAL_ATTRIBUTE_SPACE_DOCUMENT, "@internal ", 9);