diff --git a/crates/psrs-driver/src/tests/assertions.rs b/crates/psrs-driver/src/tests/assertions.rs index a1513c9c..55500844 100644 --- a/crates/psrs-driver/src/tests/assertions.rs +++ b/crates/psrs-driver/src/tests/assertions.rs @@ -135,3 +135,40 @@ fn assert_true_and_assert_false_report_the_value_that_did_not_hold() { "Assertion failed: Expected: true\nActual: false\n", ); } + +/// The renderings `Data.Show` produces, so this fails if `logShow` grows a +/// stringifier of its own instead of composing the library `show`. +/// +/// The negative case is written `0 - 7` rather than `- 7` on purpose: unary +/// minus resolves through an ordinary in-scope `negate`, which this library +/// does not declare yet, and this test is about the `Show` rendering. +#[test] +fn log_show_writes_the_library_rendering() { + let source = r#" +module Main where + +import Prelude +import Effect.Console (logShow) + +checks :: Effect Unit +checks = do + logShow 42 + logShow (0 - 7) + logShow "hi" + logShow 'c' + logShow true + logShow [1, 2, 3] + pure unit + +main = let ignored = runEffect checks in 0 +"#; + let Some(output) = run_with_wasmtime(source) else { + eprintln!("skipping: wasmtime is not installed"); + return; + }; + assert_eq!(output.status.code(), Some(0), "{output:?}"); + assert_eq!( + output.stdout, b"42\n-7\n\"hi\"\n'c'\ntrue\n[1,2,3]\n", + "`logShow` must write exactly what `Data.Show.show` produces: {output:?}" + ); +} diff --git a/docs/design/D-04-suite-roadmap.md b/docs/design/D-04-suite-roadmap.md index cc493b04..1635c837 100644 --- a/docs/design/D-04-suite-roadmap.md +++ b/docs/design/D-04-suite-roadmap.md @@ -345,6 +345,30 @@ The Show, Foldable, and Tuple figures were measured independently on `6f66524` and are not additive. +**`Effect.Console.logShow` (2026-10-04)** lands the wrapper over the now-declared +`Show` class and moves `passing` resolution from 253/413 to **270/413**. `logShow` +was the largest single P3 name at 24 cases, 19 of them reported as *"`logShow` is +not exported by the imported module"*; it is `log` of `Data.Show.show`, so the +wrapper adds no I/O and no second stringifier. Of the 24, **17 resolve past L2** +and 7 stop at the next P3 name, which is now visible for the first time: +`Monad` and `compare` 2 each, and `negate`, `not`, and `_` 1 each. The 17 land at +later stages as 10 P10, 4 P5 typecheck, 1 P8, and 2 P6, so P10 moves 177 to 187, +P5 typecheck 42 to 46, P8 13 to 14, and P6 4 to 6. M2 failing agreement stays +72/72 and L1 stays 904/908. + +**`Test.Assert.assertEqual`/`assertEqual'` are still absent, and the reason is a +compiler defect rather than a missing class.** Both classes are declared, but the +official signature is `forall a. Eq a => Show a => String -> { actual :: a, +expected :: a } -> Effect Unit`, and a constraint whose quantified variable +appears inside a record type is elaborated with the *record* as the constraint's +argument, so `Eq a` is wanted for `{ actual :: a, expected :: a }`. Probes +separate this from record syntax: `forall a. Eq a => a -> Int` is accepted, a +record with quantified fields is accepted on its own, and both field access and a +record pattern on such a record work; only the constraint combined with the record +fails, and a type synonym for the record fails identically. Changing the argument +shape would change the API the corpus calls, so the two functions stay out and the +defect is filed as #137. That leaves 2 `passing` cases blocked on them. + ### M3 — Kinds and higher-kinded types - **Suite:** `KindsDoNotUnify` (24), `PartiallyAppliedSynonym` (12), @@ -691,9 +715,30 @@ failure must reach the guest as a trap to be visible, which is the only execution signal the corpus can express. Nothing in the corpus needs argv, stdin, or a preopened directory, so the runner passes none. -The 413 rejections, by the first phase that blocks them. `Show` and `Data.Monoid`/`Data.Foldable` each remeasured this table on `6f66524`. The figures were measured independently on `6f66524` and are not additive. +The 413 rejections, by the first phase that blocks them. The current figures come +from one `PSRS_REQUIRE_WASMTIME=1 PSRS_ORACLE=annotations` run of all five boards +on 2026-10-04 at `071fb11` plus the `logShow` slice, so they are a single +consistent measurement. The two tables after it are earlier measurements taken +independently on `6f66524` and are **not** additive with it or with each other; +they are kept because the M2 paragraphs cite them. + +After `Effect.Console.logShow` (`PSRS_REQUIRE_WASMTIME=1` L6/M7 scoreboard run on +2026-10-04 at `071fb11` plus this slice): + +| Blocker | Cases | Recovered by | +| --- | --- | --- | +| Missing library module | 53 | Phase 3: #94 `Prelude`, #95 `Effect`/`Effect.Console`/`Test.Assert`, #96 `Proxy`/`Partial.Unsafe`, and #124 the unowned `Data.*` modules. | +| P10 Wasm structuring | 187 | Reached the backend; 124 have a `main` that is not the zero-argument `Int` entry, and 63 have no selectable `main`. | +| P3 resolve | 86 | Another resolution error behind the library gap; the library surface owns most of them. | +| P5 typecheck | 46 | A type error behind the other blockers. | +| P5 kind check | 17 | A kind error behind the other blockers. | +| P8 closure conversion | 14 | A representation behind the other blockers. | +| P0 lex | 4 | The DEC-16 lone-surrogate cases, which are also L1 differences. | +| P6 Core lowering | 6 | `Prim.undefined` has no runtime representation, plus partially applied field constructors. | +| P2 surface lowering | 0 | No `passing` file stops in surface lowering; Phase 2 closed this row. | +| Harness loading | 0 | Nothing: every case assembles. | -After `Show` (`PSRS_ORACLE=annotations` L6/M7 scoreboard run on 2026-10-04): +Earlier, after `Show` (`PSRS_ORACLE=annotations` L6/M7 scoreboard run on 2026-10-04): | Blocker | Cases | Recovered by | | --- | --- | --- | @@ -708,7 +753,7 @@ After `Show` (`PSRS_ORACLE=annotations` L6/M7 scoreboard run on 2026-10-04): | P2 surface lowering | 0 | No `passing` file stops in surface lowering; Phase 2 closed this row. | | Harness loading | 0 | Nothing: every case assembles. | -After `Data.Monoid` and `Data.Foldable` (`PSRS_REQUIRE_WASMTIME=1` compile-path measurement on 2026-10-04 at `6f66524`, taken without `Show`): +Earlier again, after `Data.Monoid` and `Data.Foldable` (`PSRS_REQUIRE_WASMTIME=1` compile-path measurement on 2026-10-04 at `6f66524`, taken without `Show`): | Blocker | Cases | Recovered by | | --- | --- | --- | @@ -771,17 +816,21 @@ The list is `Prelude` (the `Effect` interface, including `pure`, `bind`, `runEffect`, and `trap`), `Data.Function`, `Data.Semigroup` (the `Semigroup` class, `append`, and `<>`), `Data.Monoid` (`mempty`), `Data.Eq`, `Data.Ord`, `Data.Semiring`, `Data.Show` (`Show` and `show`), `Effect` and `Effect.Console` -(the corpus-facing names over that interface and `WASI.Console`), `Test.Assert` -(the four checks that need no class surface), `Data.Maybe`, `Data.Either`, -`Data.Tuple` (the closed record `{ _1, _2 }`), `Data.Foldable` (`foldr`, -`foldl`, `foldMap`), and the nine `WASI` modules. The corpus imports `Effect.Console` 339 times, `Effect` 57 +(the corpus-facing names over that interface and `WASI.Console`, plus `logShow` +as `log` of `show`), `Test.Assert` (the four checks that need no class surface), +`Data.Maybe`, `Data.Either`, `Data.Tuple` (the closed record `{ _1, _2 }`), +`Data.Foldable` (`foldr`, `foldl`, `foldMap`), and the nine `WASI` modules. The +list is unchanged in count by the `logShow` slice, which added an export rather +than a module. The corpus imports `Effect.Console` 339 times, `Effect` 57 times, `Test.Assert` 29 times, then `Type.Proxy` (17), `Partial.Unsafe` (12), `Data.Tuple` (8), `Prim.Row` (7), and the `Prim.*` and `Data.*` hierarchies. -`Test.Assert` exports only `assert`, `assert'`, `assertTrue`, and `assertFalse`. -`Eq` and `Show` are declared; `assertEqual` and `logShow` are still #95's -exports and are not added here. `assertThrows` needs to observe a trap, which -the target profile does not provide. `logShow` remains the largest single -export gap in the library. +`Test.Assert` still exports only `assert`, `assert'`, `assertTrue`, and +`assertFalse`: `Eq` and `Show` are declared, so `assertEqual` and `assertEqual'` +are blocked only by #137, which prevents a constraint on a variable that appears +inside a record type from being elaborated, and `assertThrows` needs to observe a +trap, which the target profile does not provide. `logShow` is no longer an export +gap; the largest single *export* blocker is now `Monad` at 13 cases, and the +largest single *module* blocker is `Partial.Unsafe` at 9. ### M8 — Warnings and optimization @@ -878,7 +927,7 @@ concrete slice issues as sub-issues; this table is the index. | 0 | [#80](https://github.com/biuld/purescript-rs/issues/80) Lexer and layout agreement | `failing/2434.purs`, `layout/Commas.purs`, `layout/CaseGuards.purs` | Self-contained parse agreement. L1 is measured at 904/908; the four remaining cases are the DEC-16 intentional differences. String values follow [DEC-16](../decision/DEC-16-scalar-strings-and-utf8-storage.md); preserving lone UTF-16 surrogates is not a remaining gate. | | 1 | [#74](https://github.com/biuld/purescript-rs/issues/74) Make every gate measurable | [#81](https://github.com/biuld/purescript-rs/issues/81) official `errorCode` mapping, [#82](https://github.com/biuld/purescript-rs/issues/82) lenient type check and L4/L5 scoreboards, [#83](https://github.com/biuld/purescript-rs/issues/83) harness module path, [#93](https://github.com/biuld/purescript-rs/issues/93) runtime scoreboard | Nothing else can be verified until L4, L5, L6/M7, and M8-W report numbers. Changes no user-visible behavior. | | 2 | [#75](https://github.com/biuld/purescript-rs/issues/75) Frontend surface lowering — **complete** | [#84](https://github.com/biuld/purescript-rs/issues/84) ascription, [#85](https://github.com/biuld/purescript-rs/issues/85) patterns, [#86](https://github.com/biuld/purescript-rs/issues/86) operator aliases, [#87](https://github.com/biuld/purescript-rs/issues/87) type wildcards and rows, [#88](https://github.com/biuld/purescript-rs/issues/88) guards and multi-scrutinee `case`, [#89](https://github.com/biuld/purescript-rs/issues/89) `Prim` and unary minus, [#90](https://github.com/biuld/purescript-rs/issues/90) instance resolution | **No `passing` file stops in surface lowering.** From the 61-case `5298aad` baseline, #88 moved 22 past P2, the 34 fixed pattern paths plus 22 type, kind, instance, and declaration forms brought it to 5, and [#87](https://github.com/biuld/purescript-rs/pull/122) removed the last five. All six slices are `Done`. | -| 3 | [#76](https://github.com/biuld/purescript-rs/issues/76) Standard library — **next** | [#94](https://github.com/biuld/purescript-rs/issues/94) `Prelude` (54 measured by the `Show` slice, first blocking stage; Foldable left the earlier 80 and did not remeasure this counter), [#95](https://github.com/biuld/purescript-rs/issues/95) `Effect`/`Effect.Console`/`Test.Assert` (305), [#96](https://github.com/biuld/purescript-rs/issues/96) tuples, `Proxy`, `Partial.Unsafe` (12), [#124](https://github.com/biuld/purescript-rs/issues/124) the `Data` modules no slice owned (25) | **342 of the 354** files L2 originally could not resolve were blocked on a missing library module. #95's first slice landed `Effect` and `Effect.Console`, dropping that to **83** and `passing` resolution from 59/413 to **199/413**; its second slice landed the `unit` value and `Test.Assert`, dropping the missing-module count to **62** and `passing` resolution to **209/413**. #94's first slice then landed `Data.Function` and the `$`/`#` operators, taking resolution to **225/413** and the missing-module count to **61**; its next slice landed `Data.Semigroup` and `<>`, taking resolution to **235/413**. `Eq`, `Ord`, and `Semiring` then landed with the intrinsic layer. The `Show` slice, measured on that master, moves resolution from 245/413 to **253/413**. `Data.Monoid` and `Data.Foldable`, measured independently on `6f66524` before `Show`, leave lenient resolution at **245/413** and move the compile-path missing-module count from 57 to 55. The figures were measured independently on `6f66524` and are not additive. The `Data.Tuple` slice, re-measured with the L2 scoreboard on `6f66524`, leaves resolution at **245/413** and moves the missing-module count from 57 to **56**, with other P3 blockers from 107 to **108**. That measurement is independent of the Show and Foldable figures and is not additive with them. Tuple's row said `logShow` and `assertEqual` were still waiting on the `Show`/`Eq` class surface, and that the GitHub `Corpus cases` field records the measured count; Show later recorded that the classes are declared and that the field was not written. What #95 still owns is `logShow` and `assertEqual`; the classes they need are declared, and the wrappers are not part of either slice. Depends on Phase 2, now complete: the library itself uses ascriptions, guards, sections, and instances. #94 measures 0 by first blocking stage only because `stdlib/lib/Prelude.purs` resolves; its missing surface is latent, surfacing as the P3 `Monad`, `identity`, `Functor`, and `negate` blockers *behind* the library modules. After `Show`, 54 `passing` files still stop first on a Prelude class or value name (`Monad` 11, `identity` 8, `Functor` 6, `negate` 5, `&&`/`otherwise`/`<$>`/`<<<` 3 each, `Partial` 3, `when`/`mod` 2 each, and one each of `between`, `>>>`, `||`, `Applicative`, and `P.identity`). `show` and `Show` are not in that set. Foldable did not remeasure that Prelude-name counter. Show recorded that the GitHub `Corpus cases` field was not written because `gh` is not authenticated on that machine; Foldable's row said the field records the measured count, and that claim was not rechecked here. | +| 3 | [#76](https://github.com/biuld/purescript-rs/issues/76) Standard library — **next** | [#94](https://github.com/biuld/purescript-rs/issues/94) `Prelude` (77 measured after the `logShow` slice: the 7 cases that stopped on `logShow` stop instead on a `Prelude` name #94 also owns), [#95](https://github.com/biuld/purescript-rs/issues/95) `Effect`/`Effect.Console`/`Test.Assert` (305), [#96](https://github.com/biuld/purescript-rs/issues/96) `Proxy`/`Partial.Unsafe` (17), [#124](https://github.com/biuld/purescript-rs/issues/124) the `Data` modules no slice owned (27) | **342 of the 354** files L2 originally could not resolve were blocked on a missing library module. #95's first slice landed `Effect` and `Effect.Console`, dropping that to **83** and `passing` resolution from 59/413 to **199/413**; its second slice landed the `unit` value and `Test.Assert`, dropping the missing-module count to **62** and `passing` resolution to **209/413**. #94's first slice then landed `Data.Function` and the `$`/`#` operators, taking resolution to **225/413** and the missing-module count to **61**; its next slice landed `Data.Semigroup` and `<>`, taking resolution to **235/413**. `Eq`, `Ord`, and `Semiring` then landed with the intrinsic layer. The `Show` slice, measured on that master, moves resolution from 245/413 to **253/413**. `Data.Monoid` and `Data.Foldable`, measured independently on `6f66524` before `Show`, leave lenient resolution at **245/413** and move the compile-path missing-module count from 57 to 55. The figures were measured independently on `6f66524` and are not additive. The `Data.Tuple` slice, re-measured with the L2 scoreboard on `6f66524`, leaves resolution at **245/413** and moves the missing-module count from 57 to **56**, with other P3 blockers from 107 to **108**. That measurement is independent of the Show and Foldable figures and is not additive with them. Tuple's row said `logShow` and `assertEqual` were still waiting on the `Show`/`Eq` class surface, and that the GitHub `Corpus cases` field records the measured count; Show later recorded that the classes are declared and that the field was not written. What #95 still owns is `assertEqual` and `assertEqual'`; `logShow` has since landed as `log` of `show`, taking resolution to **270/413**. Depends on Phase 2, now complete: the library itself uses ascriptions, guards, sections, and instances. #94 measures 0 by first blocking stage only because `stdlib/lib/Prelude.purs` resolves; its missing surface is latent, surfacing as the P3 `Monad`, `identity`, `Functor`, and `negate` blockers *behind* the library modules. After the `logShow` slice, the 86 files blocked first at P3 on a name split as 77 to the `Prelude`/class surface #94 owns, 4 to #124 (`Unit` 2, `P.Unit` 1, `between` 1), 3 to #96 (`Partial`), and 2 to #95 (`assertEqual`). The 77 are `Monad` 13, `identity` 9, `_` 8, `negate` 6, `Functor` 6, `Eq1` 4, `<<<` 4, `otherwise` 3, `<$>` 3, `&&` 3, `when` 2, `mod` 2, `compare` 2, and one each of `P.identity`, `Applicative`, `||`, `>>>`, `not`, `-`, and `Foo.Bar`. `show` and `Show` are not in that set, and neither is `logShow` any longer. Show recorded that the GitHub `Corpus cases` field was not written because `gh` is not authenticated on that machine; Foldable's row said the field records the measured count, and that claim was not rechecked here. | | 4 | [#77](https://github.com/biuld/purescript-rs/issues/77) L4 and L5 to 100% | [#97](https://github.com/biuld/purescript-rs/issues/97) missing class checks, [#98](https://github.com/biuld/purescript-rs/issues/98) deriving and fundeps, [#99](https://github.com/biuld/purescript-rs/issues/99) hole inference, [#100](https://github.com/biuld/purescript-rs/issues/100) M3 kind gate, [#123](https://github.com/biuld/purescript-rs/issues/123) `forall` binder visibility | Turns "measurable" into "passing". #81 makes 153 cases trackable; the rest need rules. #123 is the shared root cause behind the three visible-type-application limits #87 recorded, and #100's polykind instantiation needs the same machinery, so it is filed as one foundational change rather than three patches. | | 5 | [#78](https://github.com/biuld/purescript-rs/issues/78) Backend on real programs | [#73](https://github.com/biuld/purescript-rs/issues/73) aggregate fixture execution, [#101](https://github.com/biuld/purescript-rs/issues/101) CC/MIR coverage | Consumes the output of Phases 2–4. The backend rows are `Partial` on source coverage, not on design. | | 6 | [#79](https://github.com/biuld/purescript-rs/issues/79) M8 warnings and optimization | [#91](https://github.com/biuld/purescript-rs/issues/91) warning scoreboard, [#92](https://github.com/biuld/purescript-rs/issues/92) optimize comparison | Last, because both need a harness first and neither blocks another phase. | @@ -961,11 +1010,11 @@ for matrix status. | --- | --- | --- | --- | | L0 | Layout goldens | 15/15 official parse outcomes agree (12 accepted, 3 rejected), enforced by regression tests. | 15/15 agreement, with all layout cases covered by regression tests. | | L1 | Non-excluded parse behavior | 904/908 agreement using the annotations oracle; `passing` 410/413, `failing` 412/413, `warning` 67/67, `layout` 15/15, with the four remaining cases recorded as DEC-16 intentional differences | 100% agreement apart from the DEC-16 intentional differences. | -| L2 | Module, import, export, and name resolution | 72/72 failing cases. Three measurements on `6f66524`, not additive: `Show` has 253/413 passing modules resolving, 57 blocked on a missing module, and 99 at P3, and its L6 run stops 177 at P10; `Data.Foldable`'s compile-path leaves resolution at 245/413, with 55 missing modules and 109 other P3 blockers; `Data.Tuple`'s L2 remeasurement leaves resolution at 245/413, with 56 missing modules and 108 at P3, and that L2 command does not report P10. None at P2, and none blocked on assembly. | The mapped resolution cases and all required passing-module cases agree. | +| L2 | Module, import, export, and name resolution | 72/72 failing cases. `passing` resolution is **270/413**, with 53 first-stage blockers on a missing module and 86 at P3; none at P2, and none blocked on assembly. One `PSRS_ORACLE=annotations` run of all boards at `071fb11` plus the `logShow` slice, so these replace the three earlier independent `6f66524` measurements. | The mapped resolution cases and all required passing-module cases agree. | | L3 | Kinds and higher-kinded types | 35/48 failing cases on the `Show` remeasurement (`KindsDoNotUnify` 15/24, `PartiallyAppliedSynonym` 10/12, and the other mapped code totals as measured in M3). The gate row Tuple left in place still said 34/48; Tuple did not remeasure L3, so 34/48 and 35/48 are not a combined result. | 100% agreement for the mapped kind cases. | | L4 | Core type checking | 35/50 failing cases; `TypesDoNotUnify` 32/41, `IntOutOfRange` 1/1, `InfiniteType` 2/2, `CannotApplyExpressionOfTypeOnType` 1/2, `EscapedSkolem` 0/2, `ExpectedType` 0/2, `AmbiguousTypeVariables` 0/1. | 100% agreement for the mapped type cases. | -| L5 | Classes and instances | 53/84 failing cases; `OverlappingInstances` 8/8, `NoInstanceFound` 41/52, `MissingClassMember` 2/2, `DuplicateInstance` 1/1, `InvalidInstanceHead` 1/5, and 0 for the other mapped codes. | 100% agreement for the mapped class cases. | -| L6/M7 | Runtime and standard library | 0/413 non-FFI passing files compile, validate, and run; 57 stop on missing modules, 177 at P10, 99 at P3, 42 at P5 typecheck, 17 at P5 kind checking, 13 at P8, 4 at P6, and 4 at P0; no P2 surface-lowering blockers and no harness-loading blockers. | Every in-scope passing file for the feature compiles, validates, and runs with the expected result. | +| L5 | Classes and instances | 53/80 failing cases; `OverlappingInstances` 8/8, `NoInstanceFound` 41/52, `MissingClassMember` 2/2, `DuplicateInstance` 1/1, `InvalidInstanceHead` 1/5, and 0 for the other mapped codes. The row previously said 84; the per-code totals sum to 80 and the remeasurement confirms 80. | 100% agreement for the mapped class cases. | +| L6/M7 | Runtime and standard library | 0/413 non-FFI passing files compile, validate, and run; 53 stop on missing modules, 187 at P10, 86 at P3, 46 at P5 typecheck, 17 at P5 kind checking, 14 at P8, 6 at P6, and 4 at P0; no P2 surface-lowering blockers and no harness-loading blockers. | Every in-scope passing file for the feature compiles, validates, and runs with the expected result. | | M8-W | Warnings | 67 non-FFI warning files are in scope; no warning-code scoreboard exists | Warning-code agreement reaches 100% for the tracked warning corpus. | | M8-O | Optimization | 10 optimize files are in scope; they are not vendored and their goldens are JavaScript output | Expected optimize/CoreFn output agrees for all tracked optimize files. | @@ -1006,7 +1055,7 @@ resolved, type checked, and represented in Typed Core as required. | ID | Feature | Current support | Status | Next landing | | --- | --- | --- | --- | --- | | FE-01 | Lexing, Unicode tokens, comments, literals, and layout | Lexer and layout agree with the L1 annotations scoreboard at 904/908, including 15/15 layout cases. The four differences are the DEC-16 intentional differences: a supplementary scalar is accepted as one `Char` (`failing/2434.purs`), and an unpaired surrogate escape is rejected in `StringEscapes.purs` and the two `StringEdgeCases` files. A paired surrogate escape decodes as one scalar, and no surrogate becomes U+FFFD. Parse agreement does not verify string values. | Partial | Cover the remaining literal forms the corpus exercises. | -| FE-02 | Module headers, imports, exports, qualified names, aliases, and hiding | Module graph, stable module IDs, value/type/constructor/class imports and exports, fixity aliases, virtual `Prim.*` type/class interfaces, instance dictionary identities, per-branch instance exports, and unary minus through ordinary `negate` resolution work in a subset; 72/72 mapped failing cases agree. `Show` measured 253/413 passing modules resolving; `Data.Tuple` measured 245/413 on the same `6f66524` base. The figures are not additive. A re-exported operator alias carries its target's identity and does not require the target's name unless the target is declared in the re-exporting module. Class-only imports do not import methods into the value namespace; selective imports still receive visible instances through the module dependency graph. P3 checks explicit signatures and declaration dependencies; P5 checks inferred public schemes by stable type identity. `Prim.undefined` has a compiler-owned identity, type, and interface export, but Core lowering still rejects it because no runtime representation is defined. The [primitives topic](frontend/type-system/prim.md) owns the `Prim.*` inventory, the evidence-class dispatch order, relation outcomes, and diagnostic behavior; #120 adds the missing relation and report paths. Broader pattern-binding support remains incomplete. | Partial | Complete pattern-binding support; add the `Prim.undefined` runtime representation and continue official-suite coverage for primitive solving. | +| FE-02 | Module headers, imports, exports, qualified names, aliases, and hiding | Module graph, stable module IDs, value/type/constructor/class imports and exports, fixity aliases, virtual `Prim.*` type/class interfaces, instance dictionary identities, per-branch instance exports, and unary minus through ordinary `negate` resolution work in a subset; 72/72 mapped failing cases agree. `passing` resolution is **270/413**, with 53 first-stage blockers on a missing module and 86 at P3, from one run of all boards at `071fb11` plus the `logShow` slice; this replaces the three earlier independent `6f66524` measurements. A re-exported operator alias carries its target's identity and does not require the target's name unless the target is declared in the re-exporting module. Class-only imports do not import methods into the value namespace; selective imports still receive visible instances through the module dependency graph. P3 checks explicit signatures and declaration dependencies; P5 checks inferred public schemes by stable type identity. `Prim.undefined` has a compiler-owned identity, type, and interface export, but Core lowering still rejects it because no runtime representation is defined. The [primitives topic](frontend/type-system/prim.md) owns the `Prim.*` inventory, the evidence-class dispatch order, relation outcomes, and diagnostic behavior; #120 adds the missing relation and report paths. Broader pattern-binding support remains incomplete. | Partial | Complete pattern-binding support; add the `Prim.undefined` runtime representation and continue official-suite coverage for primitive solving. | | FE-03 | Value declarations, signatures, recursive groups, pattern bindings, and `where` | Named declarations, signatures, recursive local groups, and top-level SCC inference work; selected local pattern declarations, including `LetPattern`, lower through the pattern pipeline. The full declaration and `where` forms are not end-to-end. | Partial | Complete remaining pattern declarations and local `where` blocks. | | FE-04 | Declaration forms: `data`, `newtype`, `type`, `class`, `instance`, `derive`, `foreign`, roles, fixities, and kind signatures | Data/newtype roles are inferred and checked, foreign role signatures enter the checked kind environment, and source role errors retain spans. Instance declarations resolve into dictionary-scoped members; signatures associate with consecutive equations, reject orphan/repeated declaration groups, and check against the class method specialized by the instance head. Deriving and several declaration forms remain incomplete. | Partial | Complete deriving and the remaining declaration-form semantics. | | FE-05 | Expressions: application, operators, lambdas, `if`, `let`, `case`, records, arrays, literals, sections, `do`, and `ado` | Application, value and type operators with resolved fixities, the `Data.Function` application operators `$` and `#` with their official associativity and precedence, unary minus through the ordinary in-scope `negate` value, lambdas, `if`, `let`, `case`, scalar arrays, empty array literals whose element type is determined, records, and selected literals work; `do`/`ado` lower to bind, discard, and `let`. The ascription `e :: T` is checked against its written type and remains explicit through Typed Core. Sections lower through P4 and have runtime coverage. Remaining literal and expression forms are open. | Partial | Complete the remaining literal and expression forms. | @@ -1070,7 +1119,7 @@ Wasm is the target encoding, and WIT/WASI are the platform integration layers. | BE-24 | WASI sockets and HTTP | Not part of the current synchronous portable-program target. | Excluded | Revisit as a separate platform scope after the core target is stable. | | BE-25 | WASI 0.3 async streams and futures | The current compiler targets synchronous WASI 0.2. | Planned | Revisit only with an explicit platform decision and async language/library plan. | | BE-26 | Standard library and user module loading | User modules are discovered from the entry files' directories and linked transitively ([WASI-09](../implementation/backend/wasi-platform.md) Verified); the PureScript-facing standard library is loaded from `stdlib/lib` in trusted-prefix order ([WASI-10](../implementation/backend/wasi-platform.md) Verified). | Partial | Pass the L6/M7 module-loading scoreboard. | -| BE-27 | Wasm/WASI execution and official passing-suite runtime coverage | Vertical execution tests pass for the bootstrap slice, and the `l6_runtime_scoreboard` harness compiles, validates, and runs the 413 non-FFI `passing` files; it measures 0/413 today. The first blockers are 61 missing library modules, 160 P10 entry-point-selection failures, 113 P3 resolution failures, 43 P5 type errors, 16 P5 kind errors, 12 P8 representation errors, 4 P6 Core-lowering failures, and 4 P0 lexing failures; there are 0 harness-loading blockers. The library surface this row was waiting on is landed: `Effect`/`Effect.Console` and `Test.Assert`, whose failure path is a real guest trap (`Prelude.trap`). The remaining library work is the `Prelude` class and value surface (#94) and the unowned `Data.*` modules (#124). The 26 FFI files are excluded. | Partial | Land the `Prelude` class surface, then track per-feature runtime cases against the board. | +| BE-27 | Wasm/WASI execution and official passing-suite runtime coverage | Vertical execution tests pass for the bootstrap slice, and the `l6_runtime_scoreboard` harness compiles, validates, and runs the 413 non-FFI `passing` files; it measures 0/413 today. The first blockers are 53 missing library modules, 187 P10 entry-point-selection failures, 86 P3 resolution failures, 46 P5 type errors, 17 P5 kind errors, 14 P8 representation errors, 6 P6 Core-lowering failures, and 4 P0 lexing failures; there are 0 harness-loading blockers. The library surface this row was waiting on is landed: `Effect`/`Effect.Console` — including `logShow` over the library `show` — and `Test.Assert`, whose failure path is a real guest trap (`Prelude.trap`). The remaining library work is the `Prelude` class and value surface (#94), the unowned `Data.*` modules (#124), and `Test.Assert.assertEqual` (#95), which #137 blocks because a constraint on a variable inside a record type is elaborated against the record. P10 is the largest single blocker at 187 and is not a library gap, so this row cannot move above 0/413 until Phase 5 selects an entry for `main :: Effect Unit`. The 26 FFI files are excluded. | Partial | Land the `Prelude` class surface, then track per-feature runtime cases against the board. | | BE-28 | JavaScript/Node.js FFI compatibility | Not emitted or executed by this backend. | Excluded | No work planned under this decision. | ### Topic implementation acceptance diff --git a/docs/design/backend/wasm/primitive-ffi-and-stdlib.md b/docs/design/backend/wasm/primitive-ffi-and-stdlib.md index d36b0953..4ebb5ec2 100644 --- a/docs/design/backend/wasm/primitive-ffi-and-stdlib.md +++ b/docs/design/backend/wasm/primitive-ffi-and-stdlib.md @@ -346,7 +346,7 @@ stdlib/lib/ Data/Function.purs const, flip, apply, applyFlipped, on, $, # Data/Semigroup.purs class Semigroup, append, <> Effect.purs the corpus-facing name for that interface - Effect/Console.purs log, warn, error over WASI.Console + Effect/Console.purs log, warn, error over WASI.Console; logShow over show Test/Assert.purs assert, assert', assertTrue, assertFalse Data/Maybe.purs data Maybe a = Nothing | Just a, plus eliminators Data/Either.purs data Either a b = Left a | Right b, plus eliminators @@ -373,8 +373,10 @@ crates/psrs-backend/src/ `exitWithCode :: Int -> Effect Unit` and does not export `exitWithCodeRaw`. The `WASI` umbrella re-exports the curated API of every focused module. - The corpus-facing modules wrap that surface rather than adding imports: - `Effect.Console` re-exports `WASI.Console` under the names the suite imports, - and `Test.Assert` composes `Effect.Console.error` with `Prelude.trap`. A + `Effect.Console` re-exports `WASI.Console` under the names the suite imports + and adds `logShow` as `log` of the library's `show`, so it is a wrapper over + `Data.Show` and not a second stringifier. `Test.Assert` composes + `Effect.Console.error` with `Prelude.trap`. A failure is a guest trap because that is the only signal a compilable corpus case can carry; `Prelude.trap` is a `psrs:effect` external, not a host call. - Each raw binding is an unexported `foreign import` whose parameters and diff --git a/docs/design/backend/wasm/wasi-platform-library.md b/docs/design/backend/wasm/wasi-platform-library.md index e35c75f2..5ef2b4a5 100644 --- a/docs/design/backend/wasm/wasi-platform-library.md +++ b/docs/design/backend/wasm/wasi-platform-library.md @@ -131,7 +131,7 @@ wrapper owns the corpus-facing name. | `Data.Foldable` | `class Foldable`, `foldr`, `foldl`, `foldMap` | `Data.Monoid` and the array index primitives; `Array`, `Maybe`, and `Either a` instances | FE-14, FE-16 | | `Data.Tuple` | `Tuple`, `fst`, `snd`, `curry`, `uncurry`, `swap` | the closed record `{ _1 :: a, _2 :: b }` that FE-06 already lowers a tuple to; `type Tuple a b` is that record, not an algebraic `data Tuple a b = Tuple a b` | FE-06 | | `Effect` | re-exports the `Prelude` surface above | `Prelude` | FE-02 | -| `Effect.Console` | `log`, `warn`, `error` | `WASI.Console` | BE-21 | +| `Effect.Console` | `log`, `warn`, `error`, `logShow` | `WASI.Console`, and the library `Data.Show.show` for `logShow` | BE-21 | | `Test.Assert` | `assert`, `assert'`, `assertTrue`, `assertFalse` | `Effect.Console.error` and `Prelude.trap` | BE-21, BE-27 | ### Capability matrix diff --git a/docs/implementation/backend/wasi-platform.md b/docs/implementation/backend/wasi-platform.md index cd9a0dfc..c07cb3f4 100644 --- a/docs/implementation/backend/wasi-platform.md +++ b/docs/implementation/backend/wasi-platform.md @@ -240,7 +240,8 @@ WASI-10: a_failed_assertion_traps_with_its_message_when_wasmtime_is_available, a_failed_assertion_writes_the_message_before_it_traps, a_statement_after_a_failed_assertion_never_runs, - assert_true_and_assert_false_report_the_value_that_did_not_hold}; + assert_true_and_assert_false_report_the_value_that_did_not_hold, + log_show_writes_the_library_rendering}; tests::wasi::{prints_hello_world_when_wasmtime_is_available, reads_the_monotonic_clock_when_wasmtime_is_available, rejects_an_import_of_unexported_exit_with_code_raw, diff --git a/stdlib/lib/Effect/Console.purs b/stdlib/lib/Effect/Console.purs index 194fd227..e336027e 100644 --- a/stdlib/lib/Effect/Console.purs +++ b/stdlib/lib/Effect/Console.purs @@ -7,11 +7,17 @@ -- | That keeps one place that decides where output goes, rather than a -- | wrapper that could drift from it. -- | --- | `logShow` is **absent**, not approximated. `Data.Show` declares the class --- | it needs (`forall a. Show a => a -> Effect Unit` is `log` of `show`), but --- | the wrapper is the remaining `Effect.Console` surface and stays with #95 --- | rather than being slipped in beside the class. -module Effect.Console (log, warn, error) where +-- | `logShow` adds no I/O of its own: it is `log` of `Data.Show.show`, so the +-- | rendering is the library's `Show` and the destination is still the one +-- | `WASI.Console.log` decides. It is a wrapper like `log`, not a second +-- | stringifier. +module Effect.Console (log, warn, error, logShow) where import Prelude -import WASI.Console (error, log, warn) \ No newline at end of file +import WASI.Console (error, log, warn) +import Data.Show (class Show, show) + +-- | Writes the `Show` rendering of a value. `log` already writes the newline, +-- | so this is `log` composed with the library's `show`. +logShow :: forall a. Show a => a -> Effect Unit +logShow value = log (show value) diff --git a/stdlib/lib/Test/Assert.purs b/stdlib/lib/Test/Assert.purs index e4279f0c..477c987f 100644 --- a/stdlib/lib/Test/Assert.purs +++ b/stdlib/lib/Test/Assert.purs @@ -11,9 +11,20 @@ -- | **Deliberately absent**, with the reason recorded rather than approximated: -- | -- | - `assertEqual` and `assertEqual'` compare with `Eq` and print with `Show`. --- | Both classes are declared (`Data.Eq`, `Data.Show`); the functions stay --- | with #95, which owns the rest of this module. Providing them here would --- | take that slice's exports. `assertThrows` is a separate gap, below. +-- | Both classes are declared (`Data.Eq`, `Data.Show`), so the surface itself +-- | is writable — the official signature is what this compiler cannot yet +-- | elaborate. The corpus calls the record form +-- | (`assertEqual' "label" { expected: e, actual: a }`), whose official type is +-- | `forall a. Eq a => Show a => String -> { actual :: a, expected :: a } -> +-- | Effect Unit`. A constraint whose quantified variable appears inside a +-- | record type is elaborated with the *record* as the constraint's argument, +-- | so `Eq a` is wanted for `{ actual :: a, expected :: a }` and the +-- | declaration is rejected with `NoInstanceFound`. The same signature with a +-- | type synonym for the record fails identically, so it is constraint +-- | elaboration rather than the record syntax. Approximating the signature +-- | would change the official API the corpus calls, so the functions stay out +-- | until that is fixed. #137 carries the minimal reproduction and the probes +-- | that separate this defect from record syntax. -- | - `assertThrows` and `assertThrows'` need to observe that evaluating an -- | argument failed. A trap is not observable from inside the guest without -- | the Wasm exceptions proposal, which is outside the target profile