//! Bug regression: the workspace-monomorphisation pass must seed //! `env.imports` for the current module before re-running `synth` on //! a fn body, otherwise a qualified cross-module reference like //! `OtherMod.helper` is rejected with `unknown module prefix` whenever //! the workspace also contains a `Def::Class` (which makes the mono //! pass actually traverse bodies instead of identity-cloning the //! workspace). //! //! Sibling to commit 13b36cc (`env.globals` seeding for self-recursive //! top-level fn) and 5c5180f (`env.types` / `env.ctor_index` seeding for //! user ADTs). Same shape: `synth` walks every fn body during the mono //! pass and consults env tables that `check_in_workspace` populates //! per-module but `mono::build_workspace_env` / //! `collect_mono_targets` / `collect_residuals_ordered` do not. //! //! The qualified-ref synth path lives at `lib.rs:1697`: //! //! ```ignore //! let target_module = match env.imports.get(prefix) { //! Some(m) => m.clone(), //! None => return Err(CheckError::UnknownModule { module: prefix.to_string() }), //! }; //! ``` //! //! `env.imports` is the per-module alias→module-name map populated at //! `lib.rs:1147-1152` from `m.imports`. The mono entry points never //! seed it, so a body whose synth walk hits a qualified-name reference //! errors out — even though the import is declared in the source and //! visible to the typechecker. use std::path::Path; use std::process::Command; fn ail_bin() -> &'static str { env!("CARGO_BIN_EXE_ail") } /// Property: a workspace where module A declares a class+instance and /// also a regular top-level fn `helper`, and module B imports A and /// calls `A.helper` via a qualified `Term::Var { name: "A.helper" }`, /// must build and run end-to-end. The mono pass re-runs `synth` on /// every fn body to recover residual class constraints; that env must /// agree with the env the main typecheck path used, including /// `env.imports` for the current module so the qualified-name lookup /// resolves. /// /// Pre-fix: `ail check` passes (typecheck seeds `env.imports` per /// module via `check_in_workspace`); `ail build` fails at the mono /// pass with `monomorphise_workspace: unknown module prefix /// \`test_mono_imports_classmod\` in qualified reference` because /// `mono::build_workspace_env` and the per-fn entry points never seed /// `env.imports`. `synth`'s qualified-name path (`lib.rs:1697`) thus /// falls through to `UnknownModule`. #[test] fn mono_pass_handles_qualified_cross_module_reference() { let manifest_dir = env!("CARGO_MANIFEST_DIR"); let workspace = Path::new(manifest_dir).parent().unwrap().parent().unwrap(); let src = workspace .join("examples") .join("test_mono_imports_main.ail"); let tmp = std::env::temp_dir().join(format!( "ailang_mono_xmod_qualified_{}", std::process::id() )); std::fs::create_dir_all(&tmp).unwrap(); let out = tmp.join("bin"); let build = Command::new(ail_bin()) .args(["build", src.to_str().unwrap(), "-o"]) .arg(&out) .output() .expect("ail build failed to run"); assert!( build.status.success(), "ail build failed: stderr={}", String::from_utf8_lossy(&build.stderr) ); let run = Command::new(&out).output().expect("execute binary"); assert!( run.status.success(), "binary {} exited non-zero", out.display() ); // helper(42) = 42 + 100 = 142 assert_eq!( String::from_utf8(run.stdout).expect("stdout utf8").trim(), "142" ); }