Skip to content

Commit d23b71d

Browse files
fix: cross-module imports for stdlib (ADR-011) + module builtin seeding (#135) (#172)
The ruled ADR-011 model is explicit `use module::{...}`. io.affine needs string.affine's split/join. Three coupled fixes: 1. resolve.ml: factor builtin seeding into `seed_builtins` and call it in `resolve_and_typecheck_module` — imported modules previously got a bare context, so resolving e.g. string.affine (pulled in by io's `use`) failed with UndefinedVariable "len". 2. resolve.ml: call `Typecheck.register_builtins` in the imported-module typecheck path (the manual check_decl fold skipped it, giving "Unbound variable: len" at the typecheck stage). 3. typecheck.ml: `register_builtins` was missing eprint/eprintln (resolver seeds them; typecheck did not). Plus: `pub` on string.split/join; `use string::{ split, join };` in io. Result: io.affine now passes resolution fully (ADR-011 cross-module import works end to end). io's remaining typecheck failure is a separate pre-existing register_builtins drift (file-I/O builtin family + read_line type), tracked separately. Full suite 233/233 green, zero regression; stdlib 12/19 (unchanged count, io advanced resolve→tc). Refs #128
1 parent 44c0644 commit d23b71d

4 files changed

Lines changed: 33 additions & 15 deletions

File tree

lib/resolve.ml

Lines changed: 26 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -43,16 +43,14 @@ type context = {
4343
(* Helper for Result bind *)
4444
let ( let* ) = Result.bind
4545

46-
(** Create a new resolution context *)
47-
let create_context () : context =
48-
let ctx = {
49-
symbols = Symbol.create ();
50-
current_module = [];
51-
imports = [];
52-
references = [];
53-
} in
54-
(* Register built-in functions — must match the interpreter's create_initial_env *)
55-
let def name = let _ = Symbol.define ctx.symbols name SKFunction Span.dummy Public in () in
46+
(** Seed the builtin functions/constructors into a symbol table.
47+
Shared by [create_context] (top-level program) and
48+
[resolve_and_typecheck_module] (imported modules) so that an imported
49+
stdlib module — e.g. [string.affine] pulled in by [use string::{...}] —
50+
resolves its own use of builtins like [len] instead of failing with
51+
[UndefinedVariable]. Must match the interpreter's create_initial_env. *)
52+
let seed_builtins (symbols : Symbol.t) : unit =
53+
let def name = let _ = Symbol.define symbols name SKFunction Span.dummy Public in () in
5654
(* Console I/O *)
5755
def "print"; def "println"; def "eprint"; def "eprintln";
5856
(* String / char builtins *)
@@ -88,9 +86,19 @@ let create_context () : context =
8886
gap. A user/prelude `type Option`/`type Result` decl cleanly shadows these
8987
(Hashtbl.replace in Symbol.define). Issue #122. *)
9088
let defc name =
91-
let _ = Symbol.define ctx.symbols name SKConstructor Span.dummy Public in ()
89+
let _ = Symbol.define symbols name SKConstructor Span.dummy Public in ()
9290
in
93-
defc "Some"; defc "None"; defc "Ok"; defc "Err";
91+
defc "Some"; defc "None"; defc "Ok"; defc "Err"
92+
93+
(** Create a new resolution context, pre-seeded with builtins. *)
94+
let create_context () : context =
95+
let ctx = {
96+
symbols = Symbol.create ();
97+
current_module = [];
98+
imports = [];
99+
references = [];
100+
} in
101+
seed_builtins ctx.symbols;
94102
ctx
95103

96104
(** Record a use-site reference for a symbol (Phase C: find-references). *)
@@ -548,6 +556,7 @@ let resolve_and_typecheck_module (loaded_mod : Module_loader.loaded_module)
548556
: (Symbol.t * Typecheck.context) result =
549557
let prog = Module_loader.get_program loaded_mod in
550558
let symbols = Symbol.create () in
559+
seed_builtins symbols;
551560
let mod_ctx = { symbols; current_module = []; imports = []; references = [] } in
552561

553562
(* Pass 1: forward-declare every top-level name (#135 slice 11). *)
@@ -559,8 +568,12 @@ let resolve_and_typecheck_module (loaded_mod : Module_loader.loaded_module)
559568
| Ok () -> resolve_decl mod_ctx decl
560569
) (Ok ()) prog.prog_decls in
561570

562-
(* Type-check all declarations *)
571+
(* Type-check all declarations. Seed builtin schemes (len, arithmetic,
572+
Some/None/…) exactly as Typecheck.check_program does for the top-level
573+
program — the manual check_decl fold below otherwise leaves an imported
574+
module's use of builtins as "Unbound variable". *)
563575
let type_ctx = Typecheck.create_context symbols in
576+
Typecheck.register_builtins type_ctx;
564577
let type_result = List.fold_left (fun acc decl ->
565578
match acc with
566579
| Error e -> Error e

lib/typecheck.ml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1194,6 +1194,8 @@ let register_builtins (ctx : context) : unit =
11941194
let float_binop = TArrow (ty_float, QOmega, TArrow (ty_float, QOmega, ty_float, EPure), EPure) in
11951195
bind_var ctx "print" (TArrow (ty_string, QOmega, ty_unit, ESingleton "IO"));
11961196
bind_var ctx "println" (TArrow (ty_string, QOmega, ty_unit, ESingleton "IO"));
1197+
bind_var ctx "eprint" (TArrow (ty_string, QOmega, ty_unit, ESingleton "IO"));
1198+
bind_var ctx "eprintln" (TArrow (ty_string, QOmega, ty_unit, ESingleton "IO"));
11971199
bind_var ctx "read_line" (TArrow (ty_unit, QOmega, ty_string, ESingleton "IO"));
11981200
bind_var ctx "int_to_string" (TArrow (ty_int, QOmega, ty_string, EPure));
11991201
bind_var ctx "int" (TArrow (ty_float, QOmega, ty_int, EPure));

stdlib/io.affine

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,9 @@
2020
// show(value) -> String
2121
// time_now() -> Float (CPU time in seconds)
2222

23+
// Cross-module import (ADR-011: explicit `use module::{...}`)
24+
use string::{ split, join };
25+
2326
// ============================================================================
2427
// Console Output
2528
// ============================================================================

stdlib/string.affine

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -116,7 +116,7 @@ fn repeat(s: String, n: Int) -> String {
116116
}
117117

118118
/// Split string by a delimiter
119-
fn split(s: String, delimiter: String) -> [String] {
119+
pub fn split(s: String, delimiter: String) -> [String] {
120120
let slen = len(s);
121121
let dlen = len(delimiter);
122122

@@ -151,7 +151,7 @@ fn split(s: String, delimiter: String) -> [String] {
151151
}
152152

153153
/// Join an array of strings with a separator
154-
fn join(arr: [String], separator: String) -> String {
154+
pub fn join(arr: [String], separator: String) -> String {
155155
if len(arr) == 0 {
156156
return "";
157157
}

0 commit comments

Comments
 (0)