-- file Pass3Xb.Mesa
-- last modified by Satterthwaite, December 17, 1979  1:45 PM

DIRECTORY
  ComData: FROM "comdata"
    USING [
      definitionsOnly, idCARDINAL, nTypeCodes, ownSymbols,
      typeMap, typeMapId,
      typeBOOLEAN, typeCHARACTER, typeINTEGER, typeSTRING],
  InlineDefs: FROM "inlinedefs" USING [BITAND],
  Log: FROM "log" USING [Error, ErrorN, ErrorNode, ErrorSei, ErrorTree],
  P3: FROM "p3"
    USING [
      Attr, EmptyAttr, FullAttr, VoidAttr,
      NPUse, BoundNP, MergeNP, SequenceNP,
      phraseNP,
      Addr, All, --And,-- Apply, Assignment, Bundling, CanonicalType, Case,
      ClearRefStack, DescOp, Discrimination, Dot, Id, IdentifiedType,
      MakeLongType, MiscXfer, OperandInline, OrderedType, PopCtx, PushCtx,
      RecordMention, SealRefStack, TargetType, TypeExp, TypeForTree, Unbundle,
      UnsealRefStack, UpArrow],
  Pass3: FROM "pass3"
    USING [implicitAttr, implicitRecord, implicitTree, implicitType],
  Symbols: FROM "symbols"
    USING [ctxType, seType,
      SEIndex, ISEIndex, CSEIndex, RecordSEIndex, CSENull,
      RecordSENull, codeCHARACTER, codeINTEGER, typeANY],
  SymbolOps: FROM "symbolops"
    USING [ConstantId, NormalType, TypeForm, UnderType],
  Table: FROM "table" USING [Base, Notifier],
  Tree: FROM "tree" USING [Index, Link, Map, Null, treeType],
  TreeOps: FROM "treeops"
    USING [
      GetNode, ListLength, PopTree, PushTree, PushNode, SetInfo, TestTree,
      UpdateList],
  Types: FROM "types" USING [SymbolTableBase, Assignable, Equivalent];

