Prechádzať zdrojové kódy

feat: emit separate generic-class instance objects

Eric Streit 3 dní pred
rodič
commit
b4a70fa8a2

+ 2 - 2
README.md

@@ -88,12 +88,12 @@ docs/              STATUS.md, plan.md, plan-*.md, analysis_*.md, summary_*.md
 ## Status (2026-10-24)
 
 - Suite **305/305**; self-hosting **fixpoint OK** (byte-identical
-  stage2/stage3, 3,806,457 bytes); corpus **519/612 compile-OK,
+  stage2/stage3, 3,807,056 bytes); corpus **519/612 compile-OK,
   0 crashes** (`tools/v3-corpus`; the corpus is gm2's testsuite, so it
   mixes conforming Modula-2 with gm2 leniencies — see `docs/gaps/`).
   The remaining `except*`/`onebyte*`-style corpus files still hit V3's
   declaration-order policy.
-- Active branch **`master`**, feature state **`v3-generic-instances`**.
+- Active branch **`master`**, feature state **`v3-generic-class-instances`**.
   Recent milestones: `PACKEDSET`, ISO exceptions (`EXCEPT`/`RETRY`,
   `EXCEPTIONS`, `M2EXCEPTION`, catchable language traps including
   `functionException`, module `FINALLY`), self-referential pointer

+ 3 - 4
TUTORIAL.md

