Kaynağa Gözat

language: set<->word VAL/CAST (BITSET as a machine word)

Track B: in Modula-2 a BITSET has a machine-word representation, so
VAL/CAST between a set and CARDINAL is well-defined.  Accept
ClSet<->ClInt in the VAL production (which now also takes CAST):
- set -> CARDINAL: load word 0 of the set,
- CARDINAL -> set: a zeroed one-word set temp holding the value.

constset, constset2, constset3 pass; corpus 389 -> 392.  Suite 236/236
(t_setword); fixpoint OK, 3,858,265 bytes.  docs/gaps gap08 retired;
gap22 added (__FILE__/__LINE__, which packed also needs).
Eric Streit 12 saat önce
ebeveyn
işleme
481b2c306d

+ 2 - 2
README.md

@@ -47,8 +47,8 @@ docs/              STATUS.md, plan.md, plan-*.md, analysis_*.md, summary_*.md
 
 
 ## Status (2026-10-18)
 ## Status (2026-10-18)
 
 
-- Suite **235/235**; self-hosting **fixpoint OK** (byte-identical
-  stage2/stage3, 3,854,595 bytes); corpus **389/612 compile-OK,
+- Suite **236/236**; self-hosting **fixpoint OK** (byte-identical
+  stage2/stage3, 3,858,265 bytes); corpus **392/612 compile-OK,
   0 crashes** (`tools/v3-corpus`; the corpus is gm2's testsuite, so it
   0 crashes** (`tools/v3-corpus`; the corpus is gm2's testsuite, so it
   mixes conforming Modula-2 with gm2 leniencies — see `docs/gaps/`).
   mixes conforming Modula-2 with gm2 leniencies — see `docs/gaps/`).
 - Active branch **`resolve-check`** (holds the AST/`Lower`/parse
 - Active branch **`resolve-check`** (holds the AST/`Lower`/parse

+ 1 - 0
compiler/run_tests.sh

@@ -100,6 +100,7 @@ expect_run t_constreal.mod 42
 expect_run t_constlong.mod 42
 expect_run t_constlong.mod 42
 expect_run t_procaddr.mod 42
 expect_run t_procaddr.mod 42
 expect_run t_convert.mod 42
 expect_run t_convert.mod 42
+expect_run t_setword.mod 42
 expect_run t_procval.mod 42
 expect_run t_procval.mod 42
 expect_run t_real64.mod 42
 expect_run t_real64.mod 42
 expect_run t_shiftneg.mod 42
 expect_run t_shiftneg.mod 42

+ 10 - 0
compiler/src/Lower.mod

@@ -1462,6 +1462,16 @@ PROCEDURE LowerBuiltinExpr (n: AST.Node; nm: SymTab.Name;
           QbeGen.ConvRI(q, qa); QbeGen.CopyOp(qa, q); t := vt
           QbeGen.ConvRI(q, qa); QbeGen.CopyOp(qa, q); t := vt
         ELSIF (c1 = SymTab.ClReal) AND (c2 = SymTab.ClLong) THEN
         ELSIF (c1 = SymTab.ClReal) AND (c2 = SymTab.ClLong) THEN
           QbeGen.ConvRL(q, qa); QbeGen.CopyOp(qa, q); t := vt
           QbeGen.ConvRL(q, qa); QbeGen.CopyOp(qa, q); t := vt
+        ELSIF (c1 = SymTab.ClSet) AND (c2 = SymTab.ClInt) THEN
+          (* a set as its (first) machine word *)
+          QbeGen.LoadW(q, qa); QbeGen.CopyOp(qa, q); t := vt
+        ELSIF (c1 = SymTab.ClInt) AND (c2 = SymTab.ClSet) THEN
+          (* a word as a set value: zero-filled one-word temp *)
+          c2 := SymTab.SetWords(vt);
+          IF c2 = 0 THEN c2 := 1 END;
+          QbeGen.NewSetTemp(VAL(CARDINAL, c2), qa);
+          QbeGen.SetZero(qa, VAL(CARDINAL, c2));
+          QbeGen.StoreW(qa, q); QbeGen.CopyOp(qa, q); t := vt
         ELSE t := vt   (* same representation: no-op *)
         ELSE t := vt   (* same representation: no-op *)
         END
         END
       END
       END

+ 5 - 1
compiler/src/M2.atg

@@ -3773,7 +3773,7 @@ PRODUCTIONS
                                         (. t := SymTab.RealType();
                                         (. t := SymTab.RealType();
    IF QbeGen.IsImm(q) AND SymTab.ConstInt(q, lo) THEN QbeGen.NormReal(q, qa); QbeGen.CopyOp(qa, q)
    IF QbeGen.IsImm(q) AND SymTab.ConstInt(q, lo) THEN QbeGen.NormReal(q, qa); QbeGen.CopyOp(qa, q)
    ELSE QbeGen.CopyOp("@", q) END; .)
    ELSE QbeGen.CopyOp("@", q) END; .)
-    | ( "VAL" | "CONVERT" ) "(" GetIdent<vn> (. astArg2 := AST.MakeLeaf(AST.NkIdent, vn); .) "," Expr<et, q> ")"
+    | ( "VAL" | "CONVERT" | "CAST" ) "(" GetIdent<vn> (. astArg2 := AST.MakeLeaf(AST.NkIdent, vn); .) "," Expr<et, q> ")"
     (. astNode := AST.MakeNode(AST.NkCall);
     (. astNode := AST.MakeNode(AST.NkCall);
    AST.SetChild(astNode, 0, AST.MakeLeaf(AST.NkIdent, "VAL"));
    AST.SetChild(astNode, 0, AST.MakeLeaf(AST.NkIdent, "VAL"));
    IF astArg2 # AST.NoNode THEN AST.SetChild(astNode, 1, astArg2) END;
    IF astArg2 # AST.NoNode THEN AST.SetChild(astNode, 1, astArg2) END;
@@ -3848,6 +3848,10 @@ PRODUCTIONS
                                                ELSIF (c1 = SymTab.ClReal)
                                                ELSIF (c1 = SymTab.ClReal)
  AND (c2 = SymTab.ClReal) THEN
  AND (c2 = SymTab.ClReal) THEN
                                                  t := vt
                                                  t := vt
+                                               ELSIF ((c1 = SymTab.ClSet) AND (c2 = SymTab.ClInt))
+  OR ((c1 = SymTab.ClInt) AND (c2 = SymTab.ClSet)) THEN
+                                                 (* a set <-> its machine word (BITSET) *)
+                                                 t := vt
                                                ELSE SemError(230);
                                                ELSE SemError(230);
                                                  t := SymTab.InvalidType
                                                  t := SymTab.InvalidType
                                                END
                                                END

+ 244 - 240
compiler/src/M2.lst

@@ -3792,7 +3792,7 @@ Listing:
  3773                                          (. t := SymTab.RealType();
  3773                                          (. t := SymTab.RealType();
  3774     IF QbeGen.IsImm(q) AND SymTab.ConstInt(q, lo) THEN QbeGen.NormReal(q, qa); QbeGen.CopyOp(qa, q)
  3774     IF QbeGen.IsImm(q) AND SymTab.ConstInt(q, lo) THEN QbeGen.NormReal(q, qa); QbeGen.CopyOp(qa, q)
  3775     ELSE QbeGen.CopyOp("@", q) END; .)
  3775     ELSE QbeGen.CopyOp("@", q) END; .)
- 3776      | ( "VAL" | "CONVERT" ) "(" GetIdent<vn> (. astArg2 := AST.MakeLeaf(AST.NkIdent, vn); .) "," Expr<et, q> ")"
+ 3776      | ( "VAL" | "CONVERT" | "CAST" ) "(" GetIdent<vn> (. astArg2 := AST.MakeLeaf(AST.NkIdent, vn); .) "," Expr<et, q> ")"
  3777      (. astNode := AST.MakeNode(AST.NkCall);
  3777      (. astNode := AST.MakeNode(AST.NkCall);
  3778     AST.SetChild(astNode, 0, AST.MakeLeaf(AST.NkIdent, "VAL"));
  3778     AST.SetChild(astNode, 0, AST.MakeLeaf(AST.NkIdent, "VAL"));
  3779     IF astArg2 # AST.NoNode THEN AST.SetChild(astNode, 1, astArg2) END;
  3779     IF astArg2 # AST.NoNode THEN AST.SetChild(astNode, 1, astArg2) END;
@@ -3867,244 +3867,248 @@ Listing:
  3848                                                 ELSIF (c1 = SymTab.ClReal)
  3848                                                 ELSIF (c1 = SymTab.ClReal)
  3849   AND (c2 = SymTab.ClReal) THEN
  3849   AND (c2 = SymTab.ClReal) THEN
  3850                                                   t := vt
  3850                                                   t := vt
- 3851                                                 ELSE SemError(230);
- 3852                                                   t := SymTab.InvalidType
- 3853                                                 END
- 3854                                               END
- 3855                                             END; .)
- 3856      | "(" Expr<et, q> ")"               (. t := et; astIsLit := TRUE; .)
- 3857      | SetLit<st, sq>                    (. astIsLit := TRUE; t := st;
- 3858                                             QbeGen.CopyOp(sq, q); .)
- 3859      | ( "NOT" | "~" ) Fact<t2, q2>      (. astNot := astCur;
- 3860                                             IF SymTab.BoolCheck(t2) THEN
- 3861                                               t := SymTab.BoolType()
- 3862                                             ELSE SemError(212);
- 3863                                               t := SymTab.InvalidType END;
- 3864                                             IF t # SymTab.InvalidType THEN
- 3865                                                QbeGen.CopyOp("@", q)
- 3866                                             ELSE QbeGen.CopyOp("0", q)
- 3867                                             END;
- 3868                                             astIsLit := TRUE;
- 3869                                             astCur := AST.MakeUn(
- 3870                                               AST.NkUnary, AST.OpNot, astNot); .)
- 3871      )
- 3872      (. IF NOT astIsLit THEN astCur := AST.NoNode END; .)
- 3873             (. IF astCur # AST.NoNode THEN AST.SetTy(astCur, t) END; .) .
- 3874    (* Set literals are SET OF [0..255] (8 words); elements validated
- 3875       0..255 statically when foldable (222 otherwise), runtime trap
- 3876       for computed elements. Ranges always lower via SetRange. *)
- 3877    SetLit<VAR t: SymTab.TypeIndex; VAR q: QbeGen.QVal>
- 3878                                          (. VAR astNode: AST.Node; .)
- 3879      = "{"                               (. t := SymTab.NewSet(
- 3880                                               SymTab.NewSubR(0, 255));
- 3881                                             astNode := AST.MakeNode(AST.NkSetLit);
- 3882                                             QbeGen.NewSetTemp(8, q);
- 3883                                             QbeGen.SetZero(q, 8); .)
- 3884        [ SetElem<t, q, astNode> { "," SetElem<t, q, astNode> } ]
- 3885        "}"                               (. astCur := astNode; .) .
- 3886    (* Typed brace constructor: TypeName{ elems } — BITSET{0} (a set)
- 3887       or ArrayName{...} (an array constructor, GNU Modula-2).  The
- 3888       declared type sets the width (set) or element type (array). *)
- 3889    TypedBraceLit<vt: SymTab.TypeIndex; VAR q: QbeGen.QVal>
- 3890                                          (. VAR nw: CARDINAL;
- 3891                                               astNode, astTail: AST.Node;
- 3892                                               savedCls: INTEGER; .)
- 3893      = "{"                               (. savedCls := braceCls;
- 3894                                             astNode := AST.MakeNode(AST.NkBraceLit);
- 3895                                             AST.SetTy(astNode, vt);
- 3896                                             astTail := astNode;
- 3897                                             IF vt = SymTab.InvalidType THEN
- 3898                                               braceCls := -1
- 3899                                             ELSE braceCls :=
- 3900                                                    SymTab.ClassOf(vt)
- 3901                                             END;
- 3902                                             IF braceCls = SymTab.ClSet THEN
- 3903                                               IF vt = SymTab.InvalidType THEN
- 3904                                                 nw := 8
- 3905                                               ELSE nw := SymTab.SetWords(vt);
- 3906                                                 IF nw = 0 THEN nw := 8 END
- 3907                                               END;
- 3908                                               QbeGen.NewSetTemp(nw, q);
- 3909                                               QbeGen.SetZero(q, nw)
- 3910                                             ELSIF (braceCls =
- 3911                                                    SymTab.ClArray)
- 3912                                                OR (braceCls =
- 3913                                                    SymTab.ClRecord)
- 3914                                                OR (braceCls =
- 3915                                                    SymTab.ClClass) THEN
- 3916                                               QbeGen.CtorBegin(vt)
- 3917                                             ELSE
- 3918                                               IF vt # SymTab.InvalidType THEN
- 3919                                                 SemError(230) END;
- 3920                                               braceCls := -1
- 3921                                             END; .)
- 3922        [ BraceElem<vt, q, astNode, astTail>
- 3923          { "," BraceElem<vt, q, astNode, astTail> } ]
- 3924        "}"                               (. IF (braceCls = SymTab.ClArray)
- 3925                                                OR (braceCls =
- 3926                                                   SymTab.ClRecord)
- 3927                                                OR (braceCls =
- 3928                                                   SymTab.ClClass) THEN
- 3929                                               QbeGen.CtorEnd(q)
- 3930                                             ELSIF braceCls # SymTab.ClSet THEN
- 3931                                               QbeGen.CopyOp("0", q)
- 3932                                             END;
- 3933                                             braceCls := savedCls; .)
- 3934                                          (. astCur := astNode; .) .
- 3935    BraceElem<vt: SymTab.TypeIndex; VAR sq: QbeGen.QVal;
- 3936              VAR node, tail: AST.Node>
- 3937                                          (. VAR et, et2: SymTab.TypeIndex;
- 3938                                               qe, q2: QbeGen.QVal;
- 3939                                               v, v2, reps, k: INTEGER;
- 3940                                               elem: SymTab.TypeIndex;
- 3941                                               lo: INTEGER;
- 3942                                               span: CARDINAL;
- 3943                                               cl, cl2: INTEGER;
- 3944                                               hasR, hasB: BOOLEAN;
- 3945                                               astEl: AST.Node; .)
- 3946      =                                   (. hasR := FALSE; hasB := FALSE;
- 3947                                             reps := 1; .)
- 3948        Expr<et, qe> (. astEl := astCur; .)
- 3949        [ ".." Expr<et2, q2>              (. astEl := AST.MakeBin(
- 3950                                               AST.NkSubrange, 0, astEl, astCur);
- 3951                                             hasR := TRUE; .) ]
- 3952        [ "BY" Expr<et2, q2>              (. hasB := TRUE; .) ]
- 3953                                          (. IF braceCls = SymTab.ClSet THEN
- 3954                                               IF hasB THEN SemError(230) END;
- 3955                                               lo := SymTab.SetBaseLo(vt);
- 3956                                               span := SymTab.SetCount(vt);
- 3957                                               IF (et = SymTab.InvalidType)
- 3958                                                  OR (hasR AND (et2 =
- 3959                                                     SymTab.InvalidType)) THEN
- 3960                                               ELSE cl :=
- 3961                                                      SymTab.ClassOf(et);
- 3962                                                 IF hasR THEN
- 3963                                                   cl2 :=
- 3964                                                     SymTab.ClassOf(et2)
- 3965                                                 ELSE cl2 := SymTab.ClInt
- 3966                                                 END;
- 3967                                                 IF NOT SymTab.SetElemClassOk(cl)
- 3968                                                    OR (hasR AND NOT
- 3969                                                       SymTab.SetElemClassOk(cl2))
- 3970                                                 THEN
- 3971                                                   SemError(222)
- 3972                                                 ELSIF hasR
- 3973   AND SymTab.ConstInt(qe, v)
- 3974   AND SymTab.ConstInt(q2,
- 3975                                                       v2)
- 3976   AND ((v < lo)
- 3977                                                       OR (v2 < lo)
- 3978                                                       OR (v >= lo +
- 3979                                                          VAL(INTEGER, span))
- 3980                                                       OR (v2 >= lo +
- 3981                                                          VAL(INTEGER, span))
- 3982                                                       OR (v > v2)) THEN
- 3983                                                   SemError(222)
- 3984                                                  ELSIF hasR THEN
- 3985                                                    QbeGen.SetRange(sq, qe, q2,
- 3986                                                      lo, span)
- 3987                                                  ELSIF SymTab.ConstInt(qe,
- 3988                                                          v)
- 3989   AND ((v < lo)
- 3990                                                        OR (v >= lo +
- 3991                                                           VAL(INTEGER,
- 3992                                                             span))) THEN
- 3993                                                    SemError(222)
- 3994                                                  ELSE QbeGen.SetBit(sq, qe,
- 3995                                                    lo, span)
- 3996                                                 END
- 3997                                               END
- 3998                                             ELSIF (braceCls = SymTab.ClArray)
- 3999                                                OR (braceCls =
- 4000                                                   SymTab.ClRecord)
- 4001                                                OR (braceCls =
- 4002                                                   SymTab.ClClass) THEN
- 4003                                               IF hasR THEN SemError(230) END;
- 4004                                               reps := 1;
- 4005                                               IF hasB THEN
- 4006                                                 IF SymTab.ConstInt(q2, v2)
- 4007   AND (v2 >= 1) THEN
- 4008                                                   reps := v2
- 4009                                                 ELSE SemError(230)
- 4010                                                 END
- 4011                                               END;
- 4012                                               k := 0;
- 4013                                               WHILE k < reps DO
- 4014                                                 QbeGen.CtorElem(qe);
- 4015                                                 INC(k)
- 4016                                               END
- 4017                                             END; .)
- 4018                                          (. (* the BY form repeats the
- 4019                                               element; keep the AST in
- 4020                                               step with CtorElem *)
- 4021                                             k := 0;
- 4022                                             WHILE k < reps DO
- 4023                                               AstAppend(AST.NkBlock,
- 4024                                                 node, tail, astEl);
- 4025                                               INC(k)
- 4026                                             END; .) .
- 4027    SetElem<st: SymTab.TypeIndex; sq: QbeGen.QVal; node: AST.Node>         (. VAR et, et2: SymTab.TypeIndex;
- 4028                                               qe, q2: QbeGen.QVal;
- 4029                                               v, v2: INTEGER;
- 4030                                               lo: INTEGER;
- 4031                                               span: CARDINAL;
- 4032                                               cl, cl2: INTEGER;
- 4033                                               hasR: BOOLEAN;
- 4034                                               astEl: AST.Node; .)
- 4035      =                                   (. hasR := FALSE; .)
- 4036        Expr<et, qe> (. astEl := astCur; .)
- 4037        [ ".." Expr<et2, q2>              (. astEl := AST.MakeBin(
- 4038                                               AST.NkSubrange, 0, astEl, astCur);
- 4039                                             hasR := TRUE; .) ]
- 4040                                          (. lo := SymTab.SetBaseLo(st);
- 4041                                             span := SymTab.SetCount(st);
- 4042                                             IF (et = SymTab.InvalidType)
- 4043                                                OR (hasR AND (et2 =
- 4044                                                   SymTab.InvalidType)) THEN
- 4045                                             ELSE cl :=
- 4046                                                    SymTab.ClassOf(et);
- 4047                                               IF hasR THEN
- 4048                                                 cl2 :=
- 4049                                                   SymTab.ClassOf(et2)
- 4050                                               ELSE cl2 := SymTab.ClInt
- 4051                                               END;
- 4052                                               IF NOT SymTab.SetElemClassOk(cl)
- 4053                                                  OR (hasR AND NOT
- 4054                                                     SymTab.SetElemClassOk(cl2))
- 4055                                               THEN
- 4056                                                 SemError(222)
- 4057                                               ELSIF hasR
- 4058   AND SymTab.ConstInt(qe, v)
- 4059   AND SymTab.ConstInt(q2,
- 4060                                                     v2)
- 4061   AND ((v < lo)
- 4062                                                     OR (v2 < lo)
- 4063                                                     OR (v >= lo +
- 4064                                                        VAL(INTEGER, span))
- 4065                                                     OR (v2 >= lo +
- 4066                                                        VAL(INTEGER, span))
- 4067                                                     OR (v > v2)) THEN
- 4068                                                 SemError(222)
- 4069                                                ELSIF hasR THEN
- 4070                                                  QbeGen.SetRange(sq, qe, q2,
- 4071                                                    lo, span)
- 4072                                                ELSIF SymTab.ConstInt(qe,
- 4073                                                        v)
- 4074   AND ((v < lo)
- 4075                                                      OR (v >= lo +
- 4076                                                         VAL(INTEGER,
- 4077                                                           span))) THEN
- 4078                                                  SemError(222)
- 4079                                                ELSE QbeGen.SetBit(sq, qe,
- 4080                                                  lo, span)
- 4081                                               END
- 4082                                             END; .)
- 4083                                          (. AST.SetChild(node,
- 4084                                               AST.NChild(node), astEl); .) .
- 4085    GetIdent<VAR n: SymTab.Name>
- 4086      = ident                             (. LexName(n); .) .
- 4087  
- 4088  END M2.
+ 3851                                                 ELSIF ((c1 = SymTab.ClSet) AND (c2 = SymTab.ClInt))
+ 3852    OR ((c1 = SymTab.ClInt) AND (c2 = SymTab.ClSet)) THEN
+ 3853                                                   (* a set <-> its machine word (BITSET) *)
+ 3854                                                   t := vt
+ 3855                                                 ELSE SemError(230);
+ 3856                                                   t := SymTab.InvalidType
+ 3857                                                 END
+ 3858                                               END
+ 3859                                             END; .)
+ 3860      | "(" Expr<et, q> ")"               (. t := et; astIsLit := TRUE; .)
+ 3861      | SetLit<st, sq>                    (. astIsLit := TRUE; t := st;
+ 3862                                             QbeGen.CopyOp(sq, q); .)
+ 3863      | ( "NOT" | "~" ) Fact<t2, q2>      (. astNot := astCur;
+ 3864                                             IF SymTab.BoolCheck(t2) THEN
+ 3865                                               t := SymTab.BoolType()
+ 3866                                             ELSE SemError(212);
+ 3867                                               t := SymTab.InvalidType END;
+ 3868                                             IF t # SymTab.InvalidType THEN
+ 3869                                                QbeGen.CopyOp("@", q)
+ 3870                                             ELSE QbeGen.CopyOp("0", q)
+ 3871                                             END;
+ 3872                                             astIsLit := TRUE;
+ 3873                                             astCur := AST.MakeUn(
+ 3874                                               AST.NkUnary, AST.OpNot, astNot); .)
+ 3875      )
+ 3876      (. IF NOT astIsLit THEN astCur := AST.NoNode END; .)
+ 3877             (. IF astCur # AST.NoNode THEN AST.SetTy(astCur, t) END; .) .
+ 3878    (* Set literals are SET OF [0..255] (8 words); elements validated
+ 3879       0..255 statically when foldable (222 otherwise), runtime trap
+ 3880       for computed elements. Ranges always lower via SetRange. *)
+ 3881    SetLit<VAR t: SymTab.TypeIndex; VAR q: QbeGen.QVal>
+ 3882                                          (. VAR astNode: AST.Node; .)
+ 3883      = "{"                               (. t := SymTab.NewSet(
+ 3884                                               SymTab.NewSubR(0, 255));
+ 3885                                             astNode := AST.MakeNode(AST.NkSetLit);
+ 3886                                             QbeGen.NewSetTemp(8, q);
+ 3887                                             QbeGen.SetZero(q, 8); .)
+ 3888        [ SetElem<t, q, astNode> { "," SetElem<t, q, astNode> } ]
+ 3889        "}"                               (. astCur := astNode; .) .
+ 3890    (* Typed brace constructor: TypeName{ elems } — BITSET{0} (a set)
+ 3891       or ArrayName{...} (an array constructor, GNU Modula-2).  The
+ 3892       declared type sets the width (set) or element type (array). *)
+ 3893    TypedBraceLit<vt: SymTab.TypeIndex; VAR q: QbeGen.QVal>
+ 3894                                          (. VAR nw: CARDINAL;
+ 3895                                               astNode, astTail: AST.Node;
+ 3896                                               savedCls: INTEGER; .)
+ 3897      = "{"                               (. savedCls := braceCls;
+ 3898                                             astNode := AST.MakeNode(AST.NkBraceLit);
+ 3899                                             AST.SetTy(astNode, vt);
+ 3900                                             astTail := astNode;
+ 3901                                             IF vt = SymTab.InvalidType THEN
+ 3902                                               braceCls := -1
+ 3903                                             ELSE braceCls :=
+ 3904                                                    SymTab.ClassOf(vt)
+ 3905                                             END;
+ 3906                                             IF braceCls = SymTab.ClSet THEN
+ 3907                                               IF vt = SymTab.InvalidType THEN
+ 3908                                                 nw := 8
+ 3909                                               ELSE nw := SymTab.SetWords(vt);
+ 3910                                                 IF nw = 0 THEN nw := 8 END
+ 3911                                               END;
+ 3912                                               QbeGen.NewSetTemp(nw, q);
+ 3913                                               QbeGen.SetZero(q, nw)
+ 3914                                             ELSIF (braceCls =
+ 3915                                                    SymTab.ClArray)
+ 3916                                                OR (braceCls =
+ 3917                                                    SymTab.ClRecord)
+ 3918                                                OR (braceCls =
+ 3919                                                    SymTab.ClClass) THEN
+ 3920                                               QbeGen.CtorBegin(vt)
+ 3921                                             ELSE
+ 3922                                               IF vt # SymTab.InvalidType THEN
+ 3923                                                 SemError(230) END;
+ 3924                                               braceCls := -1
+ 3925                                             END; .)
+ 3926        [ BraceElem<vt, q, astNode, astTail>
+ 3927          { "," BraceElem<vt, q, astNode, astTail> } ]
+ 3928        "}"                               (. IF (braceCls = SymTab.ClArray)
+ 3929                                                OR (braceCls =
+ 3930                                                   SymTab.ClRecord)
+ 3931                                                OR (braceCls =
+ 3932                                                   SymTab.ClClass) THEN
+ 3933                                               QbeGen.CtorEnd(q)
+ 3934                                             ELSIF braceCls # SymTab.ClSet THEN
+ 3935                                               QbeGen.CopyOp("0", q)
+ 3936                                             END;
+ 3937                                             braceCls := savedCls; .)
+ 3938                                          (. astCur := astNode; .) .
+ 3939    BraceElem<vt: SymTab.TypeIndex; VAR sq: QbeGen.QVal;
+ 3940              VAR node, tail: AST.Node>
+ 3941                                          (. VAR et, et2: SymTab.TypeIndex;
+ 3942                                               qe, q2: QbeGen.QVal;
+ 3943                                               v, v2, reps, k: INTEGER;
+ 3944                                               elem: SymTab.TypeIndex;
+ 3945                                               lo: INTEGER;
+ 3946                                               span: CARDINAL;
+ 3947                                               cl, cl2: INTEGER;
+ 3948                                               hasR, hasB: BOOLEAN;
+ 3949                                               astEl: AST.Node; .)
+ 3950      =                                   (. hasR := FALSE; hasB := FALSE;
+ 3951                                             reps := 1; .)
+ 3952        Expr<et, qe> (. astEl := astCur; .)
+ 3953        [ ".." Expr<et2, q2>              (. astEl := AST.MakeBin(
+ 3954                                               AST.NkSubrange, 0, astEl, astCur);
+ 3955                                             hasR := TRUE; .) ]
+ 3956        [ "BY" Expr<et2, q2>              (. hasB := TRUE; .) ]
+ 3957                                          (. IF braceCls = SymTab.ClSet THEN
+ 3958                                               IF hasB THEN SemError(230) END;
+ 3959                                               lo := SymTab.SetBaseLo(vt);
+ 3960                                               span := SymTab.SetCount(vt);
+ 3961                                               IF (et = SymTab.InvalidType)
+ 3962                                                  OR (hasR AND (et2 =
+ 3963                                                     SymTab.InvalidType)) THEN
+ 3964                                               ELSE cl :=
+ 3965                                                      SymTab.ClassOf(et);
+ 3966                                                 IF hasR THEN
+ 3967                                                   cl2 :=
+ 3968                                                     SymTab.ClassOf(et2)
+ 3969                                                 ELSE cl2 := SymTab.ClInt
+ 3970                                                 END;
+ 3971                                                 IF NOT SymTab.SetElemClassOk(cl)
+ 3972                                                    OR (hasR AND NOT
+ 3973                                                       SymTab.SetElemClassOk(cl2))
+ 3974                                                 THEN
+ 3975                                                   SemError(222)
+ 3976                                                 ELSIF hasR
+ 3977   AND SymTab.ConstInt(qe, v)
+ 3978   AND SymTab.ConstInt(q2,
+ 3979                                                       v2)
+ 3980   AND ((v < lo)
+ 3981                                                       OR (v2 < lo)
+ 3982                                                       OR (v >= lo +
+ 3983                                                          VAL(INTEGER, span))
+ 3984                                                       OR (v2 >= lo +
+ 3985                                                          VAL(INTEGER, span))
+ 3986                                                       OR (v > v2)) THEN
+ 3987                                                   SemError(222)
+ 3988                                                  ELSIF hasR THEN
+ 3989                                                    QbeGen.SetRange(sq, qe, q2,
+ 3990                                                      lo, span)
+ 3991                                                  ELSIF SymTab.ConstInt(qe,
+ 3992                                                          v)
+ 3993   AND ((v < lo)
+ 3994                                                        OR (v >= lo +
+ 3995                                                           VAL(INTEGER,
+ 3996                                                             span))) THEN
+ 3997                                                    SemError(222)
+ 3998                                                  ELSE QbeGen.SetBit(sq, qe,
+ 3999                                                    lo, span)
+ 4000                                                 END
+ 4001                                               END
+ 4002                                             ELSIF (braceCls = SymTab.ClArray)
+ 4003                                                OR (braceCls =
+ 4004                                                   SymTab.ClRecord)
+ 4005                                                OR (braceCls =
+ 4006                                                   SymTab.ClClass) THEN
+ 4007                                               IF hasR THEN SemError(230) END;
+ 4008                                               reps := 1;
+ 4009                                               IF hasB THEN
+ 4010                                                 IF SymTab.ConstInt(q2, v2)
+ 4011   AND (v2 >= 1) THEN
+ 4012                                                   reps := v2
+ 4013                                                 ELSE SemError(230)
+ 4014                                                 END
+ 4015                                               END;
+ 4016                                               k := 0;
+ 4017                                               WHILE k < reps DO
+ 4018                                                 QbeGen.CtorElem(qe);
+ 4019                                                 INC(k)
+ 4020                                               END
+ 4021                                             END; .)
+ 4022                                          (. (* the BY form repeats the
+ 4023                                               element; keep the AST in
+ 4024                                               step with CtorElem *)
+ 4025                                             k := 0;
+ 4026                                             WHILE k < reps DO
+ 4027                                               AstAppend(AST.NkBlock,
+ 4028                                                 node, tail, astEl);
+ 4029                                               INC(k)
+ 4030                                             END; .) .
+ 4031    SetElem<st: SymTab.TypeIndex; sq: QbeGen.QVal; node: AST.Node>         (. VAR et, et2: SymTab.TypeIndex;
+ 4032                                               qe, q2: QbeGen.QVal;
+ 4033                                               v, v2: INTEGER;
+ 4034                                               lo: INTEGER;
+ 4035                                               span: CARDINAL;
+ 4036                                               cl, cl2: INTEGER;
+ 4037                                               hasR: BOOLEAN;
+ 4038                                               astEl: AST.Node; .)
+ 4039      =                                   (. hasR := FALSE; .)
+ 4040        Expr<et, qe> (. astEl := astCur; .)
+ 4041        [ ".." Expr<et2, q2>              (. astEl := AST.MakeBin(
+ 4042                                               AST.NkSubrange, 0, astEl, astCur);
+ 4043                                             hasR := TRUE; .) ]
+ 4044                                          (. lo := SymTab.SetBaseLo(st);
+ 4045                                             span := SymTab.SetCount(st);
+ 4046                                             IF (et = SymTab.InvalidType)
+ 4047                                                OR (hasR AND (et2 =
+ 4048                                                   SymTab.InvalidType)) THEN
+ 4049                                             ELSE cl :=
+ 4050                                                    SymTab.ClassOf(et);
+ 4051                                               IF hasR THEN
+ 4052                                                 cl2 :=
+ 4053                                                   SymTab.ClassOf(et2)
+ 4054                                               ELSE cl2 := SymTab.ClInt
+ 4055                                               END;
+ 4056                                               IF NOT SymTab.SetElemClassOk(cl)
+ 4057                                                  OR (hasR AND NOT
+ 4058                                                     SymTab.SetElemClassOk(cl2))
+ 4059                                               THEN
+ 4060                                                 SemError(222)
+ 4061                                               ELSIF hasR
+ 4062   AND SymTab.ConstInt(qe, v)
+ 4063   AND SymTab.ConstInt(q2,
+ 4064                                                     v2)
+ 4065   AND ((v < lo)
+ 4066                                                     OR (v2 < lo)
+ 4067                                                     OR (v >= lo +
+ 4068                                                        VAL(INTEGER, span))
+ 4069                                                     OR (v2 >= lo +
+ 4070                                                        VAL(INTEGER, span))
+ 4071                                                     OR (v > v2)) THEN
+ 4072                                                 SemError(222)
+ 4073                                                ELSIF hasR THEN
+ 4074                                                  QbeGen.SetRange(sq, qe, q2,
+ 4075                                                    lo, span)
+ 4076                                                ELSIF SymTab.ConstInt(qe,
+ 4077                                                        v)
+ 4078   AND ((v < lo)
+ 4079                                                      OR (v >= lo +
+ 4080                                                         VAL(INTEGER,
+ 4081                                                           span))) THEN
+ 4082                                                  SemError(222)
+ 4083                                                ELSE QbeGen.SetBit(sq, qe,
+ 4084                                                  lo, span)
+ 4085                                               END
+ 4086                                             END; .)
+ 4087                                          (. AST.SetChild(node,
+ 4088                                               AST.NChild(node), astEl); .) .
+ 4089    GetIdent<VAR n: SymTab.Name>
+ 4090      = ident                             (. LexName(n); .) .
+ 4091  
+ 4092  END M2.
 
 
     0 errors
     0 errors
 
 
@@ -4115,7 +4119,7 @@ Statistics:
   nr of non-terminals:    90 (limit   210)
   nr of non-terminals:    90 (limit   210)
   nr of pragmas:           0 (limit   387)
   nr of pragmas:           0 (limit   387)
   nr of symbolnodes:     203 (limit   500)
   nr of symbolnodes:     203 (limit   500)
-  nr of graphnodes:     1292 (limit  1500)
+  nr of graphnodes:     1294 (limit  1500)
   nr of conditionsets:    12 (limit   100)
   nr of conditionsets:    12 (limit   100)
   nr of charactersets:    16 (limit   250)
   nr of charactersets:    16 (limit   250)
 
 

+ 13 - 0
compiler/tests/t_setword.mod

@@ -0,0 +1,13 @@
+MODULE TSetWord;
+// VAL/CAST between a set and its machine word (BITSET <-> CARDINAL).
+// Exit 42.
+VAR b : BITSET;
+    c : CARDINAL;
+    ExitCode : INTEGER;
+BEGIN
+  b := BITSET{1, 2};
+  c := VAL(CARDINAL, b);          (* 6 *)
+  b := CAST(BITSET, c);           (* {1,2} *)
+  IF (c = 6) AND (b = BITSET{1, 2})
+  THEN ExitCode := 42 ELSE ExitCode := 1 END
+END TSetWord.

+ 7 - 6
docs/STATUS.md

@@ -4,10 +4,10 @@ This answers "where are we now?".
 
 
 ## Numbers
 ## Numbers
 
 
-- Suite **235/235** (`compiler/run_tests.sh`).
+- Suite **236/236** (`compiler/run_tests.sh`).
 - Self-hosting **FIXPOINT OK** — stage2 and stage3 images are
 - Self-hosting **FIXPOINT OK** — stage2 and stage3 images are
-  byte-identical (3,854,595 bytes).
-- Corpus **389 / 612** sources compile with 0 errors, **0 crashes**
+  byte-identical (3,858,265 bytes).
+- Corpus **392 / 612** sources compile with 0 errors, **0 crashes**
   (`tools/v3-corpus/corpus.sh` over the gm2 testsuite); the rest are
   (`tools/v3-corpus/corpus.sh` over the gm2 testsuite); the rest are
   genuine language gaps, **gm2 leniencies V3 deliberately rejects**, or
   genuine language gaps, **gm2 leniencies V3 deliberately rejects**, or
   unresolved imports.
   unresolved imports.
@@ -66,8 +66,9 @@ parameters, constraints, nested generics, generic classes.
   reals (`REAL32`/`REAL64`), `FROM SYSTEM IMPORT SHIFT`/`ROTATE`/`CAST`/…
   reals (`REAL32`/`REAL64`), `FROM SYSTEM IMPORT SHIFT`/`ROTATE`/`CAST`/…
   (a builtin import is accepted), and ISO foreign modules
   (a builtin import is accepted), and ISO foreign modules
   (`DEFINITION/IMPLEMENTATION MODULE FOR "C"`, `EXPORT UNQUALIFIED`),
   (`DEFINITION/IMPLEMENTATION MODULE FOR "C"`, `EXPORT UNQUALIFIED`),
-  and ISO `CONVERT(T, x)` (a `VAL` sibling): corpus 382 → 389.
-  Fixed a `SHIFT`-by-a-negative-amount fault (wild word load).
+  and ISO `CONVERT(T, x)` (a `VAL` sibling) and set↔word `VAL`/`CAST`
+  (BITSET): corpus 382 → 392.  Fixed a `SHIFT`-by-a-negative-amount
+  fault (wild word load).
 - Clean `M2` exit codes (0 ok / 1 errors / 2 usage-IO).
 - Clean `M2` exit codes (0 ok / 1 errors / 2 usage-IO).
 - Bug fixes: `PopPos` wiping repass diagnostics; global record data
 - Bug fixes: `PopPos` wiping repass diagnostics; global record data
   size (`w 0` → `l 0` for pointer/`LONGINT`/proc fields); template
   size (`w 0` → `l 0` for pointer/`LONGINT`/proc fields); template
@@ -114,7 +115,7 @@ is the 10 gm2-leniency files now correctly rejected).
 ```sh
 ```sh
 cd compiler
 cd compiler
 ./build.sh          # M2.atg -> scanner/parser/driver -> ./M2
 ./build.sh          # M2.atg -> scanner/parser/driver -> ./M2
-./run_tests.sh      # 235/235
+./run_tests.sh      # 236/236
 cd ..
 cd ..
 ./bootstrap/fixpoint.sh   # FIXPOINT OK (byte-identical stage2/stage3)
 ./bootstrap/fixpoint.sh   # FIXPOINT OK (byte-identical stage2/stage3)
 ```
 ```

+ 10 - 7
docs/analysis_gaps.md

@@ -255,16 +255,19 @@ parse) are a separate, larger item still retained.
 
 
 **Track B (genuine gaps), 2026-10-18:** **`CONVERT(T, x)`** (ISO SYSTEM)
 **Track B (genuine gaps), 2026-10-18:** **`CONVERT(T, x)`** (ISO SYSTEM)
 landed — the `VAL` production now accepts `( "VAL" | "CONVERT" )`, so
 landed — the `VAL` production now accepts `( "VAL" | "CONVERT" )`, so
-`procconv2` passes and runs.  Corpus **388 -> 389**; suite test
-`t_convert`; `docs/gaps/gap07` retired.
+`procconv2` passes and runs.  Then **set↔word `VAL`/`CAST`** (the `VAL`
+production also takes `CAST`): set→`CARDINAL` loads word 0, and
+`CARDINAL`→set builds a zeroed one-word set temp; `constset`,
+`constset2`, `constset3` pass.  Corpus **388 -> 392**; suite tests
+`t_convert`, `t_setword`; `docs/gaps/gap07`/`gap08` retired.
 
 
 Next (recorded, genuine only): **`gap13` builtins as identifiers** (a V3
 Next (recorded, genuine only): **`gap13` builtins as identifiers** (a V3
 bug; needs de-reserving the builtin keyword tokens — a parser refactor);
 bug; needs de-reserving the builtin keyword tokens — a parser refactor);
-**`gap08` set↔word `CAST`/`VAL`** (`packed`); **`gap15` enumeration
-literals with an imported type** (`imp*`); `aochar`/`arrayofbyte`/
-`bytearray` (scalar actual into `ARRAY OF BYTE`); variant empty arms /
-`ELSE`; set-span limits.  The forward-type items above are gm2
-leniency, not targets.
+**`gap15` enumeration literals with an imported type** (`imp*`);
+`aochar`/`arrayofbyte`/`bytearray` (scalar actual into `ARRAY OF BYTE`);
+`packed` (`__FILE__`/`__LINE__`, gm2-ext); variant empty arms / `ELSE`;
+set-span limits.  The forward-type items above are gm2 leniency, not
+targets.
 
 
 Every gap above has a self-contained reproducer in `docs/gaps/`
 Every gap above has a self-contained reproducer in `docs/gaps/`
 (`docs/gaps/README.md` indexes them by tag, with a **class**:
 (`docs/gaps/README.md` indexes them by tag, with a **class**:

+ 5 - 4
docs/gaps/README.md

@@ -50,7 +50,6 @@ The `class` column says what a change would mean:
 | `gap04-fwd-const-bound` | `gap04_fwd_const_bound.mod` | `decl-order` | `undeclared identifier` | `setconst3` |
 | `gap04-fwd-const-bound` | `gap04_fwd_const_bound.mod` | `decl-order` | `undeclared identifier` | `setconst3` |
 | `gap05-fwd-type-size` | `gap05_fwd_type_size.mod` | `decl-order` | `undeclared identifier` | `constsize2` |
 | `gap05-fwd-type-size` | `gap05_fwd_type_size.mod` | `decl-order` | `undeclared identifier` | `constsize2` |
 | `gap06-fwd-setconst` | `gap06_fwd_setconst.mod` | `decl-order` | `undeclared identifier` | `setoverflow` |
 | `gap06-fwd-setconst` | `gap06_fwd_setconst.mod` | `decl-order` | `undeclared identifier` | `setoverflow` |
-| `gap08-set-word-cast` | `gap08_val_cast_set_word.mod` | `iso/system` | `not supported yet` | `constset`, `constset2`, `constset3`, `packed` |
 | `gap09-scalar-to-bytearray` | `gap09_scalar_to_bytearray.mod` | `gm2-ext` | `invalid call` | `aochar`, `arrayofbyte`, `bytearray` |
 | `gap09-scalar-to-bytearray` | `gap09_scalar_to_bytearray.mod` | `gm2-ext` | `invalid call` | `aochar`, `arrayofbyte`, `bytearray` |
 | `gap10-var-param-type` | `gap10_var_param_type.mod` | `gm2-ext` | `invalid call` | `testparam2`, `testparam3` |
 | `gap10-var-param-type` | `gap10_var_param_type.mod` | `gm2-ext` | `invalid call` | `testparam2`, `testparam3` |
 | `gap11-bare-proc-stmt` | `gap11_bare_proc_stmt.mod` | `gm2-ext` | `invalid call` | `tinyproc2`, `tinyproc4` |
 | `gap11-bare-proc-stmt` | `gap11_bare_proc_stmt.mod` | `gm2-ext` | `invalid call` | `tinyproc2`, `tinyproc4` |
@@ -65,6 +64,7 @@ The `class` column says what a change would mean:
 | `gap19-fwd-array-index` | `gap19_fwd_array_index.mod` | `decl-order` | `not supported yet` | `record7`, `complextypes` |
 | `gap19-fwd-array-index` | `gap19_fwd_array_index.mod` | `decl-order` | `not supported yet` | `record7`, `complextypes` |
 | `gap20-fwd-set-of-type` | `gap20_fwd_set_of_type.mod` | `decl-order` | `undeclared identifier` | `tinyset6`, `tinyset7` |
 | `gap20-fwd-set-of-type` | `gap20_fwd_set_of_type.mod` | `decl-order` | `undeclared identifier` | `tinyset6`, `tinyset7` |
 | `gap21-fwd-record-field` | `gap21_fwd_record_field.mod` | `decl-order` | `undeclared identifier` | `record14.def`, `record15.def`, `record16.def` |
 | `gap21-fwd-record-field` | `gap21_fwd_record_field.mod` | `decl-order` | `undeclared identifier` | `record14.def`, `record15.def`, `record16.def` |
+| `gap22-file-line` | `gap22_file_line.mod` | `gm2-ext` | `invalid Fact` | `packed` |
 
 
 First diagnostic (message) per file at commit time (line:col varies as
 First diagnostic (message) per file at commit time (line:col varies as
 comments change):
 comments change):
@@ -76,7 +76,6 @@ gap03_fwd_type_heading.mod      undeclared identifier   (large)
 gap04_fwd_const_bound.mod       undeclared identifier   (n)
 gap04_fwd_const_bound.mod       undeclared identifier   (n)
 gap05_fwd_type_size.mod         undeclared identifier   (t)
 gap05_fwd_type_size.mod         undeclared identifier   (t)
 gap06_fwd_setconst.mod          undeclared identifier   (CharSet / tab)
 gap06_fwd_setconst.mod          undeclared identifier   (CharSet / tab)
-gap08_val_cast_set_word.mod     not supported yet       (VAL of a set)
 gap09_scalar_to_bytearray.mod   invalid call            (scalar -> ARRAY OF BYTE)
 gap09_scalar_to_bytearray.mod   invalid call            (scalar -> ARRAY OF BYTE)
 gap10_var_param_type.mod        invalid call            (VAR actual type)
 gap10_var_param_type.mod        invalid call            (VAR actual type)
 gap11_bare_proc_stmt.mod        invalid call            (bare proc value)
 gap11_bare_proc_stmt.mod        invalid call            (bare proc value)
@@ -91,14 +90,16 @@ gap18_set_case_labels.mod       not supported yet       (set as CASE selector)
 gap19_fwd_array_index.mod       not supported yet       (array index declared later)
 gap19_fwd_array_index.mod       not supported yet       (array index declared later)
 gap20_fwd_set_of_type.mod       undeclared identifier   (SET OF base declared later)
 gap20_fwd_set_of_type.mod       undeclared identifier   (SET OF base declared later)
 gap21_fwd_record_field.mod      undeclared identifier   (record field type declared later)
 gap21_fwd_record_field.mod      undeclared identifier   (record field type declared later)
+gap22_file_line.mod             invalid Fact            (__LINE__)
 ```
 ```
 
 
 ## Genuine gaps (worth fixing, standard Modula-2)
 ## Genuine gaps (worth fixing, standard Modula-2)
 
 
 - **`gap07` CONVERT — fixed** (2026-10-18): `CONVERT(T, x)` is accepted as
 - **`gap07` CONVERT — fixed** (2026-10-18): `CONVERT(T, x)` is accepted as
   a `VAL` sibling (`procconv2` passes).
   a `VAL` sibling (`procconv2` passes).
-- **`gap08` set↔word CAST/VAL** — BITSET has a machine-word
-  representation in Modula-2.
+- **`gap08` set↔word `CAST`/`VAL` — fixed** (2026-10-18): `VAL`/`CAST`
+  between a set and its machine word (`constset`, `constset2`,
+  `constset3` pass).  `packed` still needs `gap22` (`__FILE__`/`__LINE__`).
 - **`gap13` builtins as identifiers** — `ORD`, `CHR`, `VAL`, `ORDL`, …
 - **`gap13` builtins as identifiers** — `ORD`, `CHR`, `VAL`, `ORDL`, …
   are *predeclared identifiers, not reserved words*, so V3 must accept
   are *predeclared identifiers, not reserved words*, so V3 must accept
   them as user names.  (This is a V3 deviation, not a corpus gap.)
   them as user names.  (This is a V3 deviation, not a corpus gap.)

+ 0 - 9
docs/gaps/gap08_val_cast_set_word.mod

@@ -1,9 +0,0 @@
-MODULE Gap08ValCastSetWord;
-(* Modula-2 gap (ISO SYSTEM): a set is represented as an address, so
-   VAL/CAST between a set (BITSET) and CARDINAL is unsupported.  In
-   Modula-2 a BITSET has a machine-word representation, so the
-   conversion is well-defined.  Class: iso/system.
-   Corpus: constset, constset2, constset3, packed.
-   V3: not supported yet. *)
-VAR c : CARDINAL;
-BEGIN c := VAL(CARDINAL, BITSET{0}) END Gap08ValCastSetWord.

+ 10 - 0
docs/gaps/gap22_file_line.mod

@@ -0,0 +1,10 @@
+MODULE Gap22FileLine;
+(* NOT standard Modula-2: __FILE__ / __LINE__ predefined constants (a
+   gm2 extension; classic identifiers cannot contain '_').  Class:
+   gm2-ext.
+   Corpus: packed.
+   V3: invalid Fact (`_` is not a legal identifier character). *)
+VAR n : CARDINAL;
+BEGIN
+  n := __LINE__
+END Gap22FileLine.

+ 14 - 5
docs/summary_set-constants-2026-10-18.md

@@ -1,12 +1,12 @@
 # Session summary — SET CONST + builtin CONST folding 2026-10-18
 # Session summary — SET CONST + builtin CONST folding 2026-10-18
 
 
-Branch **`resolve-check`**.  Suite **235/235**; fixpoint **OK
-(3,854,595 bytes)**; corpus **389/612 compile-OK, 0 crashes**.
+Branch **`resolve-check`**.  Suite **236/236**; fixpoint **OK
+(3,858,265 bytes)**; corpus **392/612 compile-OK, 0 crashes**.
 
 
 Tags: `v3-set-constants`, `v3-const-builtin-fold`, `v3-const-conv-fold`,
 Tags: `v3-set-constants`, `v3-const-builtin-fold`, `v3-const-conv-fold`,
 `v3-proc-addr`, `v3-proc-val`, `v3-real64`, `v3-shift-import`,
 `v3-proc-addr`, `v3-proc-val`, `v3-real64`, `v3-shift-import`,
 `v3-foreign-module`, `v3-m2-conformance`, `v3-strict-decl-order`,
 `v3-foreign-module`, `v3-m2-conformance`, `v3-strict-decl-order`,
-`v3-convert`, `v3-gap-examples`.
+`v3-convert`, `v3-set-word`, `v3-gap-examples`.
 
 
 ## Conformance note
 ## Conformance note
 
 
@@ -162,6 +162,15 @@ grammar's `VAL` production takes `( "VAL" | "CONVERT" )`, so it reuses
 `VAL`'s conversion rules; `procconv2` passes and runs.  Corpus
 `VAL`'s conversion rules; `procconv2` passes and runs.  Corpus
 **388 → 389**; suite test `t_convert`, `docs/gaps/gap07` retired.
 **388 → 389**; suite test `t_convert`, `docs/gaps/gap07` retired.
 
 
+## Track B: set↔word VAL/CAST
+
+`VAL`/`CAST` between a set and its machine word are now accepted: set→
+`CARDINAL` loads word 0 of the set, and `CARDINAL`→set builds a zeroed
+one-word set temp holding the value (the grammar's `VAL` production also
+takes `CAST`).  `constset`, `constset2`, `constset3` pass; corpus
+**389 → 392**; suite test `t_setword`, `docs/gaps/gap08` retired.
+`packed` still needs `__FILE__`/`__LINE__` (`gap22`).
+
 ## Remaining genuine gaps (Track B)
 ## Remaining genuine gaps (Track B)
 
 
 - **`gap13` builtins as identifiers** — a real V3 bug, but the builtins
 - **`gap13` builtins as identifiers** — a real V3 bug, but the builtins
@@ -210,7 +219,7 @@ modules),
 ```sh
 ```sh
 cd .../m2compiler-V3
 cd .../m2compiler-V3
 git checkout resolve-check
 git checkout resolve-check
-cd compiler && ./build.sh && ./run_tests.sh   # 235/235
+cd compiler && ./build.sh && ./run_tests.sh   # 236/236
 cd .. && TMP="$PWD/.scratch/fixpoint" ./bootstrap/fixpoint.sh
 cd .. && TMP="$PWD/.scratch/fixpoint" ./bootstrap/fixpoint.sh
-M2FLAGS="" ./.scratch/corpusx.sh              # 389 OK, 0 crash
+M2FLAGS="" ./.scratch/corpusx.sh              # 392 OK, 0 crash
 ```
 ```