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, FirstCtxSe, NextSe, RCType, SetCtxLevel, TransferTypes, TypeForm, UnderType],
Tree: TYPE USING [Base, Index, Link, Map, Null, Scan, NullId, NullIndex, treeType],
TreeOps: TYPE USING [FreeNode, GetHash, GetNode, MakeList, OpName, NthSon, PopTree, PushTree, PushNode, ReverseScanList, ScanList, SetAttr, SetInfo, 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]};
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[tb[passPtr.lockNode].info];
DeclList[tb[passPtr.lockNode].son[1]];
IF (lockVar ← FirstCtxSe[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[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 = 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: 
CARDINAL] = {
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 ← Tree.NullIndex; seb[sei].mark4 ← FALSE};
seb[sei].idInfo ← 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[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 ← passPtr.checkedANY};
ENDCASE => seb[dataPtr.idANY].idInfo ← typeANY;
 
safety ← new}