/* NSC -- new Scala compiler
 * Copyright 2005-2009 LAMP/EPFL
 * @author  Martin Odersky
 */

// $Id: TypeStacks.scala 18387 2009-07-24 15:28:37Z odersky $

package scala.tools.nsc
package backend
package icode

/** This trait ...
 *
 *  @author  Iulian Dragos
 *  @version 1.0
 */
trait TypeStacks { self: ICodes =>
  import opcodes._
  import global.{Symbol, Type, definitions}

  /* This class simulates the type of the opperand
   * stack of the ICode.
   */
  type Rep = List[TypeKind]

  class TypeStack {
    var types: Rep = Nil

    def this(stack: Rep) = {
      this()
      this.types = stack
    }

    def this(that: TypeStack) = this(that.types)

    def length: Int = types.length

    /** Push a type on the type stack. UNITs are ignored. */
    def push(t: TypeKind) =
      if (t != UNIT)
        types = t :: types

    def head: TypeKind = types.head

    /** Removes the value on top of the stack, and returns it. It assumes
     *  the stack contains at least one element.
     */
    def pop: TypeKind = {
      val t = types.head
      types = types.tail
      t
    }

    /** Return the topmost two values on the stack. It assumes the stack
     *  is large enough. Topmost element first.
     */
    def pop2: (TypeKind, TypeKind) = (pop, pop)

    /** Return the topmost three values on the stack. It assumes the stack
     *  is large enough. Topmost element first.
     */
    def pop3: (TypeKind, TypeKind, TypeKind) = (pop, pop, pop)

    /** Drop the first n elements of the stack. */
    def pop(n: Int): List[TypeKind] = {
      val prefix = types.take(n)
      types = types.drop(n)
      prefix
    }
    
    def apply(n: Int): TypeKind = types(n)

    /**
     * A TypeStack aggress with another one if they have the same
     * length and each type kind agrees position-wise. Two 
     * types agree if one is a subtype of the other.
     */
    def agreesWith(other: TypeStack): Boolean =
      (types.length == other.types.length) &&
      List.forall2(types, other.types) ((t1, t2) => t1 <:< t2 || t2 <:< t1)

    def mergeWith(that: TypeStack): TypeStack = {
      def merge(a: TypeStack, b: TypeStack): TypeStack = {
        val lst = List.map2(a.types, b.types) ((k1, k2) => k1 match {
          case REFERENCE(cls1) =>
            val REFERENCE(cls2) = k2
            lub(k1,k2)
          case _ => k1
        })
        new TypeStack(lst)
      }

      assert(this agreesWith that,
             "Incompatible type stacks: " + this + ", " + that)
      merge(this, that)
    }

    /* This method returns a String representation of the stack */
    override def toString() = types.mkString("<", ",", ">")

    override def equals(other: Any): Boolean =
      other.isInstanceOf[TypeStack] &&
      List.forall2(other.asInstanceOf[TypeStack].types, types)((a, b) => a == b)
  }

}