Explorar o código

language: fold representation-changing CONST conversions

VAL between representations left a runtime temp, so a CONST using one
was a 230: VAL(REAL, MAX(INTEGER)) (constcast), VAL(LONGCARD,
MAX(LONGINT)) (maxlongint).  Fold an immediate operand in the grammar's
VAL/FLOAT actions:
- INT/LONGINT -> REAL (and FLOAT) via NormReal,
- INT <-> LONGINT (identity on an immediate),
- LONGINT -> LONGINT (same class).

Corpus 389 -> 391, 0 crashes.  Suite 228/228 (t_constreal,
t_constlong).  Fixpoint OK, 3,829,455 bytes.
Eric Streit hai 12 horas
pai
achega
31a78476cf

+ 2 - 2
README.md

@@ -47,8 +47,8 @@ docs/              STATUS.md, plan.md, plan-*.md, analysis_*.md, summary_*.md
 
 ## Status (2026-10-18)
 
-- Suite **226/226**; self-hosting **fixpoint OK** (byte-identical
-  stage2/stage3, 3,826,628 bytes); corpus **389/612 compile-OK,
+- Suite **228/228**; self-hosting **fixpoint OK** (byte-identical
+  stage2/stage3, 3,829,455 bytes); corpus **391/612 compile-OK,
   0 crashes** (`tools/v3-corpus`).
 - Active branch **`resolve-check`** (holds the AST/`Lower`/parse
   refactors and generics).

+ 2 - 0
compiler/run_tests.sh

@@ -97,6 +97,8 @@ expect_run t_setenum.mod 42
 expect_run t_fwdset.mod 42
 expect_run t_setconst.mod 42
 expect_run t_constbuiltin.mod 42
+expect_run t_constreal.mod 42
+expect_run t_constlong.mod 42
 expect_run t_unicode.mod 31
 expect_run t_charbounds.mod 3
 expect_run t_record.mod 160

+ 9 - 7
compiler/src/M2.atg