@@ -149,7 +149,7 @@ TMP="$PWD/.scratch/fixpoint-separate" ./bootstrap/fixpoint-separate.sh
 tools/v3-corpus/corpus.sh
 ```
 
-Current gates: suite **305/305**, fixpoint **OK (3,806,457 bytes)**,
+Current gates: suite **305/305**, fixpoint **OK (3,807,056 bytes)**,
 corpus **519/612 compile-OK, 0 crashes**.
 
 ## 7. Current limits
@@ -159,9 +159,8 @@ corpus **519/612 compile-OK, 0 crashes**.
   the shim, or the output changes, and treats a newer compiler binary as an
   ABI event.  A small content ABI/flags stamp (`gen_ssa/.m2abi`) forces
   a rebuild when the compiler ABI or `--m2flag` settings change.
-- Type/value/procedure generic instances are emitted as separate
-  objects by M2make; generic-class instances still use the client
-  implementation path.
+- Type/value/procedure and generic-class instances are emitted as
+  separate objects by M2make.
 - Per-object self-hosting is a green gate via
   `bootstrap/fixpoint-separate.sh`; compiler units use
   `-legacyopen1d`, while user programs keep pointer views.

+ 10 - 6
compiler/src/compiler.frm

@@ -747,9 +747,11 @@ MODULE -->Grammar;
     RETURN -1
   END FindGenIdx;
 
-  PROCEDURE DoInst;
+  PROCEDURE DoInst (isTarget: BOOLEAN);
   (* Re-parse the generic's source with the instance name and the
-     actuals bound, then lower the resulting unit. *)
+     actuals bound.  In separate mode only the target unit is lowered;
+     imported instance implementations are parsed for their symbols but
+     left out of this object. *)
     VAR j: CARDINAL;
       gi: INTEGER;
       saved: FileIO.File;
@@ -792,9 +794,11 @@ MODULE -->Grammar;
     SetTwoPhase(doTwoPhase);
     Parse;
     InstEnd;
-    IF separate THEN Lower.SetDefUnit(isDef) END;
-    Lower.LowerUnitNow(GetUnit(), FALSE);
-    IF separate THEN Lower.SetDefUnit(FALSE) END;
+    IF (NOT separate) OR isTarget THEN
+      IF separate THEN Lower.SetDefUnit(isDef) END;
+      Lower.LowerUnitNow(GetUnit(), FALSE);
+      IF separate THEN Lower.SetDefUnit(FALSE) END
+    END;
     IF (src # FileIO.StdIn) & (src # FileIO.StdOut) THEN
       FileIO.Close(src)
     END;
@@ -879,7 +883,7 @@ MODULE -->Grammar;
       Parse;
       UnitNameGet(uname);
       IF RefineSeen() THEN
-        DoInst
+        DoInst(f = nFiles - 1)
       ELSIF UnitIsGeneric() THEN
         (* record the generic's source for later instantiation; do not
            emit the abstract template *)

+ 5 - 4
docs/STATUS.md

@@ -6,7 +6,7 @@ This answers "where are we now?".
 
 - Suite **305/305** (`compiler/run_tests.sh`).
 - Self-hosting **FIXPOINT OK** — stage2 and stage3 images are
-  byte-identical (3,806,457 bytes).
+  byte-identical (3,807,056 bytes).
 - Corpus **519 / 612** sources compile with 0 errors, **0 crashes**
   (`tools/v3-corpus/corpus.sh` over the gm2 testsuite; the harness now
   resolves V3's own shipped libraries); the rest are genuine language
@@ -14,7 +14,7 @@ This answers "where are we now?".
   imports of gm2-only modules.  Open-array corpus cases `unbounded`,
   `unbounded2`, `unbounded3`, `unbounded4` and `array9` now compile;
   `unbounded`/`unbounded2`/`unbounded3`/`unbounded4` also run.
-- Branch **`master`**, feature state **`v3-generic-instances`**; tree
+- Branch **`master`**, feature state **`v3-generic-class-instances`**; tree
   clean.
 
 ## Architecture (current)
@@ -202,8 +202,9 @@ declarations still work (`t_fwdproc`, `t_fwdok`).
    per-unit objects (libraries have no `main`), with stable module/class
    symbols, `export data`, and dependency-ordered init calls.  `M2make
    --separate` builds stale objects incrementally and links them.  Core
-   shipped; type/value/procedure generic instances are separate
-   objects.  Per-object self-hosting now has its own green gate.  See
+   shipped; type/value/procedure and generic-class instances are
+   separate objects.  Per-object self-hosting now has its own green
+   gate.  See
    `docs/separate-compilation.md`;
    summary: `summary_session-2026-10-24-separate-compilation.md`.
 5. **Library folders** — `stdlib/` is retired; libraries live in

+ 3 - 3
docs/comparison-PIM4-gm2-V3.md

@@ -2,7 +2,7 @@
 
 **Date:** 2026-10-24 · **V3 state:** open arrays, dialect library
 folders and separate compilation with `M2 -I`/incremental objects
-(suite 305/305, self-hosting fixpoint OK 3,806,457 bytes, corpus
+(suite 305/305, self-hosting fixpoint OK 3,807,056 bytes, corpus
 519/612, 0 crashes).
 
 **Scope and sources.**
@@ -204,9 +204,9 @@ change individual cells.
   `<Module>__<Class>__vt`.  `M2 -I` resolves interfaces transitively;
   `M2make --separate` rebuilds stale objects and relinks on source/
   interface/compiler/shim changes.  The one-shot mode remains the
-  default for the suite/corpus.  Type/value/procedure generic
+  default for the suite/corpus.  Type/value/procedure and generic-class
   instances, definition-only class vtables and per-object self-hosting
-  are covered; generic-class instances remain client-local.
+  are covered.
 - **OO** is the Clarion-form `CLASS` dialect (single inheritance,
   virtual dispatch), not ISO 10514-3.
 

+ 2 - 3
docs/features.md

@@ -178,9 +178,8 @@ Legend: ✅ done · 🔄 partial · ⏸ not started / deferred.
   composite function results (record/array/set/class, sret) and chained
   post-call method calls (`F().M()`); 🔄 per-file objects and `M2 -I`
   scanning, content ABI/flags stamps, definition-only class objects and
-  separate type/value/procedure generic-instance objects exist;
-  generic-class instances remain client-local; import cycles are
-  rejected.
+  separate generic-instance objects (type/value/procedure/class) exist;
+  import cycles are rejected.
 - 🔄 ISO exceptions: **M4** — `EXCEPT`/`RETRY`, handler stack,
   `SYSTEM.THROW`, `EXCEPTIONS`, `M2EXCEPTION`, signal mapping,
   `RETURN`/nested handlers, catchable index/range/CASE traps, and

+ 3 - 3
docs/plan-separate-compilation.md

@@ -5,7 +5,7 @@
 one-shot mode remains the default for the suite/corpus; this document
 keeps the design record and the deferred phases.
 
-**Baseline now:** suite 305/305, fixpoint OK 3,806,457 bytes, corpus
+**Baseline now:** suite 305/305, fixpoint OK 3,807,056 bytes, corpus
 519/612, 0 crashes.
 
 ---
@@ -155,8 +155,8 @@ cc Prog.o Lib.o runtime/syslib/shim.o -lm -o Prog
    main, every object exports `<mod>_init`, and the program object calls
    imported inits in dependency order.
 4. **Generics and classes.**  Classes are done for normal and
-   definition-only cases.  Type/value/procedure generic instances get
-   their own objects; generic-class instances remain client-local.
+   definition-only cases.  Type/value/procedure and generic-class
+   instances get their own objects.
 5. **M2make incremental builds.**  DONE: mtime stale checks, compiler
    binary/shim relink triggers, and a content ABI/flags stamp.
 6. **Self-host with separate compilation.**  DONE:

+ 5 - 5
docs/separate-compilation.md

@@ -70,11 +70,11 @@ The implementation is split across:
 - **Build freshness** uses mtimes plus a small content ABI/flags stamp
   (`gen_ssa/.m2abi`).  The stamp covers the ABI version and `--m2flag`
   settings; per-object binary metadata is not implemented.
-- **Type/value/procedure generic instances** are emitted as separate
-  objects by M2make: it detects `MODULE X = G(actuals)`, builds `X.o`
-  from the template plus instance declaration, and skips the abstract
-  generic object.  Generic-class instances still need the client
-  implementation path.
+- **Type/value/procedure and generic-class instances** are emitted as
+  separate objects by M2make: it detects `MODULE X = G(actuals)`, builds
+  `X.o` from the template plus instance declaration, and skips the
+  abstract generic object.  Imported instance implementations are parsed
+  for method symbols but are not lowered into the client object.
 - **Classes** have stable vtable/method symbols for normal imported
   class use.  M2make now also builds a definition-only object for a
   module with no `.mod`, so a class declared only in a `.def` gets its

+ 4 - 3
docs/summary_session-2026-10-24-separate-compilation.md

@@ -4,7 +4,7 @@ V3 now builds per-unit objects.  The one-shot mode remains the default
 for the suite/corpus.
 
 **Branch:** `master`.  **Feature state:** `v3-separate-compilation`.
-**Gates:** suite **305/305**, fixpoint **OK (3,806,457 bytes)**,
+**Gates:** suite **305/305**, fixpoint **OK (3,807,056 bytes)**,
 corpus **519/612 compile-OK, 0 crashes**.
 
 ---
@@ -98,8 +98,9 @@ Manual M2make cross-object class check: `CLib.def/.mod` + `CProg.mod`
 - An instance module is compiled to its own `gen_ssa/<X>.o` with the
   generic template sources in the same session; the program links that
   object.
-- Type, value and procedure generics work through separate objects.
-  Generic-class instances still use the client implementation path.
+- Type, value, procedure and generic-class generics work through
+  separate objects.  Imported instance implementations are parsed for
+  method information but not lowered into the client object.
 - Added M2make `GenProg` and coverage for qualified procedure actuals.
 
 ## Commits and tags

+ 2 - 2
m2make.md

@@ -74,8 +74,8 @@ event, and the link is redone when an object or the shim changes.
 ## Current limits
 
 - Incremental object checks use mtimes, not a content-based ABI marker.
-- Type/value/procedure generic instances are built as separate objects;
-  generic-class instances still use the client implementation path.
+- Type/value/procedure and generic-class instances are built as separate
+  objects.
 - The default one-shot mode remains the mode used by the suite, corpus and
   self-hosting fixpoint.
 - `M2 -c` resolves imported interfaces through `-I`; explicit interface

+ 10 - 0
tools/m2make/M2make.mod

@@ -994,6 +994,16 @@ BEGIN
             END;
             INC(k2)
           END;
+          (* Imported instance implementations are parsed (for method
+             symbols) but not lowered into the target object. *)
+          k2 := 0;
+          WHILE k2 < nOrder DO
+            IF modIsInstance[order[k2]] AND (order[k2] # m) THEN
+              GetSrc(order[k2], tmpP);
+              IF Len(tmpP) > 0 THEN AppendTok(tmpCmd, tmpP) END
+            END;
+            INC(k2)
+          END;
           AppendTok(tmpCmd, src)
         ELSE
           (* M2 resolves the target .def and all imported .defs from -I. *)

+ 8 - 0
tools/m2make/tests/GBox.def

@@ -0,0 +1,8 @@
+GENERIC DEFINITION MODULE GBox (T : TYPE);
+TYPE
+  CLASS Box;
+    v : T;
+    PROCEDURE Put(x : T);
+    PROCEDURE Get() : T;
+  END Box;
+END GBox.

+ 8 - 0
tools/m2make/tests/GBox.mod

@@ -0,0 +1,8 @@
+GENERIC IMPLEMENTATION MODULE GBox;
+CLASS IMPLEMENTATION Box;
+  PROCEDURE Put(x : T);
+  BEGIN v := x END Put;
+  PROCEDURE Get() : T;
+  BEGIN RETURN v END Get;
+END Box;
+END GBox.

+ 2 - 0
tools/m2make/tests/GCInt.def

@@ -0,0 +1,2 @@
+DEFINITION MODULE GCInt = GBox(INTEGER);
+END GCInt.

+ 2 - 0
tools/m2make/tests/GCInt.mod

@@ -0,0 +1,2 @@
+IMPLEMENTATION MODULE GCInt = GBox(INTEGER);
+END GCInt.

+ 4 - 0
tools/m2make/tests/GCProg.mod

@@ -0,0 +1,4 @@
+MODULE GCBoxProg;
+IMPORT GCInt;
+VAR b : GCInt.Box; ExitCode : INTEGER;
+BEGIN b.Put(42); ExitCode := b.Get() END GCBoxProg.

+ 12 - 1
tools/m2make/tests/run_tests.sh

@@ -153,6 +153,17 @@ else
   no "separate-mode generic instance object (build)"
 fi
 
+# --- generic class instance object ---
+rm -rf gen_ssa GCProg
+if $V3M --separate -c m2 --m2bin ../../compiler/M2 --shim ../../runtime/syslib/shim.c \
+     -I . -o GCProg tests/GCProg.mod > /tmp/m2make_t.log 2>&1; then
+  ./GCProg > /dev/null 2>&1; rc=$?
+  if [ "$rc" = "42" ]; then ok "separate-mode generic class object"
+  else no "separate-mode generic class object (rc=$rc)"; fi
+else
+  no "separate-mode generic class object (build)"
+fi
+
 # --- content ABI stamp ---
 rm -f gen_ssa/.m2abi
 out=$($V3M --separate -c m2 --m2bin ../../compiler/M2 --shim ../../runtime/syslib/shim.c \
@@ -172,7 +183,7 @@ else
   no "gm2-built M2make drives m2 build"
 fi
 
-rm -f Main MainX Main2 SepMain CDefProg GenProg *.o
+rm -f Main MainX Main2 SepMain CDefProg GenProg GCProg *.o
 rm -rf gen_ssa
 rm -f m2make.log