
    <<Pass3S.mesa>>
        <<Copyright  1985 by Xerox Corporation.  All rights reserved.>>
        <<Satterthwaite, June 18, 1986 12:29:52 pm PDT>>
        <<Donahue, 10-Dec-81 11:28:15>>
        <<Russ Atkinson (RRA) March 6, 1985 10:44:06 pm PST>>

    DIRECTORY
        A3: TYPE USING [LhsMode, CanonicalType, DefaultInit, IdentifiedType, OperandLhs, TargetType, Voidable],
        Alloc: TYPE USING [Notifier],
        ComData: TYPE USING [bodyIndex, idANY, idBOOL, monitored, ownSymbols, textIndex],
        Log: TYPE USING [Error, ErrorSei, ErrorTree, ErrorTreeOp, ErrorType, Warning, WarningTree],
        Pass3: TYPE USING [checkedANY, lockNode],
        P3: TYPE USING [Attr, NPUse, Safety, phraseNP, BoundNP, SequenceNP, voidAttr, And, Apply, Assignment, BumpCount, CheckDisjoint, 
        CloseBase, ClearRefStack, CopyLock, DeclList, Discrimination, EnterComposite, EnterType, Exp, Extract, FieldDefault, 
        FindLockParams, FirstId, LockVar, MatchFields, MiscStmt, OpenBase, PopCtx, PushCtx, Range, RAttr, RecordMention, Rhs, RPop, 
        RPush, RType, SealRefStack, UnsealRefStack, UpdateTreeAttr, UType, InsertCatchLabel],
        P3S: TYPE USING [BodyData, ImplicitInfo, implicit],
        SourceMap: TYPE USING [Loc, Up],
        SymLiteralOps: TYPE USING [DescribeRefLits],
        Symbols: TYPE USING [Base, ContextLevel,  ISEIndex, Type, RecordSEIndex, CTXIndex, BTIndex, CBTIndex, HTNull, ISENull, CSENull, 
        RecordSENull, CTXNull, BTNull, lG, RootBti, typeANY, seType, ctxType, mdType, bodyType],
        SymbolOps: TYPE USING [CopyBasicType, CtxLevel, EncodeCard, EncodeTreeIndex, EncodeType, FirstCtxSe, FromType, NextSe, RCType, 
        SetCtxLevel, ToBti, ToCtx, TransferTypes, TypeForm, UnderType],
        Tree: TYPE USING [Base, Index, Link, Map, Null, Scan, nullId, nullIndex, treeType],
        TreeOps: TYPE USING [FreeNode, FromLoc, GetHash, GetNode, GetTag, MakeList, OpName, NthSon, PopTree, PushTree, PushNode, 
        ReverseScanList, ScanList, SetAttr, SetInfo, ToLoc, UpdateList],
        Types: TYPE USING [Assignable];

    Pass3S: PROGRAM
        IMPORTS
            A3, Log, P3, P3S, SourceMap, SymLiteralOps, SymbolOps, TreeOps, Types,
            dataPtr: ComData, passPtr: Pass3
        EXPORTS P3, P3S = {
        OPEN SymbolOps, Symbols, A3, P3, TreeOps;

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

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


    <<parameter usage>>

        pathNP: PUBLIC NPUse _ none;

    <<bodies and blocks>>

        currentBody: PUBLIC P3S.BodyData;
        current: POINTER TO P3S.BodyData = @currentBody;

        currentScope: PUBLIC BTIndex;
        safety: PUBLIC Safety _ none;
        exits: BOOL;

        BodyList: PUBLIC PROC[firstBti: BTIndex] = {
            bti: BTIndex _ firstBti;
            IF bti # BTNull THEN
                DO
                    WITH bb[bti] SELECT FROM
                        Callable => IF nesting # Catch THEN Body[LOOPHOLE[bti, CBTIndex]];
                        ENDCASE => NULL;
                    IF bb[bti].link.which = parent THEN EXIT;
                    bti _ bb[bti].link.index;
                    ENDLOOP
            };

        Body: PROC[bti: CBTIndex] = {
            saved: P3S.BodyData = current^;
            saveIndex: SourceMap.Loc = dataPtr.textIndex;
            saveBodyIndex: CBTIndex = dataPtr.bodyIndex;
            saveScope: BTIndex = currentScope;
            saveSafety: Safety = safety;
            node: Tree.Index;
            lockVar: ISEIndex;
            lockBit: BOOL;
            inRecord, outRecord: RecordSEIndex;
            argLevel: ContextLevel;
            dataPtr.bodyIndex _ currentScope _ bti;
            dataPtr.textIndex _ SourceMap.Up[bb[bti].sourceIndex];
            current.bodyNode _ node _ WITH bb[bti].info SELECT FROM
                Internal => bodyTree,
                ENDCASE => ERROR;
            current.level _ bb[bti].level;  current.entry _ bb[bti].entry;
            SetSafety[SafetyAttr[node]];
            bb[bti].resident _ FALSE;
            current.lockHeld _ bb[bti].entry OR bb[bti].internal;
            argLevel _ IF bti = RootBti THEN lG ELSE current.level;
            IF bb[bti].ioType # typeANY THEN seb[bb[bti].ioType].mark4 _ FALSE;
            [inRecord, outRecord] _ TransferTypes[bb[bti].ioType];
            IF inRecord = RecordSENull THEN current.argCtx _ CTXNull
            ELSE {
                current.argCtx _ seb[inRecord].fieldCtx;
                SetCtxLevel[current.argCtx, argLevel];
                IF argLevel = lG THEN EnterTypes[current.argCtx]};
            IF outRecord # RecordSENull THEN {
                SetCtxLevel[seb[outRecord].fieldCtx, argLevel];
                IF argLevel = lG THEN EnterTypes[seb[outRecord].fieldCtx]};
            PushArgCtx[current.inputRecord _ inRecord];  SetArgRefs[inRecord, 1];
            PushArgCtx[current.returnRecord _ outRecord];  SetArgRefs[outRecord, 0];
            ClearRefStack[];
            <<initialize computed attributes>>
                current.labelList _ Tree.Null;  current.loopDepth _ 0;
                current.catchDepth _ 0;  current.unwindEnabled _ FALSE;
                current.resumeRecord _ RecordSENull;  current.resumeFlag _ FALSE;
            IF ~current.entry THEN pathNP _ none
            ELSE {
                IF (lockVar _ FindLockParams[].actual) # ISENull THEN {
                    lockBit _ seb[lockVar].immutable; seb[lockVar].immutable _ TRUE};
                tb[node].son[4] _ CopyLock[];  pathNP _ phraseNP};

            BEGIN
            ENABLE
                InsertCatchLabel => {Log.Error[catchLabel]; RESUME};
            outInit: Tree.Link _ Tree.Null;
            ScanList[tb[node].son[1], OpenItem];
            current.noXfers _ TRUE;
            IF inRecord # RecordSENull THEN CheckDisjoint[current.argCtx, bb[bti].localCtx];
            IF outRecord # RecordSENull THEN {
                CheckDisjoint[seb[outRecord].fieldCtx, bb[bti].localCtx];
                outInit _ AssignDefaults[seb[outRecord].fieldCtx, bb[bti].inline]};
            PushCtx[bb[bti].localCtx];
            IF bti = RootBti AND dataPtr.monitored THEN {
                PushCtx[ToCtx[tb[passPtr.lockNode].info]];
                DeclList[tb[passPtr.lockNode].son[1]];
                IF (lockVar _ FirstCtxSe[ToCtx[tb[passPtr.lockNode].info]]) # ISENull THEN 
                    BumpCount[lockVar];
                tb[passPtr.lockNode].son[2] _ LockVar[tb[passPtr.lockNode].son[2]];
                PopCtx[];  ClearRefStack[]};
            DeclList[tb[node].son[2]];
            IF outInit # Tree.Null THEN {
                PushTree[outInit];  PushTree[tb[node].son[2]];
                PushNode[initlist, 2];  SetInfo[FromLoc[dataPtr.textIndex]];
                tb[node].son[2] _ PopTree[]};
            END;

            IF bb[bti].type # RecordSENull THEN {
                IF bti = RootBti THEN {
                    EnterTypes[seb[bb[bti].type].fieldCtx];
                    SetBodyAttrs[bb[bti].type]};
                seb[bb[bti].type].mark3 _ TRUE};
            current.reachable _ TRUE;
            tb[node].son[3] _ UpdateList[tb[node].son[3], Stmt
                ! InsertCatchLabel => {IF ~catchSeen THEN Log.Error[catchLabel]; RESUME}];
            IF current.reachable THEN tb[node].son[3] _ ImpliedReturn[tb[node].son[3]];
            BodyList[bb[bti].firstSon];
            PopCtx[];
            ReverseScanList[tb[node].son[1], CloseItem];
            bb[bti].noXfers _ current.noXfers;
            bb[bti].hints _ [
                safe: pathNP <= ref,
                argUpdated: inRecord # RecordSENull AND ctxb[seb[inRecord].fieldCtx].varUpdated,
                nameSafe: pathNP # unsafe,
                noStrings: ];
            PopArgCtx[outRecord];  PopArgCtx[inRecord];
            IF bti = RootBti AND SymLiteralOps.DescribeRefLits[].length # 0 THEN {
                rSei: RecordSEIndex = bb[bti].type;
                seb[rSei].hints.refField _ TRUE; EnterType[rSei]};
            IF current.entry AND lockVar # ISENull THEN seb[lockVar].immutable _ lockBit;
            current^ _ saved;  currentScope _ saveScope;
            SetSafety[saveSafety];
            dataPtr.bodyIndex _ saveBodyIndex;  dataPtr.textIndex _ saveIndex};

        Scope: PUBLIC PROC[node: Tree.Index, body: Tree.Map] = {
            bti: BTIndex = ToBti[tb[node].info];
            saveIndex: SourceMap.Loc = dataPtr.textIndex;
            saveScope: BTIndex = currentScope;
            dataPtr.textIndex _ SourceMap.Up[bb[bti].sourceIndex];
            currentScope _ bti;
            PushCtx[bb[bti].localCtx];
            DeclList[tb[node].son[1] ! InsertCatchLabel => {Log.Error[catchLabel]; RESUME}];
            IF bb[bti].type # RecordSENull THEN seb[bb[bti].type].mark3 _ TRUE;
            tb[node].son[2] _ body[tb[node].son[2]];
            BodyList[bb[bti].firstSon];
            PopCtx[];
            currentScope _ saveScope;  dataPtr.textIndex _ saveIndex};


        PushArgCtx: PROC[rSei: RecordSEIndex] = INLINE {
            IF rSei # RecordSENull THEN PushCtx[seb[rSei].fieldCtx]};

        PopArgCtx: PROC[rSei: RecordSEIndex] = INLINE {IF rSei # RecordSENull THEN PopCtx[]};

        SetArgRefs: PROC[rSei: RecordSEIndex, nRefs: NAT] = {
            IF rSei # RecordSENull THEN {
                seb[rSei].mark4 _ FALSE;
                FOR sei: ISEIndex _ FirstCtxSe[seb[rSei].fieldCtx], NextSe[sei] UNTIL sei = ISENull DO
                    IF seb[sei].mark4 THEN {
                        seb[sei].idValue _ EncodeTreeIndex[Tree.nullIndex]; seb[sei].mark4 _ FALSE};
                    seb[sei].idInfo _ EncodeCard[nRefs];
                    ENDLOOP}
            };

        EnterTypes: PROC[ctx: CTXIndex] = {
            FOR sei: ISEIndex _ FirstCtxSe[ctx], NextSe[sei] UNTIL sei = ISENull DO
                IF RCType[UnderType[seb[sei].idType]] = composite THEN
                    EnterType[UnderType[seb[sei].idType]];
                ENDLOOP
            };

        AssignDefault: PROC[sei: ISEIndex] RETURNS[v: Tree.Link] = {
            IF seb[sei].hash = Symbols.HTNull AND ~seb[sei].extended THEN v _ Tree.Null
            ELSE {
                t: Tree.Link =
                    IF seb[sei].extended THEN FieldDefault[sei] ELSE DefaultInit[seb[sei].idType];
                IF t = Tree.Null THEN {
                    IF ~Voidable[seb[sei].idType] THEN Log.ErrorSei[missingInit, sei];
                    v _ Tree.Null}
                ELSE {
                    lhs: Tree.Link = [symbol[sei]];
                    mode: LhsMode;
                    RecordMention[sei];  mode _ OperandLhs[lhs];
                    PushTree[lhs];  PushTree[t];  PushNode[assign, 2];
                    SetInfo[FromLoc[dataPtr.textIndex]];  SetAttr[1, TRUE];
                    IF mode # counted THEN SetAttr[2, FALSE]
                    ELSE {
                        type: Type = seb[sei].idType;
                        SELECT RCType[type] FROM
                            simple => {SetAttr[2, OpName[t] # nil]; SetAttr[3, FALSE]};
                            composite => {
                                SetAttr[2, TRUE];  SetAttr[3, TRUE]; EnterComposite[type, t, TRUE]};
                            ENDCASE => SetAttr[2, FALSE]};
                    v _ PopTree[]};
                RPop[]};
            RETURN};

        AssignDefaults: PROC[ctx: CTXIndex, copyable: BOOL] RETURNS[Tree.Link] = INLINE {
            n: INTEGER _ 0;
            FOR sei: ISEIndex _ FirstCtxSe[ctx], NextSe[sei] UNTIL sei = ISENull DO
                IF seb[sei].hash # HTNull OR seb[sei].extended THEN {
                    t: Tree.Link = AssignDefault[sei];
                    IF t # Tree.Null THEN {
                        IF seb[sei].hash = HTNull AND copyable THEN Log.ErrorSei[defaultForm, sei];
                        PushTree[t]; n _ n+1; pathNP _ SequenceNP[pathNP][phraseNP]; ClearRefStack[]}};
                ENDLOOP;
            RETURN[MakeList[n]]};

        SetBodyAttrs: PROC[rSei: RecordSEIndex] = INLINE {
            FOR sei: ISEIndex _ FirstCtxSe[seb[rSei].fieldCtx], NextSe[sei] UNTIL sei = ISENull DO
                IF ~seb[sei].constant AND RCType[UnderType[seb[sei].idType]] # none THEN {
                    seb[rSei].hints.refField _ TRUE; EnterType[rSei]; EXIT}
                ENDLOOP
            };


        SafetyAttr: PUBLIC PROC[node: Tree.Index] RETURNS[Safety] = {
            RETURN[SELECT TRUE FROM
                tb[node].attr1 => checked,
                tb[node].attr2 => asserted,
                ENDCASE => none]
            };

        SetSafety: PUBLIC PROC[new: Safety] = {
            IF safety # new THEN {
                SELECT new FROM
                    checked => {
                        IF passPtr.checkedANY = CSENull THEN
                            passPtr.checkedANY _ CopyBasicType[typeANY];
                        seb[dataPtr.idANY].idInfo _ EncodeType[passPtr.checkedANY]};
                    ENDCASE => seb[dataPtr.idANY].idInfo _ EncodeType[typeANY];
                safety _ new}
            };


    <<statements>>

        continued: PUBLIC BOOL;
        markCatch: PUBLIC BOOL;

        Stmt: PUBLIC PROC[stmt: Tree.Link] RETURNS[val: Tree.Link] = {
            node: Tree.Index;
            saveIndex: SourceMap.Loc = dataPtr.textIndex;
            saveMark: BOOL = markCatch;
            saveContinued: BOOL = continued;
            IF stmt = Tree.Null THEN RETURN[Tree.Null];
            WITH stmt SELECT GetTag[stmt] FROM
                subtree => {
                    node _ index;
                    dataPtr.textIndex _ ToLoc[tb[node].info];
                    IF ~current.reachable AND tb[node].name # list THEN {
                        Log.Warning[unreachable]; current.reachable _ TRUE};
                    val _ stmt;        -- the default
                    markCatch _ continued _ FALSE;
                    SELECT tb[node].name FROM

                        assign => {
                            Assignment[node];  RPop[];  pathNP _ SequenceNP[pathNP][phraseNP]};

                        extract => {
                            Extract[node];  RPop[];  pathNP _ SequenceNP[pathNP][phraseNP]};

                        apply => {
                            node _ Apply[node, typeANY, TRUE];  val _ [subtree[node]];
                            SELECT tb[node].name FROM
                                wait => Log.ErrorTree[typeClash, tb[node].son[1]];
                                error => current.reachable _ FALSE;
                                ENDCASE;
                            SELECT RType[] FROM
                                CSENull, typeANY => NULL;
                                ENDCASE => Log.Error[nonVoidStmt];
                            RPop[];   pathNP _ SequenceNP[pathNP][phraseNP]};

                        block => {
                            saveSafety: Safety = safety;
                            SetSafety[SafetyAttr[node]];
                            IF saveSafety = checked AND safety = none THEN Log.Error[unsafeBlock];
                            Scope[node, Stmt];
                            SetSafety[SafetyAttr[node]]};

                        if => {
                            saveReachable: BOOL;
                            entryNP, saveNP: NPUse;
                            tb[node].son[1] _ Rhs[tb[node].son[1], dataPtr.idBOOL];  RPop[]; 
                            pathNP _ entryNP _ SequenceNP[pathNP][phraseNP];  ClearRefStack[];
                            tb[node].son[2] _ UpdateList[tb[node].son[2], Stmt];
                            saveReachable _ current.reachable;  saveNP _ pathNP;
                            current.reachable _ TRUE;  pathNP _ entryNP;
                            tb[node].son[3] _ UpdateList[tb[node].son[3], Stmt];
                            IF saveReachable THEN current.reachable _ TRUE;
                            pathNP _ BoundNP[saveNP][pathNP]};

                        case => SelectStmt[node, Case];
                        bind => SelectStmt[node, Discrimination];
                        do => DoStmt[node];

                        label => {
                            InsertLabels[tb[node].son[2]];
                            tb[node].son[1] _ UpdateList[tb[node].son[1], Stmt];
                            DeleteLabels[tb[node].son[2]];
                            LabelList[tb[node].son[2]]};

                        goto => {ValidateLabel[tb[node].son[1]]; current.reachable _ FALSE};
                        return => Return[node];

                        exit, loop => {
                            IF tb[node].name = exit THEN exits _ TRUE;
                            IF current.loopDepth = 0 THEN Log.Error[exit];
                            current.reachable _ FALSE};

                        null => NULL;
                        syserror => current.reachable _ FALSE;

                        open => {
                            ScanList[tb[node].son[1], OpenItem];
                            tb[node].son[2] _ UpdateList[tb[node].son[2], Stmt];
                            ReverseScanList[tb[node].son[1], CloseItem]};

                        checked => { 
                            saveSafety: Safety = safety;
                            SetSafety[SafetyAttr[node]];
                            IF saveSafety = checked AND safety = none THEN Log.Error[unsafeBlock];
                            tb[node].son[1] _ Stmt[tb[node].son[1]];
                            SetSafety[saveSafety]};

                        list => val _ UpdateList[val, Stmt];
                        ENDCASE => val _ MiscStmt[node]};

                ENDCASE => ERROR;
            IF markCatch THEN {
                PushTree[val];  PushNode[catchmark,1];
                SetInfo[FromLoc[dataPtr.textIndex]];  val _ PopTree[];
                IF continued THEN current.reachable _ TRUE;
                pathNP _ unsafe};
            markCatch _ saveMark;  continued _ saveContinued;
            ClearRefStack[];
            dataPtr.textIndex _ saveIndex;  RETURN};


    <<case driver>>

        Case: PUBLIC PROC[node: Tree.Index, selection: Tree.Map] = {
            OPEN tb[node];
            saveImplicit: P3S.ImplicitInfo = P3S.implicit;
            entryNP: NPUse;

            attr: Attr;
            eqTests: BOOL;

            CaseItem: Tree.Scan = {
                switchable: BOOL;
                saveIndex: SourceMap.Loc = dataPtr.textIndex;

                CaseTest: Tree.Map = {
                    node: Tree.Index = GetNode[t];
                        BEGIN  OPEN tb[node];
                        SELECT name FROM
                            relE => {
                                type: Type;
                                son[2] _ Rhs[son[2], TargetType[P3S.implicit.type]];
                                type _ RType[];
                                info _ FromType[dataPtr.idBOOL];
                                SELECT TypeForm[type] FROM
                                    $long => {attr1 _ FALSE; attr2 _ TRUE};
                                    $real => {attr1 _ TRUE; attr2 _ FALSE};
                                    ENDCASE => {
                                        IF OpName[son[2]] = shorten THEN
                                            Log.ErrorTree[typeClash, NthSon[son[2], 1]];
                                        attr1 _ attr2 _ FALSE};
                                switchable _ switchable AND RAttr[].const;  v _ t};
                            ENDCASE => {
                                v _ Rhs[t, dataPtr.idBOOL]; eqTests _ switchable _ FALSE};
                        attr _ And[RAttr[], attr];  RPop[];
                        entryNP _ SequenceNP[entryNP][phraseNP];
                        END;
                    RETURN};

                node: Tree.Index = GetNode[t];
                IF OpName[tb[node].son[1]] = decl THEN {
                    bti: BTIndex = ToBti[tb[node].info];

                    Item: Tree.Map = {phraseNP _ entryNP; v _ selection[t]};

                    dataPtr.textIndex _ SourceMap.Up[bb[bti].sourceIndex];
                    Log.Error[discrimForm];  switchable _ FALSE;
                    Scope[node, Item]}
                ELSE {
                    dataPtr.textIndex _ ToLoc[tb[node].info];
                    switchable _ TRUE;
                    tb[node].son[1] _ UpdateList[tb[node].son[1], CaseTest];
                    tb[node].attr1 _ switchable;
                    phraseNP _ entryNP;  tb[node].son[2] _ selection[tb[node].son[2]]};
                dataPtr.textIndex _ saveIndex};

            SealRefStack[];
            son[1] _ Exp[son[1], typeANY];
            P3S.implicit.type _ CanonicalType[RType[]]; 
            P3S.implicit.attr _ attr _ RAttr[];  RPop[];
            entryNP _ phraseNP;
            IF ~IdentifiedType[P3S.implicit.type] THEN Log.ErrorTreeOp[missingOp, son[1], relE];
            P3S.implicit.tree _ son[1];  eqTests _ TRUE;
            UnsealRefStack[];
            ScanList[son[2], CaseItem];  attr1 _ eqTests; attr2 _ attr.const;
            phraseNP _ entryNP;  son[3] _ selection[son[3]];
            RPush[CSENull, attr];
            P3S.implicit _ saveImplicit};


    <<selection>>

        SelectStmt: PROC[node: Tree.Index, driver: PROC[Tree.Index, Tree.Map]] = {
            newReachable: BOOL;
            newNP: NPUse;
            saveNP: NPUse = pathNP;

            Selection: Tree.Map = {
                current.reachable _ TRUE;  pathNP _ SequenceNP[saveNP][phraseNP];
                v _ Stmt[t];
                IF current.reachable THEN newReachable _ TRUE;
                newNP _ BoundNP[newNP][pathNP]};

            newReachable _ FALSE;  newNP _ none;
            driver[node, Selection];  RPop[];
            current.reachable _ newReachable;  pathNP _ newNP};



    <<iteration>>

        DoStmt: PROC[node: Tree.Index] = {
            OPEN tb[node];
            forNode: Tree.Index;
            cvType: Type;
            controlled, block, cvUpdate, newReachable, saveExits: BOOL;
            saveNP, exitNP: NPUse;
            saveScope: BTIndex = currentScope;
            newReachable _ controlled _ block _ cvUpdate _ FALSE;
            IF son[1] # Tree.Null THEN {
                sei: ISEIndex;
                mode: LhsMode;
                forNode _ GetNode[son[1]];
                IF tb[forNode].son[1] = Tree.Null THEN {
                    sei _ ISENull;  mode _ uncounted;  cvType _ typeANY}
                ELSE {
                    IF OpName[tb[forNode].son[1]] # decl THEN {
                        tb[forNode].son[1] _ Exp[tb[forNode].son[1], typeANY];
                        IF (mode _ OperandLhs[tb[forNode].son[1]]) = none THEN
                            Log.ErrorTree[nonLHS, tb[forNode].son[1]];
                        sei _ WITH tb[forNode].son[1] SELECT GetTag[tb[forNode].son[1]] FROM 
                            symbol => index, ENDCASE => ISENull}
                    ELSE {
                        bti: BTIndex = ToBti[tb[forNode].info];
                        declNode: Tree.Index = GetNode[tb[forNode].son[1]];
                        block _ TRUE;  currentScope _ bti;
                        PushCtx[bb[bti].localCtx];
                        DeclList[tb[forNode].son[1]];
                        sei _ FirstId[declNode];  seb[sei].immutable _ TRUE;
                        RPush[seb[sei].idType, voidAttr];
                        mode _ IF CtxLevel[seb[sei].idCtx] = lG THEN counted ELSE uncounted};
                    IF sei # ISENull THEN {BumpCount[sei]; BumpCount[sei]}    -- account for implicit refs
                    ELSE Log.ErrorTree[controlId, tb[forNode].son[1]];
                    cvType _ TargetType[RType[]];  RPop[]};
                SELECT mode FROM
                    counted =>
                        SELECT RCType[cvType] FROM
                            simple => {tb[forNode].attr2 _ TRUE; tb[forNode].attr3 _ FALSE};
                            composite => tb[forNode].attr2 _ tb[forNode].attr3 _ TRUE;
                            ENDCASE => tb[forNode].attr2 _ FALSE;
                    ENDCASE => tb[forNode].attr2 _ FALSE;
                SELECT tb[forNode].name FROM
                    forseq => {
                        OPEN seq: tb[forNode];
                        seq.son[2] _ Rhs[seq.son[2], cvType];  RPop[];
                        IF seq.attr2 AND seq.attr3 THEN
                            EnterComposite[cvType, seq.son[2], OpName[seq.son[1]] = decl];
                        cvUpdate _ TRUE};
                    upthru, downthru => {
                        controlled _ TRUE;
                        tb[forNode].son[2] _ Range[tb[forNode].son[2], cvType];
                        IF ~Types.Assignable[
                              [dataPtr.ownSymbols, UnderType[cvType]], [dataPtr.ownSymbols, UType[]]] THEN
                            Log.ErrorType[typeClash, tb[forNode].son[2], [dataPtr.ownSymbols, cvType]];
                        RPop[]};
                    ENDCASE => ERROR;
                pathNP _ SequenceNP[pathNP][phraseNP];  ClearRefStack[]};
            saveNP _ pathNP;  pathNP _ none;
            IF son[2] # Tree.Null THEN {
                controlled _ TRUE; son[2] _ Rhs[son[2], dataPtr.idBOOL]; RPop[];
                pathNP _ SequenceNP[pathNP][phraseNP];  ClearRefStack[]};
            ScanList[son[3], OpenItem];
            InsertLabels[son[5]];
            current.loopDepth _ current.loopDepth + 1;
            saveExits _ exits;  exits _ FALSE;  
            son[4] _ UpdateList[son[4], Stmt];
            IF exits THEN newReachable _ TRUE;  exits _ saveExits;
            DeleteLabels[son[5]];
            current.loopDepth _ current.loopDepth - 1;
            IF cvUpdate THEN {
                OPEN seq: tb[forNode];
                seq.son[3] _ Rhs[seq.son[3], cvType];  RPop[];
                IF seq.attr2 AND seq.attr3 THEN EnterComposite[cvType, seq.son[3], FALSE];
                pathNP _ SequenceNP[pathNP][phraseNP];  ClearRefStack[]};
            IF pathNP = refset THEN pathNP _ unsafe;
            saveNP _ pathNP _ SequenceNP[saveNP][pathNP];
            IF son[5] # Tree.Null THEN {
                current.reachable _ FALSE;
                LabelList[son[5]];  IF current.reachable THEN newReachable _ TRUE};
            exitNP _ pathNP;
            IF son[6] # Tree.Null THEN {
                current.reachable _ controlled;  pathNP _ saveNP;
                son[6] _ UpdateList[son[6], Stmt];
                IF current.reachable THEN newReachable _ TRUE;
                exitNP _ BoundNP[exitNP][pathNP]}
            ELSE IF controlled THEN newReachable _ TRUE;
            ReverseScanList[son[3], CloseItem];
            current.reachable _ newReachable;  pathNP _ exitNP;
            IF block THEN PopCtx[];
            currentScope _ saveScope};


    <<labels>>

        LabelList: PROC[t: Tree.Link] = {
            newReachable: BOOL;
            saveNP, newNP: NPUse;

            LabelItem: PROC[item: Tree.Link] = {
                node: Tree.Index = GetNode[item];
                current.reachable _ tb[node].attr1;  pathNP _ saveNP;
                tb[node].son[2] _ UpdateList[tb[node].son[2], Stmt];
                IF current.reachable THEN newReachable _ TRUE;
                newNP _ BoundNP[newNP][pathNP]};

            newReachable _ current.reachable;  saveNP _ pathNP;  newNP _ none;
            ScanList[t, LabelItem];
            current.reachable _ newReachable;  pathNP _ newNP};


        InsertLabels: PROC[t: Tree.Link] = {
            labelMark: Tree.Link = current.labelList;

            InsertLabel: PROC[labeled: Tree.Link] = {
                node: Tree.Index = GetNode[labeled];
                saveIndex: SourceMap.Loc = dataPtr.textIndex;
                dataPtr.textIndex _ ToLoc[tb[node].info];
                ScanList[tb[node].son[1], StackLabel];
                dataPtr.textIndex _ saveIndex};

            StackLabel: PROC[id: Tree.Link] = {
                node: Tree.Index;
                FOR t: Tree.Link _ current.labelList, tb[node].son[2] UNTIL t = labelMark DO
                    node _ GetNode[t];
                    IF tb[node].son[1] = id AND id # Tree.nullId THEN Log.ErrorTree[duplicateLabel, id];
                    ENDLOOP;
                PushTree[id];  PushTree[current.labelList];
                PushNode[item, 2];  SetAttr[1, FALSE];  current.labelList _ PopTree[]};

            ScanList[t, InsertLabel]};


        ValidateLabel: PROC[id: Tree.Link] = INLINE {
            node: Tree.Index;
            FOR t: Tree.Link _ current.labelList, tb[node].son[2] UNTIL t = Tree.Null DO
                node _ GetNode[t];
                IF tb[node].son[1] = id THEN {tb[node].attr1 _ TRUE; RETURN};
                ENDLOOP;
            Log.ErrorTree[unknownLabel, id]};


        DeleteLabels: PROC[t: Tree.Link] = {
            anyReachable: BOOL;

            DeleteLabel: PROC[labeled: Tree.Link] = {
                node: Tree.Index = GetNode[labeled];
                saveIndex: SourceMap.Loc = dataPtr.textIndex;
                dataPtr.textIndex _ ToLoc[tb[node].info];  anyReachable _ FALSE;
                ReverseScanList[tb[node].son[1], UnstackLabel];
                tb[node].attr1 _ anyReachable;
                dataPtr.textIndex _ saveIndex};

            UnstackLabel: PROC[id: Tree.Link] = {
                node: Tree.Index;
                node _ GetNode[current.labelList];
                IF tb[node].attr1 THEN anyReachable _ TRUE
                ELSE Log.WarningTree[unusedId, tb[node].son[1]];
                current.labelList _ tb[node].son[2];
                tb[node].son[2] _ Tree.Null;  FreeNode[node]};

            ReverseScanList[t, DeleteLabel]};


    <<control transfers>>

        BumpArgRefs: PUBLIC PROC[record: RecordSEIndex, write: BOOL] = {
            IF record # RecordSENull THEN
                FOR sei: ISEIndex _ FirstCtxSe[seb[record].fieldCtx], NextSe[sei] UNTIL sei = ISENull DO
                    IF write THEN BumpCount[sei] ELSE RecordMention[sei];
                    ENDLOOP};

        CheckLocals: PUBLIC PROC[t: Tree.Link] RETURNS[localsOnly: BOOL] = {
            level: ContextLevel = bb[dataPtr.bodyIndex].level;

            CheckElement: Tree.Scan = {
                WITH t SELECT GetTag[t] FROM
                    literal => NULL;
                    symbol => {
                        sei: ISEIndex = index;
                        IF ~seb[sei].constant AND CtxLevel[seb[sei].idCtx] # level THEN
                            localsOnly _ FALSE
                        };
                    ENDCASE => localsOnly _ FALSE};

            localsOnly _ TRUE;  ScanList[t, CheckElement];  RETURN};


        Return: PROC[node: Tree.Index] = {
            OPEN tb[node];
            rSei: RecordSEIndex = current.returnRecord;
            IF current.catchDepth # 0 OR (dataPtr.bodyIndex = RootBti AND rSei = RecordSENull) THEN
                Log.Error[misplacedReturn];
            IF rSei # RecordSENull AND son[1] = Tree.Null THEN {
                BumpArgRefs[rSei, FALSE];
                FOR sei: ISEIndex _ FirstCtxSe[seb[rSei].fieldCtx], NextSe[sei] UNTIL sei = ISENull DO
                    IF seb[sei].hash = HTNull AND ~seb[sei].extended THEN {
                        Log.Error[illDefinedReturn]; EXIT};
                    ENDLOOP;
                attr2 _ TRUE}
            ELSE {
                son[1] _ IF attr3 AND rSei # RecordSENull
                    THEN Rhs[son[1], rSei]
                    ELSE MatchFields[rSei, son[1]];
                RPop[];
                pathNP _ SequenceNP[pathNP][phraseNP];
                IF current.entry THEN attr2 _ CheckLocals[son[1]]};
            IF (attr1 _ current.entry) THEN {
                [] _ UpdateTreeAttr[tb[current.bodyNode].son[4]];
                pathNP _ SequenceNP[pathNP][phraseNP]};
            current.reachable _ FALSE};

        ImpliedReturn: Tree.Map = INLINE {
            IF current.returnRecord # RecordSENull OR current.entry THEN {
                PushTree[Tree.Null]; PushNode[return, 1];
                SetInfo[FromLoc[dataPtr.textIndex]];  SetAttr[3,  FALSE];
                PushTree[Stmt[PopTree[]]];
                PushTree[t];  v _ MakeList[-2]}
            ELSE v _ t;
            RETURN};


    <<basing>>

        OpenItem: Tree.Scan = {
            node: Tree.Index = GetNode[t];
            saveIndex: SourceMap.Loc = dataPtr.textIndex;
            dataPtr.textIndex _ ToLoc[tb[node].info];
            tb[node].son[2] _ OpenBase[tb[node].son[2], GetHash[tb[node].son[1]]
                ! InsertCatchLabel => {Log.Error[catchLabel]; RESUME}];
            ClearRefStack[];
            dataPtr.textIndex _ saveIndex};

        CloseItem: Tree.Scan = {
            node: Tree.Index = GetNode[t];
            CloseBase[tb[node].son[2], GetHash[tb[node].son[1]]]};

        }.