@@ -3751,7 +3751,8 @@ PRODUCTIONS
    IF astCur # AST.NoNode THEN AST.SetChild(astNode, 2, astCur) END;
    astCur := astNode; astIsLit := TRUE; .)
                                         (. t := SymTab.RealType();
-                                           QbeGen.CopyOp("@", q); .)
+   IF QbeGen.IsImm(q) AND SymTab.ConstInt(q, lo) THEN QbeGen.NormReal(q, qa); QbeGen.CopyOp(qa, q)
+   ELSE QbeGen.CopyOp("@", q) END; .)
     | "VAL" "(" GetIdent<vn> (. astArg2 := AST.MakeLeaf(AST.NkIdent, vn); .) "," Expr<et, q> ")"
     (. astNode := AST.MakeNode(AST.NkCall);
    AST.SetChild(astNode, 0, AST.MakeLeaf(AST.NkIdent, "VAL"));
@@ -3772,22 +3773,20 @@ PRODUCTIONS
                                                c2 := SymTab.ClassOf(vt);
                                                IF (c1 = SymTab.ClInt)
   AND (c2 = SymTab.ClLong) THEN
-                                                 QbeGen.WidenLong(q, qa);
-                                                 QbeGen.CopyOp(qa, q);
+                                                 IF NOT QbeGen.IsImm(q) THEN QbeGen.WidenLong(q, qa); QbeGen.CopyOp(qa, q) END;
                                                  t := vt
                                                ELSIF (c1 = SymTab.ClLong)
   AND (c2 = SymTab.ClInt) THEN
-                                                 QbeGen.NarrowLong(q, qa);
-                                                 QbeGen.CopyOp(qa, q);
+                                                 IF NOT (QbeGen.IsImm(q) AND SymTab.ConstInt(q, lo)) THEN QbeGen.NarrowLong(q, qa); QbeGen.CopyOp(qa, q) END;
                                                  t := vt
                                                ELSIF (c1 = SymTab.ClInt)
   AND (c2 = SymTab.ClReal) THEN
-                                                 QbeGen.ConvIR(q, qa);
+                                                 IF QbeGen.IsImm(q) AND SymTab.ConstInt(q, lo) THEN QbeGen.NormReal(q, qa) ELSE QbeGen.ConvIR(q, qa) END;
                                                  QbeGen.CopyOp(qa, q);
                                                  t := vt
                                                ELSIF (c1 = SymTab.ClLong)
   AND (c2 = SymTab.ClReal) THEN
-                                                 QbeGen.ConvLR(q, qa);
+                                                 IF QbeGen.IsImm(q) AND SymTab.ConstInt(q, lo) THEN QbeGen.NormReal(q, qa) ELSE QbeGen.ConvLR(q, qa) END;
                                                  QbeGen.CopyOp(qa, q);
                                                  t := vt
                                                ELSIF (c1 = SymTab.ClReal)
@@ -3800,6 +3799,9 @@ PRODUCTIONS
                                                  QbeGen.ConvRL(q, qa);
                                                  QbeGen.CopyOp(qa, q);
                                                  t := vt
+                                               ELSIF (c1 = SymTab.ClLong)
+  AND (c2 = SymTab.ClLong) THEN
+                                                 t := vt
                                                ELSIF ((c1 = SymTab.ClInt)
                                                    OR (c1 =
                                                       SymTab.ClChar)

+ 298 - 296
compiler/src/M2.lst

@@ -3770,314 +3770,316 @@ Listing:
  3751     IF astCur # AST.NoNode THEN AST.SetChild(astNode, 2, astCur) END;
  3752     astCur := astNode; astIsLit := TRUE; .)
  3753                                          (. t := SymTab.RealType();
- 3754                                             QbeGen.CopyOp("@", q); .)
- 3755      | "VAL" "(" GetIdent<vn> (. astArg2 := AST.MakeLeaf(AST.NkIdent, vn); .) "," Expr<et, q> ")"
- 3756      (. astNode := AST.MakeNode(AST.NkCall);
- 3757     AST.SetChild(astNode, 0, AST.MakeLeaf(AST.NkIdent, "VAL"));
- 3758     IF astArg2 # AST.NoNode THEN AST.SetChild(astNode, 1, astArg2) END;
- 3759     IF astCur # AST.NoNode THEN AST.SetChild(astNode, 2, astCur) END;
- 3760     astCur := astNode; astIsLit := TRUE; .)
- 3761                                          (. IF NOT SymTab.Lookup(vn) THEN
- 3762                                               SemError(201);
- 3763                                               t := SymTab.InvalidType
- 3764                                             ELSE vt := SymTab.SymType(vn);
- 3765                                               IF vt = SymTab.InvalidType THEN
- 3766                                                 t := SymTab.InvalidType
- 3767                                               ELSIF et =
- 3768                                                  SymTab.InvalidType THEN
- 3769                                                 t := vt
- 3770                                               ELSE
- 3771                                                 c1 := SymTab.ClassOf(et);
- 3772                                                 c2 := SymTab.ClassOf(vt);
- 3773                                                 IF (c1 = SymTab.ClInt)
- 3774    AND (c2 = SymTab.ClLong) THEN
- 3775                                                   QbeGen.WidenLong(q, qa);
- 3776                                                   QbeGen.CopyOp(qa, q);
+ 3754     IF QbeGen.IsImm(q) AND SymTab.ConstInt(q, lo) THEN QbeGen.NormReal(q, qa); QbeGen.CopyOp(qa, q)
+ 3755     ELSE QbeGen.CopyOp("@", q) END; .)
+ 3756      | "VAL" "(" GetIdent<vn> (. astArg2 := AST.MakeLeaf(AST.NkIdent, vn); .) "," Expr<et, q> ")"
+ 3757      (. astNode := AST.MakeNode(AST.NkCall);
+ 3758     AST.SetChild(astNode, 0, AST.MakeLeaf(AST.NkIdent, "VAL"));
+ 3759     IF astArg2 # AST.NoNode THEN AST.SetChild(astNode, 1, astArg2) END;
+ 3760     IF astCur # AST.NoNode THEN AST.SetChild(astNode, 2, astCur) END;
+ 3761     astCur := astNode; astIsLit := TRUE; .)
+ 3762                                          (. IF NOT SymTab.Lookup(vn) THEN
+ 3763                                               SemError(201);
+ 3764                                               t := SymTab.InvalidType
+ 3765                                             ELSE vt := SymTab.SymType(vn);
+ 3766                                               IF vt = SymTab.InvalidType THEN
+ 3767                                                 t := SymTab.InvalidType
+ 3768                                               ELSIF et =
+ 3769                                                  SymTab.InvalidType THEN
+ 3770                                                 t := vt
+ 3771                                               ELSE
+ 3772                                                 c1 := SymTab.ClassOf(et);
+ 3773                                                 c2 := SymTab.ClassOf(vt);
+ 3774                                                 IF (c1 = SymTab.ClInt)
+ 3775    AND (c2 = SymTab.ClLong) THEN
+ 3776                                                   IF NOT QbeGen.IsImm(q) THEN QbeGen.WidenLong(q, qa); QbeGen.CopyOp(qa, q) END;
  3777                                                   t := vt
  3778                                                 ELSIF (c1 = SymTab.ClLong)
  3779    AND (c2 = SymTab.ClInt) THEN
- 3780                                                   QbeGen.NarrowLong(q, qa);
- 3781                                                   QbeGen.CopyOp(qa, q);
- 3782                                                   t := vt
- 3783                                                 ELSIF (c1 = SymTab.ClInt)
- 3784    AND (c2 = SymTab.ClReal) THEN
- 3785                                                   QbeGen.ConvIR(q, qa);
- 3786                                                   QbeGen.CopyOp(qa, q);
- 3787                                                   t := vt
- 3788                                                 ELSIF (c1 = SymTab.ClLong)
- 3789    AND (c2 = SymTab.ClReal) THEN
- 3790                                                   QbeGen.ConvLR(q, qa);
- 3791                                                   QbeGen.CopyOp(qa, q);
- 3792                                                   t := vt
- 3793                                                 ELSIF (c1 = SymTab.ClReal)
- 3794    AND (c2 = SymTab.ClInt) THEN
- 3795                                                   QbeGen.ConvRI(q, qa);
- 3796                                                   QbeGen.CopyOp(qa, q);
- 3797                                                   t := vt
- 3798                                                 ELSIF (c1 = SymTab.ClReal)
- 3799    AND (c2 = SymTab.ClLong) THEN
- 3800                                                   QbeGen.ConvRL(q, qa);
- 3801                                                   QbeGen.CopyOp(qa, q);
- 3802                                                   t := vt
- 3803                                                 ELSIF ((c1 = SymTab.ClInt)
- 3804                                                     OR (c1 =
- 3805                                                        SymTab.ClChar)
+ 3780                                                   IF NOT (QbeGen.IsImm(q) AND SymTab.ConstInt(q, lo)) THEN QbeGen.NarrowLong(q, qa); QbeGen.CopyOp(qa, q) END;
+ 3781                                                   t := vt
+ 3782                                                 ELSIF (c1 = SymTab.ClInt)
+ 3783    AND (c2 = SymTab.ClReal) THEN
+ 3784                                                   IF QbeGen.IsImm(q) AND SymTab.ConstInt(q, lo) THEN QbeGen.NormReal(q, qa) ELSE QbeGen.ConvIR(q, qa) END;
+ 3785                                                   QbeGen.CopyOp(qa, q);
+ 3786                                                   t := vt
+ 3787                                                 ELSIF (c1 = SymTab.ClLong)
+ 3788    AND (c2 = SymTab.ClReal) THEN
+ 3789                                                   IF QbeGen.IsImm(q) AND SymTab.ConstInt(q, lo) THEN QbeGen.NormReal(q, qa) ELSE QbeGen.ConvLR(q, qa) END;
+ 3790                                                   QbeGen.CopyOp(qa, q);
+ 3791                                                   t := vt
+ 3792                                                 ELSIF (c1 = SymTab.ClReal)
+ 3793    AND (c2 = SymTab.ClInt) THEN
+ 3794                                                   QbeGen.ConvRI(q, qa);
+ 3795                                                   QbeGen.CopyOp(qa, q);
+ 3796                                                   t := vt
+ 3797                                                 ELSIF (c1 = SymTab.ClReal)
+ 3798    AND (c2 = SymTab.ClLong) THEN
+ 3799                                                   QbeGen.ConvRL(q, qa);
+ 3800                                                   QbeGen.CopyOp(qa, q);
+ 3801                                                   t := vt
+ 3802                                                 ELSIF (c1 = SymTab.ClLong)
+ 3803    AND (c2 = SymTab.ClLong) THEN
+ 3804                                                   t := vt
+ 3805                                                 ELSIF ((c1 = SymTab.ClInt)
  3806                                                     OR (c1 =
- 3807                                                        SymTab.ClBool)
+ 3807                                                        SymTab.ClChar)
  3808                                                     OR (c1 =
- 3809                                                        SymTab.ClEnum))
- 3810   AND ((c2 = SymTab.ClInt)
- 3811                                                     OR (c2 =
- 3812                                                        SymTab.ClChar)
+ 3809                                                        SymTab.ClBool)
+ 3810                                                     OR (c1 =
+ 3811                                                        SymTab.ClEnum))
+ 3812   AND ((c2 = SymTab.ClInt)
  3813                                                     OR (c2 =
- 3814                                                        SymTab.ClBool)
+ 3814                                                        SymTab.ClChar)
  3815                                                     OR (c2 =
- 3816                                                        SymTab.ClEnum)) THEN
- 3817                                                   t := vt
- 3818                                                 ELSIF (c1 = SymTab.ClPtr)
- 3819   AND (c2 = SymTab.ClPtr) THEN
- 3820                                                   t := vt
- 3821                                                 ELSIF (c1 = SymTab.ClReal)
- 3822   AND (c2 = SymTab.ClReal) THEN
- 3823                                                   t := vt
- 3824                                                 ELSE SemError(230);
- 3825                                                   t := SymTab.InvalidType
- 3826                                                 END
- 3827                                               END
- 3828                                             END; .)
- 3829      | "(" Expr<et, q> ")"               (. t := et; astIsLit := TRUE; .)
- 3830      | SetLit<st, sq>                    (. astIsLit := TRUE; t := st;
- 3831                                             QbeGen.CopyOp(sq, q); .)
- 3832      | ( "NOT" | "~" ) Fact<t2, q2>      (. astNot := astCur;
- 3833                                             IF SymTab.BoolCheck(t2) THEN
- 3834                                               t := SymTab.BoolType()
- 3835                                             ELSE SemError(212);
- 3836                                               t := SymTab.InvalidType END;
- 3837                                             IF t # SymTab.InvalidType THEN
- 3838                                                QbeGen.CopyOp("@", q)
- 3839                                             ELSE QbeGen.CopyOp("0", q)
- 3840                                             END;
- 3841                                             astIsLit := TRUE;
- 3842                                             astCur := AST.MakeUn(
- 3843                                               AST.NkUnary, AST.OpNot, astNot); .)
- 3844      )
- 3845      (. IF NOT astIsLit THEN astCur := AST.NoNode END; .)
- 3846             (. IF astCur # AST.NoNode THEN AST.SetTy(astCur, t) END; .) .
- 3847    (* Set literals are SET OF [0..255] (8 words); elements validated
- 3848       0..255 statically when foldable (222 otherwise), runtime trap
- 3849       for computed elements. Ranges always lower via SetRange. *)
- 3850    SetLit<VAR t: SymTab.TypeIndex; VAR q: QbeGen.QVal>
- 3851                                          (. VAR astNode: AST.Node; .)
- 3852      = "{"                               (. t := SymTab.NewSet(
- 3853                                               SymTab.NewSubR(0, 255));
- 3854                                             astNode := AST.MakeNode(AST.NkSetLit);
- 3855                                             QbeGen.NewSetTemp(8, q);
- 3856                                             QbeGen.SetZero(q, 8); .)
- 3857        [ SetElem<t, q, astNode> { "," SetElem<t, q, astNode> } ]
- 3858        "}"                               (. astCur := astNode; .) .
- 3859    (* Typed brace constructor: TypeName{ elems } — BITSET{0} (a set)
- 3860       or ArrayName{...} (an array constructor, GNU Modula-2).  The
- 3861       declared type sets the width (set) or element type (array). *)
- 3862    TypedBraceLit<vt: SymTab.TypeIndex; VAR q: QbeGen.QVal>
- 3863                                          (. VAR nw: CARDINAL;
- 3864                                               astNode, astTail: AST.Node;
- 3865                                               savedCls: INTEGER; .)
- 3866      = "{"                               (. savedCls := braceCls;
- 3867                                             astNode := AST.MakeNode(AST.NkBraceLit);
- 3868                                             AST.SetTy(astNode, vt);
- 3869                                             astTail := astNode;
- 3870                                             IF vt = SymTab.InvalidType THEN
- 3871                                               braceCls := -1
- 3872                                             ELSE braceCls :=
- 3873                                                    SymTab.ClassOf(vt)
- 3874                                             END;
- 3875                                             IF braceCls = SymTab.ClSet THEN
- 3876                                               IF vt = SymTab.InvalidType THEN
- 3877                                                 nw := 8
- 3878                                               ELSE nw := SymTab.SetWords(vt);
- 3879                                                 IF nw = 0 THEN nw := 8 END
- 3880                                               END;
- 3881                                               QbeGen.NewSetTemp(nw, q);
- 3882                                               QbeGen.SetZero(q, nw)
- 3883                                             ELSIF (braceCls =
- 3884                                                    SymTab.ClArray)
- 3885                                                OR (braceCls =
- 3886                                                    SymTab.ClRecord)
+ 3816                                                        SymTab.ClBool)
+ 3817                                                     OR (c2 =
+ 3818                                                        SymTab.ClEnum)) THEN
+ 3819                                                   t := vt
+ 3820                                                 ELSIF (c1 = SymTab.ClPtr)
+ 3821   AND (c2 = SymTab.ClPtr) THEN
+ 3822                                                   t := vt
+ 3823                                                 ELSIF (c1 = SymTab.ClReal)
+ 3824   AND (c2 = SymTab.ClReal) THEN
+ 3825                                                   t := vt
+ 3826                                                 ELSE SemError(230);
+ 3827                                                   t := SymTab.InvalidType
+ 3828                                                 END
+ 3829                                               END
+ 3830                                             END; .)
+ 3831      | "(" Expr<et, q> ")"               (. t := et; astIsLit := TRUE; .)
+ 3832      | SetLit<st, sq>                    (. astIsLit := TRUE; t := st;
+ 3833                                             QbeGen.CopyOp(sq, q); .)
+ 3834      | ( "NOT" | "~" ) Fact<t2, q2>      (. astNot := astCur;
+ 3835                                             IF SymTab.BoolCheck(t2) THEN
+ 3836                                               t := SymTab.BoolType()
+ 3837                                             ELSE SemError(212);
+ 3838                                               t := SymTab.InvalidType END;
+ 3839                                             IF t # SymTab.InvalidType THEN
+ 3840                                                QbeGen.CopyOp("@", q)
+ 3841                                             ELSE QbeGen.CopyOp("0", q)
+ 3842                                             END;
+ 3843                                             astIsLit := TRUE;
+ 3844                                             astCur := AST.MakeUn(
+ 3845                                               AST.NkUnary, AST.OpNot, astNot); .)
+ 3846      )
+ 3847      (. IF NOT astIsLit THEN astCur := AST.NoNode END; .)
+ 3848             (. IF astCur # AST.NoNode THEN AST.SetTy(astCur, t) END; .) .
+ 3849    (* Set literals are SET OF [0..255] (8 words); elements validated
+ 3850       0..255 statically when foldable (222 otherwise), runtime trap
+ 3851       for computed elements. Ranges always lower via SetRange. *)
+ 3852    SetLit<VAR t: SymTab.TypeIndex; VAR q: QbeGen.QVal>
+ 3853                                          (. VAR astNode: AST.Node; .)
+ 3854      = "{"                               (. t := SymTab.NewSet(
+ 3855                                               SymTab.NewSubR(0, 255));
+ 3856                                             astNode := AST.MakeNode(AST.NkSetLit);
+ 3857                                             QbeGen.NewSetTemp(8, q);
+ 3858                                             QbeGen.SetZero(q, 8); .)
+ 3859        [ SetElem<t, q, astNode> { "," SetElem<t, q, astNode> } ]
+ 3860        "}"                               (. astCur := astNode; .) .
+ 3861    (* Typed brace constructor: TypeName{ elems } — BITSET{0} (a set)
+ 3862       or ArrayName{...} (an array constructor, GNU Modula-2).  The
+ 3863       declared type sets the width (set) or element type (array). *)
+ 3864    TypedBraceLit<vt: SymTab.TypeIndex; VAR q: QbeGen.QVal>
+ 3865                                          (. VAR nw: CARDINAL;
+ 3866                                               astNode, astTail: AST.Node;
+ 3867                                               savedCls: INTEGER; .)
+ 3868      = "{"                               (. savedCls := braceCls;
+ 3869                                             astNode := AST.MakeNode(AST.NkBraceLit);
+ 3870                                             AST.SetTy(astNode, vt);
+ 3871                                             astTail := astNode;
+ 3872                                             IF vt = SymTab.InvalidType THEN
+ 3873                                               braceCls := -1
+ 3874                                             ELSE braceCls :=
+ 3875                                                    SymTab.ClassOf(vt)
+ 3876                                             END;
+ 3877                                             IF braceCls = SymTab.ClSet THEN
+ 3878                                               IF vt = SymTab.InvalidType THEN
+ 3879                                                 nw := 8
+ 3880                                               ELSE nw := SymTab.SetWords(vt);
+ 3881                                                 IF nw = 0 THEN nw := 8 END
+ 3882                                               END;
+ 3883                                               QbeGen.NewSetTemp(nw, q);
+ 3884                                               QbeGen.SetZero(q, nw)
+ 3885                                             ELSIF (braceCls =
+ 3886                                                    SymTab.ClArray)
  3887                                                OR (braceCls =
- 3888                                                    SymTab.ClClass) THEN
- 3889                                               QbeGen.CtorBegin(vt)
- 3890                                             ELSE
- 3891                                               IF vt # SymTab.InvalidType THEN
- 3892                                                 SemError(230) END;
- 3893                                               braceCls := -1
- 3894                                             END; .)
- 3895        [ BraceElem<vt, q, astNode, astTail>
- 3896          { "," BraceElem<vt, q, astNode, astTail> } ]
- 3897        "}"                               (. IF (braceCls = SymTab.ClArray)
- 3898                                                OR (braceCls =
- 3899                                                   SymTab.ClRecord)
+ 3888                                                    SymTab.ClRecord)
+ 3889                                                OR (braceCls =
+ 3890                                                    SymTab.ClClass) THEN
+ 3891                                               QbeGen.CtorBegin(vt)
+ 3892                                             ELSE
+ 3893                                               IF vt # SymTab.InvalidType THEN
+ 3894                                                 SemError(230) END;
+ 3895                                               braceCls := -1
+ 3896                                             END; .)
+ 3897        [ BraceElem<vt, q, astNode, astTail>
+ 3898          { "," BraceElem<vt, q, astNode, astTail> } ]
+ 3899        "}"                               (. IF (braceCls = SymTab.ClArray)
  3900                                                OR (braceCls =
- 3901                                                   SymTab.ClClass) THEN
- 3902                                               QbeGen.CtorEnd(q)
- 3903                                             ELSIF braceCls # SymTab.ClSet THEN
- 3904                                               QbeGen.CopyOp("0", q)
- 3905                                             END;
- 3906                                             braceCls := savedCls; .)
- 3907                                          (. astCur := astNode; .) .
- 3908    BraceElem<vt: SymTab.TypeIndex; VAR sq: QbeGen.QVal;
- 3909              VAR node, tail: AST.Node>
- 3910                                          (. VAR et, et2: SymTab.TypeIndex;
- 3911                                               qe, q2: QbeGen.QVal;
- 3912                                               v, v2, reps, k: INTEGER;
- 3913                                               elem: SymTab.TypeIndex;
- 3914                                               lo: INTEGER;
- 3915                                               span: CARDINAL;
- 3916                                               cl, cl2: INTEGER;
- 3917                                               hasR, hasB: BOOLEAN;
- 3918                                               astEl: AST.Node; .)
- 3919      =                                   (. hasR := FALSE; hasB := FALSE;
- 3920                                             reps := 1; .)
- 3921        Expr<et, qe> (. astEl := astCur; .)
- 3922        [ ".." Expr<et2, q2>              (. astEl := AST.MakeBin(
- 3923                                               AST.NkSubrange, 0, astEl, astCur);
- 3924                                             hasR := TRUE; .) ]
- 3925        [ "BY" Expr<et2, q2>              (. hasB := TRUE; .) ]
- 3926                                          (. IF braceCls = SymTab.ClSet THEN
- 3927                                               IF hasB THEN SemError(230) END;
- 3928                                               lo := SymTab.SetBaseLo(vt);
- 3929                                               span := SymTab.SetCount(vt);
- 3930                                               IF (et = SymTab.InvalidType)
- 3931                                                  OR (hasR AND (et2 =
- 3932                                                     SymTab.InvalidType)) THEN
- 3933                                               ELSE cl :=
- 3934                                                      SymTab.ClassOf(et);
- 3935                                                 IF hasR THEN
- 3936                                                   cl2 :=
- 3937                                                     SymTab.ClassOf(et2)
- 3938                                                 ELSE cl2 := SymTab.ClInt
- 3939                                                 END;
- 3940                                                 IF NOT SymTab.SetElemClassOk(cl)
- 3941                                                    OR (hasR AND NOT
- 3942                                                       SymTab.SetElemClassOk(cl2))
- 3943                                                 THEN
- 3944                                                   SemError(222)
- 3945                                                 ELSIF hasR
- 3946   AND SymTab.ConstInt(qe, v)
- 3947   AND SymTab.ConstInt(q2,
- 3948                                                       v2)
- 3949   AND ((v < lo)
- 3950                                                       OR (v2 < lo)
- 3951                                                       OR (v >= lo +
- 3952                                                          VAL(INTEGER, span))
- 3953                                                       OR (v2 >= lo +
+ 3901                                                   SymTab.ClRecord)
+ 3902                                                OR (braceCls =
+ 3903                                                   SymTab.ClClass) THEN
+ 3904                                               QbeGen.CtorEnd(q)
+ 3905                                             ELSIF braceCls # SymTab.ClSet THEN
+ 3906                                               QbeGen.CopyOp("0", q)
+ 3907                                             END;
+ 3908                                             braceCls := savedCls; .)
+ 3909                                          (. astCur := astNode; .) .
+ 3910    BraceElem<vt: SymTab.TypeIndex; VAR sq: QbeGen.QVal;
+ 3911              VAR node, tail: AST.Node>
+ 3912                                          (. VAR et, et2: SymTab.TypeIndex;
+ 3913                                               qe, q2: QbeGen.QVal;
+ 3914                                               v, v2, reps, k: INTEGER;
+ 3915                                               elem: SymTab.TypeIndex;
+ 3916                                               lo: INTEGER;
+ 3917                                               span: CARDINAL;
+ 3918                                               cl, cl2: INTEGER;
+ 3919                                               hasR, hasB: BOOLEAN;
+ 3920                                               astEl: AST.Node; .)
+ 3921      =                                   (. hasR := FALSE; hasB := FALSE;
+ 3922                                             reps := 1; .)
+ 3923        Expr<et, qe> (. astEl := astCur; .)
+ 3924        [ ".." Expr<et2, q2>              (. astEl := AST.MakeBin(
+ 3925                                               AST.NkSubrange, 0, astEl, astCur);
+ 3926                                             hasR := TRUE; .) ]
+ 3927        [ "BY" Expr<et2, q2>              (. hasB := TRUE; .) ]
+ 3928                                          (. IF braceCls = SymTab.ClSet THEN
+ 3929                                               IF hasB THEN SemError(230) END;
+ 3930                                               lo := SymTab.SetBaseLo(vt);
+ 3931                                               span := SymTab.SetCount(vt);
+ 3932                                               IF (et = SymTab.InvalidType)
+ 3933                                                  OR (hasR AND (et2 =
+ 3934                                                     SymTab.InvalidType)) THEN
+ 3935                                               ELSE cl :=
+ 3936                                                      SymTab.ClassOf(et);
+ 3937                                                 IF hasR THEN
+ 3938                                                   cl2 :=
+ 3939                                                     SymTab.ClassOf(et2)
+ 3940                                                 ELSE cl2 := SymTab.ClInt
+ 3941                                                 END;
+ 3942                                                 IF NOT SymTab.SetElemClassOk(cl)
+ 3943                                                    OR (hasR AND NOT
+ 3944                                                       SymTab.SetElemClassOk(cl2))
+ 3945                                                 THEN
+ 3946                                                   SemError(222)
+ 3947                                                 ELSIF hasR
+ 3948   AND SymTab.ConstInt(qe, v)
+ 3949   AND SymTab.ConstInt(q2,
+ 3950                                                       v2)
+ 3951   AND ((v < lo)
+ 3952                                                       OR (v2 < lo)
+ 3953                                                       OR (v >= lo +
  3954                                                          VAL(INTEGER, span))
- 3955                                                       OR (v > v2)) THEN
- 3956                                                   SemError(222)
- 3957                                                  ELSIF hasR THEN
- 3958                                                    QbeGen.SetRange(sq, qe, q2,
- 3959                                                      lo, span)
- 3960                                                  ELSIF SymTab.ConstInt(qe,
- 3961                                                          v)
- 3962   AND ((v < lo)
- 3963                                                        OR (v >= lo +
- 3964                                                           VAL(INTEGER,
- 3965                                                             span))) THEN
- 3966                                                    SemError(222)
- 3967                                                  ELSE QbeGen.SetBit(sq, qe,
- 3968                                                    lo, span)
- 3969                                                 END
- 3970                                               END
- 3971                                             ELSIF (braceCls = SymTab.ClArray)
- 3972                                                OR (braceCls =
- 3973                                                   SymTab.ClRecord)
+ 3955                                                       OR (v2 >= lo +
+ 3956                                                          VAL(INTEGER, span))
+ 3957                                                       OR (v > v2)) THEN
+ 3958                                                   SemError(222)
+ 3959                                                  ELSIF hasR THEN
+ 3960                                                    QbeGen.SetRange(sq, qe, q2,
+ 3961                                                      lo, span)
+ 3962                                                  ELSIF SymTab.ConstInt(qe,
+ 3963                                                          v)
+ 3964   AND ((v < lo)
+ 3965                                                        OR (v >= lo +
+ 3966                                                           VAL(INTEGER,
+ 3967                                                             span))) THEN
+ 3968                                                    SemError(222)
+ 3969                                                  ELSE QbeGen.SetBit(sq, qe,
+ 3970                                                    lo, span)
+ 3971                                                 END
+ 3972                                               END
+ 3973                                             ELSIF (braceCls = SymTab.ClArray)
  3974                                                OR (braceCls =
- 3975                                                   SymTab.ClClass) THEN
- 3976                                               IF hasR THEN SemError(230) END;
- 3977                                               reps := 1;
- 3978                                               IF hasB THEN
- 3979                                                 IF SymTab.ConstInt(q2, v2)
- 3980   AND (v2 >= 1) THEN
- 3981                                                   reps := v2
- 3982                                                 ELSE SemError(230)
- 3983                                                 END
- 3984                                               END;
- 3985                                               k := 0;
- 3986                                               WHILE k < reps DO
- 3987                                                 QbeGen.CtorElem(qe);
- 3988                                                 INC(k)
- 3989                                               END
- 3990                                             END; .)
- 3991                                          (. (* the BY form repeats the
- 3992                                               element; keep the AST in
- 3993                                               step with CtorElem *)
- 3994                                             k := 0;
- 3995                                             WHILE k < reps DO
- 3996                                               AstAppend(AST.NkBlock,
- 3997                                                 node, tail, astEl);
- 3998                                               INC(k)
- 3999                                             END; .) .
- 4000    SetElem<st: SymTab.TypeIndex; sq: QbeGen.QVal; node: AST.Node>         (. VAR et, et2: SymTab.TypeIndex;
- 4001                                               qe, q2: QbeGen.QVal;
- 4002                                               v, v2: INTEGER;
- 4003                                               lo: INTEGER;
- 4004                                               span: CARDINAL;
- 4005                                               cl, cl2: INTEGER;
- 4006                                               hasR: BOOLEAN;
- 4007                                               astEl: AST.Node; .)
- 4008      =                                   (. hasR := FALSE; .)
- 4009        Expr<et, qe> (. astEl := astCur; .)
- 4010        [ ".." Expr<et2, q2>              (. astEl := AST.MakeBin(
- 4011                                               AST.NkSubrange, 0, astEl, astCur);
- 4012                                             hasR := TRUE; .) ]
- 4013                                          (. lo := SymTab.SetBaseLo(st);
- 4014                                             span := SymTab.SetCount(st);
- 4015                                             IF (et = SymTab.InvalidType)
- 4016                                                OR (hasR AND (et2 =
- 4017                                                   SymTab.InvalidType)) THEN
- 4018                                             ELSE cl :=
- 4019                                                    SymTab.ClassOf(et);
- 4020                                               IF hasR THEN
- 4021                                                 cl2 :=
- 4022                                                   SymTab.ClassOf(et2)
- 4023                                               ELSE cl2 := SymTab.ClInt
- 4024                                               END;
- 4025                                               IF NOT SymTab.SetElemClassOk(cl)
- 4026                                                  OR (hasR AND NOT
- 4027                                                     SymTab.SetElemClassOk(cl2))
- 4028                                               THEN
- 4029                                                 SemError(222)
- 4030                                               ELSIF hasR
- 4031   AND SymTab.ConstInt(qe, v)
- 4032   AND SymTab.ConstInt(q2,
- 4033                                                     v2)
- 4034   AND ((v < lo)
- 4035                                                     OR (v2 < lo)
- 4036                                                     OR (v >= lo +
- 4037                                                        VAL(INTEGER, span))
- 4038                                                     OR (v2 >= lo +
+ 3975                                                   SymTab.ClRecord)
+ 3976                                                OR (braceCls =
+ 3977                                                   SymTab.ClClass) THEN
+ 3978                                               IF hasR THEN SemError(230) END;
+ 3979                                               reps := 1;
+ 3980                                               IF hasB THEN
+ 3981                                                 IF SymTab.ConstInt(q2, v2)
+ 3982   AND (v2 >= 1) THEN
+ 3983                                                   reps := v2
+ 3984                                                 ELSE SemError(230)
+ 3985                                                 END
+ 3986                                               END;
+ 3987                                               k := 0;
+ 3988                                               WHILE k < reps DO
+ 3989                                                 QbeGen.CtorElem(qe);
+ 3990                                                 INC(k)
+ 3991                                               END
+ 3992                                             END; .)
+ 3993                                          (. (* the BY form repeats the
+ 3994                                               element; keep the AST in
+ 3995                                               step with CtorElem *)
+ 3996                                             k := 0;
+ 3997                                             WHILE k < reps DO
+ 3998                                               AstAppend(AST.NkBlock,
+ 3999                                                 node, tail, astEl);
+ 4000                                               INC(k)
+ 4001                                             END; .) .
+ 4002    SetElem<st: SymTab.TypeIndex; sq: QbeGen.QVal; node: AST.Node>         (. VAR et, et2: SymTab.TypeIndex;
+ 4003                                               qe, q2: QbeGen.QVal;
+ 4004                                               v, v2: INTEGER;
+ 4005                                               lo: INTEGER;
+ 4006                                               span: CARDINAL;
+ 4007                                               cl, cl2: INTEGER;
+ 4008                                               hasR: BOOLEAN;
+ 4009                                               astEl: AST.Node; .)
+ 4010      =                                   (. hasR := FALSE; .)
+ 4011        Expr<et, qe> (. astEl := astCur; .)
+ 4012        [ ".." Expr<et2, q2>              (. astEl := AST.MakeBin(
+ 4013                                               AST.NkSubrange, 0, astEl, astCur);
+ 4014                                             hasR := TRUE; .) ]
+ 4015                                          (. lo := SymTab.SetBaseLo(st);
+ 4016                                             span := SymTab.SetCount(st);
+ 4017                                             IF (et = SymTab.InvalidType)
+ 4018                                                OR (hasR AND (et2 =
+ 4019                                                   SymTab.InvalidType)) THEN
+ 4020                                             ELSE cl :=
+ 4021                                                    SymTab.ClassOf(et);
+ 4022                                               IF hasR THEN
+ 4023                                                 cl2 :=
+ 4024                                                   SymTab.ClassOf(et2)
+ 4025                                               ELSE cl2 := SymTab.ClInt
+ 4026                                               END;
+ 4027                                               IF NOT SymTab.SetElemClassOk(cl)
+ 4028                                                  OR (hasR AND NOT
+ 4029                                                     SymTab.SetElemClassOk(cl2))
+ 4030                                               THEN
+ 4031                                                 SemError(222)
+ 4032                                               ELSIF hasR
+ 4033   AND SymTab.ConstInt(qe, v)
+ 4034   AND SymTab.ConstInt(q2,
+ 4035                                                     v2)
+ 4036   AND ((v < lo)
+ 4037                                                     OR (v2 < lo)
+ 4038                                                     OR (v >= lo +
  4039                                                        VAL(INTEGER, span))
- 4040                                                     OR (v > v2)) THEN
- 4041                                                 SemError(222)
- 4042                                                ELSIF hasR THEN
- 4043                                                  QbeGen.SetRange(sq, qe, q2,
- 4044                                                    lo, span)
- 4045                                                ELSIF SymTab.ConstInt(qe,
- 4046                                                        v)
- 4047   AND ((v < lo)
- 4048                                                      OR (v >= lo +
- 4049                                                         VAL(INTEGER,
- 4050                                                           span))) THEN
- 4051                                                  SemError(222)
- 4052                                                ELSE QbeGen.SetBit(sq, qe,
- 4053                                                  lo, span)
- 4054                                               END
- 4055                                             END; .)
- 4056                                          (. AST.SetChild(node,
- 4057                                               AST.NChild(node), astEl); .) .
- 4058    GetIdent<VAR n: SymTab.Name>
- 4059      = ident                             (. LexName(n); .) .
- 4060  
- 4061  END M2.
+ 4040                                                     OR (v2 >= lo +
+ 4041                                                        VAL(INTEGER, span))
+ 4042                                                     OR (v > v2)) THEN
+ 4043                                                 SemError(222)
+ 4044                                                ELSIF hasR THEN
+ 4045                                                  QbeGen.SetRange(sq, qe, q2,
+ 4046                                                    lo, span)
+ 4047                                                ELSIF SymTab.ConstInt(qe,
+ 4048                                                        v)
+ 4049   AND ((v < lo)
+ 4050                                                      OR (v >= lo +
+ 4051                                                         VAL(INTEGER,
+ 4052                                                           span))) THEN
+ 4053                                                  SemError(222)
+ 4054                                                ELSE QbeGen.SetBit(sq, qe,
+ 4055                                                  lo, span)
+ 4056                                               END
+ 4057                                             END; .)
+ 4058                                          (. AST.SetChild(node,
+ 4059                                               AST.NChild(node), astEl); .) .
+ 4060    GetIdent<VAR n: SymTab.Name>
+ 4061      = ident                             (. LexName(n); .) .
+ 4062  
+ 4063  END M2.
 
     0 errors
 

