-- file Pass3D.Mesa
-- last modified by Satterthwaite, December 20, 1979  11:08 AM

DIRECTORY
  ComData: FROM "comdata"
    USING [
      definitionsOnly, idANY, idCARDINAL, mainCtx, moduleCtx, seAnon,
      textIndex, typeINTEGER, typeSTRING],
  Log: FROM "log" USING [Error, ErrorHti, ErrorSei, ErrorTree],
  P3: FROM "p3"
    USING [
      CircuitCheck, CircuitSignal, Mark, NPUse, SequenceNP,
      pathNP, phraseNP,
      CheckDisjoint, ClearRefStack, CompleteRecord, Exp, FindSe, Interval,
      MakeFrameRecord, PopCtx, PushCtx, RAttr, RecordLhs, RecordMention, 
      Rhs, RPop, RType, SealRefStack, SearchCtxList, SelectVariantType,
      TopCtx, UnsealRefStack, UpdateTreeAttr, VariantUnionType],
  Symbols: FROM "symbols"
    USING [seType, ctxType, mdType,
      SERecord, 
      HTIndex, SEIndex, ISEIndex, CSEIndex, RecordSEIndex, CTXIndex,
      SENull, CTXNull, codeANY, codeINTEGER, lG, lZ, typeANY, typeTYPE],
  SymbolOps: FROM "symbolops"
    USING [
      CtxEntries, EnterExtension, FindExtension, LinkMode, MakeNonCtxSe,
      NormalType, TypeForm, UnderType, XferMode],
  Table: FROM "table" USING [Base, Notifier],
  Tree: FROM "tree" USING [Index, Link, Map, Null, NullIndex, Scan, treeType],
  TreeOps: FROM "treeops"
    USING [
      FreeTree, GetNode, IdentityMap, ListHead, ListLength,
      ScanList, TestTree, UpdateList];

Pass3D: PROGRAM
    IMPORTS
	Log, P3, SymbolOps, TreeOps,
	dataPtr: ComData
    EXPORTS P3 =
  BEGIN
  OPEN TreeOps, SymbolOps, Symbols, P3;

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

  DeclNotify: 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];
    END;


-- signals for type loop detection

  CheckTypeLoop: PUBLIC CircuitCheck = CODE;
  LogTypeLoop: CircuitSignal = CODE;


