-- file Pass3V.Mesa
-- last modified by Satterthwaite, September 17, 1982 2:19 pm

DIRECTORY
  Alloc: TYPE USING [Notifier],
  ComData: TYPE USING [seAnon, textIndex, typeBOOL],
  Copier: TYPE USING [CopyUnion],
  Log: TYPE USING [Error, ErrorHti, ErrorSei, ErrorTree, Warning],
  P3: TYPE USING [
    Attr, NarrowOp, NPUse, phraseNP,
    BaseTree, CanonicalType, EnterType, Exp, FindSe, FirstId, ForceType, LongPath,
    MarkedType, Narrowing, OpenPointer, OperandType, PopCtx,
    PushCtx, PushHtCtx, PushRecordCtx, RAttr, Rhs, RPop, RPush, RType, Scope,
    SealRefStack, SearchCtxList, TargetType, TopCtx, TypeForTree, UnsealRefStack,
    UpdateTreeAttr, VoidExp],
  P3S: TYPE USING [ImplicitInfo, implicit, implicitRecord, safety],
  Symbols: TYPE USING [
    Base, HTIndex, SEIndex, ISEIndex, CSEIndex, RecordSEIndex, CTXIndex,
    HTNull, SENull, ISENull, CSENull, CTXNull, typeANY, typeTYPE, seType, ctxType],
  SymbolOps: TYPE USING [NextSe, NormalType, RCType, ReferentType, TypeForm, UnderType],
  Tree: TYPE USING [Base, Index, Link, Map, Null, Scan, treeType],
  TreeOps: TYPE USING [
    GetHash, GetNode, ListHead, ListTail, OpName, PopTree, PushTree, PushNode, PushSe,
    ScanList, SetAttr, SetInfo, UpdateList];