+ 11 - 0
compiler/tests/t_constlong.mod

@@ -0,0 +1,11 @@
+MODULE TConstLong;
+// CONST initialisers that convert between INTEGER and LONGINT fold when
+// the operand is an immediate.  Exit 42.
+CONST a = VAL(LONGINT, 5);
+      b = VAL(INTEGER, 5);
+      c = VAL(LONGINT, -3);
+VAR ExitCode : INTEGER;
+BEGIN
+  IF (a = 5) AND (b = 5) AND (c = -3)
+  THEN ExitCode := 42 ELSE ExitCode := 1 END
+END TConstLong.

+ 11 - 0
compiler/tests/t_constreal.mod

@@ -0,0 +1,11 @@
+MODULE TConstReal;
+// CONST initialisers that convert an integer immediate to REAL:
+// VAL(REAL, ...) and FLOAT(...) fold to a real immediate.  Exit 42.
+CONST r = VAL(REAL, 5);
+      f = FLOAT(7);
+      n = VAL(REAL, -3);
+VAR ExitCode : INTEGER;
+BEGIN
+  IF (r = 5.0) AND (f = 7.0) AND (n = -3.0)
+  THEN ExitCode := 42 ELSE ExitCode := 1 END
+END TConstReal.

+ 3 - 2
docs/INDEX.md

