-- file Pass3Xb.mesa
-- last modified by Satterthwaite, September 13, 1982 11:33 am
-- last modified by Donahue,  9-Dec-81 15:32:12

DIRECTORY
  Alloc: TYPE USING [Notifier],
  ComData: TYPE USING [
    definitionsOnly, idCARDINAL, idTEXT, ownSymbols,
    typeATOM, typeAtomRecord, typeBOOL, typeCHAR, typeINT, typeREAL, typeSTRING],
  LiteralOps: TYPE USING [FindHeapString],
  Log: TYPE USING [Error, ErrorHti, ErrorN, ErrorNode, ErrorSei, ErrorTree, Warning],
  P3: TYPE USING [
    Attr, EmptyAttr, FullAttr, VoidAttr,
    NarrowOp, NPUse, BoundNP, MergeNP, SequenceNP, TextForm, phraseNP,
    AddrOp, All, And, Apply, Assignment, Bundling, CanonicalType, Case,
    CatchPhrase, ClearRefStack, Discrimination, Dot, EnterType, Extract,
    Id, IdentifiedType, IndexType, MakeLongType, MakeRefType, MarkedType,
    MiscXfer, Narrowing, NullableType, OperandInline, OrderedType,
    PopCtx, PushCtx, RecordMention, SealRefStack, SearchCtxList, TargetType,
    TextRep, TypeAppl, TypeExp, TypeForTree, Unbundle, UnsealRefStack],
  P3S: TYPE USING [ImplicitInfo, implicitRecord, safety, self],
  SymLiteralOps: TYPE USING [EnterAtom, EnterText],
  Symbols: TYPE USING [
    Base, HTIndex, SEIndex, ISEIndex, CSEIndex, RecordSEIndex, CSENull,
    RecordSENull, codeANY, codeCHAR, codeINT, typeANY, ctxType, seType],
  SymbolOps: TYPE USING [ConstantId, NormalType, RCType, TypeForm, TypeRoot, UnderType],
  Tree: TYPE USING [Base, Index, Link, Map, Null, NullIndex, treeType],
  TreeOps: TYPE USING [
    FreeNode, GetHash, GetNode, ListLength, PopTree, PushTree, PushNode,
    SetAttr, SetInfo, UpdateList],
  Types: TYPE USING [SymbolTableBase, Assignable, Equivalent];

