-- file Pass3S.Mesa
-- last modified by Satterthwaite, October 31, 1979  1:54 PM

DIRECTORY
  ComData: FROM "comdata"
    USING [
      bodyIndex, mainBody, monitored, nTypeCodes, stopping, textIndex,
      typeBOOLEAN, typeMap],
  Copier: FROM "copier" USING [CopyXferType],
  InlineDefs: FROM "inlinedefs" USING [BITAND],
  Log: FROM "log" USING [Error, ErrorTree, Warning, WarningTree],
  Pass3: FROM "pass3"
    USING [
      continued, currentBody, implicitAttr, implicitTree, implicitType,
      enclosingBody, lockHeld, lockNode, markCatch],
  P3: FROM "p3"
    USING [
      Attr, BodyData, NPUse, BoundNP, SequenceNP,
      phraseNP,
      --And,-- Apply, Assignment, BumpCount, CanonicalType, CheckDisjoint,
      CloseBase, ClearRefStack, CopyLock, DeclList, Discrimination, Exp,
      Extract, FindLockParams, IdentifiedType, LockVar, MatchFields,
      MiscStmt, OpenBase, OperandLhs, OrderedType, PopCtx, PushCtx,
      Range, RAttr, RecordMention, Rhs, RPop, RPush, RType,
      SealRefStack, TargetType, TypeForTree, UnsealRefStack, UpdateTreeAttr,
      InsertCatchLabel],
  Symbols: FROM "symbols"
    USING [seType, ctxType, mdType, bodyType,
      ContextLevel, 
      SEIndex, ISEIndex, CSEIndex, RecordSEIndex, CTXIndex, BTIndex, CBTIndex,
      HTNull, SENull, CSENull, RecordSENull, CTXNull, BTNull,
      lG, typeANY],
  SymbolOps: FROM "symbolops"
    USING [FirstCtxSe, NextSe, TransferTypes, UnderType],
  SystemDefs: FROM "systemdefs" USING [AllocateHeapNode],
  Table: FROM "table" USING [Base, Notifier],
  Tree: FROM "tree" USING [treeType, Index, Link, Map, Null, Scan, NullId],
  TreeOps: FROM "treeops"
    USING [
      FreeNode, GetNode, MakeList, PopTree, PushTree, PushNode,
      ReverseScanList, ScanList, SetAttr, SetInfo, UpdateList];