@@ -27,8 +27,9 @@ bisecting *why* a decision was made, not for reading end-to-end).
 
 - `summary_corpus-sweep-2026-10-18.md` — corpus sweep (builtins, bool/enum
   bounds, set-of-enum, forward set); set-constant + folding findings.
-- `summary_set-constants-2026-10-18.md` — SET CONST landed (static
-  masks, folded unions) + builtin CONST folding (CAP/ABS/ODD, BOOLEAN consts).
+- `summary_set-constants-2026-10-18.md` — SET CONST (static masks,
+  folded unions), builtin CONST folding (CAP/ABS/ODD, BOOLEAN consts),
+  and CONST representation conversions.
 - `summary_dlist-showcase-2026-10-16.md` — showcase23 + limitation fixes.
 - `summary_generics-limitations-fixed-2026-10-16.md` — the two
   template/proc-param limitations, fixed.

+ 7 - 6
docs/STATUS.md

@@ -4,10 +4,10 @@ This answers "where are we now?".
 
 ## Numbers
 
-- Suite **226/226** (`compiler/run_tests.sh`).
+- Suite **228/228** (`compiler/run_tests.sh`).
 - Self-hosting **FIXPOINT OK** — stage2 and stage3 images are
-  byte-identical (3,826,628 bytes).
-- Corpus **389 / 612** sources compile with 0 errors, **0 crashes**
+  byte-identical (3,829,455 bytes).
+- Corpus **391 / 612** sources compile with 0 errors, **0 crashes**
   (`tools/v3-corpus/corpus.sh` over the gm2 testsuite); the rest are
   genuine language gaps or unresolved imports.
 - Branch **`resolve-check`**; tree clean.