Pass3Xb: PROGRAM
    IMPORTS
	InlineDefs, Log, P3, SymbolOps, TreeOps, Types,
	dataPtr: ComData, passPtr: Pass3
    EXPORTS P3 =
  BEGIN
  OPEN SymbolOps, TreeOps, P3;

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

 -- pervasive definitions from SymDefs

  SEIndex: TYPE = Symbols.SEIndex;
  ISEIndex: TYPE = Symbols.ISEIndex;
  CSEIndex: TYPE = Symbols.CSEIndex;
  RecordSEIndex: TYPE = Symbols.RecordSEIndex;
  typeANY: Symbols.CSEIndex = Symbols.typeANY;


  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)

  own: Types.SymbolTableBase;

  ExpBNotify: PUBLIC Table.Notifier =
    BEGIN  -- called by allocator whenever table area is repacked
    seb ← base[Symbols.seType];  ctxb ← base[Symbols.ctxType];
    tb ← base[Tree.treeType];  RETURN
    END;


 -- intermediate result bookkeeping

  OperandDescriptor: TYPE = RECORD[
    type: CSEIndex,		-- type of operand
    attr: Attr];		-- attributes

  RStackLimit: INTEGER = 32;
  rStack: ARRAY [0 .. RStackLimit] OF OperandDescriptor;
  rI: INTEGER;			-- index into rStack

  OperandStackOverflow: SIGNAL = CODE;

  RPush: PUBLIC PROCEDURE [type: CSEIndex, attr: Attr] =
    BEGIN
    IF rI >= RStackLimit THEN ERROR OperandStackOverflow;
    rI ← rI + 1;  
    rStack[rI] ← OperandDescriptor[type:type, attr:attr];
    END;

  RPop: PUBLIC PROCEDURE =
    BEGIN
    IF rI < 0 THEN ERROR;
    rI ← rI-1;
    END;

  RType: PUBLIC PROCEDURE RETURNS [CSEIndex] =
    BEGIN
    RETURN [rStack[rI].type]
    END;

  RAttr: PUBLIC PROCEDURE RETURNS [Attr] =
    BEGIN
    RETURN [rStack[rI].attr]
    END;

  longUnsigned: CSEIndex;	-- a hint for mwconst

  ExpInit: PUBLIC PROCEDURE =
    BEGIN
    passPtr.implicitType ← typeANY;  passPtr.implicitTree ← Tree.Null;
    passPtr.implicitRecord ← Symbols.RecordSENull;
    own ← dataPtr.ownSymbols;	-- make a parameter?
    longUnsigned ← Symbols.CSENull;
    rI ← -1;
    END;


 -- tree manipulation utilities

  OperandType: PUBLIC PROCEDURE [t: Tree.Link] RETURNS [CSEIndex] =
    BEGIN
    RETURN [WITH e:t SELECT FROM
      symbol => UnderType[seb[e.index].idType],
      literal =>
	WITH e.info SELECT FROM
	  string => dataPtr.typeSTRING,
	  ENDCASE => dataPtr.typeINTEGER,
      subtree => tb[e.index].info,
      ENDCASE => Symbols.CSENull]
    END;


 -- type manipulation

  UnresolvedTypes: SIGNAL RETURNS [CSEIndex] = CODE;

  BalanceTypes: PROCEDURE [type1, type2: CSEIndex] RETURNS [type: CSEIndex] =
    BEGIN
    n1, n2: CARDINAL;
    SELECT TRUE FROM
      (type1 = type2), (type2 = typeANY) =>  type ← type1;
      (type1 = typeANY) =>  type ← type2;
      ENDCASE =>
	BEGIN
	n1 ← Bundling[type1];
	n2 ← Bundling[type2];
	WHILE n1 > n2
	  DO  type1 ← Unbundle[LOOPHOLE[type1]];  n1 ← n1-1  ENDLOOP;
	WHILE n2 > n1
	  DO  type2 ← Unbundle[LOOPHOLE[type2]];  n2 ← n2-1  ENDLOOP;
	-- check bundling
	  DO
	  type1 ← TargetType[type1];
	  type2 ← TargetType[type2];
	  SELECT TRUE FROM
	    Types.Assignable[[own, type1], [own, type2]] =>
	      BEGIN  type ← type1;  EXIT  END;
	    Types.Assignable[[own, type2], [own, type1]] =>
	      BEGIN  type ← type2;  EXIT  END;
	    ENDCASE;
	  IF n1 = 0 THEN GO TO Fail;
	  n1 ← n1-1;
	  type1 ← Unbundle[LOOPHOLE[type1]];
	  type2 ← Unbundle[LOOPHOLE[type2]];
	  REPEAT
	    Fail =>  type ← SIGNAL UnresolvedTypes;
	  ENDLOOP;
	END;
    RETURN
    END;

  ForceType: PUBLIC PROCEDURE [t: Tree.Link, type: CSEIndex] RETURNS [Tree.Link] =
    BEGIN
    PushTree[t];
    WITH t SELECT FROM
      subtree =>
	SELECT tb[index].name FROM
	  construct, union, rowcons =>  PushNode[cast, 1];
	  openx =>  PushNode[cast, 1];
	  ENDCASE;
      ENDCASE =>  PushNode[cast, 1];
    SetInfo[type];  RETURN [PopTree[]]
    END;


 -- expressions

  Exp: PUBLIC PROCEDURE [exp: Tree.Link, target: CSEIndex] RETURNS [val: Tree.Link] =
    BEGIN
    type: CSEIndex;
    attr: Attr;
    phraseNP ← none;
    IF exp = Tree.Null
      THEN
	BEGIN
	RPush[passPtr.implicitType, passPtr.implicitAttr]; RETURN [Tree.Null]
	END;
    WITH e:exp SELECT FROM
      symbol =>
	BEGIN
	sei: ISEIndex = e.index;
	attr.noXfer ← attr.noAssign ← TRUE;  RecordMention[sei];
	type ← UnderType[seb[sei].idType];
	SELECT ctxb[seb[sei].idCtx].ctxType FROM
	  included =>
	    IF ~(attr.const←ConstantId[sei])
	      THEN Log.ErrorSei[unimplemented, sei];
	  imported =>  attr.const ← ConstantId[sei];
	  ENDCASE =>  attr.const ← seb[sei].constant;
	RPush[type, attr];  val ← exp;
	END;
      hash =>
	WITH seb[target] SELECT FROM
	  enumerated =>
	    BEGIN  PushCtx[valueCtx];  val ← Id[e.index];  PopCtx[]  END;
	  ENDCASE =>  val ← Id[e.index];
      literal =>
	BEGIN
	attr.noXfer ← attr.noAssign ← TRUE;
	WITH e.info SELECT FROM
	  string =>
	    BEGIN
	    type ← dataPtr.typeSTRING; attr.const ← FALSE;
	    IF dataPtr.definitionsOnly THEN Log.ErrorTree[unimplemented, exp];
	    END;
	  ENDCASE =>  BEGIN type ← dataPtr.typeINTEGER; attr.const ←TRUE END;
	RPush[type, attr];  val ← exp;
	END;
      subtree =>
	BEGIN
	node: Tree.Index ← e.index;
	val ← exp;	-- the default
	SELECT tb[node].name FROM
	  dot =>  Dot[node];
	  uparrow =>  UpArrow[node];
	  apply =>
	    BEGIN Apply[node, target, FALSE]; CheckNonVoid[node, target] END;
	  uminus, abs =>  UnaryOp[node];
	  plus =>  Plus[node];
	  minus =>  Minus[node];
	  times, div, mod =>  ArithOp[node];
	  relE, relN =>  RelOp[node, FALSE];
	  relL, relGE, relG, relLE =>  RelOp[node, TRUE];
	  in, notin =>  In[node];
	  not =>  tb[node].son[1] ← Rhs[tb[node].son[1], dataPtr.typeBOOLEAN];
	  or, and =>  BoolOp[node];
	  ifx =>  IfExp[node, target];
	  casex =>  SelectExp[node, target, Case];
	  bindx =>  SelectExp[node, target, Discrimination];
	  assignx =>  Assignment[node];
	  min, max =>  MinMax[node, target];
	  addr =>  Addr[node, target];
	  base, length, arraydesc =>  DescOp[node, target];
	  all =>  All[node, target];
	  mwconst =>
	    BEGIN
	    IF longUnsigned = Symbols.CSENull
	      THEN longUnsigned ← MakeLongType[dataPtr.idCARDINAL, typeANY];
	    RPush[longUnsigned, FullAttr];
	    END;
	  void =>  RPush[target, VoidAttr];
	  clit =>  RPush[dataPtr.typeCHARACTER, FullAttr];
	  llit =>
	    BEGIN
	    attr ← FullAttr;  attr.const ← FALSE;
	    RPush[dataPtr.typeSTRING, attr];
	    END;
	  signalx, errorx, fork, joinx, new, startx =>
	    BEGIN  
	    val ← MiscXfer[node, target];
	    node ← GetNode[val];  CheckNonVoid[node, target];
	    END;
	  syserrorx =>
	    BEGIN
	    RPush[Symbols.CSENull, EmptyAttr];  CheckNonVoid[node, target];
	    END;
	  lengthen =>
	    BEGIN  OPEN tb[node];
	    type: CSEIndex;
	    son[1] ← GenericRhs[son[1], target];  type ← rStack[rI].type;
	    IF type = dataPtr.typeINTEGER
	     OR seb[type].typeTag = pointer
	     OR seb[type].typeTag = arraydesc
	      THEN  rStack[rI].type ← MakeLongType[type, target]
	      ELSE
		BEGIN
		Log.ErrorTree[typeClash, son[1]];  rStack[rI].type ← typeANY;
		END;
	    END;
	  safen =>  tb[node].son[1] ← Exp[tb[node].son[1], target];
	  loophole =>
	    BEGIN  OPEN tb[node];
	    son[1] ← Exp[son[1], typeANY];
	    IF son[2] = Tree.Null
	      THEN
		BEGIN
		IF target = typeANY THEN Log.ErrorNode[noTarget, node];
		rStack[rI].type ← target;
		END
	      ELSE
		BEGIN  son[2] ← TypeExp[son[2]];
		rStack[rI].type ← UnderType[TypeForTree[son[2]]];
		END;
	    END;
	  size =>
	    BEGIN  OPEN tb[node];
	    son[1] ← TypeExp[son[1]];  RPush[dataPtr.typeINTEGER, FullAttr];
	    END;
	  first, last =>  EndPoint[node];
	  typecode =>
	    BEGIN  OPEN tb[node];
	    IF dataPtr.definitionsOnly THEN Log.Error[unimplemented];
	    son[1] ← TypeExp[son[1]];
	    EnterTypeCode[TypeForTree[son[1]]];  RPush[typeANY, FullAttr];
	    END;
	  item =>  tb[node].son[2] ← Exp[tb[node].son[2], target];
	  ENDCASE =>
	    BEGIN  Log.Error[unimplemented];  RPush[typeANY, EmptyAttr]  END;
	tb[node].info ← rStack[rI].type;
	END;
      ENDCASE;
    RETURN
    END;

    CheckNonVoid: PROCEDURE [node: Tree.Index, target: CSEIndex] =
      BEGIN
      IF rStack[rI].type = Symbols.CSENull THEN
	SELECT tb[node].name FROM
	  error => BEGIN tb[node].name ← errorx; rStack[rI].type ← target END;
	  errorx, syserrorx => rStack[rI].type ← target;
	  ENDCASE =>
	    BEGIN
	    Log.ErrorNode[typeClash, node];  rStack[rI].type ← typeANY;
	    END;
      END;

  VoidExp: PUBLIC PROCEDURE [exp: Tree.Link] RETURNS [val: Tree.Link] =
    BEGIN
    val ← Exp[exp, typeANY];  RPop[];  RETURN
    END;

  UniOperand: PROCEDURE [node: Tree.Index] RETURNS [valid: BOOLEAN] =
    BEGIN
    l: CARDINAL = ListLength[tb[node].son[1]];
    IF ~(valid ← l=1) THEN
      BEGIN
      IF l > 1 THEN Log.ErrorN[listLong, l-1] ELSE Log.ErrorN[listShort, l+1];
      tb[node].son[1] ← UpdateList[tb[node].son[1], VoidExp];
      RPush[typeANY, EmptyAttr];
      END;
    RETURN
    END;


 -- arithmetic expression manipulation

  NumericAny: PROCEDURE [type: CSEIndex] RETURNS [CSEIndex] =
    BEGIN
    RETURN [SELECT seb[type].typeTag FROM
      long => MakeLongType[dataPtr.typeINTEGER, type],
      ENDCASE => dataPtr.typeINTEGER]
    END;

  EvalNumeric: PROCEDURE [t: Tree.Link] RETURNS [val: Tree.Link] =
    BEGIN
    val ← GenericRhs[t, dataPtr.typeINTEGER];
    SELECT NormalType[rStack[rI].type] FROM
      dataPtr.typeINTEGER =>  NULL;
      typeANY => rStack[rI].type ← NumericAny[rStack[rI].type];
      ENDCASE => Log.ErrorTree[typeClash, val];
    RETURN
    END;

  ArithOp: PROCEDURE [node: Tree.Index] =
    BEGIN  OPEN tb[node];
    saveNP: NPUse;
    son[1] ← EvalNumeric[son[1]];  saveNP ← phraseNP;
    son[2] ← EvalNumeric[son[2]]; 
    BalanceAttributes[node];
    rStack[rI-1].attr ← And[rStack[rI-1].attr, rStack[rI].attr];
    IF attr1 THEN rStack[rI-1].attr.const ← FALSE;
    RPop[];  phraseNP ← MergeNP[saveNP][phraseNP];
    END;


  ArithType: PROCEDURE [type: CSEIndex] RETURNS [CSEIndex] =
    BEGIN
    type ← NormalType[type];
    RETURN [WITH seb[type] SELECT FROM
      relative => NormalType[UnderType[offsetType]],
      ENDCASE => type]
    END;

  Plus: PROCEDURE [node: Tree.Index] =
    BEGIN  OPEN tb[node];
    type: CSEIndex;
    lr: BOOLEAN;
    saveNP: NPUse;
    son[1] ← GenericRhs[son[1], typeANY];  saveNP ← phraseNP;
    type ← ArithType[rStack[rI].type];
    IF seb[type].typeTag = pointer OR type = dataPtr.typeCHARACTER
      THEN  BEGIN lr ← TRUE; son[2] ← EvalNumeric[son[2]] END
      ELSE
	BEGIN
	SELECT type FROM
	  dataPtr.typeINTEGER, typeANY => NULL;
	  ENDCASE => Log.ErrorTree[typeClash, son[1]];
	son[2] ← GenericRhs[son[2], typeANY];
	lr ← FALSE;  type ← ArithType[rStack[rI].type];
	SELECT TRUE FROM
	  type = dataPtr.typeINTEGER, type = dataPtr.typeCHARACTER  => NULL;
	  seb[type].typeTag = pointer  => NULL;
	  ENDCASE =>
	    BEGIN
	    IF type # typeANY THEN Log.ErrorTree[typeClash, son[2]];
	    rStack[rI].type ← NumericAny[rStack[rI].type];
	    END;
	END;
    BalanceAttributes[node];
    rStack[rI-1].attr ← And[rStack[rI-1].attr, rStack[rI].attr];
    IF attr1 THEN rStack[rI-1].attr.const ← FALSE;
    IF ~lr THEN rStack[rI-1].type ← rStack[rI].type;
    RPop[];  phraseNP ← MergeNP[saveNP][phraseNP];
    END;

  Minus: PROCEDURE [node: Tree.Index] =
    BEGIN  OPEN tb[node];
    type, subType: CSEIndex;
    lr: BOOLEAN;
    saveNP: NPUse;
    son[1] ← GenericRhs[son[1], typeANY];  saveNP ← phraseNP;
    type ← NormalType[rStack[rI].type]; subType ← ArithType[type];  lr ← TRUE;
    IF seb[subType].typeTag = pointer OR subType = dataPtr.typeCHARACTER
      THEN
	BEGIN
	son[2] ← GenericRhs[son[2], typeANY];
	subType ← NormalType[rStack[rI].type];
	SELECT TRUE FROM
	  subType = typeANY =>  NULL;
	  Types.Equivalent[[own, type], [own, subType]] =>  lr ← FALSE;
	  subType = dataPtr.typeINTEGER =>  NULL;
	  ENDCASE => Log.ErrorTree[typeClash, son[2]];
	END
      ELSE
	BEGIN
	SELECT type FROM
	  dataPtr.typeINTEGER, typeANY =>  NULL;
	  ENDCASE =>
	    BEGIN
	    Log.ErrorTree[typeClash, son[1]];  rStack[rI].type ← typeANY;
	    END;
	son[2] ← EvalNumeric[son[2]];
	END;
    BalanceAttributes[node];
    rStack[rI-1].attr ← And[rStack[rI-1].attr, rStack[rI].attr];
    IF attr1 THEN rStack[rI-1].attr.const ← FALSE;
    IF ~lr
      THEN rStack[rI-1].type ← IF attr2
		THEN MakeLongType[dataPtr.typeINTEGER, rStack[rI].type]
		ELSE dataPtr.typeINTEGER;
    RPop[];  phraseNP ← MergeNP[saveNP][phraseNP];
    END;

  UnaryOp: PROCEDURE [node: Tree.Index] =
    BEGIN
    IF UniOperand[node] THEN
      BEGIN  OPEN tb[node];
      son[1] ← EvalNumeric[son[1]];  SetAttributes[node];
      IF attr1 THEN rStack[rI].attr.const ← FALSE;
      END;
    END;

  RelOp: PROCEDURE [node: Tree.Index, ordered: BOOLEAN] =
    BEGIN  OPEN tb[node];
    type: CSEIndex;
    attr: Attr;
    saveNP: NPUse;
    implicitOp: BOOLEAN;
    son[1] ← GenericRhs[son[1], typeANY];  saveNP ← phraseNP; 
    type ← NormalType[rStack[rI].type];  implicitOp ← (son[1] = Tree.Null);
    son[2] ← GenericRhs[son[2], type];
    type ← BalanceTypes[type, NormalType[rStack[rI].type]
      !UnresolvedTypes =>
	BEGIN  Log.ErrorTree[typeClash, son[2]];  RESUME [typeANY]  END];
    IF (ordered AND ~OrderedType[type]) OR
       (~ordered AND ~IdentifiedType[type])
      THEN Log.ErrorNode[relationType, node];
    BalanceAttributes[node];
    attr ← And[rStack[rI-1].attr, rStack[rI].attr];
    IF implicitOp AND son[1] # Tree.Null
      THEN  Log.ErrorTree[typeClash, son[2]];
    SELECT seb[type].typeTag FROM
      basic, enumerated => NULL;
      transfer =>
	BEGIN
	IF OperandInline[son[1]] THEN Log.ErrorTree[misusedInline, son[1]];
	IF OperandInline[son[2]] THEN Log.ErrorTree[misusedInline, son[2]];
	attr.const ← FALSE;
	END;
      real => attr.const ← FALSE;
      ENDCASE;
    RPop[];  RPop[];
    RPush[dataPtr.typeBOOLEAN, attr];  phraseNP ← MergeNP[saveNP][phraseNP];
    END;

  In: PROCEDURE [node: Tree.Index] =
    BEGIN  OPEN tb[node];
    saveNP: NPUse;
    son[1] ← GenericRhs[son[1], typeANY];  saveNP ← phraseNP;
    son[2] ← Range[son[2], rStack[rI].type];
    SetAttributes[node];
    rStack[rI-1].attr ← And[rStack[rI-1].attr, rStack[rI].attr];  RPop[];
    rStack[rI].type ← dataPtr.typeBOOLEAN;
    phraseNP ← MergeNP[saveNP][phraseNP];
    END;

  BoolOp: PROCEDURE [node: Tree.Index] =
    BEGIN  OPEN tb[node];
    attr: Attr;
    saveNP: NPUse;
    SealRefStack[];
    son[1] ← Rhs[son[1], dataPtr.typeBOOLEAN];  saveNP ← phraseNP; 
    ClearRefStack[];
    son[2] ← Rhs[son[2], dataPtr.typeBOOLEAN]; 
    UnsealRefStack[];
    attr ← And[rStack[rI-1].attr, rStack[rI].attr];
    RPop[];  RPop[];
    RPush[dataPtr.typeBOOLEAN, attr]; phraseNP ← SequenceNP[saveNP][phraseNP];
    END;


  Interval: PUBLIC PROCEDURE [t: Tree.Link, type: CSEIndex, constant: BOOLEAN] =
    BEGIN
    node: Tree.Index = GetNode[t];
    saveNP: NPUse;
    type ← TargetType[type];
    tb[node].son[1] ← Rhs[tb[node].son[1], type];  saveNP ← phraseNP;
    IF constant AND ~rStack[rI].attr.const
      THEN Log.ErrorTree[nonConstant, tb[node].son[1]];
    tb[node].son[2] ← Rhs[tb[node].son[2], type];  
    IF constant AND ~rStack[rI].attr.const
      THEN Log.ErrorTree[nonConstant, tb[node].son[2]];
    SetAttributes[node];
    rStack[rI-1].attr ← And[rStack[rI-1].attr, rStack[rI].attr];  RPop[];
    IF seb[type].typeTag = real THEN rStack[rI].attr.const ← FALSE;
    phraseNP ← MergeNP[saveNP][phraseNP];
    END;

  Range: PUBLIC PROCEDURE [t: Tree.Link, type: CSEIndex] RETURNS [val: Tree.Link] =
    BEGIN
    subType: CSEIndex;
    node: Tree.Index;
    WITH t SELECT FROM
      subtree =>
	BEGIN  node ← index;
	SELECT tb[node].name FROM
	  subrangeTC =>
	    BEGIN  val ← t;
	    tb[node].son[1] ← TypeExp[tb[node].son[1]];
	    subType ← TargetType[UnderType[TypeForTree[tb[node].son[1]]]];
	    Interval[tb[node].son[2], subType, FALSE];
	    END;
	  IN [intOO .. intCC] =>
	    BEGIN  val ← t;
	    subType ← IF type # typeANY THEN type ELSE dataPtr.typeINTEGER;
	    Interval[t, subType, FALSE];
	    END;
	  ENDCASE =>
	    BEGIN
	    val ← TypeExp[t];
	    subType ← TargetType[UnderType[TypeForTree[val]]];
	    RPush[subType, FullAttr];  phraseNP ← none;
	    END;
	END;
      ENDCASE =>
	BEGIN
	val ← TypeExp[t];  subType ← TargetType[UnderType[TypeForTree[val]]];
	RPush[subType, FullAttr];  phraseNP ← none;
	END;
    IF ~OrderedType[subType] AND subType # typeANY
      THEN Log.Error[nonOrderedType];
    IF ~Types.Assignable[
		[dataPtr.ownSymbols, type],
		[dataPtr.ownSymbols, subType]]
      THEN Log.ErrorTree[typeClash, val];
    RETURN
    END;


  BalancedTarget: PROCEDURE [target, type: CSEIndex] RETURNS [CSEIndex] =
    BEGIN
    RETURN [IF target = typeANY
	OR (~Types.Equivalent[[own, type], [own, target]]
	    AND NormalType[type] = target)
      THEN TargetType[type]
      ELSE target]
    END;

  ResolveTypes: PROCEDURE [type1, type2, target: CSEIndex, t: Tree.Link]
      RETURNS [type: CSEIndex] =
    BEGIN
    failed: BOOLEAN;
    IF target = typeANY
      THEN  failed ← TRUE
      ELSE
	BEGIN
	ENABLE UnresolvedTypes =>  BEGIN failed ← TRUE; RESUME [typeANY] END;
	failed ← FALSE;
	type1 ← BalanceTypes[target, type1];
	type2 ← BalanceTypes[target, type2];
	type ← BalanceTypes[type1, type2];
	END;
    IF failed THEN BEGIN Log.ErrorTree[typeClash, t]; type ← typeANY END;
    RETURN
    END;

  IfExp: PROCEDURE [node: Tree.Index, target: CSEIndex] =
    BEGIN  OPEN tb[node];
    type: CSEIndex;
    attr: Attr;
    entryNP, saveNP: NPUse;
    SealRefStack[];
    son[1] ← Rhs[son[1], dataPtr.typeBOOLEAN]; 
    attr ← rStack[rI].attr;  RPop[];  entryNP ← phraseNP;
    UnsealRefStack[];
    son[2] ← BalancedRhs[son[2], target];
    attr ← And[attr, rStack[rI].attr]; saveNP ← SequenceNP[entryNP][phraseNP];
    type ← rStack[rI].type;  RPop[];
    target ← BalancedTarget[target, type];
    son[3] ← BalancedRhs[son[3], target];  attr ← And[attr, rStack[rI].attr];
    type ← BalanceTypes[type, rStack[rI].type
	!UnresolvedTypes =>
	  RESUME [ResolveTypes[type, rStack[rI].type, target, son[3]]]];
    phraseNP ← BoundNP[saveNP][SequenceNP[entryNP][phraseNP]];
    RPop[];  RPush[type, attr];
    END;

  SelectExp: PROCEDURE [node: Tree.Index, target: CSEIndex, driver: PROCEDURE [Tree.Index, Tree.Map]] =
    BEGIN
    type: CSEIndex;
    attr: Attr;
    saveNP: NPUse;
    started: BOOLEAN;

    Selection: Tree.Map =
      BEGIN
      subType: CSEIndex;
      entryNP: NPUse = phraseNP;
      v ← BalancedRhs[t, target];
      subType ← BalanceTypes[type, rStack[rI].type
	!UnresolvedTypes =>
	  RESUME [ResolveTypes[type, rStack[rI].type, target, v]]];
      saveNP ← BoundNP[saveNP][SequenceNP[entryNP][phraseNP]];
      IF subType # typeANY THEN type ← subType;
      IF ~started THEN target ← BalancedTarget[target, type];
      attr ← And[attr, rStack[rI].attr];  RPop[];  started ← TRUE;  RETURN
      END;

    type ← typeANY;  attr ← FullAttr;  started ← FALSE;  saveNP ← none;
    driver[node, Selection];  attr ← And[attr, rStack[rI].attr];  RPop[];
    attr.const ← FALSE;  RPush[type, attr];  phraseNP ← saveNP;
    END;



  MinMax: PROCEDURE [node: Tree.Index, target: CSEIndex] =
    BEGIN  OPEN tb[node];
    attr: Attr;
    saveNP: NPUse;
    started: BOOLEAN;
    type: CSEIndex;
    
    SubMinMax: Tree.Map =
      BEGIN
      subType: CSEIndex;
      v ← BalancedRhs[t, target];
      attr ← And[attr, rStack[rI].attr];  saveNP ← MergeNP[saveNP][phraseNP];
      subType ← CanonicalType[rStack[rI].type]; 
      subType ← BalanceTypes[subType, type 
		!UnresolvedTypes =>
		  RESUME[ResolveTypes[subType, type, target, v]]];
      IF type # subType AND subType # typeANY
	THEN
	  BEGIN
	  IF ~OrderedType[subType] THEN Log.ErrorNode[relationType, node];
	  type ← subType;
	  IF ~started THEN target ← BalancedTarget[target, type];
	  END;
      RPop[];  started ← TRUE;  RETURN
      END;

    attr ← FullAttr;  saveNP ← none;  started ← FALSE;  type ← typeANY;
    son[1] ← UpdateList[son[1], SubMinMax];
    SELECT seb[type].typeTag FROM
      long =>  BEGIN  attr1 ← FALSE;  attr2 ← TRUE  END;
      real =>  BEGIN  attr1 ← TRUE;  attr2 ← FALSE; attr.const ← FALSE  END;
      ENDCASE =>  attr1 ← attr2 ← FALSE;
    RPush[type, attr];  phraseNP ← saveNP;
    END;


  EndPoint: PROCEDURE [node: Tree.Index] =
    BEGIN  OPEN tb[node];
    type: CSEIndex;
    son[1] ← TypeExp[son[1]];
    type ← UnderType[TypeForTree[son[1]]];
      BEGIN
      WITH seb[type] SELECT FROM
	basic =>
	  SELECT code FROM
	    Symbols.codeINTEGER, Symbols.codeCHARACTER =>  NULL;
	    ENDCASE => GO TO fail;
        enumerated =>  NULL;
	relative =>  IF TypeForm[offsetType] # subrange THEN GO TO fail;
        subrange =>  NULL;
	long =>
	  IF NormalType[UnderType[rangeType]] # dataPtr.typeINTEGER
	    THEN GO TO fail;
        ENDCASE =>  GO TO fail;
      EXITS
	fail =>  Log.ErrorTree[typeClash, son[1]];
      END;
    RPush[type, FullAttr];
    END;

  EnterTypeCode: PROCEDURE [code: SEIndex] =
    BEGIN
    i: CARDINAL;
    FOR i IN [0 .. dataPtr.nTypeCodes)
      DO
      IF code = dataPtr.typeMap[i] THEN EXIT;
      REPEAT
        FINISHED =>
	  BEGIN
	  dataPtr.typeMap[dataPtr.nTypeCodes] ← code;
	  dataPtr.nTypeCodes ← dataPtr.nTypeCodes + 1;
	  END;
      ENDLOOP;
    RecordMention[dataPtr.typeMapId];
    END;

  Rhs: PUBLIC PROCEDURE [exp: Tree.Link, lhsType: CSEIndex] RETURNS [val: Tree.Link] =
    BEGIN
    rhsType: CSEIndex;
    val ← Exp[exp, lhsType];
    rhsType ← rStack[rI].type;
    SELECT TRUE FROM
      (lhsType = rhsType), (lhsType = typeANY) =>  NULL;
      (rhsType = typeANY) =>
	BEGIN
	SELECT seb[lhsType].typeTag FROM
	  long, real =>  val ← Lengthen[val, MakeLongType[typeANY, lhsType]];
	  ENDCASE;
	rStack[rI].type ← lhsType;
	END;
      ENDCASE =>
	BEGIN  -- immediate matching is inconclusive
	UNTIL Types.Assignable[[own, lhsType], [own, rhsType]]
	  DO
	  WITH seb[rhsType] SELECT FROM
	    subrange =>  rhsType ← UnderType[rangeType];
	    record =>
	      BEGIN
	      IF Bundling[rhsType] = 0 THEN GO TO nomatch;
	      rhsType ← Unbundle[LOOPHOLE[rhsType, RecordSEIndex]];
	      val ← ForceType[val, rhsType];
	      END;
	    ENDCASE =>
	      BEGIN
	      SELECT seb[lhsType].typeTag FROM
		long =>
		  BEGIN
		  IF ~Types.Assignable[
		      [own, NormalType[lhsType]], [own, rhsType]] 
		    THEN GO TO nomatch;
		  IF seb[rhsType].typeTag # real
		    THEN  val ← Lengthen[val, lhsType];
		  END;
		real =>
		  SELECT NormalType[rhsType] FROM
		    dataPtr.typeINTEGER, typeANY =>
		      BEGIN
		      val ← Float[val, rhsType, lhsType];
		      rStack[rI].attr.const ← FALSE;
		      END; 
		    ENDCASE =>  GO TO nomatch;
		ENDCASE => GO TO nomatch;
	      rhsType ← lhsType;
	      END
	  REPEAT
	    nomatch =>
	      BEGIN	-- no coercion is possible
	      Log.ErrorTree[typeClash,
		IF exp = Tree.Null THEN passPtr.implicitTree ELSE val];
	      rhsType ← lhsType;
	      END;
	  ENDLOOP;
	rStack[rI].type ← IF rhsType = typeANY THEN lhsType ELSE rhsType;
	END;
    IF seb[rhsType].typeTag = transfer AND OperandInline[val]
      THEN  Log.ErrorTree[misusedInline, val];
    RETURN
    END;


  GenericRhs: PROCEDURE [exp: Tree.Link, target: CSEIndex] RETURNS [val: Tree.Link] =
    BEGIN
    type: CSEIndex;
    val ← Exp[exp, target];  type ← rStack[rI].type;
    -- put value in canonical form
      DO
      WITH seb[type] SELECT FROM
	subrange =>  type ← UnderType[rangeType];
	record =>
	  BEGIN
	  IF Bundling[type] = 0 THEN EXIT;
	  type ← Unbundle[LOOPHOLE[type, RecordSEIndex]];
	  val ← ForceType[val, type];
	  END;
	ENDCASE => EXIT;
      rStack[rI].type ← type;
      ENDLOOP;
    RETURN
    END;

  BalancedRhs: PROCEDURE [exp: Tree.Link, target: CSEIndex] RETURNS [val: Tree.Link] =
    BEGIN
    type: CSEIndex;
    val ← Exp[exp, target];
    SELECT seb[target].typeTag FROM
      long, real =>
	BEGIN
	type ← CanonicalType[rStack[rI].type];
	IF type # typeANY AND seb[target].typeTag # seb[type].typeTag
	 AND Types.Equivalent[
	    [own, NormalType[target]], [own, type]] 
	  THEN
	    BEGIN
	    SELECT seb[target].typeTag FROM
	      long =>
		IF seb[type].typeTag # real THEN val ← Lengthen[val, target];
	      real =>
		BEGIN
		val ← Float[val, type, target]; rStack[rI].attr.const ← FALSE;
		END;
	      ENDCASE;
	    rStack[rI].type ← target;
	    END;
	END;
      ENDCASE;
    RETURN
    END;


  SetAttributes: PROCEDURE [node: Tree.Index] =
    BEGIN
    SELECT seb[rStack[rI].type].typeTag FROM
      long =>  BEGIN  tb[node].attr1 ← FALSE;  tb[node].attr2 ← TRUE  END;
      real =>  BEGIN  tb[node].attr1 ← TRUE;  tb[node].attr2 ← FALSE  END;
      ENDCASE =>  tb[node].attr1 ← tb[node].attr2 ← FALSE;
    END;


  BalanceAttributes: PROCEDURE [node: Tree.Index] =
    BEGIN
    lType, rType: CSEIndex;
    lType ← rStack[rI-1].type;  rType ← rStack[rI].type;
    SELECT seb[lType].typeTag FROM
      long =>
	BEGIN
	SELECT seb[rType].typeTag FROM
	  long =>  BEGIN tb[node].attr1 ← FALSE; tb[node].attr2 ← TRUE END;
	  real =>
	    BEGIN
	    rStack[rI-1].type ← rType;
	    tb[node].son[1] ← Float[tb[node].son[1], lType, rType];
	    rStack[rI-1].attr.const ← FALSE;
	    tb[node].attr1 ← TRUE;  tb[node].attr2 ← FALSE;
	    END;
	  ENDCASE =>
	    BEGIN
	    rStack[rI].type ← rType ← MakeLongType[rType, lType];
	    tb[node].son[2] ← Lengthen[tb[node].son[2], rType];
	    tb[node].attr1 ← FALSE;  tb[node].attr2 ← TRUE;
	    END;
	END;
      real =>
	BEGIN
	tb[node].attr1 ← TRUE;  tb[node].attr2 ← FALSE;
	SELECT seb[rType].typeTag FROM
	  real =>  NULL;
	  ENDCASE =>
	    BEGIN
	    rStack[rI].type ← lType;
	    tb[node].son[2] ← Float[tb[node].son[2], rType, lType];
	    rStack[rI].attr.const ← FALSE;
	    END;
	END;
      ENDCASE =>
	SELECT seb[rType].typeTag FROM
	  long =>
	    BEGIN
	    rStack[rI-1].type ← lType ← MakeLongType[lType, rType];
	    tb[node].son[1] ← Lengthen[tb[node].son[1], lType];
	    tb[node].attr1 ← FALSE;  tb[node].attr2 ← TRUE;
	    END;
	  real =>
	    BEGIN
	    rStack[rI-1].type ← rType;
	    tb[node].son[1] ← Float[tb[node].son[1], lType, rType];
	    rStack[rI-1].attr.const ← FALSE;
	    tb[node].attr1 ← TRUE;  tb[node].attr2 ← FALSE;
	    END;
	  ENDCASE =>  tb[node].attr1 ← tb[node].attr2 ← FALSE;
    END;

  Lengthen: PROCEDURE [t: Tree.Link, target: CSEIndex] RETURNS [v: Tree.Link] =
    BEGIN
    IF TestTree[t, arraydesc]
      THEN  v ← LengthenDesc[t, target]
      ELSE
	BEGIN
	PushTree[t];  PushNode[lengthen, 1];  SetInfo[target];  v ← PopTree[];
	END;
    RETURN
    END;

  LengthenDesc: PROCEDURE [t: Tree.Link, target: CSEIndex] RETURNS [Tree.Link] =
    BEGIN
    node: Tree.Index = GetNode[t];
    subNode: Tree.Index = GetNode[tb[node].son[1]];
    tb[subNode].son[1] ← Lengthen[tb[subNode].son[1],
	MakeLongType[OperandType[tb[subNode].son[1]], typeANY]];
    tb[node].info ← MakeLongType[tb[node].info, target];
    tb[node].attr2 ← TRUE;
    RETURN [t]
    END;

  Float: PROCEDURE [t: Tree.Link, type, target: CSEIndex] RETURNS [Tree.Link] =
    BEGIN
    PushTree[IF seb[type].typeTag = long
      THEN t
      ELSE Lengthen[t, MakeLongType[type, typeANY]]];
    SELECT NormalType[type] FROM
      dataPtr.typeINTEGER => BEGIN PushNode[float, 1];  SetInfo[target] END;
      typeANY => NULL;
      ENDCASE => Log.ErrorTree[typeClash, t];
    RETURN [PopTree[]]
    END;

  END.