From 67ae7ec1e036b93b6d53167dd250f6adae054748 Mon Sep 17 00:00:00 2001 From: Owais Jamil Date: Thu, 11 Jun 2026 16:40:28 -0500 Subject: [PATCH] feat: name collision detector * debug output * split up tests --- crates/cli/src/commands.rs | 2 +- crates/core/src/ir.rs | 231 ++++++++++++++++-- crates/core/src/ir/lowerer.rs | 1 + ..._lowerer__tests__core_control_flow_ir.snap | 3 +- crates/core/src/naming/identity.rs | 38 ++- crates/core/src/naming/render.rs | 45 +--- crates/core/src/wasm/tests.rs | 103 ++------ crates/core/src/wasm/validator.rs | 71 +++++- ...14_project_compilation_and_dependencies.md | 4 +- 9 files changed, 345 insertions(+), 153 deletions(-) diff --git a/crates/cli/src/commands.rs b/crates/cli/src/commands.rs index 05c3f96..40794c5 100644 --- a/crates/cli/src/commands.rs +++ b/crates/cli/src/commands.rs @@ -222,7 +222,7 @@ fn write_project_debug_dumps( dump_dir: &Path, ir: &compiler_core::ir::Module, wasm: &compiler_core::wasm::WasmModule, ) -> std::io::Result<()> { fs::create_dir_all(dump_dir)?; - fs::write(dump_dir.join("ir.txt"), format!("{ir:#?}\n"))?; + fs::write(dump_dir.join("ir.txt"), ir.linked_debug_dump())?; fs::write(dump_dir.join("wat.wat"), &wasm.wat)?; Ok(()) } diff --git a/crates/core/src/ir.rs b/crates/core/src/ir.rs index 629599b..f904e3f 100644 --- a/crates/core/src/ir.rs +++ b/crates/core/src/ir.rs @@ -2,11 +2,11 @@ pub mod bit_slices; mod closure; mod lowerer; -use std::collections::{HashMap, HashSet}; +use std::collections::{BTreeMap, HashMap}; use crate::{ ast::{self, Declaration as AstDeclaration, LiteralKind}, - diagnostic::{Diagnostic, DiagnosticCode, Diagnostics}, + diagnostic::{Diagnostic, DiagnosticCode, Diagnostics, Label}, naming::{ BackendItem, BackendItemKind, BackendName, CompilerGeneratedIndex, HelperKind, ModuleName, render_backend_name, }, @@ -83,6 +83,43 @@ pub struct Module { pub references: Vec, pub exports: Vec, pub functions: Vec, + /// Source-to-generated names assigned by the project linker. + /// + /// This is empty for single-file compilation. + pub linked_names: Vec, +} + +impl Module { + pub fn linked_debug_dump(&self) -> String { + use std::fmt::Write; + + let mut out = String::new(); + if !self.linked_names.is_empty() { + writeln!(&mut out, "linked names:").expect("write linked IR debug dump"); + let mut names = self.linked_names.iter().collect::>(); + names.sort_by_key(|name| { + ( + name.generated_name.as_str(), + name.source_name.as_str(), + &name.kind, + name.span.file_id.0, + name.span.start, + name.span.end, + ) + }); + for name in names { + writeln!( + &mut out, + " {:?} source={} generated={}", + name.kind, name.source_name, name.generated_name + ) + .expect("write linked IR debug dump"); + } + writeln!(&mut out).expect("write linked IR debug dump"); + } + writeln!(&mut out, "{self:#?}").expect("write linked IR debug dump"); + out + } } #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] @@ -94,6 +131,22 @@ pub struct ModuleIdentity { pub module: String, } +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct LinkedName { + pub source_name: String, + pub generated_name: String, + pub kind: LinkedNameKind, + pub span: Span, +} + +#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)] +pub enum LinkedNameKind { + Function, + Constant, + Constructor, + Helper, +} + #[derive(Debug, Clone, PartialEq, Eq)] pub struct Import { pub module: String, @@ -878,11 +931,15 @@ fn link_modules(modules: Vec) -> Result { references: Vec::new(), exports: Vec::new(), functions: Vec::new(), + linked_names: Vec::new(), }; - let mut functions = HashSet::new(); - let mut diagnostics = Vec::new(); - - let global_renames = global_backend_renames(&modules); + let rename_plan = global_backend_renames(&modules); + let diagnostics = generated_name_collision_diagnostics(&rename_plan.linked_names); + if !diagnostics.is_empty() { + return Err(diagnostics); + } + linked.linked_names = rename_plan.linked_names; + let global_renames = rename_plan.renames; for module in modules { let mut renames = module_backend_renames(&module, &global_renames); @@ -894,14 +951,6 @@ fn link_modules(modules: Vec) -> Result { let mut module = module; rewrite_module_backend_names(&mut module, &renames); - for function in &module.functions { - if !functions.insert(function.name.clone()) { - diagnostics.push(Diagnostic::new( - DiagnosticCode::ProjectError, - format!("duplicate generated function `{}`", function.name), - )); - } - } linked.imports.extend(module.imports); linked.declarations.extend(module.declarations); linked.constants.extend(module.constants); @@ -911,11 +960,17 @@ fn link_modules(modules: Vec) -> Result { linked.functions.extend(module.functions); } - if diagnostics.is_empty() { Ok(linked) } else { Err(diagnostics) } + Ok(linked) +} + +struct BackendRenamePlan { + renames: HashMap, + linked_names: Vec, } -fn global_backend_renames(modules: &[Module]) -> HashMap { +fn global_backend_renames(modules: &[Module]) -> BackendRenamePlan { let mut renames = HashMap::new(); + let mut linked_names = Vec::new(); for module in modules { let Some(identity) = &module.identity else { continue }; let module_name = ModuleName::from_path(&identity.module); @@ -960,28 +1015,87 @@ fn global_backend_renames(modules: &[Module]) -> HashMap { } else { BackendName::function(identity.package.as_str(), module_name.clone(), function.name.as_str()) }; - renames.insert( - format!("{}.{}", identity.module, function.name), - render_backend_name(&backend), - ); + let generated_name = render_backend_name(&backend); + let source_name = format!("{}.{}", identity.module, function.name); + let kind = if function.name.starts_with("__") + || matches!( + function.abi.boundary, + CallBoundary::HostImport { .. } | CallBoundary::ModuleImport { .. } + ) { + LinkedNameKind::Helper + } else { + LinkedNameKind::Function + }; + renames.insert(source_name.clone(), generated_name.clone()); + linked_names.push(LinkedName { source_name, generated_name, kind, span: function.span }); } for constant in &module.constants { let backend = BackendName::constant(identity.package.as_str(), module_name.clone(), constant.name.as_str()); - renames.insert( - format!("{}.{}", identity.module, constant.name), - render_backend_name(&backend), - ); + let generated_name = render_backend_name(&backend); + let source_name = format!("{}.{}", identity.module, constant.name); + renames.insert(source_name.clone(), generated_name.clone()); + linked_names.push(LinkedName { + source_name, + generated_name, + kind: LinkedNameKind::Constant, + span: constant.span, + }); } for declaration in &module.declarations { if declaration.kind == DeclarationKind::TypeDefinition && let Some(name) = &declaration.name { let backend = BackendName::constructor(identity.package.as_str(), module_name.clone(), name.as_str()); - renames.insert(format!("{}.{}", identity.module, name), render_backend_name(&backend)); + let generated_name = render_backend_name(&backend); + let source_name = format!("{}.{}", identity.module, name); + renames.insert(source_name.clone(), generated_name.clone()); + linked_names.push(LinkedName { + source_name, + generated_name, + kind: LinkedNameKind::Constructor, + span: declaration.span, + }); } } } - renames + BackendRenamePlan { renames, linked_names } +} + +fn generated_name_collision_diagnostics(linked_names: &[LinkedName]) -> Diagnostics { + let mut by_generated: BTreeMap<&str, Vec<&LinkedName>> = BTreeMap::new(); + for name in linked_names { + by_generated.entry(name.generated_name.as_str()).or_default().push(name); + } + + let mut diagnostics = Vec::new(); + for (generated_name, mut origins) in by_generated { + if origins.len() < 2 { + continue; + } + origins.sort_by_key(|origin| { + ( + origin.source_name.as_str(), + &origin.kind, + origin.span.file_id.0, + origin.span.start, + origin.span.end, + ) + }); + + let mut diagnostic = Diagnostic::new( + DiagnosticCode::ProjectError, + format!("duplicate generated backend name `{generated_name}`"), + ) + .with_note("generated backend names must be unique after project linking"); + for origin in origins { + diagnostic = diagnostic.with_label(Label::primary( + origin.span, + format!("`{}` generated `{}`", origin.source_name, origin.generated_name), + )); + } + diagnostics.push(diagnostic); + } + diagnostics } fn module_backend_renames(module: &Module, global: &HashMap) -> HashMap { @@ -1414,3 +1528,68 @@ fn declaration_name(source: &str, keyword: &str) -> Option { fn visibility(public: bool) -> Visibility { if public { Visibility::Public } else { Visibility::Private } } + +#[cfg(test)] +mod tests { + use crate::source::{SourceFileId, Span}; + + use super::*; + + #[test] + fn reports_generated_name_collisions_with_source_declarations() { + let first_span = Span::new(SourceFileId(1), 10, 20); + let second_span = Span::new(SourceFileId(2), 30, 40); + let names = vec![ + LinkedName { + source_name: "app/main.run".into(), + generated_name: "generated/run".into(), + kind: LinkedNameKind::Function, + span: first_span, + }, + LinkedName { + source_name: "test/main.run".into(), + generated_name: "generated/run".into(), + kind: LinkedNameKind::Function, + span: second_span, + }, + ]; + + let diagnostics = generated_name_collision_diagnostics(&names); + + assert_eq!(diagnostics.len(), 1); + insta::assert_snapshot!(diagnostics[0].render_plain(), @r#" +ProjectError: duplicate generated backend name `generated/run` + --> file 1 bytes 10..20 + `app/main.run` generated `generated/run` + --> file 2 bytes 30..40 + `test/main.run` generated `generated/run` + note: generated backend names must be unique after project linking +"#); + } + + #[test] + fn linked_debug_dump_shows_source_and_generated_names() { + let span = Span::new(SourceFileId(1), 0, 3); + let module = Module { + span, + identity: None, + imports: Vec::new(), + declarations: Vec::new(), + constants: Vec::new(), + init: ModuleInit::default(), + references: Vec::new(), + exports: Vec::new(), + functions: Vec::new(), + linked_names: vec![LinkedName { + source_name: "app/main.run".into(), + generated_name: "generated/run".into(), + kind: LinkedNameKind::Function, + span, + }], + }; + + let dump = module.linked_debug_dump(); + + assert!(dump.starts_with("linked names:\n Function source=app/main.run generated=generated/run\n\nModule")); + } +} diff --git a/crates/core/src/ir/lowerer.rs b/crates/core/src/ir/lowerer.rs index 8178edb..a38a5f9 100644 --- a/crates/core/src/ir/lowerer.rs +++ b/crates/core/src/ir/lowerer.rs @@ -171,6 +171,7 @@ impl Lowerer { references, exports, functions, + linked_names: Vec::new(), }) } else { Err(self.diagnostics) diff --git a/crates/core/src/ir/snapshots/compiler_core__ir__lowerer__tests__core_control_flow_ir.snap b/crates/core/src/ir/snapshots/compiler_core__ir__lowerer__tests__core_control_flow_ir.snap index ba2c3fa..51da481 100644 --- a/crates/core/src/ir/snapshots/compiler_core__ir__lowerer__tests__core_control_flow_ir.snap +++ b/crates/core/src/ir/snapshots/compiler_core__ir__lowerer__tests__core_control_flow_ir.snap @@ -1,6 +1,6 @@ --- source: crates/core/src/ir/lowerer.rs -assertion_line: 1234 +assertion_line: 2663 expression: module --- Module { @@ -990,4 +990,5 @@ Module { }, }, ], + linked_names: [], } diff --git a/crates/core/src/naming/identity.rs b/crates/core/src/naming/identity.rs index d8c843e..419980b 100644 --- a/crates/core/src/naming/identity.rs +++ b/crates/core/src/naming/identity.rs @@ -1,4 +1,4 @@ -use std::fmt; +use std::fmt::{self, Display}; /// Package identity used for compiler-owned backend names. #[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] @@ -108,6 +108,12 @@ impl fmt::Display for MemberName { #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] pub struct CompilerGeneratedIndex(pub u32); +impl Display for CompilerGeneratedIndex { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "i{}", self.0) + } +} + /// Owner namespace for backend symbols controlled by the compiler. #[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] pub enum BackendOwner { @@ -149,6 +155,18 @@ pub enum BackendItemKind { Helper(HelperKind), } +impl Display for BackendItemKind { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.write_str(match self { + BackendItemKind::Function => "fn", + BackendItemKind::Constant => "const", + BackendItemKind::Constructor => "ctor", + BackendItemKind::TypeHelper => "type", + BackendItemKind::Helper(_) => "helper", + }) + } +} + #[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] pub enum HelperKind { Closure, @@ -161,6 +179,24 @@ pub enum HelperKind { Other(String), } +impl Display for HelperKind { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + HelperKind::Closure => f.write_str("closure"), + HelperKind::LiftedFunction => f.write_str("lifted"), + HelperKind::RecordUpdateConstructor => f.write_str("record_update"), + HelperKind::ImportWrapper => f.write_str("import_wrapper"), + HelperKind::Runtime => f.write_str("runtime"), + HelperKind::Stdlib => f.write_str("stdlib"), + HelperKind::Debug => f.write_str("debug"), + HelperKind::Other(name) => { + let d = super::escape_segment(name); + f.write_str(d.as_str()) + } + } + } +} + /// Complete compiler-owned backend name before rendering. #[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] pub struct BackendName { diff --git a/crates/core/src/naming/render.rs b/crates/core/src/naming/render.rs index 9332646..c88a1c1 100644 --- a/crates/core/src/naming/render.rs +++ b/crates/core/src/naming/render.rs @@ -1,6 +1,4 @@ -use super::{ - BackendItem, BackendItemKind, BackendName, BackendOwner, CompilerGeneratedIndex, HelperKind, escape_segment, -}; +use super::{BackendItemKind, BackendName, BackendOwner, escape_segment}; /// Render a structured backend name into a deterministic symbol string. pub fn render_backend_name(name: &BackendName) -> String { @@ -17,15 +15,12 @@ pub fn render_backend_name(name: &BackendName) -> String { BackendOwner::Compiler => parts.push("compiler".into()), } - push_item_parts(&mut parts, &name.item); - parts.join("$") -} + let item = &name.item; -fn push_item_parts(parts: &mut Vec, item: &BackendItem) { - parts.push(item_kind_tag(&item.kind).to_string()); + parts.push(item.kind.to_string()); if let BackendItemKind::Helper(helper) = &item.kind { - parts.push(helper_kind_tag(helper)); + parts.push(helper.to_string()); } if let Some(member) = &item.member { @@ -33,46 +28,20 @@ fn push_item_parts(parts: &mut Vec, item: &BackendItem) { } if let Some(index) = item.index { - parts.push(render_index(index)); - } -} - -fn item_kind_tag(kind: &BackendItemKind) -> &'static str { - match kind { - BackendItemKind::Function => "fn", - BackendItemKind::Constant => "const", - BackendItemKind::Constructor => "ctor", - BackendItemKind::TypeHelper => "type", - BackendItemKind::Helper(_) => "helper", + parts.push(index.to_string()); } -} -fn helper_kind_tag(kind: &HelperKind) -> String { - match kind { - HelperKind::Closure => "closure".into(), - HelperKind::LiftedFunction => "lifted".into(), - HelperKind::RecordUpdateConstructor => "record_update".into(), - HelperKind::ImportWrapper => "import_wrapper".into(), - HelperKind::Runtime => "runtime".into(), - HelperKind::Stdlib => "stdlib".into(), - HelperKind::Debug => "debug".into(), - HelperKind::Other(name) => escape_segment(name), - } -} - -fn render_index(index: CompilerGeneratedIndex) -> String { - format!("i{}", index.0) + parts.join("$") } #[cfg(test)] mod tests { use super::*; - use crate::naming::{BackendItem, BackendItemKind, HelperKind, ModuleName}; + use crate::naming::{BackendItem, BackendItemKind, CompilerGeneratedIndex, HelperKind, ModuleName}; #[test] fn renders_package_function_names() { let name = BackendName::function("app", ModuleName::from_path("app/main"), "run"); - assert_eq!( render_backend_name(&name), "r$pkg$x617070$mod$x617070$x6d61696e$fn$x72756e" diff --git a/crates/core/src/wasm/tests.rs b/crates/core/src/wasm/tests.rs index 0f6ecac..37479b9 100644 --- a/crates/core/src/wasm/tests.rs +++ b/crates/core/src/wasm/tests.rs @@ -51,6 +51,7 @@ fn ir_module(functions: Vec, span: Span) -> ir::Module { references: Vec::new(), exports: Vec::new(), functions, + linked_names: Vec::new(), } } @@ -125,11 +126,11 @@ fn emits_wat_for_public_scalar_function() { insta::assert_snapshot!(wasm.wat, @r#" (module -(type (func (param i64) (result i64))) -(func $id (type 0) (param i64) (result i64) - local.get 0 -) -(export "id" (func 0)) + (type (func (param i64) (result i64))) + (func $id (type 0) (param i64) (result i64) + local.get 0 + ) + (export "id" (func 0)) ) "#); assert!(!wasm.bytes.is_empty()); @@ -179,14 +180,14 @@ fn renders_deterministic_structured_wat_for_managed_values() { insta::assert_snapshot!(wasm.wat, @r#" (module -(type (func (result i32))) -(memory 1) -(func $pair (type 0) (result i32) - i32.const 1024 -) -(export "pair" (func 0)) -(export "memory" (memory 0)) -(data (memory 0) (offset i32.const 1024) "\03\00\00\00\02\00\00\00\01\00\00\00\00\00\00\00\02\00\00\00\00\00\00\00") + (type (func (result i32))) + (memory 1) + (func $pair (type 0) (result i32) + i32.const 1024 + ) + (export "pair" (func 0)) + (export "memory" (memory 0)) + (data (memory 0) (offset i32.const 1024) "\03\00\00\00\02\00\00\00\01\00\00\00\00\00\00\00\02\00\00\00\00\00\00\00") ) "#); } @@ -455,32 +456,6 @@ case Some(Some(2)) { } } -fn invalid_structured_module(span: Span, body: Vec) -> builder::Module { - let mut module = builder::Module::new(); - module.source_span = Some(span); - let type_id = module.push_type(builder::FunctionType::new([], [builder::ValueType::I64])); - let mut function = builder::Function::new(type_id); - function.body = body; - module.push_function(function); - module -} - -fn assert_source_spanned_wasm_error(module: &builder::Module, expected: &str, span: Span) { - let errors = module - .structured_wat() - .expect_err("invalid module should fail before byte emission"); - assert!( - errors.iter().any(|diagnostic| diagnostic.message.contains(expected)), - "{errors:?}" - ); - assert!( - errors - .iter() - .any(|diagnostic| diagnostic.labels.iter().any(|label| label.span == span)), - "{errors:?}" - ); -} - fn exported_function_with_body(name: &str, return_type: &Type, result: ir::Expression, span: Span) -> ir::Function { ir::Function { closure_captures: Vec::new(), @@ -673,40 +648,6 @@ fn structured_codegen_ports_failure_ir() { assert!(fails.call(&mut store, ()).is_err()); } -#[test] -fn backend_validation_reports_source_spanned_stack_diagnostics() { - let span = Span::new(SourceFileId(0), 5, 9); - let module = invalid_structured_module(span, vec![builder::Instruction::I32Const(1)]); - - assert_source_spanned_wasm_error(&module, "leaves stack", span); -} - -#[test] -fn backend_validation_reports_source_spanned_signature_diagnostics() { - let span = Span::new(SourceFileId(0), 10, 14); - let mut module = invalid_structured_module( - span, - vec![builder::Instruction::Call { - function: builder::FunctionId(0), - type_: builder::FunctionType::new([], [builder::ValueType::I32]), - }], - ); - module.functions[0].body.push(builder::Instruction::I64Const(0)); - - assert_source_spanned_wasm_error(&module, "call to function 0 has signature", span); -} - -#[test] -fn backend_validation_reports_source_spanned_local_diagnostics() { - let span = Span::new(SourceFileId(0), 15, 20); - let module = invalid_structured_module( - span, - vec![builder::Instruction::LocalGet { local: builder::LocalId(9), type_: builder::ValueType::I64 }], - ); - - assert_source_spanned_wasm_error(&module, "unknown local index", span); -} - #[test] fn backend_validation_reports_source_spanned_target_adapter_diagnostics() { let module = host_import_module(Span::new(SourceFileId(0), 21, 30)); @@ -733,14 +674,14 @@ fn emits_host_import_before_exported_function() { insta::assert_snapshot!(module.emit_wat().expect("emit wat"), @r#" (module -(type (func (param i64) (result i64))) -(type (func (result i64))) -(import "env" "inc" (func (type 0) (param i64) (result i64))) -(func $main (type 1) (result i64) - i64.const 41 - call 0 -) -(export "main" (func 1)) + (type (func (param i64) (result i64))) + (type (func (result i64))) + (import "env" "inc" (func (type 0) (param i64) (result i64))) + (func $main (type 1) (result i64) + i64.const 41 + call 0 + ) + (export "main" (func 1)) ) "#); } diff --git a/crates/core/src/wasm/validator.rs b/crates/core/src/wasm/validator.rs index 0ee0870..cdaf977 100644 --- a/crates/core/src/wasm/validator.rs +++ b/crates/core/src/wasm/validator.rs @@ -6,6 +6,11 @@ use super::builder::*; +#[derive(Debug, Clone)] +struct LabelType { + branch_results: Vec, +} + #[derive(Debug, Clone, PartialEq, Eq)] pub struct ValidationError { pub message: String, @@ -532,7 +537,67 @@ impl FunctionContext { } } -#[derive(Debug, Clone)] -struct LabelType { - branch_results: Vec, +#[cfg(test)] +mod tests { + use crate::source::{SourceFileId, Span}; + use crate::wasm::builder; + + fn invalid_structured_module(span: Span, body: Vec) -> builder::Module { + let mut module = builder::Module::new(); + module.source_span = Some(span); + + let type_id = module.push_type(builder::FunctionType::new([], [builder::ValueType::I64])); + let mut function = builder::Function::new(type_id); + function.body = body; + module.push_function(function); + module + } + + fn assert_source_spanned_wasm_error(module: &builder::Module, expected: &str, span: Span) { + let errors = module + .structured_wat() + .expect_err("invalid module should fail before byte emission"); + assert!( + errors.iter().any(|diagnostic| diagnostic.message.contains(expected)), + "{errors:?}" + ); + assert!( + errors + .iter() + .any(|diagnostic| diagnostic.labels.iter().any(|label| label.span == span)), + "{errors:?}" + ); + } + + #[test] + fn backend_validation_reports_source_spanned_stack_diagnostics() { + let span = Span::new(SourceFileId(0), 5, 9); + let module = invalid_structured_module(span, vec![builder::Instruction::I32Const(1)]); + assert_source_spanned_wasm_error(&module, "leaves stack", span); + } + + #[test] + fn backend_validation_reports_source_spanned_signature_diagnostics() { + let span = Span::new(SourceFileId(0), 10, 14); + let mut module = invalid_structured_module( + span, + vec![builder::Instruction::Call { + function: builder::FunctionId(0), + type_: builder::FunctionType::new([], [builder::ValueType::I32]), + }], + ); + + module.functions[0].body.push(builder::Instruction::I64Const(0)); + assert_source_spanned_wasm_error(&module, "call to function 0 has signature", span); + } + + #[test] + fn backend_validation_reports_source_spanned_local_diagnostics() { + let span = Span::new(SourceFileId(0), 15, 20); + let module = invalid_structured_module( + span, + vec![builder::Instruction::LocalGet { local: builder::LocalId(9), type_: builder::ValueType::I64 }], + ); + assert_source_spanned_wasm_error(&module, "unknown local index", span); + } } diff --git a/docs/internal/tasks/14_project_compilation_and_dependencies.md b/docs/internal/tasks/14_project_compilation_and_dependencies.md index 5d34423..39b9e10 100644 --- a/docs/internal/tasks/14_project_compilation_and_dependencies.md +++ b/docs/internal/tasks/14_project_compilation_and_dependencies.md @@ -39,9 +39,9 @@ The goal here is to make linked project names deterministic and collision-free. record update constructors, and debug references to generated names. - [x] Keep host import and module import ABI names stable while namespacing compiler-owned wrapper functions. -- [ ] Detect generated-name collisions deterministically and report the source +- [x] Detect generated-name collisions deterministically and report the source declarations that caused them. -- [ ] Show source names and generated names in linked IR debug dumps. +- [x] Show source names and generated names in linked IR debug dumps. - [ ] Add fixtures for duplicate function names in different modules, duplicate module basenames, dependency module name overlap, and lifted closures. -- 2.51.2