//! Embedding-ABI M1 codegen (spec §4): `Target::StaticLib` lowering //! emits NO `@main` trampoline, NO `MissingEntryMain`, and one //! external `define i64 @backtest_step(...)` (the author-chosen C //! symbol from `(export "backtest_step")`) forwarding to the //! internal `@ail_embed_backtest_step_step` (module //! `embed_backtest_step` == file stem, per Design-decision 6). //! Executable-target lowering is byte-unchanged (the //! `missing_entry_main_is_error` in-source pin still holds). use ailang_core::Workspace; use std::path::PathBuf; fn load(file: &str) -> Workspace { let path = PathBuf::from(env!("CARGO_MANIFEST_DIR")) .parent().unwrap().parent().unwrap() .join("examples").join(file); ailang_surface::load_workspace(&path).unwrap() } #[test] fn staticlib_emits_forwarder_no_main() { let ws = load("embed_backtest_step.ail"); let ir = ailang_codegen::lower_workspace_staticlib_with_alloc( &ws, ailang_codegen::AllocStrategy::Rc).unwrap(); assert!(!ir.contains("define i32 @main()"), "staticlib IR must not emit the @main trampoline"); assert!(ir.contains("@backtest_step("), "staticlib IR must emit the external @backtest_step entrypoint \ (the author-chosen C symbol — invariant under module rename)"); assert!(ir.contains("@ail_embed_backtest_step_step("), "the forwarder must call the internal \ @ail_embed_backtest_step_step (module embed_backtest_step)"); } #[test] fn executable_target_still_emits_main() { let ws = load("embed_backtest_step.ail"); // Default executable lowering is unaffected by the new field; // backtest has no `main`, so exe-target lowering still rejects. let err = ailang_codegen::lower_workspace_with_alloc( &ws, ailang_codegen::AllocStrategy::Rc).unwrap_err(); assert!(format!("{err}").contains("has no `main` def"), "exe target must still surface MissingEntryMain; got {err}"); }