Pass3Xb: PROGRAM
    IMPORTS
      LiteralOps, Log, P3, P3S, SymLiteralOps, SymbolOps, TreeOps, Types,
      dataPtr: ComData
    EXPORTS P3, P3S = {
  OPEN SymbolOps, Symbols, TreeOps, P3;

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

  own: Types.SymbolTableBase;
  zone: UNCOUNTED ZONE ← NIL;
  
  ExpBNotify: PUBLIC Alloc.Notifier = {
    -- called by allocator whenever table area is repacked
    seb ← base[seType];  ctxb ← base[ctxType];
    tb ← base[Tree.treeType]};


 -- intermediate result bookkeeping

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

  RStack: TYPE = RECORD [SEQUENCE length: NAT OF OperandDescriptor];
  rStack: LONG POINTER TO RStack ← NIL;
  rI: INTEGER;			-- index into rStack

  RPush: PUBLIC PROC [type: CSEIndex, attr: Attr] = {
    rI ← rI + 1;
    WHILE rI >= rStack.length DO
      newLength: NAT = rStack.length + 16;
      newStack: LONG POINTER TO RStack = zone.NEW[RStack[newLength]];
      FOR i: INTEGER IN [0 .. rI) DO newStack[i] ← rStack[i] ENDLOOP;
      zone.FREE[@rStack];
      rStack ← newStack;
      ENDLOOP;
    rStack[rI] ← [type:type, attr:attr]};

  RPop: PUBLIC PROC = {IF rI < 0 THEN ERROR; rI ← rI-1};

  RType: PUBLIC PROC RETURNS [CSEIndex] = {RETURN [rStack[rI].type]};

  RAttr: PUBLIC PROC RETURNS [Attr] = {RETURN [rStack[rI].attr]};

  longUnsigned: CSEIndex;	-- a hint for mwconst

  textType: ARRAY TextForm OF CSEIndex;		-- a hint for text literals

  ExpInit: PUBLIC PROC [scratchZone: UNCOUNTED ZONE] = {
    zone ← scratchZone;
    implicit ← [type: typeANY, tree: Tree.Null, attr: EmptyAttr];
    P3S.implicitRecord ← RecordSENull;
    own ← dataPtr.ownSymbols;	-- make a parameter?
    longUnsigned ← CSENull;  textType ← ALL[CSENull];
    rStack ← zone.NEW[RStack[32]];
    rI ← -1};
    
  ExpReset: PUBLIC PROC = {
    IF rStack # NIL THEN zone.FREE[@rStack];
    zone ← NIL};


 -- tree manipulation utilities

  OperandType: PUBLIC PROC [t: Tree.Link] RETURNS [CSEIndex] = {
    RETURN [WITH e:t SELECT FROM
      symbol => UnderType[seb[e.index].idType],
      literal =>
	WITH e.info SELECT FROM
	  string => dataPtr.typeSTRING,
	  ENDCASE => dataPtr.typeINT,
      subtree => tb[e.index].info,
      ENDCASE => CSENull]};


  LongPath: PUBLIC PROC [t: Tree.Link] RETURNS [long: BOOLEAN] = {
    WITH t SELECT FROM
      subtree => {
	node: Tree.Index = index;
	long ← IF node = Tree.NullIndex
	  THEN FALSE
	  ELSE SELECT tb[node].name FROM
	    loophole, cast, openx, pad, chop => LongPath[tb[node].son[1]],
	    ENDCASE  -- dot, uparrow, dindex, reloc, seqindex, dollar, index -- =>
	      tb[node].attr2};
      ENDCASE => long ← FALSE;
    RETURN};


 -- type manipulation

  EqualTypes: PROC [type1, type2: CSEIndex] RETURNS [BOOLEAN] = {
    RETURN [Types.Equivalent[[own, type1], [own, type2]]]};
    
    
  UnresolvedTypes: SIGNAL RETURNS [CSEIndex] = CODE;

  BalanceTypes: PROC [type1, type2: CSEIndex] RETURNS [type: CSEIndex] = {
    n1, n2: CARDINAL;
    SELECT TRUE FROM
      (type1 = type2), (type2 = typeANY) => type ← type1;
      (type1 = typeANY) => type ← type2;
      ENDCASE => {
	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]] => {type ← type1; EXIT};
	    Types.Assignable[[own, type2], [own, type1]] => {type ← type2; EXIT};
	    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};
    RETURN};

  ForceType: PUBLIC PROC [t: Tree.Link, type: CSEIndex] RETURNS [Tree.Link] = {
    PushTree[t];
    IF t = Tree.Null THEN PushNode[cast, 1]
    ELSE 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[]]};


 -- operators
 
  UpArrow: PUBLIC PROC [node: Tree.Index] = {
    OPEN tb[node];
    type, nType: CSEIndex;
    attr: Attr;
    son[1] ← Exp[son[1], typeANY];
    type ← RType[];  attr ← RAttr[];  RPop[];  attr.const ← FALSE;
    DO
      nType ← NormalType[type];
      WITH t: seb[nType] SELECT FROM
	ref => {
	  RPush[UnderType[t.refType], attr];
	  attr2 ← seb[type].typeTag = long;
	  IF P3S.safety = checked AND ~t.counted THEN
	    Log.ErrorNode[unsafeOperation, node];
	  EXIT};
	record => {
	  IF Bundling[nType] = 0 THEN GO TO fail;
	  type ← Unbundle[LOOPHOLE[nType, RecordSEIndex]]};
	ENDCASE => GO TO fail;
      REPEAT
	fail => {
	  IF type # typeANY THEN Log.ErrorTree[typeClash, son[1]];
	  RPush[typeANY, attr]};
      ENDLOOP};


 -- arithmetic expression manipulation

  NumericAny: PROC [type: CSEIndex] RETURNS [CSEIndex] = {
    RETURN [SELECT seb[type].typeTag FROM
      long => MakeLongType[dataPtr.typeINT, type],
      ENDCASE => dataPtr.typeINT]};

  EvalNumeric: PROC [t: Tree.Link] RETURNS [val: Tree.Link] = {
    val ← GenericRhs[t, dataPtr.typeINT];
    SELECT NormalType[rStack[rI].type] FROM
      dataPtr.typeINT => NULL;
      typeANY => rStack[rI].type ← NumericAny[rStack[rI].type];
      ENDCASE => Log.ErrorTree[typeClash, val];
    RETURN};

  ArithOp: PROC [node: Tree.Index] = {
    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]};


  ArithType: PROC [type: CSEIndex] RETURNS [CSEIndex] = {
    type ← NormalType[type];
    RETURN [WITH seb[type] SELECT FROM
      relative => NormalType[UnderType[offsetType]],
      ENDCASE => type]};

  Plus: PROC [node: Tree.Index] = {
    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 = ref OR type = dataPtr.typeCHAR THEN {
      IF RCType[type] # none THEN Log.ErrorTree[typeClash, son[1]];
      lr ← TRUE; son[2] ← EvalNumeric[son[2]]}
    ELSE {
      SELECT type FROM
	dataPtr.typeINT, 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.typeINT, type = dataPtr.typeCHAR => NULL;
	seb[type].typeTag = ref =>
	  IF RCType[type] # none THEN Log.ErrorTree[typeClash, son[2]];
	ENDCASE => {
	  IF type # typeANY THEN Log.ErrorTree[typeClash, son[2]];
	  rStack[rI].type ← NumericAny[rStack[rI].type]}};
    IF P3S.safety = checked AND seb[type].typeTag = ref THEN
      Log.ErrorNode[unsafeOperation, node];
    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]};

  Minus: PROC [node: Tree.Index] = {
    OPEN tb[node];
    type, lType, rType: CSEIndex;
    lr: BOOLEAN;
    saveNP: NPUse;
    son[1] ← GenericRhs[son[1], typeANY];  saveNP ← phraseNP;
    type ← NormalType[rStack[rI].type]; lType ← ArithType[type];  lr ← TRUE;
    IF seb[lType].typeTag = ref OR lType = dataPtr.typeCHAR THEN {
      IF RCType[lType] # none THEN Log.ErrorTree[typeClash, son[1]];
      son[2] ← GenericRhs[son[2], typeANY];  rType ← ArithType[rStack[rI].type];
      SELECT TRUE FROM
	rType = typeANY => NULL;
	Types.Equivalent[[own, lType], [own, rType]] => lr ← FALSE;
	rType = dataPtr.typeINT => NULL;
	ENDCASE => Log.ErrorTree[typeClash, son[2]]}
    ELSE {
      SELECT type FROM
	dataPtr.typeINT, typeANY => NULL;
	ENDCASE => {Log.ErrorTree[typeClash, son[1]]; rStack[rI].type ← typeANY};
      son[2] ← EvalNumeric[son[2]]};
    IF P3S.safety = checked AND seb[lType].typeTag = ref THEN
      Log.ErrorNode[unsafeOperation, node];
    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.typeINT, rStack[rI].type] ELSE dataPtr.typeINT;
    RPop[];  phraseNP ← MergeNP[saveNP][phraseNP]};

  UnaryOp: PROC [node: Tree.Index] = {
    IF UniOperand[node] THEN {
      tb[node].son[1] ← EvalNumeric[tb[node].son[1]];  SetAttributes[node];
      IF tb[node].attr1 THEN rStack[rI].attr.const ← FALSE}};

  EnumOp: PROC [node: Tree.Index, target: CSEIndex] = {
    IF UniOperand[node] THEN {
      tb[node].son[1] ← GenericRhs[tb[node].son[1], target];  SetAttributes[node];
      IF ~IndexType[RType[]] THEN Log.ErrorTree[typeClash, tb[node].son[1]]}};

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

  In: PROC [node: Tree.Index] = {
    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.typeBOOL;
    phraseNP ← MergeNP[saveNP][phraseNP]};

  BoolOp: PROC [node: Tree.Index] = {
    OPEN tb[node];
    attr: Attr;
    saveNP: NPUse;
    SealRefStack[];
    son[1] ← Rhs[son[1], dataPtr.typeBOOL]; attr ← RAttr[]; saveNP ← phraseNP; 
    ClearRefStack[];
    son[2] ← Rhs[son[2], dataPtr.typeBOOL]; 
    UnsealRefStack[];
    attr ← And[attr, RAttr[]];
    RPop[];  RPop[];
    RPush[dataPtr.typeBOOL, attr]; phraseNP ← SequenceNP[saveNP][phraseNP]};


  Interval: PUBLIC PROC [t: Tree.Link, type: CSEIndex, constant: BOOLEAN] = {
    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]};

  Range: PUBLIC PROC [t: Tree.Link, type: CSEIndex] RETURNS [val: Tree.Link] = {
    subType: CSEIndex;
    node: Tree.Index;
    WITH t SELECT FROM
      subtree => {
	node ← index;
	SELECT tb[node].name FROM
	  subrangeTC => {
	    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]};
	  IN [intOO .. intCC] => {
	    val ← t;
	    subType ← IF type # typeANY THEN type ELSE dataPtr.typeINT;
	    Interval[t, subType, FALSE]};
	  ENDCASE => {
	    val ← TypeExp[t];
	    subType ← TargetType[UnderType[TypeForTree[val]]];
	    RPush[subType, FullAttr];  phraseNP ← none}};
      ENDCASE => {
	val ← TypeExp[t];  subType ← TargetType[UnderType[TypeForTree[val]]];
	RPush[subType, FullAttr];  phraseNP ← none};
    IF ~OrderedType[subType] AND subType # typeANY THEN Log.Error[nonOrderedType];
    IF ~Types.Assignable[[own, type], [own, subType]] THEN Log.ErrorTree[typeClash, val];
    RETURN};


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

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

  IfExp: PROC [node: Tree.Index, target: CSEIndex] = {
    OPEN tb[node];
    type: CSEIndex;
    attr: Attr;
    entryNP, saveNP: NPUse;
    SealRefStack[];
    son[1] ← Rhs[son[1], dataPtr.typeBOOL]; 
    attr ← RAttr[];  RPop[];  entryNP ← phraseNP;
    UnsealRefStack[];
    son[2] ← BalancedRhs[son[2], target];
    attr ← And[attr, RAttr[]]; saveNP ← SequenceNP[entryNP][phraseNP];
    type ← RType[];  RPop[];
    target ← BalancedTarget[target, type];
    son[3] ← BalancedRhs[son[3], target];  attr ← And[attr, RAttr[]];
    type ← BalanceTypes[type, RType[]
	! UnresolvedTypes => {RESUME [ResolveTypes[type, RType[], target, son[3]]]}];
    IF seb[type].typeTag = transfer THEN {
      IF OperandInline[son[2]] THEN Log.ErrorTree[misusedInline, son[2]];
      IF OperandInline[son[3]] THEN Log.ErrorTree[misusedInline, son[3]];
      attr.const ← FALSE};
    phraseNP ← BoundNP[saveNP][SequenceNP[entryNP][phraseNP]];
    RPop[];  RPush[type, attr]};

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

    Selection: Tree.Map = {
      subType: CSEIndex;
      entryNP: NPUse = phraseNP;
      v ← BalancedRhs[t, target];
      subType ← BalanceTypes[type, RType[]
	! UnresolvedTypes => {RESUME [ResolveTypes[type, RType[], target, v]]}];
      IF seb[subType].typeTag = transfer AND OperandInline[v] THEN
        Log.ErrorTree[misusedInline, v];
      saveNP ← BoundNP[saveNP][SequenceNP[entryNP][phraseNP]];
      IF subType # typeANY THEN type ← subType;
      IF ~started THEN target ← BalancedTarget[target, type];
      attr ← And[attr, RAttr[]];  RPop[];  started ← TRUE;  RETURN};

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


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

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


  TypeTest: PROC [node: Tree.Index, from, to: CSEIndex] = {
    subType: CSEIndex = CanonicalType[from];
    op: NarrowOp = Narrowing[type: subType, target: to];
    SELECT TRUE FROM
      op.error => Log.ErrorTree[typeClash, tb[node].son[1]];
      op.computed => Log.ErrorTree[missingBinding, tb[node].son[1]];
      op.unImpl => Log.Warning[unimplemented];
      ENDCASE;
    IF subType # from THEN tb[node].son[1] ← ForceType[tb[node].son[1], subType];
    tb[node].attr1 ← op.indirect;
    IF (tb[node].attr2 ← op.rtTest) THEN EnterType[MarkedType[to]];
    tb[node].attr3 ← op.tagTest};
    
    
  EndPoint: PROC [node: Tree.Index] = {
    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
	    codeINT, codeCHAR => NULL;
	    ENDCASE => GO TO fail;
        enumerated => NULL;
	relative => IF TypeForm[offsetType] # subrange THEN GO TO fail;
        subrange => NULL;
	long =>
	  IF NormalType[UnderType[rangeType]] # dataPtr.typeINT THEN GO TO fail;
        ENDCASE => GO TO fail;
      EXITS
	fail => Log.ErrorTree[typeClash, son[1]];
      END;
    RPush[type, FullAttr]};


  Unspec: PROC [type: CSEIndex] RETURNS [BOOLEAN] = {
    RETURN [WITH t: seb[type] SELECT FROM
      basic => t.code = codeANY,
      ENDCASE => FALSE]};
      
  SafeForUnspec: PROC [target: CSEIndex] RETURNS [BOOLEAN] = {
    RETURN [P3S.safety # checked OR RCType[target] = none]};
    
      
  Rhs: PUBLIC PROC [exp: Tree.Link, lhsType: CSEIndex] RETURNS [val: Tree.Link] = {
    rhsType: CSEIndex;
    val ← Exp[exp, lhsType];
    rhsType ← rStack[rI].type;
    SELECT TRUE FROM
      (lhsType = rhsType), Unspec[lhsType] => NULL;
      ENDCASE => {  -- immediate matching is inconclusive
	UNTIL Types.Assignable[[own, lhsType], [own, rhsType]] DO
	  WITH t: seb[rhsType] SELECT FROM
	    subrange => rhsType ← UnderType[t.rangeType];
	    record => {
	      IF Bundling[rhsType] = 0 THEN GO TO nomatch;
	      rhsType ← Unbundle[LOOPHOLE[rhsType, RecordSEIndex]];
	      val ← ForceType[val, IF Unspec[rhsType] THEN typeANY ELSE rhsType]};
	    ref, arraydesc => {
	      SELECT seb[lhsType].typeTag FROM
		long => {
		  IF ~Types.Assignable[[own, NormalType[lhsType]], [own, rhsType]] THEN
		    GO TO nomatch;
		  val ← Lengthen[val, lhsType]};
		ENDCASE => GO TO nomatch;
	      rhsType ← lhsType};
	    basic => {
	      IF Unspec[rhsType] AND SafeForUnspec[lhsType] THEN
		SELECT seb[lhsType].typeTag FROM
		  long => val ← Lengthen[val, MakeLongType[typeANY, lhsType]];
		  ENDCASE
	      ELSE SELECT seb[lhsType].typeTag FROM
		long => {
		  IF ~Types.Assignable[[own, NormalType[lhsType]], [own, rhsType]] THEN
		    GO TO nomatch;
		  val ← Lengthen[val, lhsType]};
		real =>
		  IF rhsType = dataPtr.typeINT THEN {
		    val ← Float[val, rhsType, lhsType]; rStack[rI].attr.const ← FALSE} 
		  ELSE GO TO nomatch;
		ENDCASE => GO TO nomatch;
	      rhsType ← lhsType};
	    long => {
	      subType: CSEIndex = NormalType[rhsType];
	      SELECT seb[lhsType].typeTag FROM
		long =>
		  SELECT TRUE FROM
		    Unspec[NormalType[lhsType]] => lhsType ← rhsType;
		    Unspec[subType] AND SafeForUnspec[lhsType] =>
		      rhsType ← lhsType;
		    ENDCASE => GO TO nomatch;
		real =>
		  IF subType = dataPtr.typeINT THEN {
		    val ← Float[val, rhsType, lhsType];  rStack[rI].attr.const ← FALSE;
		    rhsType ← lhsType} 
		  ELSE GO TO nomatch;
		basic, subrange => {
		  IF ~Types.Assignable[[own, subType], [own, lhsType]] THEN
		    GO TO nomatch;
		  rhsType ← UnderType[t.rangeType]; val ← Shorten[val, rhsType]};
		enumerated =>
		  IF Types.Equivalent[[own, rhsType], [own, dataPtr.typeATOM]] THEN {
		    Log.ErrorTree[missingCoercion, val];  rhsType ← lhsType}
		  ELSE GOTO nomatch;
		ENDCASE => GO TO nomatch};
	    ENDCASE => GO TO nomatch;
	  REPEAT
	    nomatch => {	-- no coercion is possible
	      Log.ErrorTree[typeClash,
		IF exp = Tree.Null THEN implicit.tree ELSE val];
	      rhsType ← lhsType};
	  ENDLOOP;
	rStack[rI].type ← rhsType};
    IF seb[rhsType].typeTag = transfer AND OperandInline[val] THEN
      Log.ErrorTree[misusedInline, val];
    RETURN};


  GenericRhs: PROC [exp: Tree.Link, target: CSEIndex] RETURNS [val: Tree.Link] = {
    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 => {
	  IF Bundling[type] = 0 THEN EXIT;
	  type ← Unbundle[LOOPHOLE[type, RecordSEIndex]];
	  val ← ForceType[val, type]};
	ENDCASE => EXIT;
      rStack[rI].type ← type;
      ENDLOOP;
    SELECT seb[target].typeTag FROM
      enumerated =>
        IF EqualTypes[type, dataPtr.typeATOM] THEN {
          Log.ErrorTree[missingCoercion, val];  rStack[rI].type ← target};
      ENDCASE;
    RETURN};

  BalancedRhs: PROC [exp: Tree.Link, target: CSEIndex] RETURNS [val: Tree.Link] = {
    type: CSEIndex;
    val ← Exp[exp, target];
    SELECT seb[target].typeTag FROM
      long, real => {
	type ← CanonicalType[rStack[rI].type];
	IF type # typeANY AND seb[target].typeTag # seb[type].typeTag
	 AND Types.Equivalent[[own, NormalType[target]], [own, type]] THEN {
	  SELECT seb[target].typeTag FROM
	    long => IF seb[type].typeTag # real THEN val ← Lengthen[val, target];
	    real => {val ← Float[val, type, target]; rStack[rI].attr.const ← FALSE};
	    ENDCASE;
	  rStack[rI].type ← target}};
      enumerated =>
        IF EqualTypes[rStack[rI].type, dataPtr.typeATOM] THEN {
          Log.ErrorTree[missingCoercion, val];  rStack[rI].type ← target};
      ENDCASE;
    RETURN};


  AttrClass: PROC [type: CSEIndex] RETURNS [{short, long, real}] = {
    RETURN [WITH t: seb[type] SELECT FROM
      long => long,
      real => real,
      relative => AttrClass[UnderType[t.offsetType]],
      ENDCASE => short]};

  SetAttributes: PROC [node: Tree.Index] = {
    SELECT AttrClass[rStack[rI].type] FROM
      long => {tb[node].attr1 ← FALSE; tb[node].attr2 ← TRUE};
      real => {tb[node].attr1 ← TRUE; tb[node].attr2 ← FALSE};
      ENDCASE => tb[node].attr1 ← tb[node].attr2 ← FALSE};

  BalanceAttributes: PROC [node: Tree.Index] = {
    lType, rType: CSEIndex;
    lType ← rStack[rI-1].type;  rType ← rStack[rI].type;
    SELECT AttrClass[lType] FROM
      long => {
	SELECT AttrClass[rType] FROM
	  long => {tb[node].attr1 ← FALSE; tb[node].attr2 ← TRUE};
	  real => {
	    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};
	  ENDCASE => {
	    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}};
      real => {
	tb[node].attr1 ← TRUE;  tb[node].attr2 ← FALSE;
	SELECT AttrClass[rType] FROM
	  real => NULL;
	  ENDCASE => {
	    rStack[rI].type ← lType;
	    tb[node].son[2] ← Float[tb[node].son[2], rType, lType];
	    rStack[rI].attr.const ← FALSE}};
      ENDCASE =>
	SELECT AttrClass[rType] FROM
	  long => {
	    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};
	  real => {
	    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};
	  ENDCASE => tb[node].attr1 ← tb[node].attr2 ← FALSE};

  Lengthen: PROC [t: Tree.Link, target: CSEIndex] RETURNS [Tree.Link] = {
    PushTree[t]; PushNode[lengthen, 1]; SetInfo[target]; RETURN [PopTree[]]};

  Shorten: PROC [t: Tree.Link, target: CSEIndex] RETURNS [Tree.Link] = {
    PushTree[t]; PushNode[shorten, 1]; SetInfo[target]; RETURN [PopTree[]]};

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


 -- expressions

  implicit: PUBLIC P3S.ImplicitInfo;	-- implied attributes of Tree.Null
  
  Exp: PUBLIC PROC [exp: Tree.Link, target: CSEIndex] RETURNS [val: Tree.Link] = {
    type: CSEIndex;
    phraseNP ← none;
    IF exp = Tree.Null THEN {RPush[implicit.type, implicit.attr]; RETURN [Tree.Null]};
    WITH e: exp SELECT FROM
      symbol => {
	sei: ISEIndex = e.index;
	attr: Attr;
	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};
      hash =>
	WITH t: seb[target] SELECT FROM
	  enumerated => {PushCtx[t.valueCtx]; val ← Id[e.index]; PopCtx[]};
	  ENDCASE => val ← Id[e.index];
      literal => {
	attr: Attr;
	attr.noXfer ← attr.noAssign ← TRUE;
	WITH e.info SELECT FROM
	  string => {
	    [val, type] ← StringRef[exp, target];
	    attr.const ← FALSE;
	    IF dataPtr.definitionsOnly THEN Log.ErrorTree[unimplemented, exp]};
	  ENDCASE => {type ← dataPtr.typeINT; attr.const ← TRUE; val ← exp};
	RPush[type, attr]};
      subtree => {
	node: Tree.Index ← e.index;
	val ← exp;	-- the default
	SELECT tb[node].name FROM

	  dot => {node ← Dot[node, target]; val ← [subtree[node]]};
	  uparrow => UpArrow[node];

	  apply => {
	    node ← Apply[node, target, FALSE]; val ← [subtree[node]];
	    CheckNonVoid[node, target]};

	  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.typeBOOL];
	  or, and => BoolOp[node];
	  ifx => IfExp[node, target];
	  casex => SelectExp[node, target, Case];
	  bindx => SelectExp[node, target, Discrimination];
	  assignx => Assignment[node];
	  extractx => {Extract[node]; CheckNonVoid[node, target]};
	  min, max => MinMax[node, target];
	  pred, succ => EnumOp[node, target];
	  addr, base, length, arraydesc => AddrOp[node, target];
	  all => All[node, target];

	  mwconst =>
	    IF tb[node].attr1 THEN RPush[dataPtr.typeREAL, FullAttr]
	    ELSE {
	      IF longUnsigned = CSENull THEN
	        longUnsigned ← MakeLongType[dataPtr.idCARDINAL, typeANY];
	      RPush[longUnsigned, FullAttr]};

	  void => RPush[target, VoidAttr];
	  clit => RPush[dataPtr.typeCHAR, FullAttr];

	  llit => {
	    attr: Attr ← FullAttr;
	    attr.const ← FALSE;  RPush[dataPtr.typeSTRING, attr]};

	  atom => {
	    hti: HTIndex = GetHash[tb[node].son[1]];
	    WITH t: seb[target] SELECT FROM
	      enumerated => {
	        sei: ISEIndex;
	        IF ~([sei: sei]←SearchCtxList[hti, t.valueCtx]).found THEN
	          Log.ErrorHti[unknownId, hti];
	        tb[node].son[1] ← Tree.Null;  FreeNode[node];  node ← Tree.NullIndex;
	        val ← [symbol[index: sei]];  RPush[target, FullAttr]};
	      ENDCASE => {
	        SymLiteralOps.EnterAtom[hti]; EnterType[dataPtr.typeAtomRecord, FALSE];
		RPush[dataPtr.typeATOM, FullAttr]}};

	  nil => {
	    OPEN tb[node];
	    SELECT TRUE FROM
	      (son[1] # Tree.Null) => {
		son[1] ← TypeExp[son[1]];  type ← UnderType[TypeForTree[son[1]]]};
	      (target # typeANY) => type ← target;
	      ENDCASE => type ← MakeRefType[typeANY, typeANY];
	    IF ~NullableType[type] THEN Log.ErrorTree[typeClash, val];
	    RPush[type, FullAttr]};

	  new, signalx, errorx, fork, joinx, create, startx, cons, listcons => {
	    val ← MiscXfer[node, target];
	    node ← GetNode[val];  CheckNonVoid[node, target]};

	  syserrorx => {
	    RPush[CSENull, EmptyAttr]; CheckNonVoid[node, target]};

	  lengthen => {
	    OPEN tb[node];
	    subType: CSEIndex;
	    son[1] ← GenericRhs[
		son[1],
		WITH seb[target] SELECT FROM
		  long => TargetType[UnderType[rangeType]],
		  ENDCASE => target];
	    subType ← TargetType[rStack[rI].type];
	    IF subType = dataPtr.typeINT
	     OR seb[subType].typeTag = ref
	     OR seb[subType].typeTag = arraydesc
	     OR subType = typeANY THEN rStack[rI].type ← MakeLongType[subType, target]
	    ELSE {Log.ErrorTree[typeClash, son[1]]; rStack[rI].type ← typeANY}};

	  narrow => {
	    OPEN tb[node];
	    IF son[2] = Tree.Null THEN {
	      IF target = typeANY THEN Log.ErrorNode[noTarget, node];
	      type ← target}
	    ELSE {son[2] ← TypeExp[son[2]]; type ← UnderType[TypeForTree[son[2]]]};
	    son[1] ← Exp[son[1], TargetType[type]];
	    TypeTest[node: node, from: rStack[rI].type, to: type];
	    IF attr3 AND ~attr1 AND son[2] = Tree.Null THEN Log.ErrorNode[noTarget, node];
	    IF RCType[type] = simple THEN {
	      nType: CSEIndex = NormalType[type];
	      WITH t: seb[nType] SELECT FROM
	        ref => EnterType[t.refType, FALSE];
	        ENDCASE => NULL};
	    IF tb[node].nSons > 2 THEN [] ← CatchPhrase[tb[node].son[3]];
	    rStack[rI].type ← type;
	    rStack[rI].attr.const ← rStack[rI].attr.noXfer ← FALSE};

	  istype => {
	    OPEN tb[node];
	    son[1] ← Exp[son[1], typeANY];
	    son[2] ← TypeExp[son[2]]; type ← UnderType[TypeForTree[son[2]]];
	    TypeTest[node: node, from: RType[], to: type];
	    rStack[rI].type ← dataPtr.typeBOOL; rStack[rI].attr.const ← FALSE};

	  safen => tb[node].son[1] ← Exp[tb[node].son[1], target];

	  loophole => {
	    OPEN tb[node];
	    subType: CSEIndex;
	    son[1] ← Exp[son[1], typeANY];  subType ← RType[];
	    IF seb[subType].typeTag = transfer AND OperandInline[son[1]] THEN
	      Log.ErrorTree[misusedInline, son[1]];
	    IF son[2] = Tree.Null THEN {
	      IF target = typeANY THEN Log.ErrorNode[noTarget, node];
	      rStack[rI].type ← target}
	    ELSE {
	      son[2] ← TypeExp[son[2]];
	      rStack[rI].type ← UnderType[TypeForTree[son[2]]]};
	    IF RCType[rStack[rI].type] # none THEN {
	      rStack[rI].attr.const ← FALSE;
	      IF P3S.safety = checked THEN Log.ErrorNode[unsafeOperation, node]}};

	  size => {
	    OPEN tb[node];
	    attr: Attr;
	    son[1] ← TypeAppl[son[1]]; attr ← RAttr[]; RPop[];
	    IF son[2] # Tree.Null THEN {
	      saveNP: NPUse = phraseNP;
	      son[2] ← Rhs[son[2], dataPtr.typeINT];
	      attr ← And[attr, RAttr[]]; RPop[];
	      phraseNP ← MergeNP[saveNP][phraseNP]};
	    RPush[dataPtr.typeINT, attr]};

	  first, last => EndPoint[node];

	  typecode => {
	    tb[node].son[1] ← TypeExp[tb[node].son[1]];
	    EnterType[TypeForTree[tb[node].son[1]], FALSE];
	    RPush[typeANY, FullAttr]};

	  self => {
	    val ← P3S.self.tree;  P3S.self.tree ← Tree.Null;
	    phraseNP ← P3S.self.np;  RPush[P3S.self.type, P3S.self.attr];
	    FreeNode[node];  node ← Tree.NullIndex};

	  cast => {
	    tb[node].son[1] ← Exp[tb[node].son[1], target];
	    rStack[rI].type ← target};

	  stringinit => {
	    tb[node].son[2] ← Rhs[tb[node].son[2], dataPtr.typeINT];
	    IF ~RAttr[].const THEN Log.ErrorTree[nonConstant, tb[node].son[2]];
	    RPop[];  RPush[dataPtr.typeSTRING, VoidAttr]};

	  item => tb[node].son[2] ← Exp[tb[node].son[2], target];
	  ENDCASE => {Log.Error[unimplemented]; RPush[typeANY, EmptyAttr]};

	IF node # Tree.NullIndex THEN tb[node].info ← rStack[rI].type};
      ENDCASE;
    RETURN};

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

  VoidExp: PUBLIC PROC [exp: Tree.Link] RETURNS [val: Tree.Link] = {
    val ← Exp[exp, typeANY]; RPop[]; RETURN};

  UniOperand: PROC [node: Tree.Index] RETURNS [valid: BOOLEAN] = {
    l: CARDINAL = ListLength[tb[node].son[1]];
    IF ~(valid ← l=1) THEN {
      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]};
    RETURN};


 -- overloaded string literals
 
  StringRef: PROC [t: Tree.Link, target: CSEIndex] RETURNS [v: Tree.Link, type: CSEIndex] = {
    IF RCType[target] = none THEN {type ← dataPtr.typeSTRING; v ← t}
    ELSE {
      nType: CSEIndex = NormalType[target];
      rType: SEIndex = WITH t: seb[nType] SELECT FROM
	  ref => t.refType,
	  ENDCASE => dataPtr.idTEXT;
      form: TextForm = TextRep[rType];
      cType: SEIndex = IF form = text THEN dataPtr.idTEXT ELSE rType;
      type ← textType[form];
      IF type = CSENull THEN {
	type ← MakeLongType[MakeRefType[cType: cType, hint: nType, counted: TRUE], target];
	textType[form] ← type};
      EnterType[TypeRoot[cType], FALSE];  EnterType[type, FALSE];
      WITH e: t SELECT FROM
	literal =>
	  WITH e.info SELECT FROM
	    string => {
	      index ← LiteralOps.FindHeapString[index, TypeRoot[cType]];
	      SymLiteralOps.EnterText[index]};
	    ENDCASE;
	ENDCASE;
      PushTree[t];  PushNode[textlit, 1];
      SetAttr[2, TRUE];  SetInfo[type];  v ← PopTree[]};
    RETURN};

  }.