-- declaration processing

  DeclList: PUBLIC Tree.Scan =
    BEGIN
    ScanList[t, DeclItemA];  ScanList[t, DeclItemBI];
    END;

  DeclItemA: Tree.Scan =
    BEGIN
    node: Tree.Index = GetNode[t];
    type: SEIndex;
    saveIndex: CARDINAL = dataPtr.textIndex;
    IF tb[node].attr3 = P3.Mark THEN RETURN;	-- already processed
    tb[node].attr3 ← P3.Mark;
    dataPtr.textIndex ← tb[node].info;
    tb[node].son[2] ← TypeLink[tb[node].son[2]
	  ! CheckTypeLoop => IF loopNode=node THEN RESUME [TRUE];
	    LogTypeLoop => IF loopNode=node THEN RESUME];
    type ← TypeForTree[tb[node].son[2]];
    IF tb[node].name = typedecl
      THEN  DefineTypeSe[tb[node].son[1], type]
      ELSE  DefineSeType[tb[node].son[1], type, tb[node].attr1];
    ClearRefStack[];
    dataPtr.textIndex ← saveIndex;
    END;

  DeclItemB: Tree.Scan =
    BEGIN
    node: Tree.Index = GetNode[t];
    type: SEIndex;

    ExpInit: PROCEDURE [t: Tree.Link] RETURNS [val: Tree.Link] =
      BEGIN
      val ← Rhs[t, TargetType[UnderType[type]]];  RPop[];
      pathNP ← SequenceNP[pathNP][phraseNP];
      RETURN
      END;

    saveIndex: CARDINAL = dataPtr.textIndex;
    IF tb[node].attr2 = P3.Mark THEN RETURN;	-- already processed
    tb[node].attr2 ← P3.Mark;
    dataPtr.textIndex ← tb[node].info;
    TypeAttr[tb[node].son[2]];
    SELECT tb[node].name FROM
      typedecl => NULL;
      ENDCASE =>
	BEGIN
	type ← TypeForTree[tb[node].son[2]];
	IF tb[node].son[3] # Tree.Null
	  THEN
	    BEGIN
	    ScanList[tb[node].son[1], RecordDeclInit];
	    tb[node].son[3] ← UpdateList[tb[node].son[3], ExpInit];
	    IF VoidItem[tb[node].son[3]] AND ~Voidable[type]
	      THEN Log.ErrorTree[defaultForm, ListHead[tb[node].son[1]]];
	    END;
	DefineSeValue[tb[node].son[1], FALSE, FALSE];
	END;
    ClearRefStack[];
    dataPtr.textIndex ← saveIndex;
    END;

  DeclItemBI: Tree.Scan =
    BEGIN
    node: Tree.Index = GetNode[t];
    expNode: Tree.Index;
    type: SEIndex;
    eqFlag, constFlag, extFlag: BOOLEAN;

    ExpInit: PROCEDURE [t: Tree.Link] RETURNS [val: Tree.Link] =
      BEGIN
      val ← Rhs[t, TargetType[UnderType[type]]];
      constFlag ← eqFlag AND RAttr[].const;  RPop[];
      pathNP ← SequenceNP[pathNP][phraseNP];
      RETURN
      END;

    saveIndex: CARDINAL = dataPtr.textIndex;
    IF tb[node].attr2 = P3.Mark THEN RETURN;	-- already processed
    tb[node].attr2 ← P3.Mark;
    dataPtr.textIndex ← tb[node].info;
    TypeAttr[tb[node].son[2]];  type ← TypeForTree[tb[node].son[2]];
    SELECT tb[node].name FROM
      typedecl =>
	BEGIN
	IF tb[node].son[3] # Tree.Null
	  THEN
	    BEGIN
	    tb[node].son[3] ← UpdateList[tb[node].son[3], ExpInit];
	    IF VoidItem[tb[node].son[3]] AND ~Voidable[type]
	      THEN Log.ErrorTree[defaultForm, ListHead[tb[node].son[1]]];
	    END;
	IF tb[node].son[3] # Tree.Null THEN ProcessDefaults[t, TRUE];
	END;
      ENDCASE =>
	BEGIN
	extFlag ← FALSE;  eqFlag ← tb[node].attr1;
	IF tb[node].son[3] = Tree.Null
	  THEN
	    BEGIN
	    v: Tree.Link = DefaultInit[type];
	    IF v # Tree.Null
	      THEN
		BEGIN
		tb[node].son[3] ← v;  [] ← UpdateTreeAttr[v];
		pathNP ← SequenceNP[pathNP][phraseNP];
		END;
	    constFlag ← FALSE;
	    END
	  ELSE
	    BEGIN
	    ScanList[tb[node].son[1], RecordDeclInit];
	    WITH tb[node].son[3] SELECT FROM
	      subtree =>
		BEGIN  expNode ← index;
		SELECT tb[expNode].name FROM
		  body =>
		    BEGIN  -- defer processing of bodies (see Body)
		    constFlag ← FALSE;
		    SELECT XferMode[type] FROM
		      procedure, program => NULL;
		      ENDCASE =>
			IF TypeForm[type] = definition
			  THEN  constFlag ← TRUE
			  ELSE  Log.Error[bodyType];
		    extFlag ← eqFlag AND tb[expNode].attr3;  -- inline
		    END;
		  inline =>
		    BEGIN
		    IF XferMode[type] # procedure OR ~eqFlag
		      THEN Log.Error[inlineType];
		    tb[expNode].son[1] ←
			    UpdateList[tb[expNode].son[1], InlineOp];
		    constFlag ← eqFlag;
		    END;
		  apply =>
		    IF tb[expNode].son[1] # Tree.Null
		     OR UnderType[type] # dataPtr.typeSTRING
		     OR ListLength[tb[expNode].son[2]] # 1
		      THEN  tb[node].son[3] ← ExpInit[tb[node].son[3]]
		      ELSE
			BEGIN  tb[expNode].name ← stringinit;
			tb[expNode].info ← dataPtr.typeSTRING; 
			tb[expNode].son[2] ← Rhs[ 
				tb[expNode].son[2], dataPtr.typeINTEGER];
			IF ~RAttr[].const
			  THEN Log.ErrorTree[nonConstant, tb[expNode].son[2]];
			RPop[];  constFlag ← FALSE;
			pathNP ← SequenceNP[pathNP][phraseNP];
			END;
		  signalinit =>  constFlag ← FALSE;
		  void =>
		    BEGIN
		    IF ~Voidable[type] THEN
		      Log.ErrorTree[defaultForm, ListHead[tb[node].son[1]]];
		    tb[node].son[3] ← FreeTree[tb[node].son[3]];
		    constFlag ← FALSE;
		    END;
		  ENDCASE =>  tb[node].son[3] ← ExpInit[tb[node].son[3]];
		END;
	      ENDCASE =>  tb[node].son[3] ← ExpInit[tb[node].son[3]];
	    END;
	DefineSeValue[tb[node].son[1], constFlag, extFlag];
	END;
    ClearRefStack[];
    dataPtr.textIndex ← saveIndex;
    END;


  RecordDeclInit: Tree.Scan =
    BEGIN
    sei: ISEIndex;
    WITH t SELECT FROM
      symbol =>  BEGIN sei ← index; RecordMention[sei]; RecordLhs[sei] END;
      ENDCASE => ERROR;
    END;

  InlineOp: Tree.Map =
    BEGIN

    EvalConst: Tree.Map =
      BEGIN
      v ← Rhs[t, dataPtr.typeINTEGER];
      IF ~RAttr[].const THEN Log.ErrorTree[nonConstant, v];
      RPop[];  RETURN
      END;

    RETURN [UpdateList[t, EvalConst]]
    END;


  InterfaceSe: PROCEDURE [sei: ISEIndex] RETURNS [BOOLEAN] = INLINE
    BEGIN
    RETURN [dataPtr.definitionsOnly AND ctxb[seb[sei].idCtx].level = lG]
    END;


  DefineSeType: PROCEDURE [t: Tree.Link, type: SEIndex, fixed: BOOLEAN] =
    BEGIN

    UpdateSe: Tree.Scan =
      BEGIN
      sei: ISEIndex;
      WITH t SELECT FROM
	symbol =>
	  BEGIN  sei ← index;
	  seb[sei].idType ← type;  seb[sei].constant ← FALSE;
	  IF InterfaceSe[sei]
	    THEN  seb[sei].immutable ← seb[sei].immutable OR fixed
	    ELSE
	      BEGIN
	      IF seb[sei].immutable THEN Log.ErrorSei[attrClash, sei];
	      seb[sei].immutable ← fixed
	      END;
	  seb[sei].mark3 ← TRUE;
	  END;
	ENDCASE =>  ERROR;
      END;

    ScanList[t, UpdateSe];
    END;

  DefineSeValue: PROCEDURE [t: Tree.Link, const, ext: BOOLEAN] =
    BEGIN

    UpdateSe: Tree.Scan =
      BEGIN
      sei: ISEIndex;
      WITH t SELECT FROM
	symbol =>
	  BEGIN  sei ← index;
	  seb[sei].constant ← const;  seb[sei].extended ← ext;
	  IF InterfaceSe[sei] AND LinkMode[sei] = val
	    THEN  seb[sei].immutable ← TRUE;
	  END;
	ENDCASE =>  ERROR;
      END;

    ScanList[t, UpdateSe];
    END;


  DefineTypeSe: PROCEDURE [t: Tree.Link, info: SEIndex] =
    BEGIN
    first: BOOLEAN ← TRUE;

    UpdateSe: Tree.Scan =
      BEGIN
      sei: ISEIndex;
      WITH t SELECT FROM
	symbol =>
	  BEGIN  sei ← index;
	  seb[sei].idType ← typeTYPE;  seb[sei].idInfo ← info;
	  seb[sei].immutable ← seb[sei].constant ← TRUE;
	  IF first THEN  BEGIN  info ← sei;  first ← FALSE  END;
	  seb[sei].mark3 ← TRUE;
	  END;
	ENDCASE =>  ERROR;
      END;

    ScanList[t, UpdateSe];   RETURN
    END;


  ProcessDefaults: PROCEDURE [t: Tree.Link, valid: BOOLEAN] =
    BEGIN

    ProcessDefault: Tree.Scan =
      BEGIN
      copy: BOOLEAN;
      node: Tree.Index = GetNode[t];

      DefineDefault: Tree.Scan =
	BEGIN
	WITH t SELECT FROM
	  symbol =>
	    EnterExtension[index, default,
	      IF copy THEN IdentityMap[tb[node].son[3]] ELSE tb[node].son[3]];
	  ENDCASE => ERROR;
	copy ← TRUE;
	END;

      IF tb[node].son[3] # Tree.Null THEN
	BEGIN
	IF ~valid
	  THEN Log.ErrorTree[default, ListHead[tb[node].son[1]]];
	IF TestTree[tb[node].son[3], stringinit]
	  THEN Log.ErrorTree[defaultForm, ListHead[tb[node].son[1]]];
	copy ← FALSE;
	ScanList[tb[node].son[1], DefineDefault]; tb[node].son[3] ← Tree.Null;
	END;
      END;

    ScanList[t, ProcessDefault];
    END;


 -- default merging

  DefaultInit: PUBLIC PROCEDURE [type: SEIndex] RETURNS [v: Tree.Link] =
    BEGIN
    s, next: SEIndex;
    v ← Tree.Null;
    FOR s ← type, next
      DO
      WITH seb[s] SELECT FROM
	id =>
	  BEGIN
	  sei: ISEIndex = LOOPHOLE[s];

	  CopyNonVoid: Tree.Scan =
	    BEGIN
	    IF ~TestTree[t, void] AND v = Tree.Null THEN v ← IdentityMap[t];
	    END;

	  IF seb[sei].extended THEN
	    BEGIN ScanList[FindExtension[sei].tree, CopyNonVoid]; EXIT END;
	  next ← seb[sei].idInfo;
	  END;
	ENDCASE => EXIT;
      ENDLOOP;
    RETURN
    END;

  VoidItem: PUBLIC PROCEDURE [t: Tree.Link] RETURNS [void: BOOLEAN] =
    BEGIN
    TestVoid: Tree.Scan = BEGIN IF TestTree[t, void] THEN void ← TRUE END;
    void ← FALSE;  ScanList[t, TestVoid];  RETURN
    END;

  Voidable: PUBLIC PROCEDURE [type: SEIndex] RETURNS [BOOLEAN] =
    BEGIN
    s, next: SEIndex;
    FOR s ← type, next
      DO
      WITH seb[s] SELECT FROM
	id =>
	  BEGIN
	  sei: ISEIndex = LOOPHOLE[s];
	  IF seb[sei].extended
	    THEN RETURN [VoidItem[FindExtension[sei].tree]];
	  next ← seb[sei].idInfo;
	  END;
	ENDCASE =>  RETURN [TRUE];
      ENDLOOP;
    END;


  ResolveType: PUBLIC PROCEDURE [sei: ISEIndex] =
    BEGIN
    currentCtx: CTXIndex = TopCtx[];
    IF seb[sei].idCtx # currentCtx
      THEN  BEGIN PopCtx[]; ResolveType[sei]; PushCtx[currentCtx] END
      ELSE
	BEGIN
	SealRefStack[];
	DeclItemA[[subtree[index: seb[sei].idValue]]];
	UnsealRefStack[];
	END;
    END;

  ResolveValue: PUBLIC PROCEDURE [sei: ISEIndex] =
    BEGIN
    currentCtx: CTXIndex = TopCtx[];
    IF seb[sei].idCtx # currentCtx
      THEN  BEGIN PopCtx[]; ResolveValue[sei]; PushCtx[currentCtx] END
      ELSE
	BEGIN
	SealRefStack[];
	IF currentCtx = CTXNull OR
	 (ctxb[currentCtx].level = lZ AND currentCtx # dataPtr.moduleCtx)
	  THEN  DeclItemB[[subtree[index: seb[sei].idValue]]]
	  ELSE  DeclItemBI[[subtree[index: seb[sei].idValue]]];
	UnsealRefStack[];
	END;
    END;


  CheckTypeId: PROCEDURE [sei: ISEIndex] RETURNS [BOOLEAN] =
    BEGIN
    node: Tree.Index;
    IF seb[sei].mark3 THEN RETURN [seb[sei].idType = typeTYPE];
    node ← seb[sei].idValue;
    RETURN [node = Tree.NullIndex OR tb[node].name = typedecl]
    END;

  TypeSymbol: PROCEDURE [sei: ISEIndex] RETURNS [val: Tree.Link] =
    BEGIN
    declNode: Tree.Index;
    saveIndex: CARDINAL;
    entryIndex: CARDINAL = dataPtr.textIndex;
    circular: BOOLEAN;
    circular ← FALSE;
    IF ~seb[sei].mark3
      THEN
	BEGIN
	ENABLE
	  LogTypeLoop =>
	    BEGIN  saveIndex ← dataPtr.textIndex;
	    dataPtr.textIndex ← entryIndex;
	    Log.ErrorSei[circularType, sei];
	    circular ← TRUE;
	    dataPtr.textIndex ← saveIndex;
	    END;
	declNode ← seb[sei].idValue;
	IF tb[declNode].attr3 # P3.Mark
	  THEN ResolveType[sei]
	  ELSE
	    IF SIGNAL CheckTypeLoop[declNode]
	      THEN SIGNAL LogTypeLoop[declNode];
	END;
    IF CheckTypeId[sei] AND ~circular
      THEN  val ← Tree.Link[symbol[index: sei]]
      ELSE
	BEGIN
	IF ~circular AND sei # dataPtr.seAnon
	  THEN Log.ErrorSei[nonTypeId, sei];
	val ← Tree.Link[symbol[index: dataPtr.idANY]];
	END;
    RETURN
    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;


  TypeExp: PUBLIC PROCEDURE [typeExp: Tree.Link] RETURNS [val: Tree.Link] =
    BEGIN
    val ← TypeLink[typeExp];  TypeAttr[val];  RETURN
    END;

  TypeForTree: PUBLIC PROCEDURE [t: Tree.Link] RETURNS [SEIndex] =
    -- N.B. assumes t evaluated by TypeLink or Exp
    BEGIN
    RETURN [WITH t SELECT FROM
      symbol => index,
      subtree =>
	SELECT tb[index].name FROM
	  cdot => TypeForTree[tb[index].son[2]],
	  ENDCASE => tb[index].info,
      ENDCASE => typeANY]
    END;


  TypeLink: PROCEDURE [typeExp: Tree.Link] RETURNS [val: Tree.Link] =
    BEGIN
    WITH typeExp SELECT FROM
      hash =>    val ← TypeSymbol[FindSe[index].symbol];
      symbol =>  val ← TypeSymbol[index];
      subtree =>
	BEGIN
	node: Tree.Index = index;
	iSei: ISEIndex;
	SELECT tb[node].name FROM
	  discrimTC =>
	    BEGIN  OPEN tb[node];
	    son[1] ← TypeLink[son[1]];
	    iSei ← WITH son[2] SELECT FROM
	      hash => SelectVariantType[TypeForTree[son[1]], index],
	      ENDCASE =>  ERROR;
	    info ← iSei;  son[2] ← Tree.Link[symbol[index: iSei]];
	    END;
	  dot =>
	    BEGIN  OPEN tb[node];
	    found: BOOLEAN;
	    nDerefs: CARDINAL;
	    sei: SEIndex;
	    subType: CSEIndex;
	    ctx: CTXIndex;
	    son[1] ← Exp[son[1], typeANY];
	    WITH son[2] SELECT FROM
	      hash =>
		BEGIN
		nDerefs ← 0;
		FOR subType ← RType[], UnderType[sei]
		  DO
		  WITH t: seb[subType] SELECT FROM
		    definition =>  BEGIN  ctx ← t.defCtx;  GO TO search  END;
		    record =>  BEGIN  ctx ← t.fieldCtx;  GO TO search  END;
		    pointer =>
		      BEGIN
		      IF (nDerefs ← nDerefs+1) > 255 THEN GO TO fail;
		      t.dereferenced ← TRUE;  sei ← t.refType;
		      END;
		    long =>  sei ← t.rangeType;
		    subrange =>  sei ← t.rangeType;
		    ENDCASE =>  GO TO fail;
		  REPEAT
		    fail =>  found ← FALSE;
		    search =>  [found, iSei] ← SearchCtxList[index, ctx];
		  ENDLOOP;
		IF ~found
		  THEN
		    BEGIN
		    iSei ← dataPtr.idANY;  Log.ErrorHti[unknownField, index];
		    END;
		name ← cdot;  info ← iSei;  son[2] ← TypeSymbol[iSei];
		END;
	      ENDCASE =>  ERROR;
	    RPop[];
	    END;
	  frameTC =>
	    BEGIN  OPEN tb[node];
	    son[1] ← Exp[son[1], typeANY];  RPop[];
	    info ← MakeFrameRecord[son[1]];
	    END;
	  ENDCASE =>
	    BEGIN  OPEN tb[node];
	    type: CSEIndex = info;
	    WITH t: seb[type] SELECT FROM
	      enumerated =>  NULL;
	      record =>
		BEGIN
		PushCtx[t.fieldCtx];  ScanList[son[1], DeclItemA];  PopCtx[];
		END;
	      pointer =>
		BEGIN
		son[1] ← TypeLink[son[1] ! CheckTypeLoop => RESUME [FALSE]];
		t.refType ← TypeForTree[son[1]];
		END;
	      array =>
		BEGIN
		IF son[1] = Tree.Null
		  THEN  t.indexType ← dataPtr.idCARDINAL
		  ELSE
		    BEGIN
		    son[1] ← TypeLink[son[1]];
		    t.indexType ← TypeForTree[son[1]];
		    END;
		son[2] ← TypeLink[son[2]];
		t.componentType ← TypeForTree[son[2]];
		END;
	      arraydesc =>
		BEGIN
		son[1] ← TypeLink[son[1] ! CheckTypeLoop => RESUME [FALSE]];
		t.describedType ← TypeForTree[son[1]];
		END;
	      transfer =>
		BEGIN
		ENABLE  CheckTypeLoop => RESUME [FALSE];
		IF t.inRecord # SENull AND t.outRecord # SENull
		  THEN CheckDisjoint[
			seb[t.inRecord].fieldCtx, seb[t.outRecord].fieldCtx];
		PushArgCtx[t.inRecord];   ScanList[son[1], DeclItemA];
		PushArgCtx[t.outRecord];  ScanList[son[2], DeclItemA];
		PopArgCtx[t.outRecord];  PopArgCtx[t.inRecord];
		END;
	      definition =>  t.defCtx ← dataPtr.mainCtx;
	      union =>
		BEGIN
		DeclItemA[son[1]];  ScanList[son[2], DeclItemA];
		END;
	      relative =>
		BEGIN
		son[1] ← TypeLink[son[1] ! CheckTypeLoop => RESUME [FALSE]];
		t.baseType ← TypeForTree[son[1]];
		son[2] ← TypeLink[son[2]];
		t.resultType ← t.offsetType ← TypeForTree[son[2]];
		END;
	      subrange =>
		BEGIN
		t.range ← LOOPHOLE[node];    -- to allow symbolic evaluation
		son[1] ← TypeLink[son[1]]; t.rangeType ← TypeForTree[son[1]];
		END;
	      long =>
		BEGIN
		son[1] ← TypeLink[son[1]]; t.rangeType ← TypeForTree[son[1]];
		END;
	      ENDCASE =>  ERROR;
	    seb[type].mark3 ← TRUE;
	    END;
	val ← typeExp;
	END;
      ENDCASE =>  ERROR;
    RETURN
    END;


  TypeAttr: PROCEDURE [typeExp: Tree.Link] =
    BEGIN
    WITH typeExp SELECT FROM
      symbol =>
	BEGIN
	sei: ISEIndex = index;
	declNode: Tree.Index;
	IF ~seb[sei].mark4
	  THEN
	    BEGIN
	    declNode ← seb[sei].idValue;
	    IF seb[sei].mark3 AND tb[declNode].attr2 # P3.Mark
	      THEN ResolveValue[sei];
	    END;
	END;
      subtree =>
	BEGIN
	node: Tree.Index = index;
	SELECT tb[node].name FROM
	  discrimTC, cdot, frameTC =>  NULL;
	  ENDCASE =>
	    BEGIN  OPEN tb[node];
	    type: CSEIndex = info;
	    subType: CSEIndex;
	    WITH t: seb[type] SELECT FROM
	      enumerated =>  NULL;
	      record =>
		BEGIN
		saveNP: NPUse = pathNP;
		PushCtx[t.fieldCtx];  pathNP ← none;
		ScanList[son[1], DeclItemB];  ProcessDefaults[son[1], TRUE];
		PopCtx[];  pathNP ← saveNP;
		END;
	      pointer =>  TypeAttr[son[1]];
	      array =>
		BEGIN
		IF son[1] # Tree.Null THEN TypeAttr[son[1]];
		SELECT TRUE FROM
		  ~OrderedType[t.indexType] =>
		    BEGIN t.indexType←typeANY; Log.Error[nonOrderedType] END;
		  (TypeForm[t.indexType]=long) => Log.Error[subrangeNesting];
		  ENDCASE;
		TypeAttr[son[2]];
		END;
	      arraydesc =>
		BEGIN
		TypeAttr[son[1]];
		IF TypeForm[t.describedType] # array
		  THEN Log.Error[descriptor];
		END;
	      transfer =>
		BEGIN
		saveNP: NPUse = pathNP;
		PushArgCtx[t.inRecord];
		ScanList[son[1], DeclItemB];  ProcessDefaults[son[1], TRUE];
		PushArgCtx[t.outRecord];
		ScanList[son[2], DeclItemB];  ProcessDefaults[son[2], FALSE];
		PopArgCtx[t.outRecord];  PopArgCtx[t.inRecord];
		pathNP ← saveNP;
		END;
	      definition =>  NULL;
	      union =>
		BEGIN
		tagType: CSEIndex;
		DeclItemB[son[1]];
		seb[t.tagSei].immutable ← TRUE;
		tagType ← TargetType[UnderType[seb[t.tagSei].idType]];
		IF seb[tagType].typeTag # enumerated
		  THEN
		    BEGIN
		    Log.ErrorSei[nonTagType, t.tagSei]; tagType ← typeANY;
		    END;
		VariantList[son[2], tagType];
		END;
	      relative =>
		BEGIN
		vType: CSEIndex;
		TypeAttr[son[1]];
		IF seb[NormalType[UnderType[t.baseType]]].typeTag # pointer
		  THEN Log.Error[relative];
		TypeAttr[son[2]];
		vType ← UnderType[t.offsetType];  subType ← NormalType[vType];
		SELECT seb[subType].typeTag FROM
		  pointer, arraydesc => NULL;
		  ENDCASE =>
		    BEGIN  Log.Error[relative];  subType ← typeANY  END;
		IF seb[UnderType[t.baseType]].typeTag = long
		 OR seb[vType].typeTag = long
		  THEN subType ← MakeLongType[subType, vType];
		t.resultType ← subType;
		END;
	      subrange =>
		BEGIN
		TypeAttr[son[1]];  subType ← UnderType[t.rangeType];
		SELECT TRUE FROM
		  (TypeForm[subType] = pointer) =>
		    BEGIN
		    Interval[son[2], dataPtr.typeINTEGER, TRUE];  RPop[];
		    END;
		  OrderedType[subType] =>
		    BEGIN
		    IF TypeForm[subType] = long
		      THEN Log.Error[subrangeNesting];
		    Interval[son[2], subType, TRUE];  RPop[];
		    END;
		  ENDCASE =>
		    BEGIN  Log.Error[nonOrderedType];
		    Interval[son[2], typeANY, TRUE];  RPop[];
		    END;
		END;
	      long =>
		BEGIN
		TypeAttr[son[1]];  subType ← UnderType[t.rangeType];
		WITH s: seb[subType] SELECT FROM
		  basic =>
		    SELECT s.code FROM
		      codeINTEGER, codeANY => NULL;
		      ENDCASE => Log.Error[long];
		  pointer, arraydesc => NULL;
		  subrange =>
		    IF t.rangeType # dataPtr.idCARDINAL THEN Log.Error[long];
		  ENDCASE =>  Log.Error[long];
		END;
	      ENDCASE =>  ERROR;
	    END;
	END;
      ENDCASE =>  ERROR;
    END;


  VariantList: PROCEDURE [t: Tree.Link, tagType: CSEIndex] =
    BEGIN

    DefineTag: Tree.Scan =
      BEGIN
      sei: ISEIndex;
      WITH t SELECT FROM
	symbol =>
	  BEGIN  sei ← index;
	  seb[sei].idValue ← TagValue[seb[sei].hash, tagType];
	  END;
	ENDCASE =>  ERROR;
      END;

    VariantItem: Tree.Scan =
      BEGIN
      node: Tree.Index = GetNode[t];
      saveIndex: CARDINAL = dataPtr.textIndex;
      dataPtr.textIndex ← tb[node].info;
      ScanList[tb[node].son[1], DefineTag];
      DeclItemB[t];
      dataPtr.textIndex ← saveIndex;
      END;

    ScanList[t, VariantItem];
    END;

  TagValue: PROCEDURE [tag: HTIndex, tagType: CSEIndex] RETURNS [CARDINAL] =
    BEGIN
    matched: BOOLEAN;
    sei: ISEIndex;
    WITH seb[tagType] SELECT FROM
      enumerated =>
	BEGIN
	[matched, sei] ← SearchCtxList[tag, valueCtx];
	IF matched THEN RETURN [seb[sei].idValue];
	END;
      ENDCASE;
    Log.ErrorHti[unknownTag, tag];  RETURN [0]
    END;


 -- type mappings and predicates

  Bundling: PUBLIC PROCEDURE [type: CSEIndex] RETURNS [nLevels: CARDINAL] =
    BEGIN
    next: CSEIndex;
    ctx: CTXIndex;
    nLevels ← 0;
      DO
      IF type = SENull THEN EXIT;
      WITH seb[type] SELECT FROM
	record =>
	  BEGIN
	  IF ~hints.unifield THEN EXIT;
	  ctx ← fieldCtx;
	  WITH ctxb[ctx] SELECT FROM
	    included =>
	      BEGIN
	      IF hints.privateFields AND ~mdb[module].shared THEN EXIT;
	      IF ~complete THEN CompleteRecord[LOOPHOLE[type, RecordSEIndex]];
	      IF ~complete THEN EXIT;
	      END;
	    ENDCASE;
	  IF CtxEntries[fieldCtx] # 1 OR hints.variant THEN EXIT;
	  nLevels ← nLevels + 1;
	  next ← Unbundle[LOOPHOLE[type, RecordSEIndex]];
	  END;
	ENDCASE => EXIT;
      type ← next;
      ENDLOOP;
    RETURN
    END;

  Unbundle: PUBLIC PROCEDURE [record: RecordSEIndex] RETURNS [CSEIndex] =
    BEGIN  OPEN seb[record];
    RETURN [UnderType[seb[ctxb[fieldCtx].seList].idType]]
    END;


  TargetType: PUBLIC PROCEDURE [type: CSEIndex] RETURNS [target: CSEIndex] =
    BEGIN
    next: CSEIndex;
    FOR target ← type, next
      DO
      WITH seb[target] SELECT FROM
	subrange =>  next ← UnderType[rangeType];
	ENDCASE =>  EXIT;
      ENDLOOP;
    RETURN [target]
    END;

  CanonicalType: PUBLIC PROCEDURE [sType: CSEIndex] RETURNS [type: CSEIndex] =
    BEGIN
    next: CSEIndex;
    FOR type ← sType, next
      DO
      WITH seb[type] SELECT FROM
	subrange =>  next ← UnderType[rangeType];
	record =>
	  IF Bundling[type] # 0
	    THEN  next ← Unbundle[LOOPHOLE[type, RecordSEIndex]]
	    ELSE RETURN;
	ENDCASE =>  RETURN
      ENDLOOP;
    END;


  IdentifiedType: PUBLIC PROCEDURE [type: CSEIndex] RETURNS [BOOLEAN] =
    BEGIN
    WITH seb[type] SELECT FROM
      mode, definition, nil => RETURN [FALSE];
      record =>
	BEGIN
	IF hints.variant AND ~hints.comparable
	  THEN [] ← VariantUnionType[type];	-- force copying now
	RETURN [TRUE]
	END;
      ENDCASE => RETURN [TRUE]
    END;

  OrderedType: PUBLIC PROCEDURE [type: SEIndex] RETURNS [BOOLEAN] =
    BEGIN
    sei: CSEIndex;
      DO
      sei ← UnderType[type];
      WITH seb[sei] SELECT FROM
	basic =>  RETURN [ordered];
	enumerated =>  RETURN [ordered];
	pointer => RETURN [ordered];
	relative =>  type ← offsetType;
	subrange =>  type ← rangeType;
	long, real =>  type ← rangeType;
	ENDCASE =>  RETURN [FALSE];
      ENDLOOP;
    END;


  MakeLongType: PUBLIC PROCEDURE [rType: SEIndex, hint: CSEIndex] RETURNS [type: CSEIndex] =
    BEGIN
    WITH seb[hint] SELECT FROM
      long =>
	IF TargetType[UnderType[rangeType]] = TargetType[UnderType[rType]]
	  THEN RETURN [hint];
      ENDCASE;
    type ← MakeNonCtxSe[SIZE[long cons SERecord]];
    seb[type] ← SERecord[mark3: TRUE, mark4: TRUE,
	body: cons[long[rangeType: rType]]];
    RETURN
    END;

  MakePointerType: PUBLIC PROCEDURE [cType: SEIndex, hint: CSEIndex, readOnly: BOOLEAN]
      RETURNS [type: CSEIndex] =
    BEGIN
    WITH t: seb[hint] SELECT FROM
      pointer =>
	IF ~t.ordered AND t.readOnly = readOnly
	 AND UnderType[t.refType] = UnderType[cType]
	  THEN RETURN [hint];
      ENDCASE;
    type ← MakeNonCtxSe[SIZE[pointer cons SERecord]];
    seb[type] ← SERecord[mark3: TRUE, mark4: TRUE,
	body: cons[pointer[
	    ordered: FALSE,
	    readOnly: readOnly,
	    basing: FALSE,
	    dereferenced: FALSE,
	    refType: cType]]];
    RETURN
    END;

  END.