@@ -58,8 +58,9 @@ parameters, constraints, nested generics, generic classes.
 - **SET CONST** (static masks): a set constant (`CharSet{"0".."9"}`,
   a union of set constants) is folded to a mask at parse and re-emitted
   as a pooled `data $setN` descriptor.  **Builtin `CONST` folding**
-  (`CAP`/`ABS`/`ODD` over immediates) and `BOOLEAN` `CONST`s also
-  landed: corpus 382 → 389.
+  (`CAP`/`ABS`/`ODD` over immediates), `BOOLEAN` `CONST`s, and
+  representation-changing `CONST` conversions (`VAL(REAL, int)`,
+  `VAL(LONGINT, int)`, …) also landed: corpus 382 → 391.
 - Clean `M2` exit codes (0 ok / 1 errors / 2 usage-IO).
 - Bug fixes: `PopPos` wiping repass diagnostics; global record data
   size (`w 0` → `l 0` for pointer/`LONGINT`/proc fields); template
@@ -87,7 +88,7 @@ parameters, constraints, nested generics, generic classes.
 ```sh
 cd compiler
 ./build.sh          # M2.atg -> scanner/parser/driver -> ./M2
-./run_tests.sh      # 226/226
+./run_tests.sh      # 228/228
 cd ..
 ./bootstrap/fixpoint.sh   # FIXPOINT OK (byte-identical stage2/stage3)
 ```

