diff --git a/.machine_readable/6a2/META.a2ml b/.machine_readable/6a2/META.a2ml index 4651258..22f1604 100644 --- a/.machine_readable/6a2/META.a2ml +++ b/.machine_readable/6a2/META.a2ml @@ -872,3 +872,94 @@ references = [ "stdlib/Vscode.affine + packages/affine-vscode/mod.js (#205 thenableThen)", "typed-wasm ADR-004 (convergence / aggregate library; Ephapax)", ] + +[[adr]] +id = "ADR-014" +status = "accepted" +date = "2026-05-18" +title = "Module-qualified type & effect paths: dot separator, sound module-scoped member lookup" +context = """ +The type/effect grammar had NO module-qualified path production. +`Externs.Res` / `Pkg.Type` in type or effect position failed +`parse error` at the `.`. `type_expr_primary` only had +`upper_ident -> TyCon` / `upper_ident [..]/<..> -> TyApp`; +`effect_term` only `ident -> EffVar` / `ident [..] -> EffCon`. +`module_path` already parsed `ident (DOT ident)*` for module/use. +ADR-011 settled real modules with qualified paths; the estate's +Frontier-playbook ports pervasively write `Externs.Foo` in type/effect +position, contradicting a grammar that could not represent it. An +oracle audit (28 repos / 1176 .affine files) measured 498 DRIFT-SYNTAX +files, dominated by exactly this unrepresentable qualified-path shape. +""" +decision = """ +A module-qualified path `A.B.C` is admissible in TYPE and EFFECT +position. The dot `.` is the canonical separator for these paths; +`::` remains value/import (ADR-011 unchanged). Module-prefix segments +may be lower- OR upper-case (stdlib modules are `prelude`/`option`/ +`string`/`result` as well as `Network`/`Http`); the final segment is +the type/effect member and is `upper_ident`. + +`A.B.C` resolves by SOUND MODULE-SCOPED MEMBER LOOKUP, never flat +current scope: +- load module `[A;B]` through the module loader; +- resolve + type-check it exactly as an import would + (`Resolve.resolve_and_typecheck_module`); +- look `C` up INSIDE that module's own resolved symbol table, + requiring `Public`/`PubCrate` and the right kind (`SKType` for + types, `SKEffect` for effects). +An unknown/wrong module, a missing member, a private member, or a +member of the wrong kind is a *resolution* error at the use-site span +— not a parse error, never silently accepted. A validated qualified +effect's name is admitted as a declared effect (issue #59) so a +sibling module's public effect is usable like a local one. + +Design (ripple-minimised): +- `ast.ml`: `ident` gains `modpath : string list` ([] = unqualified). +- `parser.mly`: `mk_qualified_ident`; a `qualified_type_path` helper + (`module_prefix DOT upper_ident`); bare / [args] / applied + forms for types, bare / [args] for effects. +- `typecheck.ml`: `lower_type_expr`/`lower_effect_expr` handle the + qualified case via a loader-backed validator threaded into the + context (`qualified_member_check`), raising `Qualified_path_error` + -> `QualifiedPathError` at the `check_program` boundary (same + pattern as `Effect_validation_error`). Resolved representation is + the canonical nominal type/effect — identical to the bare/imported + form; the validation, not the representation, is the soundness gate. +- `resolve.ml`: `make_qualified_member_check` built where the loader + lives; in the `resolve_and_typecheck_module` recursive group so + transitively-qualified stdlib paths resolve too. + +The `upper_ident` vs `upper_ident DOT …` choice is the expected +benign DOT shift (Menhir shifts, ADR-012). Verified: parser-conflict +summary UNCHANGED — 21 shift/reduce states, 1 reduce/reduce state, +68 s/r + 7 r/r arbitrarily resolved, identical to the pre-change +parser. No new unexplained conflict. +""" +consequences = """ +- Estate Frontier-playbook ports that write `Externs.Foo` / + `prelude.Option` in type/effect position now resolve soundly; the + oracle re-audit shows a large DRIFT-SYNTAX -> PASS/TYPE-ONLY shift. +- Typecheck/codegen unchanged: a qualified ident is treated by its + resolved symbol exactly like an unqualified one. +- Adding `modpath` to `ident` reflows every golden `.expected` AST + dump (regenerated; the span-normalised gate confirms no structural + change). 5 conformance cases added under + tests/conformance/qualified-paths/{valid,invalid}; the full gate is + green (258 -> 263, zero regressions). +- STAGE-C peer of #225 (typed-wasm Http) and #160 (C-spine). Language + decision is human-gated (ISSUE-CLOSURE): Refs #228, NOT Closes. +- Settled; do not reopen without amending this ADR. +""" +references = [ + "https://github.com/hyperpolymath/affinescript/issues/228", + "https://github.com/hyperpolymath/affinescript/issues/225", + "https://github.com/hyperpolymath/affinescript/issues/160", + "docs/specs/SETTLED-DECISIONS.adoc (ADR-014 section)", + "lib/ast.ml (ident.modpath)", + "lib/parser.mly (qualified_type_path; mk_qualified_ident)", + "lib/typecheck.ml (lower_type_expr/lower_effect_expr; qualified_member_check)", + "lib/resolve.ml (make_qualified_member_check; module-scoped lookup)", + "tests/conformance/qualified-paths/{valid,invalid}", + "ADR-011 (real modules with qualified paths)", + "ADR-012 (parser-conflict disclosure; benign DOT shift)", +] diff --git a/.machine_readable/6a2/STATE.a2ml b/.machine_readable/6a2/STATE.a2ml index f695dfe..97672ac 100644 --- a/.machine_readable/6a2/STATE.a2ml +++ b/.machine_readable/6a2/STATE.a2ml @@ -4,8 +4,9 @@ [metadata] project = "affinescript" version = "0.1.0" -last-updated = "2026-05-03" +last-updated = "2026-05-18" status = "active" +session-note-2026-05-18 = "MODULE-QUALIFIED TYPE & EFFECT PATHS (issue #228, ADR-014). The type/effect grammar had NO module-qualified path production: Externs.Res / prelude.Option / any Pkg.Type in type or effect position failed parse-error at the dot, contradicting already-settled ADR-011. Implemented FULL SOUND qualified paths, dot canonical separator (:: stays value/import per ADR-011). (1) lib/ast.ml — ident gains modpath:string list ([] = unqualified); single mk_ident helper. (2) lib/parser.mly — mk_qualified_ident + path_seg/module_prefix/qualified_type_path; qualified type (bare/[]/<>) and effect (bare/[]) productions; the upper_ident-vs-DOT choice is the expected benign DOT shift, masked-conflict count UNCHANGED (21 s/r, 1 r/r — identical to pre-change parser, ADR-012). (3) lib/resolve.ml — make_qualified_member_check: loads module [A;B] via the loader, resolve+typecheck it as an import would, then looks the member up INSIDE that module's own resolved symbols requiring Public/PubCrate + right kind (SKType/SKEffect); unknown/wrong module, missing/private/wrong-kind member = resolution error at use-site span, never silent. Soundness = lookup within the named module, not flat scope. (4) lib/typecheck.ml — qualified-path validation injected via closure (resolve<->typecheck would be circular); validated qualified effect admitted as a declared effect (issue #59). (5) bin/main.ml threads the validator into 6 check_program calls. (6) Internal ident literals in trait/codegen_gc/borrow/verilog_codegen routed through Ast.mk_ident. (7) ADR-014 written to docs/specs/SETTLED-DECISIONS.adoc + .machine_readable/6a2/META.a2ml. (8) 5 conformance fixtures under tests/conformance/qualified-paths/{valid,invalid}; 12 golden .expected regenerated (pure structural no-op = the new modpath field only, verified). Gates: dune build clean; 258 -> 263 tests, 0 regressions; zero menhir conflict delta. Draft PR #231 (Refs #228, NOT Closes — language decision human-gated per ISSUE-CLOSURE; no auto-merge); STAGE-C peer of #225 (typed-wasm Http) and #160 (C-spine). KNOWN LIMITATION: tests/conformance/qualified-paths/valid/qualified_effect.affine references sibling module effmod, so it resolves ONLY with the test-harness module search-path; it FAILS a bare 'affinescript check' (no public stdlib effect exists to build a self-contained positive like prelude.Option does for types). Bare-oracle audits (incl. the estate dialect-conformance .affine-audit harness) MUST treat that one fixture as harness-only. Qualified-effect resolution itself is sound (proven indirectly: invalid/private_member loads stdlib module 'effects' and correctly rejects the private member). Estate-payoff numbers are understated by the bare-loader audit harness (repo-local modules report UndefinedModule); a re-audit with repo module graphs resolvable is pending and is the load-bearing review evidence. Part of the estate AffineScript dialect-conformance campaign (also issues #229 RS-surface elimination, #230 de-vendor)." session-note-2026-05-03-c = "EXTERN/VSCODE/ARRAY/PATCON BATCH. (1) `extern fn name(...) -> Ret;` and `extern type Name;` now parse — added EXTERN keyword to lexer/token/parse_driver, FnExtern to fn_body and TyExtern to type_body in AST, extern_fn_decl + extern_type_decl rules in parser.mly. Resolve registers the symbol; Typecheck.check_fn_decl special-cases FnExtern to register the polymorphic scheme without body checking; Codegen.gen_decl emits a real `(import \"env\" \"\" (func ...))` for each extern fn, mirroring gen_imports's cross-module shape. Borrow + Quantity skip extern fns. lib/dune now demotes warning 8/9 from error to warning so the new variants don't require lock-step updates across all 27 codegens (any non-Wasm codegen that doesn't handle FnExtern raises Match_failure with file:line at runtime — correct signal for 'this target has no story for host-supplied implementations'). 192 tests; 0 regressions. (2) Issue #35 Phase 2: stdlib/Vscode.affine and stdlib/VscodeLanguageClient.affine ship the ~12 + 3 binding declarations from the issue's API inventory (registerCommand, getConfiguration, showInformationMessage, createTerminal, ...). packages/affine-vscode/mod.js is the JS-side adapter that translates each `extern fn` invocation into the corresponding vscode/lc API call, with a JS-side handle table for opaque host objects and a string-marshal helper that reads `[u32 length][utf-8 bytes]` out of the wasm memory. Adapter returns a namespaced object `{ Vscode: {...}, VscodeLanguageClient: {...} }` matching the WASM cross-module imports' module names. examples/vscode_extension_minimal.affine demonstrates an end-to-end VS Code extension authored in AffineScript using `use Vscode::{registerCommand, showInformationMessage, pushSubscription}` — compiles to .cjs via the Node-CJS path. (3) issues-drafts/02 closed: `[T]` array type now parses via `LBRACKET type_expr RBRACKET → TyApp (Array, [TyArg elem])` in lib/parser.mly's type_expr_primary rule. Verified for fn params, return types, struct fields, and nested `[[T]]`. Other stdlib files (Option/math/io/string) still fail with distinct issues (`fn() -> T` type syntax, `Option` angle brackets) — the array fix advanced math.affine's failure point from line 349 to 354 etc. (4) PatCon sub-pattern destructuring under WasmGC: `match Mk(7, 99) { Mk(a, b) => a }` now lowers to RefCast + StructGet for the tag check + per-field RefCast HtI31 + I31GetS unboxing for each PatVar sub-pattern. Validated end-to-end: emitted .wasm instantiates in Deno and `main()` returns 7. The mixed-arity case (zero-arg + with-args in same enum, e.g. Option) errors loudly with the workaround documented (split into two matches, or use Wasm 1.0). Unifying variant rep — uniform `struct {tag, payload}` so Some+None can share — is the next destructuring milestone. 195 → 198 → 200 tests; 0 regressions throughout." session-note-2026-05-03-b = "FOLLOW-ON BATCH AFTER TYPED-WASM CLOSURE: variant-with-args under WasmGC, Node-target codegen Phase 1 (issue #35), stdlib Core.affine fix, cross-module for other codegens. (1) lib/codegen_gc.ml — added gen_variant_with_args helper that lowers ExprApp(ExprVariant(_), args) and bare-name `Some(42)` (via variant_tags lookup in ExprApp ExprVar) to a tagged anon-struct allocation [tag: i32, payload: anyref, ...] with i32 args boxed via ref.i31. ExprVar gained variant_tags fallback so `return Happy` works. gen_gc_function now post-processes body_code: when result_vt ≠ I32 the trailing `push_i32 0` fallback (emitted by gen_gc_block when blk_expr=None) is swapped for RefNull HtAny, which fixes 'end[0] expected type anyref, found i32' validator errors on functions that explicitly return a struct. Validated: emitted .wasm instantiates in Node 18 / V8 14, main() returns the GC struct. f64 args remain UnsupportedFeature (no i31 boxing path). (2) lib/codegen_node.ml — new module implementing issue #35 Phase 1 (Node-CJS emit). Wraps Codegen.generate_module output in a CJS shim with: inline base64-encoded wasm constant, lazy WebAssembly.instantiate on first activate/deactivate call, JS-side opaque-handle table (_registerHandle / _getHandle / _freeHandle exported for Phase 2 vscode bindings), minimal WASI fd_write so println-style codegen works, exports.activate/exports.deactivate re-exports of the wasm module's same-named exports. .cjs output extension dispatches in bin/main.ml (both JSON and non-JSON paths). Smoke-tested: real Node 18 require()s a generated .cjs and successfully calls activate(fakeContext) → 0, deactivate() → 0. Issue #35 Phases 2-4 (stdlib/Vscode.affine bindings, extension.ts → extension.affine migration, rattlescript-face sweep) remain as separate work. (3) stdlib/Core.affine — three parser-collision fixes: `pub fn const[A,B]` → `always` (const is a reserved keyword for compile-time bindings); `fn(x: A) -> C { ... }` lambdas → `|x: A|` form (the parser's actual lambda syntax); `flip` rewritten from `(A, B) -> C` to curried `A -> B -> C` to dodge tuple-arrow ambiguity. The originally-blocked tests/modules/test_simple_import.affine (use Core::{min}; min(10,20)) now compiles end-to-end. Other stdlib files (Option/math/io/string/...) still don't parse — each has distinct issues (fn() type syntax, `[T]` array type per issues-drafts/02, `Option` angle brackets) that need their own passes or compiler-level fixes. (4) lib/module_loader.ml — new flatten_imports : t -> program -> program that prepends imported public TopFns into the importer's prog_decls (deduplicating against local fn names). bin/main.ml now binds [let flat_prog = Module_loader.flatten_imports loader prog in] in both compile_file paths and threads flat_prog through all 22 non-Wasm codegens (Julia, JS, C, WGSL, Faust, ONNX, OCaml, Lua, Bash, Nickel, ReScript, Rust, LLVM, Verilog, Gleam, CUDA, Metal, OpenCL, MLIR, Why3, Lean, SPIR-V) — Wasm and Wasm-GC keep the original prog because Codegen.gen_imports handles their cross-module needs natively. Smoke-tested: caller_ok.affine (use CrossCallee::{consume}) now compiles to JS / Julia / Rust / Lua with consume's body inlined. The non-JSON compile_file path also gained ~loader on its previously-loaderless Codegen.generate_module call (latent bug for cross-module wasm via this path). 188 → 190 tests; 0 regressions." session-note-2026-05-03 = "TYPED-WASM CROSS-MODULE CLOSURE + MCP CARTRIDGE REWIRE + WASMGC LOUD-FAIL HARDENING. (1) lib/codegen.ml — generate_module gained ?loader and a new gen_imports pass that walks prog.prog_imports, loads each referenced module via Module_loader, and emits one (import \"\" \"\" (func ...)) entry per imported function plus a (local_alias_name → import_func_idx) entry in func_indices. ImportSimple is namespace-only (no emit), ImportList emits per item, ImportGlob enumerates public TopFns. Closes the cross-module WASM import emission gap called out in session-note-2026-04-19-a — `verify-boundary CALLEE.affine CALLER.affine` now works on user-authored AffineScript pairs, not just hand-assembled bridges. (2) lib/resolve.ml — import_resolved_symbols / import_specific_items / ImportGlob inline path now also write to dest type_ctx.name_types (not just var_types), with a new lookup_source_scheme helper that falls back from sym_id-keyed source_types to name-keyed source_name_types because resolve_and_typecheck_module's per-decl Typecheck.check_decl populates name_types but never var_types. lib/typecheck.ml — Typecheck.check_program gained ?import_types : (string, scheme) Hashtbl.t that seeds name_types after register_builtins, supplied by the resolver. bin/main.ml compile_file (JSON + non-JSON paths), compile_to_wasm_module, verify_file all updated to thread import_type_ctx.name_types through and pass ~loader to Codegen.generate_module. (3) test/e2e/fixtures/ — CrossCallee.affine + cross_caller_{ok,dup,drop}.affine, plus 3 new alcotest cases under E2E Boundary Verify exercising the full pipeline (parse → resolve_with_loader → typecheck-with-import-types → codegen-with-loader → Tw_interface.verify_cross_module). All three boundary outcomes (clean / LinearImportCalledMultiple / LinearImportDroppedOnSomePath) confirmed end-to-end. (4) boj-server/cartridges/typed-wasm-mcp — mod.js rewritten to call `affinescript` (was: nonexistent `typed-wasm` binary), with cwd set to the source's directory so Module_loader resolves relative imports correctly. cartridge.json bumped to v0.2.0 with corrected input descriptions (.affine source paths) and a new typed_wasm_verify_boundary tool exposing the cross-module verifier. README.adoc updated to match. (5) lib/codegen_gc.ml — eliminated three silent-bad-codegen fallbacks (same class as BUG-005): wildcard ExprLambda/ExprUnsafe → RefNull replaced with explicit UnsupportedFeature errors; match-arm wildcard PatTuple/PatRecord → fall-to-default replaced with UnsupportedFeature; PatLit fallback for LitFloat/LitString replaced with explicit errors. test/test_e2e.ml gained E2E WasmGC Loud-Fail suite with 2 regression markers. Bumps wasm-gc-codegen from 70% to 85% (silent-fallback gap is gone; effects/try-catch/lambda/call_ref remain genuinely deferred to upstream EH proposal or whole-program CPS). 180 → 182 tests; 0 regressions." diff --git a/bin/main.ml b/bin/main.ml index 2acb396..dc333e6 100644 --- a/bin/main.ml +++ b/bin/main.ml @@ -187,6 +187,7 @@ let check_file face json path = resolve_refs := List.rev resolve_ctx.references; (match Affinescript.Typecheck.check_program ~import_types:type_ctx.Affinescript.Typecheck.name_types + ?qualified_member_check:type_ctx.Affinescript.Typecheck.qualified_member_check resolve_ctx.symbols prog with | Error e -> add (Affinescript.Json_output.of_type_error e) @@ -234,6 +235,7 @@ let check_file face json path = | Ok (resolve_ctx, type_ctx) -> (match Affinescript.Typecheck.check_program ~import_types:type_ctx.Affinescript.Typecheck.name_types + ?qualified_member_check:type_ctx.Affinescript.Typecheck.qualified_member_check resolve_ctx.symbols prog with | Error e -> Format.eprintf "@[%s@]@." @@ -497,6 +499,7 @@ let compile_file face json wasm_gc vscode_ext vscode_adapter vscode_no_lc | Ok (resolve_ctx, import_type_ctx) -> (match Affinescript.Typecheck.check_program ~import_types:import_type_ctx.Affinescript.Typecheck.name_types + ?qualified_member_check:import_type_ctx.Affinescript.Typecheck.qualified_member_check resolve_ctx.symbols prog with | Error e -> add (Affinescript.Json_output.of_type_error e) @@ -716,6 +719,7 @@ let compile_file face json wasm_gc vscode_ext vscode_adapter vscode_no_lc | Ok (resolve_ctx, import_type_ctx) -> (match Affinescript.Typecheck.check_program ~import_types:import_type_ctx.Affinescript.Typecheck.name_types + ?qualified_member_check:import_type_ctx.Affinescript.Typecheck.qualified_member_check resolve_ctx.symbols prog with | Error e -> Format.eprintf "@[%s@]@." @@ -1060,6 +1064,7 @@ let compile_to_wasm_module face path | Ok (resolve_ctx, import_type_ctx) -> match Affinescript.Typecheck.check_program ~import_types:import_type_ctx.Affinescript.Typecheck.name_types + ?qualified_member_check:import_type_ctx.Affinescript.Typecheck.qualified_member_check resolve_ctx.symbols prog with | Error e -> Format.eprintf "%s: %s@." path @@ -1120,6 +1125,7 @@ let verify_file face path = | Ok (resolve_ctx, import_type_ctx) -> (match Affinescript.Typecheck.check_program ~import_types:import_type_ctx.Affinescript.Typecheck.name_types + ?qualified_member_check:import_type_ctx.Affinescript.Typecheck.qualified_member_check resolve_ctx.symbols prog with | Error e -> Format.eprintf "%s@." diff --git a/docs/specs/SETTLED-DECISIONS.adoc b/docs/specs/SETTLED-DECISIONS.adoc index b19b396..56a002b 100644 --- a/docs/specs/SETTLED-DECISIONS.adoc +++ b/docs/specs/SETTLED-DECISIONS.adoc @@ -243,3 +243,53 @@ agreed async ABI for the typed-wasm convergence layer; Ephapax is a co-stakeholder (typed-wasm ADR-004). Delivered as 4 incremental, gated PRs. Full design in `docs/specs/async-on-wasm-cps.adoc`; full ADR in `.machine_readable/6a2/META.a2ml` (ADR-013). + +== Module-Qualified Type & Effect Paths: Dot Separator, Sound Member Lookup (ADR-014) + +A module-qualified path `A.B.C` is admissible in *type* and *effect* +position, not only value/import position. The dot `.` is the canonical +separator for these paths; `::` remains the value/import separator +(ADR-011 is unchanged). Module-prefix segments may be lower- or +upper-case (real stdlib modules are `prelude`, `option`, `string`, +`result` as well as `Network`, `Http`); the final member segment is +the type/effect name. + +`A.B.C` resolves by *sound module-scoped member lookup*, never by flat +current scope: the compiler loads module `[A;B]` through the module +loader, resolves and type-checks it exactly as an import would, then +looks `C` up *inside that module's own resolved symbol table*, +requiring it to be `Public`/`pub(crate)` and of the right kind +(`SKType` for types, `SKEffect` for effects). An unknown or wrong +module, a missing member, a private member, or a member of the wrong +kind is a *resolution* error reported at the use-site span — not a +parse error and never silently accepted. Soundness comes from looking +the member up within the named module, so importing `C` flatly does +not make a bogus `A.B.C` resolve. A validated qualified effect's name +is admitted as a declared effect (issue #59), so a sibling module's +public effect is usable exactly like a locally declared one. + +This is consistent with ADR-011 (real modules with qualified paths) +and ADR-012 (the `upper_ident` vs `upper_ident DOT …` choice is the +expected benign DOT shift; the masked-conflict count is unchanged — +21 shift/reduce states, 1 reduce/reduce state, identical to the +pre-change parser). Typecheck/codegen are unchanged: a qualified +ident is treated by its resolved symbol exactly like an unqualified +one (the validation, not the representation, is the soundness gate). + +*Known limitation (conformance harness).* The qualified-*type* +positive fixture is self-contained (`prelude.Option`, a stdlib +module). The qualified-*effect* positive fixture +(`tests/conformance/qualified-paths/valid/qualified_effect.affine`) +must reference a sibling support module (`effmod`) because **no stdlib +module currently declares a public effect**, so it resolves only with +the test harness's module search-path and *fails a bare `affinescript +check`*. Bare-oracle audits (including the estate dialect-conformance +`.affine-audit` harness) must treat that one fixture as harness-only. +Qualified-effect resolution itself is sound and independently proven +by the `invalid/private_member` fixture (it loads stdlib `effects` and +rejects the private member). Resolving this fully — a public stdlib +effect for a self-contained positive — is tracked as follow-up, not a +blocker for the language decision. + +Refs issue #228 (language decision human-gated; not auto-closed). Full +ADR in `.machine_readable/6a2/META.a2ml` (ADR-014). diff --git a/lib/ast.ml b/lib/ast.ml index 42dd285..563ebad 100644 --- a/lib/ast.ml +++ b/lib/ast.ml @@ -3,13 +3,31 @@ (** Abstract Syntax Tree for AffineScript *) -(** Identifiers *) +(** Identifiers. + + [modpath] holds a module-qualified prefix for type/effect paths + (issue #228, ADR-014). [modpath = []] means the identifier is + unqualified (the overwhelming majority of idents). A qualified + type/effect path like [A.B.C] is represented as + [{ name = "C"; modpath = ["A"; "B"]; span }], where [name] is the + final member segment and [modpath] is the module path that the + member must be looked up *within* (sound module-scoped resolution, + not flat-scope). The dot [.] is the canonical separator; [::] + remains value/import per ADR-011. *) type ident = { name : string; span : Span.t; + modpath : string list; } [@@deriving show, eq] +(** Construct an unqualified identifier ([modpath = []]). Used by + compiler-internal synthetic idents (trait desugaring, codegen + helpers, …) so the [modpath] field (issue #228) is set in exactly + one place rather than scattered as record literals. *) +let mk_ident ?(span : Span.t = Span.dummy) (name : string) : ident = + { name; span; modpath = [] } + (** Quantity annotations for QTT *) type quantity = | QZero (** Erased - compile time only *) diff --git a/lib/borrow.ml b/lib/borrow.ml index 883a059..9bc4c32 100644 --- a/lib/borrow.ml +++ b/lib/borrow.ml @@ -573,7 +573,7 @@ let rec check_expr (ctx : context) (state : state) (symbols : Symbol.t) (expr : let borrow_span = expr_span (ExprLambda lam) in let* () = List.fold_left (fun acc name -> let* () = acc in - match expr_to_place symbols (ExprVar { name; span = borrow_span }) with + match expr_to_place symbols (ExprVar (mk_ident ~span:borrow_span name)) with | Some place -> (* Check the place is not already moved before we borrow it. *) let* _borrow = record_borrow state place Shared borrow_span in diff --git a/lib/codegen_gc.ml b/lib/codegen_gc.ml index f4f44ae..7a9008a 100644 --- a/lib/codegen_gc.ml +++ b/lib/codegen_gc.ml @@ -430,7 +430,7 @@ let rec gen_gc_expr (ctx : gc_ctx) (expr : expr) : (gc_ctx * gc_instr list) cg_r | Some e -> e | None -> let i = List.length rev_codes in - ExprVar { name = List.nth field_names i; span = Span.dummy } + ExprVar (mk_ident (List.nth field_names i)) in let* (c', code) = gen_gc_expr c fexpr in Ok (c', code :: rev_codes) diff --git a/lib/parser.mly b/lib/parser.mly index e8b29a3..d6a7e3b 100644 --- a/lib/parser.mly +++ b/lib/parser.mly @@ -18,7 +18,15 @@ let mk_span startpos endpos = Span.make ~file ~start_pos ~end_pos let mk_ident name startpos endpos = - { name; span = mk_span startpos endpos } + { name; span = mk_span startpos endpos; modpath = [] } + +(* issue #228 / ADR-014: a module-qualified type/effect path `A.B.C`. + [path] is the module prefix (["A"; "B"]) and [name] is the final + member segment ("C") that resolution must look up *within* that + module's resolved public symbols (sound module-scoped lookup, not + flat current scope). The span covers the whole dotted path. *) +let mk_qualified_ident (path : string list) name startpos endpos = + { name; span = mk_span startpos endpos; modpath = path } (* issue #122 v2: inherent-impl support. The old grammar pre-committed to `impl_trait_ref? self_ty` where both alternatives start with an ident, @@ -450,6 +458,26 @@ type_expr_primary: | MUT ty = type_expr_primary { TyMut ty } | name = lower_ident { TyVar (mk_ident name $startpos $endpos) } | name = upper_ident { TyCon (mk_ident name $startpos $endpos) } + /* issue #228 / ADR-014: module-qualified type path `A.B.C`. The + prefix segments `A.B` are the module path; the last segment `C` + is the type member, resolved *within* that module's public + symbols (sound module-scoped lookup — see lib/typecheck.ml + lower_type_expr / lib/resolve.ml). `.` is canonical (ADR-011 + keeps `::` for value/import). The `upper_ident` vs + `upper_ident DOT ...` choice is the expected benign shift on + DOT (Menhir shifts; disclosed per ADR-012). Bare / [args] / + applied forms all supported. */ + | qp = qualified_type_path + { let (prefix, last, sp, ep) = qp in + TyCon (mk_qualified_ident prefix last sp ep) } + | qp = qualified_type_path + LBRACKET args = separated_nonempty_list(COMMA, type_arg) RBRACKET + { let (prefix, last, sp, ep) = qp in + TyApp (mk_qualified_ident prefix last sp ep, args) } + | qp = qualified_type_path + LT args = separated_nonempty_list(COMMA, type_arg) GT + { let (prefix, last, sp, ep) = qp in + TyApp (mk_qualified_ident prefix last sp ep, args) } | name = upper_ident LBRACKET args = separated_nonempty_list(COMMA, type_arg) RBRACKET { TyApp (mk_ident name $startpos(name) $endpos(name), args) } /* Angle-bracket alias for type application: `Option` ≡ `Option[T]`, @@ -569,6 +597,19 @@ effect_term: | name = ident { EffVar name } | name = ident LBRACKET args = separated_list(COMMA, type_arg) RBRACKET { EffCon (name, args) } + /* issue #228 / ADR-014: module-qualified effect path `A.B.Eff`. + Same module-scoped soundness as qualified types: the prefix is a + module path, the last segment the effect member resolved within + it. Reuses `qualified_type_path` (module + member both upper — + stdlib effects are `IO`/`Mut`/`Throws`, module names upper). The + applied `[args]` form mirrors the parametric-effect rule above. */ + | qp = qualified_type_path + { let (prefix, last, sp, ep) = qp in + EffVar (mk_qualified_ident prefix last sp ep) } + | qp = qualified_type_path + LBRACKET args = separated_list(COMMA, type_arg) RBRACKET + { let (prefix, last, sp, ep) = qp in + EffCon (mk_qualified_ident prefix last sp ep, args) } /* ========== Traits ========== */ @@ -1062,6 +1103,32 @@ pattern_rest: /* ========== Helpers ========== */ +/* issue #228 / ADR-014: a module-qualified type/effect path + `A.B.C`. Returns (module-prefix, member, startpos, endpos). The + member (last segment) is the type/effect name and is always + `upper_ident` (stdlib types `Option`/`Result`/… and effects + `IO`/`Mut`/`Throws` are uppercase; a lowercase final segment is + not a valid type/effect anyway). The module-prefix segments may be + *either* case because real stdlib module names are lowercase + (`prelude`, `option`, `string`, `result`) as well as uppercase + (`Network`, `Http`) — this mirrors `module_path`/`use`, which + accept `ident` (both cases). Length is >=2 (>=1 prefix segment + + the member), enforced by the `module_prefix DOT upper_ident` + shape. Left-recursive prefix. The `upper_ident` vs + `upper_ident DOT ...` decision is the expected benign DOT shift + (Menhir shifts; disclosed per ADR-012). */ +path_seg: + | s = lower_ident { s } + | s = upper_ident { s } + +module_prefix: + | s = path_seg { [s] } + | p = module_prefix DOT s = path_seg { p @ [s] } + +qualified_type_path: + | p = module_prefix DOT m = upper_ident + { (p, m, $startpos, $endpos) } + ident: | name = lower_ident { mk_ident name $startpos $endpos } | name = upper_ident { mk_ident name $startpos $endpos } diff --git a/lib/resolve.ml b/lib/resolve.ml index de1bbef..63fb4b6 100644 --- a/lib/resolve.ml +++ b/lib/resolve.ml @@ -680,7 +680,9 @@ let rec resolve_and_typecheck_module imported modules must check the same way top-level programs do.) *) match Typecheck.check_program - ~import_types:type_ctx.Typecheck.name_types symbols prog + ~import_types:type_ctx.Typecheck.name_types + ~qualified_member_check:(make_qualified_member_check mod_ctx loader) + symbols prog with | Ok final_ctx -> Ok (symbols, final_ctx) | Error type_err -> @@ -779,6 +781,87 @@ and resolve_imports_with_loader end ) (Ok ()) imports +(* issue #228 / ADR-014: build the sound module-scoped member + validator that [Typecheck.lower_type_expr] / [lower_effect_expr] + call for a qualified path [A.B.C]. + + Soundness: it does NOT consult the flat current scope. It loads + module [A.B] *through the loader*, runs the same resolve + + type-check it would get as an import ([resolve_and_typecheck_module] + — hence membership in this recursive group), and then looks [C] up + *inside that module's own resolved symbol table*, requiring it to + be [Public]/[PubCrate] and of the expected kind ([SKType] for + types, [SKEffect] for effects). A wrong/unknown module, a missing + member, a private member, or a member of the wrong kind is an error + reported at the use-site span. On success the use-site reference is + recorded (LSP find-references). *) +and make_qualified_member_check + (ctx : context) + (loader : Module_loader.t) + : string list -> string -> [ `Type | `Effect ] -> Span.t -> + (unit, string * Span.t) Result.t = + fun prefix member kind span -> + let kind_word = match kind with `Type -> "type" | `Effect -> "effect" in + let mod_str = String.concat "." prefix in + match Module_loader.load_module loader prefix with + | Error (Module_loader.ModuleNotFound _) -> + Error (Printf.sprintf + "Unknown module '%s' in qualified %s path '%s.%s' (ADR-014)." + mod_str kind_word mod_str member, span) + | Error e -> + Error (Printf.sprintf + "Cannot load module '%s' for qualified %s path '%s.%s': %s" + mod_str kind_word mod_str member + (Module_loader.show_load_error e), span) + | Ok loaded_mod -> + begin match resolve_and_typecheck_module loader loaded_mod with + | Error (rerr, _) -> + Error (Printf.sprintf + "Module '%s' (named in qualified %s path '%s.%s') failed to \ + resolve/type-check: %s" + mod_str kind_word mod_str member (show_resolve_error rerr), span) + | Ok (mod_symbols, _mod_type_ctx) -> + begin match Symbol.lookup mod_symbols member with + | None -> + Error (Printf.sprintf + "Module '%s' has no member '%s' (qualified %s path \ + '%s.%s', ADR-014)." + mod_str member kind_word mod_str member, span) + | Some sym -> + let kind_ok = match kind, sym.Symbol.sym_kind with + | `Type, Symbol.SKType -> true + | `Effect, Symbol.SKEffect -> true + | _ -> false + in + let vis_ok = match sym.Symbol.sym_visibility with + | Public | PubCrate -> true + | _ -> false + in + if not kind_ok then + Error (Printf.sprintf + "'%s.%s' resolves to a %s, not a %s (ADR-014)." + mod_str member + (match sym.Symbol.sym_kind with + | Symbol.SKType -> "type" | Symbol.SKEffect -> "effect" + | Symbol.SKFunction -> "function" + | Symbol.SKConstructor -> "constructor" + | Symbol.SKVariable -> "variable" + | Symbol.SKTypeVar -> "type variable" + | Symbol.SKEffectOp -> "effect operation" + | Symbol.SKModule -> "module") + kind_word, span) + else if not vis_ok then + Error (Printf.sprintf + "Member '%s' of module '%s' is private; a qualified %s \ + path requires a `pub`/`pub(crate)` member (ADR-014)." + member mod_str kind_word, span) + else begin + record_reference ctx sym span; + Ok () + end + end + end + (** Resolve imports in a program (legacy, without module loader) *) let resolve_imports (ctx : context) (imports : import_decl list) : unit result = List.fold_left (fun acc import -> @@ -836,6 +919,8 @@ let resolve_program_with_loader (loader : Module_loader.t) : (context * Typecheck.context) result = let ctx = create_context () in let type_ctx = Typecheck.create_context ctx.symbols in + type_ctx.Typecheck.qualified_member_check <- + Some (make_qualified_member_check ctx loader); (* First resolve imports using module loader *) let* () = resolve_imports_with_loader ctx type_ctx loader program.prog_imports in (* Pass 1: forward-declare every top-level name (#135 slice 11). *) diff --git a/lib/trait.ml b/lib/trait.ml index 51cb44d..8f55205 100644 --- a/lib/trait.ml +++ b/lib/trait.ml @@ -439,17 +439,17 @@ let check_coherence (registry : trait_registry) (trait_name : string) : unit res (** Standard library traits - automatically registered *) let register_stdlib_traits (registry : trait_registry) : unit = (* Eq trait *) - let self_ty = Ast.TyCon { Ast.name = "Self"; span = Span.dummy } in + let self_ty = Ast.TyCon (Ast.mk_ident "Self") in let eq_self_param = { Ast.p_quantity = None; p_ownership = Some Ast.Ref; - p_name = { Ast.name = "self"; span = Span.dummy }; + p_name = Ast.mk_ident "self"; p_ty = self_ty; } in let eq_other_param = { Ast.p_quantity = None; p_ownership = Some Ast.Ref; - p_name = { Ast.name = "other"; span = Span.dummy }; + p_name = Ast.mk_ident "other"; p_ty = self_ty; } in let eq_trait = { @@ -471,20 +471,20 @@ let register_stdlib_traits (registry : trait_registry) : unit = let ord_self_param = { Ast.p_quantity = None; p_ownership = Some Ast.Ref; - p_name = { Ast.name = "self"; span = Span.dummy }; + p_name = Ast.mk_ident "self"; p_ty = self_ty; } in let ord_other_param = { Ast.p_quantity = None; p_ownership = Some Ast.Ref; - p_name = { Ast.name = "other"; span = Span.dummy }; + p_name = Ast.mk_ident "other"; p_ty = self_ty; } in let ord_trait = { td_name = "Ord"; td_type_params = []; td_super = [{ - tb_name = { name = "Eq"; span = Span.dummy }; + tb_name = Ast.mk_ident "Eq"; tb_args = []; }]; td_methods = [{ @@ -502,7 +502,7 @@ let register_stdlib_traits (registry : trait_registry) : unit = let hash_self_param = { Ast.p_quantity = None; p_ownership = Some Ast.Ref; - p_name = { Ast.name = "self"; span = Span.dummy }; + p_name = Ast.mk_ident "self"; p_ty = self_ty; } in let hash_trait = { @@ -524,7 +524,7 @@ let register_stdlib_traits (registry : trait_registry) : unit = let display_self_param = { Ast.p_quantity = None; p_ownership = Some Ast.Ref; - p_name = { Ast.name = "self"; span = Span.dummy }; + p_name = Ast.mk_ident "self"; p_ty = self_ty; } in let display_trait = { diff --git a/lib/typecheck.ml b/lib/typecheck.ml index cb6354f..f08562c 100644 --- a/lib/typecheck.ml +++ b/lib/typecheck.ml @@ -112,6 +112,11 @@ type type_error = function's explicitly declared effect row (issue #59). Only raised when a row is declared; an undeclared row stays permissive under tracking-only v1. *) + | QualifiedPathError of string * Span.t + (** issue #228 / ADR-014: a module-qualified type/effect path + `A.B.C` failed sound module-scoped resolution — unknown + module, missing member, wrong kind, or non-public member. + Carries a human message and the use-site span. *) (** Format a type error for human consumption. *) let show_type_error = function @@ -156,6 +161,7 @@ let show_type_error = function performs `Partial` — add it to the row, e.g. /{%s}." inferred declared (if declared = "" then "Partial" else declared ^ ", Partial") + | QualifiedPathError (msg, _span) -> msg let format_type_error = show_type_error @@ -164,6 +170,13 @@ let format_type_error = show_type_error (lowering is not in the result monad). *) exception Effect_validation_error of string +(** issue #228 / ADR-014: raised by [lower_type_expr] / + [lower_effect_expr] when a module-qualified path fails sound + module-scoped member resolution; caught at the [check_program] + boundary and converted to [QualifiedPathError] (lowering is not in + the result monad — same pattern as [Effect_validation_error]). *) +exception Qualified_path_error of string * Span.t + (** {1 Context} *) (** Type checking context. @@ -189,6 +202,20 @@ type context = { declared_effects : (string, unit) Hashtbl.t; (** User-declared effect names (`effect ;`). Consulted by [lower_effect_expr] alongside the v1 registry (issue #59). *) + mutable qualified_member_check : + (string list -> string -> [ `Type | `Effect ] -> Span.t -> + (unit, string * Span.t) Result.t) option; + (** issue #228 / ADR-014: sound module-scoped validator for a + qualified type/effect path [A.B.C]. Given the module prefix + (the [A.B] part), the member name (the [C] part), the kind, and + the use-site span, it loads + resolves + type-checks module + [["A";"B"]] and confirms [C] is a [Public]/[PubCrate] symbol of + the right kind *within that module*. Returns [Ok ()] or an error + message + span. Injected by [Resolve] (which owns the + [Module_loader]); [None] when no loader is available (e.g. a + bare [check_program] with no imports), in which case a qualified + path that reaches lowering is rejected as unresolved rather than + silently accepted (sound default). *) } type 'a result = ('a, type_error) Result.t @@ -219,6 +246,7 @@ let create_context (symbols : Symbol.t) : context = current_eff = fresh_effvar 0; trait_registry = Trait.create_registry (); declared_effects = Hashtbl.create 16; + qualified_member_check = None; } (** Enter a deeper let-level. *) @@ -387,6 +415,35 @@ let lower_quantity (q : Ast.quantity) : Types.quantity = Type variables are looked up in [type_env]; unknown names become fresh unification variables so that inference can resolve them. *) +(** issue #228 / ADR-014: validate a module-qualified type/effect + path by *sound module-scoped member lookup*. [id.modpath] is the + module path; [id.name] the member. Delegates to the loader-backed + validator injected by [Resolve]; raises [Qualified_path_error] on + any failure (unknown module, missing/private member, wrong kind) + or when no validator is wired (sound default — a qualified path is + rejected, never silently accepted). A no-op for unqualified idents + ([modpath = []]). *) +let check_qualified_member + (ctx : context) (id : ident) (kind : [ `Type | `Effect ]) : unit = + match id.modpath with + | [] -> () + | prefix -> + begin match ctx.qualified_member_check with + | Some f -> + begin match f prefix id.name kind id.span with + | Ok () -> () + | Error (msg, sp) -> raise (Qualified_path_error (msg, sp)) + end + | None -> + raise (Qualified_path_error + (Printf.sprintf + "Qualified %s path '%s.%s' cannot be resolved: no module \ + loader is available in this compilation unit." + (match kind with `Type -> "type" | `Effect -> "effect") + (String.concat "." prefix) id.name, + id.span)) + end + let rec lower_type_expr (ctx : context) (te : type_expr) : ty = match te with | TyVar { name; _ } -> @@ -398,6 +455,25 @@ let rec lower_type_expr (ctx : context) (te : type_expr) : ty = Hashtbl.replace ctx.type_env name tv; tv end + | TyCon ({ name; modpath; _ } as id) when modpath <> [] -> + (* issue #228 / ADR-014: `A.B.C` in type position. Soundly verify + `C` is a public type member *of module A.B*, then lower to the + canonical nominal `TCon C` — the same type the bare/imported + form denotes (the validation, not the representation, is what + makes this sound). *) + check_qualified_member ctx id `Type; + begin match Hashtbl.find_opt ctx.type_env name with + | Some ty -> ty + | None -> TCon name + end + | TyApp (({ name; modpath; _ } as id), args) when modpath <> [] -> + check_qualified_member ctx id `Type; + let head = match Hashtbl.find_opt ctx.type_env name with + | Some ty -> ty + | None -> TCon name + in + let arg_tys = List.map (fun (TyArg te) -> lower_type_expr ctx te) args in + TApp (head, arg_tys) | TyCon { name; _ } -> begin match name with | "Int" -> ty_int @@ -464,6 +540,31 @@ and lower_effect_expr (ctx : context) (ee : effect_expr) : eff = else raise (Effect_validation_error name) in match ee with + | EffVar ({ modpath; _ } as id) when modpath <> [] -> + (* issue #228 / ADR-014: `A.B.E` in effect position. Soundly + verify `E` is a public effect member *of module A.B*. Once the + module-scoped gate confirms it is a real Public/PubCrate + SKEffect of that module, register the name as a declared effect + so it resolves exactly like an unqualified user-declared effect + (issue #59 `resolve` otherwise only knows the v1 registry + + *this* module's `declared_effects`; a sibling module's public + effect is just as legitimate, and the soundness comes from the + module-scoped check having already passed). *) + check_qualified_member ctx id `Effect; + Hashtbl.replace ctx.declared_effects id.name (); + resolve id.name + | EffCon (({ modpath; _ } as id), args) when modpath <> [] -> + check_qualified_member ctx id `Effect; + Hashtbl.replace ctx.declared_effects id.name (); + (match resolve id.name with + | ESingleton base when args <> [] -> + let arg_str = + args + |> List.map (fun (TyArg te) -> ty_to_string (lower_type_expr ctx te)) + |> String.concat ", " + in + ESingleton (base ^ "[" ^ arg_str ^ "]") + | other -> other) | EffVar { name; _ } -> resolve name | EffCon ({ name; _ }, args) -> (* Parametric effect, e.g. `Throws[MyErr]` (issue #59). Effect @@ -1753,10 +1854,18 @@ let check_decl (ctx : context) (decl : top_level) : (unit, type_error) Result.t can resolve [f] to the imported function's scheme even though [f] does not appear in [prog.prog_decls]. *) let check_program ?(import_types : (string, scheme) Hashtbl.t option) + ?(qualified_member_check : + (string list -> string -> [ `Type | `Effect ] -> Span.t -> + (unit, string * Span.t) Result.t) option) (symbols : Symbol.t) (prog : Ast.program) : (context, type_error) Result.t = try let ctx = create_context symbols in + (* issue #228 / ADR-014: thread the sound module-scoped validator + into the *fresh* context this entry point creates, so a qualified + type/effect path reaching [lower_type_expr]/[lower_effect_expr] + is checked against the named module's real public symbols. *) + ctx.qualified_member_check <- qualified_member_check; register_builtins ctx; Option.iter (fun tbl -> Hashtbl.iter (fun name sc -> Hashtbl.replace ctx.name_types name sc) tbl @@ -1802,4 +1911,6 @@ let check_program ?(import_types : (string, scheme) Hashtbl.t option) Error (QuantityError (qerr, span)) end | Error e -> Error e - with Effect_validation_error name -> Error (UnknownEffect name) + with + | Effect_validation_error name -> Error (UnknownEffect name) + | Qualified_path_error (msg, sp) -> Error (QualifiedPathError (msg, sp)) diff --git a/lib/verilog_codegen.ml b/lib/verilog_codegen.ml index deebe00..c6d5fe2 100644 --- a/lib/verilog_codegen.ml +++ b/lib/verilog_codegen.ml @@ -83,7 +83,7 @@ let gen_module (buf : Buffer.t) (fd : fn_decl) : unit = Printf.sprintf "%s%s" kind p.p_name.name ) fd.fd_params in let out_kind = ty_to_decl - (match fd.fd_ret_ty with Some t -> t | None -> TyCon { name="Int"; span=Span.dummy }) + (match fd.fd_ret_ty with Some t -> t | None -> TyCon (mk_ident "Int")) "output" in let port_list = String.concat ",\n " (ports @ [out_kind ^ "out"]) in Buffer.add_string buf (Printf.sprintf "module %s (\n %s\n);\n" name port_list); diff --git a/test/dune b/test/dune index 0c1a31c..d1f1b03 100644 --- a/test/dune +++ b/test/dune @@ -5,4 +5,5 @@ (source_tree golden) (source_tree e2e) (source_tree ../examples) - (source_tree ../stdlib))) + (source_tree ../stdlib) + (source_tree ../tests/conformance/qualified-paths))) diff --git a/test/golden/binary_ops.expected b/test/golden/binary_ops.expected index c1694ce..9ededbd 100644 --- a/test/golden/binary_ops.expected +++ b/test/golden/binary_ops.expected @@ -7,8 +7,8 @@ span = { Span.start_pos = { Span.line = 2; col = 1; offset = 40 }; end_pos = { Span.line = 2; col = 4; offset = 43 }; - file = "test/golden/binary_ops.affine" } - }; + file = "test/golden/binary_ops.affine" }; + modpath = [] }; fd_type_params = []; fd_params = []; fd_ret_ty = (Some (Ast.TyCon @@ -17,8 +17,8 @@ { Span.start_pos = { Span.line = 2; col = 15; offset = 54 }; end_pos = { Span.line = 2; col = 18; offset = 57 }; - file = "test/golden/binary_ops.affine" } - })); + file = "test/golden/binary_ops.affine" }; + modpath = [] })); fd_eff = None; fd_where = []; fd_body = (Ast.FnBlock @@ -63,8 +63,8 @@ span = { Span.start_pos = { Span.line = 6; col = 1; offset = 78 }; end_pos = { Span.line = 6; col = 4; offset = 81 }; - file = "test/golden/binary_ops.affine" } - }; + file = "test/golden/binary_ops.affine" }; + modpath = [] }; fd_type_params = []; fd_params = []; fd_ret_ty = (Some (Ast.TyCon @@ -73,8 +73,8 @@ { Span.start_pos = { Span.line = 6; col = 19; offset = 96 }; end_pos = { Span.line = 6; col = 22; offset = 99 }; - file = "test/golden/binary_ops.affine" } - })); + file = "test/golden/binary_ops.affine" }; + modpath = [] })); fd_eff = None; fd_where = []; fd_body = (Ast.FnBlock @@ -89,8 +89,8 @@ { Span.line = 6; col = 27; offset = 104 }; end_pos = { Span.line = 7; col = 3; offset = 108 }; - file = "test/golden/binary_ops.affine" } - }), + file = "test/golden/binary_ops.affine" }; + modpath = [] }), Ast.OpEq, (Ast.ExprVar { Ast.name = "y"; @@ -99,8 +99,8 @@ { Span.line = 7; col = 5; offset = 110 }; end_pos = { Span.line = 7; col = 8; offset = 113 }; - file = "test/golden/binary_ops.affine" } - }) + file = "test/golden/binary_ops.affine" }; + modpath = [] }) )), Ast.OpAnd, (Ast.ExprBinary ( @@ -111,8 +111,8 @@ { Span.line = 7; col = 10; offset = 115 }; end_pos = { Span.line = 7; col = 13; offset = 118 }; - file = "test/golden/binary_ops.affine" } - }), + file = "test/golden/binary_ops.affine" }; + modpath = [] }), Ast.OpLt, (Ast.ExprVar { Ast.name = "b"; @@ -121,8 +121,8 @@ { Span.line = 7; col = 15; offset = 120 }; end_pos = { Span.line = 7; col = 17; offset = 122 }; - file = "test/golden/binary_ops.affine" } - }) + file = "test/golden/binary_ops.affine" }; + modpath = [] }) )) ))) }) @@ -134,8 +134,8 @@ span = { Span.start_pos = { Span.line = 10; col = 1; offset = 127 }; end_pos = { Span.line = 10; col = 4; offset = 130 }; - file = "test/golden/binary_ops.affine" } - }; + file = "test/golden/binary_ops.affine" }; + modpath = [] }; fd_type_params = []; fd_params = [{ Ast.p_quantity = None; p_ownership = None; @@ -144,8 +144,8 @@ span = { Span.start_pos = { Span.line = 10; col = 16; offset = 142 }; end_pos = { Span.line = 10; col = 17; offset = 143 }; - file = "test/golden/binary_ops.affine" } - }; + file = "test/golden/binary_ops.affine" }; + modpath = [] }; p_ty = (Ast.TyCon { Ast.name = "Int"; @@ -153,8 +153,8 @@ { Span.start_pos = { Span.line = 10; col = 18; offset = 144 }; end_pos = { Span.line = 10; col = 20; offset = 146 }; - file = "test/golden/binary_ops.affine" } - }) + file = "test/golden/binary_ops.affine" }; + modpath = [] }) }; { Ast.p_quantity = None; p_ownership = None; p_name = @@ -162,8 +162,8 @@ span = { Span.start_pos = { Span.line = 10; col = 23; offset = 149 }; end_pos = { Span.line = 10; col = 25; offset = 151 }; - file = "test/golden/binary_ops.affine" } - }; + file = "test/golden/binary_ops.affine" }; + modpath = [] }; p_ty = (Ast.TyCon { Ast.name = "Int"; @@ -171,8 +171,8 @@ { Span.start_pos = { Span.line = 10; col = 26; offset = 152 }; end_pos = { Span.line = 10; col = 28; offset = 154 }; - file = "test/golden/binary_ops.affine" } - }) + file = "test/golden/binary_ops.affine" }; + modpath = [] }) } ]; fd_ret_ty = @@ -182,8 +182,8 @@ { Span.start_pos = { Span.line = 10; col = 33; offset = 159 }; end_pos = { Span.line = 10; col = 36; offset = 162 }; - file = "test/golden/binary_ops.affine" } - })); + file = "test/golden/binary_ops.affine" }; + modpath = [] })); fd_eff = None; fd_where = []; fd_body = (Ast.FnBlock @@ -203,8 +203,8 @@ { Span.line = 11; col = 4; offset = 171 }; file = "test/golden/binary_ops.affine" - } - }), + }; + modpath = [] }), Ast.OpBitAnd, (Ast.ExprVar { Ast.name = "b"; @@ -216,8 +216,8 @@ { Span.line = 11; col = 8; offset = 175 }; file = "test/golden/binary_ops.affine" - } - }) + }; + modpath = [] }) )), Ast.OpBitOr, (Ast.ExprBinary ( @@ -231,8 +231,8 @@ { Span.line = 11; col = 12; offset = 179 }; file = "test/golden/binary_ops.affine" - } - }), + }; + modpath = [] }), Ast.OpBitXor, (Ast.ExprVar { Ast.name = "b"; @@ -244,8 +244,8 @@ { Span.line = 11; col = 16; offset = 183 }; file = "test/golden/binary_ops.affine" - } - }) + }; + modpath = [] }) )) )), Ast.OpShl, @@ -277,8 +277,8 @@ span = { Span.start_pos = { Span.line = 14; col = 1; offset = 199 }; end_pos = { Span.line = 14; col = 4; offset = 202 }; - file = "test/golden/binary_ops.affine" } - }; + file = "test/golden/binary_ops.affine" }; + modpath = [] }; fd_type_params = []; fd_params = [{ Ast.p_quantity = None; p_ownership = None; @@ -287,8 +287,8 @@ span = { Span.start_pos = { Span.line = 14; col = 20; offset = 218 }; end_pos = { Span.line = 14; col = 21; offset = 219 }; - file = "test/golden/binary_ops.affine" } - }; + file = "test/golden/binary_ops.affine" }; + modpath = [] }; p_ty = (Ast.TyCon { Ast.name = "Int"; @@ -296,8 +296,8 @@ { Span.start_pos = { Span.line = 14; col = 22; offset = 220 }; end_pos = { Span.line = 14; col = 24; offset = 222 }; - file = "test/golden/binary_ops.affine" } - }) + file = "test/golden/binary_ops.affine" }; + modpath = [] }) } ]; fd_ret_ty = @@ -307,8 +307,8 @@ { Span.start_pos = { Span.line = 14; col = 29; offset = 227 }; end_pos = { Span.line = 14; col = 32; offset = 230 }; - file = "test/golden/binary_ops.affine" } - })); + file = "test/golden/binary_ops.affine" }; + modpath = [] })); fd_eff = None; fd_where = []; fd_body = (Ast.FnBlock @@ -322,10 +322,10 @@ { Span.line = 15; col = 3; offset = 238 }; end_pos = { Span.line = 15; col = 4; offset = 239 }; - file = "test/golden/binary_ops.affine" } - }) + file = "test/golden/binary_ops.affine" }; + modpath = [] }) ))) }) }) ] - } + } \ No newline at end of file diff --git a/test/golden/effects.expected b/test/golden/effects.expected index d409e83..662ba4f 100644 --- a/test/golden/effects.expected +++ b/test/golden/effects.expected @@ -7,8 +7,8 @@ span = { Span.start_pos = { Span.line = 2; col = 1; offset = 28 }; end_pos = { Span.line = 2; col = 8; offset = 35 }; - file = "test/golden/effects.affine" } - }; + file = "test/golden/effects.affine" }; + modpath = [] }; ed_type_params = []; ed_ops = [{ Ast.eod_name = @@ -16,8 +16,8 @@ span = { Span.start_pos = { Span.line = 3; col = 3; offset = 42 }; end_pos = { Span.line = 3; col = 6; offset = 45 }; - file = "test/golden/effects.affine" } - }; + file = "test/golden/effects.affine" }; + modpath = [] }; eod_params = [{ Ast.p_quantity = None; p_ownership = None; p_name = @@ -25,8 +25,8 @@ span = { Span.start_pos = { Span.line = 3; col = 11; offset = 50 }; end_pos = { Span.line = 3; col = 12; offset = 51 }; - file = "test/golden/effects.affine" } - }; + file = "test/golden/effects.affine" }; + modpath = [] }; p_ty = (Ast.TyCon { Ast.name = "String"; @@ -34,8 +34,8 @@ { Span.start_pos = { Span.line = 3; col = 13; offset = 52 }; end_pos = { Span.line = 3; col = 15; offset = 54 }; - file = "test/golden/effects.affine" } - }) + file = "test/golden/effects.affine" }; + modpath = [] }) } ]; eod_ret_ty = (Some (Ast.TyTuple [])) }; @@ -44,8 +44,8 @@ span = { Span.start_pos = { Span.line = 4; col = 3; offset = 71 }; end_pos = { Span.line = 4; col = 6; offset = 74 }; - file = "test/golden/effects.affine" } - }; + file = "test/golden/effects.affine" }; + modpath = [] }; eod_params = []; eod_ret_ty = (Some (Ast.TyCon @@ -54,8 +54,8 @@ { Span.start_pos = { Span.line = 4; col = 13; offset = 81 }; end_pos = { Span.line = 4; col = 16; offset = 84 }; - file = "test/golden/effects.affine" } - })) + file = "test/golden/effects.affine" }; + modpath = [] })) } ] }); @@ -66,8 +66,8 @@ span = { Span.start_pos = { Span.line = 7; col = 1; offset = 95 }; end_pos = { Span.line = 7; col = 8; offset = 102 }; - file = "test/golden/effects.affine" } - }; + file = "test/golden/effects.affine" }; + modpath = [] }; ed_type_params = [{ Ast.tp_quantity = None; tp_name = @@ -75,8 +75,8 @@ span = { Span.start_pos = { Span.line = 7; col = 13; offset = 107 }; end_pos = { Span.line = 7; col = 14; offset = 108 }; - file = "test/golden/effects.affine" } - }; + file = "test/golden/effects.affine" }; + modpath = [] }; tp_kind = None } ]; ed_ops = @@ -85,8 +85,8 @@ span = { Span.start_pos = { Span.line = 8; col = 3; offset = 115 }; end_pos = { Span.line = 8; col = 6; offset = 118 }; - file = "test/golden/effects.affine" } - }; + file = "test/golden/effects.affine" }; + modpath = [] }; eod_params = []; eod_ret_ty = (Some (Ast.TyCon @@ -95,16 +95,16 @@ { Span.start_pos = { Span.line = 8; col = 12; offset = 124 }; end_pos = { Span.line = 8; col = 15; offset = 127 }; - file = "test/golden/effects.affine" } - })) + file = "test/golden/effects.affine" }; + modpath = [] })) }; { Ast.eod_name = { Ast.name = "put"; span = { Span.start_pos = { Span.line = 9; col = 3; offset = 132 }; end_pos = { Span.line = 9; col = 6; offset = 135 }; - file = "test/golden/effects.affine" } - }; + file = "test/golden/effects.affine" }; + modpath = [] }; eod_params = [{ Ast.p_quantity = None; p_ownership = None; p_name = @@ -112,8 +112,8 @@ span = { Span.start_pos = { Span.line = 9; col = 9; offset = 138 }; end_pos = { Span.line = 9; col = 10; offset = 139 }; - file = "test/golden/effects.affine" } - }; + file = "test/golden/effects.affine" }; + modpath = [] }; p_ty = (Ast.TyCon { Ast.name = "S"; @@ -121,12 +121,12 @@ { Span.start_pos = { Span.line = 9; col = 11; offset = 140 }; end_pos = { Span.line = 9; col = 13; offset = 142 }; - file = "test/golden/effects.affine" } - }) + file = "test/golden/effects.affine" }; + modpath = [] }) } ]; eod_ret_ty = (Some (Ast.TyTuple [])) } ] }) ] - } + } \ No newline at end of file diff --git a/test/golden/functions.expected b/test/golden/functions.expected index 7fdf28c..cfcd929 100644 --- a/test/golden/functions.expected +++ b/test/golden/functions.expected @@ -7,8 +7,8 @@ span = { Span.start_pos = { Span.line = 2; col = 1; offset = 30 }; end_pos = { Span.line = 2; col = 4; offset = 33 }; - file = "test/golden/functions.affine" } - }; + file = "test/golden/functions.affine" }; + modpath = [] }; fd_type_params = []; fd_params = [{ Ast.p_quantity = None; p_ownership = None; @@ -17,16 +17,16 @@ span = { Span.start_pos = { Span.line = 2; col = 10; offset = 39 }; end_pos = { Span.line = 2; col = 11; offset = 40 }; - file = "test/golden/functions.affine" } - }; + file = "test/golden/functions.affine" }; + modpath = [] }; p_ty = (Ast.TyCon { Ast.name = "Int"; span = { Span.start_pos = { Span.line = 2; col = 12; offset = 41 }; end_pos = { Span.line = 2; col = 14; offset = 43 }; - file = "test/golden/functions.affine" } - }) + file = "test/golden/functions.affine" }; + modpath = [] }) } ]; fd_ret_ty = @@ -36,8 +36,8 @@ { Span.start_pos = { Span.line = 2; col = 19; offset = 48 }; end_pos = { Span.line = 2; col = 22; offset = 51 }; - file = "test/golden/functions.affine" } - })); + file = "test/golden/functions.affine" }; + modpath = [] })); fd_eff = None; fd_where = []; fd_body = (Ast.FnBlock @@ -51,8 +51,8 @@ { Span.line = 2; col = 26; offset = 55 }; end_pos = { Span.line = 3; col = 3; offset = 59 }; - file = "test/golden/functions.affine" } - }), + file = "test/golden/functions.affine" }; + modpath = [] }), Ast.OpAdd, (Ast.ExprLit (Ast.LitInt (1, @@ -72,8 +72,8 @@ span = { Span.start_pos = { Span.line = 6; col = 7; offset = 74 }; end_pos = { Span.line = 6; col = 10; offset = 77 }; - file = "test/golden/functions.affine" } - }; + file = "test/golden/functions.affine" }; + modpath = [] }; fd_type_params = []; fd_params = [{ Ast.p_quantity = None; p_ownership = None; @@ -82,16 +82,16 @@ span = { Span.start_pos = { Span.line = 6; col = 14; offset = 81 }; end_pos = { Span.line = 6; col = 15; offset = 82 }; - file = "test/golden/functions.affine" } - }; + file = "test/golden/functions.affine" }; + modpath = [] }; p_ty = (Ast.TyCon { Ast.name = "Nat"; span = { Span.start_pos = { Span.line = 6; col = 16; offset = 83 }; end_pos = { Span.line = 6; col = 18; offset = 85 }; - file = "test/golden/functions.affine" } - }) + file = "test/golden/functions.affine" }; + modpath = [] }) } ]; fd_ret_ty = @@ -101,8 +101,8 @@ { Span.start_pos = { Span.line = 6; col = 23; offset = 90 }; end_pos = { Span.line = 6; col = 26; offset = 93 }; - file = "test/golden/functions.affine" } - })); + file = "test/golden/functions.affine" }; + modpath = [] })); fd_eff = None; fd_where = []; fd_body = (Ast.FnBlock @@ -114,8 +114,8 @@ { Span.start_pos = { Span.line = 6; col = 30; offset = 97 }; end_pos = { Span.line = 7; col = 3; offset = 101 }; - file = "test/golden/functions.affine" } - })) + file = "test/golden/functions.affine" }; + modpath = [] })) }) }); (Ast.TopFn @@ -125,8 +125,8 @@ span = { Span.start_pos = { Span.line = 10; col = 1; offset = 106 }; end_pos = { Span.line = 10; col = 4; offset = 109 }; - file = "test/golden/functions.affine" } - }; + file = "test/golden/functions.affine" }; + modpath = [] }; fd_type_params = [{ Ast.tp_quantity = None; tp_name = @@ -134,8 +134,8 @@ span = { Span.start_pos = { Span.line = 10; col = 11; offset = 116 }; end_pos = { Span.line = 10; col = 12; offset = 117 }; - file = "test/golden/functions.affine" } - }; + file = "test/golden/functions.affine" }; + modpath = [] }; tp_kind = None } ]; fd_params = @@ -145,8 +145,8 @@ span = { Span.start_pos = { Span.line = 10; col = 14; offset = 119 }; end_pos = { Span.line = 10; col = 15; offset = 120 }; - file = "test/golden/functions.affine" } - }; + file = "test/golden/functions.affine" }; + modpath = [] }; p_ty = (Ast.TyCon { Ast.name = "T"; @@ -154,8 +154,8 @@ { Span.start_pos = { Span.line = 10; col = 16; offset = 121 }; end_pos = { Span.line = 10; col = 18; offset = 123 }; - file = "test/golden/functions.affine" } - }) + file = "test/golden/functions.affine" }; + modpath = [] }) } ]; fd_ret_ty = @@ -165,8 +165,8 @@ { Span.start_pos = { Span.line = 10; col = 21; offset = 126 }; end_pos = { Span.line = 10; col = 24; offset = 129 }; - file = "test/golden/functions.affine" } - })); + file = "test/golden/functions.affine" }; + modpath = [] })); fd_eff = None; fd_where = []; fd_body = (Ast.FnBlock @@ -179,8 +179,8 @@ { Span.line = 10; col = 26; offset = 131 }; end_pos = { Span.line = 11; col = 3; offset = 135 }; - file = "test/golden/functions.affine" } - })) + file = "test/golden/functions.affine" }; + modpath = [] })) }) }); (Ast.TopFn @@ -190,8 +190,8 @@ span = { Span.start_pos = { Span.line = 14; col = 5; offset = 144 }; end_pos = { Span.line = 14; col = 8; offset = 147 }; - file = "test/golden/functions.affine" } - }; + file = "test/golden/functions.affine" }; + modpath = [] }; fd_type_params = []; fd_params = []; fd_ret_ty = (Some (Ast.TyTuple [])); fd_eff = None; fd_where = []; fd_body = @@ -208,4 +208,4 @@ }) }) ] - } + } \ No newline at end of file diff --git a/test/golden/hello.expected b/test/golden/hello.expected index 572310e..8cafd3c 100644 --- a/test/golden/hello.expected +++ b/test/golden/hello.expected @@ -7,8 +7,8 @@ span = { Span.start_pos = { Span.line = 2; col = 1; offset = 22 }; end_pos = { Span.line = 2; col = 4; offset = 25 }; - file = "test/golden/hello.affine" } - }; + file = "test/golden/hello.affine" }; + modpath = [] }; fd_type_params = []; fd_params = []; fd_ret_ty = (Some (Ast.TyTuple [])); fd_eff = None; fd_where = []; fd_body = @@ -24,4 +24,4 @@ }) }) ] - } + } \ No newline at end of file diff --git a/test/golden/imports.expected b/test/golden/imports.expected index a79e643..9534c87 100644 --- a/test/golden/imports.expected +++ b/test/golden/imports.expected @@ -5,14 +5,14 @@ span = { Span.start_pos = { Span.line = 2; col = 1; offset = 23 }; end_pos = { Span.line = 2; col = 5; offset = 27 }; - file = "test/golden/imports.affine" } - }; + file = "test/golden/imports.affine" }; + modpath = [] }; { Ast.name = "io"; span = { Span.start_pos = { Span.line = 2; col = 8; offset = 30 }; end_pos = { Span.line = 2; col = 9; offset = 31 }; - file = "test/golden/imports.affine" } - } + file = "test/golden/imports.affine" }; + modpath = [] } ], None)); (Ast.ImportSimple ( @@ -20,57 +20,57 @@ span = { Span.start_pos = { Span.line = 3; col = 1; offset = 35 }; end_pos = { Span.line = 3; col = 5; offset = 39 }; - file = "test/golden/imports.affine" } - }; + file = "test/golden/imports.affine" }; + modpath = [] }; { Ast.name = "collections"; span = { Span.start_pos = { Span.line = 3; col = 8; offset = 42 }; end_pos = { Span.line = 3; col = 9; offset = 43 }; - file = "test/golden/imports.affine" } - }; + file = "test/golden/imports.affine" }; + modpath = [] }; { Ast.name = "Vec"; span = { Span.start_pos = { Span.line = 3; col = 20; offset = 54 }; end_pos = { Span.line = 3; col = 21; offset = 55 }; - file = "test/golden/imports.affine" } - } + file = "test/golden/imports.affine" }; + modpath = [] } ], (Some { Ast.name = "Vector"; span = { Span.start_pos = { Span.line = 3; col = 25; offset = 59 }; end_pos = { Span.line = 3; col = 28; offset = 62 }; - file = "test/golden/imports.affine" } - }) + file = "test/golden/imports.affine" }; + modpath = [] }) )); (Ast.ImportList ( [{ Ast.name = "std"; span = { Span.start_pos = { Span.line = 4; col = 1; offset = 70 }; end_pos = { Span.line = 4; col = 5; offset = 74 }; - file = "test/golden/imports.affine" } - }; + file = "test/golden/imports.affine" }; + modpath = [] }; { Ast.name = "prelude"; span = { Span.start_pos = { Span.line = 4; col = 8; offset = 77 }; end_pos = { Span.line = 4; col = 9; offset = 78 }; - file = "test/golden/imports.affine" } - } + file = "test/golden/imports.affine" }; + modpath = [] } ], [{ Ast.ii_name = { Ast.name = "Option"; span = { Span.start_pos = { Span.line = 4; col = 18; offset = 87 }; end_pos = { Span.line = 4; col = 19; offset = 88 }; - file = "test/golden/imports.affine" } - }; + file = "test/golden/imports.affine" }; + modpath = [] }; ii_alias = None }; { Ast.ii_name = { Ast.name = "Result"; span = { Span.start_pos = { Span.line = 4; col = 25; offset = 94 }; end_pos = { Span.line = 4; col = 27; offset = 96 }; - file = "test/golden/imports.affine" } - }; + file = "test/golden/imports.affine" }; + modpath = [] }; ii_alias = None } ] )) @@ -83,8 +83,8 @@ span = { Span.start_pos = { Span.line = 6; col = 1; offset = 106 }; end_pos = { Span.line = 6; col = 4; offset = 109 }; - file = "test/golden/imports.affine" } - }; + file = "test/golden/imports.affine" }; + modpath = [] }; fd_type_params = []; fd_params = []; fd_ret_ty = (Some (Ast.TyTuple [])); fd_eff = None; fd_where = []; fd_body = @@ -100,4 +100,4 @@ }) }) ] - } + } \ No newline at end of file diff --git a/test/golden/lambda.expected b/test/golden/lambda.expected index b9551e2..7125e1a 100644 --- a/test/golden/lambda.expected +++ b/test/golden/lambda.expected @@ -7,8 +7,8 @@ span = { Span.start_pos = { Span.line = 2; col = 1; offset = 27 }; end_pos = { Span.line = 2; col = 4; offset = 30 }; - file = "test/golden/lambda.affine" } - }; + file = "test/golden/lambda.affine" }; + modpath = [] }; fd_type_params = []; fd_params = []; fd_ret_ty = (Some (Ast.TyCon @@ -17,8 +17,8 @@ { Span.start_pos = { Span.line = 2; col = 18; offset = 44 }; end_pos = { Span.line = 2; col = 21; offset = 47 }; - file = "test/golden/lambda.affine" } - })); + file = "test/golden/lambda.affine" }; + modpath = [] })); fd_eff = None; fd_where = []; fd_body = (Ast.FnBlock @@ -31,8 +31,8 @@ { Span.start_pos = { Span.line = 3; col = 3; offset = 55 }; end_pos = { Span.line = 3; col = 7; offset = 59 }; - file = "test/golden/lambda.affine" } - }); + file = "test/golden/lambda.affine" }; + modpath = [] }); sl_ty = None; sl_value = Ast.ExprLambda { @@ -44,8 +44,8 @@ { Span.start_pos = { Span.line = 3; col = 11; offset = 63 }; end_pos = { Span.line = 3; col = 12; offset = 64 }; - file = "test/golden/lambda.affine" } - }; + file = "test/golden/lambda.affine" }; + modpath = [] }; p_ty = Ast.TyHole } ]; elam_ret_ty = None; @@ -58,8 +58,8 @@ { Span.line = 3; col = 13; offset = 65 }; end_pos = { Span.line = 3; col = 15; offset = 67 }; - file = "test/golden/lambda.affine" } - }), + file = "test/golden/lambda.affine" }; + modpath = [] }), Ast.OpAdd, (Ast.ExprLit (Ast.LitInt (1, @@ -80,8 +80,8 @@ { Span.line = 3; col = 20; offset = 72 }; end_pos = { Span.line = 4; col = 3; offset = 76 }; - file = "test/golden/lambda.affine" } - }), + file = "test/golden/lambda.affine" }; + modpath = [] }), [(Ast.ExprLit (Ast.LitInt (42, { Span.start_pos = @@ -101,8 +101,8 @@ span = { Span.start_pos = { Span.line = 7; col = 1; offset = 85 }; end_pos = { Span.line = 7; col = 4; offset = 88 }; - file = "test/golden/lambda.affine" } - }; + file = "test/golden/lambda.affine" }; + modpath = [] }; fd_type_params = []; fd_params = []; fd_ret_ty = (Some (Ast.TyCon @@ -111,8 +111,8 @@ { Span.start_pos = { Span.line = 7; col = 24; offset = 108 }; end_pos = { Span.line = 7; col = 27; offset = 111 }; - file = "test/golden/lambda.affine" } - })); + file = "test/golden/lambda.affine" }; + modpath = [] })); fd_eff = None; fd_where = []; fd_body = (Ast.FnBlock @@ -125,8 +125,8 @@ { Span.start_pos = { Span.line = 8; col = 3; offset = 119 }; end_pos = { Span.line = 8; col = 7; offset = 123 }; - file = "test/golden/lambda.affine" } - }); + file = "test/golden/lambda.affine" }; + modpath = [] }); sl_ty = None; sl_value = Ast.ExprLambda { @@ -138,8 +138,8 @@ { Span.start_pos = { Span.line = 8; col = 11; offset = 127 }; end_pos = { Span.line = 8; col = 12; offset = 128 }; - file = "test/golden/lambda.affine" } - }; + file = "test/golden/lambda.affine" }; + modpath = [] }; p_ty = (Ast.TyCon { Ast.name = "Int"; @@ -148,8 +148,8 @@ { Span.line = 8; col = 13; offset = 129 }; end_pos = { Span.line = 8; col = 15; offset = 131 }; - file = "test/golden/lambda.affine" } - }) + file = "test/golden/lambda.affine" }; + modpath = [] }) } ]; elam_ret_ty = None; @@ -162,8 +162,8 @@ { Span.line = 8; col = 18; offset = 134 }; end_pos = { Span.line = 8; col = 20; offset = 136 }; - file = "test/golden/lambda.affine" } - }), + file = "test/golden/lambda.affine" }; + modpath = [] }), Ast.OpMul, (Ast.ExprLit (Ast.LitInt (2, @@ -184,8 +184,8 @@ { Span.line = 8; col = 25; offset = 141 }; end_pos = { Span.line = 9; col = 3; offset = 145 }; - file = "test/golden/lambda.affine" } - }), + file = "test/golden/lambda.affine" }; + modpath = [] }), [(Ast.ExprLit (Ast.LitInt (21, { Span.start_pos = @@ -199,4 +199,4 @@ }) }) ] - } + } \ No newline at end of file diff --git a/test/golden/literals.expected b/test/golden/literals.expected index 3fa75ec..04148df 100644 --- a/test/golden/literals.expected +++ b/test/golden/literals.expected @@ -7,8 +7,8 @@ span = { Span.start_pos = { Span.line = 2; col = 1; offset = 26 }; end_pos = { Span.line = 2; col = 4; offset = 29 }; - file = "test/golden/literals.affine" } - }; + file = "test/golden/literals.affine" }; + modpath = [] }; fd_type_params = []; fd_params = []; fd_ret_ty = (Some (Ast.TyTuple [])); fd_eff = None; fd_where = []; fd_body = @@ -22,8 +22,8 @@ { Span.start_pos = { Span.line = 3; col = 3; offset = 55 }; end_pos = { Span.line = 3; col = 7; offset = 59 }; - file = "test/golden/literals.affine" } - }); + file = "test/golden/literals.affine" }; + modpath = [] }); sl_ty = None; sl_value = (Ast.ExprLit @@ -41,8 +41,8 @@ { Span.start_pos = { Span.line = 4; col = 3; offset = 69 }; end_pos = { Span.line = 4; col = 7; offset = 73 }; - file = "test/golden/literals.affine" } - }); + file = "test/golden/literals.affine" }; + modpath = [] }); sl_ty = None; sl_value = (Ast.ExprLit @@ -60,8 +60,8 @@ { Span.start_pos = { Span.line = 5; col = 3; offset = 85 }; end_pos = { Span.line = 5; col = 7; offset = 89 }; - file = "test/golden/literals.affine" } - }); + file = "test/golden/literals.affine" }; + modpath = [] }); sl_ty = None; sl_value = (Ast.ExprLit @@ -79,8 +79,8 @@ { Span.start_pos = { Span.line = 6; col = 3; offset = 101 }; end_pos = { Span.line = 6; col = 7; offset = 105 }; - file = "test/golden/literals.affine" } - }); + file = "test/golden/literals.affine" }; + modpath = [] }); sl_ty = None; sl_value = (Ast.ExprLit @@ -98,8 +98,8 @@ { Span.start_pos = { Span.line = 7; col = 3; offset = 120 }; end_pos = { Span.line = 7; col = 7; offset = 124 }; - file = "test/golden/literals.affine" } - }); + file = "test/golden/literals.affine" }; + modpath = [] }); sl_ty = None; sl_value = (Ast.ExprLit @@ -117,8 +117,8 @@ { Span.start_pos = { Span.line = 8; col = 3; offset = 135 }; end_pos = { Span.line = 8; col = 7; offset = 139 }; - file = "test/golden/literals.affine" } - }); + file = "test/golden/literals.affine" }; + modpath = [] }); sl_ty = None; sl_value = (Ast.ExprLit @@ -131,4 +131,4 @@ blk_expr = None }) }) ] - } + } \ No newline at end of file diff --git a/test/golden/ownership.expected b/test/golden/ownership.expected index e14de0b..5a6e742 100644 --- a/test/golden/ownership.expected +++ b/test/golden/ownership.expected @@ -7,8 +7,8 @@ span = { Span.start_pos = { Span.line = 2; col = 1; offset = 28 }; end_pos = { Span.line = 2; col = 4; offset = 31 }; - file = "test/golden/ownership.affine" } - }; + file = "test/golden/ownership.affine" }; + modpath = [] }; fd_type_params = []; fd_params = [{ Ast.p_quantity = None; p_ownership = None; @@ -17,8 +17,8 @@ span = { Span.start_pos = { Span.line = 2; col = 18; offset = 45 }; end_pos = { Span.line = 2; col = 19; offset = 46 }; - file = "test/golden/ownership.affine" } - }; + file = "test/golden/ownership.affine" }; + modpath = [] }; p_ty = (Ast.TyOwn (Ast.TyCon @@ -27,8 +27,8 @@ { Span.start_pos = { Span.line = 2; col = 22; offset = 49 }; end_pos = { Span.line = 2; col = 26; offset = 53 }; - file = "test/golden/ownership.affine" } - })) + file = "test/golden/ownership.affine" }; + modpath = [] })) }; { Ast.p_quantity = None; p_ownership = None; p_name = @@ -36,8 +36,8 @@ span = { Span.start_pos = { Span.line = 2; col = 32; offset = 59 }; end_pos = { Span.line = 2; col = 34; offset = 61 }; - file = "test/golden/ownership.affine" } - }; + file = "test/golden/ownership.affine" }; + modpath = [] }; p_ty = (Ast.TyRef (Ast.TyCon @@ -46,8 +46,8 @@ { Span.start_pos = { Span.line = 2; col = 37; offset = 64 }; end_pos = { Span.line = 2; col = 41; offset = 68 }; - file = "test/golden/ownership.affine" } - })) + file = "test/golden/ownership.affine" }; + modpath = [] })) }; { Ast.p_quantity = None; p_ownership = None; p_name = @@ -55,8 +55,8 @@ span = { Span.start_pos = { Span.line = 2; col = 44; offset = 71 }; end_pos = { Span.line = 2; col = 46; offset = 73 }; - file = "test/golden/ownership.affine" } - }; + file = "test/golden/ownership.affine" }; + modpath = [] }; p_ty = (Ast.TyMut (Ast.TyCon @@ -65,8 +65,8 @@ { Span.start_pos = { Span.line = 2; col = 49; offset = 76 }; end_pos = { Span.line = 2; col = 53; offset = 80 }; - file = "test/golden/ownership.affine" } - })) + file = "test/golden/ownership.affine" }; + modpath = [] })) } ]; fd_ret_ty = (Some (Ast.TyTuple [])); fd_eff = None; fd_where = []; @@ -89,8 +89,8 @@ span = { Span.start_pos = { Span.line = 6; col = 1; offset = 102 }; end_pos = { Span.line = 6; col = 8; offset = 109 }; - file = "test/golden/ownership.affine" } - }; + file = "test/golden/ownership.affine" }; + modpath = [] }; td_type_params = []; td_body = (Ast.TyStruct @@ -100,8 +100,8 @@ span = { Span.start_pos = { Span.line = 6; col = 13; offset = 114 }; end_pos = { Span.line = 7; col = 3; offset = 118 }; - file = "test/golden/ownership.affine" } - }; + file = "test/golden/ownership.affine" }; + modpath = [] }; sf_ty = (Ast.TyOwn (Ast.TyCon @@ -110,10 +110,10 @@ { Span.start_pos = { Span.line = 7; col = 7; offset = 122 }; end_pos = { Span.line = 7; col = 11; offset = 126 }; - file = "test/golden/ownership.affine" } - })) + file = "test/golden/ownership.affine" }; + modpath = [] })) } ]) }) ] - } + } \ No newline at end of file diff --git a/test/golden/patterns.expected b/test/golden/patterns.expected index 37e2fe3..71a2eeb 100644 --- a/test/golden/patterns.expected +++ b/test/golden/patterns.expected @@ -7,8 +7,8 @@ span = { Span.start_pos = { Span.line = 2; col = 1; offset = 25 }; end_pos = { Span.line = 2; col = 4; offset = 28 }; - file = "test/golden/patterns.affine" } - }; + file = "test/golden/patterns.affine" }; + modpath = [] }; fd_type_params = []; fd_params = []; fd_ret_ty = (Some (Ast.TyCon @@ -17,8 +17,8 @@ { Span.start_pos = { Span.line = 2; col = 17; offset = 41 }; end_pos = { Span.line = 2; col = 20; offset = 44 }; - file = "test/golden/patterns.affine" } - })); + file = "test/golden/patterns.affine" }; + modpath = [] })); fd_eff = None; fd_where = []; fd_body = (Ast.FnBlock @@ -32,8 +32,8 @@ { Span.start_pos = { Span.line = 3; col = 3; offset = 52 }; end_pos = { Span.line = 3; col = 9; offset = 58 }; - file = "test/golden/patterns.affine" } - }); + file = "test/golden/patterns.affine" }; + modpath = [] }); em_arms = [{ Ast.ma_pat = (Ast.PatLit @@ -63,8 +63,8 @@ { Span.line = 4; col = 11; offset = 72 }; end_pos = { Span.line = 5; col = 5; offset = 78 }; - file = "test/golden/patterns.affine" } - }); + file = "test/golden/patterns.affine" }; + modpath = [] }); ma_guard = None; ma_body = (Ast.ExprBinary ( @@ -75,8 +75,8 @@ { Span.line = 5; col = 7; offset = 80 }; end_pos = { Span.line = 5; col = 10; offset = 83 }; - file = "test/golden/patterns.affine" } - }), + file = "test/golden/patterns.affine" }; + modpath = [] }), Ast.OpAdd, (Ast.ExprLit (Ast.LitInt (1, @@ -98,8 +98,8 @@ span = { Span.start_pos = { Span.line = 9; col = 1; offset = 96 }; end_pos = { Span.line = 9; col = 4; offset = 99 }; - file = "test/golden/patterns.affine" } - }; + file = "test/golden/patterns.affine" }; + modpath = [] }; fd_type_params = []; fd_params = []; fd_ret_ty = (Some (Ast.TyCon @@ -108,8 +108,8 @@ { Span.start_pos = { Span.line = 9; col = 23; offset = 118 }; end_pos = { Span.line = 9; col = 26; offset = 121 }; - file = "test/golden/patterns.affine" } - })); + file = "test/golden/patterns.affine" }; + modpath = [] })); fd_eff = None; fd_where = []; fd_body = (Ast.FnBlock @@ -124,8 +124,8 @@ { Span.line = 10; col = 3; offset = 129 }; end_pos = { Span.line = 10; col = 9; offset = 135 }; - file = "test/golden/patterns.affine" } - }); + file = "test/golden/patterns.affine" }; + modpath = [] }); em_arms = [{ Ast.ma_pat = (Ast.PatTuple @@ -136,8 +136,8 @@ { Span.line = 11; col = 5; offset = 146 }; end_pos = { Span.line = 11; col = 6; offset = 147 }; - file = "test/golden/patterns.affine" } - }); + file = "test/golden/patterns.affine" }; + modpath = [] }); (Ast.PatVar { Ast.name = "b"; span = @@ -147,8 +147,8 @@ end_pos = { Span.line = 11; col = 9; offset = 150 }; - file = "test/golden/patterns.affine" } - }) + file = "test/golden/patterns.affine" }; + modpath = [] }) ]); ma_guard = None; ma_body = @@ -160,8 +160,8 @@ { Span.line = 11; col = 12; offset = 153 }; end_pos = { Span.line = 11; col = 15; offset = 156 }; - file = "test/golden/patterns.affine" } - }), + file = "test/golden/patterns.affine" }; + modpath = [] }), Ast.OpAdd, (Ast.ExprVar { Ast.name = "b"; @@ -170,8 +170,8 @@ { Span.line = 11; col = 17; offset = 158 }; end_pos = { Span.line = 11; col = 19; offset = 160 }; - file = "test/golden/patterns.affine" } - }) + file = "test/golden/patterns.affine" }; + modpath = [] }) )) } ]}) @@ -184,8 +184,8 @@ span = { Span.start_pos = { Span.line = 15; col = 1; offset = 169 }; end_pos = { Span.line = 15; col = 4; offset = 172 }; - file = "test/golden/patterns.affine" } - }; + file = "test/golden/patterns.affine" }; + modpath = [] }; fd_type_params = []; fd_params = []; fd_ret_ty = (Some (Ast.TyCon @@ -194,8 +194,8 @@ { Span.start_pos = { Span.line = 15; col = 23; offset = 191 }; end_pos = { Span.line = 15; col = 26; offset = 194 }; - file = "test/golden/patterns.affine" } - })); + file = "test/golden/patterns.affine" }; + modpath = [] })); fd_eff = None; fd_where = []; fd_body = (Ast.FnBlock @@ -210,8 +210,8 @@ { Span.line = 16; col = 3; offset = 202 }; end_pos = { Span.line = 16; col = 9; offset = 208 }; - file = "test/golden/patterns.affine" } - }); + file = "test/golden/patterns.affine" }; + modpath = [] }); em_arms = [{ Ast.ma_pat = (Ast.PatCon ( @@ -221,8 +221,8 @@ { Span.line = 16; col = 13; offset = 212 }; end_pos = { Span.line = 17; col = 5; offset = 218 }; - file = "test/golden/patterns.affine" } - }, + file = "test/golden/patterns.affine" }; + modpath = [] }, [(Ast.PatVar { Ast.name = "x"; span = @@ -231,8 +231,8 @@ end_pos = { Span.line = 17; col = 10; offset = 223 }; - file = "test/golden/patterns.affine" } - }) + file = "test/golden/patterns.affine" }; + modpath = [] }) ] )); ma_guard = None; @@ -244,8 +244,8 @@ { Span.line = 17; col = 13; offset = 226 }; end_pos = { Span.line = 17; col = 16; offset = 229 }; - file = "test/golden/patterns.affine" } - }) + file = "test/golden/patterns.affine" }; + modpath = [] }) }; { Ast.ma_pat = (Ast.PatCon ( @@ -255,8 +255,8 @@ { Span.line = 17; col = 17; offset = 230 }; end_pos = { Span.line = 18; col = 5; offset = 236 }; - file = "test/golden/patterns.affine" } - }, + file = "test/golden/patterns.affine" }; + modpath = [] }, [])); ma_guard = None; ma_body = @@ -273,4 +273,4 @@ }) }) ] - } + } \ No newline at end of file diff --git a/test/golden/rows.expected b/test/golden/rows.expected index 3011684..dd5a6d2 100644 --- a/test/golden/rows.expected +++ b/test/golden/rows.expected @@ -7,8 +7,8 @@ span = { Span.start_pos = { Span.line = 2; col = 1; offset = 25 }; end_pos = { Span.line = 2; col = 4; offset = 28 }; - file = "test/golden/rows.affine" } - }; + file = "test/golden/rows.affine" }; + modpath = [] }; fd_type_params = [{ Ast.tp_quantity = None; tp_name = @@ -16,8 +16,8 @@ span = { Span.start_pos = { Span.line = 2; col = 8; offset = 32 }; end_pos = { Span.line = 2; col = 9; offset = 33 }; - file = "test/golden/rows.affine" } - }; + file = "test/golden/rows.affine" }; + modpath = [] }; tp_kind = (Some Ast.KRow) } ]; fd_params = @@ -27,8 +27,8 @@ span = { Span.start_pos = { Span.line = 2; col = 13; offset = 37 }; end_pos = { Span.line = 2; col = 14; offset = 38 }; - file = "test/golden/rows.affine" } - }; + file = "test/golden/rows.affine" }; + modpath = [] }; p_ty = (Ast.TyRecord ( [{ Ast.rf_name = @@ -37,8 +37,8 @@ { Span.start_pos = { Span.line = 2; col = 17; offset = 41 }; end_pos = { Span.line = 2; col = 18; offset = 42 }; - file = "test/golden/rows.affine" } - }; + file = "test/golden/rows.affine" }; + modpath = [] }; rf_ty = (Ast.TyCon { Ast.name = "Int"; @@ -46,8 +46,8 @@ { Span.start_pos = { Span.line = 2; col = 19; offset = 43 }; end_pos = { Span.line = 2; col = 21; offset = 45 }; - file = "test/golden/rows.affine" } - }) + file = "test/golden/rows.affine" }; + modpath = [] }) } ], (Some { Ast.name = "r"; @@ -55,8 +55,8 @@ { Span.start_pos = { Span.line = 2; col = 24; offset = 48 }; end_pos = { Span.line = 2; col = 26; offset = 50 }; - file = "test/golden/rows.affine" } - }) + file = "test/golden/rows.affine" }; + modpath = [] }) )) } ]; @@ -67,8 +67,8 @@ { Span.start_pos = { Span.line = 2; col = 32; offset = 56 }; end_pos = { Span.line = 2; col = 35; offset = 59 }; - file = "test/golden/rows.affine" } - })); + file = "test/golden/rows.affine" }; + modpath = [] })); fd_eff = None; fd_where = []; fd_body = (Ast.FnBlock @@ -82,15 +82,15 @@ { Span.line = 2; col = 39; offset = 63 }; end_pos = { Span.line = 3; col = 3; offset = 67 }; - file = "test/golden/rows.affine" } - }), + file = "test/golden/rows.affine" }; + modpath = [] }), { Ast.name = "x"; span = { Span.start_pos = { Span.line = 3; col = 4; offset = 68 }; end_pos = { Span.line = 3; col = 5; offset = 69 }; - file = "test/golden/rows.affine" } - } + file = "test/golden/rows.affine" }; + modpath = [] } ))) }) }); @@ -101,8 +101,8 @@ span = { Span.start_pos = { Span.line = 6; col = 1; offset = 74 }; end_pos = { Span.line = 6; col = 4; offset = 77 }; - file = "test/golden/rows.affine" } - }; + file = "test/golden/rows.affine" }; + modpath = [] }; fd_type_params = [{ Ast.tp_quantity = None; tp_name = @@ -110,8 +110,8 @@ span = { Span.start_pos = { Span.line = 6; col = 8; offset = 81 }; end_pos = { Span.line = 6; col = 9; offset = 82 }; - file = "test/golden/rows.affine" } - }; + file = "test/golden/rows.affine" }; + modpath = [] }; tp_kind = (Some Ast.KRow) } ]; fd_params = @@ -121,8 +121,8 @@ span = { Span.start_pos = { Span.line = 6; col = 13; offset = 86 }; end_pos = { Span.line = 6; col = 14; offset = 87 }; - file = "test/golden/rows.affine" } - }; + file = "test/golden/rows.affine" }; + modpath = [] }; p_ty = (Ast.TyRecord ([], (Some { Ast.name = "r"; @@ -130,8 +130,8 @@ { Span.start_pos = { Span.line = 6; col = 17; offset = 90 }; end_pos = { Span.line = 6; col = 18; offset = 91 }; - file = "test/golden/rows.affine" } - }) + file = "test/golden/rows.affine" }; + modpath = [] }) )) } ]; @@ -143,8 +143,8 @@ { Span.start_pos = { Span.line = 6; col = 27; offset = 100 }; end_pos = { Span.line = 6; col = 28; offset = 101 }; - file = "test/golden/rows.affine" } - }; + file = "test/golden/rows.affine" }; + modpath = [] }; rf_ty = (Ast.TyCon { Ast.name = "Int"; @@ -153,8 +153,8 @@ { Span.line = 6; col = 29; offset = 102 }; end_pos = { Span.line = 6; col = 31; offset = 104 }; - file = "test/golden/rows.affine" } - }) + file = "test/golden/rows.affine" }; + modpath = [] }) } ], (Some { Ast.name = "r"; @@ -163,8 +163,8 @@ { Span.line = 6; col = 34; offset = 107 }; end_pos = { Span.line = 6; col = 36; offset = 109 }; - file = "test/golden/rows.affine" } - }) + file = "test/golden/rows.affine" }; + modpath = [] }) ))); fd_eff = None; fd_where = []; fd_body = @@ -178,16 +178,16 @@ { Span.start_pos = { Span.line = 7; col = 3; offset = 118 }; end_pos = - { Span.line = 7; col = 4; offset = 119 }; - file = "test/golden/rows.affine" } - }, + { Span.line = 7; col = 5; offset = 120 }; + file = "test/golden/rows.affine" }; + modpath = [] }, (Some (Ast.ExprLit (Ast.LitInt (0, { Span.start_pos = - { Span.line = 7; col = 5; offset = 120 + { Span.line = 7; col = 6; offset = 121 }; end_pos = - { Span.line = 7; col = 7; offset = 122 + { Span.line = 7; col = 8; offset = 123 }; file = "test/golden/rows.affine" } ))))) @@ -197,12 +197,12 @@ { Ast.name = "p"; span = { Span.start_pos = - { Span.line = 7; col = 8; offset = 123 }; + { Span.line = 7; col = 9; offset = 124 }; end_pos = - { Span.line = 7; col = 10; offset = 125 }; - file = "test/golden/rows.affine" } - }))}) + { Span.line = 7; col = 11; offset = 126 }; + file = "test/golden/rows.affine" }; + modpath = [] }))}) }) }) ] - } + } \ No newline at end of file diff --git a/test/golden/traits.expected b/test/golden/traits.expected index d6260e5..a0e4c6e 100644 --- a/test/golden/traits.expected +++ b/test/golden/traits.expected @@ -7,8 +7,8 @@ span = { Span.start_pos = { Span.line = 2; col = 1; offset = 27 }; end_pos = { Span.line = 2; col = 7; offset = 33 }; - file = "test/golden/traits.affine" } - }; + file = "test/golden/traits.affine" }; + modpath = [] }; trd_type_params = []; trd_super = []; trd_items = [(Ast.TraitFn @@ -18,8 +18,8 @@ span = { Span.start_pos = { Span.line = 3; col = 3; offset = 40 }; end_pos = { Span.line = 3; col = 6; offset = 43 }; - file = "test/golden/traits.affine" } - }; + file = "test/golden/traits.affine" }; + modpath = [] }; fs_type_params = []; fs_params = [{ Ast.p_quantity = None; p_ownership = (Some Ast.Ref); @@ -28,8 +28,8 @@ span = { Span.start_pos = { Span.line = 3; col = 8; offset = 45 }; end_pos = { Span.line = 3; col = 13; offset = 50 }; - file = "test/golden/traits.affine" } - }; + file = "test/golden/traits.affine" }; + modpath = [] }; p_ty = (Ast.TyCon { Ast.name = "Self"; @@ -37,8 +37,8 @@ { Span.start_pos = { Span.line = 3; col = 8; offset = 45 }; end_pos = { Span.line = 3; col = 13; offset = 50 }; - file = "test/golden/traits.affine" } - }) + file = "test/golden/traits.affine" }; + modpath = [] }) }; { Ast.p_quantity = None; p_ownership = (Some Ast.Ref); p_name = @@ -47,8 +47,8 @@ { Span.start_pos = { Span.line = 3; col = 19; offset = 56 }; end_pos = { Span.line = 3; col = 23; offset = 60 }; - file = "test/golden/traits.affine" } - }; + file = "test/golden/traits.affine" }; + modpath = [] }; p_ty = (Ast.TyCon { Ast.name = "Self"; @@ -56,8 +56,8 @@ { Span.start_pos = { Span.line = 3; col = 28; offset = 65 }; end_pos = { Span.line = 3; col = 30; offset = 67 }; - file = "test/golden/traits.affine" } - }) + file = "test/golden/traits.affine" }; + modpath = [] }) } ]; fs_ret_ty = @@ -67,8 +67,8 @@ { Span.start_pos = { Span.line = 3; col = 36; offset = 73 }; end_pos = { Span.line = 3; col = 39; offset = 76 }; - file = "test/golden/traits.affine" } - })); + file = "test/golden/traits.affine" }; + modpath = [] })); fs_eff = None }) ] }); @@ -79,8 +79,8 @@ span = { Span.start_pos = { Span.line = 6; col = 1; offset = 85 }; end_pos = { Span.line = 6; col = 7; offset = 91 }; - file = "test/golden/traits.affine" } - }; + file = "test/golden/traits.affine" }; + modpath = [] }; trd_type_params = []; trd_super = [{ Ast.tb_name = @@ -88,8 +88,8 @@ span = { Span.start_pos = { Span.line = 6; col = 10; offset = 94 }; end_pos = { Span.line = 6; col = 12; offset = 96 }; - file = "test/golden/traits.affine" } - }; + file = "test/golden/traits.affine" }; + modpath = [] }; tb_args = [] } ]; trd_items = @@ -100,8 +100,8 @@ span = { Span.start_pos = { Span.line = 7; col = 3; offset = 103 }; end_pos = { Span.line = 7; col = 6; offset = 106 }; - file = "test/golden/traits.affine" } - }; + file = "test/golden/traits.affine" }; + modpath = [] }; fs_type_params = []; fs_params = [{ Ast.p_quantity = None; p_ownership = (Some Ast.Ref); @@ -111,8 +111,8 @@ { Span.start_pos = { Span.line = 7; col = 9; offset = 109 }; end_pos = { Span.line = 7; col = 14; offset = 114 }; - file = "test/golden/traits.affine" } - }; + file = "test/golden/traits.affine" }; + modpath = [] }; p_ty = (Ast.TyCon { Ast.name = "Self"; @@ -120,8 +120,8 @@ { Span.start_pos = { Span.line = 7; col = 9; offset = 109 }; end_pos = { Span.line = 7; col = 14; offset = 114 }; - file = "test/golden/traits.affine" } - }) + file = "test/golden/traits.affine" }; + modpath = [] }) }; { Ast.p_quantity = None; p_ownership = (Some Ast.Ref); p_name = @@ -130,8 +130,8 @@ { Span.start_pos = { Span.line = 7; col = 20; offset = 120 }; end_pos = { Span.line = 7; col = 24; offset = 124 }; - file = "test/golden/traits.affine" } - }; + file = "test/golden/traits.affine" }; + modpath = [] }; p_ty = (Ast.TyCon { Ast.name = "Self"; @@ -139,8 +139,8 @@ { Span.start_pos = { Span.line = 7; col = 29; offset = 129 }; end_pos = { Span.line = 7; col = 31; offset = 131 }; - file = "test/golden/traits.affine" } - }) + file = "test/golden/traits.affine" }; + modpath = [] }) } ]; fs_ret_ty = @@ -151,8 +151,8 @@ { Span.line = 7; col = 37; offset = 137 }; end_pos = { Span.line = 7; col = 40; offset = 140 }; - file = "test/golden/traits.affine" } - })); + file = "test/golden/traits.affine" }; + modpath = [] })); fs_eff = None }) ] }); @@ -165,8 +165,8 @@ { Span.start_pos = { Span.line = 10; col = 1; offset = 153 }; end_pos = { Span.line = 10; col = 6; offset = 158 }; - file = "test/golden/traits.affine" } - }; + file = "test/golden/traits.affine" }; + modpath = [] }; tr_args = [] }); ib_self_ty = (Ast.TyCon @@ -174,8 +174,8 @@ span = { Span.start_pos = { Span.line = 10; col = 9; offset = 161 }; end_pos = { Span.line = 10; col = 13; offset = 165 }; - file = "test/golden/traits.affine" } - }); + file = "test/golden/traits.affine" }; + modpath = [] }); ib_where = []; ib_items = [(Ast.ImplFn @@ -185,8 +185,8 @@ span = { Span.start_pos = { Span.line = 11; col = 3; offset = 173 }; end_pos = { Span.line = 11; col = 6; offset = 176 }; - file = "test/golden/traits.affine" } - }; + file = "test/golden/traits.affine" }; + modpath = [] }; fd_type_params = []; fd_params = [{ Ast.p_quantity = None; p_ownership = (Some Ast.Ref); @@ -196,8 +196,8 @@ { Span.start_pos = { Span.line = 11; col = 8; offset = 178 }; end_pos = { Span.line = 11; col = 13; offset = 183 }; - file = "test/golden/traits.affine" } - }; + file = "test/golden/traits.affine" }; + modpath = [] }; p_ty = (Ast.TyCon { Ast.name = "Self"; @@ -205,8 +205,8 @@ { Span.start_pos = { Span.line = 11; col = 8; offset = 178 }; end_pos = { Span.line = 11; col = 13; offset = 183 }; - file = "test/golden/traits.affine" } - }) + file = "test/golden/traits.affine" }; + modpath = [] }) }; { Ast.p_quantity = None; p_ownership = (Some Ast.Ref); p_name = @@ -215,8 +215,8 @@ { Span.start_pos = { Span.line = 11; col = 19; offset = 189 }; end_pos = { Span.line = 11; col = 23; offset = 193 }; - file = "test/golden/traits.affine" } - }; + file = "test/golden/traits.affine" }; + modpath = [] }; p_ty = (Ast.TyCon { Ast.name = "Int"; @@ -225,8 +225,8 @@ { Span.line = 11; col = 28; offset = 198 }; end_pos = { Span.line = 11; col = 30; offset = 200 }; - file = "test/golden/traits.affine" } - }) + file = "test/golden/traits.affine" }; + modpath = [] }) } ]; fd_ret_ty = @@ -237,8 +237,8 @@ { Span.line = 11; col = 35; offset = 205 }; end_pos = { Span.line = 11; col = 38; offset = 208 }; - file = "test/golden/traits.affine" } - })); + file = "test/golden/traits.affine" }; + modpath = [] })); fd_eff = None; fd_where = []; fd_body = (Ast.FnBlock @@ -257,4 +257,4 @@ ] }) ] - } + } \ No newline at end of file diff --git a/test/golden/types.expected b/test/golden/types.expected index 72980a3..5de3b77 100644 --- a/test/golden/types.expected +++ b/test/golden/types.expected @@ -7,8 +7,8 @@ span = { Span.start_pos = { Span.line = 2; col = 1; offset = 26 }; end_pos = { Span.line = 2; col = 6; offset = 31 }; - file = "test/golden/types.affine" } - }; + file = "test/golden/types.affine" }; + modpath = [] }; td_type_params = []; td_body = (Ast.TyAlias @@ -17,8 +17,8 @@ span = { Span.start_pos = { Span.line = 2; col = 12; offset = 37 }; end_pos = { Span.line = 2; col = 14; offset = 39 }; - file = "test/golden/types.affine" } - })) + file = "test/golden/types.affine" }; + modpath = [] })) }); (Ast.TopType { Ast.td_vis = Ast.Private; @@ -27,8 +27,8 @@ span = { Span.start_pos = { Span.line = 4; col = 1; offset = 45 }; end_pos = { Span.line = 4; col = 8; offset = 52 }; - file = "test/golden/types.affine" } - }; + file = "test/golden/types.affine" }; + modpath = [] }; td_type_params = []; td_body = (Ast.TyStruct @@ -38,8 +38,8 @@ span = { Span.start_pos = { Span.line = 4; col = 14; offset = 58 }; end_pos = { Span.line = 5; col = 3; offset = 62 }; - file = "test/golden/types.affine" } - }; + file = "test/golden/types.affine" }; + modpath = [] }; sf_ty = (Ast.TyCon { Ast.name = "Int"; @@ -47,8 +47,8 @@ { Span.start_pos = { Span.line = 5; col = 4; offset = 63 }; end_pos = { Span.line = 5; col = 6; offset = 65 }; - file = "test/golden/types.affine" } - }) + file = "test/golden/types.affine" }; + modpath = [] }) }; { Ast.sf_vis = Ast.Private; sf_name = @@ -56,8 +56,8 @@ span = { Span.start_pos = { Span.line = 5; col = 9; offset = 68 }; end_pos = { Span.line = 6; col = 3; offset = 72 }; - file = "test/golden/types.affine" } - }; + file = "test/golden/types.affine" }; + modpath = [] }; sf_ty = (Ast.TyCon { Ast.name = "Int"; @@ -65,8 +65,8 @@ { Span.start_pos = { Span.line = 6; col = 4; offset = 73 }; end_pos = { Span.line = 6; col = 6; offset = 75 }; - file = "test/golden/types.affine" } - }) + file = "test/golden/types.affine" }; + modpath = [] }) } ]) }); @@ -77,8 +77,8 @@ span = { Span.start_pos = { Span.line = 9; col = 1; offset = 82 }; end_pos = { Span.line = 9; col = 6; offset = 87 }; - file = "test/golden/types.affine" } - }; + file = "test/golden/types.affine" }; + modpath = [] }; td_type_params = [{ Ast.tp_quantity = None; tp_name = @@ -86,8 +86,8 @@ span = { Span.start_pos = { Span.line = 9; col = 12; offset = 93 }; end_pos = { Span.line = 9; col = 13; offset = 94 }; - file = "test/golden/types.affine" } - }; + file = "test/golden/types.affine" }; + modpath = [] }; tp_kind = None } ]; td_body = @@ -97,8 +97,8 @@ span = { Span.start_pos = { Span.line = 9; col = 16; offset = 97 }; end_pos = { Span.line = 10; col = 3; offset = 101 }; - file = "test/golden/types.affine" } - }; + file = "test/golden/types.affine" }; + modpath = [] }; vd_fields = []; vd_ret_ty = None }; { Ast.vd_name = { Ast.name = "Some"; @@ -106,8 +106,8 @@ { Span.start_pos = { Span.line = 10; col = 7; offset = 105 }; end_pos = { Span.line = 11; col = 3; offset = 109 }; - file = "test/golden/types.affine" } - }; + file = "test/golden/types.affine" }; + modpath = [] }; vd_fields = [(Ast.TyCon { Ast.name = "T"; @@ -115,11 +115,11 @@ { Span.start_pos = { Span.line = 11; col = 7; offset = 113 }; end_pos = { Span.line = 11; col = 8; offset = 114 }; - file = "test/golden/types.affine" } - }) + file = "test/golden/types.affine" }; + modpath = [] }) ]; vd_ret_ty = None } ]) }) ] - } + } \ No newline at end of file diff --git a/test/test_e2e.ml b/test/test_e2e.ml index 44404c2..505ebab 100644 --- a/test/test_e2e.ml +++ b/test/test_e2e.ml @@ -3468,6 +3468,92 @@ let type_syntax_sugar_tests = [ Alcotest.test_case "(A, B) without arrow remains tuple" `Quick test_tuple_type_still_works; ] +(* ---- issue #228 / ADR-014: sound module-qualified type & effect paths ---- + + These exercise the FULL sound pipeline exactly as `main.exe check` + does: parse -> resolve_program_with_loader (which builds the + loader-backed module-scoped validator) -> check_program with that + validator threaded in. The conformance fixtures live under + tests/conformance/qualified-paths/{valid,invalid}/. The loader + search-path includes the valid/ dir so the `effmod` support module + (a public effect) is found by sound module-scoped lookup. *) + +let qpath_dir sub = + let cands = [ + "../tests/conformance/qualified-paths/" ^ sub; (* _build/default/test (dune source_tree) *) + "tests/conformance/qualified-paths/" ^ sub; (* project root *) + "../../../tests/conformance/qualified-paths/" ^ sub; (* fallback *) + ] in + match List.find_opt Sys.file_exists cands with + | Some p -> p + | None -> Alcotest.failf "qualified-paths/%s not found (tried: %s)" + sub (String.concat ", " cands) + +(* Run the sound frontend the way main.exe does: the validator built by + resolve_program_with_loader is threaded into check_program. *) +let qpath_run file = + let valid_dir = qpath_dir "valid" in + let loader = Module_loader.create { + Module_loader.stdlib_path = "stdlib"; + search_paths = [valid_dir; "../../../stdlib"; "../../../../stdlib"]; + current_dir = Filename.dirname file; + } in + match parse_fixture file with + | Error e -> Error (Printf.sprintf "parse error: %s" e) + | Ok prog -> + match Resolve.resolve_program_with_loader prog loader with + | Error (e, _) -> Error (Printf.sprintf "resolve error: %s" + (Resolve.show_resolve_error e)) + | Ok (resolve_ctx, type_ctx) -> + match + Typecheck.check_program + ~import_types:type_ctx.Typecheck.name_types + ?qualified_member_check:type_ctx.Typecheck.qualified_member_check + resolve_ctx.symbols prog + with + | Ok _ -> Ok () + | Error e -> Error (Typecheck.format_type_error e) + +let test_qpath_valid_type () = + match qpath_run (Filename.concat (qpath_dir "valid") "qualified_type.affine") with + | Ok () -> () + | Error e -> Alcotest.failf "expected qualified type to resolve, got: %s" e + +let test_qpath_valid_effect () = + match qpath_run (Filename.concat (qpath_dir "valid") "qualified_effect.affine") with + | Ok () -> () + | Error e -> Alcotest.failf "expected qualified effect to resolve, got: %s" e + +let expect_err ~contains file = + match qpath_run file with + | Ok () -> Alcotest.failf "%s: expected an error, but it passed" file + | Error msg -> + let re = Str.regexp_string contains in + (try ignore (Str.search_forward re msg 0) + with Not_found -> + Alcotest.failf "%s: error did not mention %S; got: %s" + file contains msg) + +let test_qpath_unknown_module () = + expect_err ~contains:"Unknown module" + (Filename.concat (qpath_dir "invalid") "unknown_module.affine") + +let test_qpath_private_member () = + expect_err ~contains:"is private" + (Filename.concat (qpath_dir "invalid") "private_member.affine") + +let test_qpath_wrong_member () = + expect_err ~contains:"has no member" + (Filename.concat (qpath_dir "invalid") "wrong_member.affine") + +let qualified_path_tests = [ + Alcotest.test_case "valid: prelude.Option[Int]/ resolves (#228)" `Quick test_qpath_valid_type; + Alcotest.test_case "valid: effmod.Logging effect resolves (#228)" `Quick test_qpath_valid_effect; + Alcotest.test_case "invalid: unknown module -> resolution error (#228)" `Quick test_qpath_unknown_module; + Alcotest.test_case "invalid: private member rejected (#228)" `Quick test_qpath_private_member; + Alcotest.test_case "invalid: absent member rejected (#228)" `Quick test_qpath_wrong_member; +] + (* ---- PatCon sub-pattern destructuring under WasmGC ---- `match Mk(7, 99) { Mk(a, b) => a }` correctly extracts the first payload @@ -3576,4 +3662,5 @@ let tests = ("E2E Array Type Sugar", array_type_tests); ("E2E WasmGC PatCon Destructure", wasm_gc_patcon_tests); ("E2E Type Syntax Sugar", type_syntax_sugar_tests); + ("E2E Qualified Paths (#228)", qualified_path_tests); ] diff --git a/tests/conformance/qualified-paths/invalid/private_member.affine b/tests/conformance/qualified-paths/invalid/private_member.affine new file mode 100644 index 0000000..f088c6c --- /dev/null +++ b/tests/conformance/qualified-paths/invalid/private_member.affine @@ -0,0 +1,15 @@ +// SPDX-License-Identifier: MPL-2.0 +// issue #228 / ADR-014 conformance (NEGATIVE): the named module exists +// but the member is NOT public. Sound module-scoped lookup must +// reject a private member (visibility is enforced *inside* the named +// module, not via flat scope). +// +// `effects` is a real stdlib module; it declares `effect IO;` WITHOUT +// `pub`, so a qualified path to it is a VisibilityError. +// Expected: type error "Member 'IO' of module 'effects' is private +// ... (ADR-014)". +module ConformancePrivateMember; + +pub fn f(x: Int) -> Int / effects.IO { + x +} diff --git a/tests/conformance/qualified-paths/invalid/unknown_module.affine b/tests/conformance/qualified-paths/invalid/unknown_module.affine new file mode 100644 index 0000000..caa1a1f --- /dev/null +++ b/tests/conformance/qualified-paths/invalid/unknown_module.affine @@ -0,0 +1,10 @@ +// SPDX-License-Identifier: MPL-2.0 +// issue #228 / ADR-014 conformance (NEGATIVE): an unknown module in a +// qualified type path must be a *resolution* error (sound +// module-scoped lookup), NOT a parse error and NOT silently accepted. +// Expected: type error "Unknown module 'Nope' ... (ADR-014)". +module ConformanceUnknownModule; + +pub fn f(x: Int) -> Nope.Bar[Int] { + x +} diff --git a/tests/conformance/qualified-paths/invalid/wrong_member.affine b/tests/conformance/qualified-paths/invalid/wrong_member.affine new file mode 100644 index 0000000..7f815a7 --- /dev/null +++ b/tests/conformance/qualified-paths/invalid/wrong_member.affine @@ -0,0 +1,19 @@ +// SPDX-License-Identifier: MPL-2.0 +// issue #228 / ADR-014 conformance (NEGATIVE): the module exists and +// is public, but it has no such member. Sound module-scoped lookup +// must reject a member that is absent from *that* module's resolved +// public symbols (it must NOT fall back to flat current scope, even +// though `Option` IS imported below — soundness comes from looking +// the member up inside the named module). +// +// `prelude` is real and `Option` is public there, but `prelude` has +// no member `DefinitelyNotHere`. +// Expected: type error "Module 'prelude' has no member +// 'DefinitelyNotHere' ... (ADR-014)". +module ConformanceWrongMember; + +use prelude::{Option, Some, None}; + +pub fn f(x: Int) -> prelude.DefinitelyNotHere { + x +} diff --git a/tests/conformance/qualified-paths/valid/effmod.affine b/tests/conformance/qualified-paths/valid/effmod.affine new file mode 100644 index 0000000..c155dd4 --- /dev/null +++ b/tests/conformance/qualified-paths/valid/effmod.affine @@ -0,0 +1,7 @@ +// SPDX-License-Identifier: MPL-2.0 +// issue #228 / ADR-014 conformance support module: declares a PUBLIC +// effect so the sibling fixture can name it through a sound qualified +// effect path. Loaded via the module loader's search path. +module effmod; + +pub effect Logging; diff --git a/tests/conformance/qualified-paths/valid/qualified_effect.affine b/tests/conformance/qualified-paths/valid/qualified_effect.affine new file mode 100644 index 0000000..32938cf --- /dev/null +++ b/tests/conformance/qualified-paths/valid/qualified_effect.affine @@ -0,0 +1,25 @@ +// SPDX-License-Identifier: MPL-2.0 +// issue #228 / ADR-014 conformance: a module-qualified EFFECT path +// that resolves by sound module-scoped member lookup. +// +// `effmod.Logging` must load module `effmod`, resolve+typecheck it, +// and find `Logging` as a Public SKEffect *within that module*. +// Expected: type checking passes. +// +// ⚠️ KNOWN LIMITATION (issue #228 / ADR-014): the sibling support +// module `effmod` is NOT stdlib, so this fixture resolves ONLY when +// the test harness adds this directory to the module-loader +// search-path. A bare `affinescript check` on this file alone WILL +// fail with "Unknown module 'effmod'". There is no public stdlib +// effect to build a self-contained positive (unlike `prelude.Option` +// for the qualified-*type* fixture). Bare-oracle audits — including +// the estate dialect-conformance `.affine-audit` harness — MUST treat +// THIS fixture as harness-only and exclude it from drift counts. +// Qualified-effect resolution itself is sound and independently +// proven by invalid/private_member.affine (loads stdlib `effects`, +// rejects the private member). +module ConformanceQualifiedEffect; + +pub fn act(x: Int) -> Int / effmod.Logging { + x +} diff --git a/tests/conformance/qualified-paths/valid/qualified_type.affine b/tests/conformance/qualified-paths/valid/qualified_type.affine new file mode 100644 index 0000000..9bca818 --- /dev/null +++ b/tests/conformance/qualified-paths/valid/qualified_type.affine @@ -0,0 +1,19 @@ +// SPDX-License-Identifier: MPL-2.0 +// issue #228 / ADR-014 conformance: a module-qualified TYPE path that +// resolves by sound module-scoped member lookup. +// +// `prelude.Option` must load module `prelude`, resolve+typecheck it, +// and find `Option` as a Public SKType *within that module*. The dot +// `.` is the canonical separator (ADR-011 keeps `::` for value/import). +// Expected: type checking passes. +module ConformanceQualifiedType; + +use prelude::{Option, Some, None}; + +pub fn wrap(x: Int) -> prelude.Option[Int] { + Some(x) +} + +pub fn wrap_angle(x: Int) -> prelude.Option { + Some(x) +}