Pass3S: PROGRAM
    IMPORTS
	InlineDefs, Copier, Log, P3, SymbolOps, SystemDefs, TreeOps,
	dataPtr: ComData, passPtr: Pass3
    EXPORTS P3 =
  BEGIN
  OPEN SymbolOps, Symbols, P3, TreeOps;

  And: PROCEDURE [Attr, Attr] RETURNS [Attr] = LOOPHOLE[InlineDefs.BITAND];

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

  StmtNotify: PUBLIC Table.Notifier =
    BEGIN  -- called by allocator whenever table area is repacked
    tb ← base[Tree.treeType];
    seb ← base[seType];  ctxb ← base[ctxType];  mdb ← base[mdType];
    bb ← base[bodyType];
    END;


 -- parameter usage

  pathNP: PUBLIC NPUse;

 -- bodies and blocks

  current: POINTER TO P3.BodyData = @passPtr.currentBody;
  currentArgCtx: PUBLIC CTXIndex;

  exits: BOOLEAN;

  BodyList: PUBLIC PROCEDURE [firstBti: BTIndex] =
    BEGIN
    bti: BTIndex;
    IF (bti ← firstBti) # BTNull
      THEN
	DO
	WITH bb[bti] SELECT FROM
	  Callable =>  Body[LOOPHOLE[bti, CBTIndex]];
	  ENDCASE => NULL;
	IF bb[bti].link.which = parent THEN EXIT;
	bti ← bb[bti].link.index;
	ENDLOOP;
    END;

  Body: PROCEDURE [bti: CBTIndex] =
    BEGIN
    item: Tree.Index;	-- item designates a declitem with body as last son
    saved: P3.BodyData = current↑;
    saveIndex: CARDINAL = dataPtr.textIndex;
    saveBodyIndex: CBTIndex = dataPtr.bodyIndex;
    saveEnclosing: BTIndex = passPtr.enclosingBody;
    saveLockHeld: BOOLEAN = passPtr.lockHeld;
    node: Tree.Index;
    lockVar: ISEIndex;
    lockBit: BOOLEAN;
    bodyType: SEIndex;
    inRecord, outRecord: RecordSEIndex;
    dataPtr.bodyIndex ← passPtr.enclosingBody ← bti;
    WITH bb[bti].info SELECT FROM
      Internal =>
	BEGIN
	dataPtr.textIndex ← sourceIndex;
	item ← bodyTree;
	current.bodyNode ← node ← GetNode[tb[item].son[3]];
	bodyTree ← node;
	END;
      ENDCASE => ERROR;
    current.level ← bb[bti].level;
    current.entry ← bb[bti].entry ← tb[node].attr1;
    bb[bti].internal ← tb[node].attr2;
    bb[bti].resident ← FALSE;
    passPtr.lockHeld ← bb[bti].entry OR bb[bti].internal;
    bodyType ← TypeForTree[tb[item].son[2]];
    bb[bti].ioType ← SELECT seb[bodyType].seTag FROM
	cons => LOOPHOLE[bodyType, CSEIndex],
	ENDCASE => Copier.CopyXferType[UnderType[bodyType]];
    [inRecord, outRecord] ← TransferTypes[bb[bti].ioType];
    IF inRecord = SENull
      THEN  currentArgCtx ← CTXNull
      ELSE  ctxb[currentArgCtx←seb[inRecord].fieldCtx].level ← bb[bti].level;
    IF outRecord # SENull
      THEN ctxb[seb[outRecord].fieldCtx].level ← bb[bti].level;
    IF current.level = lG AND dataPtr.nTypeCodes # 0
      THEN
	BEGIN
	dataPtr.typeMap ← DESCRIPTOR [
		SystemDefs.AllocateHeapNode[dataPtr.nTypeCodes*SIZE[SEIndex]],
		dataPtr.nTypeCodes];
	dataPtr.nTypeCodes ← 0;
	END;
    PushArgCtx[current.inputRecord ← inRecord];
    BumpArgRefs[current.inputRecord, TRUE];
    PushArgCtx[current.returnRecord ← outRecord];
    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
	BEGIN
	IF (lockVar ← FindLockParams[].actual) # SENull
	  THEN
	    BEGIN
	    lockBit ← seb[lockVar].immutable; seb[lockVar].immutable ← TRUE;
	    END;
	tb[node].son[4] ← CopyLock[];  pathNP ← phraseNP;
	END;
      BEGIN
      ENABLE
	InsertCatchLabel => BEGIN Log.Error[catchLabel]; RESUME END;
      ScanList[tb[node].son[1], OpenItem];
      IF inRecord # SENull THEN
	CheckDisjoint[seb[inRecord].fieldCtx, bb[bti].localCtx];
      IF outRecord # SENull THEN
	CheckDisjoint[seb[outRecord].fieldCtx, bb[bti].localCtx];
      PushCtx[bb[bti].localCtx];
      IF bti = dataPtr.mainBody AND dataPtr.monitored
	THEN
	  BEGIN
	  DeclList[tb[passPtr.lockNode].son[1]];
	  PushCtx[tb[passPtr.lockNode].info];
	  IF (lockVar ← FirstCtxSe[tb[passPtr.lockNode].info]) # SENull
	    THEN  BumpCount[lockVar];
	  tb[passPtr.lockNode].son[2] ← LockVar[tb[passPtr.lockNode].son[2]];
	  PopCtx[];  ClearRefStack[];
	  END;
      DeclList[tb[node].son[2]];
      END;
    current.reachable ← TRUE;
    tb[node].son[3] ← UpdateList[tb[node].son[3], Stmt
	! InsertCatchLabel =>
	  BEGIN IF ~catchSeen THEN Log.Error[catchLabel]; RESUME END];
    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].stopping ← dataPtr.stopping AND bb[bti].level = lG;
    bb[bti].hints ← [	-- temporary
	safe: FALSE,
	argUpdated:
	  inRecord # SENull AND ctxb[seb[inRecord].fieldCtx].varUpdated,
	nameSafe: pathNP # unsafe,
	attr4: FALSE, attr5: FALSE];
    PopArgCtx[outRecord];  PopArgCtx[inRecord];
    IF current.entry AND lockVar # SENull
      THEN seb[lockVar].immutable ← lockBit;
    current↑ ← saved;  passPtr.lockHeld ← saveLockHeld;
    passPtr.enclosingBody ← saveEnclosing;
    dataPtr.bodyIndex ← saveBodyIndex;  dataPtr.textIndex ← saveIndex;
    END;

  Block: PROCEDURE [node: Tree.Index] =
    BEGIN  OPEN tb[node];
    bti: BTIndex = info;
    saveIndex: CARDINAL = dataPtr.textIndex;
    saveEnclosing: BTIndex = passPtr.enclosingBody;
    WITH bb[bti].info SELECT FROM
      Internal => dataPtr.textIndex ← sourceIndex;
      ENDCASE => ERROR;
    passPtr.enclosingBody ← bti;
    PushCtx[bb[bti].localCtx];
    DeclList[son[1]
      ! InsertCatchLabel => BEGIN  Log.Error[catchLabel];  RESUME  END];
    son[2] ← UpdateList[son[2], Stmt];
    BodyList[bb[bti].firstSon];
    PopCtx[];
    passPtr.enclosingBody ← saveEnclosing;  dataPtr.textIndex ← saveIndex;
    END;


  PushArgCtx: PROCEDURE [sei: RecordSEIndex] =
    BEGIN
    IF sei # SENull THEN PushCtx[seb[sei].fieldCtx];
    END;

  PopArgCtx: PROCEDURE [sei: RecordSEIndex] =
    BEGIN
    IF sei # SENull THEN PopCtx[];
    END;

 -- statements

  Stmt: PUBLIC PROCEDURE [stmt: Tree.Link] RETURNS [val: Tree.Link] =
    BEGIN
    node: Tree.Index;
    saveIndex: CARDINAL = dataPtr.textIndex;
    saveMark: BOOLEAN = passPtr.markCatch;
    saveContinued: BOOLEAN = passPtr.continued;
    IF stmt = Tree.Null THEN RETURN [Tree.Null];
    WITH stmt SELECT FROM
      subtree =>
	BEGIN  node ← index;
	dataPtr.textIndex ← tb[node].info;
	IF ~current.reachable AND tb[node].name # list
	  THEN BEGIN Log.Warning[unreachable]; current.reachable ← TRUE END;
	val ← stmt;		-- the default
	passPtr.markCatch ← passPtr.continued ← FALSE;
	SELECT tb[node].name FROM

	  assign =>
	    BEGIN
	    Assignment[node];  RPop[];  pathNP ← SequenceNP[pathNP][phraseNP];
	    END;

	  extract =>
	    BEGIN Extract[node]; pathNP ← SequenceNP[pathNP][phraseNP] END;

	  apply =>
	    BEGIN
	    Apply[node, typeANY, TRUE];
	    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];
	    END;

	  block =>  Block[node];

	  if =>
	    BEGIN  OPEN tb[node];
	    saveReachable: BOOLEAN;
	    entryNP, saveNP: NPUse;
	    son[1] ← Rhs[son[1], dataPtr.typeBOOLEAN];  RPop[]; 
	    pathNP ← entryNP ← SequenceNP[pathNP][phraseNP];  ClearRefStack[];
	    son[2] ← UpdateList[son[2], Stmt];
	    saveReachable ← current.reachable;  saveNP ← pathNP;
	    current.reachable ← TRUE;  pathNP ← entryNP;
	    son[3] ← UpdateList[son[3], Stmt];
	    IF saveReachable THEN current.reachable ← TRUE;
	    pathNP ← BoundNP[saveNP][pathNP];
	    END;

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

	  label =>
	    BEGIN  OPEN tb[node];
	    InsertLabels[son[2]];
	    son[1] ← UpdateList[son[1], Stmt];
	    DeleteLabels[son[2]];
	    LabelList[son[2]];
	    END;

	  goto =>
	    BEGIN
	    ValidateLabel[tb[node].son[1]];  current.reachable ← FALSE;
	    END;

	  return =>  Return[node];

	  exit, loop =>
	    BEGIN
	    IF tb[node].name = exit THEN exits ← TRUE;
	    IF current.loopDepth = 0 THEN Log.Error[exit];
	    current.reachable ← FALSE;
	    END;

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

	  open =>
	    BEGIN  OPEN tb[node];
	    ScanList[son[1], OpenItem];
	    son[2] ← UpdateList[son[2], Stmt];
	    ReverseScanList[son[1], CloseItem];
	    END;

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

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


 -- case driver

  Case: PUBLIC PROCEDURE [node: Tree.Index, selection: Tree.Map] =
    BEGIN  OPEN tb[node];
    saveType: CSEIndex = passPtr.implicitType;
    saveAttr: Attr = passPtr.implicitAttr;
    saveTree: Tree.Link = passPtr.implicitTree;
    entryNP: NPUse;

    attr: Attr;
    eqTests: BOOLEAN;

    CaseItem: Tree.Scan =
      BEGIN
      switchable: BOOLEAN;
      saveIndex: CARDINAL = dataPtr.textIndex;

      CaseTest: Tree.Map =
	BEGIN
	node: Tree.Index = GetNode[t];
	  BEGIN  OPEN tb[node];
	  SELECT name FROM
	    relE =>
	      BEGIN
	      type: CSEIndex;
	      son[2] ← Rhs[son[2], TargetType[passPtr.implicitType]];
	      type ← RType[];
	      info ← dataPtr.typeBOOLEAN;
	      SELECT seb[type].typeTag FROM
		long =>  BEGIN  attr1 ← FALSE;  attr2 ← TRUE  END;
		real =>  BEGIN  attr1 ← TRUE;  attr2 ← FALSE  END;
		ENDCASE =>  attr1 ← attr2 ← FALSE;
	      switchable ← switchable AND RAttr[].const;  v ← t;
	      END;
	    ENDCASE =>
	      BEGIN
	      v ← Rhs[t, dataPtr.typeBOOLEAN];
	      eqTests ← switchable ← FALSE;
	      END;
	  attr ← And[RAttr[], attr];  RPop[];
	  entryNP ← SequenceNP[entryNP][phraseNP];
	  END;
	RETURN
	END;

      node: Tree.Index = GetNode[t];
      dataPtr.textIndex ← tb[node].info;
	BEGIN  OPEN tb[node];
	switchable ← TRUE;
	son[1] ← UpdateList[son[1], CaseTest];  attr1 ← switchable;
	phraseNP ← entryNP;  son[2] ← selection[son[2]];
	END;
      dataPtr.textIndex ← saveIndex;
      END;

    SealRefStack[];
    son[1] ← Exp[son[1], typeANY];
    passPtr.implicitType ← CanonicalType[RType[]]; 
    passPtr.implicitAttr ← attr ← RAttr[];  RPop[];
    entryNP ← phraseNP;
    IF ~IdentifiedType[passPtr.implicitType]
      THEN Log.ErrorTree[relationType, son[1]];
    passPtr.implicitTree ← son[1];  eqTests ← TRUE;
    UnsealRefStack[];
    ScanList[son[2], CaseItem];  attr1 ← eqTests;
    phraseNP ← entryNP;  son[3] ← selection[son[3]];
    RPush[CSENull, attr];
    passPtr.implicitAttr ← saveAttr;
    passPtr.implicitType ← saveType;  passPtr.implicitTree ← saveTree;
    END;


 -- selection

  SelectStmt: PROCEDURE [node: Tree.Index, driver: PROCEDURE [Tree.Index, Tree.Map]] =
    BEGIN
    newReachable: BOOLEAN;
    newNP: NPUse;
    saveNP: NPUse = pathNP;

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

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



 -- iteration

  DoStmt: PROCEDURE [node: Tree.Index] =
    BEGIN  OPEN tb[node];
    forNode: Tree.Index;
    cvType: CSEIndex;
    controlled, cvUpdate, newReachable, saveExits: BOOLEAN;
    saveNP, exitNP: NPUse;
    newReachable ← controlled ← cvUpdate ← FALSE;
    IF son[1] # Tree.Null
      THEN
	BEGIN  forNode ← GetNode[son[1]];
	IF tb[forNode].son[1] = Tree.Null
	  THEN RPush[typeANY, [noXfer:TRUE, noAssign:TRUE, const:FALSE]]
	  ELSE
	    BEGIN
	    tb[forNode].son[1] ← Exp[tb[forNode].son[1], typeANY];
	    IF ~OperandLhs[tb[forNode].son[1]]
	      THEN Log.ErrorTree[nonLHS, tb[forNode].son[1]];
	    WITH tb[forNode].son[1] SELECT FROM
	      symbol =>
		BEGIN	-- account for implicit references
		BumpCount[index];  BumpCount[index];
		END;
	      ENDCASE =>  Log.ErrorTree[controlId, tb[forNode].son[1]];
	    END;
	cvType ← TargetType[RType[]];  RPop[];
	SELECT tb[forNode].name FROM
	  forseq =>
	    BEGIN  -- sequencing using a generator
	    OPEN seq: tb[forNode];
	    seq.son[2] ← Rhs[seq.son[2], cvType];  RPop[];
	    cvUpdate ← TRUE;
	    END;
	  upthru, downthru =>
	    BEGIN  -- stepping through an interval
	    controlled ← TRUE;
	    IF ~OrderedType[cvType] AND cvType # typeANY
	      THEN Log.Error[nonOrderedType];
	    tb[forNode].son[2] ← Range[tb[forNode].son[2], cvType];  RPop[];
	    END;
	  ENDCASE =>  ERROR;
	pathNP ← SequenceNP[pathNP][phraseNP];  ClearRefStack[];
	END;
    saveNP ← pathNP;  pathNP ← none;
    IF son[2] # Tree.Null THEN
      BEGIN
      controlled ← TRUE; son[2] ← Rhs[son[2], dataPtr.typeBOOLEAN]; RPop[];
      pathNP ← SequenceNP[pathNP][phraseNP];  ClearRefStack[];
      END;
    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
      BEGIN
      tb[forNode].son[3] ← Rhs[tb[forNode].son[3], cvType];  RPop[];
      pathNP ← SequenceNP[pathNP][phraseNP];  ClearRefStack[];
      END;
    IF pathNP = refset THEN pathNP ← unsafe;
    saveNP ← pathNP ← SequenceNP[saveNP][pathNP];
    IF son[5] # Tree.Null THEN
      BEGIN
      current.reachable ← FALSE;
      LabelList[son[5]];  IF current.reachable THEN newReachable ← TRUE;
      END;
    exitNP ← pathNP;
    IF son[6] # Tree.Null
      THEN
	BEGIN
	current.reachable ← controlled;  pathNP ← saveNP;
	son[6] ← UpdateList[son[6], Stmt];
	IF current.reachable THEN newReachable ← TRUE;
	exitNP ← BoundNP[exitNP][pathNP];
	END
      ELSE IF controlled THEN newReachable ← TRUE;
    ReverseScanList[son[3], CloseItem];
    current.reachable ← newReachable;  pathNP ← exitNP;
    END;


 -- labels

  LabelList: PROCEDURE [t: Tree.Link] =
    BEGIN
    newReachable: BOOLEAN;
    saveNP, newNP: NPUse;

    LabelItem: PROCEDURE [item: Tree.Link] =
      BEGIN
      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];
      END;

    newReachable ← current.reachable;  saveNP ← pathNP;  newNP ← none;
    ScanList[t, LabelItem];
    current.reachable ← newReachable;  pathNP ← newNP;
    END;


  InsertLabels: PROCEDURE [t: Tree.Link] =
    BEGIN
    labelMark: Tree.Link = current.labelList;

    InsertLabel: PROCEDURE [labeled: Tree.Link] =
      BEGIN
      node: Tree.Index = GetNode[labeled];
      saveIndex: CARDINAL = dataPtr.textIndex;
      dataPtr.textIndex ← tb[node].info;
      ScanList[tb[node].son[1], StackLabel];
      dataPtr.textIndex ← saveIndex;
      END;

    StackLabel: PROCEDURE [id: Tree.Link] =
      BEGIN
      t: Tree.Link;
      node: Tree.Index;
      FOR t ← 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[];
      END;

    ScanList[t, InsertLabel];
    END;


  ValidateLabel: PROCEDURE [id: Tree.Link] =
    BEGIN
    t: Tree.Link;
    node: Tree.Index;
    FOR t ← current.labelList, tb[node].son[2] UNTIL t = Tree.Null
      DO
      node ← GetNode[t];
      IF tb[node].son[1] = id THEN
	BEGIN  tb[node].attr1 ← TRUE;  RETURN  END;
      ENDLOOP;
    Log.ErrorTree[unknownLabel, id];
    END;


  DeleteLabels: PROCEDURE [t: Tree.Link] =
    BEGIN
    anyReachable: BOOLEAN;

    DeleteLabel: PROCEDURE [labeled: Tree.Link] =
      BEGIN
      node: Tree.Index = GetNode[labeled];
      saveIndex: CARDINAL = dataPtr.textIndex;
      dataPtr.textIndex ← tb[node].info;  anyReachable ← FALSE;
      ReverseScanList[tb[node].son[1], UnstackLabel];
      tb[node].attr1 ← anyReachable;
      dataPtr.textIndex ← saveIndex;
      END;

    UnstackLabel: PROCEDURE [id: Tree.Link] =
      BEGIN
      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];
      END;

    ReverseScanList[t, DeleteLabel];
    END;


 -- control transfers

  BumpArgRefs: PUBLIC PROCEDURE [record: RecordSEIndex, write: BOOLEAN] =
    BEGIN
    sei: ISEIndex;
    IF record # SENull THEN
      FOR sei ← FirstCtxSe[seb[record].fieldCtx], NextSe[sei] UNTIL sei = SENull
	DO
	IF write THEN BumpCount[sei] ELSE RecordMention[sei];
	ENDLOOP;
    END;

  CheckLocals: PUBLIC PROCEDURE [t: Tree.Link] RETURNS [localsOnly: BOOLEAN] =
    BEGIN
    level: ContextLevel = bb[dataPtr.bodyIndex].level;

    CheckElement: Tree.Scan =
      BEGIN
      sei: ISEIndex;
      WITH t SELECT FROM
	literal => NULL;
	symbol =>
	  BEGIN  sei ← index;
	  IF ~seb[sei].constant AND ctxb[seb[sei].idCtx].level # level
	    THEN localsOnly ← FALSE;
	  END;
	ENDCASE => localsOnly ← FALSE;
      END;

    localsOnly ← TRUE;  ScanList[t, CheckElement];  RETURN
    END;


  Return: PROCEDURE [node: Tree.Index] =
    BEGIN  OPEN tb[node];
    rSei: RecordSEIndex = current.returnRecord;
    IF current.catchDepth # 0
     OR (dataPtr.bodyIndex = dataPtr.mainBody AND rSei = SENull)
      THEN Log.Error[misplacedReturn];
    IF rSei # SENull AND son[1] = Tree.Null
      THEN
	BEGIN
	BumpArgRefs[rSei, FALSE];
	IF seb[ctxb[seb[rSei].fieldCtx].seList].hash = HTNull
	  THEN Log.Error[illDefinedReturn];
	attr2 ← TRUE;
	END
      ELSE
	BEGIN
	son[1] ← MatchFields[rSei, son[1], FALSE];  RPop[];
	pathNP ← SequenceNP[pathNP][phraseNP];
	IF current.entry THEN  attr2 ← CheckLocals[son[1]];
	END;
    IF (attr1 ← current.entry)
      THEN
	BEGIN
	[] ← UpdateTreeAttr[tb[current.bodyNode].son[4]];
	pathNP ← SequenceNP[pathNP][phraseNP];
	END;
    current.reachable ← FALSE;
    END;

  ImpliedReturn: Tree.Map =
    BEGIN
    IF current.returnRecord # SENull OR current.entry
      THEN
	BEGIN
	PushTree[Tree.Null]; PushNode[return, 1]; SetInfo[dataPtr.textIndex];
	PushTree[Stmt[PopTree[]]];
	PushTree[t];  v ← MakeList[-2];
	END
      ELSE  v ← t;
    RETURN
    END;


 -- basing

  OpenItem: Tree.Scan =
    BEGIN
    node: Tree.Index = GetNode[t];
    saveIndex: CARDINAL = dataPtr.textIndex;
    dataPtr.textIndex ← tb[node].info;
    WITH tb[node].son[1] SELECT FROM
      hash =>
	tb[node].son[2] ← OpenBase[tb[node].son[2], index
	 ! InsertCatchLabel => BEGIN Log.Error[catchLabel]; RESUME END];
      ENDCASE =>  ERROR;
    ClearRefStack[];
    dataPtr.textIndex ← saveIndex;
    END;

  CloseItem: Tree.Scan =
    BEGIN
    node: Tree.Index = GetNode[t];
    WITH tb[node].son[1] SELECT FROM
      hash =>  CloseBase[tb[node].son[2], index];
      ENDCASE =>  ERROR;
    END;

  END.