iter 24.3: fn print + E2E + 3 compiler-path repairs; milestone 24 close
Ships fn print : forall a. Show a => (a borrow) -> () !IO in the
prelude with explicit-let body \\x -> let s = show x in do
io/print_str s. Three new E2E fixtures + tests verify the full
path:
- show_print_smoke: 4 primitives smoke (print 42 / true / 'hello' /
3.14) — compile, run, expected stdout
- show_user_adt: data IntBox + instance prelude.Show IntBox +
print (MkIntBox 7) — stdout '7'
- show_no_instance: let f : Int -> Int = \\x -> x in do print f —
fires Show-aware NoInstance with DESIGN.md §Prelude(built-in)
classes cross-reference
IR-shape pin in print_mono_body_shape.rs asserts post-mono
print__Int.body is structurally Term::Let → App(show__Int) →
Do(io/print_str). Protects the explicit let-binder for the heap-Str
RC discipline per eob.1 Str carve-out.
Three plan-defects-fixed-inline surfaced during user-ADT E2E,
necessary repairs to make the spec's stated user-ADT trajectory
work end-to-end:
(a) mono.rs (2 sites): MonoTarget::FreeFn::type_args were carrying
bare type-cons references; normalised to canonical
<owner>.<bare> form via workspace_registry.normalize_type_for_lookup
so synthesised cross-module bodies' post-mono walks reach the
registry-keyed instance entries. Symmetric to the existing
class-method-arm normalisation.
(b) codegen/lib.rs (3 sites: resolve_top_level_fn, lower_app
cross-module arm, synth_with_extras Var arm): post-mono
synthesised bodies may carry cross-module references to modules
their source template didn't import (prelude.print__<UserType>
references show_user_adt.show__<UserType> even though prelude
does not import user modules). Fall back to direct
module_user_fns lookup when prefix not in import_map. Both
ends were independently typechecked before mono ran; the
cross-module ref is created by mono not by source.
(c) lib.rs synth FreeFnCall arm: walked Type::Forall.constraints,
substituted rigid vars with fresh metavars, pushed one
ResidualConstraint per declared constraint. Without this,
print f at Int -> Int would silently typecheck and fail with
a confusing 'unknown variable: show' at codegen rather than
fire the right typecheck-time NoInstance diagnostic.
NoInstance Float-aware arm in check/lib.rs:770-779 extended with
a parallel class == 'prelude.Show' branch that cross-references
DESIGN.md §Prelude(built-in) classes verbatim. Negative-fixture
test asserts code 'no-instance' + Show substring + Prelude-(built-in)-
classes substring.
DESIGN.md §Prelude(built-in) classes milestone-24 paragraph flips
fn print from 'ships in 24.3' to 'shipped in iter 24.3' with body
shape + pin file reference. §Float semantics gains a Show-Float
NaN-spelling cross-reference paragraph linking show 1.5 / show nan /
show inf to instance Show Float via float_to_str.
Roadmap P1 'Post-22 Prelude — Show + print rewire' flipped to [x]
with closing summary naming all three shipped iters (24.1 / 24.2 /
24.3). New P2 entry 'Retire io/print_int|bool|float effect-ops +
migrate example corpus to print' inserted at top of P2.
Tests: 556 passed (was 552 + 4 new). bench/cross_lang exit 0;
bench/compile_check + bench/check exit 1 on documented noise-class
metrics per the audit-cma lineage envelope (8th consecutive
audit-grade observation, baseline pristine per conservative-call).
Milestone 24 closes structurally with this iter. Standard audit
pipeline next.
This commit is contained in:
@@ -1980,11 +1980,24 @@ impl<'a> Emitter<'a> {
|
||||
// Logic identical to the typechecker (see `synth` for `Term::Var`).
|
||||
if name.matches('.').count() == 1 {
|
||||
let (prefix, suffix) = name.split_once('.').expect("checked");
|
||||
let target_module = self.import_map.get(prefix).cloned().ok_or_else(|| {
|
||||
CodegenError::Internal(format!(
|
||||
"cross-module call `{name}`: prefix `{prefix}` not in import map"
|
||||
))
|
||||
})?;
|
||||
// Iter 24.3: fall back to direct module-name lookup when
|
||||
// the prefix isn't in the current module's import_map. A
|
||||
// post-mono synthesised body may carry cross-module
|
||||
// references to modules its source template didn't import
|
||||
// (e.g. `prelude.print__<UserType>` synthesised in
|
||||
// `prelude` calls `show_user_adt.show__<UserType>` even
|
||||
// though prelude does not import user modules). See the
|
||||
// matching fallback in `resolve_top_level_fn` for the
|
||||
// companion change on the fn-pointer Var-resolution path.
|
||||
let target_module = match self.import_map.get(prefix).cloned() {
|
||||
Some(m) => m,
|
||||
None if self.module_user_fns.contains_key(prefix) => prefix.to_string(),
|
||||
None => {
|
||||
return Err(CodegenError::Internal(format!(
|
||||
"cross-module call `{name}`: prefix `{prefix}` not in import map"
|
||||
)))
|
||||
}
|
||||
};
|
||||
// iter 23.4: codegen-time poly-call dispatch removed. Post-mono
|
||||
// every poly call site has been rewritten by `rewrite_mono_calls`
|
||||
// to a monomorphic symbol, so the lookup-ladder below sees only
|
||||
@@ -2200,7 +2213,22 @@ impl<'a> Emitter<'a> {
|
||||
fn resolve_top_level_fn(&self, name: &str) -> Option<(String, FnSig)> {
|
||||
if name.matches('.').count() == 1 {
|
||||
let (prefix, suffix) = name.split_once('.')?;
|
||||
let target = self.import_map.get(prefix)?;
|
||||
// Iter 24.3: dotted form resolves through the current
|
||||
// module's import_map first (the standard cross-module
|
||||
// reference path). If the prefix isn't in import_map,
|
||||
// fall back to a direct module-name lookup against
|
||||
// `module_user_fns` — a post-mono synthesised body may
|
||||
// carry cross-module references to modules its source
|
||||
// template didn't import (e.g. `prelude.print__<UserType>`
|
||||
// synthesised in `prelude` calls `show_user_adt.show__<UserType>`
|
||||
// even though prelude does not import user modules; this is
|
||||
// a valid post-mono construct because both ends were
|
||||
// independently typechecked under their original module
|
||||
// contexts before mono ran).
|
||||
let target = match self.import_map.get(prefix) {
|
||||
Some(m) => m,
|
||||
None => prefix,
|
||||
};
|
||||
let sig = self.module_user_fns.get(target)?.get(suffix)?.clone();
|
||||
return Some((format!("@ail_{target}_{suffix}_clos"), sig));
|
||||
}
|
||||
@@ -2748,7 +2776,24 @@ impl<'a> Emitter<'a> {
|
||||
}
|
||||
if name.matches('.').count() == 1 {
|
||||
let (prefix, suffix) = name.split_once('.').expect("checked");
|
||||
if let Some(target) = self.import_map.get(prefix) {
|
||||
// Iter 24.3: try the current module's import_map
|
||||
// first (standard cross-module reference), then
|
||||
// fall back to a direct module-name lookup for
|
||||
// post-mono synthesised cross-module references
|
||||
// (see `resolve_top_level_fn` and the cross-module
|
||||
// call arm in `lower_app` for matching fallbacks).
|
||||
let target_opt: Option<&str> = self
|
||||
.import_map
|
||||
.get(prefix)
|
||||
.map(|s| s.as_str())
|
||||
.or_else(|| {
|
||||
if self.module_def_ail_types.contains_key(prefix) {
|
||||
Some(prefix)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
});
|
||||
if let Some(target) = target_opt {
|
||||
if let Some(ty) = self
|
||||
.module_def_ail_types
|
||||
.get(target)
|
||||
|
||||
Reference in New Issue
Block a user