//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\
// PLEASE DO NOT EDIT WITHOUT THE EXPLICIT CONSENT OF THE AUTHOR! \\
//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\

package ilog.language.design.kernel;

/**
 * @version     Last modified on Fri Oct 18 17:50:36 2002 by hak
 * @author      <a href="mailto:hak@ilog.fr">Hassan A&iuml;t-Kaci</a>
 * @copyright   &copy; 2000-2002 <a href="http://www.ilog.fr/">ILOG, S.A.</a>
 */

import ilog.language.design.types.*;
import ilog.language.design.base.*;

import java.util.ArrayList;

/**
 * This class is just a <a href="Let.html"><tt>Let</tt></a>. It is needed only to fix the
 * type boxing of its constituents before compiling it.
 */

public class Comprehension extends Let
{
  public Comprehension (ArrayList parameters, ArrayList values, Expression body)
    {
      super(parameters,values,body);
    }

  public final void typeCheck (TypeChecker typeChecker) throws TypingErrorException
    {
      if (typeCheckLocked()) return;

      Expression op = _arguments[0];    
      Expression id = _arguments[1];    

      Scope scope = (Scope)function();
      typeChecker.unify(scope.parameter(0).typeRef(),op.typeRef(),this);
      typeChecker.unify(scope.parameter(1).typeRef(),id.typeRef(),this);

      Type[] argumentTypes = { op.typeRef(), id.typeRef() };
      FunctionType functionType = new FunctionType(argumentTypes,_type).setNoCurrying();

      id.typeCheck(typeChecker);
      _function.typeCheck(functionType,typeChecker);
      op.typeCheck(functionType.domains()[0],typeChecker);
    }

  public final void compile (Compiler compiler)
    {
      _fixTypeBoxing();
      super.compile(compiler);
    }

  /**
   * This fixes the boxing of the monoid operator and identity by systematically unboxing
   * all occurrences of the collection element type. This is a necessary hack [:( sigh!]
   * because collection-building built-in dummy instructions like <tt>SET_ADD</tt> have a
   * needlessly polymorphic type that becomes instantiated only when it is applied. However,
   * a monoid comprehension construct is a <a href="./Let.html"><tt>Let</tt></a> that
   * abstracts the monoid operation and identity. Now, when the operation is <tt>SET_ADD</tt>,
   * for example, as the compiler compiles the application corresponding to the "let", it sees
   * it as a non-applied function argument with a polymorphic type and will proceed to "pad" it
   * (see <a href="./Expression.html"><tt>Expression</tt></a>). This "padding" must be avoided,
   * as well as all boxing of the types corresponding to the collection's elements.
   */
  private final void _fixTypeBoxing ()
    {
      FunctionType potype = (FunctionType)((FunctionType)function().checkedType()).domain(0);
      FunctionType otype = (FunctionType)argument(0).checkedType();
      Type itype = argument(1).checkedType();

      if (itype.kind() == Type.BOXABLE)
        ((BoxableTypeConstant)itype).setBoxed(false);

      if (otype.domain(0).kind() == Type.BOXABLE)
        {
          ((BoxableTypeConstant)otype.domain(0)).setBoxed(false);
          otype.unsetDomainBox(0);

          ((BoxableTypeConstant)potype.domain(0)).setBoxed(false);
          potype.unsetDomainBox(0);

          if (otype.domain(0).isEqualTo(otype.domain(1)))   // primitive comprehension
            {
              ((BoxableTypeConstant)otype.domain(1)).setBoxed(false);
              otype.unsetDomainBox(1);

              ((BoxableTypeConstant)potype.domain(1)).setBoxed(false);
              potype.unsetDomainBox(1);

              ((BoxableTypeConstant)otype.range()).setBoxed(false);
              otype.unsetRangeBox();

              ((BoxableTypeConstant)potype.range()).setBoxed(false);
              potype.unsetRangeBox();

            }
        }
    }
}
