
    <<Pass4D.mesa>>
        <<Copyright © 1985 by Xerox Corporation.  All rights reserved.>>
        <<Satterthwaite, April 11, 1986 12:28:38 pm PST>>
        <<Russ Atkinson (RRA) March 6, 1985 10:51:16 pm PST>>

    DIRECTORY
        Alloc: TYPE USING [Notifier],
        BcdDefs: TYPE USING [ModuleIndex],
        ComData: TYPE USING [idCARDINAL, interface, mainCtx, textIndex],
        PrincOps: TYPE USING [globalbase, localbase],
        Log: TYPE USING [Error, ErrorSei, ErrorTree, Warning],
        P4: TYPE USING [AdjustBias, BitsForType, both, CheckFields, ConstantInterval, currentLevel, EmptyInterval, ForceType, Interval, 
        LayoutArgs, LayoutFields, MakeEPLink, mark, none, OpWordCount, other, ownGfi, Prop, Repr, RewriteAssign, Rhs, signed, 
        StructuredLiteral, TreeLiteral, TreeLiteralValue, unsigned, VPop, VProp, VRep],
        SourceMap: TYPE USING [Loc],
        Symbols: TYPE USING [Base, BitAddress, BitCount, bodyType, CBTIndex, CBTNull, codeANY, codeCHAR, codeINT, CSEIndex, CSENull, 
        CTXIndex, ctxType, ExtensionType, ISEIndex, ISENull, lG, lZ, nullType, RecordSEIndex, RecordSENull, RootBti, seType, Type, 
        typeANY, TypeClass, typeTYPE, WordCount, WordLength],
        SymbolOps: TYPE USING [ArgRecord, BitsPerElement, Cardinality, ConstantId, CtxEntries, CtxLevel, EnterExtension, FindExtension, 
        FirstCtxSe, LinkMode, NextSe, NormalType, RCType, SearchContext, TypeLink, UnderType, WordsForType],
        Tree: TYPE USING [Base, Index, Link, Map, NodeName, Null, Scan, treeType],
        TreeOps: TYPE USING [CopyTree, FreeNode, FreeTree, GetNode, IdentityMap, ListHead, ListLength, NthSon, OpName, PopTree, PushList, 
        PushNode, PushTree, ScanList, SetAttr, SetInfo, UpdateList];

    Pass4D: PROGRAM
        IMPORTS Log, P4, SymbolOps, TreeOps, dataPtr: ComData
        EXPORTS P4 = {
        OPEN TreeOps, SymbolOps, Symbols;

        tb: Tree.Base;    -- tree base address (local copy)
        seb: Symbols.Base;    -- se table base address (local copy)
        ctxb: Symbols.Base;    -- context table base address (local copy)
        bb: Symbols.Base;    -- body table base address (local copy)

        DeclNotify: PUBLIC Alloc.Notifier = {
            <<called by allocator whenever table area is repacked>>
            tb _ base[Tree.treeType];
            seb _ base[seType];  ctxb _ base[ctxType];
            bb _ base[bodyType]};


        VarInit: PUBLIC SIGNAL RETURNS[BOOL] = CODE;

        ownGfi: BcdDefs.ModuleIndex = P4.ownGfi;


        ItemId: PROC[t: Tree.Link] RETURNS[ISEIndex] = {
            RETURN[WITH t SELECT FROM
                symbol => index,
                subtree => ItemId[tb[index].son[1]],
                ENDCASE => ERROR]
            };

        FirstId: PROC[node: Tree.Index] RETURNS[ISEIndex] = {
            RETURN[ItemId[ListHead[tb[node].son[1]]]]};


        DeclItem: PUBLIC PROC[item: Tree.Link] = {
            node: Tree.Index = GetNode[item];
            initFlag, eqFlag: BOOL;

            ExpInit: PROC = INLINE {
                OPEN tb[node];
                type: Type = TypeForDecl[node];
                class: TypeClass = seb[NormalType[type]].typeTag;
                son[3] _ P4.Rhs[son[3], type, $init];
                IF eqFlag THEN {
                    t: Tree.Link _ son[3];
                    prop: P4.Prop = P4.VProp[];
                    WHILE OpName[t] = cast DO t _ NthSon[t, 1] ENDLOOP;
                    IF P4.TreeLiteral[t] THEN {
                        DefineSEValue[ids:son[1], value:P4.TreeLiteralValue[t]]; GO TO defined};
                    IF prop.noFreeVar AND prop.noXfer AND class # $transfer THEN {
                        DefineSEValue[ids:son[1]];
                        AugmentSEValue[son[1], value, son[3]]; son[3] _ Tree.Null; GO TO defined};
                    IF class = $transfer THEN
                        WITH t SELECT FROM
                            symbol => {
                                sei: ISEIndex = index;
                                IF seb[sei].constant THEN {
                                    DefineSEValue[ids:son[1], value:seb[sei].idValue, info:seb[sei].idInfo];
                                    IF seb[sei].extended THEN
                                        AugmentSEValue[son[1], form, FindExtension[sei].tree, TRUE];
                                    GO TO defined}};
                            ENDCASE;
                    DefineSEVar[ids:son[1]];
                    EXITS
                        defined => son[3] _ FreeTree[son[3]]};
                SELECT class FROM
                    $ref, $arraydesc, $relative =>
                        IF ListLength[son[1]] # 1 AND son[3] # Tree.Null
                            AND ~P4.StructuredLiteral[son[3]] THEN
                                Log.Warning[pointerInit];
                    ENDCASE;
                P4.VPop[]};

            BodyInit: PROC = INLINE {
                expNode: Tree.Index = GetNode[tb[node].son[3]];
                bti: CBTIndex = tb[expNode].info;
                IF eqFlag THEN {
                    IF tb[expNode].attr3 THEN {  -- inline
                        DefineSEValue[ids:tb[node].son[1], info:bti];
                        AugmentSEValue[tb[node].son[1], form,
                            IF dataPtr.interface THEN TrimTree[tb[node].son[3]] ELSE Tree.Null]}
                    ELSE DefineSEValue[
                        ids: tb[node].son[1],
                        value: P4.MakeEPLink[bb[bti].entryIndex, ownGfi], info: bti];
                    tb[node].son[3] _ Tree.Null}
                ELSE {PushNode[body, 0]; SetInfo[bti]; tb[node].son[3] _ PopTree[]}};

            saveIndex: SourceMap.Loc = dataPtr.textIndex;
            IF tb[node].attr3 = P4.mark THEN RETURN;    -- already processed
            tb[node].attr3 _ P4.mark;
            dataPtr.textIndex _ tb[node].info;
            initFlag _ (tb[node].son[3] # Tree.Null);
            IF tb[node].name = typedecl THEN {
                ENABLE VarInit => {RESUME[FALSE]};
                TypeExp[tb[node].son[2]];  CheckDefaults[item]}
            ELSE {
                OPEN tb[node];
                op: Tree.NodeName = OpName[son[3]];
                IF son[2] # Tree.Null THEN TypeExp[son[2], op = body];
                IF initFlag THEN {
                    eqFlag _ attr1;
                    SELECT op FROM
                        body, procinit => BodyInit[];
                        signalinit =>
                            IF eqFlag THEN {
                                expNode: Tree.Index = GetNode[son[3]];
                                DefineSEValue[son[1], P4.MakeEPLink[tb[expNode].info, ownGfi], RootBti]; 
                                son[3] _ FreeTree[son[3]]};
                        inline => {
                            expNode: Tree.Index = GetNode[son[3]];
                            tb[expNode].son[1] _ UpdateList[tb[expNode].son[1], InlineOp];
                            DefineSEValue[ids:son[1]];
                            AugmentSEValue[son[1], value, son[3]];  son[3] _ Tree.Null};
                        ENDCASE => ExpInit[]}};
            MarkAndCheckSE[tb[node].son[1], initFlag];
            dataPtr.textIndex _ saveIndex};

        TypeForDecl: PROC[node: Tree.Index] RETURNS[Type] = {
            RETURN[IF tb[node].son[2] # Tree.Null
                THEN TypeForTree[tb[node].son[2]]
                ELSE seb[FirstId[node]].idType]
            };


        ConstInit: PROC[t: Tree.Link] RETURNS[BOOL] = {
            RETURN[IF OpName[t] # all THEN P4.StructuredLiteral[t] ELSE ConstInit[NthSon[t, 1]]]};

        InlineByte: Tree.Map = {
            v _ P4.Rhs[t, dataPtr.idCARDINAL];  P4.VPop[];
            IF ~P4.TreeLiteral[v] THEN Log.ErrorTree[nonConstant, v];
            RETURN};

        InlineOp: Tree.Map = {RETURN[UpdateList[t, InlineByte]]};


        DefineSEVar: PROC[ids: Tree.Link] = {

            UpdateSE: Tree.Scan = {
                sei: ISEIndex = ItemId[t];
                seb[sei].constant _ FALSE};

            ScanList[ids, UpdateSE]};

        DefineSEValue: PROC[ids: Tree.Link, value: UNSPECIFIED_0, info: CBTIndex_CBTNull] = {

            UpdateSE: Tree.Scan = {
                sei: ISEIndex = ItemId[t];
                seb[sei].constant _ TRUE;
                seb[sei].idValue _ value;  seb[sei].idInfo _ info};

            ScanList[ids, UpdateSE]};

        AugmentSEValue: PROC[
            ids: Tree.Link,
            type: ExtensionType, extension: Tree.Link,
            copy: BOOL_FALSE] = {

            UpdateSE: Tree.Scan = {
                sei: ISEIndex = ItemId[t];
                EnterExtension[sei, type, IF copy THEN IdentityMap[extension] ELSE extension];
                copy _ TRUE};

            ScanList[ids, UpdateSE]};


        MarkAndCheckSE: PROC[ids: Tree.Link, initialized: BOOL] = {

            UpdateSE: Tree.Scan = {
                sei: ISEIndex = ItemId[t];
                seb[sei].mark4 _ TRUE;
                IF dataPtr.interface THEN CheckDefinition[sei, initialized];
                IF seb[sei].idType = typeTYPE AND CtxLevel[seb[sei].idCtx] # lZ THEN
                    seb[sei].idValue _ sei - ISEIndex.FIRST
                };

            ScanList[ids, UpdateSE]};


        CheckDefinition: PROC[sei: ISEIndex, initialized: BOOL] = {
            SELECT seb[sei].idCtx FROM
                dataPtr.mainCtx =>
                    SELECT LinkMode[sei] FROM
                        val => IF ~initialized OR seb[sei].extended THEN RETURN;
                        ref => IF ~initialized THEN RETURN;
                        manifest, type => IF ConstantId[sei] THEN RETURN;
                        ENDCASE;
                ENDCASE => RETURN;
            Log.ErrorSei[nonDefinition, sei]};


        CheckDefaults: PROC[t: Tree.Link] = {

            TestDefaults: Tree.Scan = {
                node: Tree.Index = GetNode[t];
                saveIndex: SourceMap.Loc = dataPtr.textIndex;
                sei: ISEIndex = FirstId[node];
                dataPtr.textIndex _ tb[node].info;
                IF seb[sei].extended THEN {
                    type: Type = (IF seb[sei].idType = typeTYPE THEN sei ELSE seb[sei].idType);
                    nType: CSEIndex = NormalType[type];

                    TestDefault: Tree.Map = {
                        IF OpName[t] = void THEN v _ t
                        ELSE {
                            v _ P4.AdjustBias[P4.Rhs[t, type, $init], -BiasForType[type]];
                            IF P4.TreeLiteral[v] AND (
                                WITH n: seb[nType] SELECT FROM
                                    basic => n.code # codeINT OR P4.VRep[] = P4.signed,
                                    ENDCASE => TRUE) THEN
                                    v _ P4.ForceType[v, type];
                            IF ~(P4.VProp[].noFreeVar OR (SIGNAL VarInit[])) THEN
                                Log.ErrorTree[nonConstant, v];
                            P4.VPop[]};
                        RETURN};

                    t: Tree.Link _ FindExtension[sei].tree;
                    v: Tree.Link _ UpdateList[IdentityMap[t], TestDefault];
                    IF t.tag # symbol AND P4.StructuredLiteral[v] THEN
                        UpdateDefaults[tb[node].son[1], v]
                    ELSE v _ FreeTree[v]};
                dataPtr.textIndex _ saveIndex};

            IF dataPtr.interface THEN ScanList[t, TestDefaults]};

        UpdateDefaults: PROC[ids: Tree.Link, v: Tree.Link] = {
            copy: BOOL _ FALSE;

            UpdateDefault: Tree.Scan = {
                sei: ISEIndex = ItemId[t];
                old: Tree.Link _ FindExtension[sei].tree;
                EnterExtension[sei, default, IF copy THEN IdentityMap[v] ELSE v];
                copy _ TRUE;
                [] _ FreeTree[old]};

            ScanList[ids, UpdateDefault]};


        TrimTree: Tree.Map = {
            WITH t SELECT FROM
                subtree => {
                    node: Tree.Index = index;
                    SELECT tb[node].name FROM
                        body => {
                            OPEN tb[node];
                            PushTree[TrimTree[son[1]]];
                            PushTrimDecls[son[2]];
                            PushTree[TrimTree[son[3]]];  PushTree[TrimTree[son[4]]];
                            PushNode[body, 4];  SetInfo[info];
                            SetAttr[1, attr1]; SetAttr[2, attr2]; SetAttr[3, attr3];
                            v _ PopTree[]};
                        block => {
                            OPEN tb[node];
                            PushTrimDecls[son[1]];  PushTree[TrimTree[son[2]]];
                            PushNode[block, 2];  SetInfo[info];
                            SetAttr[1, attr1]; SetAttr[2, attr2]; SetAttr[3, attr3];
                            v _ PopTree[]};
                        cdot => v _ TrimTree[tb[node].son[2]];
                        ENDCASE => v _ CopyTree[[@tb, t], TrimTree]};
                ENDCASE => v _ t;
            RETURN};

        PushTrimDecls: PROC[t: Tree.Link] = {
            IF OpName[t] = initlist THEN {
                node: Tree.Index = GetNode[t];
                PushTree[TrimTree[tb[node].son[1]]];  PushTrimDecls[tb[node].son[2]];
                PushNode[initlist, 2];  SetInfo[tb[node].info]}
            ELSE {
                n: CARDINAL _ 0;

                PushDecl: Tree.Scan = {
                    node: Tree.Index = GetNode[t];
                    SELECT tb[node].name FROM
                        typedecl => NULL;
                        decl =>
                            IF tb[node].son[3] # Tree.Null THEN {
                                OPEN  tb[node];
                                PushTree[TrimTree[son[1]]];  PushTree[Tree.Null];
                                PushTree[TrimTree[son[3]]];
                                PushNode[decl, 3];  SetInfo[info];
                                SetAttr[1, attr1]; SetAttr[2, attr2]; SetAttr[3, ~P4.mark];
                                n _ n+1};
                        ENDCASE => ERROR};

                ScanList[t, PushDecl];  PushList[n]}
            };


        DeclUpdate: PUBLIC PROC[item: Tree.Link] RETURNS[update: Tree.Link] = {
            node: Tree.Index = GetNode[item];
            IF tb[node].name = typedecl OR tb[node].son[3] = Tree.Null THEN
                update _ Tree.Null
            ELSE {
                OPEN tb[node];
                type: Type = TypeForDecl[node];
                rewrite: BOOL = SELECT OpName[tb[node].son[3]] FROM
                    body, signalinit => FALSE,
                    ENDCASE => TRUE;
                n: CARDINAL = ListLength[tb[node].son[1]];
                ScanList[tb[node].son[1], PushTree];
                PushTree[tb[node].son[3]];
                FOR i: CARDINAL IN [1 .. n] DO
                    IF i = n THEN PushNode[assign, 2]
                    ELSE {PushNode[assignx, 2]; SetInfo[type]};
                    SetInitAttr[type, ConstInit[tb[node].son[3]]];
                    IF rewrite THEN PushTree[P4.RewriteAssign[GetNode[PopTree[]], type]];
                    ENDLOOP;
                SetInfo[info];  update _ PopTree[];  tb[node].son[3] _ Tree.Null};
            FreeNode[node];
            RETURN};


        SetInitAttr: PROC[type: Type, const: BOOL] = {
            SetAttr[1, TRUE];
            IF P4.currentLevel = lG AND ~const THEN
                SELECT RCType[type] FROM
                    simple => {SetAttr[2, TRUE]; SetAttr[3, FALSE]};
                    composite => {SetAttr[2, TRUE]; SetAttr[3, TRUE]};
                    ENDCASE => SetAttr[2, FALSE]
            ELSE SetAttr[2, FALSE]};


        TypeExp: PUBLIC PROC[typeExp: Tree.Link, body, indirect: BOOL _ FALSE] = {
            <<body => arg records subsumed by frame>>
            WITH typeExp SELECT FROM
                symbol =>
                    IF ~indirect THEN {
                        iSei: ISEIndex = index;
                        IF ~seb[iSei].mark4 THEN DeclItem[[subtree[index: seb[iSei].idValue]]]};
                subtree => {
                    node: Tree.Index = index;
                    SELECT tb[node].name FROM
                        discrimTC => TypeExp[tb[node].son[1], FALSE, indirect];
                        cdot => TypeExp[tb[node].son[2], body, indirect];
                        implicitTC, linkTC => NULL;
                        frameTC => NULL;
                        ENDCASE => {
                            OPEN tb[node];
                            sei: CSEIndex = UnderType[info];
                            IF ~seb[sei].mark4 THEN
                                WITH type: seb[sei] SELECT FROM
                                    enumerated =>
                                        IF type.machineDep THEN
                                            [nValues:type.nValues, sparse:type.sparse] _
                                                LayoutEnum[son[1], type.valueCtx];
                                    record => {
                                        ENABLE VarInit => {RESUME[FALSE]};
                                        ScanList[son[1], DeclItem];
                                        IF attr1 THEN ScanList[son[1], AssignPositions];
                                        WITH type SELECT FROM
                                            notLinked =>
                                                IF attr1 THEN P4.CheckFields[LOOPHOLE[sei, RecordSEIndex], 0]
                                                ELSE P4.LayoutFields[LOOPHOLE[sei, RecordSEIndex], 0];
                                            ENDCASE;
                                        ExtractFieldAttributes[LOOPHOLE[sei, RecordSEIndex]];
                                        CheckDefaults[son[1]]};
                                    ref => {
                                        IF type.var AND FALSE THEN Log.Error[unimplemented];
                                        TypeExp[son[1], FALSE, TRUE]};
                                    array => {
                                        maxArraySize: WordCount = BitCount.LAST/WordLength;
                                        IF son[1] # Tree.Null THEN TypeExp[son[1]];
                                        TypeExp[son[2], FALSE, indirect];
                                        IF Cardinality[type.indexType] >
                                            MaxCardinality[type.componentType, type.packed, maxArraySize] THEN
                                                Log.Error[arraySize]};
                                    arraydesc => TypeExp[son[1], FALSE, TRUE];
                                    transfer => {
                                        origin, newOrigin: CARDINAL;
                                        rSei: RecordSEIndex;
                                        origin _ SELECT type.mode FROM
                                            program => PrincOps.globalbase,
                                            signal, error => PrincOps.localbase+3,
                                            proc => PrincOps.localbase,
                                            ENDCASE => 0;
                                        IF OpName[son[1]] # anyTC THEN {
                                            ScanList[son[1], DeclItem];  CheckDefaults[son[1]]};
                                        rSei _ ArgRecord[type.typeIn];
                                        IF rSei # RecordSENull THEN {
                                            seb[rSei].hints.comparable _ TRUE;    -- for now
                                            newOrigin _ P4.LayoutArgs[rSei, origin, body];
                                            seb[rSei].length _ (newOrigin - origin)*WordLength;
                                            seb[rSei].mark4 _ TRUE;
                                            origin _ newOrigin};
                                        IF OpName[son[2]] # anyTC THEN {
                                            ScanList[son[2], DeclItem];  CheckDefaults[son[2]]};
                                        rSei _ ArgRecord[type.typeOut];
                                        IF rSei # RecordSENull THEN {
                                            seb[rSei].hints.comparable _ TRUE;    -- for now
                                            seb[rSei].length _ (P4.LayoutArgs[rSei, origin, body]-origin)*WordLength;
                                            seb[rSei].mark4 _ TRUE}
                                        };
                                    definition => NULL;
                                    union => {
                                        DeclItem[son[1]];
                                        IF attr1 AND type.controlled THEN AssignPositions[son[1]];
                                        ProcessVariants[UnderType[seb[type.tagSei].idType], son[2]]};
                                    sequence => {
                                        DeclItem[son[1]];
                                        IF attr1 AND type.controlled THEN AssignPositions[son[1]];
                                        TypeExp[son[2], FALSE, indirect]};
                                    relative => {TypeExp[son[1], FALSE, TRUE];  TypeExp[son[2], FALSE, TRUE]};
                                    opaque =>
                                        IF son[1] # Tree.Null THEN {
                                            son[1] _ P4.Rhs[son[1], dataPtr.idCARDINAL];  P4.VPop[];
                                            IF P4.TreeLiteral[son[1]] THEN
                                                type.length _ P4.TreeLiteralValue[son[1]]*WordLength};
                                    zone => NULL;
                                    subrange => {
                                        subNode: Tree.Index;
                                        tSei: CSEIndex = UnderType[type.rangeType];
                                        TypeExp[son[1], FALSE, indirect];
                                        subNode _ GetNode[son[2]];
                                        IF P4.Interval[subNode, 0, P4.both] THEN
                                            [type.origin, type.range] _ P4.ConstantInterval[subNode
                                                ! P4.EmptyInterval => {type.empty _ TRUE; RESUME}]
                                        ELSE type.origin _ type.range _ 0;
                                        type.filled _ TRUE;
                                        SELECT P4.VRep[] FROM
                                            P4.none => Log.ErrorTree[mixedRepresentation, son[2]];
                                            P4.unsigned => IF type.origin < 0 THEN Log.Error[subrangeNesting];
                                            ENDCASE;
                                        P4.VPop[];
                                        WITH cover: seb[tSei] SELECT FROM
                                            subrange =>    -- incomplete test
                                                IF  type.origin < cover.origin
                                                    OR (~type.empty AND type.range > cover.range) THEN
                                                        Log.Error[subrangeNesting];
                                            ENDCASE => NULL;
                                        son[2] _ FreeTree[son[2]]};
                                    long => TypeExp[son[1], FALSE, indirect];
                                    any => NULL;
                                    ENDCASE => ERROR;
                            seb[sei].mark4 _ TRUE}
                    };
                ENDCASE => ERROR};


    <<machine dependent representations>>

        MaxCardinality: PUBLIC PROC[type: Type, packed: BOOL, maxSize: WordCount]
            RETURNS [LONG CARDINAL] = {
            maxBits: BitCount =
                (IF maxSize > BitCount.LAST/WordLength THEN BitCount.LAST ELSE maxSize*WordLength);
            eSize: BitCount = BitsPerElement[type, packed];
            RETURN[maxBits/(IF eSize # 0 THEN eSize ELSE 1)]};


        EvalUnsigned: PROC[t: Tree.Link, default: CARDINAL]
            RETURNS [v: Tree.Link, n: CARDINAL] = {
            v _ P4.Rhs[t, dataPtr.idCARDINAL];  P4.VPop[];
            IF P4.TreeLiteral[v] THEN n _ P4.TreeLiteralValue[v]
            ELSE {Log.ErrorTree[nonConstant, v]; n _ default};
            RETURN};


        LayoutEnum: PROC[t: Tree.Link, ctx: CTXIndex]
            RETURNS [sparse: BOOL, nValues: CARDINAL] = {
            sei: ISEIndex;
            started: BOOL;
            last: CARDINAL;

            AssignElement: Tree.Scan = {
                val: CARDINAL;
                WITH t SELECT FROM
                    subtree => {
                        node: Tree.Index = index;
                        [tb[node].son[2], val] _
                            EvalUnsigned[tb[node].son[2], IF started THEN last+1 ELSE 0]};
                    ENDCASE => val _ IF started THEN last+1 ELSE 0;
                IF ~started THEN {sparse _ (val#0); started _ TRUE}
                ELSE {
                    IF val <= last THEN Log.ErrorSei[enumOrder, sei];
                    IF val # last+1 THEN sparse _ TRUE};
                last _ seb[sei].idValue _ val;  sei _ NextSe[sei]};

            started _ sparse _ FALSE; sei _ FirstCtxSe[ctx];
            ScanList[t, AssignElement];
            nValues _ IF ~started THEN 0 ELSE last+1;  RETURN};


        AssignPositions: PROC[item: Tree.Link] = {
            node: Tree.Index = GetNode[item];
            saveIndex: SourceMap.Loc = dataPtr.textIndex;
            type: Type = TypeForTree[tb[node].son[2]];
            nB, nW: CARDINAL;

            AssignPosition: Tree.Scan = {
                wd, bL, bR: CARDINAL;
                dB: CARDINAL = IF nB=0 THEN 0 ELSE nB-1;
                sei: ISEIndex = ItemId[t];
                node: Tree.Index = GetNode[NthSon[t, 2]];
                [tb[node].son[1], wd] _ EvalUnsigned[tb[node].son[1], 0];
                IF tb[node].son[2] = Tree.Null THEN {
                    bL _ 0;  bR _ IF nB = 0 THEN 0 ELSE nW*WordLength - 1}
                ELSE {
                    subNode: Tree.Index = GetNode[tb[node].son[2]];
                    [tb[subNode].son[1], bL] _ EvalUnsigned[tb[subNode].son[1], 0];
                    [tb[subNode].son[2], bR] _ EvalUnsigned[tb[subNode].son[2], dB]};
                wd _ wd + bL/WordLength;
                IF bR >= bL THEN bR _ bR - (bL/WordLength)*WordLength;
                bL _ bL MOD WordLength;
                IF (SELECT TRUE FROM
                    (nB = 0) => bR < bL,
                    (nB >= WordLength) => bL # 0 OR bR # bL + dB,
                    ENDCASE => bR > WordLength OR bR < bL + dB) THEN {
                    Log.ErrorSei[fieldPosition, sei]; bR _ bL + dB};
                seb[sei].idValue _ BitAddress[wd:wd, bd:bL];
                seb[sei].idInfo _ IF nB=0 AND tb[node].son[2] = Tree.Null THEN 0 ELSE bR-bL + 1};

            dataPtr.textIndex _ tb[node].info;
            nB _ P4.BitsForType[type];  nW _ (nB+(WordLength-1))/WordLength;
            ScanList[tb[node].son[1], AssignPosition];
            dataPtr.textIndex _ saveIndex};


        ExtractFieldAttributes: PROC[rType: RecordSEIndex] = {
            <<compatibility version>>
            type: CSEIndex;
            comparable, privateFields: BOOL;
            comparable _ TRUE;   privateFields _ FALSE;
            FOR sei: ISEIndex _ FirstCtxSe[seb[rType].fieldCtx], NextSe[sei] UNTIL sei = ISENull DO
                IF ~seb[sei].public THEN privateFields _ TRUE;
                type _ UnderType[seb[sei].idType];
                WITH t: seb[type] SELECT FROM
                    record =>
                        IF ~t.hints.comparable AND ~ComparableType[type] THEN comparable _ FALSE;
                    array => IF ~ComparableType[type] THEN comparable _ FALSE;
                    union => IF ~t.hints.equalLengths THEN comparable _ FALSE;
                    sequence => comparable _ FALSE;
                    ENDCASE;
                ENDLOOP;
            seb[rType].hints.comparable _ comparable;
            seb[rType].hints.privateFields _ privateFields};


        ProcessVariants: PROC[tagType: CSEIndex, list: Tree.Link] = {
            lb, ub: CARDINAL;

            MapTag: PROC [vSei: ISEIndex] RETURNS [CARDINAL] = {
                WITH t: seb[tagType] SELECT FROM
                    enumerated =>
                        IF t.machineDep THEN {
                            sei: ISEIndex = SearchContext[seb[vSei].hash, t.valueCtx];
                            IF sei # ISENull THEN RETURN[seb[sei].idValue]};
                    ENDCASE;
                RETURN[seb[vSei].idValue]};

            CheckTag: Tree.Scan = {
                sei: ISEIndex = ItemId[t];
                tag: CARDINAL = MapTag[sei];
                IF tag NOT IN [lb .. ub) THEN Log.ErrorSei[boundsFault, sei];
                seb[sei].idValue _ tag - lb};

            ProcessVariant: Tree.Scan = {
                saveIndex: SourceMap.Loc = dataPtr.textIndex;
                node: Tree.Index = GetNode[t];
                dataPtr.textIndex _ tb[node].info;
                ScanList[tb[node].son[1], CheckTag];
                DeclItem[t];
                dataPtr.textIndex _ saveIndex};

            lb _ BiasForType[tagType];  ub _ lb + CARDINAL[Cardinality[tagType]];
            ScanList[list, ProcessVariant]};


        TypeForTree: PUBLIC PROC[t: Tree.Link] RETURNS[Type] = {
            RETURN[WITH t SELECT FROM
                symbol => index,
                subtree => tb[index].info,
                ENDCASE => typeANY]
            };



        CanonicalType: PUBLIC PROC[type: Type] RETURNS[Type] = {
            sei: CSEIndex = UnderType[type];
            RETURN[WITH t: seb[sei] SELECT FROM
                subrange => CanonicalType[t.rangeType],
                record =>
                    IF t.hints.unifield AND CtxEntries[t.fieldCtx] = 1
                        THEN CanonicalType[seb[ctxb[t.fieldCtx].seList].idType]
                        ELSE type,
                ENDCASE => type]
            };


        BiasForType: PUBLIC PROC[type: Type] RETURNS[INTEGER] = {
            sei: CSEIndex = UnderType[type];
            RETURN[IF sei = CSENull
                THEN 0
                ELSE
                    WITH t: seb[sei] SELECT FROM
                        subrange => t.origin,
                        record =>
                            IF t.hints.unifield AND CtxEntries[t.fieldCtx] = 1
                                THEN BiasForType[seb[ctxb[t.fieldCtx].seList].idType]
                                ELSE 0,
                        ENDCASE => 0]
            };

        RepForType: PUBLIC PROC[type: Type] RETURNS[P4.Repr] = {
            sei: CSEIndex = UnderType[type];
            RETURN[IF sei = CSENull
                THEN P4.none
                ELSE
                    WITH t: seb[sei] SELECT FROM
                        basic =>
                            SELECT t.code FROM
                                codeANY => P4.both + P4.other,
                                codeINT => P4.signed,
                                codeCHAR => P4.both,
                                ENDCASE => P4.other,
                        enumerated => P4.both,
                        ref => P4.unsigned,
                        record =>
                            IF t.hints.unifield AND CtxEntries[t.fieldCtx] = 1
                                THEN RepForType[seb[ctxb[t.fieldCtx].seList].idType]
                                ELSE P4.other,
                        relative => RepForType[t.offsetType],
                        subrange =>
                            IF t.origin >= 0
                                THEN (IF CARDINAL[t.origin] + t.range > 77777b THEN P4.unsigned ELSE P4.both)
                                ELSE (IF t.range <= 77777b THEN P4.signed ELSE P4.none),
                        long => RepForType[t.rangeType],
                        opaque => IF t.lengthKnown THEN P4.other ELSE P4.none,
                        ENDCASE => P4.other]
            };

        SparseRep: PUBLIC PROC[type: Type] RETURNS[BOOL] = {
            nType: CSEIndex = NormalType[type];
            RETURN[WITH t: seb[nType] SELECT FROM
                enumerated => t.sparse,
                ENDCASE => FALSE]
            };


        WordsForType: PUBLIC PROC[type: Type] RETURNS[WordCount] = {
            RETURN[IF ~seb[type].mark4
                THEN (P4.BitsForType[type]+(WordLength-1))/WordLength
                ELSE SymbolOps.WordsForType[type]]
            };


        ComparableType: PUBLIC PROC[type: Type] RETURNS[BOOL] = {
            <<compatibility version>>
            sei: CSEIndex = UnderType[type];
            RETURN[WITH t: seb[sei] SELECT FROM
                record => t.hints.comparable OR t.argument,    -- for now
                array => ~SparseRep[t.indexType]
                    AND ComparableType[t.componentType]
                    AND WordsForType[type] <= P4.OpWordCount.LAST,
                opaque => t.lengthKnown,
                any => FALSE,
                ENDCASE => TRUE]
            };

        DefaultBasicOps: PUBLIC PROC[type: Type, size: BitCount] RETURNS[BOOL] = {
            next: Type;
            FOR s: Type _ type, next DO
                WITH se: seb[s] SELECT FROM
                    id => {
                        sei: ISEIndex = LOOPHOLE[s];
                        IF seb[sei].extended THEN {
                            IF OpName[FindExtension[sei].tree] # void THEN RETURN[FALSE] ELSE EXIT};
                        next _ seb[sei].idInfo};
                    cons =>
                        WITH t: se SELECT FROM
                            ref => IF t.counted THEN RETURN[FALSE] ELSE EXIT;
                            array => next _ t.componentType;
                            record => IF t.hints.default THEN RETURN[FALSE] ELSE EXIT;
                            transfer => IF t.mode = port THEN RETURN[FALSE] ELSE EXIT;
                            long => next _ t.rangeType;
                            zone => IF t.counted THEN RETURN[FALSE] ELSE EXIT;
                            ENDCASE => EXIT;
                    ENDCASE;
                ENDLOOP;
            RETURN[WordsForType[type]*WordLength = size
                AND ComparableType[type] AND TypeLink[type] = nullType]};

        }.