+ 11 - 1
docs/analysis_gaps.md

@@ -195,4 +195,14 @@ one-liners** (deliberately left as `230`, no silent miscompile):
    suite test `t_constbuiltin`.  `constcast` (REAL<->CARDINAL `CONST`
    conversions) and `minmaxreal3` (SYSTEM `REAL64`) remain.
 
-Both recorded features are now closed.
+Both recorded features are now closed.  Pushing further on `CONST`
+initialisers also landed **representation-changing conversions**:
+`VAL(REAL, intimm)` / `FLOAT` (`constcast`), `VAL(LONGCARD, longimm)`
+(`maxlongint`), `INT`↔`LONGINT` and `LONGINT`→`LONGINT` fold on an
+immediate.  Corpus **389 -> 391**; suite tests `t_constreal`,
+`t_constlong`.
+
+Next (recorded): forward *type* references in an array index / `CONST`
+conversion (`subrange4`/`7`/`11`, `record7`, `complextypes`,
+`setconst3`), and the `imp*` qualified-import family (partly a
+corpus-harness transitive-`.def` gap).

+ 28 - 13
docs/summary_set-constants-2026-10-18.md

@@ -1,16 +1,16 @@
 # Session summary — SET CONST + builtin CONST folding 2026-10-18
 
-Branch **`resolve-check`**.  Suite **226/226**; fixpoint **OK
-(3,826,628 bytes)**; corpus **389/612 compile-OK, 0 crashes**.
+Branch **`resolve-check`**.  Suite **228/228**; fixpoint **OK
+(3,829,455 bytes)**; corpus **391/612 compile-OK, 0 crashes**.
 