Pass3V: PROGRAM
    IMPORTS
      Copier, Log, P3, P3S, SymbolOps, TreeOps, 
      dataPtr: ComData
    EXPORTS P3 = { 
  OPEN SymbolOps, P3, Symbols, TreeOps;

-- tables defining the current symbol table

  tb: Tree.Base;		-- tree base
  seb: Symbols.Base;		-- se table
  ctxb: Symbols.Base;		-- context table

  VRNotify: PUBLIC Alloc.Notifier = {
    -- called whenever the main symbol table is repacked
    tb ← base[Tree.treeType];  seb ← base[seType];  ctxb ← base[ctxType]};


-- finding union and discriminated types
-- N. B. the following two entries cannot assume well-formed type links

  VariantUnionType: PUBLIC PROC [type: SEIndex] RETURNS [CSEIndex] = {
    rType: CSEIndex = ConsType[type];
    RETURN [WITH seb[rType] SELECT FROM
      record =>
	IF hints.variant
	  THEN ConsType[TypeForSe[UnionField[LOOPHOLE[rType, RecordSEIndex]]]]
	  ELSE typeANY,
      ENDCASE => typeANY]};


  SelectVariantType: PUBLIC PROC [type: SEIndex, tag: HTIndex] RETURNS [sei: ISEIndex] = {
    matched: BOOLEAN;
    vType: CSEIndex = VariantUnionType[type];
    WITH seb[vType] SELECT FROM
      union => [matched, sei] ← SearchCtxList[tag, caseCtx];
      ENDCASE => matched ← FALSE;
    IF ~matched THEN sei ← ISENull;
    RETURN};


  SequenceField: PUBLIC PROC [rSei: RecordSEIndex] RETURNS [ISEIndex] = {
    sei: ISEIndex = UnionField[rSei];
    RETURN [IF TypeForm[seb[sei].idType] = sequence THEN sei ELSE ISENull]};


 -- auxiliary procedures (for avoiding UnderType when potentially unsafe)

  UnionField: PROC [rSei: RecordSEIndex] RETURNS [ISEIndex] = {
    sei, root, next: ISEIndex;
    ctx: CTXIndex = seb[rSei].fieldCtx;
    IF ctxb[ctx].ctxType = simple THEN
      FOR sei ← ctxb[ctx].seList, next UNTIL sei = ISENull DO
	next ← NextSe[sei];
	IF next = ISENull THEN RETURN [sei];
	ENDLOOP
    ELSE {	-- defined in another module, UnderType is safe
      repeated: BOOLEAN ← FALSE;
      DO
	sei ← root ← ctxb[ctx].seList;
	DO
	  IF sei = ISENull THEN EXIT;
	  SELECT TypeForm[seb[sei].idType] FROM
	    union, sequence => RETURN [sei];
	    ENDCASE;
	  IF (sei ← NextSe[sei]) = root THEN EXIT;
	  ENDLOOP;
	IF repeated THEN EXIT;
	Copier.CopyUnion[seb[rSei].fieldCtx];  repeated ← TRUE;
	ENDLOOP};
    RETURN [dataPtr.seAnon]};

  ResolveId: PROC [hti: HTIndex, ctx: CTXIndex] RETURNS [sei: ISEIndex] = {
    currentCtx: CTXIndex = TopCtx[];
    IF ctx = currentCtx THEN sei ← FindSe[hti].symbol
    ELSE {PopCtx[]; sei ← ResolveId[hti, ctx]; PushCtx[currentCtx]};
    RETURN};

  TypeForSe: PROC [sei: ISEIndex] RETURNS [type: SEIndex] = INLINE {
    node: Tree.Index;
    t: Tree.Link;
    IF seb[sei].mark3 THEN RETURN [seb[sei].idType];
    node ← seb[sei].idValue;
    IF tb[node].name # decl THEN RETURN [typeTYPE];
    t ← tb[node].son[2];
    type ← WITH t SELECT FROM
      hash => ResolveId[index, seb[sei].idCtx],
      symbol => index,
      subtree => tb[index].info,
      ENDCASE => typeANY;
    RETURN};

  ConsType: PROC [type: SEIndex] RETURNS [CSEIndex] = {
    WITH se: seb[type] SELECT FROM
      id =>
	IF se.mark3 THEN
	  RETURN [IF se.idType # typeTYPE THEN typeANY ELSE ConsType[se.idInfo]]
	ELSE {
	  node: Tree.Index = se.idValue;
	  RETURN [IF tb[node].name # typedecl
	    THEN typeANY
	    ELSE ConsType[ResolveTreeType[tb[node].son[2], se.idCtx]]]};
      cons => RETURN [LOOPHOLE[type, CSEIndex]];
      ENDCASE => ERROR};

  ResolveTreeType: PROC [t: Tree.Link, ctx: CTXIndex] RETURNS [type: SEIndex] = {
    WITH t SELECT FROM
      hash => type ← ResolveId[index, ctx];
      symbol => type ← index;
      subtree => {
	node: Tree.Index = index;
	type ← IF tb[node].info # SENull
	  THEN tb[node].info
	  ELSE
	    SELECT tb[node].name FROM
	      discrimTC =>
		WITH tb[node].son[2] SELECT FROM
		  hash => SelectVariantType[ResolveTreeType[tb[node].son[1], ctx], index],
		  ENDCASE => ERROR,
	      ENDCASE => ERROR};
      ENDCASE => ERROR;
    RETURN};


 -- type discrimination

  DiscriminatedType: PUBLIC PROC [baseType: CSEIndex, t: Tree.Link]
      RETURNS [type: CSEIndex] = {
    IF t = Tree.Null THEN type ← P3S.implicitRecord
    ELSE
      WITH t SELECT FROM
	subtree => {
	  node: Tree.Index = index;
	  temp: Tree.Link;
	  SELECT tb[node].name FROM
	    union => {
	      type ← WITH tb[node].son[1] SELECT FROM symbol => UnderType[index], ENDCASE => ERROR;
	      WITH seb[type] SELECT FROM
		record =>
		  IF hints.variant AND tb[node].son[2] # Tree.Null
		   AND (temp←ListTail[tb[node].son[2]]) # Tree.Null THEN
		    type ← DiscriminatedType[type, temp];
		ENDCASE => ERROR};
	    dollar => type ← OperandType[tb[node].son[1]];
	    dot => {
	      subType: CSEIndex = NormalType[OperandType[tb[node].son[1]]];
	      type ← WITH seb[subType] SELECT FROM ref => UnderType[refType], ENDCASE => ERROR};
	    assignx => type ← DiscriminatedType[baseType, tb[node].son[2]];
	    ENDCASE => type ← baseType};
	ENDCASE => type ← baseType;
    RETURN};


-- binding of variant records

  Discrimination: PUBLIC PROC [node: Tree.Index, selection: Tree.Map] = {
    OPEN tb[node];
    copy: BOOLEAN = (OpName[ListHead[son[3]]] = ditem);
    type, subType: CSEIndex;
    vCtx: CTXIndex;
    base, discBase: Tree.Link;
    attr: Attr;
    entryNP: NPUse ← none;
    unreachable: BOOLEAN ← FALSE;

    BindError: PROC = {
      IF son[2] # Tree.Null THEN son[2] ← VoidExp[son[2]]; vCtx ← CTXNull};

    PushCommonCtx: PROC = {
      SELECT TRUE FROM
	copy OR (seb[type].typeTag # record) => PushCtx[CTXNull];
	(baseId = HTNull) => PushRecordCtx[LOOPHOLE[type], base, indirect];
	ENDCASE => PushHtCtx[baseId, base, indirect]};

    BindItem: Tree.Scan = {
      subNode: Tree.Index = GetNode[t];
      saveIndex: CARDINAL = dataPtr.textIndex;
      IF tb[subNode].name = ditem THEN {
	declNode: Tree.Index = GetNode[tb[subNode].son[1]];
	declType: CSEIndex;
	Item: Tree.Map = {phraseNP ← entryNP; v ← selection[t]};
	op: NarrowOp;
	dataPtr.textIndex ← tb[declNode].info;
	IF unreachable THEN {Log.Warning[unreachable]; unreachable ← FALSE};
	Scope[subNode, Item];
	declType ← UnderType[TypeForTree[tb[declNode].son[2]]];
	op ← Narrowing[subType, declType];
	SELECT TRUE FROM
	  ~copy => Log.Error[discrimForm];
	  op.error => Log.ErrorSei[typeClash, FirstId[declNode]];
	  op.computed => Log.ErrorTree[missingBinding, base];
	  op.unImpl => Log.Warning[unimplemented];
	  ~(op.rtTest OR op.tagTest) => unreachable ← TRUE;
	  ENDCASE;
	tb[subNode].attr1 ← op.indirect;
	IF (tb[subNode].attr2 ← op.rtTest) THEN EnterType[MarkedType[declType]];
	tb[subNode].attr3 ← op.tagTest}
      ELSE {
	vType: CSEIndex;
	dataPtr.textIndex ← tb[subNode].info;
	IF copy THEN {Log.Error[discrimForm]; attr3 ← FALSE};
	[tb[subNode].son[1], vType] ← BindTest[tb[subNode].son[1], vCtx];
	IF vType = typeANY THEN PushCommonCtx[]
	ELSE {
	  WITH discBase SELECT FROM
	    subtree => tb[index].info ← vType;
	    ENDCASE => ERROR;
	  IF baseId = HTNull THEN PushRecordCtx[LOOPHOLE[vType], discBase, FALSE]
	  ELSE PushHtCtx[baseId, discBase, FALSE]};
	phraseNP ← entryNP;
	tb[subNode].son[2] ← selection[tb[subNode].son[2]];
	PopCtx[];
	tb[subNode].attr1 ← TRUE};
      dataPtr.textIndex ← saveIndex};

    saveImplicit: P3S.ImplicitInfo = P3S.implicit;
    idNode: Tree.Index = GetNode[son[1]];
    baseId: HTIndex = GetHash[tb[idNode].son[1]];
    indirect: BOOLEAN;
    SealRefStack[];
    base ← tb[idNode].son[2] ← Exp[tb[idNode].son[2], typeANY];
    type ← RType[];  attr ← RAttr[];  RPop[];
    UnsealRefStack[];
    subType ← CanonicalType[type];
    IF subType # type THEN tb[idNode].son[2] ← ForceType[tb[idNode].son[2], subType];
    type ← NormalType[subType];
    P3S.implicit ← [tree: base, type: subType, attr: attr];
    IF (attr3 ← copy) THEN {
      P3S.implicit.attr.noAssign ← P3S.implicit.attr.noXfer ← TRUE;
      SELECT TypeForm[type] FROM
        ref => {
	  attr2 ← (TypeForm[ReferentType[type]] = any);
	  indirect ← TRUE};
	transfer => {attr2 ← TRUE; indirect ← FALSE};
	ENDCASE => {attr2 ← FALSE; indirect ← FALSE};
      IF baseId # HTNull THEN Log.Error[discrimForm]}
    ELSE {
      long: BOOLEAN;
      WITH t: seb[type] SELECT FROM
	ref => {
	  indirect ← TRUE;
	  [base, type] ← OpenPointer[base, subType];
	  subType ← OperandType[base];  long ← seb[subType].typeTag = long};
	ENDCASE => {indirect ← FALSE; long ← LongPath[base]};
      IF P3S.safety = checked AND RCType[type] # none THEN
        Log.ErrorTree[unsafeSelection, base];
      WITH seb[type] SELECT FROM
	record => {
	  tb[idNode].son[2] ← base ← BaseTree[base, subType];
	  IF hints.variant THEN {
	    uType: CSEIndex = VariantUnionType[type];
	    WITH u: seb[uType] SELECT FROM
	      union => {
		tagType: CSEIndex = UnderType[seb[u.tagSei].idType];
		vCtx ← u.caseCtx;
		IF son[2] = Tree.Null THEN {
		  IF ~u.controlled THEN Log.ErrorTree[missingBinding, base];
		  [] ← UpdateTreeAttr[base];  entryNP ← phraseNP;
		  PushTree[base];  PushSe[u.tagSei];
		  PushNode[IF indirect THEN dot ELSE dollar, 2];
		  SetInfo[tagType]; SetAttr[2, long]; son[2] ← PopTree[]}
		ELSE {
		  IF u.controlled THEN Log.ErrorTree[spuriousBinding, son[2]];
		  PushCommonCtx[];
		  son[2] ← Rhs[son[2], TargetType[tagType]];
		  entryNP ← phraseNP;  RPop[];
		  PopCtx[]}};
	      ENDCASE => {Log.Error[noAccess]; BindError[]}}
	  ELSE {Log.ErrorTree[noVariants, tb[idNode].son[2]]; BindError[]};
	  PushTree[base];
	  IF indirect THEN {PushNode[uparrow, 1]; SetAttr[2, long]}
	  ELSE PushNode[cast, 1];
	  discBase ← PopTree[]};
	ENDCASE => {
	  Log.ErrorTree[noVariants, tb[idNode].son[2]];  BindError[];
	  discBase ← Tree.Null}};
    attr1 ← indirect;
    ScanList[son[3], BindItem];
    PushCommonCtx[]; phraseNP ← entryNP; son[4] ← selection[son[4]]; PopCtx[];
    RPush[CSENull, attr];
    P3S.implicit ← saveImplicit};


  BindTest: PROC [t: Tree.Link, vCtx: CTXIndex]
      RETURNS [val: Tree.Link, vType: CSEIndex] = {
    mixed: BOOLEAN ← FALSE;

    TestItem: Tree.Map = {
      WITH t SELECT FROM
	subtree => {
	  subNode: Tree.Index = index;
	  SELECT tb[subNode].name FROM
	    relE =>
	      WITH tb[subNode].son[2] SELECT FROM
		hash => {
		  iType: ISEIndex;
		  uType: CSEIndex;
		  found: BOOLEAN;
		  [found, iType] ← SearchCtxList[index, vCtx];
		  IF found THEN {
		    uType ← UnderType[iType];
		    tb[subNode].son[2] ← [symbol[index: iType]];
		    SELECT vType FROM
		      uType => NULL;
		      typeANY => vType ← uType;
		      ENDCASE => mixed ← TRUE}
		  ELSE IF vCtx # CTXNull THEN Log.ErrorHti[unknownVariant, index];
		  tb[subNode].info ← dataPtr.typeBOOL;
		  tb[subNode].attr1 ← tb[subNode].attr2 ← FALSE;
		  v ← t};
		ENDCASE => {
		  v ← Rhs[t, dataPtr.typeBOOL];  RPop[];
		  Log.ErrorTree[nonVariantLabel, t]};
	    ENDCASE => {
	      v ← Rhs[t, dataPtr.typeBOOL];  RPop[];
	      Log.ErrorTree[nonVariantLabel, t]}};
	ENDCASE => ERROR;
      RETURN};

    vType ← typeANY;  val ← UpdateList[t, TestItem];
    IF mixed THEN vType ← typeANY;
    RETURN};

  }.