-Tags: `v3-set-constants`, `v3-const-builtin-fold`.
+Tags: `v3-set-constants`, `v3-const-builtin-fold`, `v3-const-conv-fold`.
 
 ## What this session did
 
 Landed the two features recorded in `docs/analysis_gaps.md` §9 by the
-previous corpus sweep: **set constants** (static masks) and **builtin
-calls in `CONST` initialisers**.  Corpus **382 → 389** (+7 files, 0
-crashes).
+previous corpus sweep — **set constants** (static masks) and **builtin
+calls in `CONST` initialisers** — then went further on `CONST`
+initialisers.  Corpus **382 → 391** (+9 files, 0 crashes).
 
 ```mod
 CONST Digits = CharSet{"0".."9"};          (* char range *)
@@ -67,29 +67,44 @@ is unchanged).
 designator — the grammar's `Design` class list was missing `ClBool`
 (Lower already had it); added.  Corpus **387 → 389**.
 
+## CONST conversions
+
+`VAL` between representations used to leave a runtime temp, so
+`CONST r = VAL(REAL, MAX(INTEGER))` (`constcast`) and
+`CONST a = VAL(LONGCARD, MAX(LONGINT))` (`maxlongint`) were `230`.  The
+grammar now folds an immediate operand for the representation-changing
+conversions: `INT`/`LONGINT`→`REAL` (and `FLOAT`, via `NormReal`),
+`INT`↔`LONGINT` (identity on an immediate), and identical-class
+`LONGINT`→`LONGINT`.  Corpus **389 → 391**.
+
 ## Not fixed (recorded)
 
 - **`setconst3.mod`** still fails: its `VAR mem: ARRAY [0..CARDINAL(
   addressLines)]` names a `CONST` declared *after* the `VAR` (a forward
   CONST reference), which is a separate gap.  Its `SET CONST` itself now
   parses.
-- `constcast.mod` (`REAL`↔`CARDINAL` `CONST` conversions) and
-  `minmaxreal3.mod` (SYSTEM `REAL64`) remain.
+- Forward *type* references in an array index (`VAR a: ARRAY ind OF …`
+  with `ind` later, `record7`, `complextypes`) and in a `CONST`
+  conversion (`subrange4`/`7`); `minmaxreal3` (SYSTEM `REAL64`); the
+  `imp*` qualified-import family (partly a corpus-harness transitive
+  `.def` gap).
 
 ## Files
 
 `compiler/src/Lower.{def,mod}` (evaluator + `PoolSet` call),
 `compiler/src/QbeGen.{def,mod}` (`PoolSet`/`FlushSets`/pool state),
-`compiler/src/M2.atg` (`ConstDecl`, `Design`, `CAP`/`ABS`/`ODD` folds),
-`compiler/tests/t_setconst.mod`, `compiler/tests/t_bad_setconst.mod`,
-`compiler/tests/t_constbuiltin.mod`, `compiler/run_tests.sh`.
+`compiler/src/M2.atg` (`ConstDecl`, `Design`, `CAP`/`ABS`/`ODD` folds,
+`VAL` conversion folds),
+`compiler/tests/t_setconst.mod`, `t_bad_setconst.mod`,
+`t_constbuiltin.mod`, `t_constreal.mod`, `t_constlong.mod`,
+`compiler/run_tests.sh`.
 
 ## Resume
 
 ```sh
 cd .../m2compiler-V3
 git checkout resolve-check
-cd compiler && ./build.sh && ./run_tests.sh   # 226/226
+cd compiler && ./build.sh && ./run_tests.sh   # 228/228
 cd .. && TMP="$PWD/.scratch/fixpoint" ./bootstrap/fixpoint.sh
-M2FLAGS="" ./.scratch/corpusx.sh              # 389 OK, 0 crash
+M2FLAGS="" ./.scratch/corpusx.sh              # 391 OK, 0 crash
 ```